@garygentry/feature-forge 0.1.4 → 0.2.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 +19 -1
- package/adapters/GENERATION-REPORT.md +17 -13
- package/adapters/claude/.feature-forge-bundle.json +6 -0
- package/adapters/claude/references/forge-config-schema.json +36 -10
- package/adapters/claude/references/pipeline-state-schema.json +4 -0
- package/adapters/claude/references/portable-root.md +8 -5
- package/adapters/claude/references/process-overview.md +15 -5
- package/adapters/claude/references/shared-conventions.md +69 -4
- package/adapters/claude/references/stack-resolution.md +4 -1
- package/adapters/claude/references/stacks/go.md +1 -1
- package/adapters/claude/references/stacks/python.md +1 -1
- package/adapters/claude/references/stacks/rust.md +1 -1
- package/adapters/claude/references/stacks/typescript.md +1 -1
- package/adapters/claude/references/templates/specs-hygiene/AGENTS.md +23 -0
- package/adapters/claude/references/templates/specs-hygiene/CLAUDE.md +22 -0
- package/adapters/claude/scripts/epic-manifest.py +1379 -0
- package/adapters/claude/scripts/forge-bootstrap.py +991 -0
- package/adapters/claude/scripts/forge-init.sh +44 -0
- package/adapters/claude/scripts/forge-root.sh +30 -8
- package/adapters/claude/scripts/validate-traceability.py +150 -0
- package/adapters/claude/skills/forge/SKILL.md +5 -5
- package/adapters/claude/skills/forge-0-epic/SKILL.md +13 -15
- package/adapters/claude/skills/forge-0-epic/references/edit-mode.md +2 -2
- package/adapters/claude/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
- package/adapters/claude/skills/forge-1-prd/SKILL.md +6 -4
- package/adapters/claude/skills/forge-2-tech/SKILL.md +8 -7
- package/adapters/claude/skills/forge-2-tech/references/stack-discovery-checklist.md +4 -4
- package/adapters/claude/skills/forge-3-specs/SKILL.md +1 -1
- package/adapters/claude/skills/forge-4-backlog/SKILL.md +2 -2
- package/adapters/claude/skills/forge-5-loop/SKILL.md +20 -18
- package/adapters/claude/skills/forge-5-loop/references/result-reporting.md +13 -0
- package/adapters/claude/skills/forge-5-loop/references/runner-contract.md +40 -0
- package/adapters/claude/skills/forge-6-docs/SKILL.md +11 -1
- package/adapters/claude/skills/forge-bootstrap/SKILL.md +240 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/ci/github-actions.yml +12 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/generic/run.sh +3 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/generic/test.sh +13 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/go/go.mod +3 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/go/main.go +12 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/go/main_test.go +11 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/hygiene/AGENTS.md +24 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/hygiene/CLAUDE.md +25 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/hygiene/README.md +11 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/licenses/Apache-2.0/LICENSE +198 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/licenses/MIT/LICENSE +21 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/python/pyproject.toml +24 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/__init__.py +5 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/main.py +13 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/python/tests/test_smoke.py +8 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/rust/Cargo.toml +15 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/rust/src/lib.rs +7 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/rust/src/main.rs +5 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/rust/tests/smoke.rs +6 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/typescript/package.json +15 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/typescript/src/index.ts +4 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/typescript/test/smoke.test.ts +6 -0
- package/adapters/claude/skills/forge-bootstrap/references/templates/typescript/tsconfig.json +14 -0
- package/adapters/claude/skills/forge-fix/SKILL.md +1 -1
- package/adapters/claude/skills/forge-init/SKILL.md +1 -1
- package/adapters/claude/skills/forge-verify/SKILL.md +7 -2
- package/adapters/claude/skills/forge-verify/references/verification-checklists.md +1 -1
- package/adapters/codex/.feature-forge-bundle.json +6 -0
- package/adapters/codex/agents/{forge-researcher.md → forge-researcher.toml} +4 -4
- package/adapters/codex/agents/{forge-spec-writer.md → forge-spec-writer.toml} +4 -4
- package/adapters/codex/agents/{forge-verifier.md → forge-verifier.toml} +4 -4
- package/adapters/codex/references/forge-config-schema.json +36 -10
- package/adapters/codex/references/pipeline-state-schema.json +4 -0
- package/adapters/codex/references/portable-root.md +8 -5
- package/adapters/codex/references/process-overview.md +15 -5
- package/adapters/codex/references/shared-conventions.md +69 -4
- package/adapters/codex/references/stack-resolution.md +4 -1
- package/adapters/codex/references/stacks/go.md +1 -1
- package/adapters/codex/references/stacks/python.md +1 -1
- package/adapters/codex/references/stacks/rust.md +1 -1
- package/adapters/codex/references/stacks/typescript.md +1 -1
- package/adapters/codex/references/templates/specs-hygiene/AGENTS.md +23 -0
- package/adapters/codex/references/templates/specs-hygiene/CLAUDE.md +22 -0
- package/adapters/codex/scripts/epic-manifest.py +1379 -0
- package/adapters/codex/scripts/forge-bootstrap.py +991 -0
- package/adapters/codex/scripts/forge-init.sh +44 -0
- package/adapters/codex/scripts/forge-root.sh +30 -8
- package/adapters/codex/scripts/validate-traceability.py +150 -0
- package/adapters/codex/skills/forge/{forge.md → SKILL.md} +16 -6
- package/adapters/codex/skills/forge-0-epic/{forge-0-epic.md → SKILL.md} +33 -25
- package/adapters/codex/skills/forge-0-epic/references/edit-mode.md +2 -2
- package/adapters/codex/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
- package/adapters/codex/skills/forge-1-prd/{forge-1-prd.md → SKILL.md} +22 -10
- package/adapters/codex/skills/forge-2-tech/{forge-2-tech.md → SKILL.md} +26 -15
- package/adapters/codex/skills/forge-2-tech/references/stack-discovery-checklist.md +4 -4
- package/adapters/codex/skills/forge-3-specs/{forge-3-specs.md → SKILL.md} +16 -6
- package/adapters/codex/skills/forge-4-backlog/{forge-4-backlog.md → SKILL.md} +15 -5
- package/adapters/codex/skills/forge-5-loop/{forge-5-loop.md → SKILL.md} +40 -28
- package/adapters/codex/skills/forge-5-loop/references/result-reporting.md +13 -0
- package/adapters/codex/skills/forge-5-loop/references/runner-contract.md +40 -0
- package/adapters/codex/skills/forge-6-docs/{forge-6-docs.md → SKILL.md} +26 -6
- package/adapters/codex/skills/forge-bootstrap/SKILL.md +249 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/ci/github-actions.yml +12 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/generic/run.sh +3 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/generic/test.sh +13 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/go/go.mod +3 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/go/main.go +12 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/go/main_test.go +11 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/hygiene/AGENTS.md +24 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/hygiene/CLAUDE.md +25 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/hygiene/README.md +11 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/licenses/Apache-2.0/LICENSE +198 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/licenses/MIT/LICENSE +21 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/python/pyproject.toml +24 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/__init__.py +5 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/main.py +13 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/python/tests/test_smoke.py +8 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/rust/Cargo.toml +15 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/rust/src/lib.rs +7 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/rust/src/main.rs +5 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/rust/tests/smoke.rs +6 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/typescript/package.json +15 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/typescript/src/index.ts +4 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/typescript/test/smoke.test.ts +6 -0
- package/adapters/codex/skills/forge-bootstrap/references/templates/typescript/tsconfig.json +14 -0
- package/adapters/codex/skills/forge-fix/{forge-fix.md → SKILL.md} +12 -2
- package/adapters/codex/skills/forge-init/{forge-init.md → SKILL.md} +11 -1
- package/adapters/codex/skills/forge-verify/{forge-verify.md → SKILL.md} +24 -9
- package/adapters/codex/skills/forge-verify/references/verification-checklists.md +1 -1
- package/adapters/copilot/.feature-forge-bundle.json +6 -0
- package/adapters/copilot/references/forge-config-schema.json +36 -10
- package/adapters/copilot/references/pipeline-state-schema.json +4 -0
- package/adapters/copilot/references/portable-root.md +8 -5
- package/adapters/copilot/references/process-overview.md +15 -5
- package/adapters/copilot/references/shared-conventions.md +69 -4
- package/adapters/copilot/references/stack-resolution.md +4 -1
- package/adapters/copilot/references/stacks/go.md +1 -1
- package/adapters/copilot/references/stacks/python.md +1 -1
- package/adapters/copilot/references/stacks/rust.md +1 -1
- package/adapters/copilot/references/stacks/typescript.md +1 -1
- package/adapters/copilot/references/templates/specs-hygiene/AGENTS.md +23 -0
- package/adapters/copilot/references/templates/specs-hygiene/CLAUDE.md +22 -0
- package/adapters/copilot/scripts/epic-manifest.py +1379 -0
- package/adapters/copilot/scripts/forge-bootstrap.py +991 -0
- package/adapters/copilot/scripts/forge-init.sh +44 -0
- package/adapters/copilot/scripts/forge-root.sh +30 -8
- package/adapters/copilot/scripts/validate-traceability.py +150 -0
- package/adapters/copilot/skills/forge/forge.md +16 -6
- package/adapters/copilot/skills/forge-0-epic/forge-0-epic.md +33 -25
- package/adapters/copilot/skills/forge-0-epic/references/edit-mode.md +2 -2
- package/adapters/copilot/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
- package/adapters/copilot/skills/forge-1-prd/forge-1-prd.md +22 -10
- package/adapters/copilot/skills/forge-2-tech/forge-2-tech.md +26 -15
- package/adapters/copilot/skills/forge-2-tech/references/stack-discovery-checklist.md +4 -4
- package/adapters/copilot/skills/forge-3-specs/forge-3-specs.md +16 -6
- package/adapters/copilot/skills/forge-4-backlog/forge-4-backlog.md +15 -5
- package/adapters/copilot/skills/forge-5-loop/forge-5-loop.md +40 -28
- package/adapters/copilot/skills/forge-5-loop/references/result-reporting.md +13 -0
- package/adapters/copilot/skills/forge-5-loop/references/runner-contract.md +40 -0
- package/adapters/copilot/skills/forge-6-docs/forge-6-docs.md +26 -6
- package/adapters/copilot/skills/forge-bootstrap/forge-bootstrap.md +249 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/ci/github-actions.yml +12 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/generic/run.sh +3 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/generic/test.sh +13 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/go/go.mod +3 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/go/main.go +12 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/go/main_test.go +11 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/hygiene/AGENTS.md +24 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/hygiene/CLAUDE.md +25 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/hygiene/README.md +11 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/licenses/Apache-2.0/LICENSE +198 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/licenses/MIT/LICENSE +21 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/python/pyproject.toml +24 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/__init__.py +5 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/main.py +13 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/python/tests/test_smoke.py +8 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/rust/Cargo.toml +15 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/rust/src/lib.rs +7 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/rust/src/main.rs +5 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/rust/tests/smoke.rs +6 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/typescript/package.json +15 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/typescript/src/index.ts +4 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/typescript/test/smoke.test.ts +6 -0
- package/adapters/copilot/skills/forge-bootstrap/references/templates/typescript/tsconfig.json +14 -0
- package/adapters/copilot/skills/forge-fix/forge-fix.md +12 -2
- package/adapters/copilot/skills/forge-init/forge-init.md +11 -1
- package/adapters/copilot/skills/forge-verify/forge-verify.md +24 -9
- package/adapters/copilot/skills/forge-verify/references/verification-checklists.md +1 -1
- package/adapters/cursor/.feature-forge-bundle.json +6 -0
- package/adapters/cursor/references/forge-config-schema.json +36 -10
- package/adapters/cursor/references/pipeline-state-schema.json +4 -0
- package/adapters/cursor/references/portable-root.md +8 -5
- package/adapters/cursor/references/process-overview.md +15 -5
- package/adapters/cursor/references/shared-conventions.md +69 -4
- package/adapters/cursor/references/stack-resolution.md +4 -1
- package/adapters/cursor/references/stacks/go.md +1 -1
- package/adapters/cursor/references/stacks/python.md +1 -1
- package/adapters/cursor/references/stacks/rust.md +1 -1
- package/adapters/cursor/references/stacks/typescript.md +1 -1
- package/adapters/cursor/references/templates/specs-hygiene/AGENTS.md +23 -0
- package/adapters/cursor/references/templates/specs-hygiene/CLAUDE.md +22 -0
- package/adapters/cursor/scripts/epic-manifest.py +1379 -0
- package/adapters/cursor/scripts/forge-bootstrap.py +991 -0
- package/adapters/cursor/scripts/forge-init.sh +44 -0
- package/adapters/cursor/scripts/forge-root.sh +30 -8
- package/adapters/cursor/scripts/validate-traceability.py +150 -0
- package/adapters/cursor/skills/forge/forge.mdc +16 -6
- package/adapters/cursor/skills/forge-0-epic/forge-0-epic.mdc +33 -25
- package/adapters/cursor/skills/forge-0-epic/references/edit-mode.md +2 -2
- package/adapters/cursor/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
- package/adapters/cursor/skills/forge-1-prd/forge-1-prd.mdc +22 -10
- package/adapters/cursor/skills/forge-2-tech/forge-2-tech.mdc +26 -15
- package/adapters/cursor/skills/forge-2-tech/references/stack-discovery-checklist.md +4 -4
- package/adapters/cursor/skills/forge-3-specs/forge-3-specs.mdc +16 -6
- package/adapters/cursor/skills/forge-4-backlog/forge-4-backlog.mdc +15 -5
- package/adapters/cursor/skills/forge-5-loop/forge-5-loop.mdc +40 -28
- package/adapters/cursor/skills/forge-5-loop/references/result-reporting.md +13 -0
- package/adapters/cursor/skills/forge-5-loop/references/runner-contract.md +40 -0
- package/adapters/cursor/skills/forge-6-docs/forge-6-docs.mdc +26 -6
- package/adapters/cursor/skills/forge-bootstrap/forge-bootstrap.mdc +250 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/ci/github-actions.yml +12 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/generic/run.sh +3 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/generic/test.sh +13 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/go/go.mod +3 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/go/main.go +12 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/go/main_test.go +11 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/hygiene/AGENTS.md +24 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/hygiene/CLAUDE.md +25 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/hygiene/README.md +11 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/licenses/Apache-2.0/LICENSE +198 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/licenses/MIT/LICENSE +21 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/python/pyproject.toml +24 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/__init__.py +5 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/main.py +13 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/python/tests/test_smoke.py +8 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/rust/Cargo.toml +15 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/rust/src/lib.rs +7 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/rust/src/main.rs +5 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/rust/tests/smoke.rs +6 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/typescript/package.json +15 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/typescript/src/index.ts +4 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/typescript/test/smoke.test.ts +6 -0
- package/adapters/cursor/skills/forge-bootstrap/references/templates/typescript/tsconfig.json +14 -0
- package/adapters/cursor/skills/forge-fix/forge-fix.mdc +12 -2
- package/adapters/cursor/skills/forge-init/forge-init.mdc +11 -1
- package/adapters/cursor/skills/forge-verify/forge-verify.mdc +24 -9
- package/adapters/cursor/skills/forge-verify/references/verification-checklists.md +1 -1
- package/adapters/gemini/.feature-forge-bundle.json +6 -0
- package/adapters/gemini/gemini-extension.json +5 -1
- package/adapters/gemini/references/forge-config-schema.json +36 -10
- package/adapters/gemini/references/pipeline-state-schema.json +4 -0
- package/adapters/gemini/references/portable-root.md +8 -5
- package/adapters/gemini/references/process-overview.md +15 -5
- package/adapters/gemini/references/shared-conventions.md +69 -4
- package/adapters/gemini/references/stack-resolution.md +4 -1
- package/adapters/gemini/references/stacks/go.md +1 -1
- package/adapters/gemini/references/stacks/python.md +1 -1
- package/adapters/gemini/references/stacks/rust.md +1 -1
- package/adapters/gemini/references/stacks/typescript.md +1 -1
- package/adapters/gemini/references/templates/specs-hygiene/AGENTS.md +23 -0
- package/adapters/gemini/references/templates/specs-hygiene/CLAUDE.md +22 -0
- package/adapters/gemini/scripts/epic-manifest.py +1379 -0
- package/adapters/gemini/scripts/forge-bootstrap.py +991 -0
- package/adapters/gemini/scripts/forge-init.sh +44 -0
- package/adapters/gemini/scripts/forge-root.sh +30 -8
- package/adapters/gemini/scripts/validate-traceability.py +150 -0
- package/adapters/gemini/skills/forge/forge.md +16 -6
- package/adapters/gemini/skills/forge-0-epic/forge-0-epic.md +33 -25
- package/adapters/gemini/skills/forge-0-epic/references/edit-mode.md +2 -2
- package/adapters/gemini/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
- package/adapters/gemini/skills/forge-1-prd/forge-1-prd.md +22 -10
- package/adapters/gemini/skills/forge-2-tech/forge-2-tech.md +26 -15
- package/adapters/gemini/skills/forge-2-tech/references/stack-discovery-checklist.md +4 -4
- package/adapters/gemini/skills/forge-3-specs/forge-3-specs.md +16 -6
- package/adapters/gemini/skills/forge-4-backlog/forge-4-backlog.md +15 -5
- package/adapters/gemini/skills/forge-5-loop/forge-5-loop.md +40 -28
- package/adapters/gemini/skills/forge-5-loop/references/result-reporting.md +13 -0
- package/adapters/gemini/skills/forge-5-loop/references/runner-contract.md +40 -0
- package/adapters/gemini/skills/forge-6-docs/forge-6-docs.md +26 -6
- package/adapters/gemini/skills/forge-bootstrap/forge-bootstrap.md +249 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/ci/github-actions.yml +12 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/generic/run.sh +3 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/generic/test.sh +13 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/go/go.mod +3 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/go/main.go +12 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/go/main_test.go +11 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/hygiene/AGENTS.md +24 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/hygiene/CLAUDE.md +25 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/hygiene/README.md +11 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/licenses/Apache-2.0/LICENSE +198 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/licenses/MIT/LICENSE +21 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/python/pyproject.toml +24 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/__init__.py +5 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/python/src/{{PKG}}/main.py +13 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/python/tests/test_smoke.py +8 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/rust/Cargo.toml +15 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/rust/src/lib.rs +7 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/rust/src/main.rs +5 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/rust/tests/smoke.rs +6 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/typescript/package.json +15 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/typescript/src/index.ts +4 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/typescript/test/smoke.test.ts +6 -0
- package/adapters/gemini/skills/forge-bootstrap/references/templates/typescript/tsconfig.json +14 -0
- package/adapters/gemini/skills/forge-fix/forge-fix.md +12 -2
- package/adapters/gemini/skills/forge-init/forge-init.md +11 -1
- package/adapters/gemini/skills/forge-verify/forge-verify.md +24 -9
- package/adapters/gemini/skills/forge-verify/references/verification-checklists.md +1 -1
- package/dist/agent-targets.d.ts +20 -4
- package/dist/agent-targets.js +29 -4
- package/dist/apply.js +245 -18
- package/dist/cli.js +12 -6
- package/dist/hash.d.ts +5 -0
- package/dist/hash.js +7 -0
- package/dist/manifest.d.ts +4 -2
- package/dist/manifest.js +58 -2
- package/dist/placements.d.ts +69 -0
- package/dist/placements.js +116 -0
- package/dist/plan.d.ts +7 -0
- package/dist/plan.js +87 -1
- package/dist/rauf.d.ts +4 -4
- package/dist/rauf.js +3 -3
- package/dist/report.js +21 -0
- package/dist/source.d.ts +4 -3
- package/dist/source.js +4 -3
- package/dist/types.d.ts +163 -19
- package/dist/types.js +42 -11
- package/package.json +1 -1
- package/adapters/codex/agents/openai.yaml +0 -10
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secondary install placements (A4b) — the second-root generalization the single-`destination`
|
|
3
|
+
* install model can't express. Two kinds (see {@link PlacementKind}):
|
|
4
|
+
* - "mirror" — codex copies the bundle's `agents/*.toml` FLAT into `.codex/agents/`, where
|
|
5
|
+
* Codex loads custom agents (it does NOT read them from `.agents/skills`).
|
|
6
|
+
* - "managed-block" — copilot writes a sentinel-delimited pointer block into the (possibly
|
|
7
|
+
* user-owned) `.github/copilot-instructions.md`, preserving the rest of it.
|
|
8
|
+
*
|
|
9
|
+
* This module is PURE: it resolves declarative {@link PlacementSpec}s to absolute roots, selects the
|
|
10
|
+
* mirror source files, and provides the managed-block string transforms (render/upsert/remove/read).
|
|
11
|
+
* The planner (plan.ts) decides actions and the apply engine (apply.ts) executes them; neither knows
|
|
12
|
+
* the per-kind string mechanics — those live here. Zero runtime dependencies; only `node:` built-ins.
|
|
13
|
+
*/
|
|
14
|
+
import * as path from "node:path";
|
|
15
|
+
import { MANAGED_BLOCK_END, MANAGED_BLOCK_START, } from "./types.js";
|
|
16
|
+
import { resolveRoots } from "./agent-targets.js";
|
|
17
|
+
/**
|
|
18
|
+
* Resolve every secondary placement declared on `target` to absolute roots under `scope` (A4b).
|
|
19
|
+
* Returns `[]` for agents with no placements (claude/cursor/gemini). Pure; the single derivation
|
|
20
|
+
* point so a new rule stays one `AGENT_TARGETS` edit (REQ-SCALE-01).
|
|
21
|
+
*/
|
|
22
|
+
export function resolvePlacements(target, scope, opts) {
|
|
23
|
+
const roots = resolveRoots(opts);
|
|
24
|
+
const scopeRoot = scope === "global" ? roots.home : roots.cwd;
|
|
25
|
+
return (target.placements ?? []).map((spec) => {
|
|
26
|
+
const root = path.resolve(scopeRoot, spec.baseDir);
|
|
27
|
+
return { kind: spec.kind, root, destination: path.resolve(root, spec.subpath), spec };
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Select the bundle files a "mirror" placement copies (A4b): every `source.files` entry whose
|
|
32
|
+
* POSIX relpath starts with `spec.sourcePrefix`, copied FLAT (basename only) into the destination.
|
|
33
|
+
* Sorted by destination basename for deterministic plans. Pure.
|
|
34
|
+
*/
|
|
35
|
+
export function selectMirrorFiles(source, spec) {
|
|
36
|
+
const prefix = spec.sourcePrefix ?? "";
|
|
37
|
+
return source.files
|
|
38
|
+
.filter((f) => f.relpath.startsWith(prefix))
|
|
39
|
+
.map((f) => ({ srcRelpath: f.relpath, destRelpath: path.posix.basename(f.relpath), srcHash: f.sha256 }))
|
|
40
|
+
.sort((a, b) => (a.destRelpath < b.destRelpath ? -1 : a.destRelpath > b.destRelpath ? 1 : 0));
|
|
41
|
+
}
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Managed-block string transforms (pure)
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
/**
|
|
46
|
+
* Render the managed-block BODY (without sentinels) for copilot (A4b). Points Copilot — which has no
|
|
47
|
+
* skills loader — at the staged bundle under `.github/feature-forge/` and lists the available skills.
|
|
48
|
+
* Deterministic given the bundle's skill ids. Pure.
|
|
49
|
+
*/
|
|
50
|
+
export function renderCopilotBlock(skills) {
|
|
51
|
+
const lines = [
|
|
52
|
+
"# feature-forge",
|
|
53
|
+
"",
|
|
54
|
+
"The feature-forge skill suite is installed in this repository under",
|
|
55
|
+
"`.github/feature-forge/`. GitHub Copilot has no skills loader, so consult those files",
|
|
56
|
+
"directly when a feature-forge workflow is requested.",
|
|
57
|
+
"",
|
|
58
|
+
"Each skill lives at `.github/feature-forge/skills/<name>/SKILL.md`. Available skills:",
|
|
59
|
+
"",
|
|
60
|
+
...[...skills].sort().map((s) => `- ${s}`),
|
|
61
|
+
"",
|
|
62
|
+
"Shared references are under `.github/feature-forge/references/`; helper scripts under",
|
|
63
|
+
"`.github/feature-forge/scripts/`.",
|
|
64
|
+
];
|
|
65
|
+
return lines.join("\n");
|
|
66
|
+
}
|
|
67
|
+
/** Wrap a rendered block body in the managed sentinels — the exact region written on disk. */
|
|
68
|
+
export function wrapBlock(body) {
|
|
69
|
+
return `${MANAGED_BLOCK_START}\n${body}\n${MANAGED_BLOCK_END}`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Extract the full managed region (sentinels INCLUDED) currently present in `content`, or `null` if
|
|
73
|
+
* no well-formed `start…end` region exists. The region is what `wrapBlock` produces, so its hash is
|
|
74
|
+
* directly comparable to a freshly rendered block. Pure.
|
|
75
|
+
*/
|
|
76
|
+
export function extractManagedRegion(content) {
|
|
77
|
+
const startIdx = content.indexOf(MANAGED_BLOCK_START);
|
|
78
|
+
if (startIdx === -1)
|
|
79
|
+
return null;
|
|
80
|
+
const endIdx = content.indexOf(MANAGED_BLOCK_END, startIdx + MANAGED_BLOCK_START.length);
|
|
81
|
+
if (endIdx === -1)
|
|
82
|
+
return null;
|
|
83
|
+
return content.slice(startIdx, endIdx + MANAGED_BLOCK_END.length);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Insert or replace the managed block in `existing`, preserving all user content outside the
|
|
87
|
+
* sentinels (A4b). If a region exists it is replaced in place; otherwise the block is appended after
|
|
88
|
+
* the existing content (separated by a blank line). `existing` is `""` for a not-yet-created file.
|
|
89
|
+
* The result always ends with a single trailing newline. Pure.
|
|
90
|
+
*/
|
|
91
|
+
export function upsertBlock(existing, body) {
|
|
92
|
+
const region = wrapBlock(body);
|
|
93
|
+
const current = extractManagedRegion(existing);
|
|
94
|
+
if (current !== null) {
|
|
95
|
+
return ensureTrailingNewline(existing.replace(current, region));
|
|
96
|
+
}
|
|
97
|
+
if (existing.trim() === "")
|
|
98
|
+
return ensureTrailingNewline(region);
|
|
99
|
+
return ensureTrailingNewline(`${existing.replace(/\n+$/, "")}\n\n${region}`);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Remove the managed block from `existing`, preserving the rest (A4b uninstall). Returns the
|
|
103
|
+
* remaining content (trailing whitespace trimmed to a single newline), or `""` if nothing but the
|
|
104
|
+
* block (and whitespace) remains — the caller deletes the file in that case. Pure.
|
|
105
|
+
*/
|
|
106
|
+
export function removeBlock(existing) {
|
|
107
|
+
const region = extractManagedRegion(existing);
|
|
108
|
+
if (region === null)
|
|
109
|
+
return existing;
|
|
110
|
+
const without = existing.replace(region, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
111
|
+
return without === "" ? "" : `${without}\n`;
|
|
112
|
+
}
|
|
113
|
+
/** Ensure exactly one trailing newline. */
|
|
114
|
+
function ensureTrailingNewline(s) {
|
|
115
|
+
return `${s.replace(/\n+$/, "")}\n`;
|
|
116
|
+
}
|
package/dist/plan.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { AgentId, FileActionKind, Mode, PlannedAction, Result, Scope, InstallManifest } from "./types.js";
|
|
11
11
|
import { type LocatedSource } from "./source.js";
|
|
12
|
+
import { type ResolvedPlacement } from "./placements.js";
|
|
12
13
|
/**
|
|
13
14
|
* Everything the pure planner needs to diff source ⇆ destination ⇆ manifest for ONE agent
|
|
14
15
|
* (spec 04 §4). Built by cli.ts (07); the planner reads these and writes nothing.
|
|
@@ -30,6 +31,12 @@ export interface PlanContext {
|
|
|
30
31
|
readonly force: boolean;
|
|
31
32
|
/** The pinned rauf coordinate to surface on the plan (06); the planner only echoes it. */
|
|
32
33
|
readonly raufPin?: string | null;
|
|
34
|
+
/**
|
|
35
|
+
* Resolved secondary placements for this agent (A4b), or absent/empty when it has none. Supplied by
|
|
36
|
+
* cli.ts (which holds the scope roots); the planner diffs each against its destination and the prior
|
|
37
|
+
* manifest's matching placement inventory.
|
|
38
|
+
*/
|
|
39
|
+
readonly placements?: ResolvedPlacement[];
|
|
33
40
|
}
|
|
34
41
|
/**
|
|
35
42
|
* Classify one bundle-relative path (spec 04 §6 table). PURE: hashes are read, nothing is written.
|
package/dist/plan.js
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
import * as fs from "node:fs";
|
|
11
11
|
import * as path from "node:path";
|
|
12
12
|
import { ok, err } from "./types.js";
|
|
13
|
-
import { sha256File } from "./hash.js";
|
|
13
|
+
import { sha256File, sha256String } from "./hash.js";
|
|
14
|
+
import { selectMirrorFiles, renderCopilotBlock, wrapBlock, extractManagedRegion, } from "./placements.js";
|
|
14
15
|
import { planUninstall } from "./manifest.js";
|
|
15
16
|
import { isWindows } from "./fsutil.js";
|
|
16
17
|
/**
|
|
@@ -98,15 +99,100 @@ function buildPlan(ctx, withOrphans) {
|
|
|
98
99
|
const files = ctx.mode === "symlink"
|
|
99
100
|
? planSymlink(ctx)
|
|
100
101
|
: planCopy(ctx, withOrphans);
|
|
102
|
+
// Secondary placements (A4b) are always copy-style regardless of the primary mode: a mirror is a
|
|
103
|
+
// few flat files and a managed-block is a merge, neither of which a whole-dir symlink expresses.
|
|
104
|
+
const placements = planPlacements(ctx, withOrphans);
|
|
101
105
|
const action = {
|
|
102
106
|
agent: ctx.agent,
|
|
103
107
|
scope: ctx.scope,
|
|
104
108
|
mode: ctx.mode,
|
|
105
109
|
files,
|
|
106
110
|
...(ctx.raufPin !== undefined ? { raufPin: ctx.raufPin } : {}),
|
|
111
|
+
...(placements.length > 0 ? { placements } : {}),
|
|
107
112
|
};
|
|
108
113
|
return ok(action);
|
|
109
114
|
}
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// Secondary placements (A4b)
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
/** Plan every resolved secondary placement (A4b). `ctx.source` is non-null here. */
|
|
119
|
+
function planPlacements(ctx, withOrphans) {
|
|
120
|
+
const resolved = ctx.placements ?? [];
|
|
121
|
+
if (resolved.length === 0)
|
|
122
|
+
return [];
|
|
123
|
+
const source = ctx.source;
|
|
124
|
+
const priorByDest = priorPlacementIndex(ctx.priorManifest);
|
|
125
|
+
return resolved.map((rp) => rp.kind === "mirror"
|
|
126
|
+
? planMirror(ctx, rp, source, priorByDest.get(rp.destination) ?? null, withOrphans)
|
|
127
|
+
: planManagedBlock(ctx, rp, source, priorByDest.get(rp.destination) ?? null));
|
|
128
|
+
}
|
|
129
|
+
/** Index prior-manifest placements by their absolute destination, for clean/orphan reconciliation. */
|
|
130
|
+
function priorPlacementIndex(prior) {
|
|
131
|
+
const m = new Map();
|
|
132
|
+
for (const p of prior?.placements ?? [])
|
|
133
|
+
m.set(p.destination, p);
|
|
134
|
+
return m;
|
|
135
|
+
}
|
|
136
|
+
/** Diff a "mirror" placement: each selected bundle file vs its flat destination + recorded hash. */
|
|
137
|
+
function planMirror(ctx, rp, source, prior, withOrphans) {
|
|
138
|
+
const recorded = new Map();
|
|
139
|
+
for (const f of prior?.files ?? [])
|
|
140
|
+
recorded.set(f.path, f);
|
|
141
|
+
const mirror = selectMirrorFiles(source, rp.spec);
|
|
142
|
+
const files = mirror.map((mf) => {
|
|
143
|
+
const destAbs = path.join(rp.destination, mf.destRelpath);
|
|
144
|
+
const destHash = hashIfExists(destAbs);
|
|
145
|
+
const manifestHash = recorded.get(mf.destRelpath)?.sha256;
|
|
146
|
+
const action = classifyFile(mf.destRelpath, mf.srcHash, destHash, manifestHash, ctx.force);
|
|
147
|
+
return { relpath: mf.destRelpath, action, srcRelpath: mf.srcRelpath };
|
|
148
|
+
});
|
|
149
|
+
if (withOrphans && prior !== null) {
|
|
150
|
+
const live = new Set(mirror.map((mf) => mf.destRelpath));
|
|
151
|
+
for (const f of prior.files) {
|
|
152
|
+
if (!live.has(f.path))
|
|
153
|
+
files.push({ relpath: f.path, action: "remove" });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return { kind: "mirror", root: rp.root, destination: rp.destination, files };
|
|
157
|
+
}
|
|
158
|
+
/** Diff a "managed-block" placement: render the block, compare its region to the on-disk region. */
|
|
159
|
+
function planManagedBlock(ctx, rp, source, prior) {
|
|
160
|
+
const blockContent = renderCopilotBlock(source.skills);
|
|
161
|
+
const newHash = sha256String(wrapBlock(blockContent));
|
|
162
|
+
const basename = path.basename(rp.destination);
|
|
163
|
+
const current = readManagedRegionHash(rp.destination);
|
|
164
|
+
const recordedHash = prior?.files.find((f) => f.path === basename)?.sha256;
|
|
165
|
+
let action;
|
|
166
|
+
if (current === undefined) {
|
|
167
|
+
action = "create"; // no managed region present yet
|
|
168
|
+
}
|
|
169
|
+
else if (current === newHash) {
|
|
170
|
+
action = "unchanged";
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
const clean = recordedHash !== undefined && current === recordedHash;
|
|
174
|
+
action = clean ? "overwrite" : ctx.force ? "overwrite" : "skip-modified";
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
kind: "managed-block",
|
|
178
|
+
root: rp.root,
|
|
179
|
+
destination: rp.destination,
|
|
180
|
+
files: [{ relpath: basename, action }],
|
|
181
|
+
blockContent,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/** Hash of the managed region currently in the target file, or undefined if absent/unreadable. */
|
|
185
|
+
function readManagedRegionHash(file) {
|
|
186
|
+
let content;
|
|
187
|
+
try {
|
|
188
|
+
content = fs.readFileSync(file, "utf8");
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
const region = extractManagedRegion(content);
|
|
194
|
+
return region === null ? undefined : sha256String(region);
|
|
195
|
+
}
|
|
110
196
|
/** Copy-mode per-file diff (spec 04 §6). `ctx.source` is non-null here. */
|
|
111
197
|
function planCopy(ctx, withOrphans) {
|
|
112
198
|
const source = ctx.source;
|
package/dist/rauf.d.ts
CHANGED
|
@@ -19,13 +19,13 @@ import { type Result } from "./types.js";
|
|
|
19
19
|
*
|
|
20
20
|
* Shape: `<name>@<version>` — the SCOPED package `@garygentry/rauf` (the unscoped `rauf` name is
|
|
21
21
|
* blocked by npm's similarity filter). Advanced on each feature-forge release to a new
|
|
22
|
-
* known-compatible rauf (REQ-RAUF-03). The current rauf version is 0.
|
|
22
|
+
* known-compatible rauf (REQ-RAUF-03). The current rauf version is 0.8.0.
|
|
23
23
|
*
|
|
24
|
-
* rauf is now PUBLISHED (rauf#28): `@garygentry/rauf@0.
|
|
24
|
+
* rauf is now PUBLISHED (rauf#28): `@garygentry/rauf@0.8.0` resolves from the npm registry, so the
|
|
25
25
|
* preflight below passes by default. (Historically this pin pointed at an unpublished package and
|
|
26
26
|
* the preflight was a designed-to-fail check — see the `--skip-rauf` escape hatch.)
|
|
27
27
|
*/
|
|
28
|
-
export declare const RAUF_PIN = "@garygentry/rauf@0.
|
|
28
|
+
export declare const RAUF_PIN = "@garygentry/rauf@0.8.0";
|
|
29
29
|
/**
|
|
30
30
|
* An injectable, READ-ONLY registry query (D1). Given a coordinate `name@version`, returns the
|
|
31
31
|
* resolved version string on success, or an `InstallerError` if it is not resolvable.
|
|
@@ -37,7 +37,7 @@ export declare const RAUF_PIN = "@garygentry/rauf@0.7.0";
|
|
|
37
37
|
* Contract: the query MUST be read-only — it MUST NOT install, MUST NOT mutate global npm
|
|
38
38
|
* state, and MUST NOT execute rauf. `npm view` satisfies this (it only reads registry metadata).
|
|
39
39
|
*
|
|
40
|
-
* @param coordinate - the `name@version` to resolve, e.g. "@garygentry/rauf@0.
|
|
40
|
+
* @param coordinate - the `name@version` to resolve, e.g. "@garygentry/rauf@0.8.0"
|
|
41
41
|
* @returns Result<string> — the resolved version on success; RAUF_UNRESOLVABLE on failure.
|
|
42
42
|
*/
|
|
43
43
|
export type RegistryQuery = (coordinate: string) => Result<string>;
|
package/dist/rauf.js
CHANGED
|
@@ -20,13 +20,13 @@ import { err, ok } from "./types.js";
|
|
|
20
20
|
*
|
|
21
21
|
* Shape: `<name>@<version>` — the SCOPED package `@garygentry/rauf` (the unscoped `rauf` name is
|
|
22
22
|
* blocked by npm's similarity filter). Advanced on each feature-forge release to a new
|
|
23
|
-
* known-compatible rauf (REQ-RAUF-03). The current rauf version is 0.
|
|
23
|
+
* known-compatible rauf (REQ-RAUF-03). The current rauf version is 0.8.0.
|
|
24
24
|
*
|
|
25
|
-
* rauf is now PUBLISHED (rauf#28): `@garygentry/rauf@0.
|
|
25
|
+
* rauf is now PUBLISHED (rauf#28): `@garygentry/rauf@0.8.0` resolves from the npm registry, so the
|
|
26
26
|
* preflight below passes by default. (Historically this pin pointed at an unpublished package and
|
|
27
27
|
* the preflight was a designed-to-fail check — see the `--skip-rauf` escape hatch.)
|
|
28
28
|
*/
|
|
29
|
-
export const RAUF_PIN = "@garygentry/rauf@0.
|
|
29
|
+
export const RAUF_PIN = "@garygentry/rauf@0.8.0";
|
|
30
30
|
/**
|
|
31
31
|
* Resolvability preflight for the pinned default loop runner (D1; REQ-RAUF-01/02/03, OQ-1).
|
|
32
32
|
*
|
package/dist/report.js
CHANGED
|
@@ -47,6 +47,9 @@ function renderAgent(subcommand, a) {
|
|
|
47
47
|
// Decode the synthetic status rows (§3.3) into one human line.
|
|
48
48
|
const status = a.actions.map((f) => f.relpath).join(" ");
|
|
49
49
|
lines.push(`${a.agent}: ${a.detected ? "detected" : "not detected"} ${status}`);
|
|
50
|
+
const note = confidenceNote(a);
|
|
51
|
+
if (note)
|
|
52
|
+
lines.push(` ${note}`);
|
|
50
53
|
return lines;
|
|
51
54
|
}
|
|
52
55
|
if (!a.ok && a.error) {
|
|
@@ -58,10 +61,28 @@ function renderAgent(subcommand, a) {
|
|
|
58
61
|
for (const f of a.actions) {
|
|
59
62
|
lines.push(` ${actionVerb(f.action)} ${f.relpath}`);
|
|
60
63
|
}
|
|
64
|
+
const note = confidenceNote(a);
|
|
65
|
+
if (note)
|
|
66
|
+
lines.push(` ${note}`);
|
|
61
67
|
if (a.raufPin)
|
|
62
68
|
lines.push(` rauf default runner pinned: ${a.raufPin}`);
|
|
63
69
|
return lines;
|
|
64
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Honest per-agent confidence note (A4 / Finding 6): silent for fully-trusted targets
|
|
73
|
+
* (`confirmed`/`verified-current`), explicit for `best-known`/`unsupported` so a user knows
|
|
74
|
+
* an install path may not be auto-loaded by that agent and where to check current docs. Pure.
|
|
75
|
+
*/
|
|
76
|
+
function confidenceNote(a) {
|
|
77
|
+
if (!a.confidence || a.confidence === "confirmed" || a.confidence === "verified-current") {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const where = a.docsUrl ? ` — see ${a.docsUrl}` : "";
|
|
81
|
+
if (a.confidence === "unsupported") {
|
|
82
|
+
return `note: ${a.agent} has no confirmed install surface (unsupported)${where}`;
|
|
83
|
+
}
|
|
84
|
+
return `note: ${a.agent} install path is best-known, not vendor-confirmed${where}`;
|
|
85
|
+
}
|
|
65
86
|
/** Map a FileActionKind to its human verb (REQ-OBS-01 vocabulary). */
|
|
66
87
|
export function actionVerb(kind) {
|
|
67
88
|
switch (kind) {
|
package/dist/source.d.ts
CHANGED
|
@@ -44,9 +44,10 @@ export interface LocatedSource {
|
|
|
44
44
|
export declare function locateBundle(agent: AgentId, opts?: LocateBundleOpts): Result<string>;
|
|
45
45
|
/**
|
|
46
46
|
* Minimal integrity check for a located bundle (REQ-OPS-06). Valid iff the `BUNDLE_REQUIRED_PATHS`
|
|
47
|
-
* are present: `skills/` is a non-empty dir,
|
|
48
|
-
* `
|
|
49
|
-
* `
|
|
47
|
+
* are present: `skills/` is a non-empty dir, the neutral `.feature-forge-bundle.json` sentinel and
|
|
48
|
+
* every bundled runtime helper (`forge-root.sh`, `forge-init.sh`, `epic-manifest.py`,
|
|
49
|
+
* `validate-traceability.py`, `forge-bootstrap.py`) exist, and (gemini) `gemini-extension.json`
|
|
50
|
+
* exists. Keys on the neutral sentinel, NOT the Claude-only `.claude-plugin/plugin.json`.
|
|
50
51
|
*
|
|
51
52
|
* @returns ok(undefined) when every required path is present; err(SOURCE_INVALID) naming the
|
|
52
53
|
* first missing/invalid required path otherwise.
|
package/dist/source.js
CHANGED
|
@@ -38,9 +38,10 @@ export function locateBundle(agent, opts = {}) {
|
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
40
|
* Minimal integrity check for a located bundle (REQ-OPS-06). Valid iff the `BUNDLE_REQUIRED_PATHS`
|
|
41
|
-
* are present: `skills/` is a non-empty dir,
|
|
42
|
-
* `
|
|
43
|
-
* `
|
|
41
|
+
* are present: `skills/` is a non-empty dir, the neutral `.feature-forge-bundle.json` sentinel and
|
|
42
|
+
* every bundled runtime helper (`forge-root.sh`, `forge-init.sh`, `epic-manifest.py`,
|
|
43
|
+
* `validate-traceability.py`, `forge-bootstrap.py`) exist, and (gemini) `gemini-extension.json`
|
|
44
|
+
* exists. Keys on the neutral sentinel, NOT the Claude-only `.claude-plugin/plugin.json`.
|
|
44
45
|
*
|
|
45
46
|
* @returns ok(undefined) when every required path is present; err(SOURCE_INVALID) naming the
|
|
46
47
|
* first missing/invalid required path otherwise.
|
package/dist/types.d.ts
CHANGED
|
@@ -32,19 +32,76 @@ export declare const EXIT: {
|
|
|
32
32
|
readonly USAGE: 2;
|
|
33
33
|
};
|
|
34
34
|
export type ExitCode = (typeof EXIT)[keyof typeof EXIT];
|
|
35
|
-
/**
|
|
36
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Manifest schema version. v2 (A4b) adds the optional `placements[]` array for agents whose
|
|
37
|
+
* install spans a second filesystem root (codex `.codex/agents`, copilot `.github/copilot-
|
|
38
|
+
* instructions.md`). A v1 manifest (no `placements`) is still read verbatim by the back-compat
|
|
39
|
+
* reader — bumped only on a breaking manifest-shape change.
|
|
40
|
+
*/
|
|
41
|
+
export declare const SCHEMA_VERSION: 2;
|
|
42
|
+
/** Versions of the manifest this installer can READ (write is always SCHEMA_VERSION). */
|
|
43
|
+
export declare const READABLE_SCHEMA_VERSIONS: readonly [1, 2];
|
|
44
|
+
/** A manifest schemaVersion this installer accepts on read (back-compat v1 + current v2). */
|
|
45
|
+
export type ReadableSchemaVersion = (typeof READABLE_SCHEMA_VERSIONS)[number];
|
|
37
46
|
/** The single namespace directory name written inside each agent's install location [D5]. */
|
|
38
47
|
export declare const FEATURE_FORGE_NS: "feature-forge";
|
|
39
48
|
/** Filename prefix for the hidden parent-sibling manifest, completed by the scope (§3, spec 05). */
|
|
40
49
|
export declare const MANIFEST_PREFIX: ".feature-forge.";
|
|
50
|
+
/**
|
|
51
|
+
* How honestly an agent's install paths track current vendor docs (Finding 6 / A4).
|
|
52
|
+
* - "confirmed" — source-verified end-to-end (claude).
|
|
53
|
+
* - "verified-current" — matches the vendor's current docs, re-verified in A0 (2026-06-26).
|
|
54
|
+
* - "best-known" — plausible but not doc-confirmed for this scope; install proceeds,
|
|
55
|
+
* labeled honestly in the report so users know it may not be loaded.
|
|
56
|
+
* - "unsupported" — no usable install surface; not installed (reserved; none today).
|
|
57
|
+
*/
|
|
58
|
+
export type Confidence = "confirmed" | "verified-current" | "best-known" | "unsupported";
|
|
59
|
+
/** How an agent consumes the bundle (documentation + future per-kind apply behavior). */
|
|
60
|
+
export type InstallKind = "skills" | "extension" | "rules" | "instructions";
|
|
61
|
+
/**
|
|
62
|
+
* Kind of a SECONDARY install placement (A4b). The primary bundle (the `feature-forge/` namespace
|
|
63
|
+
* dir) is always present and is never a placement; placements describe extra writes under a
|
|
64
|
+
* DIFFERENT filesystem root than the primary bundle:
|
|
65
|
+
* - "mirror" — copy a subset of bundle files flat into a second dir (codex `.codex/agents/`,
|
|
66
|
+
* where Codex loads custom agents — it does not read them from `.agents/skills`).
|
|
67
|
+
* - "managed-block" — write/merge a sentinel-delimited block into a (possibly user-owned) instructions
|
|
68
|
+
* file (copilot `.github/copilot-instructions.md`), preserving the rest of the file.
|
|
69
|
+
*/
|
|
70
|
+
export type PlacementKind = "mirror" | "managed-block";
|
|
71
|
+
/** HTML-comment sentinels delimiting a `managed-block` placement (mirrors rauf's convention). */
|
|
72
|
+
export declare const MANAGED_BLOCK_START: "<!-- feature-forge:managed:start -->";
|
|
73
|
+
export declare const MANAGED_BLOCK_END: "<!-- feature-forge:managed:end -->";
|
|
74
|
+
/**
|
|
75
|
+
* Declarative descriptor of one secondary placement on an {@link AgentTarget} (A4b). Pure data, so a
|
|
76
|
+
* new agent (or a new second-root rule) stays one table edit (REQ-SCALE-01). The absolute root and
|
|
77
|
+
* destination are derived under a scope by {@link resolvePlacements}; nothing here is stored.
|
|
78
|
+
*/
|
|
79
|
+
export interface PlacementSpec {
|
|
80
|
+
readonly kind: PlacementKind;
|
|
81
|
+
/** Second-root dir under the scope root, e.g. ".codex" (mirror) or ".github" (managed-block). */
|
|
82
|
+
readonly baseDir: string;
|
|
83
|
+
/**
|
|
84
|
+
* Path under `baseDir`. For "mirror" this is the destination DIR (e.g. "agents"); for
|
|
85
|
+
* "managed-block" this is the target FILE (e.g. "copilot-instructions.md").
|
|
86
|
+
*/
|
|
87
|
+
readonly subpath: string;
|
|
88
|
+
/**
|
|
89
|
+
* "mirror" only: bundle-relative prefix selecting which source files to copy (e.g. "agents/").
|
|
90
|
+
* Files matching the prefix are copied FLAT (basename only) into the placement destination.
|
|
91
|
+
*/
|
|
92
|
+
readonly sourcePrefix?: string;
|
|
93
|
+
}
|
|
41
94
|
/**
|
|
42
95
|
* One row of the static per-agent detection map (REQ-DET-01, REQ-DET-05). Adding a new
|
|
43
96
|
* agent is exactly adding one entry to `AGENT_TARGETS` — no logic change (REQ-SCALE-01).
|
|
44
97
|
*
|
|
45
98
|
* The on-disk destination for an agent under a given scope is derived, not stored:
|
|
46
|
-
* <scopeRoot>/<
|
|
47
|
-
* where scopeRoot is the resolved home (global) or cwd (project)
|
|
99
|
+
* <scopeRoot>/<installBaseDir>/<installSubpath>/<FEATURE_FORGE_NS>/
|
|
100
|
+
* where scopeRoot is the resolved home (global) or cwd (project), and `installSubpath` may
|
|
101
|
+
* be "" (installed directly under `installBaseDir`). Detection (`configDirName`) is decoupled
|
|
102
|
+
* from the install location because some agents load content from a different tree than the
|
|
103
|
+
* dir that signals their presence (A4): codex detects ".codex" but loads skills from
|
|
104
|
+
* ".agents/skills"; copilot detects ".copilot" but instructions live under ".github".
|
|
48
105
|
*/
|
|
49
106
|
export interface AgentTarget {
|
|
50
107
|
/** Stable agent identifier. */
|
|
@@ -55,22 +112,36 @@ export interface AgentTarget {
|
|
|
55
112
|
*/
|
|
56
113
|
readonly configDirName: string;
|
|
57
114
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
115
|
+
* Top-level dir under the scope root that holds the install AND is the containment boundary
|
|
116
|
+
* every write is checked against (REQ-SEC-02). Usually equals `configDirName`; decoupled for
|
|
117
|
+
* codex (".agents") and copilot (".github").
|
|
118
|
+
*/
|
|
119
|
+
readonly installBaseDir: string;
|
|
120
|
+
/**
|
|
121
|
+
* Path under `installBaseDir` to the namespace parent, e.g. "skills" (claude/codex),
|
|
122
|
+
* "rules" (cursor), "extensions" (gemini), "" (copilot — directly under `.github`).
|
|
60
123
|
*/
|
|
61
|
-
readonly
|
|
124
|
+
readonly installSubpath: string;
|
|
125
|
+
/** How this agent consumes the bundle (REQ-SCALE-01; per-kind placement extended in A4b). */
|
|
126
|
+
readonly installKind: InstallKind;
|
|
62
127
|
/**
|
|
63
|
-
* Informational: the skill-file form this agent's bundle uses — "SKILL.md" (claude),
|
|
64
|
-
* "<name>.md" (
|
|
128
|
+
* Informational: the skill-file form this agent's bundle uses — "SKILL.md" (claude, codex),
|
|
129
|
+
* "<name>.md" (copilot/gemini), "<name>.mdc" (cursor). The installer copies the
|
|
65
130
|
* bundle verbatim (REQ-SCALE-02) and does not parse skill files, so this is documentation.
|
|
66
131
|
*/
|
|
67
132
|
readonly skillFileForm: string;
|
|
133
|
+
/** Confidence for the default (global) scope. */
|
|
134
|
+
readonly confidence: Confidence;
|
|
135
|
+
/** Optional scope-specific override for project scope (e.g. gemini project = best-known). */
|
|
136
|
+
readonly projectConfidence?: Confidence;
|
|
137
|
+
/** Current vendor/docs URL for this target's install convention (surfaced in reports). */
|
|
138
|
+
readonly docsUrl: string;
|
|
68
139
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
140
|
+
* Secondary install placements under a DIFFERENT root than the primary bundle (A4b). Absent for
|
|
141
|
+
* agents whose whole install is the single namespace dir (claude/cursor/gemini). Declarative data
|
|
142
|
+
* only — resolved to absolute roots by {@link resolvePlacements}; a new rule is one table edit.
|
|
72
143
|
*/
|
|
73
|
-
readonly
|
|
144
|
+
readonly placements?: readonly PlacementSpec[];
|
|
74
145
|
}
|
|
75
146
|
/** Options for path resolution, injectable so tests never touch the real `~` (spec 02, spec 08). */
|
|
76
147
|
export interface ResolveOpts {
|
|
@@ -95,6 +166,10 @@ export interface DetectionResult {
|
|
|
95
166
|
readonly cliOnPath?: boolean;
|
|
96
167
|
/** Resolved absolute install destination for the active scope (the `feature-forge/` namespace dir). */
|
|
97
168
|
readonly destination: string;
|
|
169
|
+
/** Effective confidence for the active scope (REQ-DET-05; honest dry-run/list labeling, A4). */
|
|
170
|
+
readonly confidence: Confidence;
|
|
171
|
+
/** Current vendor/docs URL for this target (REQ-DET-05). */
|
|
172
|
+
readonly docsUrl: string;
|
|
98
173
|
}
|
|
99
174
|
/** One file recorded in the manifest inventory (REQ-SAFE-01). */
|
|
100
175
|
export interface ManifestFile {
|
|
@@ -103,13 +178,30 @@ export interface ManifestFile {
|
|
|
103
178
|
/** SHA-256 of the written bytes. Omitted for symlink mode (no per-file copy exists). */
|
|
104
179
|
readonly sha256?: string;
|
|
105
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* One persisted SECONDARY placement (manifest v2, A4b). Records the absolute second root + dest and
|
|
183
|
+
* an inventory of what we wrote there, so `update`/`uninstall` reconcile it exactly like the primary
|
|
184
|
+
* bundle but under its own containment boundary (`root`). For "managed-block", `files` holds a single
|
|
185
|
+
* entry whose `path` is the file basename and whose `sha256` fingerprints the block region we wrote
|
|
186
|
+
* (drift/clean detection); the rest of that file is user-owned and never recorded.
|
|
187
|
+
*/
|
|
188
|
+
export interface Placement {
|
|
189
|
+
readonly kind: PlacementKind;
|
|
190
|
+
/** Absolute containment boundary every write for this placement is checked against (REQ-SEC-02). */
|
|
191
|
+
readonly root: string;
|
|
192
|
+
/** Absolute destination: a DIR ("mirror") or a FILE ("managed-block"). */
|
|
193
|
+
readonly destination: string;
|
|
194
|
+
/** Per-file inventory written under this placement (paths relative to `destination`'s dir). */
|
|
195
|
+
readonly files: ManifestFile[];
|
|
196
|
+
}
|
|
106
197
|
/**
|
|
107
198
|
* The persisted per-install manifest (REQ-SAFE-01/03), written as the hidden parent-sibling
|
|
108
199
|
* `<installSubdir>/.feature-forge.<scope>.json` (spec 05). It is the sole record `list`/`update`/
|
|
109
200
|
* `uninstall` use to tell installer-written content from user content and to detect drift.
|
|
110
201
|
*/
|
|
111
202
|
export interface InstallManifest {
|
|
112
|
-
|
|
203
|
+
/** Always {@link SCHEMA_VERSION} when written; v1 is still accepted on read (back-compat). */
|
|
204
|
+
readonly schemaVersion: ReadableSchemaVersion;
|
|
113
205
|
readonly agent: AgentId;
|
|
114
206
|
readonly scope: Scope;
|
|
115
207
|
readonly mode: Mode;
|
|
@@ -124,7 +216,7 @@ export interface InstallManifest {
|
|
|
124
216
|
readonly featureForgeVersion: string | null;
|
|
125
217
|
/** SHA-256 over the source bundle's canonical (sorted-path) file set — drift anchor (OQ-4, spec 03). */
|
|
126
218
|
readonly sourceHash: string;
|
|
127
|
-
/** Pinned rauf coordinate recorded at install, e.g. "@garygentry/rauf@0.
|
|
219
|
+
/** Pinned rauf coordinate recorded at install, e.g. "@garygentry/rauf@0.8.0"; `null` if `--skip-rauf` (spec 06). */
|
|
128
220
|
readonly raufPin: string | null;
|
|
129
221
|
/** ISO-8601 timestamps. */
|
|
130
222
|
readonly installedAt: string;
|
|
@@ -137,6 +229,11 @@ export interface InstallManifest {
|
|
|
137
229
|
readonly link?: {
|
|
138
230
|
readonly target: string;
|
|
139
231
|
};
|
|
232
|
+
/**
|
|
233
|
+
* Secondary placements written under a different root than the primary bundle (manifest v2, A4b).
|
|
234
|
+
* Absent on agents without a second-root rule (claude/cursor/gemini) and on legacy v1 manifests.
|
|
235
|
+
*/
|
|
236
|
+
readonly placements?: Placement[];
|
|
140
237
|
}
|
|
141
238
|
/**
|
|
142
239
|
* The per-file action the planner assigns by diffing source ⇆ destination ⇆ manifest (spec 04).
|
|
@@ -165,6 +262,36 @@ export interface PlannedAction {
|
|
|
165
262
|
readonly files: FileAction[];
|
|
166
263
|
/** Surfaced in the plan/report for visibility (spec 06); not a file action. */
|
|
167
264
|
readonly raufPin?: string | null;
|
|
265
|
+
/**
|
|
266
|
+
* Resolved secondary-placement plans (A4b). Each carries its own absolute root + destination, the
|
|
267
|
+
* per-file diff actions, and (for "managed-block") the exact block content a real run will write —
|
|
268
|
+
* so dry-run shows, and `apply` performs, identical work. Absent when the agent has no placements.
|
|
269
|
+
*/
|
|
270
|
+
readonly placements?: PlannedPlacement[];
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* One secondary placement's resolved plan (A4b): the dry-run = real-run unit for a second root. The
|
|
274
|
+
* planner fills `files` by diffing source ⇆ destination (same classifier as the primary bundle) and,
|
|
275
|
+
* for "managed-block", attaches the rendered `blockContent` the apply step writes between sentinels.
|
|
276
|
+
*/
|
|
277
|
+
export interface PlannedPlacement {
|
|
278
|
+
readonly kind: PlacementKind;
|
|
279
|
+
/** Absolute containment boundary (REQ-SEC-02). */
|
|
280
|
+
readonly root: string;
|
|
281
|
+
/** Absolute destination dir ("mirror") or file ("managed-block"). */
|
|
282
|
+
readonly destination: string;
|
|
283
|
+
/**
|
|
284
|
+
* Per-file diff actions. "mirror" entries carry `srcRelpath` (the bundle-relative source to copy);
|
|
285
|
+
* "managed-block" has a single entry whose `relpath` is the file basename (no `srcRelpath`).
|
|
286
|
+
*/
|
|
287
|
+
readonly files: PlacementFileAction[];
|
|
288
|
+
/** "managed-block" only: the rendered block body (without sentinels) a create/overwrite writes. */
|
|
289
|
+
readonly blockContent?: string;
|
|
290
|
+
}
|
|
291
|
+
/** A placement file action: a {@link FileAction} plus the bundle-relative source for "mirror" copies. */
|
|
292
|
+
export interface PlacementFileAction extends FileAction {
|
|
293
|
+
/** "mirror" only: bundle-relative source path to copy into `<destination>/<relpath>`. */
|
|
294
|
+
readonly srcRelpath?: string;
|
|
168
295
|
}
|
|
169
296
|
/** One agent's outcome in a run summary (REQ-OBS-01/03). */
|
|
170
297
|
export interface AgentReport {
|
|
@@ -177,6 +304,10 @@ export interface AgentReport {
|
|
|
177
304
|
/** Present iff `ok` is false. */
|
|
178
305
|
readonly error?: InstallerError;
|
|
179
306
|
readonly raufPin?: string | null;
|
|
307
|
+
/** Effective install-path confidence for the active scope (A4; honest report labeling). */
|
|
308
|
+
readonly confidence?: Confidence;
|
|
309
|
+
/** Vendor/docs URL for this target (A4; shown when confidence is not full). */
|
|
310
|
+
readonly docsUrl?: string;
|
|
180
311
|
}
|
|
181
312
|
/** The whole-run summary, rendered human-readable or as `--json` (REQ-OBS-01, REQ-DET-05). */
|
|
182
313
|
export interface RunReport {
|
|
@@ -205,17 +336,30 @@ export interface RunReport {
|
|
|
205
336
|
*
|
|
206
337
|
* Verified ground truth: every bundle has `skills/` (11 skills), `references/`,
|
|
207
338
|
* `scripts/forge-root.sh`, `agents/`; gemini adds a root `gemini-extension.json`,
|
|
208
|
-
* codex
|
|
339
|
+
* codex ships `skills/<name>/SKILL.md` + standalone `agents/<name>.toml` custom agents,
|
|
340
|
+
* cursor uses `.mdc` files.
|
|
341
|
+
*
|
|
342
|
+
* A4 per-agent reality check (A0 re-verified vendor docs, 2026-06-26):
|
|
343
|
+
* - codex — detects `.codex`, loads skills from `.agents/skills` (verified-current).
|
|
344
|
+
* The `.codex/agents/*.toml` second placement is added in A4b.
|
|
345
|
+
* - copilot — detects `.copilot`, but has no skills loader; the bundle is staged under
|
|
346
|
+
* `.github/feature-forge` and the managed instructions block (.github/
|
|
347
|
+
* copilot-instructions.md) is added in A4b (best-known).
|
|
348
|
+
* - cursor — `.cursor/rules/*.mdc` confirmed current (verified-current).
|
|
349
|
+
* - gemini — `~/.gemini/extensions/feature-forge` global confirmed; project scope is
|
|
350
|
+
* best-known (project extension install is not clearly documented).
|
|
209
351
|
*/
|
|
210
352
|
export declare const AGENT_TARGETS: Readonly<Record<AgentId, AgentTarget>>;
|
|
211
353
|
/**
|
|
212
354
|
* Minimal integrity check (REQ-OPS-06, spec 03): a located bundle is valid iff `skills/` is a
|
|
213
|
-
* non-empty dir,
|
|
214
|
-
*
|
|
355
|
+
* non-empty dir, the neutral bundle sentinel `.feature-forge-bundle.json` exists, every runtime
|
|
356
|
+
* helper script a skill can invoke is present (so helper-backed skills run after install on ANY
|
|
357
|
+
* agent), and — for gemini only — `gemini-extension.json` exists at the bundle root. Defined here
|
|
358
|
+
* as data so the check is a localized table read.
|
|
215
359
|
*/
|
|
216
360
|
export declare const BUNDLE_REQUIRED_PATHS: {
|
|
217
361
|
/** Required of every agent bundle. */
|
|
218
|
-
readonly common: readonly ["skills", "scripts/forge-root.sh"];
|
|
362
|
+
readonly common: readonly ["skills", ".feature-forge-bundle.json", "scripts/forge-root.sh", "scripts/forge-init.sh", "scripts/epic-manifest.py", "scripts/validate-traceability.py", "scripts/forge-bootstrap.py"];
|
|
219
363
|
/** Additional per-agent requirements. */
|
|
220
364
|
readonly perAgent: Partial<Record<AgentId, readonly string[]>>;
|
|
221
365
|
};
|