@haposoft/cafekit 0.16.7 → 0.18.0

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.
Files changed (193) hide show
  1. package/README.md +14 -22
  2. package/bin/__tests__/codex-hooks-ownership.test.js +2 -2
  3. package/bin/__tests__/codex-hooks.test.js +0 -36
  4. package/bin/__tests__/codex-native.test.js +46 -88
  5. package/bin/__tests__/develop-contract.test.js +26 -57
  6. package/bin/__tests__/docs-sync-on-demand.test.js +93 -0
  7. package/bin/__tests__/grok-envelope.test.js +0 -56
  8. package/bin/__tests__/hook-payload.test.js +4 -6
  9. package/bin/__tests__/legacy-authority-decoupled.test.js +159 -0
  10. package/bin/__tests__/legacy-hooks-retired.test.js +221 -0
  11. package/bin/__tests__/omp-bridge.test.js +20 -17
  12. package/bin/__tests__/omp-hooks.test.js +0 -19
  13. package/bin/__tests__/optional-skill-inventory.test.js +112 -4
  14. package/bin/__tests__/orca-session.test.js +0 -2
  15. package/bin/__tests__/package-inventory.test.js +100 -541
  16. package/bin/__tests__/runtime-dir.test.js +0 -3
  17. package/bin/__tests__/runtime-schema.test.js +3 -2
  18. package/bin/__tests__/skill-routing-source.test.js +27 -15
  19. package/bin/__tests__/spec-narrowing.test.js +0 -27
  20. package/bin/__tests__/specs-v2-execution-closeout.test.js +4 -69
  21. package/bin/__tests__/ui-ux-skill.test.js +101 -0
  22. package/bin/__tests__/usage-hook-retired.test.js +114 -0
  23. package/bin/__tests__/validator-grounding.test.js +9 -28
  24. package/bin/phases/codex-runtime.js +24 -0
  25. package/bin/phases/omp-runtime.js +22 -0
  26. package/package.json +1 -1
  27. package/src/claude/CLAUDE.md +3 -10
  28. package/src/claude/agents/brainstormer.md +7 -3
  29. package/src/claude/agents/code-auditor.md +35 -50
  30. package/src/claude/agents/debugger.md +5 -79
  31. package/src/claude/agents/deployer.md +3 -5
  32. package/src/claude/agents/docs-keeper.md +2 -7
  33. package/src/claude/agents/implementer.md +6 -9
  34. package/src/claude/agents/inspector.md +2 -3
  35. package/src/claude/agents/project-manager.md +7 -15
  36. package/src/claude/agents/researcher.md +9 -3
  37. package/src/claude/agents/spec-maker.md +11 -11
  38. package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -1
  39. package/src/claude/hooks/__tests__/spec-gate.test.js +74 -3
  40. package/src/claude/hooks/__tests__/spec-state-touched.test.js +134 -0
  41. package/src/claude/hooks/__tests__/state.test.js +68 -0
  42. package/src/claude/hooks/__tests__/statusline.test.js +140 -11
  43. package/src/claude/hooks/agent.cjs +2 -1
  44. package/src/claude/hooks/lib/context.cjs +0 -89
  45. package/src/claude/hooks/lib/hook-payload.cjs +2 -2
  46. package/src/claude/hooks/rules.cjs +2 -1
  47. package/src/claude/hooks/session.cjs +0 -1
  48. package/src/claude/hooks/spec-gate.cjs +17 -28
  49. package/src/claude/hooks/spec-state.cjs +95 -1
  50. package/src/claude/hooks/state.cjs +21 -2
  51. package/src/claude/migration-manifest.json +30 -15
  52. package/src/claude/rules/ai-dev-rules.md +1 -1
  53. package/src/claude/rules/hook-protocols.md +8 -15
  54. package/src/claude/rules/manage-docs.md +9 -23
  55. package/src/claude/rules/orchestrator.md +3 -4
  56. package/src/claude/rules/review-audit-self-decision.md +3 -3
  57. package/src/claude/rules/skill-domain-routing.md +1 -3
  58. package/src/claude/rules/skill-workflow-routing.md +4 -5
  59. package/src/claude/rules/state-sync.md +3 -12
  60. package/src/claude/rules/workflow.md +10 -18
  61. package/src/claude/runtime.schema.json +5 -3
  62. package/src/claude/scripts/generate-skill-catalog.cjs +7 -6
  63. package/src/claude/scripts/provenance.cjs +23 -5
  64. package/src/claude/scripts/spec-receipt.cjs +24 -7
  65. package/src/claude/scripts/spec-resolver.cjs +1 -2
  66. package/src/claude/scripts/validate-spec-output.cjs +5 -32
  67. package/src/claude/settings/settings.json +1 -32
  68. package/src/claude/skills/{question → ask}/SKILL.md +9 -75
  69. package/src/claude/skills/brainstorm/SKILL.md +43 -131
  70. package/src/claude/skills/code-review/SKILL.md +48 -17
  71. package/src/claude/skills/code-review/references/spec-compliance-review.md +2 -3
  72. package/src/claude/skills/code-review/references/verification-gate.md +22 -27
  73. package/src/claude/skills/debug/SKILL.md +29 -149
  74. package/src/claude/skills/develop/SKILL.md +27 -36
  75. package/src/claude/skills/develop/references/parallel-waves.md +0 -8
  76. package/src/claude/skills/develop/references/quality-gate.md +15 -13
  77. package/src/claude/skills/docs/SKILL.md +2 -2
  78. package/src/claude/skills/docs/references/update-workflow.md +1 -1
  79. package/src/claude/skills/fix/SKILL.md +153 -0
  80. package/src/claude/skills/{hotfix → fix}/references/diagnosis-protocol.md +2 -2
  81. package/src/claude/skills/{hotfix → fix}/references/parallel-patterns.md +2 -2
  82. package/src/claude/skills/{hotfix → fix}/references/prevention-gate.md +3 -3
  83. package/src/claude/skills/{hotfix → fix}/references/review-cycle.md +1 -1
  84. package/src/claude/skills/git/SKILL.md +48 -9
  85. package/src/claude/skills/git/references/commit-protocols.md +35 -16
  86. package/src/claude/skills/git/references/worktree-blueprint.md +42 -6
  87. package/src/claude/skills/orca/SKILL.md +9 -8
  88. package/src/claude/skills/research/SKILL.md +2 -0
  89. package/src/claude/skills/scout/SKILL.md +61 -0
  90. package/src/claude/skills/{inspect → scout}/references/internal-inspection.md +26 -10
  91. package/src/claude/skills/specs/SKILL.md +22 -29
  92. package/src/claude/skills/specs/references/review.md +8 -6
  93. package/src/claude/skills/specs/references/templates.md +34 -41
  94. package/src/claude/skills/sync/SKILL.md +8 -15
  95. package/src/claude/skills/sync/references/rebind-and-audit.md +59 -0
  96. package/src/claude/skills/sync/references/sync-protocols.md +4 -13
  97. package/src/claude/skills/test/SKILL.md +22 -3
  98. package/src/claude/skills/test/references/execution-strategy.md +83 -0
  99. package/src/claude/skills/test/references/failure-triage.md +1 -1
  100. package/src/claude/skills/ui-ux/LICENSE +21 -0
  101. package/src/claude/skills/ui-ux/SKILL.md +497 -0
  102. package/src/claude/skills/ui-ux/references/brand-tokens.md +90 -0
  103. package/src/claude/skills/ui-ux/references/budgets.md +113 -0
  104. package/src/claude/skills/ui-ux/references/checklist.md +271 -0
  105. package/src/claude/skills/ui-ux/references/components/accordion.md +246 -0
  106. package/src/claude/skills/ui-ux/references/components/avatar.md +153 -0
  107. package/src/claude/skills/ui-ux/references/components/banner.md +43 -0
  108. package/src/claude/skills/ui-ux/references/components/breadcrumb.md +201 -0
  109. package/src/claude/skills/ui-ux/references/components/button.md +142 -0
  110. package/src/claude/skills/ui-ux/references/components/card.md +113 -0
  111. package/src/claude/skills/ui-ux/references/components/charts.md +376 -0
  112. package/src/claude/skills/ui-ux/references/components/chat.md +132 -0
  113. package/src/claude/skills/ui-ux/references/components/choice-controls.md +369 -0
  114. package/src/claude/skills/ui-ux/references/components/comment-thread.md +17 -0
  115. package/src/claude/skills/ui-ux/references/components/description-list.md +116 -0
  116. package/src/claude/skills/ui-ux/references/components/empty-state.md +98 -0
  117. package/src/claude/skills/ui-ux/references/components/file-upload.md +107 -0
  118. package/src/claude/skills/ui-ux/references/components/inline-edit.md +128 -0
  119. package/src/claude/skills/ui-ux/references/components/input.md +161 -0
  120. package/src/claude/skills/ui-ux/references/components/list-row.md +56 -0
  121. package/src/claude/skills/ui-ux/references/components/loading.md +198 -0
  122. package/src/claude/skills/ui-ux/references/components/logo.md +147 -0
  123. package/src/claude/skills/ui-ux/references/components/otp-input.md +43 -0
  124. package/src/claude/skills/ui-ux/references/components/quantity-input.md +100 -0
  125. package/src/claude/skills/ui-ux/references/components/range-slider.md +16 -0
  126. package/src/claude/skills/ui-ux/references/components/small-controls.md +340 -0
  127. package/src/claude/skills/ui-ux/references/components/sortable-header.md +86 -0
  128. package/src/claude/skills/ui-ux/references/components/tag-input.md +14 -0
  129. package/src/claude/skills/ui-ux/references/components/timeline.md +38 -0
  130. package/src/claude/skills/ui-ux/references/components/tree.md +30 -0
  131. package/src/claude/skills/ui-ux/references/design-process.md +650 -0
  132. package/src/claude/skills/ui-ux/references/layouts/app-kanban.html +303 -0
  133. package/src/claude/skills/ui-ux/references/layouts/app.md +1369 -0
  134. package/src/claude/skills/ui-ux/references/layouts/form.md +430 -0
  135. package/src/claude/skills/ui-ux/references/layouts/overlay.md +552 -0
  136. package/src/claude/skills/ui-ux/references/layouts/pricing.md +204 -0
  137. package/src/claude/skills/ui-ux/references/locked-rules.md +35 -0
  138. package/src/claude/skills/ui-ux/references/principles.md +408 -0
  139. package/src/claude/skills/ui-ux/references/refactor.md +245 -0
  140. package/src/claude/skills/ui-ux/references/responsive.md +195 -0
  141. package/src/claude/skills/ui-ux/references/review.md +528 -0
  142. package/src/claude/skills/ui-ux/references/rules-color.md +623 -0
  143. package/src/claude/skills/ui-ux/references/rules-form.md +235 -0
  144. package/src/claude/skills/ui-ux/references/rules-state.md +680 -0
  145. package/src/claude/skills/ui-ux/references/rules-type.md +369 -0
  146. package/src/claude/skills/ui-ux/references/styles.md +311 -0
  147. package/src/claude/skills/ui-ux/references/system.md +219 -0
  148. package/src/claude/skills/ui-ux/references/tailwind-v4-traps.md +237 -0
  149. package/src/claude/skills/ui-ux/references/tokens.css +362 -0
  150. package/src/claude/skills/ui-ux/scripts/lint-skill.mjs +216 -0
  151. package/src/claude/skills/ui-ux/scripts/package.json +9 -0
  152. package/src/claude/skills/ui-ux/scripts/probe.mjs +4447 -0
  153. package/src/claude/skills/ui-ux-pro-max/SKILL.md +1 -1
  154. package/src/claude/status.cjs +149 -144
  155. package/src/codex/AGENTS.md +8 -13
  156. package/src/codex/hooks/agent.cjs +2 -1
  157. package/src/codex/hooks/lib/spec-receipt.cjs +2 -2
  158. package/src/codex/hooks/lib/spec-utils.cjs +0 -7
  159. package/src/codex/hooks/rules.cjs +2 -1
  160. package/src/codex/hooks/session.cjs +0 -2
  161. package/src/codex/hooks/spec-gate.cjs +7 -18
  162. package/src/codex/hooks.json +0 -20
  163. package/src/codex/rules/state-sync.md +3 -12
  164. package/src/common/AGENTS.md +2 -2
  165. package/src/omp/extensions/cafekit-bridge.mjs +6 -6
  166. package/src/claude/hooks/__tests__/completion-authority.test.js +0 -990
  167. package/src/claude/hooks/__tests__/semantic-review-authority.test.js +0 -259
  168. package/src/claude/hooks/completion-authority-check.cjs +0 -56
  169. package/src/claude/hooks/completion-authority-state.cjs +0 -527
  170. package/src/claude/hooks/completion-authority.cjs +0 -163
  171. package/src/claude/hooks/semantic-review-authority.cjs +0 -154
  172. package/src/claude/hooks/task-scaffold-guard.cjs +0 -114
  173. package/src/claude/hooks/usage.cjs +0 -197
  174. package/src/claude/skills/delegate/SKILL.md +0 -124
  175. package/src/claude/skills/delegate/references/codex-delegation.md +0 -71
  176. package/src/claude/skills/delegate/references/grok-delegation.md +0 -84
  177. package/src/claude/skills/hotfix/SKILL.md +0 -289
  178. package/src/claude/skills/inspect/SKILL.md +0 -142
  179. package/src/claude/skills/loop/SKILL.md +0 -114
  180. package/src/claude/skills/loop/references/bounded-loop-protocol.md +0 -104
  181. package/src/claude/skills/loop/references/metric-and-guard-contract.md +0 -76
  182. package/src/claude/skills/route/SKILL.md +0 -94
  183. package/src/claude/skills/route/references/agent-timing.md +0 -46
  184. package/src/claude/skills/route/references/chaining-patterns.md +0 -53
  185. package/src/claude/skills/route/references/task-taxonomy.md +0 -48
  186. package/src/codex/hooks/completion-authority-check.cjs +0 -67
  187. package/src/codex/hooks/completion-authority-state.cjs +0 -527
  188. package/src/codex/hooks/completion-authority.cjs +0 -109
  189. package/src/codex/hooks/semantic-review-authority.cjs +0 -130
  190. package/src/codex/hooks/task-scaffold-guard.cjs +0 -63
  191. /package/src/claude/skills/{question → ask}/templates/question.md +0 -0
  192. /package/src/claude/skills/{hotfix → fix}/references/escalation-tactics.md +0 -0
  193. /package/src/claude/skills/{hotfix → fix}/references/workflow-specialized.md +0 -0
