@haposoft/cafekit 0.16.0-rc.8 → 0.16.1
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 +117 -28
- package/bin/__tests__/codex-hooks-ownership.test.js +239 -0
- package/bin/__tests__/codex-hooks.test.js +175 -0
- package/bin/__tests__/codex-native.test.js +1882 -70
- package/bin/__tests__/develop-contract.test.js +442 -23
- package/bin/__tests__/grok-envelope.test.js +271 -0
- package/bin/__tests__/grok-platform.test.js +133 -0
- package/bin/__tests__/hook-payload.test.js +194 -0
- package/bin/__tests__/installer-safety.test.js +3 -3
- package/bin/__tests__/omp-bridge.test.js +168 -0
- package/bin/__tests__/omp-hooks.test.js +178 -0
- package/bin/__tests__/omp-runtime.test.js +150 -0
- 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-dir.test.js +158 -0
- package/bin/__tests__/runtime-schema.test.js +113 -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 +28 -1
- package/bin/install.js +15 -5
- package/bin/lib/codex-install.js +170 -5
- package/bin/lib/context.js +64 -3
- package/bin/lib/i18n.js +15 -36
- 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 +15 -2
- package/bin/phases/omp-runtime.js +140 -0
- package/bin/phases/root-config.js +3 -0
- package/bin/phases/select-platform.js +22 -1
- 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/summary.js +0 -4
- 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__/semantic-review-authority.test.js +2 -0
- package/src/claude/hooks/__tests__/spec-gate.test.js +348 -3
- package/src/claude/hooks/__tests__/state.test.js +7 -0
- package/src/claude/hooks/__tests__/statusline.test.js +196 -0
- package/src/claude/hooks/agent.cjs +13 -8
- package/src/claude/hooks/completion-authority.cjs +10 -4
- package/src/claude/hooks/docs-sync.cjs +14 -11
- package/src/claude/hooks/inspect-block.cjs +28 -8
- package/src/claude/hooks/lib/config.cjs +8 -2
- package/src/claude/hooks/lib/counter.cjs +7 -5
- package/src/claude/hooks/lib/hook-payload.cjs +160 -0
- package/src/claude/hooks/lib/hook-state-dir.cjs +39 -0
- package/src/claude/hooks/lib/runtime-dir.cjs +49 -0
- package/src/claude/hooks/lib/secret-keywords.cjs +42 -0
- package/src/claude/hooks/precompact.cjs +106 -0
- package/src/claude/hooks/privacy-block.cjs +4 -2
- package/src/claude/hooks/rules.cjs +5 -3
- package/src/claude/hooks/secret-output-guardrail.cjs +44 -0
- package/src/claude/hooks/semantic-review-authority.cjs +6 -4
- package/src/claude/hooks/session.cjs +37 -9
- package/src/claude/hooks/spec-gate.cjs +21 -11
- package/src/claude/hooks/spec-state.cjs +11 -7
- package/src/claude/hooks/state.cjs +7 -5
- package/src/claude/hooks/task-scaffold-guard.cjs +30 -9
- package/src/claude/hooks/usage.cjs +6 -5
- package/src/claude/migration-manifest.json +31 -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/state-sync.md +7 -1
- package/src/claude/rules/workflow.md +5 -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 +29 -9
- package/src/claude/scripts/spec-receipt.cjs +65 -4
- 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 +51 -71
- 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 +3 -2
- package/src/codex/hooks.json +5 -0
- package/src/codex/rules/state-sync.md +7 -1
- package/src/codex/runtime.json +2 -0
- package/src/common/.env.example +1 -1
- package/src/omp/extensions/cafekit-bridge.mjs +233 -0
- package/src/omp/hooks/privacy-block.cjs +250 -0
- package/src/omp/runtime.json +33 -0
- package/bin/lib/rtk-run.js +0 -28
- package/bin/phases/setup-rtk.js +0 -159
- 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
|
@@ -1,184 +1,218 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* This mirrors the Research/.claude approach: expose the installed skill set as
|
|
6
|
-
* a readable catalog so the model can choose skills from rules, not from an
|
|
7
|
-
* automatic prompt-scoring hook.
|
|
8
|
-
*/
|
|
2
|
+
/** Generate a fail-closed catalog from the current runtime's installed skills. */
|
|
3
|
+
|
|
4
|
+
'use strict';
|
|
9
5
|
|
|
10
6
|
const fs = require('fs');
|
|
11
7
|
const path = require('path');
|
|
12
8
|
|
|
9
|
+
// Keep synchronized with migration-manifest.json obsolete.skills. Modified
|
|
10
|
+
// retired directories may remain on disk, but they are never automatic routes.
|
|
11
|
+
const RETIRED_DIRECTORIES = new Set([
|
|
12
|
+
'backend-development',
|
|
13
|
+
'frontend-development',
|
|
14
|
+
'frontend-design',
|
|
15
|
+
'mobile-development',
|
|
16
|
+
'devops',
|
|
17
|
+
'react-best-practices',
|
|
18
|
+
]);
|
|
19
|
+
const PUBLIC_SUFFIX_BY_DIRECTORY = {
|
|
20
|
+
hotfix: 'fix',
|
|
21
|
+
inspect: 'scout',
|
|
22
|
+
question: 'ask',
|
|
23
|
+
};
|
|
24
|
+
|
|
13
25
|
function usage() {
|
|
14
26
|
console.log(`Usage:
|
|
15
|
-
node
|
|
27
|
+
node <runtime>/scripts/generate-skill-catalog.cjs [--skills] [--json] [--root <skills-dir>]
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
node .claude/scripts/generate-skill-catalog.cjs --json --root .claude/skills`);
|
|
29
|
+
The default root is bound to the script runtime: .claude/skills for Claude,
|
|
30
|
+
.agents/skills for Codex, and ../skills for the source tree.`);
|
|
20
31
|
}
|
|
21
32
|
|
|
22
33
|
function parseArgs(argv) {
|
|
23
34
|
const args = { json: false, help: false, root: null };
|
|
24
|
-
for (let
|
|
25
|
-
const arg = argv[
|
|
35
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
36
|
+
const arg = argv[index];
|
|
26
37
|
if (arg === '--help' || arg === '-h') args.help = true;
|
|
27
38
|
else if (arg === '--json') args.json = true;
|
|
28
39
|
else if (arg === '--skills') continue;
|
|
29
40
|
else if (arg === '--root') {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
args.root =
|
|
34
|
-
}
|
|
41
|
+
if (!argv[index + 1] || argv[index + 1].startsWith('--')) {
|
|
42
|
+
throw new Error('--root requires a skills directory');
|
|
43
|
+
}
|
|
44
|
+
args.root = argv[++index];
|
|
45
|
+
} else throw new Error(`unknown option: ${arg}`);
|
|
35
46
|
}
|
|
36
47
|
return args;
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
function defaultSkillsRoot() {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return candidates.find((candidate) => fs.existsSync(candidate)) || candidates[0];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function readText(filePath) {
|
|
50
|
-
return fs.readFileSync(filePath, 'utf8');
|
|
51
|
+
const runtimeRoot = path.dirname(__dirname);
|
|
52
|
+
const runtimeName = path.basename(runtimeRoot);
|
|
53
|
+
if (runtimeName === '.codex') return path.join(path.dirname(runtimeRoot), '.agents', 'skills');
|
|
54
|
+
const claudeRuntimeName = `.${['cla', 'ude'].join('')}`;
|
|
55
|
+
if (runtimeName === claudeRuntimeName) return path.join(runtimeRoot, 'skills');
|
|
56
|
+
return path.resolve(__dirname, '..', 'skills');
|
|
51
57
|
}
|
|
52
58
|
|
|
53
59
|
function extractFrontmatter(content) {
|
|
54
|
-
const match = content.match(/^---\
|
|
55
|
-
if (!match) return
|
|
60
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
61
|
+
if (!match) return null;
|
|
56
62
|
const frontmatter = {};
|
|
57
63
|
const lines = match[1].split(/\r?\n/);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
const seen = new Set();
|
|
65
|
+
let nestedKey = null;
|
|
66
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
67
|
+
const line = lines[index];
|
|
68
|
+
if (line.trim() === '') continue;
|
|
69
|
+
if (/^\s+/.test(line)) {
|
|
70
|
+
const nested = line.match(/^ ([A-Za-z0-9_-]+):\s*(\S.*)$/);
|
|
71
|
+
if (!nestedKey || !nested) return null;
|
|
72
|
+
const identity = `${nestedKey}.${nested[1]}`;
|
|
73
|
+
if (seen.has(identity) || quotedValue(nested[2]) === null) return null;
|
|
74
|
+
seen.add(identity);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
nestedKey = null;
|
|
78
|
+
const parsed = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
79
|
+
if (!parsed || seen.has(parsed[1])) return null;
|
|
80
|
+
seen.add(parsed[1]);
|
|
81
|
+
let value = parsed[2].trim();
|
|
82
|
+
if (['>', '>-', '|', '|-'].includes(value)) {
|
|
65
83
|
const block = [];
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
block.push(lines[i].trim());
|
|
69
|
-
i += 1;
|
|
84
|
+
while (index + 1 < lines.length && /^\s+/.test(lines[index + 1])) {
|
|
85
|
+
block.push(lines[++index].trim());
|
|
70
86
|
}
|
|
71
|
-
|
|
87
|
+
if (block.length === 0) return null;
|
|
72
88
|
value = block.join(' ');
|
|
73
89
|
}
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
value = value.slice(1, -1);
|
|
90
|
+
if (value === '') {
|
|
91
|
+
nestedKey = parsed[1];
|
|
92
|
+
frontmatter[parsed[1]] = '';
|
|
93
|
+
continue;
|
|
79
94
|
}
|
|
80
|
-
|
|
95
|
+
value = quotedValue(value);
|
|
96
|
+
if (value === null) return null;
|
|
97
|
+
frontmatter[parsed[1]] = value;
|
|
81
98
|
}
|
|
82
99
|
return frontmatter;
|
|
83
100
|
}
|
|
84
101
|
|
|
85
|
-
function
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
paragraph.push(line);
|
|
95
|
-
if (paragraph.join(' ').length > 160) break;
|
|
102
|
+
function quotedValue(value) {
|
|
103
|
+
const startsQuote = value.startsWith('"') || value.startsWith("'");
|
|
104
|
+
const endsQuote = value.endsWith('"') || value.endsWith("'");
|
|
105
|
+
const startsFlow = value.startsWith('[') || value.startsWith('{');
|
|
106
|
+
const endsFlow = value.endsWith(']') || value.endsWith('}');
|
|
107
|
+
if (startsFlow || endsFlow) {
|
|
108
|
+
const expected = value.startsWith('[') ? ']' : value.startsWith('{') ? '}' : null;
|
|
109
|
+
if (!expected || !value.endsWith(expected)) return null;
|
|
96
110
|
}
|
|
97
|
-
|
|
111
|
+
if (!startsQuote && !endsQuote) return value;
|
|
112
|
+
if (value.length < 2 || value[0] !== value[value.length - 1]) return null;
|
|
113
|
+
return value.slice(1, -1);
|
|
98
114
|
}
|
|
99
115
|
|
|
100
|
-
function
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (/debug|hotfix|inspect|impact|review|test|agent-browser|chrome/.test(text)) return 'quality';
|
|
104
|
-
if (/frontend|react|ui|ux|mobile/.test(text)) return 'frontend-mobile';
|
|
105
|
-
if (/pdf|docx|pptx|xlsx|multimodal/.test(text)) return 'artifacts-tools';
|
|
106
|
-
if (/backend|devops|api|database|deploy/.test(text)) return 'backend-infra';
|
|
107
|
-
if (/docs|research|graph/.test(text)) return 'knowledge';
|
|
108
|
-
if (/git/.test(text)) return 'git';
|
|
109
|
-
return 'other';
|
|
116
|
+
function parseKeywords(value) {
|
|
117
|
+
if (!value || !/^\[[^\]]*\]$/.test(value)) return [];
|
|
118
|
+
return value.slice(1, -1).split(',').map((item) => item.trim()).filter(Boolean);
|
|
110
119
|
}
|
|
111
120
|
|
|
112
|
-
function
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
121
|
+
function publicIdentity(name) {
|
|
122
|
+
const match = String(name).match(/^cf(?::|-)([a-z0-9]+(?:-[a-z0-9]+)*)$/);
|
|
123
|
+
return match ? `cf:${match[1]}` : null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function expectedIdentity(directory) {
|
|
127
|
+
return `cf:${PUBLIC_SUFFIX_BY_DIRECTORY[directory] || directory}`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function diagnostic(code, directory, message) {
|
|
131
|
+
return { code, directory, message };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function scanSkills(requestedRoot) {
|
|
135
|
+
if (!fs.existsSync(requestedRoot)) throw new Error(`skills root not found: ${requestedRoot}`);
|
|
136
|
+
const root = fs.realpathSync(requestedRoot);
|
|
137
|
+
const diagnostics = [];
|
|
138
|
+
const candidates = [];
|
|
139
|
+
const entries = fs.readdirSync(root, { withFileTypes: true })
|
|
140
|
+
.filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))
|
|
141
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
142
|
+
|
|
118
143
|
for (const entry of entries) {
|
|
119
|
-
if (!entry.isDirectory() || entry.name.startsWith('.')) continue;
|
|
120
144
|
const skillPath = path.join(root, entry.name, 'SKILL.md');
|
|
121
145
|
if (!fs.existsSync(skillPath)) continue;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
146
|
+
if (RETIRED_DIRECTORIES.has(entry.name)) {
|
|
147
|
+
diagnostics.push(diagnostic('retired_skill', entry.name, 'excluded from automatic routing'));
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const frontmatter = extractFrontmatter(fs.readFileSync(skillPath, 'utf8'));
|
|
151
|
+
if (!frontmatter) {
|
|
152
|
+
diagnostics.push(diagnostic('malformed_frontmatter', entry.name, 'missing frontmatter block'));
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const identity = publicIdentity(frontmatter.name);
|
|
156
|
+
if (!identity || !frontmatter.description || frontmatter.description.trim() === '') {
|
|
157
|
+
diagnostics.push(diagnostic('missing_routing_metadata', entry.name, 'requires valid name and description'));
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (identity !== expectedIdentity(entry.name)) {
|
|
161
|
+
diagnostics.push(diagnostic('folder_name_mismatch', entry.name, `${identity} does not match directory`));
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
candidates.push({
|
|
165
|
+
name: frontmatter.name,
|
|
166
|
+
public_id: identity,
|
|
128
167
|
directory: entry.name,
|
|
129
|
-
description,
|
|
130
|
-
|
|
168
|
+
description: frontmatter.description,
|
|
169
|
+
when_to_use: frontmatter.when_to_use || '',
|
|
170
|
+
category: frontmatter.category || 'other',
|
|
171
|
+
keywords: parseKeywords(frontmatter.keywords),
|
|
172
|
+
user_invocable: frontmatter['user-invocable'] !== 'false',
|
|
131
173
|
has_references: fs.existsSync(path.join(root, entry.name, 'references')),
|
|
132
174
|
has_scripts: fs.existsSync(path.join(root, entry.name, 'scripts')),
|
|
133
175
|
});
|
|
134
176
|
}
|
|
135
|
-
|
|
177
|
+
|
|
178
|
+
const counts = new Map();
|
|
179
|
+
for (const skill of candidates) counts.set(skill.public_id, (counts.get(skill.public_id) || 0) + 1);
|
|
180
|
+
const skills = candidates.filter((skill) => {
|
|
181
|
+
if (counts.get(skill.public_id) === 1) return true;
|
|
182
|
+
diagnostics.push(diagnostic('duplicate_public_name', skill.directory, `${skill.public_id} requires explicit user disambiguation`));
|
|
183
|
+
return false;
|
|
184
|
+
}).sort((left, right) => left.public_id.localeCompare(right.public_id));
|
|
185
|
+
|
|
186
|
+
return { root, skills, diagnostics: diagnostics.sort((left, right) =>
|
|
187
|
+
`${left.directory}:${left.code}`.localeCompare(`${right.directory}:${right.code}`)) };
|
|
136
188
|
}
|
|
137
189
|
|
|
138
|
-
function renderMarkdown(
|
|
139
|
-
const lines = [
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
`
|
|
143
|
-
|
|
144
|
-
'',
|
|
145
|
-
];
|
|
146
|
-
const categories = [...new Set(skills.map((skill) => skill.category))].sort();
|
|
147
|
-
for (const category of categories) {
|
|
148
|
-
lines.push(`## ${category}`, '');
|
|
149
|
-
for (const skill of skills.filter((item) => item.category === category)) {
|
|
150
|
-
const badges = [
|
|
151
|
-
skill.has_references ? 'refs' : null,
|
|
152
|
-
skill.has_scripts ? 'scripts' : null,
|
|
153
|
-
].filter(Boolean);
|
|
154
|
-
lines.push(
|
|
155
|
-
`- \`${skill.name}\` (${skill.directory})${badges.length ? ` [${badges.join(', ')}]` : ''}: ${skill.description}`
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
lines.push('');
|
|
190
|
+
function renderMarkdown(catalog) {
|
|
191
|
+
const lines = ['# CafeKit Skills Catalog', '', `Skills root: \`${catalog.root}\``,
|
|
192
|
+
`Routable skills: ${catalog.skills.length}`, ''];
|
|
193
|
+
for (const skill of catalog.skills) {
|
|
194
|
+
const intent = skill.when_to_use ? ` Use: ${skill.when_to_use}` : '';
|
|
195
|
+
lines.push(`- \`${skill.name}\` (${skill.directory}, ${skill.category}): ${skill.description}${intent}`);
|
|
159
196
|
}
|
|
160
|
-
|
|
197
|
+
if (catalog.diagnostics.length > 0) {
|
|
198
|
+
lines.push('', '## Diagnostics', '');
|
|
199
|
+
for (const item of catalog.diagnostics) lines.push(`- ${item.code} (${item.directory}): ${item.message}`);
|
|
200
|
+
}
|
|
201
|
+
return `${lines.join('\n').trim()}\n`;
|
|
161
202
|
}
|
|
162
203
|
|
|
163
204
|
function main() {
|
|
164
205
|
const args = parseArgs(process.argv.slice(2));
|
|
165
|
-
if (args.help)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const root = path.resolve(process.cwd(), args.root || defaultSkillsRoot());
|
|
171
|
-
const skills = scanSkills(root);
|
|
172
|
-
if (args.json) {
|
|
173
|
-
console.log(JSON.stringify({ root, total: skills.length, skills }, null, 2));
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
console.log(renderMarkdown(skills, root));
|
|
206
|
+
if (args.help) return usage();
|
|
207
|
+
const requestedRoot = path.resolve(process.cwd(), args.root || defaultSkillsRoot());
|
|
208
|
+
const catalog = scanSkills(requestedRoot);
|
|
209
|
+
if (args.json) console.log(JSON.stringify({ ...catalog, total: catalog.skills.length }, null, 2));
|
|
210
|
+
else console.log(renderMarkdown(catalog));
|
|
177
211
|
}
|
|
178
212
|
|
|
179
213
|
try {
|
|
180
214
|
main();
|
|
181
215
|
} catch (error) {
|
|
182
216
|
console.error(`[skill-catalog] ${error.message}`);
|
|
183
|
-
process.
|
|
217
|
+
process.exitCode = 1;
|
|
184
218
|
}
|
|
@@ -83,9 +83,22 @@ function oneLine(value, label) {
|
|
|
83
83
|
return output.replace(/\n$/, '');
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
function gitBase(root) {
|
|
87
|
-
const
|
|
88
|
-
|
|
86
|
+
function gitBase(root, specsRoot) {
|
|
87
|
+
const specsRelative = path.relative(root, specsRoot).split(path.sep).join('/');
|
|
88
|
+
const sourceHistory = decode(runGit(
|
|
89
|
+
root,
|
|
90
|
+
['log', '-1', '--format=%H', '--', '.', `:(exclude,literal)${specsRelative}`],
|
|
91
|
+
'git log source base',
|
|
92
|
+
)).trim();
|
|
93
|
+
const roots = sourceHistory ? [] : decode(
|
|
94
|
+
runGit(root, ['rev-list', '--max-parents=0', '--reverse', 'HEAD'], 'git rev-list root'),
|
|
95
|
+
'git rev-list root',
|
|
96
|
+
).trim().split('\n');
|
|
97
|
+
if (!sourceHistory && (roots.length === 0 || roots.some((rootId) => !/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/i.test(rootId)))) {
|
|
98
|
+
fail('malformed_git_output', 'git rev-list root did not return commit ids');
|
|
99
|
+
}
|
|
100
|
+
const output = (sourceHistory || roots[0]).toLowerCase();
|
|
101
|
+
if (!/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/.test(output)) fail('malformed_git_output', 'git source base did not return a commit id');
|
|
89
102
|
return output;
|
|
90
103
|
}
|
|
91
104
|
|
|
@@ -136,15 +149,21 @@ function parseStatus(bytes) {
|
|
|
136
149
|
return entries;
|
|
137
150
|
}
|
|
138
151
|
|
|
152
|
+
// Generated runtime state, not source evidence. Shared with the receipt binding
|
|
153
|
+
// selector so the gate's own cache writes cannot be read as a dirty worktree.
|
|
154
|
+
const RUNTIME_STATE_ROOTS = Object.freeze([
|
|
155
|
+
'.git', 'node_modules',
|
|
156
|
+
'.claude/hooks/.logs', '.codex/hooks/.logs', '.omp/hooks/.logs',
|
|
157
|
+
'.claude/.logs', '.codex/.logs', '.omp/.logs',
|
|
158
|
+
'.claude/runtime.json', '.codex/runtime.json', '.omp/runtime.json',
|
|
159
|
+
]);
|
|
160
|
+
|
|
139
161
|
function excluded(root, absolute, specsRoot) {
|
|
140
162
|
const relative = path.relative(root, absolute).split(path.sep).join('/');
|
|
141
163
|
const specsRelative = path.relative(root, specsRoot);
|
|
142
164
|
// These roots are generated runtime state, not source evidence. The selected
|
|
143
165
|
// canonical specs root is excluded above to avoid receipt/state recursion.
|
|
144
|
-
const roots =
|
|
145
|
-
'.git', 'node_modules', '.claude/hooks/.logs', '.codex/hooks/.logs',
|
|
146
|
-
'.claude/.logs', '.codex/.logs', '.claude/runtime.json', '.codex/runtime.json',
|
|
147
|
-
];
|
|
166
|
+
const roots = RUNTIME_STATE_ROOTS;
|
|
148
167
|
if (relative === specsRelative || relative.startsWith(`${specsRelative}/`)) return true;
|
|
149
168
|
return roots.some((entry) => relative === entry || relative.startsWith(`${entry}/`));
|
|
150
169
|
}
|
|
@@ -211,11 +230,11 @@ function identity(input) {
|
|
|
211
230
|
|
|
212
231
|
function deriveRuntimeProvenance(input) {
|
|
213
232
|
const current = identity(input);
|
|
214
|
-
const base = gitBase(current.root);
|
|
233
|
+
const base = gitBase(current.root, current.specsRoot);
|
|
215
234
|
const first = manifest(current.root, current.specsRoot);
|
|
216
235
|
const head = manifestDigest(first);
|
|
217
236
|
const second = manifest(current.root, current.specsRoot);
|
|
218
|
-
if (JSON.stringify(first) !== JSON.stringify(second) || gitBase(current.root) !== base) fail('worktree_race', 'checkout changed during provenance capture');
|
|
237
|
+
if (JSON.stringify(first) !== JSON.stringify(second) || gitBase(current.root, current.specsRoot) !== base) fail('worktree_race', 'checkout changed during provenance capture');
|
|
219
238
|
const stable = {
|
|
220
239
|
schema_version: CONTEXT_SCHEMA_VERSION,
|
|
221
240
|
project_root: current.root,
|
|
@@ -302,6 +321,7 @@ function runCli(argv = process.argv.slice(2)) {
|
|
|
302
321
|
if (require.main === module) process.exitCode = runCli();
|
|
303
322
|
|
|
304
323
|
module.exports = {
|
|
324
|
+
RUNTIME_STATE_ROOTS,
|
|
305
325
|
CONTEXT_SCHEMA_VERSION,
|
|
306
326
|
PROVENANCE_MODE,
|
|
307
327
|
ProvenanceError,
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const { spawnSync } = require('child_process');
|
|
6
7
|
const { annotatedMarkdownLines } = require('./spec-resolver.cjs');
|
|
8
|
+
const { RUNTIME_STATE_ROOTS } = require('./provenance.cjs');
|
|
7
9
|
|
|
8
10
|
const EVIDENCE_HEADINGS = new Set([
|
|
9
11
|
'Evidence',
|
|
@@ -177,11 +179,67 @@ function identity(body) {
|
|
|
177
179
|
return crypto.createHash('sha256').update(JSON.stringify({ fields, artifacts })).digest('hex');
|
|
178
180
|
}
|
|
179
181
|
|
|
180
|
-
function
|
|
182
|
+
function tryGitBytes(root, args) {
|
|
183
|
+
const result = spawnSync('git', ['-C', root, ...args], { encoding: 'buffer', maxBuffer: 64 * 1024 * 1024 });
|
|
184
|
+
if (result.error || result.status !== 0) return null;
|
|
185
|
+
return result.stdout;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// A receipt is bound to live Base/Head while it is being written or altered.
|
|
189
|
+
// Once its task file matches the committed bytes and nothing outside the specs
|
|
190
|
+
// root is uncommitted, later commits must not reopen it: rebinding every done
|
|
191
|
+
// receipt on every Stop made each unrelated commit a refresh of all of them.
|
|
192
|
+
// Every state that cannot be read as committed-and-clean keeps the binding,
|
|
193
|
+
// including an unborn HEAD, a staged-only file, and any git failure. The git
|
|
194
|
+
// calls pin the project root; a working-directory-relative status query run
|
|
195
|
+
// from inside the specs root reports a dirty tree as clean.
|
|
196
|
+
function receiptBindingMode(featureDir, taskPath, taskBytes, runtimeContext) {
|
|
197
|
+
const root = runtimeContext && typeof runtimeContext.project_root === 'string' ? runtimeContext.project_root : null;
|
|
198
|
+
const specsRoot = runtimeContext && typeof runtimeContext.specs_root === 'string' ? runtimeContext.specs_root : null;
|
|
199
|
+
if (!root || !specsRoot || !Buffer.isBuffer(taskBytes)) return 'binding';
|
|
200
|
+
let canonicalFeature;
|
|
201
|
+
try { canonicalFeature = fs.realpathSync(featureDir); } catch { return 'binding'; }
|
|
202
|
+
const toPosix = (value) => value.split(path.sep).join('/');
|
|
203
|
+
const taskRelative = toPosix(path.relative(root, path.join(canonicalFeature, taskPath)));
|
|
204
|
+
const specsRelative = toPosix(path.relative(root, specsRoot));
|
|
205
|
+
const escapes = (value) => !value || value === '..' || value.startsWith('../') || path.isAbsolute(value);
|
|
206
|
+
if (escapes(taskRelative) || escapes(specsRelative)) return 'binding';
|
|
207
|
+
// `--filters` applies the same smudge/eol conversion the worktree copy has,
|
|
208
|
+
// so a checkout that converts line endings can still reach structure mode.
|
|
209
|
+
const committed = tryGitBytes(root, ['cat-file', '--filters', `HEAD:${taskRelative}`]);
|
|
210
|
+
if (committed === null || !committed.equals(taskBytes)) return 'binding';
|
|
211
|
+
// The same roots the provenance manifest calls generated runtime state rather
|
|
212
|
+
// than source evidence. Without them the gate defeats itself: writing its own
|
|
213
|
+
// cache dirties the tree, so a project that tracks its runtime directory could
|
|
214
|
+
// never reach structure mode.
|
|
215
|
+
const outsideSpecs = ['--', '.', `:(exclude,literal)${specsRelative}`,
|
|
216
|
+
...RUNTIME_STATE_ROOTS.map((entry) => `:(exclude,literal)${entry}`)];
|
|
217
|
+
const status = tryGitBytes(root, ['status', '--porcelain', '--untracked-files=all', ...outsideSpecs]);
|
|
218
|
+
if (status === null || status.length !== 0) return 'binding';
|
|
219
|
+
// `git status` obeys skip-worktree and assume-unchanged, so a tracked file can
|
|
220
|
+
// be modified while the tree reports clean. Require every index entry outside
|
|
221
|
+
// the specs root to carry the plain cached tag `H`; any other tag, including
|
|
222
|
+
// `S` and the lowercase assume-unchanged tags, keeps the binding.
|
|
223
|
+
const indexTags = tryGitBytes(root, ['ls-files', '-v', ...outsideSpecs]);
|
|
224
|
+
if (indexTags === null) return 'binding';
|
|
225
|
+
const hidden = indexTags.toString('utf8').split('\n')
|
|
226
|
+
.filter((line) => line.trim() !== '')
|
|
227
|
+
.some((line) => line[0] !== 'H');
|
|
228
|
+
if (hidden) return 'binding';
|
|
229
|
+
return 'structure';
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function canonicalFailures(body, task, runtimeContext, policy, { bindProvenance = true } = {}) {
|
|
181
233
|
if (!policy || typeof policy.validateCanonicalReceipt !== 'function') return ['validator_unavailable'];
|
|
182
234
|
const options = typeof policy.receiptValidatorOptions === 'function'
|
|
183
235
|
? policy.receiptValidatorOptions(task || {}, { runtimeContext, requireProvenanceBinding: true })
|
|
184
236
|
: { requireProvenanceBinding: true };
|
|
237
|
+
// Structure-only mode drops the live Base/Head comparison and nothing else.
|
|
238
|
+
// It is granted only while a trusted artifact root survived, because
|
|
239
|
+
// withholding the runtime context would also silence artifact byte checks.
|
|
240
|
+
if (!bindProvenance && typeof options.artifactRoot === 'string' && options.artifactRoot.trim() !== '') {
|
|
241
|
+
return policy.validateCanonicalReceipt(body, { ...options, expectedProvenance: null, requireProvenanceBinding: false });
|
|
242
|
+
}
|
|
185
243
|
return policy.validateCanonicalReceipt(body, options);
|
|
186
244
|
}
|
|
187
245
|
|
|
@@ -261,12 +319,15 @@ function checkWorkflowTaskReceipt(featureDir, taskPath, runtimeContext, policy)
|
|
|
261
319
|
const options = typeof policy?.receiptValidatorOptions === 'function'
|
|
262
320
|
? policy.receiptValidatorOptions({}, { runtimeContext, requireProvenanceBinding: true })
|
|
263
321
|
: { requireProvenanceBinding: true };
|
|
264
|
-
|
|
265
|
-
|
|
322
|
+
const requested = receiptBindingMode(featureDir, taskPath, taskFile.bytes, runtimeContext);
|
|
323
|
+
const artifactRootAvailable = typeof options.artifactRoot === 'string' && options.artifactRoot.trim() !== '';
|
|
324
|
+
const bindProvenance = !(requested === 'structure' && artifactRootAvailable);
|
|
325
|
+
failures.push(...canonicalFailures(canonicalBody, {}, runtimeContext, policy, { bindProvenance }));
|
|
326
|
+
failures.push(...canonicalFailures(body, {}, runtimeContext, policy, { bindProvenance })
|
|
266
327
|
.filter((failure) => ['placeholder', 'verification_state', 'exit_result'].includes(failure)));
|
|
267
328
|
failures.push(...workflowCommandFailures(taskText, canonicalBody));
|
|
268
329
|
if (!hasNonEmptyFencedBlock(body)) failures.push('command_output');
|
|
269
|
-
if (!options.expectedProvenance) failures.push('provenance');
|
|
330
|
+
if (bindProvenance && !options.expectedProvenance) failures.push('provenance');
|
|
270
331
|
}
|
|
271
332
|
return {
|
|
272
333
|
status: failures.length === 0 ? 'ok' : 'invalid',
|
|
@@ -1733,7 +1733,7 @@ function evaluateFlashPromotion(task, verdict, proof = 'Verification: PASS', cap
|
|
|
1733
1733
|
status: 'in_progress',
|
|
1734
1734
|
receipt: 'FLASH_UNVERIFIED',
|
|
1735
1735
|
blocker: normalized.diagnostic === 'NO_TESTS'
|
|
1736
|
-
? 'awaiting test proof from /
|
|
1736
|
+
? 'awaiting test proof from /cf:test <feature>'
|
|
1737
1737
|
: normalized.diagnostic === 'PARTIAL'
|
|
1738
1738
|
? 'verification is partial; awaiting complete test proof'
|
|
1739
1739
|
: `verification returned ${normalized.verdict}`,
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/status.cjs\"",
|
|
5
5
|
"padding": 0
|
|
6
6
|
},
|
|
7
|
+
"outputStyle": "Junior Developer Mode (Level 1)",
|
|
7
8
|
"hooks": {
|
|
8
9
|
"SessionStart": [
|
|
9
10
|
{
|
|
@@ -35,9 +36,24 @@
|
|
|
35
36
|
]
|
|
36
37
|
}
|
|
37
38
|
],
|
|
39
|
+
"PreCompact": [
|
|
40
|
+
{
|
|
41
|
+
"matcher": "*",
|
|
42
|
+
"hooks": [
|
|
43
|
+
{
|
|
44
|
+
"type": "command",
|
|
45
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/precompact.cjs\""
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
],
|
|
38
50
|
"UserPromptSubmit": [
|
|
39
51
|
{
|
|
40
52
|
"hooks": [
|
|
53
|
+
{
|
|
54
|
+
"type": "command",
|
|
55
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/secret-output-guardrail.cjs\""
|
|
56
|
+
},
|
|
41
57
|
{
|
|
42
58
|
"type": "command",
|
|
43
59
|
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/rules.cjs\""
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: cf:agent-browser
|
|
3
3
|
description: AI-optimized browser automation CLI with context-efficient snapshots. Use for long autonomous sessions, self-verifying workflows, video recording, and cloud browser testing (Browserbase).
|
|
4
4
|
license: Apache-2.0
|
|
5
5
|
user-invocable: true
|