@haposoft/cafekit 0.18.0 → 0.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/bin/__tests__/claude-settings.test.js +123 -0
- package/bin/__tests__/codex-ask-guard.test.js +123 -0
- package/bin/__tests__/codex-ask-projection.test.js +51 -0
- package/bin/__tests__/codex-hooks-ownership.test.js +80 -1
- package/bin/__tests__/codex-hooks.test.js +938 -237
- package/bin/__tests__/codex-native-hook-boundaries.test.js +136 -0
- package/bin/__tests__/codex-native.test.js +222 -149
- package/bin/__tests__/codex-projection-parity.test.js +314 -0
- package/bin/__tests__/codex-quiet-hooks.test.js +170 -0
- package/bin/__tests__/codex-research-projection.test.js +70 -0
- package/bin/__tests__/codex-specs-projection.test.js +76 -0
- package/bin/__tests__/develop-contract.test.js +102 -1362
- package/bin/__tests__/docs-sync-on-demand.test.js +25 -20
- package/bin/__tests__/inspect-paths.test.js +183 -0
- package/bin/__tests__/installer-safety.test.js +335 -0
- package/bin/__tests__/legacy-authority-decoupled.test.js +14 -19
- package/bin/__tests__/legacy-hooks-retired.test.js +18 -8
- package/bin/__tests__/omp-bridge.test.js +66 -12
- package/bin/__tests__/omp-hooks.test.js +11 -5
- package/bin/__tests__/orca-session.test.js +7 -0
- package/bin/__tests__/package-inventory.test.js +1 -395
- package/bin/__tests__/privacy-paths.test.js +317 -0
- package/bin/__tests__/spec-narrowing.test.js +30 -134
- package/bin/__tests__/specs-v2-execution-closeout.test.js +0 -372
- package/bin/__tests__/test-runtime-dependency-closure.cjs +1 -2
- package/bin/__tests__/usage-hook-retired.test.js +5 -3
- package/bin/__tests__/validator-grounding.test.js +17 -2929
- package/bin/install.js +25 -0
- package/bin/lib/codex-install.js +134 -12
- package/bin/lib/context.js +1 -1
- package/bin/lib/managed-writer.js +2 -3
- package/bin/phases/claude-runtime.js +91 -25
- package/bin/phases/claude-settings.js +22 -1
- package/bin/phases/codex-hooks.js +96 -33
- package/bin/phases/codex-runtime.js +45 -49
- package/bin/phases/copy-payload.js +11 -28
- package/bin/phases/omp-runtime.js +6 -3
- package/package.json +2 -3
- package/src/claude/CLAUDE.md +4 -0
- package/src/claude/agents/code-auditor.md +9 -6
- package/src/claude/agents/debugger.md +3 -9
- package/src/claude/agents/docs-keeper.md +4 -7
- package/src/claude/agents/git-ops.md +2 -4
- package/src/claude/agents/implementer.md +6 -7
- package/src/claude/agents/inspector.md +2 -0
- package/src/claude/agents/project-manager.md +2 -2
- package/src/claude/agents/spec-maker.md +4 -4
- package/src/claude/agents/test-runner.md +8 -11
- package/src/claude/agents/ui-ux-designer.md +0 -1
- package/src/claude/gitignore +3 -0
- package/src/claude/hooks/__tests__/installed-root.test.js +111 -0
- package/src/claude/hooks/__tests__/precompact.test.js +206 -10
- package/src/claude/hooks/__tests__/privacy-block.test.js +27 -0
- package/src/claude/hooks/__tests__/session-writeenv.test.js +25 -0
- package/src/claude/hooks/__tests__/spec-gate.test.js +311 -340
- package/src/claude/hooks/__tests__/spec-state-touched.test.js +230 -8
- package/src/claude/hooks/__tests__/state.test.js +207 -7
- package/src/claude/hooks/__tests__/update-check.test.js +88 -0
- package/src/claude/hooks/agent.cjs +7 -6
- package/src/claude/hooks/inspect-block.cjs +15 -63
- package/src/claude/hooks/lib/inspect-paths.cjs +304 -0
- package/src/claude/hooks/lib/parser.cjs +1 -1
- package/src/claude/hooks/lib/privacy-command-analysis.cjs +191 -17
- package/src/claude/hooks/lib/privacy-paths.cjs +313 -0
- package/src/claude/hooks/lib/project-root.cjs +15 -0
- package/src/claude/hooks/lib/rules-reservation.cjs +36 -0
- package/src/claude/hooks/lib/spec-touch.cjs +67 -0
- package/src/claude/hooks/lib/update-check.cjs +77 -0
- package/src/claude/hooks/precompact.cjs +8 -15
- package/src/claude/hooks/privacy-block.cjs +23 -205
- package/src/claude/hooks/rules.cjs +6 -28
- package/src/claude/hooks/session.cjs +34 -117
- package/src/claude/hooks/spec-gate.cjs +17 -89
- package/src/claude/hooks/spec-state.cjs +89 -96
- package/src/claude/hooks/state.cjs +96 -51
- package/src/claude/migration-manifest.json +20 -8
- package/src/claude/output-styles/coding-level-0-eli5.md +9 -9
- package/src/claude/output-styles/coding-level-1-junior.md +3 -3
- package/src/claude/output-styles/coding-level-2-mid.md +3 -3
- package/src/claude/output-styles/coding-level-3-senior.md +2 -2
- package/src/claude/output-styles/coding-level-4-lead.md +5 -5
- package/src/claude/output-styles/coding-level-5-god.md +6 -6
- package/src/claude/rules/ai-dev-rules.md +1 -2
- package/src/claude/rules/hook-protocols.md +1 -1
- package/src/claude/rules/workflow.md +1 -1
- package/src/claude/runtime.json +0 -3
- package/src/claude/scripts/generate-skill-catalog.cjs +2 -2
- package/src/claude/scripts/provenance.cjs +14 -0
- package/src/claude/scripts/spec-receipt.cjs +0 -114
- package/src/claude/scripts/spec-resolver.cjs +41 -457
- package/src/claude/scripts/workflow-policy.cjs +3 -1801
- package/src/claude/settings/settings.json +1 -1
- package/src/claude/skills/agent-browser/SKILL.md +1 -1
- package/src/claude/skills/ai-multimodal/scripts/check_setup.py +8 -68
- package/src/claude/skills/ai-multimodal/scripts/gemini_batch_process.py +6 -28
- package/src/claude/skills/chrome-devtools/SKILL.md +14 -31
- package/src/claude/skills/code-review/SKILL.md +25 -108
- package/src/claude/skills/code-review/references/spec-compliance-review.md +4 -7
- package/src/claude/skills/develop/SKILL.md +22 -70
- package/src/claude/skills/develop/references/subagent-patterns.md +0 -6
- package/src/claude/skills/git/SKILL.md +1 -1
- package/src/claude/skills/git/references/worktree-blueprint.md +1 -1
- package/src/claude/skills/research/SKILL.md +24 -55
- package/src/claude/skills/sync/SKILL.md +12 -34
- package/src/claude/skills/sync/references/rebind-and-audit.md +2 -2
- package/src/claude/skills/test/SKILL.md +39 -123
- package/src/claude/skills/test/references/execution-strategy.md +11 -22
- package/src/claude/skills/ui-ux/SKILL.md +1 -1
- package/src/claude/skills/ui-ux-pro-max/SKILL.md +24 -79
- package/src/claude/skills/web-testing/SKILL.md +2 -20
- package/src/codex/AGENTS.md +8 -0
- package/src/codex/hooks/ask-guard.cjs +54 -0
- package/src/codex/hooks/inspect-block.cjs +11 -7
- package/src/codex/hooks/lib/inspect-paths.cjs +1 -199
- package/src/codex/hooks/lib/privacy-paths.cjs +1 -0
- package/src/codex/hooks/lib/privacy-state.cjs +98 -16
- package/src/codex/hooks/lib/rules-reservation.cjs +38 -0
- package/src/codex/hooks/lib/secret-keywords.cjs +1 -0
- package/src/codex/hooks/lib/spec-receipt.cjs +2 -30
- package/src/codex/hooks/lib/spec-session-touch.cjs +100 -0
- package/src/codex/hooks/lib/spec-touch.cjs +3 -0
- package/src/codex/hooks/lib/spec-utils.cjs +6 -45
- package/src/codex/hooks/lib/update-check.cjs +1 -0
- package/src/codex/hooks/precompact.cjs +39 -0
- package/src/codex/hooks/privacy-approval.cjs +28 -17
- package/src/codex/hooks/privacy-block.cjs +9 -215
- package/src/codex/hooks/rules.cjs +4 -30
- package/src/codex/hooks/secret-output-guardrail.cjs +1 -1
- package/src/codex/hooks/session.cjs +47 -11
- package/src/codex/hooks/spec-gate.cjs +32 -74
- package/src/codex/hooks/spec-state.cjs +68 -46
- package/src/codex/hooks/state.cjs +25 -4
- package/src/codex/hooks.json +39 -7
- package/src/codex/rules/hook-protocols.md +13 -6
- package/src/common/AGENTS.md +4 -10
- package/src/omp/extensions/cafekit-bridge.mjs +12 -4
- package/src/omp/hooks/privacy-block.cjs +24 -206
- package/bin/__tests__/change-firewall.semantic-firewall.test.js +0 -2133
- package/bin/__tests__/finalizer-pass-gate.semantic-firewall.test.js +0 -342
- package/bin/__tests__/release-preflight.semantic-firewall.test.js +0 -586
- package/bin/__tests__/review-receipt-c2.semantic-firewall.test.js +0 -378
- package/bin/__tests__/specs-v2-policy-and-scaffold.test.js +0 -665
- package/bin/__tests__/specs-v2-validator-grounder-contract.test.js +0 -1132
- package/src/claude/hooks/lib/context.cjs +0 -527
- package/src/claude/hooks/lib/detect.cjs +0 -474
- package/src/claude/scripts/change-firewall.cjs +0 -1748
- package/src/claude/scripts/spec-authoring-digest.cjs +0 -151
- package/src/claude/scripts/spec-authoring-validation.cjs +0 -126
- package/src/claude/scripts/spec-final-state.cjs +0 -290
- package/src/claude/scripts/spec-ground.cjs +0 -650
- package/src/claude/scripts/spec-readiness.cjs +0 -425
- package/src/claude/scripts/spec-scaffold.cjs +0 -2317
- package/src/claude/scripts/spec-semantic-model.cjs +0 -396
- package/src/claude/scripts/validate-spec-output.cjs +0 -3914
- package/src/claude/skills/code-review/references/verification-gate.md +0 -41
- package/src/claude/skills/specs/templates/design.md +0 -82
- package/src/claude/skills/specs/templates/requirements-init.md +0 -8
- package/src/claude/skills/specs/templates/requirements.md +0 -42
- package/src/claude/skills/specs/templates/spec-state.json +0 -56
- package/src/claude/skills/specs/templates/task.md +0 -47
- package/src/codex/rules/state-sync.md +0 -60
|
@@ -17,21 +17,81 @@ const { PLATFORMS } = require('../lib/context');
|
|
|
17
17
|
|
|
18
18
|
const CODEX_SRC = path.join(__dirname, '../../src/codex');
|
|
19
19
|
const HOOK_COMMAND_TEMPLATE = /^node "\.codex\/hooks\/([a-z0-9-]+\.cjs)"$/;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
const LEGACY_GIT_COMMAND = /^node "\$\(git rev-parse --show-toplevel\)\/\.codex\/hooks\/([a-z0-9-]+\.cjs)"$/;
|
|
21
|
+
const SHELL_QUOTED = "'(?:[^']|'\\\\'')*'";
|
|
22
|
+
const POSIX_COMMAND = new RegExp(`^(?:PATH="\\$PATH"(?::${SHELL_QUOTED})+ )?node (${SHELL_QUOTED})$`);
|
|
23
|
+
const WINDOWS_LAUNCHER = 'node -e "process.argv[1]=Buffer.from(process.argv[1],\'base64url\').toString(\'utf8\');require(\'module\').runMain()" ';
|
|
24
|
+
|
|
25
|
+
// Former template values from the hooks.json history. Custom expressions never
|
|
26
|
+
// establish ownership, even when every handler belongs to CafeKit.
|
|
27
|
+
const FORMER_MATCHERS = {
|
|
28
|
+
PreToolUse: new Set([
|
|
29
|
+
'Bash|apply_patch|Edit|Write|Read|Glob|Grep|mcp__.*',
|
|
30
|
+
'Bash|exec_command|apply_patch|Edit|Write|Read|Glob|Grep|mcp__.*|shell',
|
|
31
|
+
]),
|
|
32
|
+
PostToolUse: new Set([
|
|
33
|
+
'Agent|spawn_agent|update_plan|task|Task|TodoWrite',
|
|
34
|
+
'apply_patch|Edit|Write|MultiEdit',
|
|
35
|
+
]),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function refreshManagedMatchers(config, template, ctx) {
|
|
39
|
+
let updated = 0;
|
|
40
|
+
const hooks = { ...(config.hooks || {}) };
|
|
41
|
+
for (const [event, former] of Object.entries(FORMER_MATCHERS)) {
|
|
42
|
+
if (!Array.isArray(hooks[event])) continue;
|
|
43
|
+
hooks[event] = hooks[event].map(group => {
|
|
44
|
+
const handlers = Array.isArray(group?.hooks) ? group.hooks : [];
|
|
45
|
+
const scripts = handlers.map(managedHandlerScript);
|
|
46
|
+
if (!scripts.some(Boolean)) return group;
|
|
47
|
+
const candidates = handlers.length && scripts.every(Boolean)
|
|
48
|
+
? (template.hooks[event] || []).filter(target => {
|
|
49
|
+
const wanted = target.hooks.map(managedHandlerScript);
|
|
50
|
+
return scripts.every(script => wanted.includes(script));
|
|
51
|
+
}) : [];
|
|
52
|
+
const target = candidates.length === 1 ? candidates[0] : null;
|
|
53
|
+
if (target && (group.matcher || '') === (target.matcher || '')) return group;
|
|
54
|
+
if (target && former.has(group.matcher)) {
|
|
55
|
+
updated += 1;
|
|
56
|
+
ctx.ui.detail(` ↻ ${ctx.dryRun ? '[dry-run] ' : ''}Codex hooks: ${event} matcher updated`);
|
|
57
|
+
return { ...group, matcher: target.matcher };
|
|
58
|
+
}
|
|
59
|
+
ctx.ui.warn(`Codex hooks: ${event} matcher preserved; custom, mixed, or ambiguous group. Review it manually.`);
|
|
60
|
+
return group;
|
|
61
|
+
});
|
|
33
62
|
}
|
|
34
|
-
return
|
|
63
|
+
return { config: { ...config, hooks }, updated };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// A basename alone cannot prove ownership: users may also run tools/hooks/session.cjs.
|
|
67
|
+
// Only known source scripts at the native runtime path and an exact emitted launcher
|
|
68
|
+
// shape may be rebound or count as already registered.
|
|
69
|
+
const MANAGED_SCRIPTS = new Set(fs.readdirSync(path.join(CODEX_SRC, 'hooks')).filter((name) => /^[a-z0-9-]+\.cjs$/.test(name)));
|
|
70
|
+
|
|
71
|
+
function nativeScript(target) {
|
|
72
|
+
if (!path.posix.isAbsolute(target) && !path.win32.isAbsolute(target)) return null;
|
|
73
|
+
const match = target.match(/[/\\]\.codex[/\\]hooks[/\\]([a-z0-9-]+\.cjs)$/);
|
|
74
|
+
return match && MANAGED_SCRIPTS.has(match[1]) ? match[1] : null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function managedCommandScript(command) {
|
|
78
|
+
if (typeof command !== 'string') return null;
|
|
79
|
+
const template = command.match(HOOK_COMMAND_TEMPLATE) || command.match(LEGACY_GIT_COMMAND);
|
|
80
|
+
if (template) return MANAGED_SCRIPTS.has(template[1]) ? template[1] : null;
|
|
81
|
+
const posix = command.match(POSIX_COMMAND);
|
|
82
|
+
if (posix) return nativeScript(posix[1].slice(1, -1).replace(/'\\''/g, "'"));
|
|
83
|
+
if (!command.startsWith(WINDOWS_LAUNCHER)) return null;
|
|
84
|
+
const encoded = command.slice(WINDOWS_LAUNCHER.length);
|
|
85
|
+
if (!/^[A-Za-z0-9_-]+$/.test(encoded)) return null;
|
|
86
|
+
const target = Buffer.from(encoded, 'base64url').toString('utf8');
|
|
87
|
+
return Buffer.from(target, 'utf8').toString('base64url') === encoded ? nativeScript(target) : null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function managedHandlerScript(handler) {
|
|
91
|
+
if (handler?.type !== 'command') return null;
|
|
92
|
+
const fields = ['command', 'commandWindows'].filter((field) => handler[field] !== undefined);
|
|
93
|
+
const scripts = fields.map((field) => managedCommandScript(handler[field]));
|
|
94
|
+
return scripts.length > 0 && scripts.every((script) => script && script === scripts[0]) ? scripts[0] : null;
|
|
35
95
|
}
|
|
36
96
|
|
|
37
97
|
/** The hook script a command runs, which is stable across installs and platforms. */
|
|
@@ -57,8 +117,8 @@ function hookPathFor(script, projectRoot) {
|
|
|
57
117
|
* nothing outside a repository and the *outer* root for a project nested inside one. Both
|
|
58
118
|
* cases produced a path that does not exist, so Codex started every hook, every hook died,
|
|
59
119
|
* and the gates were silently absent. The path is known at install time, so it is written
|
|
60
|
-
* directly. The literal
|
|
61
|
-
*
|
|
120
|
+
* directly. The literal `.codex/hooks/<script>.cjs` path and emitted launcher shape
|
|
121
|
+
* let managedHandlerScript() recognize CafeKit's own entries on a reinstall.
|
|
62
122
|
*/
|
|
63
123
|
/**
|
|
64
124
|
* Directories to fall back on when PATH carries no `node`.
|
|
@@ -88,7 +148,7 @@ function posixHookCommand(script, projectRoot) {
|
|
|
88
148
|
*/
|
|
89
149
|
function windowsHookCommand(script, projectRoot) {
|
|
90
150
|
const encoded = Buffer.from(hookPathFor(script, projectRoot), 'utf8').toString('base64url');
|
|
91
|
-
return
|
|
151
|
+
return WINDOWS_LAUNCHER + encoded;
|
|
92
152
|
}
|
|
93
153
|
|
|
94
154
|
/** Rewrite a managed template handler into absolute launchers for both platforms. */
|
|
@@ -104,10 +164,9 @@ function materializeHookCommands(handler, projectRoot) {
|
|
|
104
164
|
}
|
|
105
165
|
|
|
106
166
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* CafeKit itself emitted is rewritten; anything else stays untouched.
|
|
167
|
+
* Rebind recognized CafeKit launchers after an upgrade or a project move. Registration
|
|
168
|
+
* preserves the user's event/matcher placement; only emitted command shapes change,
|
|
169
|
+
* and a custom field for another platform remains untouched.
|
|
111
170
|
*/
|
|
112
171
|
function repairLegacyCodexLaunchers(config, projectRoot) {
|
|
113
172
|
if (!config.hooks) return { config, repaired: 0 };
|
|
@@ -120,13 +179,13 @@ function repairLegacyCodexLaunchers(config, projectRoot) {
|
|
|
120
179
|
return {
|
|
121
180
|
...group,
|
|
122
181
|
hooks: group.hooks.map((handler) => {
|
|
123
|
-
|
|
124
|
-
const template = typeof handler?.commandWindows === 'string'
|
|
125
|
-
&& handler.commandWindows.match(HOOK_COMMAND_TEMPLATE);
|
|
126
|
-
if (!legacy && !template) return handler;
|
|
182
|
+
if (handler?.type !== 'command') return handler;
|
|
127
183
|
const next = { ...handler };
|
|
128
|
-
|
|
129
|
-
|
|
184
|
+
for (const [field, build] of [['command', posixHookCommand], ['commandWindows', windowsHookCommand]]) {
|
|
185
|
+
const script = managedCommandScript(handler[field]);
|
|
186
|
+
if (script) next[field] = build(script, projectRoot);
|
|
187
|
+
}
|
|
188
|
+
if (next.command === handler.command && next.commandWindows === handler.commandWindows) return handler;
|
|
130
189
|
repaired += 1;
|
|
131
190
|
return next;
|
|
132
191
|
}),
|
|
@@ -139,7 +198,7 @@ function repairLegacyCodexLaunchers(config, projectRoot) {
|
|
|
139
198
|
/** Drop CafeKit hooks the manifest has retired, leaving foreign entries untouched. */
|
|
140
199
|
function pruneObsoleteCodexHooks(config, ctx) {
|
|
141
200
|
const obsolete = ctx.manifest?.obsolete?.settingsHookCommandSubstrings || [];
|
|
142
|
-
if (!config.hooks
|
|
201
|
+
if (!config.hooks) return { config, removed: 0 };
|
|
143
202
|
|
|
144
203
|
let removed = 0;
|
|
145
204
|
const pruned = {};
|
|
@@ -150,7 +209,8 @@ function pruneObsoleteCodexHooks(config, ctx) {
|
|
|
150
209
|
if (!Array.isArray(group?.hooks)) { keptGroups.push(group); continue; }
|
|
151
210
|
const keptHooks = group.hooks.filter((handler) => {
|
|
152
211
|
const command = handler?.command || '';
|
|
153
|
-
const isObsolete = obsolete.some((substring) => command.includes(substring))
|
|
212
|
+
const isObsolete = obsolete.some((substring) => command.includes(substring))
|
|
213
|
+
|| (eventName === 'SessionStart' && managedHandlerScript(handler) === 'docs-sync.cjs');
|
|
154
214
|
if (isObsolete) removed += 1;
|
|
155
215
|
return !isObsolete;
|
|
156
216
|
});
|
|
@@ -196,12 +256,15 @@ function mergeCodexHooks(ctx, platformKey, projectRoot = process.cwd()) {
|
|
|
196
256
|
ctx.results.updated++;
|
|
197
257
|
}
|
|
198
258
|
|
|
199
|
-
const { config:
|
|
259
|
+
const { config: rebound, repaired } = repairLegacyCodexLaunchers(pruned, projectRoot);
|
|
200
260
|
if (repaired > 0) {
|
|
201
|
-
ctx.ui.detail(` ↻ ${ctx.dryRun ? '[dry-run] ' : ''}Codex hooks: rebound ${repaired} launcher(s)
|
|
261
|
+
ctx.ui.detail(` ↻ ${ctx.dryRun ? '[dry-run] ' : ''}Codex hooks: rebound ${repaired} launcher(s) to the current project`);
|
|
202
262
|
ctx.results.updated++;
|
|
203
263
|
}
|
|
204
264
|
|
|
265
|
+
const { config: base, updated: matcherUpdates } = refreshManagedMatchers(rebound, managed, ctx);
|
|
266
|
+
ctx.results.updated += matcherUpdates;
|
|
267
|
+
|
|
205
268
|
const merged = { ...base };
|
|
206
269
|
if (typeof managed.description === 'string' && typeof merged.description !== 'string') {
|
|
207
270
|
merged.description = managed.description;
|
|
@@ -215,7 +278,7 @@ function mergeCodexHooks(ctx, platformKey, projectRoot = process.cwd()) {
|
|
|
215
278
|
// it. Matching on the script rather than the whole command survives the path
|
|
216
279
|
// differences between installs and platforms.
|
|
217
280
|
const registered = new Set(
|
|
218
|
-
groups.flatMap((group) => (group?.hooks
|
|
281
|
+
groups.flatMap((group) => (Array.isArray(group?.hooks) ? group.hooks : []).map(managedHandlerScript)).filter(Boolean),
|
|
219
282
|
);
|
|
220
283
|
|
|
221
284
|
for (const managedGroup of managedGroups) {
|
|
@@ -242,7 +305,7 @@ function mergeCodexHooks(ctx, platformKey, projectRoot = process.cwd()) {
|
|
|
242
305
|
merged.hooks[eventName] = groups;
|
|
243
306
|
}
|
|
244
307
|
|
|
245
|
-
if (added === 0 && removed === 0) {
|
|
308
|
+
if (added === 0 && removed === 0 && matcherUpdates === 0) {
|
|
246
309
|
ctx.ui.detail(` → ${ctx.dryRun ? '[dry-run] ' : ''}Codex hooks: already registered`);
|
|
247
310
|
ctx.results.unchanged++;
|
|
248
311
|
}
|
|
@@ -5,7 +5,8 @@ const path = require('path');
|
|
|
5
5
|
const { PLATFORMS } = require('../lib/context');
|
|
6
6
|
const { writeManagedFile, copyManagedTree } = require('../lib/managed-writer');
|
|
7
7
|
const { report, treeAction } = require('./report');
|
|
8
|
-
const {
|
|
8
|
+
const { isGeneratedArtifact } = require('../lib/copy-utils');
|
|
9
|
+
const { copyRulesDirectory, copyOutputStylesDirectory, removeObsoleteRuntimeFiles } = require('./claude-runtime');
|
|
9
10
|
const {
|
|
10
11
|
managedRange,
|
|
11
12
|
upsertManagedCodexBlock
|
|
@@ -23,27 +24,9 @@ const CODEX_OWN_RUNTIME = [
|
|
|
23
24
|
['runtime.json', 'runtime.json']
|
|
24
25
|
];
|
|
25
26
|
|
|
26
|
-
/**
|
|
27
|
-
* Delete hooks the manifest marks obsolete. Codex tracker keys are project-relative
|
|
28
|
-
* (`.codex/hooks/…`), so the record is pruned through keyFor, and always: a record whose
|
|
29
|
-
* file is already gone must still leave the manifest.
|
|
30
|
-
*/
|
|
27
|
+
/** Use the shared ownership and symlink checks for Codex runtime cleanup. */
|
|
31
28
|
function removeObsoleteCodexHooks(ctx, platformKey) {
|
|
32
|
-
|
|
33
|
-
const tracker = ctx.trackers && ctx.trackers[platformKey];
|
|
34
|
-
const obsoleteHooks = (ctx.manifest?.obsolete?.runtimeFiles || []).filter((rel) => rel.startsWith('hooks/'));
|
|
35
|
-
for (const rel of obsoleteHooks) {
|
|
36
|
-
const target = path.join(platform.folder, rel);
|
|
37
|
-
const exists = fs.existsSync(target);
|
|
38
|
-
if (!ctx.dryRun) {
|
|
39
|
-
if (exists) fs.rmSync(target, { force: true, recursive: fs.statSync(target).isDirectory() });
|
|
40
|
-
if (tracker) tracker.prune(tracker.keyFor(target));
|
|
41
|
-
}
|
|
42
|
-
if (exists) {
|
|
43
|
-
ctx.ui.detail(` ↻ ${ctx.dryRun ? '[dry-run] ' : ''}Removed obsolete Codex runtime: ${rel}`);
|
|
44
|
-
ctx.results.updated++;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
29
|
+
removeObsoleteRuntimeFiles(ctx, platformKey);
|
|
47
30
|
}
|
|
48
31
|
|
|
49
32
|
function writeSourceFile(ctx, platformKey, src, dest, label) {
|
|
@@ -79,13 +62,32 @@ function installRuntimeFiles(ctx, platformKey) {
|
|
|
79
62
|
|
|
80
63
|
mergeCodexHooks(ctx, platformKey);
|
|
81
64
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
65
|
+
const hooksSource = path.join(CODEX_SRC, 'hooks');
|
|
66
|
+
const libsSource = path.join(hooksSource, 'lib');
|
|
67
|
+
const shimPattern = /^(?:['"]use strict['"];?\s*)?module\.exports\s*=\s*require\(['"]\.\.\/\.\.\/\.\.\/claude\/hooks\/lib\/([^'"/]+)['"]\);?\s*$/;
|
|
68
|
+
const sharedLibraries = fs.readdirSync(libsSource).filter(name => {
|
|
69
|
+
const file = path.join(libsSource, name);
|
|
70
|
+
if (!fs.lstatSync(file).isFile()) return false;
|
|
71
|
+
const match = fs.readFileSync(file, 'utf8').match(shimPattern);
|
|
72
|
+
return match && match[1] === name;
|
|
88
73
|
});
|
|
74
|
+
const agg = { created: 0, updated: 0, unchanged: 0, preserved: 0, missing: 0 };
|
|
75
|
+
const copyHookEntry = relative => {
|
|
76
|
+
if (isGeneratedArtifact(path.basename(relative))) return;
|
|
77
|
+
const child = copyManagedTree({
|
|
78
|
+
src: path.join(hooksSource, relative),
|
|
79
|
+
dest: path.join(platform.folder, 'hooks', relative),
|
|
80
|
+
sourceRoot: hooksSource,
|
|
81
|
+
platformFolder: platform.folder, ctx, tracker: ctx.trackers[platformKey]
|
|
82
|
+
});
|
|
83
|
+
for (const key of Object.keys(agg)) agg[key] += child[key];
|
|
84
|
+
};
|
|
85
|
+
for (const name of fs.readdirSync(hooksSource)) {
|
|
86
|
+
if (name !== 'lib') copyHookEntry(name);
|
|
87
|
+
else for (const lib of fs.readdirSync(libsSource)) {
|
|
88
|
+
if (!sharedLibraries.includes(lib)) copyHookEntry(path.join('lib', lib));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
89
91
|
report(ctx, treeAction(agg), 'Codex native hooks');
|
|
90
92
|
removeObsoleteCodexHooks(ctx, platformKey);
|
|
91
93
|
// Keep one canonical command analyzer in the Claude source tree, then
|
|
@@ -99,27 +101,21 @@ function installRuntimeFiles(ctx, platformKey) {
|
|
|
99
101
|
path.join(platform.folder, 'runtime.schema.json'),
|
|
100
102
|
'Codex runtime schema'
|
|
101
103
|
);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
path.
|
|
107
|
-
'Codex
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
ctx,
|
|
118
|
-
platformKey,
|
|
119
|
-
path.join(SRC, 'claude', 'hooks', 'lib', 'runtime-path-safety.cjs'),
|
|
120
|
-
path.join(platform.folder, 'hooks', 'lib', 'runtime-path-safety.cjs'),
|
|
121
|
-
'Codex runtime path safety'
|
|
122
|
-
);
|
|
104
|
+
const helperLabels = {
|
|
105
|
+
'privacy-command-analysis.cjs': 'Codex privacy command analyzer',
|
|
106
|
+
'privacy-paths.cjs': 'Codex privacy path analyzer',
|
|
107
|
+
'secret-keywords.cjs': 'Codex secret keyword matcher',
|
|
108
|
+
'runtime-path-safety.cjs': 'Codex runtime path safety',
|
|
109
|
+
'inspect-paths.cjs': 'Codex inspection path analyzer',
|
|
110
|
+
'spec-touch.cjs': 'Codex spec touch helpers',
|
|
111
|
+
'update-check.cjs': 'Codex update check'
|
|
112
|
+
};
|
|
113
|
+
for (const name of sharedLibraries) {
|
|
114
|
+
writeSourceFile(ctx, platformKey,
|
|
115
|
+
path.join(SRC, 'claude', 'hooks', 'lib', name),
|
|
116
|
+
path.join(platform.folder, 'hooks', 'lib', name),
|
|
117
|
+
helperLabels[name] || `Codex hook helper: ${name}`);
|
|
118
|
+
}
|
|
123
119
|
}
|
|
124
120
|
|
|
125
121
|
function installNativeRuleOverrides(ctx, platformKey) {
|
|
@@ -167,7 +163,7 @@ function installManagedAgentsMd(ctx) {
|
|
|
167
163
|
function installCodexRuntime(ctx, platformKey) {
|
|
168
164
|
if (platformKey !== 'codex') return ctx;
|
|
169
165
|
installRuntimeFiles(ctx, platformKey);
|
|
170
|
-
copyRulesDirectory(ctx, platformKey);
|
|
166
|
+
copyRulesDirectory(ctx, platformKey, new Set(fs.readdirSync(path.join(CODEX_SRC, 'rules'))));
|
|
171
167
|
copyOutputStylesDirectory(ctx, platformKey);
|
|
172
168
|
installNativeRuleOverrides(ctx, platformKey);
|
|
173
169
|
installManagedAgentsMd(ctx);
|
|
@@ -94,37 +94,20 @@ function copyPlatformFiles(ctx, platformKey) {
|
|
|
94
94
|
report(ctx, treeAction(agg), 'Skill: specs');
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
//
|
|
97
|
+
// The specs skill tree above carries its templates; remove the ones retired
|
|
98
|
+
// with the spec.json planning flow from earlier installs.
|
|
98
99
|
if (platformKey === 'claude' || platformKey === 'codex') {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
const retiredTemplates = ['init.json', 'spec-state.json', 'requirements-init.md', 'requirements.md', 'design.md', 'task.md'];
|
|
101
|
+
for (const fileName of retiredTemplates) {
|
|
102
|
+
const retired = path.join(platform.skillsDir, 'specs', 'templates', fileName);
|
|
103
|
+
if (!fs.existsSync(retired)) continue;
|
|
104
|
+
if (!ctx.dryRun) {
|
|
105
|
+
fs.rmSync(retired, { force: true });
|
|
106
|
+
if (tracker) tracker.prune(tracker.keyFor(retired));
|
|
107
|
+
}
|
|
108
|
+
ctx.ui.detail(` ↻ ${ctx.dryRun ? '[dry-run] ' : ''}Removed legacy template: ${retired}`);
|
|
103
109
|
ctx.results.updated++;
|
|
104
110
|
}
|
|
105
|
-
|
|
106
|
-
const specTemplates = [
|
|
107
|
-
'spec-state.json',
|
|
108
|
-
'requirements-init.md',
|
|
109
|
-
'requirements.md',
|
|
110
|
-
'design.md',
|
|
111
|
-
'research.md',
|
|
112
|
-
'task.md'
|
|
113
|
-
];
|
|
114
|
-
|
|
115
|
-
specTemplates.forEach((fileName) => {
|
|
116
|
-
const src = path.join(specSkillSource, 'templates', fileName);
|
|
117
|
-
const dest = path.join(platform.skillsDir, 'specs', 'templates', fileName);
|
|
118
|
-
const { action } = writeManagedFile({
|
|
119
|
-
src,
|
|
120
|
-
dest,
|
|
121
|
-
platformFolder,
|
|
122
|
-
ctx,
|
|
123
|
-
tracker,
|
|
124
|
-
transform: bodyTransform
|
|
125
|
-
});
|
|
126
|
-
report(ctx, action, `template: ${fileName}`);
|
|
127
|
-
});
|
|
128
111
|
}
|
|
129
112
|
|
|
130
113
|
// Core skills plus the optional document bundle selected for this runtime.
|
|
@@ -129,16 +129,19 @@ function installBridge(ctx, platformKey) {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
/**
|
|
132
|
-
* Delete hooks the manifest marks obsolete. Mirrors removeObsoleteClaudeRuntimeFiles, but the
|
|
132
|
+
* Delete hooks, scripts and skill files the manifest marks obsolete. Mirrors removeObsoleteClaudeRuntimeFiles, but the
|
|
133
133
|
* omp tracker records keys relative to the project root (`.omp/hooks/…`), so the record is
|
|
134
134
|
* pruned through keyFor rather than by the bare `hooks/…` path.
|
|
135
135
|
*/
|
|
136
136
|
function removeObsoleteOmpHooks(ctx, platformKey) {
|
|
137
137
|
const platform = PLATFORMS[platformKey];
|
|
138
138
|
const tracker = ctx.trackers && ctx.trackers[platformKey];
|
|
139
|
-
const obsoleteHooks = (ctx.manifest?.obsolete?.runtimeFiles || []).filter((rel) => rel.startsWith('hooks/'));
|
|
139
|
+
const obsoleteHooks = (ctx.manifest?.obsolete?.runtimeFiles || []).filter((rel) => rel.startsWith('hooks/') || rel.startsWith('scripts/') || rel.startsWith('skills/'));
|
|
140
140
|
for (const rel of obsoleteHooks) {
|
|
141
|
-
|
|
141
|
+
// Skill files live in the shared skills directory, not under the runtime folder.
|
|
142
|
+
const target = rel.startsWith('skills/')
|
|
143
|
+
? path.join(platform.skillsDir, rel.slice('skills/'.length))
|
|
144
|
+
: path.join(platform.folder, rel);
|
|
142
145
|
if (!fs.existsSync(target)) continue;
|
|
143
146
|
if (!ctx.dryRun) {
|
|
144
147
|
fs.rmSync(target, { force: true, recursive: fs.statSync(target).isDirectory() });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haposoft/cafekit",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
4
4
|
"description": "Native spec-driven workflows for Claude Code and Codex CLI. Bundles CafeKit skills, runtime hooks, agents, and installer scaffolding.",
|
|
5
5
|
"author": "Haposoft <nghialt@haposoft.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
"test": "node scripts/run-skill-self-tests.mjs",
|
|
13
13
|
"test:package": "node --test bin/__tests__/package-inventory.test.js",
|
|
14
14
|
"test:secrets": "node --test bin/__tests__/secret-scanner.test.js",
|
|
15
|
-
"test:policy": "node --test bin/__tests__/develop-contract.test.js"
|
|
16
|
-
"release:freeze": "node scripts/benchmark-workflow.mjs freeze"
|
|
15
|
+
"test:policy": "node --test bin/__tests__/develop-contract.test.js"
|
|
17
16
|
},
|
|
18
17
|
"files": [
|
|
19
18
|
"bin",
|
package/src/claude/CLAUDE.md
CHANGED
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
## Claude Code runtime
|
|
9
9
|
|
|
10
|
+
Project notes (commands, files to leave unchanged, slow or expensive operations)
|
|
11
|
+
belong in the root AGENTS.md after the CAFEKIT CORE END marker; Claude Code reads
|
|
12
|
+
them through `@AGENTS.md`.
|
|
13
|
+
|
|
10
14
|
- Edit project skills under `.claude/skills/`, not `~/.claude/skills`.
|
|
11
15
|
- Run Python skill scripts with the skill venv:
|
|
12
16
|
- macOS/Linux: `.claude/skills/.venv/bin/python3 scripts/<script>.py`
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: code-auditor
|
|
3
3
|
tools: Glob, Grep, Read, Bash, WebFetch, WebSearch
|
|
4
|
-
description: "Source Code Auditor. Verifies code quality, severities (🔴 Critical / 🟠 High / 🟡 Medium / 🔵 Low), Automatic Criticals, and task/spec completion drift. Returns one review verdict from the shared surface: PASS | PASS_WITH_WARNINGS | FAIL | BLOCKED."
|
|
4
|
+
description: "Source Code Auditor. Verifies code quality, severities (🔴 Critical / 🟠 High / 🟡 Medium / 🔵 Low), Automatic Criticals, and task/spec completion drift. Returns one review verdict from the shared surface: PASS | PASS_WITH_WARNINGS | FAIL | BLOCKED. It never runs tests. When it states execution proof as unavailable (owned by /cf:test) and the user asked only for a review, a caller relaying its review must not run tests itself to fill that gap and offers /cf:test instead; a user request to run tests, or a workflow that owns its own test step, is unaffected."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Code Auditor — Source Code Inspector
|
|
@@ -22,10 +22,11 @@ Never report a test result, pass count, or exit code as the review's evidence.
|
|
|
22
22
|
Without a `test-proof-v1` handoff, state execution proof as unavailable (owned by
|
|
23
23
|
`cf:test`): write the proof line exactly as `**Execution proof:** unavailable (owned by cf:test)`,
|
|
24
24
|
do not otherwise say whether tests pass, and still return the verdict.
|
|
25
|
+
When the proof line says unavailable, end the report with the caller line of the template, verbatim: if the user asked only for a review, the session that relays it must not run tests itself to fill the proof gap and offers `/cf:test` instead; a user request to run tests, or a workflow that owns its test step, is unaffected. With a consumed `test-proof-v1` handoff, omit the caller line.
|
|
25
26
|
|
|
26
|
-
## Pre-Review: Task / Spec Compliance
|
|
27
|
+
## Pre-Review: Task / Spec Compliance
|
|
27
28
|
|
|
28
|
-
If the prompt includes task file paths, requirement IDs, completion criteria, or design contracts,
|
|
29
|
+
If the prompt includes task file paths, requirement IDs, completion criteria, or design contracts, read them before reviewing code.
|
|
29
30
|
If the prompt says `SPEC COMPLIANCE REVIEW ONLY`, do not perform a general
|
|
30
31
|
quality review yet. For process-first work, first prove the implementation
|
|
31
32
|
matches `plan.md` accepted GATE-SCOPE/GATE-REVIEW decisions and the active flat `task-NN-*.md` Outcome,
|
|
@@ -47,9 +48,9 @@ Any missing declared deliverable, placeholder-only wiring, or contract drift is
|
|
|
47
48
|
Any scoped behavior omitted, unapproved behavior added, orphaned component/service/route/command/worker/provider/reducer, unmounted UI, unregistered route, uncalled loader/service, or unreachable runtime surface is a **Critical** issue even if tests/build pass.
|
|
48
49
|
If the task/spec explicitly names Better Auth, Hono, Next.js proxy routes, Redis, Drizzle, or any other concrete choice, replacing it with a custom simplification is a **Critical** issue unless the spec was amended first.
|
|
49
50
|
|
|
50
|
-
## Pre-Review: Blast Radius Check
|
|
51
|
+
## Pre-Review: Blast Radius Check
|
|
51
52
|
|
|
52
|
-
Before reading any specific logic,
|
|
53
|
+
Before reading any specific logic, run a Dependency Scope Check (Blast Radius):
|
|
53
54
|
1. Obtain the list of modified functions/components exported from the changed files.
|
|
54
55
|
2. Run a global `Grep` across `src/` to find ALL files that import or call these functions.
|
|
55
56
|
3. Identify if the signature change or internal state mutation breaks these dependents.
|
|
@@ -149,11 +150,13 @@ Keep `## Review Report`, its headings and the severity labels verbatim in Englis
|
|
|
149
150
|
|
|
150
151
|
### ✅ Positive Observations
|
|
151
152
|
- [Acknowledge good code, good patterns]
|
|
153
|
+
|
|
154
|
+
**For the caller:** this review ran no tests by design. If execution proof is unavailable and the user asked only for a review, do not run tests yourself to fill the gap; offer `/cf:test` to the user instead. A user request to run tests, or a workflow that owns its test step, is unaffected.
|
|
152
155
|
```
|
|
153
156
|
|
|
154
157
|
## Pass/Fail Thresholds (Used in Quality Gate)
|
|
155
158
|
|
|
156
|
-
When called from `develop`
|
|
159
|
+
When called from the `cf:develop` quality gate (`references/quality-gate.md`):
|
|
157
160
|
|
|
158
161
|
| Condition | Result |
|
|
159
162
|
|-----------|--------|
|
|
@@ -8,8 +8,6 @@ tools: Glob, Grep, Read, Bash, WebFetch, WebSearch
|
|
|
8
8
|
|
|
9
9
|
You are a veteran incident responder who has survived hundreds of production outages. You think in evidence chains: every hypothesis must be backed by log lines, stack traces, metrics, browser evidence, or code facts. You never guess when you can grep.
|
|
10
10
|
|
|
11
|
-
**IMPORTANT**: Ensure token efficiency while maintaining high quality.
|
|
12
|
-
|
|
13
11
|
## Core Competencies
|
|
14
12
|
|
|
15
13
|
You excel at:
|
|
@@ -23,7 +21,7 @@ You excel at:
|
|
|
23
21
|
- **Side-Effect Analysis**: Mapping blast radius and defining the checks needed to prove a fix does not regress nearby behavior
|
|
24
22
|
- **Strict Protocol (MANDATORY)**: Read the relevant manuals in `.claude/references/debugger/` before conclusions. At minimum read `core-philosophy.md`, `root-cause-tracing.md`, `verification-protocol.md`, and `side-effect-gate.md` before recommending a fix direction. Add domain references such as `log-ci-analysis.md`, `frontend-verification.md`, `performance-diagnostics.md`, or `condition-based-waiting.md` when they apply.
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
Activate a skill from the catalog when it covers part of the investigation.
|
|
27
25
|
|
|
28
26
|
## Operating Boundary
|
|
29
27
|
|
|
@@ -52,10 +50,7 @@ When investigating issues, you will:
|
|
|
52
50
|
- Read repository instructions, README, and relevant existing docs, then verify their claims against current source, tests, config, logs, and runtime evidence.
|
|
53
51
|
- Use `/cf:scout` or focused local `rg`/reads for missing or conflicting context.
|
|
54
52
|
- Use `repomix` only when the user authorized the broad snapshot and it materially improves a wide investigation. Never create or refresh documentation merely to satisfy Debug.
|
|
55
|
-
-
|
|
56
|
-
```bash
|
|
57
|
-
# usage: repomix --remote <github-repo-url>
|
|
58
|
-
```
|
|
53
|
+
- For a GitHub repository URL, the same authorization applies to `repomix --remote <github-repo-url>`.
|
|
59
54
|
|
|
60
55
|
3. **Analysis Process**
|
|
61
56
|
- Correlate events across different log sources
|
|
@@ -122,8 +117,7 @@ You will:
|
|
|
122
117
|
- Highlight critical findings that require immediate attention
|
|
123
118
|
- Offer risk assessments for proposed solutions
|
|
124
119
|
- Maintain a systematic, methodical approach to problem-solving
|
|
125
|
-
-
|
|
126
|
-
- **IMPORTANT:** In reports, list any unresolved questions at the end, if any.
|
|
120
|
+
- Keep reports concise, and list any unresolved questions at the end.
|
|
127
121
|
|
|
128
122
|
## Report Output
|
|
129
123
|
|
|
@@ -9,7 +9,7 @@ tools: Glob, Grep, Read, Edit, Write, Bash, WebFetch, TaskCreate, TaskGet, TaskU
|
|
|
9
9
|
|
|
10
10
|
You are the authoritative **Documentation Guardian** for this repository.
|
|
11
11
|
Stale docs and phantom specs are worse than no docs — they waste developer hours.
|
|
12
|
-
Your core operational rule:
|
|
12
|
+
Your core operational rule: read the code and verify it works before you write about it.
|
|
13
13
|
|
|
14
14
|
You juggle two parallel universes defined by the `specs` ecosystem: The agile feature specification lifecycle (`specs/`) and the global project documentation (`docs/`).
|
|
15
15
|
|
|
@@ -59,16 +59,13 @@ When called from `develop` after a verified task is complete:
|
|
|
59
59
|
- If impact is `minor` or `major`, prefer surgical edits to `docs/project-overview-pdr.md`, `docs/system-architecture.md`, `docs/code-standards.md`, changelog/roadmap files, or other already-existing docs
|
|
60
60
|
- Do NOT run `repomix` just because code changed; use it only if direct verification is insufficient
|
|
61
61
|
|
|
62
|
-
### 5. File Size
|
|
63
|
-
|
|
64
|
-
1. Identify semantic boundaries (distinct topics that can stand alone).
|
|
65
|
-
2. Split into `docs/{topic}/index.md` + part files.
|
|
66
|
-
3. Hyperlink heavily. Do not repeat context unnecessarily.
|
|
62
|
+
### 5. File Size
|
|
63
|
+
Split a doc only when it holds distinct topics that readers need separately, never just to meet a line count; link the parts instead of repeating context.
|
|
67
64
|
|
|
68
65
|
## Writing Style
|
|
69
66
|
- Lead with purpose, not background prose.
|
|
70
67
|
- Use Markdown tables instead of paragraph lists for structured data.
|
|
71
|
-
-
|
|
68
|
+
- One concept per section; cut sentences that carry no information.
|
|
72
69
|
- Always use relative paths for internal linking: `[text](./path.md)`.
|
|
73
70
|
|
|
74
71
|
## Integration Points & Hooks
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: git-ops
|
|
3
|
-
description: Executes staging, committing, and pushing branches using conventional commits conventions. Use this when the user
|
|
3
|
+
description: Executes staging, committing, and pushing branches using conventional commits conventions. Use this when the user asks to commit or push; finishing a feature or fix does not by itself authorize either.
|
|
4
4
|
model: haiku
|
|
5
5
|
tools: Glob, Grep, Read, Bash, TaskCreate, TaskGet, TaskUpdate, TaskList, SendMessage
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
You are the Git Operations Specialist
|
|
9
|
-
|
|
10
|
-
**CRITICAL REQUIREMENT**: Strictly optimize for token efficiency and operational speed, yet maintain high-quality conventional git histories.
|
|
8
|
+
You are the Git Operations Specialist. Activate the `git` skill and carry out the requested operation; git operations are narrow, so inspect only what the commit or push needs.
|
|
11
9
|
|
|
12
10
|
## Team Mode
|
|
13
11
|
|
|
@@ -10,25 +10,24 @@ tools: Glob, Grep, Read, Edit, Write, NotebookEdit, Bash, WebFetch, WebSearch
|
|
|
10
10
|
You are a senior engineer specialized in turning one approved CafeKit task into
|
|
11
11
|
real code. Work uses `specs/<feature>/plan.md` plus one flat
|
|
12
12
|
`task-NN-*.md`.
|
|
13
|
-
|
|
14
|
-
Any logic gaps must be clarified BEFORE typing, not discovered after bugs ship.
|
|
13
|
+
Write production code, not prototypes, and resolve logic gaps in the task before coding rather than after.
|
|
15
14
|
|
|
16
15
|
## Core Principles
|
|
17
16
|
|
|
18
17
|
- **YAGNI**: Do not add any feature outside the Spec.
|
|
19
18
|
- **KISS**: Always prefer the simplest solution.
|
|
20
19
|
- **DRY**: No code duplication. Reuse existing utils/helpers.
|
|
21
|
-
- **
|
|
20
|
+
- **Reports**: brief and specific to the task.
|
|
22
21
|
- **Canonical state ownership:** This rule applies under every dispatch mode.
|
|
23
22
|
Do NOT edit `plan.md`, task `Status:`, or inline `## Receipt`; the controller
|
|
24
23
|
is the sole process-first state-and-proof writer.
|
|
25
24
|
- **Surgical Reading (Large Files):** Never use blanket `Read` commands on files > 800 lines. Use nested `Grep` or chunked reading (offset/limit) to surgically target modified points.
|
|
26
|
-
- **Component
|
|
25
|
+
- **Component size:** Consider splitting a source file over 200 lines when it has a clear logical boundary and the split stays inside the task's Ownership.
|
|
27
26
|
|
|
28
27
|
|
|
29
28
|
## Self-Check Checklist (Before Reporting Complete)
|
|
30
29
|
|
|
31
|
-
- [ ]
|
|
30
|
+
- [ ] Errors are handled at meaningful boundaries and none is silently swallowed; do not wrap every call defensively.
|
|
32
31
|
- [ ] All external data (API requests, form inputs, env vars) is validated at system boundaries.
|
|
33
32
|
- [ ] No `TODO` or `FIXME` blocking the main flow. If a workaround is needed, it must have an explanatory comment.
|
|
34
33
|
- [ ] Public API/Interface matches the Spec requirements exactly — do not add or remove fields arbitrarily.
|
|
@@ -79,7 +78,7 @@ Upon completion, output a concise report in this format:
|
|
|
79
78
|
```markdown
|
|
80
79
|
## Implementation Report
|
|
81
80
|
|
|
82
|
-
### Status: [
|
|
81
|
+
### Status: [DONE | CONCERNS | BLOCKED | NEEDS_INFO]
|
|
83
82
|
|
|
84
83
|
### Files Modified/Created
|
|
85
84
|
- `path/to/file.ts` — Brief description of changes
|
|
@@ -111,4 +110,4 @@ When dispatched into an isolated git worktree by `cf:develop --parallel`:
|
|
|
111
110
|
- Preserve the global canonical state ownership rule above.
|
|
112
111
|
- For process-first work, do NOT touch files outside the task's Ownership
|
|
113
112
|
boundary.
|
|
114
|
-
- Commit
|
|
113
|
+
- Commit only with the user's authorization, as a conventional commit with no task ID in the message; an uncommitted worktree cannot be merged back, so without authorization report the uncommitted state to the controller.
|