@garygentry/feature-forge 0.2.13 → 0.3.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 +6 -3
- package/adapters/GENERATION-REPORT.md +20 -0
- package/adapters/claude/.feature-forge-bundle.json +1 -1
- package/adapters/claude/references/epic-manifest-schema.json +5 -0
- package/adapters/claude/references/forge-config-schema.json +2 -2
- package/adapters/claude/references/shared-conventions.md +12 -0
- package/adapters/claude/references/stacks/_generic.md +21 -0
- package/adapters/claude/references/stacks/go.md +19 -0
- package/adapters/claude/references/stacks/python.md +21 -0
- package/adapters/claude/references/stacks/rust.md +19 -0
- package/adapters/claude/references/stacks/typescript.md +23 -0
- package/adapters/claude/scripts/epic-manifest.py +75 -5
- package/adapters/claude/scripts/forge-root.sh +102 -14
- package/adapters/claude/scripts/forge-session.py +68 -9
- package/adapters/claude/skills/forge/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-0-epic/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-1-prd/SKILL.md +2 -0
- package/adapters/claude/skills/forge-1-prd/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-2-tech/SKILL.md +2 -0
- package/adapters/claude/skills/forge-2-tech/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/_generic.md +21 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/go.md +19 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/python.md +21 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/rust.md +19 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/typescript.md +23 -0
- package/adapters/claude/skills/forge-3-specs/SKILL.md +2 -0
- package/adapters/claude/skills/forge-3-specs/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/_generic.md +21 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/go.md +19 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/python.md +21 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/rust.md +19 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/typescript.md +23 -0
- package/adapters/claude/skills/forge-4-backlog/SKILL.md +5 -0
- package/adapters/claude/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
- package/adapters/claude/skills/forge-4-backlog/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-5-loop/SKILL.md +7 -9
- package/adapters/claude/skills/forge-5-loop/references/forge-config-schema.json +2 -2
- package/adapters/claude/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/claude/skills/forge-5-loop/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-6-docs/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-fix/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/claude/skills/forge-guide/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-guide/references/stacks/_generic.md +21 -0
- package/adapters/claude/skills/forge-guide/references/stacks/go.md +19 -0
- package/adapters/claude/skills/forge-guide/references/stacks/python.md +21 -0
- package/adapters/claude/skills/forge-guide/references/stacks/rust.md +19 -0
- package/adapters/claude/skills/forge-guide/references/stacks/typescript.md +23 -0
- package/adapters/claude/skills/forge-verify/SKILL.md +3 -3
- package/adapters/claude/skills/forge-verify/references/shared-conventions.md +12 -0
- package/adapters/claude/skills/forge-verify/references/verification-checklists.md +82 -3
- package/adapters/codex/.feature-forge-bundle.json +1 -1
- package/adapters/codex/references/epic-manifest-schema.json +5 -0
- package/adapters/codex/references/forge-config-schema.json +2 -2
- package/adapters/codex/references/shared-conventions.md +12 -0
- package/adapters/codex/references/stacks/_generic.md +21 -0
- package/adapters/codex/references/stacks/go.md +19 -0
- package/adapters/codex/references/stacks/python.md +21 -0
- package/adapters/codex/references/stacks/rust.md +19 -0
- package/adapters/codex/references/stacks/typescript.md +23 -0
- package/adapters/codex/scripts/epic-manifest.py +75 -5
- package/adapters/codex/scripts/forge-root.sh +102 -14
- package/adapters/codex/scripts/forge-session.py +68 -9
- package/adapters/codex/skills/forge/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-0-epic/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-1-prd/SKILL.md +2 -0
- package/adapters/codex/skills/forge-1-prd/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-2-tech/SKILL.md +2 -0
- package/adapters/codex/skills/forge-2-tech/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/_generic.md +21 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/go.md +19 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/python.md +21 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/rust.md +19 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/typescript.md +23 -0
- package/adapters/codex/skills/forge-3-specs/SKILL.md +2 -0
- package/adapters/codex/skills/forge-3-specs/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/_generic.md +21 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/go.md +19 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/python.md +21 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/rust.md +19 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/typescript.md +23 -0
- package/adapters/codex/skills/forge-4-backlog/SKILL.md +5 -0
- package/adapters/codex/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
- package/adapters/codex/skills/forge-4-backlog/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-5-loop/SKILL.md +7 -9
- package/adapters/codex/skills/forge-5-loop/references/forge-config-schema.json +2 -2
- package/adapters/codex/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/codex/skills/forge-5-loop/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-6-docs/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-fix/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/codex/skills/forge-guide/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-guide/references/stacks/_generic.md +21 -0
- package/adapters/codex/skills/forge-guide/references/stacks/go.md +19 -0
- package/adapters/codex/skills/forge-guide/references/stacks/python.md +21 -0
- package/adapters/codex/skills/forge-guide/references/stacks/rust.md +19 -0
- package/adapters/codex/skills/forge-guide/references/stacks/typescript.md +23 -0
- package/adapters/codex/skills/forge-verify/SKILL.md +3 -3
- package/adapters/codex/skills/forge-verify/references/shared-conventions.md +12 -0
- package/adapters/codex/skills/forge-verify/references/verification-checklists.md +82 -3
- package/adapters/copilot/.feature-forge-bundle.json +1 -1
- package/adapters/copilot/references/epic-manifest-schema.json +5 -0
- package/adapters/copilot/references/forge-config-schema.json +2 -2
- package/adapters/copilot/references/shared-conventions.md +12 -0
- package/adapters/copilot/references/stacks/_generic.md +21 -0
- package/adapters/copilot/references/stacks/go.md +19 -0
- package/adapters/copilot/references/stacks/python.md +21 -0
- package/adapters/copilot/references/stacks/rust.md +19 -0
- package/adapters/copilot/references/stacks/typescript.md +23 -0
- package/adapters/copilot/scripts/epic-manifest.py +75 -5
- package/adapters/copilot/scripts/forge-root.sh +102 -14
- package/adapters/copilot/scripts/forge-session.py +68 -9
- package/adapters/copilot/skills/forge/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-0-epic/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-1-prd/forge-1-prd.md +2 -0
- package/adapters/copilot/skills/forge-1-prd/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-2-tech/forge-2-tech.md +2 -0
- package/adapters/copilot/skills/forge-2-tech/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/_generic.md +21 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/go.md +19 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/python.md +21 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/rust.md +19 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/typescript.md +23 -0
- package/adapters/copilot/skills/forge-3-specs/forge-3-specs.md +2 -0
- package/adapters/copilot/skills/forge-3-specs/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/_generic.md +21 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/go.md +19 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/python.md +21 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/rust.md +19 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/typescript.md +23 -0
- package/adapters/copilot/skills/forge-4-backlog/forge-4-backlog.md +5 -0
- package/adapters/copilot/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
- package/adapters/copilot/skills/forge-4-backlog/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-5-loop/forge-5-loop.md +7 -9
- package/adapters/copilot/skills/forge-5-loop/references/forge-config-schema.json +2 -2
- package/adapters/copilot/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/copilot/skills/forge-5-loop/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-6-docs/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-fix/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/copilot/skills/forge-guide/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/_generic.md +21 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/go.md +19 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/python.md +21 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/rust.md +19 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/typescript.md +23 -0
- package/adapters/copilot/skills/forge-verify/forge-verify.md +3 -3
- package/adapters/copilot/skills/forge-verify/references/shared-conventions.md +12 -0
- package/adapters/copilot/skills/forge-verify/references/verification-checklists.md +82 -3
- package/adapters/cursor/.feature-forge-bundle.json +1 -1
- package/adapters/cursor/references/epic-manifest-schema.json +5 -0
- package/adapters/cursor/references/forge-config-schema.json +2 -2
- package/adapters/cursor/references/shared-conventions.md +12 -0
- package/adapters/cursor/references/stacks/_generic.md +21 -0
- package/adapters/cursor/references/stacks/go.md +19 -0
- package/adapters/cursor/references/stacks/python.md +21 -0
- package/adapters/cursor/references/stacks/rust.md +19 -0
- package/adapters/cursor/references/stacks/typescript.md +23 -0
- package/adapters/cursor/scripts/epic-manifest.py +75 -5
- package/adapters/cursor/scripts/forge-root.sh +102 -14
- package/adapters/cursor/scripts/forge-session.py +68 -9
- package/adapters/cursor/skills/forge/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-0-epic/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-1-prd/forge-1-prd.mdc +2 -0
- package/adapters/cursor/skills/forge-1-prd/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-2-tech/forge-2-tech.mdc +2 -0
- package/adapters/cursor/skills/forge-2-tech/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/_generic.md +21 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/go.md +19 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/python.md +21 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/rust.md +19 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/typescript.md +23 -0
- package/adapters/cursor/skills/forge-3-specs/forge-3-specs.mdc +2 -0
- package/adapters/cursor/skills/forge-3-specs/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/_generic.md +21 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/go.md +19 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/python.md +21 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/rust.md +19 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/typescript.md +23 -0
- package/adapters/cursor/skills/forge-4-backlog/forge-4-backlog.mdc +5 -0
- package/adapters/cursor/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
- package/adapters/cursor/skills/forge-4-backlog/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-5-loop/forge-5-loop.mdc +7 -9
- package/adapters/cursor/skills/forge-5-loop/references/forge-config-schema.json +2 -2
- package/adapters/cursor/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/cursor/skills/forge-5-loop/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-6-docs/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-fix/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/cursor/skills/forge-guide/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/_generic.md +21 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/go.md +19 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/python.md +21 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/rust.md +19 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/typescript.md +23 -0
- package/adapters/cursor/skills/forge-verify/forge-verify.mdc +3 -3
- package/adapters/cursor/skills/forge-verify/references/shared-conventions.md +12 -0
- package/adapters/cursor/skills/forge-verify/references/verification-checklists.md +82 -3
- package/adapters/gemini/.feature-forge-bundle.json +1 -1
- package/adapters/gemini/gemini-extension.json +1 -1
- package/adapters/gemini/references/epic-manifest-schema.json +5 -0
- package/adapters/gemini/references/forge-config-schema.json +2 -2
- package/adapters/gemini/references/shared-conventions.md +12 -0
- package/adapters/gemini/references/stacks/_generic.md +21 -0
- package/adapters/gemini/references/stacks/go.md +19 -0
- package/adapters/gemini/references/stacks/python.md +21 -0
- package/adapters/gemini/references/stacks/rust.md +19 -0
- package/adapters/gemini/references/stacks/typescript.md +23 -0
- package/adapters/gemini/scripts/epic-manifest.py +75 -5
- package/adapters/gemini/scripts/forge-root.sh +102 -14
- package/adapters/gemini/scripts/forge-session.py +68 -9
- package/adapters/gemini/skills/forge/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-0-epic/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-1-prd/forge-1-prd.md +2 -0
- package/adapters/gemini/skills/forge-1-prd/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-2-tech/forge-2-tech.md +2 -0
- package/adapters/gemini/skills/forge-2-tech/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/_generic.md +21 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/go.md +19 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/python.md +21 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/rust.md +19 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/typescript.md +23 -0
- package/adapters/gemini/skills/forge-3-specs/forge-3-specs.md +2 -0
- package/adapters/gemini/skills/forge-3-specs/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/_generic.md +21 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/go.md +19 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/python.md +21 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/rust.md +19 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/typescript.md +23 -0
- package/adapters/gemini/skills/forge-4-backlog/forge-4-backlog.md +5 -0
- package/adapters/gemini/skills/forge-4-backlog/references/forge-config-schema.json +2 -2
- package/adapters/gemini/skills/forge-4-backlog/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-5-loop/forge-5-loop.md +7 -9
- package/adapters/gemini/skills/forge-5-loop/references/forge-config-schema.json +2 -2
- package/adapters/gemini/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/gemini/skills/forge-5-loop/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-6-docs/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-fix/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/gemini/skills/forge-guide/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/_generic.md +21 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/go.md +19 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/python.md +21 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/rust.md +19 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/typescript.md +23 -0
- package/adapters/gemini/skills/forge-verify/forge-verify.md +3 -3
- package/adapters/gemini/skills/forge-verify/references/shared-conventions.md +12 -0
- package/adapters/gemini/skills/forge-verify/references/verification-checklists.md +82 -3
- package/adapters/pi/.feature-forge-bundle.json +6 -0
- package/adapters/pi/agents/forge-researcher.md +139 -0
- package/adapters/pi/agents/forge-spec-writer.md +116 -0
- package/adapters/pi/agents/forge-verifier.md +126 -0
- package/adapters/pi/extensions/ask-user-question/LICENSE +21 -0
- package/adapters/pi/extensions/ask-user-question/README.md +91 -0
- package/adapters/pi/extensions/ask-user-question/ask-user-question.ts +298 -0
- package/adapters/pi/extensions/ask-user-question/config.ts +78 -0
- package/adapters/pi/extensions/ask-user-question/events.ts +57 -0
- package/adapters/pi/extensions/ask-user-question/index.ts +61 -0
- package/adapters/pi/extensions/ask-user-question/locales/de.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/en.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/es.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/fr.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/pt-BR.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/pt.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/ru.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/uk.json +27 -0
- package/adapters/pi/extensions/ask-user-question/locales/zh.json +29 -0
- package/adapters/pi/extensions/ask-user-question/reconcile.ts +49 -0
- package/adapters/pi/extensions/ask-user-question/rpc-fallback.ts +168 -0
- package/adapters/pi/extensions/ask-user-question/state/build-questionnaire.ts +302 -0
- package/adapters/pi/extensions/ask-user-question/state/i18n-bridge.ts +53 -0
- package/adapters/pi/extensions/ask-user-question/state/key-router.ts +277 -0
- package/adapters/pi/extensions/ask-user-question/state/questionnaire-session.ts +234 -0
- package/adapters/pi/extensions/ask-user-question/state/row-intent.ts +145 -0
- package/adapters/pi/extensions/ask-user-question/state/selectors/contract.ts +26 -0
- package/adapters/pi/extensions/ask-user-question/state/selectors/derivations.ts +42 -0
- package/adapters/pi/extensions/ask-user-question/state/selectors/focus.ts +19 -0
- package/adapters/pi/extensions/ask-user-question/state/selectors/projections.ts +101 -0
- package/adapters/pi/extensions/ask-user-question/state/state-reducer.ts +292 -0
- package/adapters/pi/extensions/ask-user-question/state/state.ts +55 -0
- package/adapters/pi/extensions/ask-user-question/tool/format-answer.ts +31 -0
- package/adapters/pi/extensions/ask-user-question/tool/response-envelope.ts +49 -0
- package/adapters/pi/extensions/ask-user-question/tool/types.ts +147 -0
- package/adapters/pi/extensions/ask-user-question/tool/validate-questionnaire.ts +58 -0
- package/adapters/pi/extensions/ask-user-question/vendor-config-shim.ts +65 -0
- package/adapters/pi/extensions/ask-user-question/view/component-binding.ts +47 -0
- package/adapters/pi/extensions/ask-user-question/view/components/inline-input.ts +98 -0
- package/adapters/pi/extensions/ask-user-question/view/components/multi-select-view.ts +193 -0
- package/adapters/pi/extensions/ask-user-question/view/components/option-list-view.ts +70 -0
- package/adapters/pi/extensions/ask-user-question/view/components/preview/markdown-content-cache.ts +79 -0
- package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-block-renderer.ts +111 -0
- package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-box-renderer.ts +88 -0
- package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-layout-decider.ts +202 -0
- package/adapters/pi/extensions/ask-user-question/view/components/preview/preview-pane.ts +228 -0
- package/adapters/pi/extensions/ask-user-question/view/components/submit-picker.ts +67 -0
- package/adapters/pi/extensions/ask-user-question/view/components/tab-bar.ts +59 -0
- package/adapters/pi/extensions/ask-user-question/view/components/wrapping-select.ts +293 -0
- package/adapters/pi/extensions/ask-user-question/view/dialog-builder.ts +224 -0
- package/adapters/pi/extensions/ask-user-question/view/props-adapter.ts +125 -0
- package/adapters/pi/extensions/ask-user-question/view/stateful-view.ts +26 -0
- package/adapters/pi/extensions/ask-user-question/view/tab-components.ts +18 -0
- package/adapters/pi/extensions/ask-user-question/view/tab-content-strategy.ts +252 -0
- package/adapters/pi/package.json +26 -0
- package/adapters/pi/references/epic-manifest-schema.json +125 -0
- package/adapters/pi/references/forge-config-schema.json +236 -0
- package/adapters/pi/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/references/portable-root.md +71 -0
- package/adapters/pi/references/process-overview.md +143 -0
- package/adapters/pi/references/ralph-loop-contract.md +221 -0
- package/adapters/pi/references/shared-conventions.md +295 -0
- package/adapters/pi/references/skill-frontmatter.schema.json +17 -0
- package/adapters/pi/references/stack-resolution.md +54 -0
- package/adapters/pi/references/stacks/_generic.md +111 -0
- package/adapters/pi/references/stacks/go.md +157 -0
- package/adapters/pi/references/stacks/python.md +184 -0
- package/adapters/pi/references/stacks/rust.md +170 -0
- package/adapters/pi/references/stacks/typescript.md +134 -0
- package/adapters/pi/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/references/templates/specs-hygiene/AGENTS.md +32 -0
- package/adapters/pi/references/templates/specs-hygiene/CLAUDE.md +31 -0
- package/adapters/pi/references/vendor-construct-inventory.md +50 -0
- package/adapters/pi/scripts/epic-manifest.py +1694 -0
- package/adapters/pi/scripts/forge-bootstrap.py +1070 -0
- package/adapters/pi/scripts/forge-init.sh +58 -0
- package/adapters/pi/scripts/forge-root.sh +179 -0
- package/adapters/pi/scripts/forge-session.py +1888 -0
- package/adapters/pi/scripts/validate-traceability.py +150 -0
- package/adapters/pi/skills/forge/SKILL.md +243 -0
- package/adapters/pi/skills/forge/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge/references/process-overview.md +143 -0
- package/adapters/pi/skills/forge/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-0-epic/SKILL.md +308 -0
- package/adapters/pi/skills/forge-0-epic/references/edit-mode.md +266 -0
- package/adapters/pi/skills/forge-0-epic/references/epic-manifest-subcommands.md +75 -0
- package/adapters/pi/skills/forge-0-epic/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge-0-epic/references/portable-root.md +71 -0
- package/adapters/pi/skills/forge-0-epic/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-0-epic/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-1-prd/SKILL.md +164 -0
- package/adapters/pi/skills/forge-1-prd/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge-1-prd/references/prd-template.md +106 -0
- package/adapters/pi/skills/forge-1-prd/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-1-prd/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-2-tech/SKILL.md +225 -0
- package/adapters/pi/skills/forge-2-tech/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge-2-tech/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-2-tech/references/stack-discovery-checklist.md +95 -0
- package/adapters/pi/skills/forge-2-tech/references/stack-resolution.md +54 -0
- package/adapters/pi/skills/forge-2-tech/references/stacks/_generic.md +111 -0
- package/adapters/pi/skills/forge-2-tech/references/stacks/go.md +157 -0
- package/adapters/pi/skills/forge-2-tech/references/stacks/python.md +184 -0
- package/adapters/pi/skills/forge-2-tech/references/stacks/rust.md +170 -0
- package/adapters/pi/skills/forge-2-tech/references/stacks/typescript.md +134 -0
- package/adapters/pi/skills/forge-2-tech/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-3-specs/SKILL.md +178 -0
- package/adapters/pi/skills/forge-3-specs/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge-3-specs/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-3-specs/references/spec-archetypes.md +106 -0
- package/adapters/pi/skills/forge-3-specs/references/spec-examples.md +71 -0
- package/adapters/pi/skills/forge-3-specs/references/stacks/_generic.md +111 -0
- package/adapters/pi/skills/forge-3-specs/references/stacks/go.md +157 -0
- package/adapters/pi/skills/forge-3-specs/references/stacks/python.md +184 -0
- package/adapters/pi/skills/forge-3-specs/references/stacks/rust.md +170 -0
- package/adapters/pi/skills/forge-3-specs/references/stacks/typescript.md +134 -0
- package/adapters/pi/skills/forge-3-specs/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-4-backlog/SKILL.md +175 -0
- package/adapters/pi/skills/forge-4-backlog/references/forge-config-schema.json +236 -0
- package/adapters/pi/skills/forge-4-backlog/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge-4-backlog/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-4-backlog/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-5-loop/SKILL.md +314 -0
- package/adapters/pi/skills/forge-5-loop/references/forge-config-schema.json +236 -0
- package/adapters/pi/skills/forge-5-loop/references/ralph-loop-contract.md +221 -0
- package/adapters/pi/skills/forge-5-loop/references/result-reporting.md +85 -0
- package/adapters/pi/skills/forge-5-loop/references/runner-contract.md +341 -0
- package/adapters/pi/skills/forge-5-loop/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-5-loop/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-6-docs/SKILL.md +202 -0
- package/adapters/pi/skills/forge-6-docs/references/doc-conventions.md +126 -0
- package/adapters/pi/skills/forge-6-docs/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge-6-docs/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-bootstrap/SKILL.md +250 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/ci/github-actions.yml +12 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/generic/run.sh +3 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/generic/test.sh +13 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/go/go.mod +3 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/go/main.go +12 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/go/main_test.go +11 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/hygiene/AGENTS.md +35 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/hygiene/CLAUDE.md +36 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/hygiene/README.md +11 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/licenses/Apache-2.0/LICENSE +198 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/licenses/MIT/LICENSE +21 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/python/pyproject.toml +24 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/__init__.py +5 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/main.py +13 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/python/tests/test_smoke.py +8 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/rust/Cargo.toml +15 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/rust/src/lib.rs +7 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/rust/src/main.rs +5 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/rust/tests/smoke.rs +6 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/package.json +15 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/src/index.ts +4 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/test/smoke.test.ts +6 -0
- package/adapters/pi/skills/forge-bootstrap/references/templates/typescript/tsconfig.json +14 -0
- package/adapters/pi/skills/forge-fix/SKILL.md +98 -0
- package/adapters/pi/skills/forge-fix/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-fix/references/stage-exit-protocol.md +258 -0
- package/adapters/pi/skills/forge-guide/SKILL.md +192 -0
- package/adapters/pi/skills/forge-guide/references/forge-config-schema.json +236 -0
- package/adapters/pi/skills/forge-guide/references/process-overview.md +143 -0
- package/adapters/pi/skills/forge-guide/references/ralph-loop-contract.md +221 -0
- package/adapters/pi/skills/forge-guide/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-guide/references/stack-resolution.md +54 -0
- package/adapters/pi/skills/forge-guide/references/stacks/_generic.md +111 -0
- package/adapters/pi/skills/forge-guide/references/stacks/go.md +157 -0
- package/adapters/pi/skills/forge-guide/references/stacks/python.md +184 -0
- package/adapters/pi/skills/forge-guide/references/stacks/rust.md +170 -0
- package/adapters/pi/skills/forge-guide/references/stacks/typescript.md +134 -0
- package/adapters/pi/skills/forge-init/SKILL.md +72 -0
- package/adapters/pi/skills/forge-verify/SKILL.md +273 -0
- package/adapters/pi/skills/forge-verify/references/pipeline-state-schema.json +191 -0
- package/adapters/pi/skills/forge-verify/references/shared-conventions.md +295 -0
- package/adapters/pi/skills/forge-verify/references/verification-checklists.md +477 -0
- package/dist/agent-targets.d.ts +1 -1
- package/dist/agent-targets.js +23 -3
- package/dist/detect.d.ts +1 -1
- package/dist/detect.js +2 -1
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.js +2 -2
- package/dist/placements.js +5 -1
- package/dist/rauf.d.ts +4 -4
- package/dist/rauf.js +3 -3
- package/dist/types.d.ts +31 -6
- package/dist/types.js +6 -3
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Cross-agent installer for the [**feature-forge**](https://github.com/garygentry/feature-forge)
|
|
4
4
|
skill suite — an end-to-end feature-development pipeline that runs on any coding agent
|
|
5
|
-
(Claude, Codex, Copilot, Cursor, or
|
|
5
|
+
(Claude, Codex, Copilot, Cursor, Gemini, or Pi).
|
|
6
6
|
|
|
7
7
|
This package installs the canonical forge skills into the agents detected on your machine.
|
|
8
8
|
It is dependency-free and performs sandboxed, manifest-tracked writes.
|
|
@@ -33,7 +33,7 @@ npx @garygentry/feature-forge install --dry-run --json
|
|
|
33
33
|
|
|
34
34
|
| Flag | Description |
|
|
35
35
|
| ------------------- | --------------------------------------------------------------------- |
|
|
36
|
-
| `-a, --agent <id>` | Scope to one agent (`claude`/`codex`/`copilot`/`cursor`/`gemini`).
|
|
36
|
+
| `-a, --agent <id>` | Scope to one agent (`claude`/`codex`/`copilot`/`cursor`/`gemini`/`pi`). |
|
|
37
37
|
| `-g, --global` | Install into the user-level config dir (default: project-local). |
|
|
38
38
|
| `--symlink` | Symlink the bundle instead of copying (Windows always copies). |
|
|
39
39
|
| `--dry-run` | Print the planned actions without changing anything. |
|
|
@@ -52,6 +52,9 @@ Each agent's bundle is installed where that agent actually loads it (project sco
|
|
|
52
52
|
| copilot | `.github/feature-forge/` | managed block in `.github/copilot-instructions.md` |
|
|
53
53
|
| cursor | `.cursor/rules/feature-forge/` | — |
|
|
54
54
|
| gemini | `.gemini/extensions/feature-forge/` | — |
|
|
55
|
+
| pi | `.pi/skills/feature-forge/` | — |
|
|
56
|
+
|
|
57
|
+
For Pi, `--global` installs to `~/.pi/agent/skills/feature-forge/` and project scope installs to `./.pi/skills/feature-forge/`. The Pi bundle includes real `package.json` metadata plus the `AskUserQuestion` compatibility extension; run `/trust` (or use `--approve` in non-interactive tests) before relying on project-local Pi resources.
|
|
55
58
|
|
|
56
59
|
The Copilot block is delimited by `<!-- feature-forge:managed:start -->` /
|
|
57
60
|
`<!-- feature-forge:managed:end -->` sentinels and merged without disturbing the rest of the
|
|
@@ -71,7 +74,7 @@ Claude Code users can alternatively install via the plugin marketplace:
|
|
|
71
74
|
|
|
72
75
|
The default loop runner is [**rauf**](https://github.com/garygentry/rauf), published as
|
|
73
76
|
[`@garygentry/rauf`](https://www.npmjs.com/package/@garygentry/rauf). The installer runs a
|
|
74
|
-
read-only resolvability preflight on the pin (`@garygentry/rauf@0.
|
|
77
|
+
read-only resolvability preflight on the pin (`@garygentry/rauf@0.13.0`) and records it; pass
|
|
75
78
|
`--skip-rauf` to defer the check (e.g. offline installs). Install the rauf CLI itself with
|
|
76
79
|
`npx @garygentry/rauf` or its
|
|
77
80
|
[binary script](https://github.com/garygentry/rauf#install). See the
|
|
@@ -124,6 +124,26 @@ _No dropped constructs — every canonical construct is representable in this ag
|
|
|
124
124
|
| `skills/forge-verify/SKILL.md` | `argument-hint` | Gemini manifest hint field unconfirmed (TQ-1) |
|
|
125
125
|
| `skills/forge/SKILL.md` | `argument-hint` | Gemini manifest hint field unconfirmed (TQ-1) |
|
|
126
126
|
|
|
127
|
+
## pi
|
|
128
|
+
|
|
129
|
+
| Source | Construct | Reason |
|
|
130
|
+
|--------|-----------|--------|
|
|
131
|
+
| `agents/forge-researcher.md` | `sub-agent key 'model'` | Claude model aliases (opus/sonnet) are not Pi model ids; pin via subagents.agentOverrides.<name>.model in Pi settings instead (D1) |
|
|
132
|
+
| `agents/forge-spec-writer.md` | `sub-agent key 'model'` | Claude model aliases (opus/sonnet) are not Pi model ids; pin via subagents.agentOverrides.<name>.model in Pi settings instead (D1) |
|
|
133
|
+
| `agents/forge-verifier.md` | `sub-agent key 'model'` | Claude model aliases (opus/sonnet) are not Pi model ids; pin via subagents.agentOverrides.<name>.model in Pi settings instead (D1) |
|
|
134
|
+
| `skills/forge-0-epic/SKILL.md` | `argument-hint` | Pi skills have no invocation-hint field |
|
|
135
|
+
| `skills/forge-1-prd/SKILL.md` | `argument-hint` | Pi skills have no invocation-hint field |
|
|
136
|
+
| `skills/forge-2-tech/SKILL.md` | `argument-hint` | Pi skills have no invocation-hint field |
|
|
137
|
+
| `skills/forge-3-specs/SKILL.md` | `argument-hint` | Pi skills have no invocation-hint field |
|
|
138
|
+
| `skills/forge-4-backlog/SKILL.md` | `argument-hint` | Pi skills have no invocation-hint field |
|
|
139
|
+
| `skills/forge-5-loop/SKILL.md` | `argument-hint` | Pi skills have no invocation-hint field |
|
|
140
|
+
| `skills/forge-6-docs/SKILL.md` | `argument-hint` | Pi skills have no invocation-hint field |
|
|
141
|
+
| `skills/forge-bootstrap/SKILL.md` | `argument-hint` | Pi skills have no invocation-hint field |
|
|
142
|
+
| `skills/forge-fix/SKILL.md` | `argument-hint` | Pi skills have no invocation-hint field |
|
|
143
|
+
| `skills/forge-guide/SKILL.md` | `argument-hint` | Pi skills have no invocation-hint field |
|
|
144
|
+
| `skills/forge-verify/SKILL.md` | `argument-hint` | Pi skills have no invocation-hint field |
|
|
145
|
+
| `skills/forge/SKILL.md` | `argument-hint` | Pi skills have no invocation-hint field |
|
|
146
|
+
|
|
127
147
|
## Copied verbatim (no provenance header)
|
|
128
148
|
|
|
129
149
|
These files are transported byte-for-byte from canon into every `adapters/<agent>/` bundle and intentionally carry **no** provenance header (a header would break byte-identity / corrupt parsed files):
|
|
@@ -74,6 +74,11 @@
|
|
|
74
74
|
"type": "array",
|
|
75
75
|
"items": { "$ref": "#/definitions/consumedContract" },
|
|
76
76
|
"description": "What this feature relies on from its dependencies (REQ-EPIC-03). May be empty."
|
|
77
|
+
},
|
|
78
|
+
"mutatesShared": {
|
|
79
|
+
"type": "array",
|
|
80
|
+
"items": { "type": "string" },
|
|
81
|
+
"description": "OPTIONAL precision hint for cross-member coupling detection (#144): project-root-relative file/glob paths this feature writes or migrates that are shared across the epic (e.g. a corpus a sibling's tests pin). When present, forge-verify CHECK-E10 uses it directly; when absent, CHECK-E10 falls back to grepping specs/backlog for mutated paths. Not a dependency edge — declarative only. May be omitted entirely."
|
|
77
82
|
}
|
|
78
83
|
}
|
|
79
84
|
},
|
|
@@ -217,8 +217,8 @@
|
|
|
217
217
|
},
|
|
218
218
|
"installHint": {
|
|
219
219
|
"type": "string",
|
|
220
|
-
"default": "Provision rauf for a multi-agent setup with the cross-agent installer: `npx @garygentry/feature-forge install` (records the pinned @garygentry/rauf@0.
|
|
221
|
-
"description": "Shown when the runner BINARY is missing or too old (version gate fails, minRunnerVersion floor) — how to obtain/upgrade the CLI itself. Names two distinct binary-provisioning paths: (1) the cross-agent installer (`npx @garygentry/feature-forge install`, the multi-agent provisioning path that pins @garygentry/rauf@0.
|
|
220
|
+
"default": "Provision rauf for a multi-agent setup with the cross-agent installer: `npx @garygentry/feature-forge install` (records the pinned @garygentry/rauf@0.13.0 default). Or install/upgrade just the rauf CLI: `npx @garygentry/rauf@0.13.0 --version`, or `curl -fsSL https://raw.githubusercontent.com/garygentry/rauf/main/scripts/install-binary.sh | bash`.",
|
|
221
|
+
"description": "Shown when the runner BINARY is missing or too old (version gate fails, minRunnerVersion floor) — how to obtain/upgrade the CLI itself. Names two distinct binary-provisioning paths: (1) the cross-agent installer (`npx @garygentry/feature-forge install`, the multi-agent provisioning path that pins @garygentry/rauf@0.13.0), and (2) the direct rauf-CLI install/upgrade one-liner. Distinct from setupHint (which installs per-project artifacts); a version-gate failure is ALWAYS this hint, never setupHint."
|
|
222
222
|
},
|
|
223
223
|
"schemaVersion": {
|
|
224
224
|
"type": "string",
|
|
@@ -274,6 +274,18 @@ This write is **left uncommitted**: it is staged and committed as part of this s
|
|
|
274
274
|
|
|
275
275
|
**Incremental artifact tracking:** When a stage writes multiple files (e.g. forge-3-specs writing a suite of spec documents), update the `stages.{stage}.artifacts` array in `.pipeline-state.json` after writing each file — not just at stage completion. This is what makes the Interrupted inventory above precise about which files were successfully written.
|
|
276
276
|
|
|
277
|
+
## Stage-Completion Re-check
|
|
278
|
+
|
|
279
|
+
Invoke this block **at the head of any post-entry step that writes a stage artifact or runs the Scripted Stage Exit** — the Stage-Entry Guard runs only once, at the top of the skill. A **resumed or pasted mid-stage instruction** (e.g. "continue {stage}: write TRACEABILITY.md, run the stage exit") enters *below* the entry guard, so nothing re-checks completion before it overwrites a committed artifact and re-fires a finished exit — data-destructive if followed literally. This re-check is the idempotency backstop for that path. `{stage}` is the invoking skill's id.
|
|
280
|
+
|
|
281
|
+
**Re-read** `stages.{stage}` in `{resolvedFeatureDir}/.pipeline-state.json`, then classify by **provenance** — a legitimate completion runs in the same session that applied this stage's Entry Stamp; a replayed continuation finds a finished stage it did not produce:
|
|
282
|
+
|
|
283
|
+
1. **Proceed** when `stages.{stage}.status` is `"in-progress"` (this session's Entry Stamp — you are finishing the run you started) or absent/`pending`. Run the write / exit normally.
|
|
284
|
+
|
|
285
|
+
2. **Detect-and-refuse** when ALL of these hold: `stages.{stage}.status ∈ {"complete", "stale"}` **AND** the stage's artifacts (incl. `TRACEABILITY.md` for forge-3-specs) exist on disk **AND** a `commitHash` is recorded for the stage **AND** you did **not** author this stage earlier in the current session. This is a stale/replayed continuation of an already-finished, committed stage. Do **not** overwrite the artifact or re-run the exit. Route instead to the **Stage-Entry Guard**'s *Re-authoring* path: surface the same `AskUserQuestion` warning ("A completed {stage} artifact already exists for '{feature}' (v{n}{, marked stale}). Continuing will create a new version. Proceed?"). Only on explicit confirmation re-enter from the Entry Stamp (the version bumps at exit); otherwise **stop** and report that the stage is already complete — cite the recorded `commitHash` and offer `/feature-forge:forge {feature}` to see true state.
|
|
286
|
+
|
|
287
|
+
When you cannot confirm you authored the current run, treat it as a replay and refuse: a false refuse costs one confirmation click; a false proceed overwrites a committed artifact and re-churns a stage version. `--force` follows Force Mode (skip the gate, treat as a deliberate re-author).
|
|
288
|
+
|
|
277
289
|
## Force Mode
|
|
278
290
|
|
|
279
291
|
If the user passes `--force` as an argument, skip prerequisite validation with a warning:
|
|
@@ -79,6 +79,27 @@ Replace stack-specific checks with these generic equivalents:
|
|
|
79
79
|
| "JSDoc on every field" | "Documentation comments on every field" |
|
|
80
80
|
| "tsconfig.json extends root" | "Build configuration follows project conventions" |
|
|
81
81
|
|
|
82
|
+
### Runtime Entrypoints & Bootstrap-Wiring Sites
|
|
83
|
+
|
|
84
|
+
Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
|
|
85
|
+
`CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
|
|
86
|
+
Since no dedicated profile applies, identify these by role rather than exact filename:
|
|
87
|
+
|
|
88
|
+
- **Runtime entrypoints (a legitimate non-test call site):** the process/main entry the build's
|
|
89
|
+
run command actually launches (from the CI/`Makefile`/manifest "start" target), an HTTP/RPC handler
|
|
90
|
+
or route, a scheduled/worker task, or a CLI subcommand — **not** a file in the project's test
|
|
91
|
+
directory or matching its test-file convention.
|
|
92
|
+
- **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** any
|
|
93
|
+
module/hook the framework runs **once per process before serving** — a startup/lifecycle hook, an
|
|
94
|
+
import-time initializer, a global preload, or a shared test-setup module that bootstraps production
|
|
95
|
+
graphs. Side effects here run on every process start (and, under a dev/watch runtime, on every
|
|
96
|
+
reload).
|
|
97
|
+
- **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** database/ORM
|
|
98
|
+
clients and connection pools, message/queue clients, telemetry/observability SDKs, or a broad
|
|
99
|
+
whole-service-layer import. An init pulling any of these into a universal bootstrap entry is the
|
|
100
|
+
CHECK-I23 pattern — recommend lazy initialization at the first handler/worker that needs the graph,
|
|
101
|
+
not eagerly at process start.
|
|
102
|
+
|
|
82
103
|
## Acceptance Criteria Patterns
|
|
83
104
|
|
|
84
105
|
Use these placeholder patterns in backlog items. Replace `{typeCheckCommand}`, `{testCommand}`, and `{module}` with values from `forge.config.json` and the project structure:
|
|
@@ -84,6 +84,25 @@ When examining a Go project, check for:
|
|
|
84
84
|
- **Formatting**: `gofmt -l .` or `goimports -l .` (should produce no output)
|
|
85
85
|
- **Module tidiness**: `go mod tidy` (ensures `go.mod` and `go.sum` are consistent)
|
|
86
86
|
|
|
87
|
+
### Runtime Entrypoints & Bootstrap-Wiring Sites
|
|
88
|
+
|
|
89
|
+
Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
|
|
90
|
+
`CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
|
|
91
|
+
|
|
92
|
+
- **Runtime entrypoints (a legitimate non-test call site):** `func main()` in a `package main` under
|
|
93
|
+
`cmd/…` or the module root, an HTTP handler registered on a `*http.ServeMux` / router, a gRPC service
|
|
94
|
+
registration, or a worker/consumer goroutine started from `main` — not a `*_test.go` file.
|
|
95
|
+
- **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** a package-level
|
|
96
|
+
`func init()` that constructs heavy clients, package-level `var` initializers that dial/connect at
|
|
97
|
+
import time, or a single `bootstrap`/`wire` module `main` calls before serving. `init()` and
|
|
98
|
+
package-var side effects run on **every** binary start, before `main` gets control.
|
|
99
|
+
- **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB drivers/pools
|
|
100
|
+
(`database/sql` + a driver, `pgx`, `gorm`, `mongo-driver`), message/queue clients (`sarama`/Kafka,
|
|
101
|
+
`amqp`, `go-redis`), telemetry SDKs (`go.opentelemetry.io/*`, `sentry-go`), or a broad internal
|
|
102
|
+
service package. An `init()` or package-var that dials any of these is the CHECK-I23 pattern —
|
|
103
|
+
recommend a `sync.Once` lazy constructor invoked from the first handler that needs it, not an
|
|
104
|
+
eager package-level connect.
|
|
105
|
+
|
|
87
106
|
## Testing
|
|
88
107
|
|
|
89
108
|
- **Framework**: `testing` stdlib package
|
|
@@ -111,6 +111,27 @@ async def refresh_session_token(
|
|
|
111
111
|
- **Import validation**: `mypy` with `--strict` or `--disallow-untyped-defs` catches missing type annotations
|
|
112
112
|
- **Module export validation**: `__all__` lists in `__init__.py` match spec's public API
|
|
113
113
|
|
|
114
|
+
### Runtime Entrypoints & Bootstrap-Wiring Sites
|
|
115
|
+
|
|
116
|
+
Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
|
|
117
|
+
`CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
|
|
118
|
+
|
|
119
|
+
- **Runtime entrypoints (a legitimate non-test call site):** a `if __name__ == "__main__":` block, a
|
|
120
|
+
`[project.scripts]` console-script target in `pyproject.toml`, an ASGI/WSGI app object
|
|
121
|
+
(`app = FastAPI()` / Django `wsgi.py` / `asgi.py`), a route/view/handler, a Celery/RQ task module, or
|
|
122
|
+
a management command — not a `test_*.py` / `conftest.py`.
|
|
123
|
+
- **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** a package
|
|
124
|
+
`__init__.py` that eagerly instantiates heavy clients at import time, a framework startup hook
|
|
125
|
+
(FastAPI `lifespan` / `@app.on_event("startup")`, Django `AppConfig.ready()`, a Gunicorn/uvicorn
|
|
126
|
+
`--preload` module), or a `conftest.py` that bootstraps production graphs. Import-time side effects
|
|
127
|
+
here run on every process start.
|
|
128
|
+
- **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB/ORM engines
|
|
129
|
+
(`sqlalchemy.create_engine`, `psycopg`, Django ORM, `pymongo`, `redis`), task queues (`celery`,
|
|
130
|
+
`rq`, `kafka`), telemetry SDKs (`opentelemetry`, `sentry_sdk`), or a whole-service-layer package
|
|
131
|
+
import. An init pulling any of these at a universal entry is the CHECK-I23 pattern — recommend lazy
|
|
132
|
+
init (module-level `@lru_cache` factory, `Depends()` provider, or first-use lazy import) at the
|
|
133
|
+
handler that needs it.
|
|
134
|
+
|
|
114
135
|
## Testing
|
|
115
136
|
|
|
116
137
|
- **Framework**: pytest (with `conftest.py` for fixtures)
|
|
@@ -90,6 +90,25 @@ When examining a Rust project, check for:
|
|
|
90
90
|
- **Formatting**: `cargo fmt --check` (ensures code matches `rustfmt` style)
|
|
91
91
|
- **Unsafe audit**: `cargo geiger` (if security-sensitive — counts unsafe blocks)
|
|
92
92
|
|
|
93
|
+
### Runtime Entrypoints & Bootstrap-Wiring Sites
|
|
94
|
+
|
|
95
|
+
Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
|
|
96
|
+
`CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
|
|
97
|
+
|
|
98
|
+
- **Runtime entrypoints (a legitimate non-test call site):** `fn main()` in `main.rs` or a `[[bin]]`
|
|
99
|
+
target, an async runtime entry (`#[tokio::main]`), an HTTP handler registered on an axum/actix/warp
|
|
100
|
+
router, or a worker task spawned from `main` — not a `#[cfg(test)]` module or a `tests/` integration
|
|
101
|
+
file.
|
|
102
|
+
- **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** a `lazy_static!`
|
|
103
|
+
/ `once_cell::sync::Lazy` static that eagerly constructs heavy clients, a single `bootstrap`/`setup`
|
|
104
|
+
fn `main` calls before serving, or a `#[ctor]`-style pre-`main` initializer. A `Lazy` static is only
|
|
105
|
+
a risk when it is *forced* at startup rather than on first use.
|
|
106
|
+
- **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB pools
|
|
107
|
+
(`sqlx::Pool`, `diesel`, `deadpool`, `mongodb`), message/queue clients (`rdkafka`, `lapin`,
|
|
108
|
+
`redis`), telemetry SDKs (`opentelemetry`, `sentry`), or a broad internal service crate. Constructing
|
|
109
|
+
any of these eagerly at a universal entry is the CHECK-I23 pattern — recommend a `OnceCell`/`Lazy`
|
|
110
|
+
forced on **first use** by the handler that needs it, not at process start.
|
|
111
|
+
|
|
93
112
|
## Testing
|
|
94
113
|
|
|
95
114
|
- **Framework**: Built-in test harness (`#[test]`, `#[cfg(test)]`)
|
|
@@ -64,6 +64,29 @@ When examining a TypeScript project, check for:
|
|
|
64
64
|
- **Cross-package type checks**: `bun run typecheck` (or equivalent) passes for both the feature package AND packages that depend on it
|
|
65
65
|
- **Import path validation**: All import paths resolve correctly per the `exports` map in `package.json`
|
|
66
66
|
|
|
67
|
+
### Runtime Entrypoints & Bootstrap-Wiring Sites
|
|
68
|
+
|
|
69
|
+
Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
|
|
70
|
+
`CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
|
|
71
|
+
|
|
72
|
+
- **Runtime entrypoints (a legitimate non-test call site):** a `package.json` `bin` / CLI `main`; a
|
|
73
|
+
server entry that actually starts listening (`src/server.ts`, `src/index.ts` invoking `listen`); a
|
|
74
|
+
worker/consumer file; and for Next.js — `middleware.ts`, `app/**/route.ts` route handlers,
|
|
75
|
+
`app/**/{page,layout,template}.tsx`, server actions, and `instrumentation.ts`. Express/Hono/Fastify:
|
|
76
|
+
the file that constructs the app and calls `.listen()`.
|
|
77
|
+
- **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** Next.js
|
|
78
|
+
`instrumentation.ts` / `instrumentation.js` (its `register()` runs once per server process before
|
|
79
|
+
any request), an app-server preload/`register`/`--import` hook, a root `app/layout.tsx` that
|
|
80
|
+
eagerly imports server singletons, and global test setup (`vitest.setup.ts`, `jest.setup.ts`) if it
|
|
81
|
+
bootstraps production graphs. Wiring a heavy init here loads it on **every** cold start (and, under
|
|
82
|
+
the dev server, on every module re-evaluation).
|
|
83
|
+
- **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB/ORM clients
|
|
84
|
+
(`drizzle-orm`, `@prisma/client`, `pg`, `mongoose`, `kysely`), queue/worker libs (`bullmq`, `bull`,
|
|
85
|
+
`kafkajs`, `ioredis`), telemetry/observability SDKs (`@opentelemetry/*`, `@sentry/node`), and a
|
|
86
|
+
whole-service-layer barrel (`import * as services from "@repo/services"`). An init that pulls any of
|
|
87
|
+
these into a universal bootstrap entry is the CHECK-I23 pattern — recommend lazy init at the first
|
|
88
|
+
route/handler/worker that needs it.
|
|
89
|
+
|
|
67
90
|
## Testing
|
|
68
91
|
|
|
69
92
|
- **Framework**: Vitest (most common in modern TS), Jest, or testing-library
|
|
@@ -47,6 +47,21 @@ PIPELINE_STATE_FILENAME: Final = ".pipeline-state.json"
|
|
|
47
47
|
MANIFEST_FILENAME: Final = "epic-manifest.json"
|
|
48
48
|
NARRATIVE_FILENAME: Final = "EPIC.md"
|
|
49
49
|
|
|
50
|
+
#: The authoritative forge-verify status vocabulary. SOURCE OF TRUTH:
|
|
51
|
+
#: references/pipeline-state-schema.json (definitions.verifyEntry.properties.status.enum).
|
|
52
|
+
#: A member state carrying a forge-verify-*.status outside this set is treated as
|
|
53
|
+
#: incomplete-for-orchestration but SURFACED (never guessed silently) by render_status
|
|
54
|
+
#: — a single typo'd status otherwise poisons the epic rollup + dependency gates (#148).
|
|
55
|
+
#: NOTE: forge-session.py keeps a byte-identical copy of this constant — flat, self-
|
|
56
|
+
#: contained scripts have no shared import module (each is copied verbatim into adapters).
|
|
57
|
+
KNOWN_VERIFY_STATUSES: Final = frozenset(
|
|
58
|
+
{"pending", "passed", "findings-reported", "findings-applied", "skipped"}
|
|
59
|
+
)
|
|
60
|
+
#: The subset of KNOWN_VERIFY_STATUSES that makes a member's forge-verify-impl count as
|
|
61
|
+
#: complete-for-orchestration (00 §7). A STRICT subset — 'findings-reported' (unfixed),
|
|
62
|
+
#: 'skipped', and 'pending' do NOT unblock dependents. Not collapsible into the set above.
|
|
63
|
+
_VERIFY_ORCH_COMPLETE: Final = frozenset({"passed", "findings-applied"})
|
|
64
|
+
|
|
50
65
|
|
|
51
66
|
# --------------------------------------------------------------------------- #
|
|
52
67
|
# Type Definitions (00-core-definitions.md §4, §5; 02 §8.4)
|
|
@@ -139,6 +154,10 @@ class RenderStatus(TypedDict):
|
|
|
139
154
|
rollup: Aggregate {complete, total} counts.
|
|
140
155
|
nextCommand: Recommended next command for the first actionable feature, or
|
|
141
156
|
None when nothing is actionable (all complete, empty epic, or paused).
|
|
157
|
+
warnings: Human-readable diagnostics that do NOT invalidate the graph but
|
|
158
|
+
need surfacing — currently, members carrying a ``forge-verify-*.status``
|
|
159
|
+
outside ``KNOWN_VERIFY_STATUSES`` (treated as incomplete, but silently so
|
|
160
|
+
would poison the rollup + dependency gates, #148). Empty in the common case.
|
|
142
161
|
"""
|
|
143
162
|
|
|
144
163
|
epic: str
|
|
@@ -148,6 +167,7 @@ class RenderStatus(TypedDict):
|
|
|
148
167
|
parallelEligible: list[str]
|
|
149
168
|
rollup: Rollup
|
|
150
169
|
nextCommand: str | None
|
|
170
|
+
warnings: list[str]
|
|
151
171
|
|
|
152
172
|
|
|
153
173
|
# --------------------------------------------------------------------------- #
|
|
@@ -542,6 +562,11 @@ _TOP_REQUIRED: Final = (
|
|
|
542
562
|
)
|
|
543
563
|
#: Required keys on each Feature object (00 §2.2).
|
|
544
564
|
_FEATURE_REQUIRED: Final = ("name", "charter", "dependsOn", "exposes", "consumes")
|
|
565
|
+
#: Optional keys on each Feature object. `mutatesShared` is the #144 precision
|
|
566
|
+
#: hint for cross-member coupling (array of project-root-relative path strings);
|
|
567
|
+
#: schema-legal when present, ignored when absent (mirrors
|
|
568
|
+
#: epic-manifest-schema.json definitions.feature.properties.mutatesShared).
|
|
569
|
+
_FEATURE_OPTIONAL: Final = ("mutatesShared",)
|
|
545
570
|
#: Required keys on each Contract (exposes[]) object (00 §2.3).
|
|
546
571
|
_CONTRACT_REQUIRED: Final = ("name", "kind", "summary")
|
|
547
572
|
#: Required keys on each ConsumedContract (consumes[]) object (00 §2.4).
|
|
@@ -617,11 +642,16 @@ def _schema_findings(manifest: dict) -> list[Finding]:
|
|
|
617
642
|
findings.append(_schema(f"feature {label!r} missing required key {key!r}", fname))
|
|
618
643
|
for key in feat:
|
|
619
644
|
# 'status' is rejected separately above via the dedicated 'cached-status' code.
|
|
620
|
-
if key not in _FEATURE_REQUIRED and key != "status":
|
|
645
|
+
if key not in _FEATURE_REQUIRED and key not in _FEATURE_OPTIONAL and key != "status":
|
|
621
646
|
findings.append(_schema(f"feature {label!r} has unknown key {key!r}", fname))
|
|
622
647
|
for key in ("name", "charter"):
|
|
623
648
|
if key in feat and not isinstance(feat[key], str):
|
|
624
649
|
findings.append(_schema(f"feature {label!r} {key} must be a string", fname))
|
|
650
|
+
if "mutatesShared" in feat and (
|
|
651
|
+
not isinstance(feat["mutatesShared"], list)
|
|
652
|
+
or not all(isinstance(p, str) for p in feat["mutatesShared"])
|
|
653
|
+
):
|
|
654
|
+
findings.append(_schema(f"feature {label!r} mutatesShared must be an array of strings", fname)) # noqa: E501
|
|
625
655
|
if "dependsOn" in feat:
|
|
626
656
|
if not isinstance(feat["dependsOn"], list) or not all(isinstance(d, str) for d in feat["dependsOn"]): # noqa: E501
|
|
627
657
|
findings.append(_schema(f"feature {label!r} dependsOn must be an array of strings", fname)) # noqa: E501
|
|
@@ -792,7 +822,41 @@ def is_complete_for_orchestration(state: dict) -> bool:
|
|
|
792
822
|
return True
|
|
793
823
|
if not isinstance(impl, dict):
|
|
794
824
|
return False
|
|
795
|
-
return impl.get("status") in
|
|
825
|
+
return impl.get("status") in _VERIFY_ORCH_COMPLETE
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
def _verify_status_warnings(name: str, state: dict) -> list[str]:
|
|
829
|
+
"""Flag any ``forge-verify-*.status`` outside the known vocabulary (#148).
|
|
830
|
+
|
|
831
|
+
An unrecognized status (e.g. the eye-slip ``findings-resolved``, conflated with the
|
|
832
|
+
adjacent ``findingsResolved`` count) is correctly treated as incomplete by
|
|
833
|
+
``is_complete_for_orchestration`` — but *silently*, so one typo on one member
|
|
834
|
+
under-reports the whole epic rollup and fabricates phantom ``unmetDeps`` on
|
|
835
|
+
dependents. Surfacing it turns that non-local corruption into a visible diagnostic.
|
|
836
|
+
|
|
837
|
+
Non-string / malformed status values (a list, an int) are also flagged, and the
|
|
838
|
+
membership test is guarded so an unhashable value never raises.
|
|
839
|
+
"""
|
|
840
|
+
stages = state.get("stages", {})
|
|
841
|
+
if not isinstance(stages, dict):
|
|
842
|
+
return []
|
|
843
|
+
warnings: list[str] = []
|
|
844
|
+
known = ", ".join(sorted(KNOWN_VERIFY_STATUSES))
|
|
845
|
+
for stage_name, entry in stages.items():
|
|
846
|
+
if not (isinstance(stage_name, str) and stage_name.startswith("forge-verify-")):
|
|
847
|
+
continue
|
|
848
|
+
if not isinstance(entry, dict):
|
|
849
|
+
continue
|
|
850
|
+
status = entry.get("status")
|
|
851
|
+
if status is None:
|
|
852
|
+
continue
|
|
853
|
+
if isinstance(status, str) and status in KNOWN_VERIFY_STATUSES:
|
|
854
|
+
continue
|
|
855
|
+
warnings.append(
|
|
856
|
+
f"{name}: unknown {stage_name} status {status!r} "
|
|
857
|
+
f"(treated as incomplete; expected one of {known})"
|
|
858
|
+
)
|
|
859
|
+
return warnings
|
|
796
860
|
|
|
797
861
|
|
|
798
862
|
def _read_state_safely(state_path: Path) -> dict:
|
|
@@ -927,14 +991,15 @@ def render_status(epic_dir: Path, specs_dir: Path) -> RenderStatus:
|
|
|
927
991
|
rows: list[FeatureStatus] = []
|
|
928
992
|
feature_dir_by_name: dict[str, Path] = {}
|
|
929
993
|
complete: dict[str, bool] = {}
|
|
994
|
+
warnings: list[str] = []
|
|
930
995
|
for feat in features:
|
|
931
996
|
name = feat["name"]
|
|
932
997
|
member_dir = contained_path(epic_dir, name)
|
|
933
998
|
feature_dir_by_name[name] = member_dir
|
|
934
999
|
rows.append(derive_status(member_dir))
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
)
|
|
1000
|
+
member_state = _read_state_safely(member_dir / PIPELINE_STATE_FILENAME)
|
|
1001
|
+
complete[name] = is_complete_for_orchestration(member_state)
|
|
1002
|
+
warnings.extend(_verify_status_warnings(name, member_state))
|
|
938
1003
|
|
|
939
1004
|
# (4) per-feature unmetDeps + blocked. A feature that is itself complete is
|
|
940
1005
|
# never "blocked" — unmet deps only matter for work not yet finished.
|
|
@@ -989,6 +1054,7 @@ def render_status(epic_dir: Path, specs_dir: Path) -> RenderStatus:
|
|
|
989
1054
|
"parallelEligible": parallel_eligible,
|
|
990
1055
|
"rollup": rollup,
|
|
991
1056
|
"nextCommand": next_command,
|
|
1057
|
+
"warnings": warnings,
|
|
992
1058
|
}
|
|
993
1059
|
|
|
994
1060
|
|
|
@@ -1414,6 +1480,10 @@ def _print_status_table(status: RenderStatus) -> None:
|
|
|
1414
1480
|
marker = "⚠️ BLOCKING" if row["blockingEpicChangeRequests"] else "⚠️"
|
|
1415
1481
|
line += f" — {marker} {row['openEpicChangeRequests']} pending epic change(s)"
|
|
1416
1482
|
print(line)
|
|
1483
|
+
if status["warnings"]:
|
|
1484
|
+
print("Warnings:")
|
|
1485
|
+
for warning in status["warnings"]:
|
|
1486
|
+
print(f" ⚠️ {warning}")
|
|
1417
1487
|
if status["actionable"]:
|
|
1418
1488
|
print(f"Actionable: {', '.join(status['actionable'])}")
|
|
1419
1489
|
if status["parallelEligible"]:
|
|
@@ -21,14 +21,56 @@ is_root() { # $1 = candidate dir
|
|
|
21
21
|
[ -f "$1/.feature-forge-bundle.json" ] || [ -f "$1/.claude-plugin/plugin.json" ]
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
# Completeness gate (#152). A dir can carry the sentinel yet be a partial/stale install —
|
|
25
|
+
# a skill-only extraction, or an install predating the shared-reference fan-out — where the
|
|
26
|
+
# scripts/references the skills load at every stage are absent. Such a root looks usable but
|
|
27
|
+
# runs DEGRADED silently (hand-improvised state schema, skipped Mint Guard + scripted exit).
|
|
28
|
+
# These are the assets a complete install MUST carry beyond the sentinel; a resolved root
|
|
29
|
+
# missing any of them is reported as degraded rather than handed back as if it were whole.
|
|
30
|
+
CORE_ASSETS=(
|
|
31
|
+
"scripts/forge-session.py"
|
|
32
|
+
"references/pipeline-state-schema.json"
|
|
33
|
+
"references/stage-exit-protocol.md"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Echo the first missing core asset (relative path) for $1, or nothing when complete.
|
|
37
|
+
first_missing_asset() { # $1 = candidate dir
|
|
38
|
+
local a
|
|
39
|
+
for a in "${CORE_ASSETS[@]}"; do
|
|
40
|
+
[ -f "$1/$a" ] || { printf '%s' "$a"; return 0; }
|
|
41
|
+
done
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# A sentinel-bearing candidate that lacks a core asset is remembered here (first one wins)
|
|
45
|
+
# so a COMPLETE root found later in the probe order still takes precedence; only if the probe
|
|
46
|
+
# ends with no complete root do we report this degraded install (step 4).
|
|
47
|
+
partial_root=""
|
|
48
|
+
partial_missing=""
|
|
49
|
+
|
|
50
|
+
# Given a candidate that already passed is_root: print + exit 0 if it is complete; otherwise
|
|
51
|
+
# record it as the degraded fallback and return so probing continues. Always returns 0 on the
|
|
52
|
+
# fall-through path so `set -e` never aborts the resolver on a partial candidate.
|
|
53
|
+
accept_root() { # $1 = candidate dir (already is_root)
|
|
54
|
+
local miss
|
|
55
|
+
miss="$(first_missing_asset "$1")"
|
|
56
|
+
if [ -z "$miss" ]; then
|
|
57
|
+
printf '%s\n' "$1"
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
if [ -z "$partial_root" ]; then
|
|
61
|
+
partial_root="$1"
|
|
62
|
+
partial_missing="$miss"
|
|
63
|
+
fi
|
|
64
|
+
return 0
|
|
65
|
+
}
|
|
66
|
+
|
|
24
67
|
# ── Step 1: self-location — parent of this script's dir is the install root. ─────────────
|
|
25
68
|
# This is the PRIMARY path: a bundle ships its own scripts/forge-root.sh, so the parent of
|
|
26
69
|
# this script's dir is that bundle's root under any agent's layout.
|
|
27
70
|
self_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
28
71
|
root="$(cd -- "$self_dir/.." && pwd -P)"
|
|
29
72
|
if is_root "$root"; then
|
|
30
|
-
|
|
31
|
-
exit 0
|
|
73
|
+
accept_root "$root"
|
|
32
74
|
fi
|
|
33
75
|
|
|
34
76
|
# ── Step 2a: Claude marketplace-cache installs — ~/.claude/plugins/cache/<mp>/<plugin>/<ver>/.
|
|
@@ -42,8 +84,7 @@ fi
|
|
|
42
84
|
while IFS= read -r manifest; do
|
|
43
85
|
candidate="${manifest%/.claude-plugin/plugin.json}"
|
|
44
86
|
if is_root "$candidate"; then
|
|
45
|
-
|
|
46
|
-
exit 0
|
|
87
|
+
accept_root "$candidate"
|
|
47
88
|
fi
|
|
48
89
|
done < <(ls -t "$HOME"/.claude/plugins/cache/*/feature-forge/*/.claude-plugin/plugin.json 2>/dev/null || true)
|
|
49
90
|
|
|
@@ -51,9 +92,24 @@ done < <(ls -t "$HOME"/.claude/plugins/cache/*/feature-forge/*/.claude-plugin/pl
|
|
|
51
92
|
# Globs that match nothing expand to themselves; the is_root test rejects such literals. Covers
|
|
52
93
|
# every supported agent's install destination under BOTH global ($HOME) and project ($PWD) scope,
|
|
53
94
|
# matching the installer's per-agent layout: claude .claude/skills, codex .agents/skills, copilot
|
|
54
|
-
# .github/feature-forge, cursor .cursor/rules, gemini .gemini/extensions
|
|
55
|
-
# step 2a's path for a cache install that
|
|
56
|
-
# plugin.json for ls -t to key on).
|
|
95
|
+
# .github/feature-forge, cursor .cursor/rules, gemini .gemini/extensions, pi .pi/skills or
|
|
96
|
+
# $PI_CODING_AGENT_DIR/skills. The cache glob repeats step 2a's path for a cache install that
|
|
97
|
+
# carries only the neutral bundle sentinel (no plugin.json for ls -t to key on).
|
|
98
|
+
if [ -n "${PI_CODING_AGENT_DIR:-}" ]; then
|
|
99
|
+
for candidate in \
|
|
100
|
+
"$PI_CODING_AGENT_DIR/skills/feature-forge" \
|
|
101
|
+
"$PI_CODING_AGENT_DIR"/git/*/feature-forge/adapters/pi \
|
|
102
|
+
"$PI_CODING_AGENT_DIR"/git/*/*/feature-forge/adapters/pi \
|
|
103
|
+
"$PI_CODING_AGENT_DIR"/packages/*/feature-forge/adapters/pi \
|
|
104
|
+
"$PI_CODING_AGENT_DIR"/npm/*/@garygentry/feature-forge/adapters/pi \
|
|
105
|
+
"$PI_CODING_AGENT_DIR"/node_modules/@garygentry/feature-forge/adapters/pi \
|
|
106
|
+
; do
|
|
107
|
+
if is_root "$candidate"; then
|
|
108
|
+
accept_root "$candidate"
|
|
109
|
+
fi
|
|
110
|
+
done
|
|
111
|
+
fi
|
|
112
|
+
|
|
57
113
|
for candidate in \
|
|
58
114
|
"$HOME/.claude/skills/feature-forge" \
|
|
59
115
|
"$PWD/.claude/skills/feature-forge" \
|
|
@@ -67,25 +123,57 @@ for candidate in \
|
|
|
67
123
|
"$PWD/.cursor/rules/feature-forge" \
|
|
68
124
|
"$HOME/.gemini/extensions/feature-forge" \
|
|
69
125
|
"$PWD/.gemini/extensions/feature-forge" \
|
|
126
|
+
"$HOME/.pi/agent/skills/feature-forge" \
|
|
127
|
+
"$PWD/.pi/skills/feature-forge" \
|
|
128
|
+
"$HOME"/.pi/agent/git/*/feature-forge/adapters/pi \
|
|
129
|
+
"$HOME"/.pi/agent/git/*/*/feature-forge/adapters/pi \
|
|
130
|
+
"$HOME"/.pi/agent/packages/*/feature-forge/adapters/pi \
|
|
131
|
+
"$HOME"/.pi/agent/npm/*/@garygentry/feature-forge/adapters/pi \
|
|
132
|
+
"$HOME"/.pi/agent/node_modules/@garygentry/feature-forge/adapters/pi \
|
|
133
|
+
"$PWD"/.pi/git/*/feature-forge/adapters/pi \
|
|
134
|
+
"$PWD"/.pi/git/*/*/feature-forge/adapters/pi \
|
|
135
|
+
"$PWD"/.pi/packages/*/feature-forge/adapters/pi \
|
|
136
|
+
"$PWD"/.pi/npm/*/@garygentry/feature-forge/adapters/pi \
|
|
137
|
+
"$PWD"/.pi/node_modules/@garygentry/feature-forge/adapters/pi \
|
|
70
138
|
; do
|
|
71
139
|
if is_root "$candidate"; then
|
|
72
|
-
|
|
73
|
-
exit 0
|
|
140
|
+
accept_root "$candidate"
|
|
74
141
|
fi
|
|
75
142
|
done
|
|
76
143
|
|
|
144
|
+
# Project-scoped Pi installs may be discovered from a subdirectory of the repository. Probe
|
|
145
|
+
# ancestor .pi roots up to the filesystem root; this is bounded by path depth and avoids
|
|
146
|
+
# recursive globs. Scoped to .pi ONLY — every other agent's project layout is probed at $PWD
|
|
147
|
+
# in the fixed list above, and widening those to ancestors here would silently change
|
|
148
|
+
# established discovery behaviour for agents this bundle did not introduce.
|
|
149
|
+
probe_dir="$PWD"
|
|
150
|
+
while :; do
|
|
151
|
+
candidate="$probe_dir/.pi/skills/feature-forge"
|
|
152
|
+
if is_root "$candidate"; then
|
|
153
|
+
accept_root "$candidate"
|
|
154
|
+
fi
|
|
155
|
+
[ "$probe_dir" = "/" ] && break
|
|
156
|
+
next_probe_dir="$(dirname -- "$probe_dir")"
|
|
157
|
+
[ "$next_probe_dir" = "$probe_dir" ] && break
|
|
158
|
+
probe_dir="$next_probe_dir"
|
|
159
|
+
done
|
|
160
|
+
|
|
77
161
|
# ── Step 3: env fallback — neutral FEATURE_FORGE_ROOT, then the legacy CLAUDE_PLUGIN_ROOT. ─
|
|
78
162
|
# The neutral override works for every agent; CLAUDE_PLUGIN_ROOT is kept only for backwards
|
|
79
163
|
# compatibility with existing Claude installs (C-4).
|
|
80
164
|
if [ -n "${FEATURE_FORGE_ROOT:-}" ] && is_root "$FEATURE_FORGE_ROOT"; then
|
|
81
|
-
|
|
82
|
-
exit 0
|
|
165
|
+
accept_root "$FEATURE_FORGE_ROOT"
|
|
83
166
|
fi
|
|
84
167
|
if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && is_root "$CLAUDE_PLUGIN_ROOT"; then
|
|
85
|
-
|
|
86
|
-
exit 0
|
|
168
|
+
accept_root "$CLAUDE_PLUGIN_ROOT"
|
|
87
169
|
fi
|
|
88
170
|
|
|
89
|
-
# ── Step 4: failure
|
|
171
|
+
# ── Step 4: failure. A sentinel-bearing but asset-incomplete root found along the way is a
|
|
172
|
+
# DEGRADED install (#152) — report it distinctly and actionably rather than the generic
|
|
173
|
+
# cannot-locate message, so the operator knows to reinstall/update rather than reconfigure. ─
|
|
174
|
+
if [ -n "$partial_root" ]; then
|
|
175
|
+
echo "feature-forge: install incomplete/degraded at $partial_root (missing $partial_missing) — reinstall with 'npx @garygentry/feature-forge' (or 'feature-forge update' for a stale install)." >&2
|
|
176
|
+
exit 1
|
|
177
|
+
fi
|
|
90
178
|
echo "feature-forge: cannot locate install root. Set FEATURE_FORGE_ROOT to the bundle dir, or run from an installed skill dir." >&2
|
|
91
179
|
exit 1
|