Skip to content

feat: Add hooks, steering, and skills system with multi-tool integration#55

Merged
josealekhine merged 1 commit intomainfrom
kiro_cline
Apr 4, 2026
Merged

feat: Add hooks, steering, and skills system with multi-tool integration#55
josealekhine merged 1 commit intomainfrom
kiro_cline

Conversation

@parlakisik
Copy link
Copy Markdown
Contributor

Extend ctx from a persistence-only tool into a behavioral guidance and lifecycle automation platform. Adds steering files for AI behavioral rules, lifecycle hooks for event-driven automation, reusable skill bundles, and cross-tool setup for Kiro, Cursor, and Cline.

New commands: ctx steering, ctx hook, ctx skill.
New MCP tools: ctx_steering_get, ctx_search, ctx_session_start, ctx_session_end.
New setup targets: ctx setup kiro/cursor/cline --write.
Fix: MCP tool descriptions now resolve correctly at runtime.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Apr 3, 2026

Deploying ctx with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1d05145
Status: ✅  Deploy successful!
Preview URL: https://b87d0109.ctx-bhl.pages.dev
Branch Preview URL: https://kiro-cline.ctx-bhl.pages.dev

View logs

@parlakisik parlakisik force-pushed the kiro_cline branch 2 times, most recently from 9b354f9 to 61a84e8 Compare April 3, 2026 15:03
Copy link
Copy Markdown
Member

@josealekhine josealekhine left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @parlakisik — this is an ambitious contribution, thank you for the effort 🙏 .

Before we can review this for merge, there are several things that need to be addressed:

  1. Naming conflict: ctx hook