@@ -1,154 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const fs = require('fs');
5
- const path = require('path');
6
- const { runtimeDirName, runtimeDir, runtimePath } = require('./lib/runtime-dir.cjs');
7
- const { spawnSync } = require('child_process');
8
- const STATE = require('./completion-authority-state.cjs');
9
- const PATH_SAFETY = require('./lib/runtime-path-safety.cjs');
10
-
11
- const SCHEMA_VERSION = 1;
12
- const MARKER = 'CAFEKIT_SEMANTIC_REVIEW_ATTESTATION ';
13
- const SHA256_RE = /^sha256:[a-f0-9]{64}$/;
14
- const REVIEWER_ROLE_ALLOWLIST = new Set(['code-auditor', 'code_auditor']);
15
- const RECORD_FIELDS = ['schema_version', 'kind', 'identity', 'semantic_digest', 'event', 'verdict', 'host_session_id', 'reviewer_agent_id', 'reviewer_agent_type', 'issued_at', 'mac'];
16
-
17
- function plain(value) {
18
- return Boolean(value && typeof value === 'object' && !Array.isArray(value));
19
- }
20
-
21
- function exactKeys(value, fields) {
22
- return plain(value) && JSON.stringify(Object.keys(value).sort()) === JSON.stringify([...fields].sort());
23
- }
24
-
25
- function inside(root, target) {
26
- const relative = path.relative(root, target);
27
- return relative !== '' && relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
28
- }
29
-
30
- function projectRoot(payload = {}) {
31
- const installedRoot = path.resolve(__dirname, '..', '..');
32
- if (fs.existsSync(runtimePath(installedRoot, 'hooks', path.basename(__filename)))) {
33
- try { return STATE.canonicalProjectRoot(installedRoot); } catch {}
34
- }
35
- for (const candidate of [process.env.CLAUDE_PROJECT_DIR, process.env.PROJECT_ROOT]) {
36
- if (typeof candidate === 'string' && candidate.trim()) {
37
- try { return STATE.canonicalProjectRoot(candidate); } catch { /* continue */ }
38
- }
39
- }
40
- const cwd = typeof payload.cwd === 'string' && payload.cwd.trim() ? payload.cwd : process.cwd();
41
- return STATE.canonicalProjectRoot(cwd);
42
- }
43
-
44
- function identityFor(root, specFile, featureName) {
45
- if (typeof featureName !== 'string' || !featureName.trim() || featureName.includes('/') || featureName.includes('\\') || featureName === '.' || featureName === '..' || featureName.includes('..')) throw new Error('feature identity is malformed');
46
- if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(featureName)) throw new Error('feature identity is malformed');
47
- const canonicalRoot = STATE.canonicalProjectRoot(root);
48
- const canonicalSpec = fs.realpathSync(path.resolve(specFile));
49
- if (!inside(canonicalRoot, canonicalSpec) || path.basename(canonicalSpec) !== 'spec.json') throw new Error('spec identity escapes the project');
50
- const relative = path.relative(canonicalRoot, canonicalSpec);
51
- const expected = path.join('specs', featureName, 'spec.json');
52
- if (relative !== expected) throw new Error(`spec identity must be ${expected}, not ${relative}`);
53
- const spec = JSON.parse(fs.readFileSync(canonicalSpec, 'utf8'));
54
- if (spec.feature_name !== featureName) throw new Error('feature identity does not match spec.json');
55
- return { project_root: canonicalRoot, spec_file: canonicalSpec, feature_name: featureName };
56
- }
57
-
58
- function recordPath(root, identity) {
59
- const name = STATE.digest({ namespace: 'cafekit-semantic-review-observation-v1', identity });
60
- return path.join(STATE.stateDir(root), 'semantic-reviews', `observation-${name}.json`);
61
- }
62
-
63
- function parseClaim(message) {
64
- const lines = String(message || '').split(/\r?\n/).filter((line) => line.startsWith(MARKER));
65
- if (lines.length === 0) return null;
66
- if (lines.length !== 1) throw new Error('exactly one semantic review attestation marker is required');
67
- const claim = JSON.parse(lines[0].slice(MARKER.length));
68
- if (!exactKeys(claim, ['feature_name', 'spec_file', 'semantic_digest', 'verdict'])) throw new Error('semantic review claim fields are malformed');
69
- if (claim.verdict !== 'PASS' || !SHA256_RE.test(claim.semantic_digest || '')) throw new Error('semantic review claim must contain literal PASS and a sha256 digest');
70
- return claim;
71
- }
72
-
73
- function validatorFor(root) {
74
- const installedRoot = runtimeDir(root);
75
- const installedHook = path.join(installedRoot, 'hooks', path.basename(__filename));
76
- if (fs.existsSync(installedHook)) {
77
- const canonicalHook = PATH_SAFETY.canonicalRegularFile(root, installedHook, 'installed semantic hook');
78
- if (canonicalHook !== fs.realpathSync(__filename)) throw new Error('installed semantic hook identity is invalid');
79
- const validator = path.join(installedRoot, 'scripts', 'validate-spec-output.cjs');
80
- PATH_SAFETY.verifyCommonJsClosure(installedRoot, validator);
81
- return PATH_SAFETY.canonicalRegularFile(installedRoot, validator, 'semantic validator');
82
- }
83
-
84
- const sourceRuntime = path.resolve(__dirname, '..');
85
- const sourceRoot = path.resolve(sourceRuntime, '..');
86
- if (`.${path.basename(sourceRuntime)}` !== runtimeDirName() || path.basename(sourceRoot) !== 'src') {
87
- throw new Error('semantic validator runtime root is not recognized');
88
- }
89
- const validator = path.join(sourceRuntime, 'scripts', 'validate-spec-output.cjs');
90
- PATH_SAFETY.verifyCommonJsClosure(sourceRoot, validator);
91
- return PATH_SAFETY.canonicalRegularFile(sourceRoot, validator, 'semantic validator');
92
- }
93
-
94
- function currentDigest(root, specFile) {
95
- const validator = validatorFor(root);
96
- const result = spawnSync(process.execPath, [validator, path.dirname(specFile), '--semantic-digest'], { cwd: root, encoding: 'utf8' });
97
- const digest = result.stdout.trim();
98
- if (result.status !== 0 || !SHA256_RE.test(digest)) throw new Error(`semantic digest could not be recomputed (${result.stderr.trim() || `exit ${result.status}`})`);
99
- return digest;
100
- }
101
-
102
- function recordFromSubagentStop(payload) {
103
- // Honest-agent guardrail: the host hook observes this SubagentStop payload.
104
- // The worker-authored claim is not authenticated input or a security boundary.
105
- if (!plain(payload) || payload.hook_event_name !== 'SubagentStop') return { recorded: false, reason: 'event' };
106
- for (const field of ['session_id', 'agent_id', 'agent_type', 'last_assistant_message']) {
107
- if (typeof payload[field] !== 'string' || !payload[field].trim()) return { recorded: false, reason: `missing-${field}` };
108
- }
109
- if (!REVIEWER_ROLE_ALLOWLIST.has(payload.agent_type)) return { recorded: false, reason: 'reviewer-role' };
110
- const root = projectRoot(payload);
111
- const claim = parseClaim(payload.last_assistant_message);
112
- if (claim === null) return { recorded: false, reason: 'no-claim' };
113
- const requestedSpec = path.resolve(root, claim.spec_file);
114
- const identity = identityFor(root, requestedSpec, claim.feature_name);
115
- const digest = currentDigest(root, identity.spec_file);
116
- if (digest !== claim.semantic_digest) throw new Error('claimed semantic digest does not match current artifacts');
117
- const key = STATE.ensureKey();
118
- const record = {
119
- schema_version: SCHEMA_VERSION, kind: 'semantic-review-observation', identity,
120
- semantic_digest: digest, event: 'SubagentStop', verdict: 'PASS',
121
- host_session_id: payload.session_id, reviewer_agent_id: payload.agent_id,
122
- reviewer_agent_type: payload.agent_type, issued_at: new Date().toISOString(),
123
- };
124
- record.mac = STATE.recordMac(record, key);
125
- STATE.atomicWrite(recordPath(root, identity), record);
126
- return { recorded: true, record };
127
- }
128
-
129
- function verifyAttestation(root, specFile, featureName, semanticDigest) {
130
- let identity;
131
- try { identity = identityFor(root, specFile, featureName); } catch (error) { return { ok: false, reason: error.message }; }
132
- const file = recordPath(root, identity);
133
- let record;
134
- try { record = JSON.parse(fs.readFileSync(file, 'utf8')); } catch (error) { return { ok: false, reason: error.code === 'ENOENT' ? 'matching host-hook observation is missing' : 'host-hook observation is unreadable' }; }
135
- const key = STATE.readKey();
136
- if (key.missing) return { ok: false, reason: 'completion authority key is missing' };
137
- if (!exactKeys(record, RECORD_FIELDS) || !STATE.macMatches(record, key.key)) return { ok: false, reason: 'host-hook observation is malformed or its MAC is invalid' };
138
- if (record.schema_version !== SCHEMA_VERSION || record.kind !== 'semantic-review-observation' || record.event !== 'SubagentStop' || record.verdict !== 'PASS') return { ok: false, reason: 'host-hook observation event or verdict is invalid' };
139
- if (STATE.stableStringify(record.identity) !== STATE.stableStringify(identity)) return { ok: false, reason: 'host-hook observation project, feature, or spec identity is invalid' };
140
- if (record.semantic_digest !== semanticDigest) return { ok: false, reason: 'host-hook observation semantic digest is stale' };
141
- return { ok: true, record };
142
- }
143
-
144
- if (require.main === module) {
145
- try {
146
- const raw = fs.readFileSync(0, 'utf8').trim();
147
- const { normalizeHookPayload } = require('./lib/hook-payload.cjs');
148
- if (raw) recordFromSubagentStop(normalizeHookPayload(JSON.parse(raw)));
149
- } catch (error) {
150
- process.stderr.write(`CafeKit semantic review SubagentStop claim rejected: ${error.message}\n`);
151
- }
152
- }
153
-
154
- module.exports = { MARKER, REVIEWER_ROLE_ALLOWLIST, recordFromSubagentStop, verifyAttestation, identityFor, recordPath };
@@ -1,114 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Copyright (c) 2026 Haposoft. MIT License.
4
- *
5
- * PreToolUse Hook — task-scaffold-guard.cjs
6
- * Implements: https://docs.anthropic.com/en/docs/claude-code/hooks
7
- *
8
- * Forces spec task files to be GENERATED via spec-scaffold.cjs instead of
9
- * hand-written. Field tests showed the model dodges the (opt-in) scaffold step
10
- * and raw-`Write`s every task file — the single biggest output-token cost of a
11
- * spec run. This hook blocks `Write` to `specs/<feature>/tasks/task-*.md`, so
12
- * the only path to a task file is: scaffold (creates the stub) -> Edit (fills it).
13
- *
14
- * Scope is deliberately narrow — it only ever blocks the `Write` tool on a task
15
- * file path. `Edit`/`MultiEdit` (filling a stub), and `Write` to any other file
16
- * (requirements.md, design.md, source code) are never touched. The scaffold
17
- * script itself writes via Node fs through a Bash call, not the `Write` tool, so
18
- * it is never blocked.
19
- *
20
- * Safety valves:
21
- * 1. fail-open when spec-scaffold.cjs is absent — you cannot force a tool that
22
- * is not installed, so a hook shipped without its script must not deadlock.
23
- * 2. actionable block message — prints the exact scaffold command to run.
24
- * 3. escape hatch — "spec": { "scaffold_guard": false } in <runtime>/runtime.json.
25
- *
26
- * Exit: 0 = allow, 2 = block.
27
- */
28
-
29
- try {
30
- const fs = require('fs');
31
- const path = require('path');
32
- const { runtimeDirName, runtimeDir, runtimePath } = require('./lib/runtime-dir.cjs');
33
-
34
- /**
35
- * Deny the call with a reason every host can read.
36
- *
37
- * The reason is the JSON `permissionDecisionReason`, which Claude Code reads natively and
38
- * grok honours "regardless of exit code"; the same text also goes to stderr, because on
39
- * an exit-2 denial that is the channel Claude Code feeds back, and grok reads only the
40
- * first stderr line. Exit 2 stays so a host that reads neither still blocks.
41
- */
42
- function denyWithReason(reason) {
43
- process.stdout.write(JSON.stringify({
44
- hookSpecificOutput: {
45
- hookEventName: 'PreToolUse',
46
- permissionDecision: 'deny',
47
- permissionDecisionReason: reason
48
- }
49
- }) + '\n');
50
- process.stderr.write(reason + '\n');
51
- process.exit(2);
52
- }
53
-
54
- const stdin = fs.readFileSync(0, 'utf8').trim();
55
- if (!stdin) process.exit(0);
56
-
57
- const { normalizeHookPayload } = require('./lib/hook-payload.cjs');
58
- const data = normalizeHookPayload(JSON.parse(stdin));
59
- const toolName = data.tool_name || '';
60
- const toolInput = data.tool_input || {};
61
- const cwd = data.cwd || process.cwd();
62
-
63
- // Only `Write` can create a file from scratch. Edit/MultiEdit require an
64
- // existing file, so filling a scaffolded stub is always allowed.
65
- if (toolName !== 'Write') process.exit(0);
66
-
67
- const filePath = toolInput.file_path || toolInput.path || '';
68
- if (!filePath) process.exit(0);
69
-
70
- // Match a spec task file: .../specs/<feature>/tasks/task-<...>.md
71
- const norm = filePath.replace(/\\/g, '/');
72
- const isTaskFile = /(^|\/)specs\/[^/]+\/tasks\/task-[^/]+\.md$/.test(norm);
73
- if (!isTaskFile) process.exit(0);
74
-
75
- // Valve 3: explicit escape hatch via <runtime>/runtime.json (fail-closed: a
76
- // missing/broken runtime.json keeps the guard ON).
77
- let runtime = {};
78
- try {
79
- const rp = runtimePath(cwd, 'runtime.json');
80
- if (fs.existsSync(rp)) runtime = JSON.parse(fs.readFileSync(rp, 'utf8'));
81
- } catch { /* malformed runtime.json -> keep guard on */ }
82
- if (runtime.spec && runtime.spec.scaffold_guard === false) process.exit(0);
83
-
84
- // Valve 1: fail-open if the scaffold script is not installed next to this hook
85
- // (hook lives in <runtime>/hooks/, script in <runtime>/scripts/). Forcing a tool
86
- // that does not exist would deadlock task creation.
87
- const scaffold = path.join(__dirname, '..', 'scripts', 'spec-scaffold.cjs');
88
- if (!fs.existsSync(scaffold)) process.exit(0);
89
-
90
- // Valve 2: block with an actionable message carrying the exact command.
91
- // NOTE: deliberately does NOT mention the runtime.json escape hatch — an
92
- // Smoke testing proved the model reads the advertised override and
93
- // simply disables the guard itself. The hatch stays functional for humans.
94
- const m = norm.match(/(^|\/)specs\/([^/]+)\/tasks\//);
95
- const feature = m ? m[2] : '<feature>';
96
- denyWithReason(
97
- `TASK SCAFFOLD REQUIRED: task files must be generated, not hand-written.\n` +
98
- `Blocked Write: ${filePath}\n\n` +
99
- `Generate the stub(s), then Edit-fill the {{...}} placeholders:\n` +
100
- ` node ${runtimeDirName()}/scripts/spec-scaffold.cjs ${feature} --tasks "R0-01-slug,R1-01-slug,..." --tasks-only\n` +
101
- `Then use Edit (not Write) on each tasks/task-*.md stub.`
102
- );
103
-
104
- } catch (e) {
105
- // Never let a hook crash block the user — log and fail-open.
106
- try {
107
- const fs = require('fs'), p = require('path');
108
- const d = require('./lib/hook-state-dir.cjs').hookStateDir();
109
- if (!fs.existsSync(d)) fs.mkdirSync(d, { recursive: true });
110
- fs.appendFileSync(p.join(d, 'hook-log.jsonl'),
111
- JSON.stringify({ ts: new Date().toISOString(), hook: 'task-scaffold-guard', status: 'crash', error: e.message }) + '\n');
112
- } catch (_) {}
113
- process.exit(0);
114
- }
@@ -1,197 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Copyright (c) 2026 Haposoft. MIT License.
4
- *
5
- * UserPromptSubmit & PostToolUse Hook — usage.cjs
6
- * Implements: https://docs.anthropic.com/en/docs/claude-code/hooks
7
- *
8
- * Fetches Claude Code usage limits from Anthropic OAuth API and writes to cache.
9
- * Cache is read by status.cjs for statusline display.
10
- *
11
- * ⚠ EXPERIMENTAL / UNDOCUMENTED API: api.anthropic.com/api/oauth/usage with the
12
- * "oauth-2025-04-20" beta header is not a public contract and may break or
13
- * change shape without notice. Every failure path degrades to
14
- * status:"unavailable" (statusline hides the segment) — never block on it.
15
- * Disable entirely with "usage": { "enabled": false } if it misbehaves.
16
- *
17
- * Features:
18
- * - Cross-platform credential retrieval (macOS Keychain, file-based)
19
- * - Throttled API calls: 1 min (prompt) / 5 min (tool use)
20
- * - Disable: set "usage": { "enabled": false } in <runtime>/runtime.json
21
- */
22
-
23
- // Crash wrapper
24
- try {
25
- const fs = require("fs");
26
- const path = require("path");
27
- const os = require("os");
28
- const { execSync } = require("child_process");
29
-
30
- const { runtimeDirName, runtimeDir, runtimePath } = require('./lib/runtime-dir.cjs');
31
- function readRuntime(cwd) {
32
- try {
33
- const rp = runtimePath(cwd, 'runtime.json');
34
- if (fs.existsSync(rp)) {
35
- return JSON.parse(fs.readFileSync(rp, 'utf-8'));
36
- }
37
- } catch { /* fail-open */ }
38
- return {};
39
- }
40
-
41
- // Cache configuration
42
- const USAGE_CACHE_FILE = path.join(os.tmpdir(), "ck-usage-limits-cache.json");
43
- const CACHE_TTL_MS = 60000; // 60 seconds
44
- const FETCH_INTERVAL_MS = 300000; // 5 minutes for PostToolUse
45
- const FETCH_INTERVAL_PROMPT_MS = 60000; // 1 minute for UserPromptSubmit
46
-
47
- /**
48
- * Get Claude OAuth credentials (cross-platform)
49
- */
50
- function getClaudeCredentials() {
51
- // macOS: Try Keychain first
52
- if (os.platform() === "darwin") {
53
- try {
54
- const result = execSync('security find-generic-password -s "Claude Code-credentials" -w', {
55
- timeout: 5000,
56
- encoding: "utf-8",
57
- stdio: ["pipe", "pipe", "ignore"],
58
- }).trim();
59
- const parsed = JSON.parse(result);
60
- if (parsed.claudeAiOauth?.accessToken) {
61
- return parsed.claudeAiOauth.accessToken;
62
- }
63
- } catch {
64
- // Fallback to file-based
65
- }
66
- }
67
-
68
- // File-based credentials (Linux/Windows, or macOS fallback)
69
- const credPath = path.join(os.homedir(), ".claude", ".credentials.json");
70
- try {
71
- const content = fs.readFileSync(credPath, "utf-8");
72
- const parsed = JSON.parse(content);
73
- return parsed.claudeAiOauth?.accessToken || null;
74
- } catch {
75
- return null;
76
- }
77
- }
78
-
79
- /**
80
- * Check if we should fetch (throttled)
81
- */
82
- function shouldFetch(isUserPrompt = false) {
83
- const interval = isUserPrompt ? FETCH_INTERVAL_PROMPT_MS : FETCH_INTERVAL_MS;
84
-
85
- try {
86
- if (fs.existsSync(USAGE_CACHE_FILE)) {
87
- const cache = JSON.parse(fs.readFileSync(USAGE_CACHE_FILE, "utf-8"));
88
- if (Date.now() - cache.timestamp < interval) {
89
- return false;
90
- }
91
- }
92
- } catch {}
93
- return true;
94
- }
95
-
96
- /**
97
- * Write cache with status (available or unavailable)
98
- */
99
- function writeCache(status, data = null) {
100
- fs.writeFileSync(
101
- USAGE_CACHE_FILE,
102
- JSON.stringify({
103
- timestamp: Date.now(),
104
- status,
105
- data,
106
- })
107
- );
108
- }
109
-
110
- /**
111
- * Fetch usage limits from Anthropic OAuth API and write to cache
112
- * Always writes status to cache (available or unavailable) for statusline fallback
113
- */
114
- async function fetchAndCacheUsageLimits() {
115
- const token = getClaudeCredentials();
116
- if (!token) {
117
- writeCache("unavailable");
118
- return false;
119
- }
120
-
121
- try {
122
- const response = await fetch("https://api.anthropic.com/api/oauth/usage", {
123
- method: "GET",
124
- headers: {
125
- Accept: "application/json",
126
- "Content-Type": "application/json",
127
- Authorization: `Bearer ${token}`,
128
- "anthropic-beta": "oauth-2025-04-20",
129
- "User-Agent": "cafekit/1.0",
130
- },
131
- });
132
-
133
- if (!response.ok) {
134
- writeCache("unavailable");
135
- return false;
136
- }
137
-
138
- const data = await response.json();
139
- writeCache("available", data);
140
- return true;
141
- } catch {
142
- writeCache("unavailable");
143
- return false;
144
- }
145
- }
146
-
147
- /**
148
- * Main hook execution - just fetch and cache, no injection
149
- */
150
- async function main() {
151
- // Always allow operation to continue
152
- const result = { continue: true };
153
-
154
- try {
155
- // Read hook input
156
- let inputStr = "";
157
- try {
158
- inputStr = fs.readFileSync(0, "utf-8");
159
- } catch {}
160
-
161
- const input = JSON.parse(inputStr || "{}");
162
- const cwd = input.cwd || process.cwd();
163
- const runtime = readRuntime(cwd);
164
- if (runtime.usage?.enabled === false) {
165
- console.log(JSON.stringify(result));
166
- return;
167
- }
168
-
169
- // Detect hook type
170
- const isUserPrompt = typeof input.prompt === "string";
171
-
172
- // Check if we should fetch (throttled)
173
- if (shouldFetch(isUserPrompt)) {
174
- await fetchAndCacheUsageLimits();
175
- }
176
- } catch {}
177
-
178
- // Output result (no injection, just continue)
179
- console.log(JSON.stringify(result));
180
- }
181
-
182
- main().catch(() => {
183
- console.log(JSON.stringify({ continue: true }));
184
- process.exit(0);
185
- });
186
- } catch (e) {
187
- // Minimal crash logging (zero deps — only Node builtins)
188
- try {
189
- const fs = require('fs');
190
- const p = require('path');
191
- const logDir = require('./lib/hook-state-dir.cjs').hookStateDir();
192
- if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
193
- fs.appendFileSync(p.join(logDir, 'hook-log.jsonl'),
194
- JSON.stringify({ ts: new Date().toISOString(), hook: p.basename(__filename, '.cjs'), status: 'crash', error: e.message }) + '\n');
195
- } catch (_) {}
196
- process.exit(0); // fail-open
197
- }
@@ -1,124 +0,0 @@
1
- ---
2
- name: cf:delegate
3
- description: "Delegate implementation tasks from Claude Code to external CLI agents (Codex or Grok). Covers task-brief authoring, non-interactive dispatch, permission setup, background monitoring, resume, and independent verification of the returned work."
4
- user-invocable: true
5
- when_to_use: "Invoke when the user asks to assign/offload a coding task to codex or grok, or to run work on an external agent CLI."
6
- category: orchestration
7
- keywords: [delegate, codex, grok, external-agent, offload, assign]
8
- argument-hint: "<codex|grok> <task-description | path/to/task-brief.md>"
9
- metadata:
10
- author: haposoft
11
- version: "1.0.0"
12
- ---
13
- # Delegate to External Agents (Codex / Grok)
14
-
15
- Dispatch a well-scoped implementation task to an external agentic CLI — **Codex** (OpenAI) or **Grok** (xAI Grok Build) — from a Claude Code session acting as orchestrator. Claude Code stays responsible for: writing the task brief, choosing safe permissions, monitoring, and **independently verifying** the result before reporting completion.
16
-
17
- **Mantra:** Brief precisely, sandbox tightly, verify independently.
18
-
19
- ## When to Use
20
-
21
- - User explicitly asks to hand a task to codex/grok ("giao cho codex", "assign to grok").
22
- - Parallel workstreams: Claude Code keeps orchestrating while an external agent implements a scoped task.
23
- - Cross-checking: a second model implements or reviews independently.
24
-
25
- Do NOT delegate: tasks touching secrets/`.env`, deploys to shared environments, destructive migrations on live data, or tasks whose scope is still unclear (clarify first — a vague brief wastes an entire agent run).
26
-
27
- ## Workflow
28
-
29
- ### 1. Write the task brief (mandatory, file-based)
30
-
31
- External agents run **fresh-context** — they see none of your conversation. Write the brief to a file (convention: `plans/<slug>-task.md` in the target repo), never inline-only. Follow the orchestrator prompt rules (`.claude/rules/orchestrator.md`): the brief must be self-contained.
32
-
33
- Required sections:
34
-
35
- ```markdown
36
- # Task: <title>
37
-
38
- > Self-contained brief for a fresh-context agent. Date: <YYYY-MM-DD>.
39
- > FOUNDATION: <state of the working tree the agent builds on — branch,
40
- > uncommitted changes it must NOT revert, verified test/build status>
41
-
42
- ## Context — project, architecture, why this task exists
43
- ## Scope — numbered work items with exact file paths
44
- ## Constraints — what must NOT be touched (staging data, .env, deploys,
45
- unrelated refactors); repo rules to obey (CLAUDE.md)
46
- ## Completion Criteria — verifiable, numbered
47
- ## Evidence required — exact commands whose output proves completion
48
- ```
49
-
50
- Rules of thumb:
51
- - Name exact files and line-level anchors where known; agents burn tokens rediscovering what you already know.
52
- - State dependency order explicitly if this task builds on another agent's uncommitted work ("do NOT revert/stash existing changes").
53
- - Put user-pending decisions in Constraints as forbidden actions (e.g. "do NOT touch staging rows until user confirms").
54
-
55
- ### 2. Protect the working tree
56
-
57
- Before dispatch, snapshot state you cannot afford to lose:
58
-
59
- ```bash
60
- git stash push -u -m "backup: <reason>" && git stash apply -q # snapshot, keep tree intact
61
- ```
62
-
63
- Prefer isolation when the task is risky or parallel to other edits:
64
- - Grok: `--worktree=<name>` (built-in).
65
- - Codex: run with `-C <worktree-dir>` after creating a git worktree yourself.
66
-
67
- If the agent must build on uncommitted changes, isolation is not possible — use the stash-backup pattern above and say so in the brief.
68
-
69
- ### 3. Dispatch
70
-
71
- Load the matching reference for exact flags, permission model, and pitfalls:
72
- - **Codex:** `./references/codex-delegation.md`
73
- - **Grok:** `./references/grok-delegation.md`
74
-
75
- Common principles:
76
- - Run non-interactive/headless mode in a **background** shell; both CLIs stream progress to stdout.
77
- - Grant the **minimum** permission set that lets the agent write code and run its Evidence commands (npm/node). Never grant full-access/bypass modes for routine tasks.
78
- - Model/effort overrides: pass explicitly rather than relying on user config, so the run is reproducible.
79
- - Capture the output file path; check progress after ~20–30s to confirm the agent actually started (auth errors, bad flags, and permission blocks surface early).
80
-
81
- ### 4. Monitor
82
-
83
- - Poll the background output periodically; look for permission-block messages — both CLIs degrade to "design-only" output instead of failing loudly when a write tool is blocked.
84
- - If blocked on permissions: **resume the same session** with corrected flags (both CLIs support resume; see references) — context is preserved, work continues instead of restarting.
85
- - Apply the 3-strike rule from `.claude/rules/orchestrator.md`: same failure 3+ times → stop, escalate to user with evidence.
86
-
87
- ### 5. Verify independently (non-negotiable)
88
-
89
- External-agent claims are **not** evidence (CLAUDE.md: no completion claim without fresh proof from the current run). After the agent reports done:
90
-
91
- 1. `git status` / `git diff` — confirm the changed file set matches the brief's Scope; flag out-of-scope edits.
92
- 2. Run the brief's Evidence commands yourself (tests, build). The agent's transcript of a test run does not count.
93
- 3. Check Completion Criteria one by one.
94
- 4. Report to user: what was delegated, what came back, verification results, and any deviations.
95
-
96
- ### 6. Sync
97
-
98
- - Keep the brief file updated (checklist ticks / blockers) so it doubles as task state.
99
- - If a spec (`specs/<slug>/`) governs the work, follow the Tollgate Protocol (`.claude/rules/state-sync.md`): update `spec.json` + task files only after verification passes.
100
- - The user decides commit/merge/deploy — delegation ends at verified working-tree changes unless told otherwise.
101
-
102
- ## Choosing Codex vs Grok
103
-
104
- Both are capable general coding agents; prefer the one the user names. If unspecified:
105
-
106
- | Factor | Codex | Grok |
107
- |---|---|---|
108
- | Isolation | manual git worktree + `-C` | built-in `--worktree=<name>` |
109
- | Structured output | `--output-schema` (JSON Schema) | `--json-schema` |
110
- | Self-verification | ask in the brief | `--check` flag appends a verify loop |
111
- | Resume | `codex exec resume <id>` | `grok --resume <id>` |
112
-
113
- ## Rules
114
-
115
- - Never grant `--dangerously-bypass-approvals-and-sandbox` (codex) or `bypassPermissions` (grok) for routine delegation.
116
- - Never let an external agent touch `.env`, secrets, staging/production data, or run deploys.
117
- - Never report the agent's own success claim as completion — verify first.
118
- - Never re-dispatch a blocked task unchanged (BLOCKED ≠ retry; fix the cause: permissions, context, or scope).
119
- - One writer per file set: don't run two agents (or an agent + yourself) editing the same files concurrently.
120
-
121
- ## References
122
-
123
- - `./references/codex-delegation.md` — Codex CLI: exec mode, sandbox/approval model, resume, pitfalls
124
- - `./references/grok-delegation.md` — Grok CLI: headless mode, worktree, permission rules, resume, pitfalls
@@ -1,71 +0,0 @@
1
- # Delegating to Codex CLI
2
-
3
- Verified against codex-cli 0.144.4 (2026-07). Re-check `codex exec --help` if flags fail — the CLI evolves quickly.
4
-
5
- ## Preflight
6
-
7
- ```bash
8
- which codex && codex --version # installed?
9
- codex doctor # auth + runtime health (slow; run only when suspicious)
10
- ```
11
-
12
- User-level defaults live in `~/.codex/config.toml` (`model`, `model_reasoning_effort`, agents, MCP servers). Overrides via `-m` and `-c key=value` beat config — pass them explicitly for reproducible runs.
13
-
14
- ## Non-interactive dispatch: `codex exec`
15
-
16
- Reads the prompt from an argument, or from **stdin** when the argument is `-`. Prefer stdin + brief file:
17
-
18
- ```bash
19
- codex exec \
20
- -m gpt-5.6-terra \
21
- -c model_reasoning_effort='"max"' \
22
- --sandbox workspace-write \
23
- -o /tmp/codex-last-message.txt \
24
- - < plans/<slug>-task.md
25
- ```
26
-
27
- Run it in a background shell and poll the output.
28
-
29
- ### Key flags
30
-
31
- | Flag | Notes |
32
- |---|---|
33
- | `-m <model>` | model ID, e.g. `gpt-5.6-terra` (note spelling: *terra*) |
34
- | `-c model_reasoning_effort='"max"'` | TOML-parsed value — keep the inner quotes |
35
- | `--sandbox <mode>` | `read-only` \| `workspace-write` \| `danger-full-access` |
36
- | `-o <file>` | write the agent's final message to a file — easiest completion check |
37
- | `-C <dir>` | working root (use for delegating into a git worktree) |
38
- | `--add-dir <dir>` | extra writable dirs alongside the workspace |
39
- | `--output-schema <file>` | JSON Schema constraining the final response |
40
- | `--json` | JSONL event stream on stdout (machine parsing) |
41
- | `--ephemeral` | don't persist session files |
42
-
43
- ### Pitfalls
44
-
45
- - **`-a` / `--ask-for-approval` is TUI-only.** `codex exec` rejects it (`error: unexpected argument '-a' found`). Exec mode is implicitly `approval: never` — sandbox mode is your only control there.
46
- - **`workspace-write` limits writes, not reads**, to: workdir, `/tmp`, `$TMPDIR`. Network is available. Commands needing writes outside (global installs) will fail — keep Evidence commands repo-local.
47
- - **Effort value is TOML**: `-c model_reasoning_effort='"max"'` (double-quoted inside single quotes). Bare `max` may parse as a literal string too, but the quoted form is unambiguous.
48
- - Startup banner echoes `model:`, `reasoning effort:`, `sandbox:` — **verify these lines** ~20s after dispatch instead of assuming flags took effect.
49
- - User config may define agents/hooks/MCP servers that activate automatically; `--ignore-user-config` gives a clean run if they interfere.
50
-
51
- ## Monitor & verify liveness
52
-
53
- ```bash
54
- # ~20-30s after dispatch: banner shows model/sandbox; then agent narration follows
55
- head -c 1200 <output-file>
56
- tail -c 1500 <output-file> # progress
57
- cat /tmp/codex-last-message.txt # exists → run finished; content = final report
58
- ```
59
-
60
- ## Resume
61
-
62
- ```bash
63
- codex exec resume --last "<follow-up>" # most recent session
64
- codex exec resume <SESSION_ID> "<follow-up>" # by id (session id printed in banner)
65
- ```
66
-
67
- Use resume (not a fresh dispatch) when: permission-blocked mid-run, follow-up fixes after your verification failed, or continuing a design into implementation.
68
-
69
- ## Review mode
70
-
71
- `codex review` (or `codex exec review`) runs a non-interactive code review of the current repo — useful as an independent second-opinion pass on another agent's diff.