@haposoft/cafekit 0.16.0-rc.8 → 0.16.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.
- package/README.md +116 -27
- package/bin/__tests__/codex-hooks-ownership.test.js +239 -0
- package/bin/__tests__/codex-hooks.test.js +50 -0
- package/bin/__tests__/codex-native.test.js +1882 -70
- package/bin/__tests__/develop-contract.test.js +400 -23
- package/bin/__tests__/installer-safety.test.js +3 -3
- package/bin/__tests__/optional-skill-inventory.test.js +249 -0
- package/bin/__tests__/output-styles.test.js +214 -0
- package/bin/__tests__/package-inventory.test.js +1735 -98
- package/bin/__tests__/runtime-schema.test.js +111 -0
- package/bin/__tests__/secret-output-guardrail.test.js +122 -0
- package/bin/__tests__/skill-routing-source.test.js +172 -0
- package/bin/__tests__/specs-v2-execution-closeout.test.js +1 -1
- package/bin/install.js +10 -0
- package/bin/lib/codex-install.js +170 -5
- package/bin/lib/context.js +14 -0
- package/bin/lib/i18n.js +12 -6
- package/bin/lib/skill-deps.js +5 -5
- package/bin/phases/claude-runtime.js +23 -0
- package/bin/phases/claude-settings.js +8 -0
- package/bin/phases/codex-hooks.js +156 -0
- package/bin/phases/codex-runtime.js +29 -38
- package/bin/phases/copy-payload.js +8 -2
- package/bin/phases/select-skill-bundles.js +96 -0
- package/bin/phases/skill-inventory.js +71 -0
- package/bin/phases/skills-setup.js +9 -2
- package/bin/phases/write-metadata.js +8 -5
- package/package.json +8 -2
- package/src/claude/CLAUDE.md +3 -3
- package/src/claude/agents/brainstormer.md +52 -69
- package/src/claude/agents/code-auditor.md +32 -14
- package/src/claude/agents/debugger.md +35 -20
- package/src/claude/agents/deployer.md +12 -5
- package/src/claude/agents/docs-keeper.md +11 -1
- package/src/claude/agents/implementer.md +25 -13
- package/src/claude/agents/inspector.md +6 -3
- package/src/claude/agents/project-manager.md +1 -1
- package/src/claude/agents/researcher.md +26 -55
- package/src/claude/agents/spec-maker.md +12 -7
- package/src/claude/agents/strategist.md +52 -0
- package/src/claude/agents/test-runner.md +117 -132
- package/src/claude/agents/ui-ux-designer.md +1 -1
- package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -0
- package/src/claude/hooks/__tests__/precompact.test.js +158 -0
- package/src/claude/hooks/__tests__/spec-gate.test.js +10 -3
- package/src/claude/hooks/__tests__/state.test.js +5 -0
- package/src/claude/hooks/__tests__/statusline.test.js +196 -0
- package/src/claude/hooks/agent.cjs +1 -1
- package/src/claude/hooks/docs-sync.cjs +12 -10
- package/src/claude/hooks/inspect-block.cjs +1 -1
- package/src/claude/hooks/lib/config.cjs +3 -0
- package/src/claude/hooks/lib/hook-state-dir.cjs +39 -0
- package/src/claude/hooks/lib/secret-keywords.cjs +42 -0
- package/src/claude/hooks/precompact.cjs +105 -0
- package/src/claude/hooks/rules.cjs +1 -1
- package/src/claude/hooks/secret-output-guardrail.cjs +43 -0
- package/src/claude/hooks/session.cjs +27 -1
- package/src/claude/hooks/spec-gate.cjs +3 -3
- package/src/claude/hooks/spec-state.cjs +2 -2
- package/src/claude/hooks/state.cjs +1 -1
- package/src/claude/hooks/task-scaffold-guard.cjs +1 -1
- package/src/claude/hooks/usage.cjs +1 -1
- package/src/claude/migration-manifest.json +29 -15
- package/src/claude/output-styles/coding-level-0-eli5.md +103 -0
- package/src/claude/output-styles/coding-level-1-junior.md +124 -0
- package/src/claude/output-styles/coding-level-2-mid.md +146 -0
- package/src/claude/output-styles/coding-level-3-senior.md +148 -0
- package/src/claude/output-styles/coding-level-4-lead.md +159 -0
- package/src/claude/output-styles/coding-level-5-god.md +91 -0
- package/src/claude/references/debugger/core-philosophy.md +5 -3
- package/src/claude/references/debugger/frontend-verification.md +2 -2
- package/src/claude/references/debugger/log-ci-analysis.md +13 -1
- package/src/claude/references/debugger/parallel-agent-hydration.md +19 -34
- package/src/claude/references/debugger/performance-diagnostics.md +2 -2
- package/src/claude/references/debugger/root-cause-tracing.md +37 -6
- package/src/claude/references/debugger/side-effect-gate.md +3 -1
- package/src/claude/references/debugger/verification-protocol.md +13 -12
- package/src/claude/rules/orchestrator.md +7 -1
- package/src/claude/rules/process-management.md +39 -0
- package/src/claude/rules/review-audit-self-decision.md +48 -0
- package/src/claude/rules/skill-domain-routing.md +26 -24
- package/src/claude/rules/skill-workflow-routing.md +22 -14
- package/src/claude/rules/workflow.md +3 -3
- package/src/claude/runtime.json +1 -0
- package/src/claude/runtime.schema.json +205 -0
- package/src/claude/scripts/generate-skill-catalog.cjs +156 -122
- package/src/claude/scripts/provenance.cjs +18 -5
- package/src/claude/scripts/workflow-policy.cjs +1 -1
- package/src/claude/settings/settings.json +16 -0
- package/src/claude/skills/agent-browser/SKILL.md +1 -1
- package/src/claude/skills/ai-multimodal/SKILL.md +1 -1
- package/src/claude/skills/brainstorm/SKILL.md +193 -172
- package/src/claude/skills/brainstorm/references/question-framework.md +105 -167
- package/src/claude/skills/chrome-devtools/SKILL.md +1 -1
- package/src/claude/skills/code-review/SKILL.md +43 -25
- package/src/claude/skills/code-review/references/spec-compliance-review.md +3 -4
- package/src/claude/skills/code-review/references/verification-gate.md +1 -1
- package/src/claude/skills/debug/SKILL.md +48 -19
- package/src/claude/skills/delegate/SKILL.md +1 -1
- package/src/claude/skills/develop/SKILL.md +92 -65
- package/src/claude/skills/develop/references/parallel-waves.md +36 -63
- package/src/claude/skills/develop/references/quality-gate.md +31 -4
- package/src/claude/skills/develop/references/subagent-patterns.md +21 -7
- package/src/claude/skills/docs/SKILL.md +42 -23
- package/src/claude/skills/docs/references/init-workflow.md +4 -4
- package/src/claude/skills/docs/references/reconstruct-workflow.md +6 -6
- package/src/claude/skills/docs/references/standard-docs-workflow.md +6 -2
- package/src/claude/skills/docs/references/summarize-workflow.md +2 -2
- package/src/claude/skills/docs/references/update-workflow.md +9 -7
- package/src/claude/skills/docs/templates/requirements-as-is.md +1 -1
- package/src/claude/skills/docx/SKILL.md +1 -1
- package/src/claude/skills/git/SKILL.md +1 -1
- package/src/claude/skills/git/references/commit-protocols.md +1 -1
- package/src/claude/skills/git/references/finish-branch.md +1 -1
- package/src/claude/skills/hotfix/SKILL.md +159 -170
- package/src/claude/skills/hotfix/references/diagnosis-protocol.md +14 -9
- package/src/claude/skills/hotfix/references/parallel-patterns.md +27 -13
- package/src/claude/skills/hotfix/references/prevention-gate.md +5 -0
- package/src/claude/skills/hotfix/references/review-cycle.md +21 -15
- package/src/claude/skills/hotfix/references/workflow-specialized.md +53 -21
- package/src/claude/skills/inspect/SKILL.md +55 -46
- package/src/claude/skills/inspect/references/internal-inspection.md +21 -5
- package/src/claude/skills/loop/SKILL.md +114 -0
- package/src/claude/skills/loop/references/bounded-loop-protocol.md +104 -0
- package/src/claude/skills/loop/references/metric-and-guard-contract.md +76 -0
- package/src/claude/skills/pdf/SKILL.md +1 -1
- package/src/claude/skills/pptx/SKILL.md +1 -1
- package/src/claude/skills/question/SKILL.md +16 -13
- package/src/claude/skills/research/SKILL.md +70 -43
- package/src/claude/skills/route/SKILL.md +94 -0
- package/src/claude/skills/route/references/agent-timing.md +46 -0
- package/src/claude/skills/route/references/chaining-patterns.md +53 -0
- package/src/claude/skills/route/references/task-taxonomy.md +48 -0
- package/src/claude/skills/specs/SKILL.md +41 -60
- package/src/claude/skills/specs/references/review.md +11 -8
- package/src/claude/skills/specs/references/templates.md +34 -18
- package/src/claude/skills/specs/templates/requirements-init.md +1 -1
- package/src/claude/skills/specs/templates/research.md +2 -2
- package/src/claude/skills/sync/SKILL.md +6 -6
- package/src/claude/skills/sync/references/sync-protocols.md +1 -1
- package/src/claude/skills/test/SKILL.md +120 -120
- package/src/claude/skills/test/references/execution-strategy.md +208 -361
- package/src/claude/skills/test/references/failure-triage.md +55 -121
- package/src/claude/skills/test/references/test-memory.md +20 -34
- package/src/claude/skills/ui-ux-pro-max/SKILL.md +1 -1
- package/src/claude/skills/web-testing/SKILL.md +1 -1
- package/src/claude/skills/xlsx/SKILL.md +1 -1
- package/src/claude/status.cjs +135 -33
- package/src/codex/AGENTS.md +14 -4
- package/src/codex/hooks/docs-sync.cjs +8 -3
- package/src/codex/hooks/lib/hook-context.cjs +18 -4
- package/src/codex/hooks/rules.cjs +29 -1
- package/src/codex/hooks/secret-output-guardrail.cjs +30 -0
- package/src/codex/hooks/spec-gate.cjs +3 -8
- package/src/codex/hooks/spec-state.cjs +2 -1
- package/src/codex/hooks.json +5 -0
- package/src/codex/runtime.json +2 -0
- package/src/common/.env.example +1 -1
- package/src/claude/skills/backend-development/SKILL.md +0 -101
- package/src/claude/skills/backend-development/references/backend-api-design.md +0 -495
- package/src/claude/skills/backend-development/references/backend-architecture.md +0 -454
- package/src/claude/skills/backend-development/references/backend-authentication.md +0 -338
- package/src/claude/skills/backend-development/references/backend-code-quality.md +0 -659
- package/src/claude/skills/backend-development/references/backend-debugging.md +0 -904
- package/src/claude/skills/backend-development/references/backend-devops.md +0 -491
- package/src/claude/skills/backend-development/references/backend-mindset.md +0 -387
- package/src/claude/skills/backend-development/references/backend-performance.md +0 -397
- package/src/claude/skills/backend-development/references/backend-security.md +0 -342
- package/src/claude/skills/backend-development/references/backend-technologies.md +0 -256
- package/src/claude/skills/backend-development/references/backend-testing.md +0 -429
- package/src/claude/skills/devops/.env.example +0 -76
- package/src/claude/skills/devops/SKILL.md +0 -102
- package/src/claude/skills/devops/references/browser-rendering.md +0 -305
- package/src/claude/skills/devops/references/cloudflare-d1-kv.md +0 -123
- package/src/claude/skills/devops/references/cloudflare-platform.md +0 -271
- package/src/claude/skills/devops/references/cloudflare-r2-storage.md +0 -280
- package/src/claude/skills/devops/references/cloudflare-workers-advanced.md +0 -312
- package/src/claude/skills/devops/references/cloudflare-workers-apis.md +0 -309
- package/src/claude/skills/devops/references/cloudflare-workers-basics.md +0 -418
- package/src/claude/skills/devops/references/docker-basics.md +0 -297
- package/src/claude/skills/devops/references/docker-compose.md +0 -292
- package/src/claude/skills/devops/references/gcloud-platform.md +0 -297
- package/src/claude/skills/devops/references/gcloud-services.md +0 -304
- package/src/claude/skills/devops/references/kubernetes-basics.md +0 -95
- package/src/claude/skills/devops/references/kubernetes-helm-advanced.md +0 -70
- package/src/claude/skills/devops/references/kubernetes-helm.md +0 -81
- package/src/claude/skills/devops/references/kubernetes-kubectl.md +0 -74
- package/src/claude/skills/devops/references/kubernetes-security-advanced.md +0 -94
- package/src/claude/skills/devops/references/kubernetes-security.md +0 -89
- package/src/claude/skills/devops/references/kubernetes-troubleshooting-advanced.md +0 -74
- package/src/claude/skills/devops/references/kubernetes-troubleshooting.md +0 -49
- package/src/claude/skills/devops/references/kubernetes-workflows-advanced.md +0 -75
- package/src/claude/skills/devops/references/kubernetes-workflows.md +0 -76
- package/src/claude/skills/devops/scripts/cloudflare_deploy.py +0 -269
- package/src/claude/skills/devops/scripts/docker_optimize.py +0 -332
- package/src/claude/skills/devops/scripts/requirements.txt +0 -20
- package/src/claude/skills/devops/scripts/tests/requirements.txt +0 -3
- package/src/claude/skills/devops/scripts/tests/test_cloudflare_deploy.py +0 -285
- package/src/claude/skills/devops/scripts/tests/test_docker_optimize.py +0 -436
- package/src/claude/skills/frontend-design/SKILL.md +0 -135
- package/src/claude/skills/frontend-design/references/ai-multimodal-overview.md +0 -165
- package/src/claude/skills/frontend-design/references/analysis-best-practices.md +0 -80
- package/src/claude/skills/frontend-design/references/analysis-prompts.md +0 -141
- package/src/claude/skills/frontend-design/references/analysis-techniques.md +0 -118
- package/src/claude/skills/frontend-design/references/animejs.md +0 -396
- package/src/claude/skills/frontend-design/references/anti-slop-rules.md +0 -103
- package/src/claude/skills/frontend-design/references/asset-generation.md +0 -337
- package/src/claude/skills/frontend-design/references/bento-motion-engine.md +0 -142
- package/src/claude/skills/frontend-design/references/design-extraction-overview.md +0 -71
- package/src/claude/skills/frontend-design/references/extraction-best-practices.md +0 -141
- package/src/claude/skills/frontend-design/references/extraction-output-templates.md +0 -162
- package/src/claude/skills/frontend-design/references/extraction-prompts.md +0 -127
- package/src/claude/skills/frontend-design/references/magicui-components.md +0 -129
- package/src/claude/skills/frontend-design/references/performance-guardrails.md +0 -169
- package/src/claude/skills/frontend-design/references/premium-design-patterns.md +0 -93
- package/src/claude/skills/frontend-design/references/redesign-audit-checklist.md +0 -114
- package/src/claude/skills/frontend-design/references/technical-accessibility.md +0 -119
- package/src/claude/skills/frontend-design/references/technical-best-practices.md +0 -97
- package/src/claude/skills/frontend-design/references/technical-optimization.md +0 -44
- package/src/claude/skills/frontend-design/references/technical-overview.md +0 -90
- package/src/claude/skills/frontend-design/references/technical-workflows.md +0 -150
- package/src/claude/skills/frontend-design/references/visual-analysis-overview.md +0 -95
- package/src/claude/skills/frontend-design/references/workflow-3d.md +0 -102
- package/src/claude/skills/frontend-design/references/workflow-describe.md +0 -87
- package/src/claude/skills/frontend-design/references/workflow-immersive.md +0 -87
- package/src/claude/skills/frontend-design/references/workflow-quick.md +0 -57
- package/src/claude/skills/frontend-design/references/workflow-screenshot.md +0 -63
- package/src/claude/skills/frontend-design/references/workflow-video.md +0 -74
- package/src/claude/skills/frontend-development/SKILL.md +0 -406
- package/src/claude/skills/frontend-development/resources/common-patterns.md +0 -331
- package/src/claude/skills/frontend-development/resources/complete-examples.md +0 -872
- package/src/claude/skills/frontend-development/resources/component-patterns.md +0 -502
- package/src/claude/skills/frontend-development/resources/data-fetching.md +0 -767
- package/src/claude/skills/frontend-development/resources/file-organization.md +0 -502
- package/src/claude/skills/frontend-development/resources/loading-and-error-states.md +0 -501
- package/src/claude/skills/frontend-development/resources/performance.md +0 -406
- package/src/claude/skills/frontend-development/resources/routing-guide.md +0 -364
- package/src/claude/skills/frontend-development/resources/styling-guide.md +0 -428
- package/src/claude/skills/frontend-development/resources/typescript-standards.md +0 -418
- package/src/claude/skills/mobile-development/SKILL.md +0 -218
- package/src/claude/skills/mobile-development/references/mobile-android.md +0 -604
- package/src/claude/skills/mobile-development/references/mobile-best-practices.md +0 -544
- package/src/claude/skills/mobile-development/references/mobile-debugging.md +0 -1089
- package/src/claude/skills/mobile-development/references/mobile-frameworks.md +0 -465
- package/src/claude/skills/mobile-development/references/mobile-ios.md +0 -496
- package/src/claude/skills/mobile-development/references/mobile-mindset.md +0 -542
- package/src/claude/skills/react-best-practices/AGENTS.md +0 -2249
- package/src/claude/skills/react-best-practices/README.md +0 -123
- package/src/claude/skills/react-best-practices/SKILL.md +0 -128
- package/src/claude/skills/react-best-practices/metadata.json +0 -15
- package/src/claude/skills/react-best-practices/rules/_sections.md +0 -46
- package/src/claude/skills/react-best-practices/rules/_template.md +0 -28
- package/src/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +0 -55
- package/src/claude/skills/react-best-practices/rules/advanced-use-latest.md +0 -49
- package/src/claude/skills/react-best-practices/rules/async-api-routes.md +0 -38
- package/src/claude/skills/react-best-practices/rules/async-defer-await.md +0 -80
- package/src/claude/skills/react-best-practices/rules/async-dependencies.md +0 -36
- package/src/claude/skills/react-best-practices/rules/async-parallel.md +0 -28
- package/src/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +0 -99
- package/src/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +0 -59
- package/src/claude/skills/react-best-practices/rules/bundle-conditional.md +0 -31
- package/src/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +0 -49
- package/src/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +0 -35
- package/src/claude/skills/react-best-practices/rules/bundle-preload.md +0 -50
- package/src/claude/skills/react-best-practices/rules/client-event-listeners.md +0 -74
- package/src/claude/skills/react-best-practices/rules/client-swr-dedup.md +0 -56
- package/src/claude/skills/react-best-practices/rules/js-batch-dom-css.md +0 -82
- package/src/claude/skills/react-best-practices/rules/js-cache-function-results.md +0 -80
- package/src/claude/skills/react-best-practices/rules/js-cache-property-access.md +0 -28
- package/src/claude/skills/react-best-practices/rules/js-cache-storage.md +0 -70
- package/src/claude/skills/react-best-practices/rules/js-combine-iterations.md +0 -32
- package/src/claude/skills/react-best-practices/rules/js-early-exit.md +0 -50
- package/src/claude/skills/react-best-practices/rules/js-hoist-regexp.md +0 -45
- package/src/claude/skills/react-best-practices/rules/js-index-maps.md +0 -37
- package/src/claude/skills/react-best-practices/rules/js-length-check-first.md +0 -49
- package/src/claude/skills/react-best-practices/rules/js-min-max-loop.md +0 -82
- package/src/claude/skills/react-best-practices/rules/js-set-map-lookups.md +0 -24
- package/src/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +0 -57
- package/src/claude/skills/react-best-practices/rules/rendering-activity.md +0 -26
- package/src/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
- package/src/claude/skills/react-best-practices/rules/rendering-conditional-render.md +0 -40
- package/src/claude/skills/react-best-practices/rules/rendering-content-visibility.md +0 -38
- package/src/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +0 -46
- package/src/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
- package/src/claude/skills/react-best-practices/rules/rendering-svg-precision.md +0 -28
- package/src/claude/skills/react-best-practices/rules/rerender-defer-reads.md +0 -39
- package/src/claude/skills/react-best-practices/rules/rerender-dependencies.md +0 -45
- package/src/claude/skills/react-best-practices/rules/rerender-derived-state.md +0 -29
- package/src/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +0 -74
- package/src/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +0 -58
- package/src/claude/skills/react-best-practices/rules/rerender-memo.md +0 -44
- package/src/claude/skills/react-best-practices/rules/rerender-transitions.md +0 -40
- package/src/claude/skills/react-best-practices/rules/server-after-nonblocking.md +0 -73
- package/src/claude/skills/react-best-practices/rules/server-cache-lru.md +0 -41
- package/src/claude/skills/react-best-practices/rules/server-cache-react.md +0 -26
- package/src/claude/skills/react-best-practices/rules/server-parallel-fetching.md +0 -79
- package/src/claude/skills/react-best-practices/rules/server-serialization.md +0 -38
package/README.md
CHANGED
|
@@ -138,21 +138,97 @@ cat .claude/cafekit.json
|
|
|
138
138
|
cat .codex/cafekit.json
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
+
### Statusline configuration
|
|
142
|
+
|
|
143
|
+
The Claude statusline reads three keys from `.claude/runtime.json`:
|
|
144
|
+
|
|
145
|
+
- `"statusline"`: render mode — `full` (default), `compact`, `minimal`, or `none`.
|
|
146
|
+
- `statuslineColors`: set `false` to disable ANSI colors (`NO_COLOR` also wins).
|
|
147
|
+
- `statuslineLayout`: optional custom layout. Global shape
|
|
148
|
+
`{ "lines": [["model", "context"], ["directory", "git"]] }` — each inner
|
|
149
|
+
array is one output line; modes only slice the line count (minimal = first
|
|
150
|
+
line, compact = first two, full = all). Valid section ids: `model`,
|
|
151
|
+
`context`, `quota`, `directory`, `git`, `plan`, `cost`, `changes`. Unknown
|
|
152
|
+
ids are ignored; an empty or invalid layout falls back to the default
|
|
153
|
+
renderers, and leaving the key out keeps the default output unchanged. The
|
|
154
|
+
`cost` section renders only when enabled here, billing mode is API, and cost
|
|
155
|
+
data exists. Quota shows five-hour and weekly windows only while the usage
|
|
156
|
+
cache is fresh.
|
|
157
|
+
|
|
141
158
|
## Core Skills
|
|
142
159
|
|
|
143
160
|
CafeKit ships many skills, but the main release surface is:
|
|
144
161
|
|
|
145
|
-
- `/
|
|
146
|
-
- `/
|
|
147
|
-
- `/
|
|
148
|
-
- `/
|
|
149
|
-
- `/
|
|
150
|
-
- `/
|
|
151
|
-
- `/
|
|
152
|
-
- `/
|
|
153
|
-
- `/
|
|
162
|
+
- `/cf:ask <question> [--repo|--web|--both|--brief|--deep]`: answer questions using repo evidence first, then external/current sources when local evidence is insufficient
|
|
163
|
+
- `/cf:scout <search-target>`: discover relevant files locally first, delegating only for permitted broad independent scopes
|
|
164
|
+
- `/cf:brainstorm <idea-or-problem>`: scout the repo, clarify exact requirements, compare approaches, and hand off to specs
|
|
165
|
+
- `/cf:research <decision>`: choose proportional depth and return traceable evidence for an uncertain technical decision
|
|
166
|
+
- `/cf:route <material-request>`: classify ambiguous, multi-step, multi-domain, or elevated-risk work and compose the shortest valid installed chain
|
|
167
|
+
- `/cf:loop <bounded-experiment>`: run explicit-only numeric optimization in an isolated worktree and return a base-bound patch handoff
|
|
168
|
+
- `/cf:specs <feature-description>`: create or resume a structured spec workflow
|
|
169
|
+
- `/cf:develop <feature-name>`: implement from approved spec artifacts
|
|
170
|
+
- `/cf:debug <issue>`: run adaptive-depth, diagnostic-only root-cause analysis with elimination and prevention-aware handoff
|
|
171
|
+
- `/cf:fix <issue>`: repair the diagnosed root cause with proportional depth; Quick/local stays direct, while Standard and Incident/deep bound the repair and use post-diagnosis research, brainstorm, or staged planning only when evidence leaves a real decision; every path consumes the `cf:debug` handoff and reports shared `PASS | PASS_WITH_WARNINGS | FAIL | BLOCKED` verdicts through verification, prevention, and side-effect gates
|
|
172
|
+
- `/cf:test [scope|--full]`: run verification and return a structured verdict
|
|
173
|
+
- `/cf:code-review [scope|--pending]`: adversarial review focused on correctness, regressions, and security
|
|
174
|
+
|
|
175
|
+
### Adaptive Brainstorm controls
|
|
176
|
+
|
|
177
|
+
Brainstorm stays proportional by default: Direct classification runs before any
|
|
178
|
+
analysis overlay, then non-direct work selects the smallest adequate Standard or
|
|
179
|
+
Deep depth. Controls are parsed only from the leading control segment. Combine
|
|
180
|
+
the single-use exact flags `--deep`, `--visual`, and `--advice` in any order;
|
|
181
|
+
an unknown or duplicate leading `--*` stops with usage and no action. `--` ends
|
|
182
|
+
controls, so literal flag content can start with `/cf:brainstorm -- --dry-run`.
|
|
183
|
+
|
|
184
|
+
`--deep` raises non-direct analysis depth only. `--visual` changes presentation
|
|
185
|
+
and falls back to text. `--advice` uses the advisory brainstormer only after a
|
|
186
|
+
material choice exists. External visual/adviser context is minimized and
|
|
187
|
+
redacted first; neither overlay writes, approves, persists, dispatches, or
|
|
188
|
+
completes work. Chat is the default output. A durable file needs explicit user
|
|
189
|
+
authority, and no Brainstorm output is live proof or Specs/Develop approval or
|
|
190
|
+
execution authority.
|
|
191
|
+
|
|
192
|
+
### Adaptive Research and bounded Loop
|
|
193
|
+
|
|
194
|
+
Use `/cf:research` when the result is a decision: Quick, Standard, or Deep
|
|
195
|
+
research binds material claims to a URL or repository anchor, authority,
|
|
196
|
+
date/version, applicability, and `confirmed`, `inferred`, or `unresolved`
|
|
197
|
+
state. Research returns evidence and tradeoffs; it does not implement the
|
|
198
|
+
recommendation or guarantee correctness.
|
|
199
|
+
|
|
200
|
+
Use `/cf:loop` only by explicit request and only after its preflight freezes
|
|
201
|
+
Goal, isolated Scope, finite numeric Metric and Direction, reproducible
|
|
202
|
+
Baseline, distinct Guard, noise policy and minimum delta, budget, and stop
|
|
203
|
+
conditions. Each iteration stays in a detached worktree. The result is a
|
|
204
|
+
base-bound isolated patch handoff, never an automatic apply, commit, push, or
|
|
205
|
+
guarantee of improvement.
|
|
206
|
+
|
|
207
|
+
```text
|
|
208
|
+
/cf:research Compare current local-first search libraries for this repository
|
|
209
|
+
/cf:loop Goal="reduce parser latency" Scope="packages/parser" Metric="median ms, lower" Baseline="pinned base" Guard="npm test" Noise="MAD; minimum delta 2%" Budget="10 iterations" Stop="budget, drift, or failed guard"
|
|
210
|
+
$cf-research Compare current local-first search libraries for this repository
|
|
211
|
+
$cf-loop Goal="reduce parser latency" Scope="packages/parser" Metric="median ms, lower" Baseline="pinned base" Guard="npm test" Noise="MAD; minimum delta 2%" Budget="10 iterations" Stop="budget, drift, or failed guard"
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Optional document skills are `docs`, `docx`, `pdf`, `pptx`, `xlsx`, and
|
|
215
|
+
`ai-multimodal`. Select them interactively or run:
|
|
154
216
|
|
|
155
|
-
|
|
217
|
+
```bash
|
|
218
|
+
npx @haposoft/cafekit --with-document-skills
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Fresh non-interactive installs leave them out by default. Use
|
|
222
|
+
`--without-document-skills` to skip them or remove pristine CafeKit-owned
|
|
223
|
+
copies; user-modified files are preserved. Documentation validators, hooks,
|
|
224
|
+
and the `docs-keeper` agent remain part of the core runtime.
|
|
225
|
+
|
|
226
|
+
When installed, `cf:docs` consumes a post-task docs checkpoint from
|
|
227
|
+
Develop/Sync (`none | minor | major` — `none` reports only, `minor`/`major`
|
|
228
|
+
update only affected existing docs, and a checkpoint never invents a new
|
|
229
|
+
document), dispatches `docs-keeper` or parallel readers only through the
|
|
230
|
+
Delegation Gate, and keeps every reconstructed claim on the
|
|
231
|
+
`Observed | Inferred | Unknown` evidence taxonomy.
|
|
156
232
|
|
|
157
233
|
CafeKit uses rule-based skill routing guidance instead of an automatic prompt-scoring hook. See `.claude/rules/skill-workflow-routing.md`, `.claude/rules/skill-domain-routing.md`, or run:
|
|
158
234
|
|
|
@@ -160,7 +236,16 @@ CafeKit uses rule-based skill routing guidance instead of an automatic prompt-sc
|
|
|
160
236
|
node .claude/scripts/generate-skill-catalog.cjs --skills
|
|
161
237
|
```
|
|
162
238
|
|
|
163
|
-
|
|
239
|
+
Routing uses progressive disclosure: live catalog metadata selects a candidate,
|
|
240
|
+
the selected `SKILL.md` supplies its contract, and only relevant references are
|
|
241
|
+
loaded. Invoke an installed skill directly for explicit or obvious low-risk
|
|
242
|
+
work; use `cf:route` only when classification or chaining is material. When a
|
|
243
|
+
capability or agent is absent, continue inline when safe or name the gap.
|
|
244
|
+
Hooks are not a skill router and never grant capability presence or authority. Source
|
|
245
|
+
and installed projections are verified; live-model adherence remains
|
|
246
|
+
`UNPROVEN`, not deterministic.
|
|
247
|
+
|
|
248
|
+
On Codex, invoke the transformed native skill names as `$cf-<name>` or browse
|
|
164
249
|
them with `/skills`. Explicit custom-agent delegation uses snake_case agent
|
|
165
250
|
names and `fork_turns: "none"`.
|
|
166
251
|
|
|
@@ -169,32 +254,36 @@ names and `fork_turns: "none"`.
|
|
|
169
254
|
Claude Code:
|
|
170
255
|
|
|
171
256
|
```bash
|
|
172
|
-
/
|
|
173
|
-
/
|
|
174
|
-
/
|
|
175
|
-
/
|
|
176
|
-
/
|
|
177
|
-
/
|
|
257
|
+
/cf:ask "Which files define the current CafeKit install/runtime behavior?" --repo
|
|
258
|
+
/cf:scout "Find the runtime entrypoints for skill installation"
|
|
259
|
+
/cf:brainstorm Explore approaches for a Google Meet transcript extension
|
|
260
|
+
/cf:research Compare current transcript storage options for this repository
|
|
261
|
+
/cf:specs Build a Google Meet transcript extension with AI summaries
|
|
262
|
+
/cf:develop meet-transcript-mvp
|
|
263
|
+
/cf:test meet-transcript-mvp --full
|
|
264
|
+
/cf:code-review meet-transcript-mvp --pending
|
|
178
265
|
```
|
|
179
266
|
|
|
180
267
|
Codex CLI:
|
|
181
268
|
|
|
182
269
|
```text
|
|
183
|
-
$
|
|
184
|
-
$
|
|
185
|
-
$
|
|
186
|
-
$
|
|
187
|
-
$
|
|
188
|
-
$
|
|
270
|
+
$cf-ask "Which files define the current CafeKit runtime?" --repo
|
|
271
|
+
$cf-scout "Find the runtime entrypoints for skill installation"
|
|
272
|
+
$cf-brainstorm Explore approaches for a Google Meet transcript extension
|
|
273
|
+
$cf-research Compare current transcript storage options for this repository
|
|
274
|
+
$cf-specs Build a Google Meet transcript extension with AI summaries
|
|
275
|
+
$cf-develop meet-transcript-mvp
|
|
276
|
+
$cf-test meet-transcript-mvp --full
|
|
277
|
+
$cf-code-review meet-transcript-mvp --pending
|
|
189
278
|
```
|
|
190
279
|
|
|
191
280
|
Reconstruct as-is docs from a legacy codebase:
|
|
192
281
|
|
|
193
282
|
```bash
|
|
194
|
-
/
|
|
283
|
+
/cf:docs --reconstruct apps/legacy-admin
|
|
195
284
|
```
|
|
196
285
|
|
|
197
|
-
The reconstruct bundle includes as-is markdown/JSON evidence and a self-contained `overview.html` review dashboard before the approved docs are handed to `/
|
|
286
|
+
The reconstruct bundle includes as-is markdown/JSON evidence and a self-contained `overview.html` review dashboard before the approved docs are handed to `/cf:specs`.
|
|
198
287
|
|
|
199
288
|
## Codex hook safety
|
|
200
289
|
|
|
@@ -215,9 +304,9 @@ specs/<feature-name>/
|
|
|
215
304
|
└── task-NN-<slug>.md
|
|
216
305
|
```
|
|
217
306
|
|
|
218
|
-
The process-first workflow has three human decision gates. `
|
|
307
|
+
The process-first workflow has three human decision gates. `cf:specs` opens
|
|
219
308
|
C1 to fix scope before authoring and C2 to resolve adversarial findings, then
|
|
220
|
-
stops. A later explicit `
|
|
309
|
+
stops. A later explicit `cf:develop` invocation executes the tasks and
|
|
221
310
|
presents C3 for closeout only after real execution proof. Each task has one
|
|
222
311
|
`Status:` field and keeps its canonical `## Receipt` inline, including the
|
|
223
312
|
exact Verification Plan command, `Exit: 0`, `Verification: PASS`,
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// `.codex/hooks.json` is a shared configuration file, not a CafeKit payload. It used to be
|
|
4
|
+
// copied verbatim, so a single user-added hook turned the whole file into a user-modified
|
|
5
|
+
// artifact: a normal install then preserved CafeKit's stale hooks, and `--force-overwrite`
|
|
6
|
+
// destroyed the user's. These tests pin the merge that replaced that copy, mirroring how
|
|
7
|
+
// `.claude/settings.json` has always been handled.
|
|
8
|
+
|
|
9
|
+
const assert = require('node:assert/strict');
|
|
10
|
+
const fs = require('node:fs');
|
|
11
|
+
const os = require('node:os');
|
|
12
|
+
const path = require('node:path');
|
|
13
|
+
const { spawnSync } = require('node:child_process');
|
|
14
|
+
const { test } = require('node:test');
|
|
15
|
+
|
|
16
|
+
const PACKAGE_ROOT = path.join(__dirname, '../..');
|
|
17
|
+
const INSTALLER = path.join(PACKAGE_ROOT, 'bin/install.js');
|
|
18
|
+
const TEMPLATE = path.join(PACKAGE_ROOT, 'src/codex/hooks.json');
|
|
19
|
+
const MANIFEST = require(path.join(PACKAGE_ROOT, 'src/claude/migration-manifest.json'));
|
|
20
|
+
const { hookScript } = require(path.join(PACKAGE_ROOT, 'bin/phases/codex-hooks.js'));
|
|
21
|
+
|
|
22
|
+
const USER_STOP_COMMAND = 'node "$(git rev-parse --show-toplevel)/scripts/my-own-stop.js"';
|
|
23
|
+
const USER_PRETOOL_COMMAND = 'echo user-owned-pretooluse';
|
|
24
|
+
|
|
25
|
+
function inTempProject(run) {
|
|
26
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-codex-hooks-own-'));
|
|
27
|
+
try {
|
|
28
|
+
return run(root);
|
|
29
|
+
} finally {
|
|
30
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function install(root, extraArgs = []) {
|
|
35
|
+
return spawnSync(
|
|
36
|
+
process.execPath,
|
|
37
|
+
[INSTALLER, '--platform', 'codex', '--yes', ...extraArgs],
|
|
38
|
+
{ cwd: root, encoding: 'utf8', env: { ...process.env, PATH: '/usr/bin:/bin' } }
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function hooksPath(root) {
|
|
43
|
+
return path.join(root, '.codex', 'hooks.json');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function readHooks(root) {
|
|
47
|
+
return JSON.parse(fs.readFileSync(hooksPath(root), 'utf8'));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Every handler across every event, flattened. */
|
|
51
|
+
function allHandlers(config) {
|
|
52
|
+
return Object.values(config.hooks || {})
|
|
53
|
+
.flatMap((groups) => (Array.isArray(groups) ? groups : []))
|
|
54
|
+
.flatMap((group) => (Array.isArray(group?.hooks) ? group.hooks : []));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function allCommands(config) {
|
|
58
|
+
return allHandlers(config).map((handler) => handler?.command).filter(Boolean);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The CafeKit hook scripts the template registers, per event. */
|
|
62
|
+
function managedScripts() {
|
|
63
|
+
const template = JSON.parse(fs.readFileSync(TEMPLATE, 'utf8'));
|
|
64
|
+
const byEvent = {};
|
|
65
|
+
for (const [eventName, groups] of Object.entries(template.hooks || {})) {
|
|
66
|
+
byEvent[eventName] = groups
|
|
67
|
+
.flatMap((group) => group.hooks || [])
|
|
68
|
+
.map((handler) => hookScript(handler.command))
|
|
69
|
+
.filter(Boolean);
|
|
70
|
+
}
|
|
71
|
+
return byEvent;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Add two hooks CafeKit does not author: one under an event it owns, one under a new event. */
|
|
75
|
+
function addUserHooks(root) {
|
|
76
|
+
const config = readHooks(root);
|
|
77
|
+
config.hooks.Stop.push({ matcher: '*', hooks: [{ type: 'command', command: USER_STOP_COMMAND }] });
|
|
78
|
+
config.hooks.PreToolUse.push({ matcher: 'Bash', hooks: [{ type: 'command', command: USER_PRETOOL_COMMAND }] });
|
|
79
|
+
fs.writeFileSync(hooksPath(root), `${JSON.stringify(config, null, 2)}\n`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
test('a fresh install registers every CafeKit hook under its own event', () => {
|
|
83
|
+
inTempProject((root) => {
|
|
84
|
+
const result = install(root);
|
|
85
|
+
assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
|
|
86
|
+
|
|
87
|
+
const installed = readHooks(root);
|
|
88
|
+
for (const [eventName, scripts] of Object.entries(managedScripts())) {
|
|
89
|
+
const registered = (installed.hooks[eventName] || [])
|
|
90
|
+
.flatMap((group) => group.hooks || [])
|
|
91
|
+
.map((handler) => hookScript(handler.command))
|
|
92
|
+
.filter(Boolean);
|
|
93
|
+
for (const script of scripts) {
|
|
94
|
+
assert.ok(registered.includes(script), `${script} missing from ${eventName}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('a reinstall preserves user-added hooks instead of overwriting them', () => {
|
|
101
|
+
inTempProject((root) => {
|
|
102
|
+
assert.equal(install(root).status, 0);
|
|
103
|
+
addUserHooks(root);
|
|
104
|
+
|
|
105
|
+
const second = install(root);
|
|
106
|
+
assert.equal(second.status, 0, `${second.stdout}\n${second.stderr}`);
|
|
107
|
+
|
|
108
|
+
const commands = allCommands(readHooks(root));
|
|
109
|
+
assert.ok(commands.includes(USER_STOP_COMMAND), 'user Stop hook was erased by the reinstall');
|
|
110
|
+
assert.ok(commands.includes(USER_PRETOOL_COMMAND), 'user PreToolUse hook was erased by the reinstall');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('--force-overwrite does not destroy hooks CafeKit did not author', () => {
|
|
115
|
+
inTempProject((root) => {
|
|
116
|
+
assert.equal(install(root).status, 0);
|
|
117
|
+
addUserHooks(root);
|
|
118
|
+
|
|
119
|
+
const forced = install(root, ['--force-overwrite']);
|
|
120
|
+
assert.equal(forced.status, 0, `${forced.stdout}\n${forced.stderr}`);
|
|
121
|
+
|
|
122
|
+
const commands = allCommands(readHooks(root));
|
|
123
|
+
assert.ok(commands.includes(USER_STOP_COMMAND), '--force-overwrite erased the user Stop hook');
|
|
124
|
+
assert.ok(commands.includes(USER_PRETOOL_COMMAND), '--force-overwrite erased the user PreToolUse hook');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('repeated installs never duplicate a CafeKit hook', () => {
|
|
129
|
+
inTempProject((root) => {
|
|
130
|
+
assert.equal(install(root).status, 0);
|
|
131
|
+
const afterFirst = allHandlers(readHooks(root)).length;
|
|
132
|
+
|
|
133
|
+
assert.equal(install(root).status, 0);
|
|
134
|
+
assert.equal(install(root).status, 0);
|
|
135
|
+
|
|
136
|
+
const config = readHooks(root);
|
|
137
|
+
assert.equal(allHandlers(config).length, afterFirst, 'a reinstall added handlers that were already registered');
|
|
138
|
+
|
|
139
|
+
// Uniqueness is per event, not global: state.cjs and completion-authority.cjs each
|
|
140
|
+
// register under several events by design.
|
|
141
|
+
for (const [eventName, groups] of Object.entries(config.hooks)) {
|
|
142
|
+
const scripts = groups
|
|
143
|
+
.flatMap((group) => group.hooks || [])
|
|
144
|
+
.map((handler) => hookScript(handler.command))
|
|
145
|
+
.filter(Boolean);
|
|
146
|
+
assert.equal(
|
|
147
|
+
new Set(scripts).size, scripts.length,
|
|
148
|
+
`duplicate hook scripts under ${eventName}: ${scripts.join(', ')}`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('a hook the user moved to another event is not re-added to the one CafeKit chose', () => {
|
|
155
|
+
inTempProject((root) => {
|
|
156
|
+
assert.equal(install(root).status, 0);
|
|
157
|
+
|
|
158
|
+
// Move a CafeKit hook out of the group the template put it in, the way a user editing
|
|
159
|
+
// matchers would. Re-adding it on the next install would fire it twice.
|
|
160
|
+
const config = readHooks(root);
|
|
161
|
+
let moved = null;
|
|
162
|
+
for (const group of config.hooks.PreToolUse) {
|
|
163
|
+
const index = (group.hooks || []).findIndex((handler) => hookScript(handler.command) === 'inspect-block.cjs');
|
|
164
|
+
if (index >= 0) { moved = group.hooks.splice(index, 1)[0]; break; }
|
|
165
|
+
}
|
|
166
|
+
assert.ok(moved, 'fixture expects inspect-block.cjs under PreToolUse');
|
|
167
|
+
config.hooks.PreToolUse.push({ matcher: 'Read|Grep', hooks: [moved] });
|
|
168
|
+
fs.writeFileSync(hooksPath(root), `${JSON.stringify(config, null, 2)}\n`);
|
|
169
|
+
|
|
170
|
+
assert.equal(install(root).status, 0);
|
|
171
|
+
|
|
172
|
+
const scripts = allCommands(readHooks(root)).map(hookScript).filter(Boolean);
|
|
173
|
+
assert.equal(
|
|
174
|
+
scripts.filter((script) => script === 'inspect-block.cjs').length, 1,
|
|
175
|
+
'the moved hook was re-added under its original matcher'
|
|
176
|
+
);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('obsolete CafeKit hooks are pruned while similarly named foreign hooks stay', () => {
|
|
181
|
+
const obsolete = MANIFEST.obsolete.settingsHookCommandSubstrings;
|
|
182
|
+
assert.ok(obsolete.length > 0, 'fixture requires at least one retired hook substring');
|
|
183
|
+
|
|
184
|
+
inTempProject((root) => {
|
|
185
|
+
assert.equal(install(root).status, 0);
|
|
186
|
+
|
|
187
|
+
const config = readHooks(root);
|
|
188
|
+
const retired = `node ".codex/${obsolete[0]}"`;
|
|
189
|
+
const foreign = 'node "scripts/my-skill-router.cjs"';
|
|
190
|
+
config.hooks.UserPromptSubmit.push({
|
|
191
|
+
matcher: '*',
|
|
192
|
+
hooks: [{ type: 'command', command: retired }, { type: 'command', command: foreign }]
|
|
193
|
+
});
|
|
194
|
+
fs.writeFileSync(hooksPath(root), `${JSON.stringify(config, null, 2)}\n`);
|
|
195
|
+
|
|
196
|
+
const second = install(root);
|
|
197
|
+
assert.equal(second.status, 0, `${second.stdout}\n${second.stderr}`);
|
|
198
|
+
|
|
199
|
+
const commands = allCommands(readHooks(root));
|
|
200
|
+
assert.ok(!commands.includes(retired), 'retired CafeKit hook survived the prune');
|
|
201
|
+
assert.ok(commands.includes(foreign), 'prune removed a hook CafeKit never authored');
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('a malformed hooks.json is reported and left byte-identical, never overwritten', () => {
|
|
206
|
+
inTempProject((root) => {
|
|
207
|
+
fs.mkdirSync(path.join(root, '.codex'), { recursive: true });
|
|
208
|
+
const broken = '{ "hooks": { "Stop": [ }\n';
|
|
209
|
+
fs.writeFileSync(hooksPath(root), broken);
|
|
210
|
+
|
|
211
|
+
const result = install(root);
|
|
212
|
+
assert.notEqual(result.status, 0, 'a malformed hooks.json must fail the install, not pass silently');
|
|
213
|
+
assert.match(`${result.stdout}${result.stderr}`, /not valid JSON/);
|
|
214
|
+
assert.equal(fs.readFileSync(hooksPath(root), 'utf8'), broken, 'the malformed file was overwritten');
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test('a dry run reports the merge without writing hooks.json', () => {
|
|
219
|
+
inTempProject((root) => {
|
|
220
|
+
const result = install(root, ['--dry-run']);
|
|
221
|
+
assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
|
|
222
|
+
assert.match(result.stdout, /Codex hooks/);
|
|
223
|
+
assert.equal(fs.existsSync(hooksPath(root)), false, 'a dry run wrote hooks.json');
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('hooks.json stays outside the ownership manifest, like settings.json', () => {
|
|
228
|
+
inTempProject((root) => {
|
|
229
|
+
assert.equal(install(root).status, 0);
|
|
230
|
+
|
|
231
|
+
const manifestPath = path.join(root, '.codex', 'cafekit-manifest.json');
|
|
232
|
+
assert.equal(fs.existsSync(manifestPath), true, 'the install manifest is missing');
|
|
233
|
+
const tracked = Object.keys(JSON.parse(fs.readFileSync(manifestPath, 'utf8')).files || {});
|
|
234
|
+
assert.ok(
|
|
235
|
+
!tracked.some((file) => file.endsWith('hooks.json')),
|
|
236
|
+
'hooks.json is tracked as a payload file, so a user edit would be classified user-modified again'
|
|
237
|
+
);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
@@ -156,6 +156,56 @@ function canonicalProcessTask(root, title, dependency = 'none') {
|
|
|
156
156
|
].join('\n');
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
test('Claude and Codex docs sync ignore committed Specs state but report source changes', () => {
|
|
160
|
+
inHookFixture((root, hooks) => {
|
|
161
|
+
const docsDir = path.join(root, 'docs');
|
|
162
|
+
fs.mkdirSync(docsDir, { recursive: true });
|
|
163
|
+
fs.writeFileSync(path.join(root, 'package.json'), '{"name":"docs-sync-fixture"}\n');
|
|
164
|
+
fs.writeFileSync(path.join(docsDir, 'project-overview-pdr.md'), '# Fixture\n');
|
|
165
|
+
let result = spawnSync('git', ['-C', root, 'add', 'package.json', 'docs/project-overview-pdr.md'], { encoding: 'utf8' });
|
|
166
|
+
assert.equal(result.status, 0, result.stderr);
|
|
167
|
+
result = spawnSync('git', ['-C', root, 'commit', '-qm', 'source baseline'], { encoding: 'utf8' });
|
|
168
|
+
assert.equal(result.status, 0, result.stderr);
|
|
169
|
+
const sourceBase = spawnSync('git', ['-C', root, 'rev-parse', 'HEAD'], { encoding: 'utf8' }).stdout.trim();
|
|
170
|
+
fs.writeFileSync(path.join(docsDir, '.sync_hash'), `${sourceBase}\n`);
|
|
171
|
+
result = spawnSync('git', ['-C', root, 'add', 'docs/.sync_hash'], { encoding: 'utf8' });
|
|
172
|
+
assert.equal(result.status, 0, result.stderr);
|
|
173
|
+
result = spawnSync('git', ['-C', root, 'commit', '-qm', 'sync docs'], { encoding: 'utf8' });
|
|
174
|
+
assert.equal(result.status, 0, result.stderr);
|
|
175
|
+
|
|
176
|
+
const featureDir = path.join(root, 'specs', 'demo');
|
|
177
|
+
fs.mkdirSync(featureDir, { recursive: true });
|
|
178
|
+
fs.writeFileSync(path.join(featureDir, 'plan.md'), '# Completed packet\n');
|
|
179
|
+
result = spawnSync('git', ['-C', root, 'add', 'specs/demo/plan.md'], { encoding: 'utf8' });
|
|
180
|
+
assert.equal(result.status, 0, result.stderr);
|
|
181
|
+
result = spawnSync('git', ['-C', root, 'commit', '-qm', 'refresh receipt state'], { encoding: 'utf8' });
|
|
182
|
+
assert.equal(result.status, 0, result.stderr);
|
|
183
|
+
|
|
184
|
+
const payload = { cwd: root, session_id: 'docs-sync-session', hook_event_name: 'SessionStart' };
|
|
185
|
+
const docsSyncHooks = [
|
|
186
|
+
path.join(hooks, 'docs-sync.cjs'),
|
|
187
|
+
path.join(PACKAGE_ROOT, 'src/claude/hooks/docs-sync.cjs'),
|
|
188
|
+
];
|
|
189
|
+
for (const hook of docsSyncHooks) {
|
|
190
|
+
const afterSpecCommit = runHook(hook, root, payload);
|
|
191
|
+
assert.equal(afterSpecCommit.status, 0, afterSpecCommit.stderr);
|
|
192
|
+
assert.equal(afterSpecCommit.stdout, '', `${hook}: spec-only commits must not stale docs sync`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
fs.mkdirSync(path.join(root, 'src'), { recursive: true });
|
|
196
|
+
fs.writeFileSync(path.join(root, 'src', 'app.js'), 'module.exports = true;\n');
|
|
197
|
+
result = spawnSync('git', ['-C', root, 'add', 'src/app.js'], { encoding: 'utf8' });
|
|
198
|
+
assert.equal(result.status, 0, result.stderr);
|
|
199
|
+
result = spawnSync('git', ['-C', root, 'commit', '-qm', 'change source'], { encoding: 'utf8' });
|
|
200
|
+
assert.equal(result.status, 0, result.stderr);
|
|
201
|
+
for (const hook of docsSyncHooks) {
|
|
202
|
+
const afterSourceCommit = runHook(hook, root, payload);
|
|
203
|
+
assert.equal(afterSourceCommit.status, 0, afterSourceCommit.stderr);
|
|
204
|
+
assert.match(afterSourceCommit.stdout, /Docs sync needed/);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
159
209
|
function inInstalledProcessSpecStateFixture(run) {
|
|
160
210
|
return inHookFixture((root, hooks) => {
|
|
161
211
|
const feature = path.join(root, 'specs', 'auth');
|