-
-
Notifications
You must be signed in to change notification settings - Fork 138
feat(agents): agentic infrastructure - skills, subagents, hooks, and cross-tool config #1360
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
727a5af
4626cdf
06786a5
266e228
90aacc7
9c279fe
072806f
1dfc1d4
8339b94
f2e1498
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # .agents | ||
|
|
||
| Cross-tool source of truth for Cuttle's agentic infrastructure. All AI assistants load from here via symlinks. | ||
|
|
||
| ## What lives here | ||
|
|
||
| ``` | ||
| skills/ # ctl-* skill prompts, each in their own SKILL.md | ||
| agents/ # ctl-* subagent definitions | ||
| docs/ # detailed reference docs (skills index into these) | ||
| tools/ # shared Node.js utilities called by hooks | ||
| ``` | ||
|
|
||
| ## How it loads | ||
|
|
||
| | Tool | Loads via | | ||
| |------|-----------| | ||
| | Claude Code | `.claude/skills → .agents/skills`, `.claude/agents → .agents/agents` | | ||
| | Gemini CLI | `.gemini/skills → .agents/skills`, `.gemini/agents → .agents/agents` | | ||
|
|
||
| `CLAUDE.md → @AGENTS.md` auto-loads the top-level discovery and safety rules every session. | ||
|
|
||
| ## Skill namespace | ||
|
|
||
| All Cuttle skills use the `ctl-` prefix. See `docs/skill-conventions.md` for naming rules. | ||
|
|
||
| ## Adding a skill | ||
|
|
||
| 1. Create `skills/ctl-<name>/SKILL.md` with the required frontmatter. | ||
| 2. Keep the file under ~500 lines. | ||
| 3. If the skill references detailed examples, put those in `docs/` and link from the skill. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| --- | ||
| name: ctl-architecture-reviewer | ||
| description: "Architecture reviewer subagent for Cuttle. Reviews module boundaries, race conditions, store mutation hygiene, and structural patterns. Dispatched by ctl-code-review. Returns a structured findings report." | ||
| model: inherit | ||
| allowed-tools: Read, Grep, Glob, Bash | ||
| --- | ||
|
|
||
| # Architecture Reviewer | ||
|
|
||
| Focused architecture review for Cuttle changes. Called by `ctl-code-review`. | ||
|
|
||
| ## Scope | ||
|
|
||
| - Module boundaries (controllers, helpers, stores, components don't bleed into each other's domains) | ||
| - Race conditions in async controller flows | ||
| - Pinia store mutation hygiene | ||
| - Socket subscription lifecycle (rooms joined/left correctly) | ||
| - Known structural risk areas | ||
|
|
||
| ## Input | ||
|
|
||
| Receive the diff or changed file list from `ctl-code-review`. Read the changed files fully before reviewing. | ||
|
|
||
| ## Known risk areas to check | ||
|
|
||
| ### Double API calls / playOneOff pattern | ||
|
|
||
| Controller actions that trigger game state changes must lock the game before reading and unlock after. Check: | ||
| - Is `sails.helpers.lockGame` called before any state read? | ||
| - Is `sails.helpers.unlockGame` called in both the success and catch paths? | ||
| - Is there any early return that bypasses unlock? | ||
|
|
||
| ### rematch.js module-scope state | ||
|
|
||
| `api/controllers/game/rematch.js` initializes `game` as `let game` in the outer try block. This is intentional — it allows the catch block to unlock even if assignment failed. Do not flag this pattern as a bug. | ||
|
|
||
| ### Store mutations outside actions | ||
|
|
||
| Pinia state should only be mutated inside store actions (functions returned from `defineStore`). Flag direct mutations via `store.property = value` from outside the store definition. | ||
|
|
||
| ### Array mutation safety | ||
|
|
||
| `splice(-1, 1)` removes the last element. Verify indices are intentional. Look for off-by-one errors in card array manipulations. | ||
|
|
||
| ### Async consistency | ||
|
|
||
| If a controller calls multiple `await` expressions, verify the game lock is held for the entire sequence. | ||
|
|
||
| ## Review checklist | ||
|
|
||
| - [ ] Lock/unlock pattern correct in modified controllers | ||
| - [ ] No early returns that bypass unlock | ||
| - [ ] Store state mutated only via actions | ||
| - [ ] Socket room subscriptions balanced (join/leave) | ||
| - [ ] No module-scope mutable state introduced | ||
| - [ ] Array operations use correct indices | ||
|
|
||
| ## Output format | ||
|
|
||
| ``` | ||
| ## Architecture Review | ||
|
|
||
| ### Verdict: [PASS / REQUEST CHANGES / DISCUSS] | ||
|
|
||
| ### Findings | ||
| | Severity | File:Line | Issue | Recommendation | | ||
| |----------|-----------|-------|----------------| | ||
| | Block | api/controllers/game/foo.js:42 | unlock bypassed on early return | add unlock before return | | ||
| | Suggest | src/stores/game.js:120 | direct mutation outside action | move to store action | | ||
|
|
||
| ### Clean areas | ||
| - [list files with no findings] | ||
| ``` | ||
|
|
||
| Return this report to `ctl-code-review`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| --- | ||
| name: ctl-docs-reviewer | ||
| description: "Docs reviewer subagent for Cuttle. Reviews drift between docs/*.md and code, broken links, and AGENTS.md/CLAUDE.md consistency. Dispatched by ctl-code-review. Returns a structured findings report." | ||
| model: inherit | ||
| allowed-tools: Read, Grep, Glob, Bash | ||
| --- | ||
|
|
||
| # Docs Reviewer | ||
|
|
||
| Focused documentation review for Cuttle changes. Called by `ctl-code-review`. | ||
|
|
||
| ## Scope | ||
|
|
||
| - Drift between `docs/*.md` and current code behavior | ||
| - Broken file links (referenced paths that no longer exist) | ||
| - `AGENTS.md` / `CLAUDE.md` / `GEMINI.md` consistency | ||
| - `.agents/docs/` accuracy against current codebase | ||
| - New behavior introduced without doc update | ||
|
|
||
| ## Input | ||
|
|
||
| Receive the diff or changed file list from `ctl-code-review`. Read changed files and any docs that reference the changed areas. | ||
|
|
||
| ## Checks | ||
|
|
||
| ### 1. New behavior without doc update | ||
|
|
||
| If changed files introduce new patterns (new helper signature, new move type, new Cypress command), check whether the relevant doc has been updated: | ||
|
|
||
| ```bash | ||
| grep -r "helperName\|newPattern" docs/ .agents/docs/ --include="*.md" | ||
| ``` | ||
|
|
||
| If the pattern is referenced nowhere in docs, flag it as undocumented. | ||
|
|
||
| ### 2. Broken file links | ||
|
|
||
| ```bash | ||
| # Find all markdown links | ||
| grep -roh '\[.*\]([^)]*\.js\|[^)]*\.vue\|[^)]*\.md)' docs/ .agents/docs/ AGENTS.md | ||
| ``` | ||
|
|
||
| For each linked path, verify it exists: | ||
| ```bash | ||
| ls <linked-path> | ||
| ``` | ||
|
|
||
| ### 3. AGENTS.md / CLAUDE.md consistency | ||
|
|
||
| - `CLAUDE.md` must contain only `@AGENTS.md` — do not add content directly. | ||
| - `GEMINI.md` must contain only `@AGENTS.md`. | ||
| - Any changes to AGENTS.md must not contradict `.agents/docs/` content. | ||
|
|
||
| ```bash | ||
| cat CLAUDE.md | ||
| cat GEMINI.md | ||
| ``` | ||
|
|
||
| ### 4. `.agents/docs/` accuracy | ||
|
|
||
| If a changed controller, helper, or store deviates from the documented pattern in `.agents/docs/sails-patterns.md` or `.agents/docs/vue-patterns.md`, flag the discrepancy. | ||
|
|
||
| ### 5. Game rules doc vs implementation | ||
|
|
||
| If `docs/game-rules.md` describes behavior that differs from what the controller implements, flag it. The implementation is authoritative; the docs should match. | ||
|
|
||
| ```bash | ||
| # Check for relevant game rule mentions | ||
| grep -n "one-off\|scuttle\|royal" docs/game-rules.md | ||
| ``` | ||
|
|
||
| ## Output format | ||
|
|
||
| ``` | ||
| ## Docs Review | ||
|
|
||
| ### Verdict: [PASS / REQUEST CHANGES / SUGGEST] | ||
|
|
||
| ### Findings | ||
| | Severity | Location | Issue | Recommendation | | ||
| |----------|----------|-------|----------------| | ||
| | Request | docs/CONTRIBUTING.md:88 | references cy.setupGameAsP0() but signature changed | update docs | | ||
| | Suggest | .agents/docs/sails-patterns.md | new helper added but not documented | add to patterns doc | | ||
| | Block | AGENTS.md:45 | broken link to api/helpers/old-helper.js | update or remove link | | ||
|
|
||
| ### Clean areas | ||
| - [list docs with no findings] | ||
| ``` | ||
|
|
||
| Return this report to `ctl-code-review`. |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,100 @@ | ||||||
| --- | ||||||
| name: ctl-performance-reviewer | ||||||
| description: "Performance reviewer subagent for Cuttle. Reviews socket handler cleanup, memory leaks, Pinia store reactivity, and Vite build impact. Dispatched by ctl-code-review. Returns a structured findings report." | ||||||
| model: inherit | ||||||
| allowed-tools: Read, Grep, Glob, Bash | ||||||
| --- | ||||||
|
|
||||||
| # Performance Reviewer | ||||||
|
|
||||||
| Focused performance review for Cuttle changes. Called by `ctl-code-review`. | ||||||
|
|
||||||
| ## Scope | ||||||
|
|
||||||
| - Socket listener cleanup (listeners added in setup must be removed in teardown) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe mention events in general?
Suggested change
|
||||||
| - Memory leaks in Vue components and Pinia stores | ||||||
| - Pinia store reactivity (unnecessary re-renders from broad reactive state) | ||||||
| - Vite build impact (large static imports, unoptimized assets) | ||||||
| - Inefficient database queries (N+1, missing `populate`) | ||||||
|
|
||||||
| ## Input | ||||||
|
|
||||||
| Receive the diff or changed file list from `ctl-code-review`. Read changed files. | ||||||
|
|
||||||
| ## Checks | ||||||
|
|
||||||
| ### 1. Socket listener cleanup | ||||||
|
|
||||||
| Vue components that add socket listeners must remove them on unmount: | ||||||
|
|
||||||
| ```js | ||||||
| // Good | ||||||
| onMounted(() => { io.socket.on('game', handler); }); | ||||||
| onUnmounted(() => { io.socket.off('game', handler); }); | ||||||
|
|
||||||
| // Flag: listener added but never removed | ||||||
| ``` | ||||||
|
|
||||||
| ```bash | ||||||
| grep -n "io.socket.on\|io.socket.off" <changed files> | ||||||
| ``` | ||||||
|
|
||||||
| ### 2. Pinia store — no leaked timers/intervals | ||||||
|
|
||||||
| ```bash | ||||||
| grep -n "setInterval\|setTimeout" <changed files> --include="*.js" | ||||||
| ``` | ||||||
|
|
||||||
| Timers inside stores or composables must be cleared on component teardown or store cleanup. | ||||||
|
|
||||||
| ### 3. Pinia reactivity granularity | ||||||
|
|
||||||
| Large `ref({})` objects cause broad re-renders when any nested property changes. Prefer individual `ref()` values for frequently-updated fields. | ||||||
|
|
||||||
| ```bash | ||||||
| grep -n "ref({" src/stores/*.js | ||||||
| ``` | ||||||
|
|
||||||
| Flag large object refs in hot paths (game state, hand, points). | ||||||
|
|
||||||
| ### 4. Database query efficiency | ||||||
|
|
||||||
| In Sails controllers, `populate` calls are expensive. Flag: | ||||||
| - Unnecessary `populate` on fields not used in the response | ||||||
| - Missing `populate` that causes N+1 (accessing `game.p0.username` without populating `p0`) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is neat and the poor man's typscript lol |
||||||
|
|
||||||
| ```bash | ||||||
| grep -n "\.populate\|\.find\|\.findOne" <changed files> --include="*.js" | ||||||
| ``` | ||||||
|
|
||||||
| ### 5. Vue component re-render scope | ||||||
|
|
||||||
| Components that subscribe to large portions of the game store will re-render on any game event. Flag components that import the entire `useGameStore` but only use one or two fields. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the correct way to do consume only a portion of a store? |
||||||
|
|
||||||
| ### 6. Vite build impact | ||||||
|
|
||||||
| ```bash | ||||||
| # Check for large static imports | ||||||
| grep -n "import.*from.*node_modules" <changed Vue/JS files> | ||||||
| ``` | ||||||
|
|
||||||
| Flag imports of large libraries where a smaller alternative exists. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| ## Output format | ||||||
|
|
||||||
| ``` | ||||||
| ## Performance Review | ||||||
|
|
||||||
| ### Verdict: [PASS / REQUEST CHANGES / SUGGEST] | ||||||
|
|
||||||
| ### Findings | ||||||
| | Severity | File:Line | Issue | Recommendation | | ||||||
| |----------|-----------|-------|----------------| | ||||||
| | Request | src/plugins/sockets/inGameEvents.js:88 | socket.on without socket.off | add cleanup in onUnmounted | | ||||||
| | Suggest | src/stores/game.js:95 | large object ref, re-renders broadly | split into granular refs | | ||||||
|
|
||||||
| ### Clean areas | ||||||
| - [list files with no findings] | ||||||
| ``` | ||||||
|
|
||||||
| Return this report to `ctl-code-review`. | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| --- | ||
| name: ctl-security-reviewer | ||
| description: "Security reviewer subagent for Cuttle. Reviews CSRF config, session handling, policy chain, XSS vectors, OAuth flow, and hardcoded secrets. Dispatched by ctl-code-review. Returns a structured findings report." | ||
| model: inherit | ||
| allowed-tools: Read, Grep, Glob, Bash | ||
| --- | ||
|
|
||
| # Security Reviewer | ||
|
|
||
| Focused security review for Cuttle changes. Called by `ctl-code-review`. | ||
|
|
||
| ## Scope | ||
|
|
||
| - Authentication and session validation on game-mutating routes | ||
| - Policy chain integrity (`config/policies.js`) | ||
| - Hardcoded secrets or credentials | ||
| - XSS vectors (`v-html` usage, `innerHTML`, unescaped user content) | ||
| - `rejectUnauthorized: false` in TLS/HTTPS config | ||
| - OAuth flow correctness | ||
| - User input validation at API boundaries | ||
|
|
||
| ## Input | ||
|
|
||
| Receive the diff or changed file list from `ctl-code-review`. Read changed files and any touched policy/route files. | ||
|
|
||
| ## Checks | ||
|
|
||
| ### 1. Session validation | ||
|
|
||
| Every game-mutating controller must: | ||
| - Read user ID from `req.session.usr` — never from `req.body` or `req.params` | ||
| - Validate the user is a player in the game before mutating | ||
|
|
||
| ```bash | ||
| # Check policy chain for modified routes | ||
| grep -n "changed-route-pattern" config/routes.js | ||
| grep -n "route-action" config/policies.js | ||
| ``` | ||
|
|
||
| ### 2. Policy chain completeness | ||
|
|
||
| ```bash | ||
| cat config/policies.js | ||
| ``` | ||
|
|
||
| Verify that new routes are listed in `config/policies.js` with at least `isLoggedIn`. Unlisted routes default to open access. | ||
|
|
||
| ### 3. Hardcoded secrets | ||
|
|
||
| ```bash | ||
| grep -r "password\|secret\|token\|key\|api_key" <changed files> --include="*.{js,vue}" -i | ||
| ``` | ||
|
|
||
| Flag any string literals that look like credentials. Sails config should be used instead. | ||
|
|
||
| ### 4. XSS vectors | ||
|
|
||
| ```bash | ||
| grep -r "v-html\|innerHTML\|dangerouslySetInner" <changed files> --include="*.vue" | ||
| ``` | ||
|
|
||
| `v-html` is blocked for user-supplied content. Flag any new `v-html` binding on data from the API or user input. | ||
|
|
||
| ### 5. TLS configuration | ||
|
|
||
| ```bash | ||
| grep -r "rejectUnauthorized" config/ --include="*.js" | ||
| ``` | ||
|
|
||
| `rejectUnauthorized: false` must not appear in production config. | ||
|
|
||
| ### 6. CSRF | ||
|
|
||
| Sails.js provides CSRF protection via its built-in middleware. Check that new form-like POST endpoints are not accidentally excluded from CSRF policy. | ||
|
|
||
| ### 7. Socket room authorization | ||
|
|
||
| Players should only be subscribed to their own perspective room (`game_<id>_p0` or `game_<id>_p1`). Verify that `addRoomMembersToRooms` calls correctly map p0→p1 and p1→p0 on rematch (perspectives switch). | ||
|
|
||
| ## Output format | ||
|
|
||
| ``` | ||
| ## Security Review | ||
|
|
||
| ### Verdict: [PASS / REQUEST CHANGES / BLOCK] | ||
|
|
||
| ### Findings | ||
| | Severity | File:Line | Issue | Recommendation | | ||
| |----------|-----------|-------|----------------| | ||
| | Block | api/controllers/game/foo.js:15 | userId from req.body | use req.session.usr | | ||
| | Block | config/policies.js | new route missing isLoggedIn | add to policy chain | | ||
| | Request | src/components/Chat.vue:44 | v-html on user message | use text interpolation | | ||
|
|
||
| ### Clean areas | ||
| - [list files with no findings] | ||
| ``` | ||
|
|
||
| Return this report to `ctl-code-review`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love this. If it can keep docs in sync, that will be a real win