We explicitly renamed ctx hook → ctx setup on April 1st (https://github.com/ActiveMemory/ctx) to avoid ambiguity with the PreToolUse/PostToolUse hook system that ctx uses internally.

This PR reintroduces ctx hook as a lifecycle hooks command, which recreates the exact naming collision we resolved. The lifecycle hooks subsystem would need a different name (e.g. ctx
trigger, ctx lifecycle, or similar).

  1. Audit test modifications

This PR modifies cross_package_types_test.go, dead_exports_test.go, and compliance_test.go to add allowlist entries for the new code. Our convention is that new code should conform to
the audit checks, not loosen them.

Please review whether the new packages can be structured to pass the existing checks without allowlist additions.

  1. Convention compliance

After rebasing (you're 1 commit behind), please verify locally:

make lint   # must be 0 issues
make test   # all tests must pass, including audit checks

The audit tests in internal/audit/ enforce: magic strings, line length (≤80 chars), doc comments, no raw file I/O (os.ReadFile → ctxio.ReadFile), no inline regexp, import/variable shadowing, type file placement, and more.


After rebasing onto current main, TestTypeFileConvention will fail. The fix is mechanical: move type definitions from implementation files to types.go in each package.

Here are the exact fixes:

Design Issues

A. ctx hook naming conflict

We explicitly renamed ctx hookctx setup on April 1st to avoid ambiguity with ctx's internal hook system (PreToolUse/PostToolUse hooks in Claude Code's settings.json). This PR reintroduces ctx hook as a lifecycle hooks command — different functionality, same name, same collision we already resolved.

The lifecycle hooks subsystem needs a different command name. Some options: ctx trigger, ctx lifecycle, ctx on — open to your suggestion, but ctx hook is off the table.

This affects: internal/cli/hook/, internal/config/embed/cmd/hook.go, internal/err/hook/, internal/exec/hook/, internal/hook/, internal/write/hook/, and the bootstrap registration.

B. Audit test modifications

This PR modifies 3 existing audit/compliance tests. The allowlists and logic in these tests exist for pre-existing violations that predate the checks. New code must conform, not widen the exemptions. Here's what needs to change:

B1. compliance_test.goTestCmdDirPurity allowlist

You added:

"steering/cmd/initcmd/cmd.go": {"foundationFile": true},

This is new code, not a pre-existing violation. Remove this entry and move foundationFile out of cmd/ (see fix #5 below).

B2. dead_exports_test.go — 3 new exemption maps

  • linuxOnlyExports (12 entries) — This is legitimate. We have a known issue where go/packages only loads files matching the current GOOS, so _linux.go callers are invisible on macOS. Good catch, and the separate map with explanation is the right approach.

  • yamlOnlyExports (5 DescKey entries) — These are described as "forward declarations for future drift checks and hook tooling." We tested without the exemption and confirmed all 5 are dead code with zero callers. Delete these constants from the source files and remove the yamlOnlyExports map entirely. Add them when the consuming code lands.

    The dead constants to remove:

    • DescKeyDriftCheckSteeringTools in internal/config/embed/text/drift.go:61
    • DescKeyDriftCheckHookPerms in internal/config/embed/text/drift.go:62
    • DescKeyDriftCheckSyncStaleness in internal/config/embed/text/drift.go:63
    • DescKeyDriftCheckRCTool in internal/config/embed/text/drift.go:64
    • DescKeyHookCursor in internal/config/embed/text/hook.go:16
  • testOnlyExports: bootstrap.ResolveTool — We tested without the exemption and confirmed this is a dead export (zero callers in non-test code). Delete ResolveTool from internal/bootstrap/cmd.go:140 and remove the testOnlyExports entry. If it's needed for tests, make it unexported and access it via a test helper.

B3. cross_package_types_test.gosameModule logic widened

You added mcp/ as a consumer layer alongside cli/ via a new isConsumerLayer() helper. This means any mcp/* package can now import types from any domain package without triggering TestCrossPackageTypes.

We tested without the change and found exactly 2 cross-package types that motivated it:

  • hook.HookType (defined in internal/hook/types.go:10, used from mcp/handler)
  • hook.HookInput (defined in internal/hook/types.go:54, used from mcp/handler)

The fix is to move HookType and HookInput to internal/entity/ (the designated home for cross-package types) instead of widening sameModule. Then revert the isConsumerLayer change entirely — restore the original cli/-only consumer layer logic.

// In internal/entity/hook.go (new file):
package entity

// HookType identifies the lifecycle hook event type.
type HookType string  // + copy the const block

// HookInput carries the payload for a lifecycle hook invocation.
type HookInput struct { ... }  // + copy fields

Then update imports in internal/hook/ and internal/mcp/handler/ to use entity.HookType and entity.HookInput.


Type Placement Fixes

Here are the exact fixes for TestTypeFileConvention:

1. internal/cli/setup/core/cline/types.go (new file)

Move from cline.go:

package cline

// vscodeMCPConfig is the top-level mcp.json structure for Cline.
type vscodeMCPConfig struct {
	Servers map[string]vscodeMCPServer `json:"servers"`
}

// vscodeMCPServer describes one MCP server entry in mcp.json.
type vscodeMCPServer struct {
	Command string   `json:"command"`
	Args    []string `json:"args"`
}

2. internal/cli/setup/core/cursor/types.go (new file)

Move from cursor.go:

package cursor

// mcpConfig is the top-level mcp.json structure for Cursor.
type mcpConfig struct {
	MCPServers map[string]serverEntry `json:"mcpServers"`
}

// serverEntry describes one MCP server entry in mcp.json.
type serverEntry struct {
	Command string   `json:"command"`
	Args    []string `json:"args"`
}

3. internal/cli/setup/core/kiro/types.go (new file)

Move from kiro.go:

package kiro

// mcpConfig is the top-level mcp.json structure for Kiro.
type mcpConfig struct {
	MCPServers map[string]serverEntry `json:"mcpServers"`
}

// serverEntry describes one MCP server entry in mcp.json.
type serverEntry struct {
	Command     string   `json:"command"`
	Args        []string `json:"args"`
	Disabled    bool     `json:"disabled"`
	AutoApprove []string `json:"autoApprove"`
}

4. internal/steering/types.go (append to existing)

Move from sync_helpers.go:

// cursorFrontmatter is the YAML frontmatter for Cursor rule files.
type cursorFrontmatter struct {
	Description string `yaml:"description"`
	Globs       []any  `yaml:"globs"`
	AlwaysApply bool   `yaml:"alwaysApply"`
}

// kiroFrontmatter is the YAML frontmatter for Kiro steering files.
type kiroFrontmatter struct {
	Name        string `yaml:"name"`
	Description string `yaml:"description,omitempty"`
	Mode        string `yaml:"mode"`
}

5. internal/cli/steering/cmd/initcmd/cmd.gointernal/steering/types.go

Move foundationFile and the foundationFiles var to internal/steering/types.go, then import from cmd/initcmd. Also remove the allowlist entry you added to compliance_test.go:

- "steering/cmd/initcmd/cmd.go": {"foundationFile": true},

The allowlist in TestCmdDirPurity exists only for pre-existing violations that predate the check. New code must conform — types and data don't belong in cmd/ directories. cmd/ should only contain cmd.go, run.go, and doc.go.

6. Rename helpers.go files

Our convention is to name files after what they contain, not their role. helpers.go is a lazy name that doesn't communicate intent. Please rename:

Current Rename to Rationale
setup/core/cline/helpers.go deploy.go Contains ensureMCPConfig + syncSteering — these are deployment steps, matching the existing Deploy* naming in the write layer
setup/core/cursor/helpers.go deploy.go Same pattern
setup/core/kiro/helpers.go deploy.go Same pattern
steering/sync_helpers.go format.go Contains formatNative, nativePath, validateOutputPath — this is the native format conversion layer for sync

Verify

After making these moves, rebase and run:

git rebase main
make lint   # must be 0 issues
make test   # all 38 audit checks must pass

Everything else looks clean. Nice work on matching the project's conventions — the doc.go files, copyright headers, write/ package separation, and error package taxonomy are all spot on.

Add lifecycle hooks, steering files, and skills packages with full
convention alignment to main's audit and compliance requirements.

Key additions:
- Hook discovery, runner, and security validation (internal/hook/)
- Steering file parser, sync engine, and filter (internal/steering/)
- Skill install, load, and remove (internal/skill/)
- CLI commands: ctx hook, ctx steering, ctx skill
- Setup support for Cursor, Kiro, and Cline
- MCP steering search tool
- Drift checks for steering sync staleness

Convention alignment:
- All fmt.Errorf moved to internal/err/ helpers
- All cmd.Println/Printf moved to internal/write/ helpers
- All raw file I/O replaced with internal/io/Safe* wrappers
- All bare err := renamed to descriptive names
- All flag bindings use flagbind.* helpers
- exec.CommandContext moved to internal/exec/hook/
- Magic strings/numbers replaced with config constants
- Line length, doc comments, mixed visibility fixes

Signed-off-by: Murat Parlakisik <parlakisik@gmail.com>
@josealekhine josealekhine merged commit aa38968 into main Apr 4, 2026
12 checks passed
josealekhine added a commit that referenced this pull request Apr 4, 2026
All user-facing format strings in write/steering, write/skill,
write/trigger, write/setup, mcp/handler, trigger/, and drift/
now go through desc.Text() with YAML-backed DescKeys.

Added 42 YAML entries in ui.yaml and 30 DescKey constants across
6 config/embed/text files (steering.go, trigger.go new; skill.go,
setup.go, mcp_tool.go, drift.go updated). Also adds predicate
naming convention section to audit-conventions.md.

Spec: specs/ast-audit-contributor-guide.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
@josealekhine josealekhine deleted the kiro_cline branch April 4, 2026 10:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants