@garygentry/feature-forge 0.2.14 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/adapters/GENERATION-REPORT.md +20 -0
- package/adapters/claude/.feature-forge-bundle.json +1 -1
- package/adapters/claude/agents/forge-verifier.md +1 -1
- package/adapters/claude/references/forge-config-schema.json +2 -2
- package/adapters/claude/references/pipeline-state-schema.json +1 -1
- package/adapters/claude/references/shared-conventions.md +62 -12
- package/adapters/claude/references/stage-exit-protocol.md +14 -4
- package/adapters/claude/references/vendor-construct-inventory.md +1 -1
- package/adapters/claude/scripts/epic-manifest.py +49 -6
- package/adapters/claude/scripts/forge-root.sh +47 -3
- package/adapters/claude/scripts/forge-session.py +1179 -9
- package/adapters/claude/scripts/validate-traceability.py +6 -1
- package/adapters/claude/skills/forge/SKILL.md +11 -5
- package/adapters/claude/skills/forge/references/pipeline-state-schema.json +1 -1
- package/adapters/claude/skills/forge/references/shared-conventions.md +62 -12
- package/adapters/claude/skills/forge/references/stage-exit-protocol.md +14 -4
- package/adapters/claude/skills/forge-0-epic/SKILL.md +1 -1
- package/adapters/claude/skills/forge-0-epic/references/pipeline-state-schema.json +1 -1
- package/adapters/claude/skills/forge-0-epic/references/shared-conventions.md +62 -12
- package/adapters/claude/skills/forge-0-epic/references/stage-exit-protocol.md +14 -4
- package/adapters/claude/skills/forge-1-prd/SKILL.md +25 -8
- package/adapters/claude/skills/forge-1-prd/references/shared-conventions.md +62 -12
- package/adapters/claude/skills/forge-1-prd/references/stage-exit-protocol.md +14 -4
- package/adapters/claude/skills/forge-2-tech/SKILL.md +25 -7
- package/adapters/claude/skills/forge-2-tech/references/shared-conventions.md +62 -12
- package/adapters/claude/skills/forge-2-tech/references/stage-exit-protocol.md +14 -4
- package/adapters/claude/skills/forge-3-specs/SKILL.md +24 -7
- package/adapters/claude/skills/forge-3-specs/references/shared-conventions.md +62 -12
- package/adapters/claude/skills/forge-3-specs/references/stage-exit-protocol.md +14 -4
- package/adapters/claude/skills/forge-4-backlog/SKILL.md +23 -7
- package/adapters/claude/skills/forge-4-backlog/references/shared-conventions.md +62 -12
- package/adapters/claude/skills/forge-4-backlog/references/stage-exit-protocol.md +14 -4
- package/adapters/claude/skills/forge-5-loop/SKILL.md +25 -25
- package/adapters/claude/skills/forge-5-loop/references/agent-selection.md +116 -0
- package/adapters/claude/skills/forge-5-loop/references/runner-contract.md +14 -107
- package/adapters/claude/skills/forge-5-loop/references/shared-conventions.md +62 -12
- package/adapters/claude/skills/forge-5-loop/references/stage-exit-protocol.md +14 -4
- package/adapters/claude/skills/forge-6-docs/SKILL.md +11 -5
- package/adapters/claude/skills/forge-6-docs/references/shared-conventions.md +62 -12
- package/adapters/claude/skills/forge-fix/references/shared-conventions.md +62 -12
- package/adapters/claude/skills/forge-fix/references/stage-exit-protocol.md +14 -4
- package/adapters/claude/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/claude/skills/forge-guide/references/shared-conventions.md +62 -12
- package/adapters/claude/skills/forge-verify/SKILL.md +22 -12
- package/adapters/claude/skills/forge-verify/references/findings-template.md +157 -0
- package/adapters/claude/skills/forge-verify/references/pipeline-state-schema.json +1 -1
- package/adapters/claude/skills/forge-verify/references/shared-conventions.md +62 -12
- package/adapters/claude/skills/forge-verify/references/verification-checklists/backlog.md +97 -0
- package/adapters/claude/skills/forge-verify/references/verification-checklists/epic.md +79 -0
- package/adapters/claude/skills/forge-verify/references/verification-checklists/impl.md +48 -0
- package/adapters/claude/skills/forge-verify/references/verification-checklists/prd.md +31 -0
- package/adapters/claude/skills/forge-verify/references/verification-checklists/specs.md +64 -0
- package/adapters/claude/skills/forge-verify/references/verification-checklists/tech.md +35 -0
- package/adapters/codex/.feature-forge-bundle.json +1 -1
- package/adapters/codex/agents/forge-verifier.toml +1 -1
- package/adapters/codex/references/forge-config-schema.json +2 -2
- package/adapters/codex/references/pipeline-state-schema.json +1 -1
- package/adapters/codex/references/shared-conventions.md +62 -12
- package/adapters/codex/references/stage-exit-protocol.md +14 -4
- package/adapters/codex/references/vendor-construct-inventory.md +1 -1
- package/adapters/codex/scripts/epic-manifest.py +49 -6
- package/adapters/codex/scripts/forge-root.sh +47 -3
- package/adapters/codex/scripts/forge-session.py +1179 -9
- package/adapters/codex/scripts/validate-traceability.py +6 -1
- package/adapters/codex/skills/forge/SKILL.md +11 -5
- package/adapters/codex/skills/forge/references/pipeline-state-schema.json +1 -1
- package/adapters/codex/skills/forge/references/shared-conventions.md +62 -12
- package/adapters/codex/skills/forge/references/stage-exit-protocol.md +14 -4
- package/adapters/codex/skills/forge-0-epic/SKILL.md +1 -1
- package/adapters/codex/skills/forge-0-epic/references/pipeline-state-schema.json +1 -1
- package/adapters/codex/skills/forge-0-epic/references/shared-conventions.md +62 -12
- package/adapters/codex/skills/forge-0-epic/references/stage-exit-protocol.md +14 -4
- package/adapters/codex/skills/forge-1-prd/SKILL.md +25 -8
- package/adapters/codex/skills/forge-1-prd/references/shared-conventions.md +62 -12
- package/adapters/codex/skills/forge-1-prd/references/stage-exit-protocol.md +14 -4
- package/adapters/codex/skills/forge-2-tech/SKILL.md +25 -7
- package/adapters/codex/skills/forge-2-tech/references/shared-conventions.md +62 -12
- package/adapters/codex/skills/forge-2-tech/references/stage-exit-protocol.md +14 -4
- package/adapters/codex/skills/forge-3-specs/SKILL.md +24 -7
- package/adapters/codex/skills/forge-3-specs/references/shared-conventions.md +62 -12
- package/adapters/codex/skills/forge-3-specs/references/stage-exit-protocol.md +14 -4
- package/adapters/codex/skills/forge-4-backlog/SKILL.md +23 -7
- package/adapters/codex/skills/forge-4-backlog/references/shared-conventions.md +62 -12
- package/adapters/codex/skills/forge-4-backlog/references/stage-exit-protocol.md +14 -4
- package/adapters/codex/skills/forge-5-loop/SKILL.md +25 -25
- package/adapters/codex/skills/forge-5-loop/references/agent-selection.md +116 -0
- package/adapters/codex/skills/forge-5-loop/references/runner-contract.md +14 -107
- package/adapters/codex/skills/forge-5-loop/references/shared-conventions.md +62 -12
- package/adapters/codex/skills/forge-5-loop/references/stage-exit-protocol.md +14 -4
- package/adapters/codex/skills/forge-6-docs/SKILL.md +11 -5
- package/adapters/codex/skills/forge-6-docs/references/shared-conventions.md +62 -12
- package/adapters/codex/skills/forge-fix/references/shared-conventions.md +62 -12
- package/adapters/codex/skills/forge-fix/references/stage-exit-protocol.md +14 -4
- package/adapters/codex/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/codex/skills/forge-guide/references/shared-conventions.md +62 -12
- package/adapters/codex/skills/forge-verify/SKILL.md +22 -12
- package/adapters/codex/skills/forge-verify/references/findings-template.md +157 -0
- package/adapters/codex/skills/forge-verify/references/pipeline-state-schema.json +1 -1
- package/adapters/codex/skills/forge-verify/references/shared-conventions.md +62 -12
- package/adapters/codex/skills/forge-verify/references/verification-checklists/backlog.md +97 -0
- package/adapters/codex/skills/forge-verify/references/verification-checklists/epic.md +79 -0
- package/adapters/codex/skills/forge-verify/references/verification-checklists/impl.md +48 -0
- package/adapters/codex/skills/forge-verify/references/verification-checklists/prd.md +31 -0
- package/adapters/codex/skills/forge-verify/references/verification-checklists/specs.md +64 -0
- package/adapters/codex/skills/forge-verify/references/verification-checklists/tech.md +35 -0
- package/adapters/copilot/.feature-forge-bundle.json +1 -1
- package/adapters/copilot/agents/forge-verifier.md +1 -1
- package/adapters/copilot/references/forge-config-schema.json +2 -2
- package/adapters/copilot/references/pipeline-state-schema.json +1 -1
- package/adapters/copilot/references/shared-conventions.md +62 -12
- package/adapters/copilot/references/stage-exit-protocol.md +14 -4
- package/adapters/copilot/references/vendor-construct-inventory.md +1 -1
- package/adapters/copilot/scripts/epic-manifest.py +49 -6
- package/adapters/copilot/scripts/forge-root.sh +47 -3
- package/adapters/copilot/scripts/forge-session.py +1179 -9
- package/adapters/copilot/scripts/validate-traceability.py +6 -1
- package/adapters/copilot/skills/forge/forge.md +11 -5
- package/adapters/copilot/skills/forge/references/pipeline-state-schema.json +1 -1
- package/adapters/copilot/skills/forge/references/shared-conventions.md +62 -12
- package/adapters/copilot/skills/forge/references/stage-exit-protocol.md +14 -4
- package/adapters/copilot/skills/forge-0-epic/forge-0-epic.md +1 -1
- package/adapters/copilot/skills/forge-0-epic/references/pipeline-state-schema.json +1 -1
- package/adapters/copilot/skills/forge-0-epic/references/shared-conventions.md +62 -12
- package/adapters/copilot/skills/forge-0-epic/references/stage-exit-protocol.md +14 -4
- package/adapters/copilot/skills/forge-1-prd/forge-1-prd.md +25 -8
- package/adapters/copilot/skills/forge-1-prd/references/shared-conventions.md +62 -12
- package/adapters/copilot/skills/forge-1-prd/references/stage-exit-protocol.md +14 -4
- package/adapters/copilot/skills/forge-2-tech/forge-2-tech.md +25 -7
- package/adapters/copilot/skills/forge-2-tech/references/shared-conventions.md +62 -12
- package/adapters/copilot/skills/forge-2-tech/references/stage-exit-protocol.md +14 -4
- package/adapters/copilot/skills/forge-3-specs/forge-3-specs.md +24 -7
- package/adapters/copilot/skills/forge-3-specs/references/shared-conventions.md +62 -12
- package/adapters/copilot/skills/forge-3-specs/references/stage-exit-protocol.md +14 -4
- package/adapters/copilot/skills/forge-4-backlog/forge-4-backlog.md +23 -7
- package/adapters/copilot/skills/forge-4-backlog/references/shared-conventions.md +62 -12
- package/adapters/copilot/skills/forge-4-backlog/references/stage-exit-protocol.md +14 -4
- package/adapters/copilot/skills/forge-5-loop/forge-5-loop.md +25 -25
- package/adapters/copilot/skills/forge-5-loop/references/agent-selection.md +116 -0
- package/adapters/copilot/skills/forge-5-loop/references/runner-contract.md +14 -107
- package/adapters/copilot/skills/forge-5-loop/references/shared-conventions.md +62 -12
- package/adapters/copilot/skills/forge-5-loop/references/stage-exit-protocol.md +14 -4
- package/adapters/copilot/skills/forge-6-docs/forge-6-docs.md +11 -5
- package/adapters/copilot/skills/forge-6-docs/references/shared-conventions.md +62 -12
- package/adapters/copilot/skills/forge-fix/references/shared-conventions.md +62 -12
- package/adapters/copilot/skills/forge-fix/references/stage-exit-protocol.md +14 -4
- package/adapters/copilot/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/copilot/skills/forge-guide/references/shared-conventions.md +62 -12
- package/adapters/copilot/skills/forge-verify/forge-verify.md +22 -12
- package/adapters/copilot/skills/forge-verify/references/findings-template.md +157 -0
- package/adapters/copilot/skills/forge-verify/references/pipeline-state-schema.json +1 -1
- package/adapters/copilot/skills/forge-verify/references/shared-conventions.md +62 -12
- package/adapters/copilot/skills/forge-verify/references/verification-checklists/backlog.md +97 -0
- package/adapters/copilot/skills/forge-verify/references/verification-checklists/epic.md +79 -0
- package/adapters/copilot/skills/forge-verify/references/verification-checklists/impl.md +48 -0
- package/adapters/copilot/skills/forge-verify/references/verification-checklists/prd.md +31 -0
- package/adapters/copilot/skills/forge-verify/references/verification-checklists/specs.md +64 -0
- package/adapters/copilot/skills/forge-verify/references/verification-checklists/tech.md +35 -0
- package/adapters/cursor/.feature-forge-bundle.json +1 -1
- package/adapters/cursor/agents/forge-verifier.mdc +1 -1
- package/adapters/cursor/references/forge-config-schema.json +2 -2
- package/adapters/cursor/references/pipeline-state-schema.json +1 -1
- package/adapters/cursor/references/shared-conventions.md +62 -12
- package/adapters/cursor/references/stage-exit-protocol.md +14 -4
- package/adapters/cursor/references/vendor-construct-inventory.md +1 -1
- package/adapters/cursor/scripts/epic-manifest.py +49 -6
- package/adapters/cursor/scripts/forge-root.sh +47 -3
- package/adapters/cursor/scripts/forge-session.py +1179 -9
- package/adapters/cursor/scripts/validate-traceability.py +6 -1
- package/adapters/cursor/skills/forge/forge.mdc +11 -5
- package/adapters/cursor/skills/forge/references/pipeline-state-schema.json +1 -1
- package/adapters/cursor/skills/forge/references/shared-conventions.md +62 -12
- package/adapters/cursor/skills/forge/references/stage-exit-protocol.md +14 -4
- package/adapters/cursor/skills/forge-0-epic/forge-0-epic.mdc +1 -1
- package/adapters/cursor/skills/forge-0-epic/references/pipeline-state-schema.json +1 -1
- package/adapters/cursor/skills/forge-0-epic/references/shared-conventions.md +62 -12
- package/adapters/cursor/skills/forge-0-epic/references/stage-exit-protocol.md +14 -4
- package/adapters/cursor/skills/forge-1-prd/forge-1-prd.mdc +25 -8
- package/adapters/cursor/skills/forge-1-prd/references/shared-conventions.md +62 -12
- package/adapters/cursor/skills/forge-1-prd/references/stage-exit-protocol.md +14 -4
- package/adapters/cursor/skills/forge-2-tech/forge-2-tech.mdc +25 -7
- package/adapters/cursor/skills/forge-2-tech/references/shared-conventions.md +62 -12
- package/adapters/cursor/skills/forge-2-tech/references/stage-exit-protocol.md +14 -4
- package/adapters/cursor/skills/forge-3-specs/forge-3-specs.mdc +24 -7
- package/adapters/cursor/skills/forge-3-specs/references/shared-conventions.md +62 -12
- package/adapters/cursor/skills/forge-3-specs/references/stage-exit-protocol.md +14 -4
- package/adapters/cursor/skills/forge-4-backlog/forge-4-backlog.mdc +23 -7
- package/adapters/cursor/skills/forge-4-backlog/references/shared-conventions.md +62 -12
- package/adapters/cursor/skills/forge-4-backlog/references/stage-exit-protocol.md +14 -4
- package/adapters/cursor/skills/forge-5-loop/forge-5-loop.mdc +25 -25
- package/adapters/cursor/skills/forge-5-loop/references/agent-selection.md +116 -0
- package/adapters/cursor/skills/forge-5-loop/references/runner-contract.md +14 -107
- package/adapters/cursor/skills/forge-5-loop/references/shared-conventions.md +62 -12
- package/adapters/cursor/skills/forge-5-loop/references/stage-exit-protocol.md +14 -4
- package/adapters/cursor/skills/forge-6-docs/forge-6-docs.mdc +11 -5
- package/adapters/cursor/skills/forge-6-docs/references/shared-conventions.md +62 -12
- package/adapters/cursor/skills/forge-fix/references/shared-conventions.md +62 -12
- package/adapters/cursor/skills/forge-fix/references/stage-exit-protocol.md +14 -4
- package/adapters/cursor/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/cursor/skills/forge-guide/references/shared-conventions.md +62 -12
- package/adapters/cursor/skills/forge-verify/forge-verify.mdc +22 -12
- package/adapters/cursor/skills/forge-verify/references/findings-template.md +157 -0
- package/adapters/cursor/skills/forge-verify/references/pipeline-state-schema.json +1 -1
- package/adapters/cursor/skills/forge-verify/references/shared-conventions.md +62 -12
- package/adapters/cursor/skills/forge-verify/references/verification-checklists/backlog.md +97 -0
- package/adapters/cursor/skills/forge-verify/references/verification-checklists/epic.md +79 -0
- package/adapters/cursor/skills/forge-verify/references/verification-checklists/impl.md +48 -0
- package/adapters/cursor/skills/forge-verify/references/verification-checklists/prd.md +31 -0
- package/adapters/cursor/skills/forge-verify/references/verification-checklists/specs.md +64 -0
- package/adapters/cursor/skills/forge-verify/references/verification-checklists/tech.md +35 -0
- package/adapters/gemini/.feature-forge-bundle.json +1 -1
- package/adapters/gemini/agents/forge-verifier.md +1 -1
- package/adapters/gemini/gemini-extension.json +1 -1
- package/adapters/gemini/references/forge-config-schema.json +2 -2
- package/adapters/gemini/references/pipeline-state-schema.json +1 -1
- package/adapters/gemini/references/shared-conventions.md +62 -12
- package/adapters/gemini/references/stage-exit-protocol.md +14 -4
- package/adapters/gemini/references/vendor-construct-inventory.md +1 -1
- package/adapters/gemini/scripts/epic-manifest.py +49 -6
- package/adapters/gemini/scripts/forge-root.sh +47 -3
- package/adapters/gemini/scripts/forge-session.py +1179 -9
- package/adapters/gemini/scripts/validate-traceability.py +6 -1
- package/adapters/gemini/skills/forge/forge.md +11 -5
- package/adapters/gemini/skills/forge/references/pipeline-state-schema.json +1 -1
- package/adapters/gemini/skills/forge/references/shared-conventions.md +62 -12
- package/adapters/gemini/skills/forge/references/stage-exit-protocol.md +14 -4
- package/adapters/gemini/skills/forge-0-epic/forge-0-epic.md +1 -1
- package/adapters/gemini/skills/forge-0-epic/references/pipeline-state-schema.json +1 -1
- package/adapters/gemini/skills/forge-0-epic/references/shared-conventions.md +62 -12
- package/adapters/gemini/skills/forge-0-epic/references/stage-exit-protocol.md +14 -4
- package/adapters/gemini/skills/forge-1-prd/forge-1-prd.md +25 -8
- package/adapters/gemini/skills/forge-1-prd/references/shared-conventions.md +62 -12
- package/adapters/gemini/skills/forge-1-prd/references/stage-exit-protocol.md +14 -4
- package/adapters/gemini/skills/forge-2-tech/forge-2-tech.md +25 -7
- package/adapters/gemini/skills/forge-2-tech/references/shared-conventions.md +62 -12
- package/adapters/gemini/skills/forge-2-tech/references/stage-exit-protocol.md +14 -4
- package/adapters/gemini/skills/forge-3-specs/forge-3-specs.md +24 -7
- package/adapters/gemini/skills/forge-3-specs/references/shared-conventions.md +62 -12
- package/adapters/gemini/skills/forge-3-specs/references/stage-exit-protocol.md +14 -4
- package/adapters/gemini/skills/forge-4-backlog/forge-4-backlog.md +23 -7
- package/adapters/gemini/skills/forge-4-backlog/references/shared-conventions.md +62 -12
- package/adapters/gemini/skills/forge-4-backlog/references/stage-exit-protocol.md +14 -4
- package/adapters/gemini/skills/forge-5-loop/forge-5-loop.md +25 -25
- package/adapters/gemini/skills/forge-5-loop/references/agent-selection.md +116 -0
- package/adapters/gemini/skills/forge-5-loop/references/runner-contract.md +14 -107
- package/adapters/gemini/skills/forge-5-loop/references/shared-conventions.md +62 -12
- package/adapters/gemini/skills/forge-5-loop/references/stage-exit-protocol.md +14 -4
- package/adapters/gemini/skills/forge-6-docs/forge-6-docs.md +11 -5
- package/adapters/gemini/skills/forge-6-docs/references/shared-conventions.md +62 -12
- package/adapters/gemini/skills/forge-fix/references/shared-conventions.md +62 -12
- package/adapters/gemini/skills/forge-fix/references/stage-exit-protocol.md +14 -4
- package/adapters/gemini/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/gemini/skills/forge-guide/references/shared-conventions.md +62 -12
- package/adapters/gemini/skills/forge-verify/forge-verify.md +22 -12
- package/adapters/gemini/skills/forge-verify/references/findings-template.md +157 -0
- package/adapters/gemini/skills/forge-verify/references/pipeline-state-schema.json +1 -1
- package/adapters/gemini/skills/forge-verify/references/shared-conventions.md +62 -12
- package/adapters/gemini/skills/forge-verify/references/verification-checklists/backlog.md +97 -0
- package/adapters/gemini/skills/forge-verify/references/verification-checklists/epic.md +79 -0
- package/adapters/gemini/skills/forge-verify/references/verification-checklists/impl.md +48 -0
- package/adapters/gemini/skills/forge-verify/references/verification-checklists/prd.md +31 -0
- package/adapters/gemini/skills/forge-verify/references/verification-checklists/specs.md +64 -0
- package/adapters/gemini/skills/forge-verify/references/verification-checklists/tech.md +35 -0
- 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/{claude/skills/forge-5-loop → pi}/references/forge-config-schema.json +4 -4
- package/adapters/{claude/skills/forge-1-prd → pi}/references/pipeline-state-schema.json +1 -1
- 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 +345 -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 +268 -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 +1737 -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 +3036 -0
- package/adapters/pi/scripts/validate-traceability.py +155 -0
- package/adapters/pi/skills/forge/SKILL.md +249 -0
- package/adapters/{claude/skills/forge-4-backlog → pi/skills/forge}/references/pipeline-state-schema.json +1 -1
- package/adapters/pi/skills/forge/references/process-overview.md +143 -0
- package/adapters/pi/skills/forge/references/shared-conventions.md +345 -0
- package/adapters/pi/skills/forge/references/stage-exit-protocol.md +268 -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/{claude/skills/forge-2-tech → pi/skills/forge-0-epic}/references/pipeline-state-schema.json +1 -1
- package/adapters/pi/skills/forge-0-epic/references/portable-root.md +71 -0
- package/adapters/pi/skills/forge-0-epic/references/shared-conventions.md +345 -0
- package/adapters/pi/skills/forge-0-epic/references/stage-exit-protocol.md +268 -0
- package/adapters/pi/skills/forge-1-prd/SKILL.md +181 -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 +345 -0
- package/adapters/pi/skills/forge-1-prd/references/stage-exit-protocol.md +268 -0
- package/adapters/pi/skills/forge-2-tech/SKILL.md +243 -0
- package/adapters/pi/skills/forge-2-tech/references/shared-conventions.md +345 -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 +268 -0
- package/adapters/pi/skills/forge-3-specs/SKILL.md +195 -0
- package/adapters/pi/skills/forge-3-specs/references/shared-conventions.md +345 -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 +268 -0
- package/adapters/pi/skills/forge-4-backlog/SKILL.md +191 -0
- package/adapters/pi/skills/forge-4-backlog/references/shared-conventions.md +345 -0
- package/adapters/pi/skills/forge-4-backlog/references/stage-exit-protocol.md +268 -0
- package/adapters/pi/skills/forge-5-loop/SKILL.md +314 -0
- package/adapters/pi/skills/forge-5-loop/references/agent-selection.md +116 -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 +248 -0
- package/adapters/pi/skills/forge-5-loop/references/shared-conventions.md +345 -0
- package/adapters/pi/skills/forge-5-loop/references/stage-exit-protocol.md +268 -0
- package/adapters/pi/skills/forge-6-docs/SKILL.md +208 -0
- package/adapters/pi/skills/forge-6-docs/references/doc-conventions.md +126 -0
- package/adapters/pi/skills/forge-6-docs/references/shared-conventions.md +345 -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 +345 -0
- package/adapters/pi/skills/forge-fix/references/stage-exit-protocol.md +268 -0
- package/adapters/pi/skills/forge-guide/SKILL.md +192 -0
- package/adapters/{codex/skills/forge-4-backlog → pi/skills/forge-guide}/references/forge-config-schema.json +4 -4
- 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 +345 -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 +283 -0
- package/adapters/pi/skills/forge-verify/references/findings-template.md +157 -0
- package/adapters/{claude/skills/forge-3-specs → pi/skills/forge-verify}/references/pipeline-state-schema.json +1 -1
- package/adapters/pi/skills/forge-verify/references/shared-conventions.md +345 -0
- package/adapters/pi/skills/forge-verify/references/verification-checklists/backlog.md +97 -0
- package/adapters/pi/skills/forge-verify/references/verification-checklists/epic.md +79 -0
- package/adapters/pi/skills/forge-verify/references/verification-checklists/impl.md +48 -0
- package/adapters/pi/skills/forge-verify/references/verification-checklists/prd.md +31 -0
- package/adapters/pi/skills/forge-verify/references/verification-checklists/specs.md +64 -0
- package/adapters/pi/skills/forge-verify/references/verification-checklists/tech.md +35 -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/adapters/claude/skills/forge-4-backlog/references/forge-config-schema.json +0 -236
- package/adapters/claude/skills/forge-6-docs/references/pipeline-state-schema.json +0 -191
- package/adapters/claude/skills/forge-verify/references/verification-checklists.md +0 -477
- package/adapters/codex/skills/forge-1-prd/references/pipeline-state-schema.json +0 -191
- package/adapters/codex/skills/forge-2-tech/references/pipeline-state-schema.json +0 -191
- package/adapters/codex/skills/forge-3-specs/references/pipeline-state-schema.json +0 -191
- package/adapters/codex/skills/forge-4-backlog/references/pipeline-state-schema.json +0 -191
- package/adapters/codex/skills/forge-5-loop/references/forge-config-schema.json +0 -236
- package/adapters/codex/skills/forge-6-docs/references/pipeline-state-schema.json +0 -191
- package/adapters/codex/skills/forge-verify/references/verification-checklists.md +0 -477
- package/adapters/copilot/skills/forge-1-prd/references/pipeline-state-schema.json +0 -191
- package/adapters/copilot/skills/forge-2-tech/references/pipeline-state-schema.json +0 -191
- package/adapters/copilot/skills/forge-3-specs/references/pipeline-state-schema.json +0 -191
- package/adapters/copilot/skills/forge-4-backlog/references/forge-config-schema.json +0 -236
- package/adapters/copilot/skills/forge-4-backlog/references/pipeline-state-schema.json +0 -191
- package/adapters/copilot/skills/forge-5-loop/references/forge-config-schema.json +0 -236
- package/adapters/copilot/skills/forge-6-docs/references/pipeline-state-schema.json +0 -191
- package/adapters/copilot/skills/forge-verify/references/verification-checklists.md +0 -477
- package/adapters/cursor/skills/forge-1-prd/references/pipeline-state-schema.json +0 -191
- package/adapters/cursor/skills/forge-2-tech/references/pipeline-state-schema.json +0 -191
- package/adapters/cursor/skills/forge-3-specs/references/pipeline-state-schema.json +0 -191
- package/adapters/cursor/skills/forge-4-backlog/references/forge-config-schema.json +0 -236
- package/adapters/cursor/skills/forge-4-backlog/references/pipeline-state-schema.json +0 -191
- package/adapters/cursor/skills/forge-5-loop/references/forge-config-schema.json +0 -236
- package/adapters/cursor/skills/forge-6-docs/references/pipeline-state-schema.json +0 -191
- package/adapters/cursor/skills/forge-verify/references/verification-checklists.md +0 -477
- package/adapters/gemini/skills/forge-1-prd/references/pipeline-state-schema.json +0 -191
- package/adapters/gemini/skills/forge-2-tech/references/pipeline-state-schema.json +0 -191
- package/adapters/gemini/skills/forge-3-specs/references/pipeline-state-schema.json +0 -191
- package/adapters/gemini/skills/forge-4-backlog/references/forge-config-schema.json +0 -236
- package/adapters/gemini/skills/forge-4-backlog/references/pipeline-state-schema.json +0 -191
- package/adapters/gemini/skills/forge-5-loop/references/forge-config-schema.json +0 -236
- package/adapters/gemini/skills/forge-6-docs/references/pipeline-state-schema.json +0 -191
- package/adapters/gemini/skills/forge-verify/references/verification-checklists.md +0 -477
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/view/dialog-builder.ts
|
|
2
|
+
// Regenerate with: python3 scripts/build-adapters.py
|
|
3
|
+
import { DynamicBorder, type Theme } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { type Component, Container, type Input, Spacer } from "@earendil-works/pi-tui";
|
|
5
|
+
import type { QuestionnaireState } from "../state/state.js";
|
|
6
|
+
import type { QuestionData } from "../tool/types.js";
|
|
7
|
+
import type { PreviewPaneProps } from "./components/preview/preview-pane.js";
|
|
8
|
+
import type { TabBar } from "./components/tab-bar.js";
|
|
9
|
+
import type { StatefulView } from "./stateful-view.js";
|
|
10
|
+
import type { TabComponents } from "./tab-components.js";
|
|
11
|
+
import { QuestionTabStrategy, SubmitTabStrategy, type TabContentStrategy } from "./tab-content-strategy.js";
|
|
12
|
+
|
|
13
|
+
export const HINT_PART_ENTER = "Enter to select";
|
|
14
|
+
export const HINT_PART_NAV = "↑/↓ to navigate";
|
|
15
|
+
export const HINT_PART_TOGGLE = "Space to toggle";
|
|
16
|
+
export const HINT_PART_NOTES = "n to add notes";
|
|
17
|
+
export const HINT_PART_TAB = "Tab to switch questions";
|
|
18
|
+
export const HINT_PART_CANCEL = "Esc to cancel";
|
|
19
|
+
export const HINT_PART_COLLAPSE = "Ctrl+] to collapse";
|
|
20
|
+
export const HINT_PART_EXPAND = "Ctrl+] to expand";
|
|
21
|
+
/**
|
|
22
|
+
* `HINT_SINGLE` / `HINT_MULTI` are the resting core hint for NON-multiSelect
|
|
23
|
+
* question tabs only: `buildHintText` drops `NOTES` while the notes editor is
|
|
24
|
+
* open (`state.notesVisible`) or the "Type something." row is capturing text
|
|
25
|
+
* (`state.inputMode`), and on multiSelect tabs it interleaves `TOGGLE` between
|
|
26
|
+
* `NAV` and `NOTES`, so neither composite is a substring there — assert on
|
|
27
|
+
* `HINT_PART_*` constants in those states instead. The collapse affordance is
|
|
28
|
+
* appended AFTER cancel by `buildHintText` so the resting core stays a contiguous
|
|
29
|
+
* prefix substring of the rendered line. On narrow terminals the collapse tail
|
|
30
|
+
* clips with `…` (`OneLineClippedText`); the core is preserved.
|
|
31
|
+
*/
|
|
32
|
+
export const HINT_SINGLE = [HINT_PART_ENTER, HINT_PART_NAV, HINT_PART_NOTES, HINT_PART_CANCEL].join(" · ");
|
|
33
|
+
export const HINT_MULTI = [HINT_PART_ENTER, HINT_PART_NAV, HINT_PART_NOTES, HINT_PART_TAB, HINT_PART_CANCEL].join(
|
|
34
|
+
" · ",
|
|
35
|
+
);
|
|
36
|
+
/** Single-line footer shown by `QuestionnaireSession` when `state.collapsed === true`. Bypasses `buildHintText`. */
|
|
37
|
+
export const COLLAPSED_HINT = [HINT_PART_EXPAND, HINT_PART_CANCEL].join(" · ");
|
|
38
|
+
export const REVIEW_HEADING = "Review your answers";
|
|
39
|
+
export const READY_PROMPT = "Ready to submit your answers?";
|
|
40
|
+
export const INCOMPLETE_WARNING_PREFIX = "⚠ Answer remaining questions before submitting:";
|
|
41
|
+
|
|
42
|
+
const OVERFLOW_UP = "↑";
|
|
43
|
+
const OVERFLOW_DOWN = "↓";
|
|
44
|
+
const OVERFLOW_BOTH = "↕";
|
|
45
|
+
|
|
46
|
+
export type DialogState = QuestionnaireState;
|
|
47
|
+
|
|
48
|
+
/** Per-tick projection of dialog state. Written by the adapter; read by the strategy thunk. */
|
|
49
|
+
export interface DialogProps {
|
|
50
|
+
state: DialogState;
|
|
51
|
+
activePreviewPane: StatefulView<PreviewPaneProps>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Construction-time config for `DialogView`. Frozen after construction. */
|
|
55
|
+
export interface DialogConfig {
|
|
56
|
+
theme: Theme;
|
|
57
|
+
questions: readonly QuestionData[];
|
|
58
|
+
tabBar: TabBar | undefined;
|
|
59
|
+
notesInput: Input;
|
|
60
|
+
isMulti: boolean;
|
|
61
|
+
tabsByIndex: ReadonlyArray<TabComponents>;
|
|
62
|
+
/** Optional so single-question mode and non-submit tests can omit it; SubmitTabStrategy falls back to Spacer rows. */
|
|
63
|
+
submitPicker?: Component;
|
|
64
|
+
/** Worst-case body height across all tabs/options. Determines the stable overall dialog footprint. */
|
|
65
|
+
getBodyHeight: (width: number) => number;
|
|
66
|
+
/** Body height of the CURRENTLY active tab/option. The chrome subtracts this from `getBodyHeight` to absorb the residual OUTSIDE the bordered region. */
|
|
67
|
+
getCurrentBodyHeight: (width: number) => number;
|
|
68
|
+
/** Terminal height getter. Mirrors `getTerminalWidth` — reads `tui.terminal.rows` at render time. */
|
|
69
|
+
getTerminalRows: () => number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The 7th renderable, promoted from a structural literal to a named class so
|
|
74
|
+
* all view-layer components share one explicit `implements StatefulView<P>`
|
|
75
|
+
* contract. `setProps(DialogProps)` writes the live cell read by the
|
|
76
|
+
* strategy thunk during `render()`. `liveProps.activePreviewPane` is a
|
|
77
|
+
* resolved pane reference threaded by the adapter per tick — the dialog
|
|
78
|
+
* itself does not derive it.
|
|
79
|
+
*/
|
|
80
|
+
export class DialogView implements StatefulView<DialogProps> {
|
|
81
|
+
private liveProps: DialogProps;
|
|
82
|
+
private readonly config: DialogConfig;
|
|
83
|
+
private readonly questionStrategy: TabContentStrategy;
|
|
84
|
+
private readonly submitStrategy: TabContentStrategy | undefined;
|
|
85
|
+
private readonly maxFooterRowCount: number;
|
|
86
|
+
|
|
87
|
+
constructor(config: DialogConfig, initialProps: DialogProps) {
|
|
88
|
+
this.config = config;
|
|
89
|
+
this.liveProps = initialProps;
|
|
90
|
+
this.questionStrategy = new QuestionTabStrategy({
|
|
91
|
+
theme: config.theme,
|
|
92
|
+
questions: config.questions,
|
|
93
|
+
getPreviewPane: () => this.liveProps.activePreviewPane,
|
|
94
|
+
tabsByIndex: config.tabsByIndex,
|
|
95
|
+
notesInput: config.notesInput,
|
|
96
|
+
isMulti: config.isMulti,
|
|
97
|
+
getCurrentBodyHeight: config.getCurrentBodyHeight,
|
|
98
|
+
});
|
|
99
|
+
this.submitStrategy = config.isMulti
|
|
100
|
+
? new SubmitTabStrategy({
|
|
101
|
+
theme: config.theme,
|
|
102
|
+
questions: config.questions,
|
|
103
|
+
submitPicker: config.submitPicker,
|
|
104
|
+
})
|
|
105
|
+
: undefined;
|
|
106
|
+
this.maxFooterRowCount = Math.max(this.questionStrategy.footerRowCount, this.submitStrategy?.footerRowCount ?? 0);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
setProps(props: DialogProps): void {
|
|
110
|
+
this.liveProps = props;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
handleInput(_data: string): void {}
|
|
114
|
+
|
|
115
|
+
// Invalidation is driven by `QuestionnairePropsAdapter.invalidate()`, which
|
|
116
|
+
// owns the full set of renderables (binding registries + extras like
|
|
117
|
+
// `notesInput`). DialogView has no cached layout of its own.
|
|
118
|
+
invalidate(): void {}
|
|
119
|
+
|
|
120
|
+
render(width: number): string[] {
|
|
121
|
+
const state = this.liveProps.state;
|
|
122
|
+
const onSubmit = this.config.isMulti && state.currentTab === this.config.questions.length;
|
|
123
|
+
const strategy = onSubmit && this.submitStrategy ? this.submitStrategy : this.questionStrategy;
|
|
124
|
+
|
|
125
|
+
// Cache heading rows (avoid double construction in render and container build).
|
|
126
|
+
const headingRowCache = strategy.headingRows(state);
|
|
127
|
+
const headingCount = headingRowCache.length;
|
|
128
|
+
|
|
129
|
+
// Build container WITHOUT residual spacer — spacer handled below based on overflow.
|
|
130
|
+
const natural = this.buildContainerFromStrategy(strategy, headingRowCache).render(width);
|
|
131
|
+
|
|
132
|
+
// Fixed region sizes (deterministic from structure).
|
|
133
|
+
// TabBar.render() returns [tabLine, ""] — always 2 rows.
|
|
134
|
+
const topFixed = 1 + (this.config.isMulti && this.config.tabBar ? 2 : 0) + 1;
|
|
135
|
+
const bottomFixed = 1 + strategy.footerRowCount;
|
|
136
|
+
const middleRows = natural.length - topFixed - bottomFixed;
|
|
137
|
+
|
|
138
|
+
// Residual spacer: equalizes total height across tabs (only needed when no overflow).
|
|
139
|
+
const spacerRows = Math.max(
|
|
140
|
+
0,
|
|
141
|
+
this.config.getBodyHeight(width) +
|
|
142
|
+
this.maxFooterRowCount -
|
|
143
|
+
strategy.bodyHeight(width, state) -
|
|
144
|
+
strategy.footerRowCount,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const termRows = this.config.getTerminalRows();
|
|
148
|
+
|
|
149
|
+
if (natural.length + spacerRows <= termRows) {
|
|
150
|
+
// No overflow — add residual spacer rows after footer.
|
|
151
|
+
return spacerRows > 0 ? [...natural, ...Array<string>(spacerRows).fill("")] : natural;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// OVERFLOW — apply 3-region partition with scroll-to-focus.
|
|
155
|
+
const availableMiddle = Math.max(0, termRows - topFixed - bottomFixed);
|
|
156
|
+
if (availableMiddle === 0) {
|
|
157
|
+
// Terminal too small for any middle content — show just chrome.
|
|
158
|
+
const chromeOnly = [...natural.slice(0, topFixed), ...natural.slice(natural.length - bottomFixed)];
|
|
159
|
+
return chromeOnly.length > termRows ? chromeOnly.slice(0, termRows) : chromeOnly;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const bodyRange = strategy.focusedItemRowRange(width, state);
|
|
163
|
+
|
|
164
|
+
// Compute scroll window centered on the focused option.
|
|
165
|
+
let scrollStart: number;
|
|
166
|
+
if (bodyRange) {
|
|
167
|
+
const focusedRowInMiddle = headingCount + bodyRange[0];
|
|
168
|
+
const focusedHeight = bodyRange[1] - bodyRange[0];
|
|
169
|
+
// Center the focused item vertically in the available middle space.
|
|
170
|
+
const idealStart = focusedRowInMiddle - Math.floor(Math.max(0, availableMiddle - focusedHeight) / 2);
|
|
171
|
+
scrollStart = Math.max(0, Math.min(idealStart, middleRows - availableMiddle));
|
|
172
|
+
} else {
|
|
173
|
+
// No interactive focus (submit tab) — top-anchor the middle.
|
|
174
|
+
scrollStart = 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const scrollableMiddle = natural.slice(topFixed + scrollStart, topFixed + scrollStart + availableMiddle);
|
|
178
|
+
|
|
179
|
+
const hasUp = scrollStart > 0;
|
|
180
|
+
const hasDown = scrollStart + availableMiddle < middleRows;
|
|
181
|
+
if (hasUp && hasDown && scrollableMiddle.length === 1) {
|
|
182
|
+
// Single-row middle: combined ↕ avoids the prior collision where ↓ overwrote ↑.
|
|
183
|
+
scrollableMiddle[0] = this.config.theme.fg("dim", OVERFLOW_BOTH);
|
|
184
|
+
} else {
|
|
185
|
+
if (hasUp && scrollableMiddle.length > 0) {
|
|
186
|
+
scrollableMiddle[0] = this.config.theme.fg("dim", OVERFLOW_UP);
|
|
187
|
+
}
|
|
188
|
+
if (hasDown && scrollableMiddle.length > 0) {
|
|
189
|
+
scrollableMiddle[scrollableMiddle.length - 1] = this.config.theme.fg("dim", OVERFLOW_DOWN);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const result = [
|
|
194
|
+
...natural.slice(0, topFixed),
|
|
195
|
+
...scrollableMiddle,
|
|
196
|
+
...natural.slice(natural.length - bottomFixed),
|
|
197
|
+
];
|
|
198
|
+
// Safety: never exceed terminal rows (covers the availableMiddle === 0 case
|
|
199
|
+
// where topFixed + bottomFixed > termRows).
|
|
200
|
+
return result.length > termRows ? result.slice(0, termRows) : result;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private buildContainerFromStrategy(strategy: TabContentStrategy, headingRowCache: Component[]): Container {
|
|
204
|
+
const { theme, isMulti, tabBar } = this.config;
|
|
205
|
+
const state = this.liveProps.state;
|
|
206
|
+
const container = new Container();
|
|
207
|
+
const border = () => new DynamicBorder((s) => theme.fg("accent", s));
|
|
208
|
+
|
|
209
|
+
container.addChild(border());
|
|
210
|
+
if (isMulti && tabBar) container.addChild(tabBar);
|
|
211
|
+
container.addChild(new Spacer(1));
|
|
212
|
+
|
|
213
|
+
for (const c of headingRowCache) container.addChild(c);
|
|
214
|
+
container.addChild(strategy.bodyComponent(state));
|
|
215
|
+
container.addChild(new Spacer(1));
|
|
216
|
+
for (const c of strategy.midRows(state)) container.addChild(c);
|
|
217
|
+
|
|
218
|
+
container.addChild(border());
|
|
219
|
+
for (const c of strategy.footerRows(state)) container.addChild(c);
|
|
220
|
+
|
|
221
|
+
// BodyResidualSpacer moved out of Container — handled in render().
|
|
222
|
+
return container;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/view/props-adapter.ts
|
|
2
|
+
// Regenerate with: python3 scripts/build-adapters.py
|
|
3
|
+
import type { Input } from "@earendil-works/pi-tui";
|
|
4
|
+
import type { BindingContext, PerTabBindingContext } from "../state/selectors/contract.js";
|
|
5
|
+
import { selectActivePreviewPaneIndex } from "../state/selectors/derivations.js";
|
|
6
|
+
import { selectActiveView } from "../state/selectors/focus.js";
|
|
7
|
+
import type { QuestionnaireState } from "../state/state.js";
|
|
8
|
+
import type { QuestionData } from "../tool/types.js";
|
|
9
|
+
import type { BoundGlobalBinding, BoundPerTabBinding } from "./component-binding.js";
|
|
10
|
+
import type { WrappingSelectItem } from "./components/wrapping-select.js";
|
|
11
|
+
import type { TabComponents } from "./tab-components.js";
|
|
12
|
+
|
|
13
|
+
/** Cache-invalidation contract used by the adapter. `pi-tui` `Component` already satisfies it. */
|
|
14
|
+
interface Invalidatable {
|
|
15
|
+
invalidate(): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Reads pi-tui Input's private `cursor` field via type escape with full runtime validation.
|
|
20
|
+
* Returns `undefined` on any failure → graceful degradation to end-of-buffer cursor.
|
|
21
|
+
* Follow-up: replace with Input.getCursorOffset() when pi-tui exposes a public API.
|
|
22
|
+
*/
|
|
23
|
+
function getInputCursorOffset(input: Input): number | undefined {
|
|
24
|
+
const raw = (input as unknown as { cursor?: unknown }).cursor;
|
|
25
|
+
if (typeof raw !== "number") return undefined;
|
|
26
|
+
if (!Number.isSafeInteger(raw)) return undefined;
|
|
27
|
+
const value = input.getValue();
|
|
28
|
+
if (raw < 0 || raw > value.length) return undefined;
|
|
29
|
+
return raw;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface QuestionnairePropsAdapterConfig {
|
|
33
|
+
tui: { requestRender(): void };
|
|
34
|
+
questions: readonly QuestionData[];
|
|
35
|
+
itemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;
|
|
36
|
+
tabsByIndex: ReadonlyArray<TabComponents>;
|
|
37
|
+
inlineInput: Input;
|
|
38
|
+
globalBindings: ReadonlyArray<BoundGlobalBinding>;
|
|
39
|
+
perTabBindings: ReadonlyArray<BoundPerTabBinding>;
|
|
40
|
+
/**
|
|
41
|
+
* Renderables not reached by the binding registries (e.g. the notes
|
|
42
|
+
* `Input`, which is typed into directly and has no props). Walked by
|
|
43
|
+
* `invalidate()` after the binding-driven components.
|
|
44
|
+
*/
|
|
45
|
+
extraInvalidatables?: ReadonlyArray<Invalidatable>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* View fan-out: drives every component setter from the canonical state via
|
|
50
|
+
* two binding registries. `globalBindings` covers the cross-tab components
|
|
51
|
+
* (dialog, submitPicker?, tabBar?); `perTabBindings` covers the
|
|
52
|
+
* per-tab kinds (optionList, preview, multiSelect?). The hand-coded fan-out
|
|
53
|
+
* collapses to one global loop + one nested per-tab loop. The inline-Other
|
|
54
|
+
* value is read from the headless `inlineInput` instance per tick into ctx so
|
|
55
|
+
* `selectOptionListProps` sees the live value.
|
|
56
|
+
*/
|
|
57
|
+
export class QuestionnairePropsAdapter {
|
|
58
|
+
private readonly tui: QuestionnairePropsAdapterConfig["tui"];
|
|
59
|
+
private readonly questions: readonly QuestionData[];
|
|
60
|
+
private readonly itemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;
|
|
61
|
+
private readonly tabsByIndex: ReadonlyArray<TabComponents>;
|
|
62
|
+
private readonly inlineInput: Input;
|
|
63
|
+
private readonly globalBindings: ReadonlyArray<BoundGlobalBinding>;
|
|
64
|
+
private readonly perTabBindings: ReadonlyArray<BoundPerTabBinding>;
|
|
65
|
+
private readonly extraInvalidatables: ReadonlyArray<Invalidatable>;
|
|
66
|
+
|
|
67
|
+
constructor(config: QuestionnairePropsAdapterConfig) {
|
|
68
|
+
this.tui = config.tui;
|
|
69
|
+
this.questions = config.questions;
|
|
70
|
+
this.itemsByTab = config.itemsByTab;
|
|
71
|
+
this.tabsByIndex = config.tabsByIndex;
|
|
72
|
+
this.inlineInput = config.inlineInput;
|
|
73
|
+
this.globalBindings = config.globalBindings;
|
|
74
|
+
this.perTabBindings = config.perTabBindings;
|
|
75
|
+
this.extraInvalidatables = config.extraInvalidatables ?? [];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
apply(state: QuestionnaireState): void {
|
|
79
|
+
const totalQuestions = this.questions.length;
|
|
80
|
+
const activeView = selectActiveView(state, totalQuestions);
|
|
81
|
+
const paneIndex = selectActivePreviewPaneIndex(state.currentTab, totalQuestions);
|
|
82
|
+
const activePreviewPane = this.tabsByIndex[paneIndex]?.preview ?? this.tabsByIndex[0]!.preview;
|
|
83
|
+
|
|
84
|
+
const ctx: BindingContext = {
|
|
85
|
+
questions: this.questions,
|
|
86
|
+
itemsByTab: this.itemsByTab,
|
|
87
|
+
totalQuestions,
|
|
88
|
+
activeView,
|
|
89
|
+
inputBuffer: this.inlineInput.getValue(),
|
|
90
|
+
inputCursorOffset: getInputCursorOffset(this.inlineInput),
|
|
91
|
+
activePreviewPane,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
for (const binding of this.globalBindings) {
|
|
95
|
+
binding.apply(state, ctx);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (let i = 0; i < this.tabsByIndex.length; i++) {
|
|
99
|
+
const tab = this.tabsByIndex[i]!;
|
|
100
|
+
const tabCtx: PerTabBindingContext = { ...ctx, tab, i };
|
|
101
|
+
for (const binding of this.perTabBindings) {
|
|
102
|
+
binding.apply(state, tabCtx);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this.tui.requestRender();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Invalidates every owned renderable. Called by the session in place of
|
|
111
|
+
* the old `dialog.invalidate()` forwarding chain — DialogView no longer
|
|
112
|
+
* reaches into siblings (tabBar, notesInput, activePreviewPane).
|
|
113
|
+
* Iterates the same registries used by `apply()` plus
|
|
114
|
+
* `extraInvalidatables` for components outside the binding system.
|
|
115
|
+
*/
|
|
116
|
+
invalidate(): void {
|
|
117
|
+
for (const b of this.globalBindings) b.invalidate();
|
|
118
|
+
for (const tab of this.tabsByIndex) {
|
|
119
|
+
tab.optionList.invalidate();
|
|
120
|
+
tab.preview.invalidate();
|
|
121
|
+
tab.multiSelect?.invalidate();
|
|
122
|
+
}
|
|
123
|
+
for (const x of this.extraInvalidatables) x.invalidate();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/view/stateful-view.ts
|
|
2
|
+
// Regenerate with: python3 scripts/build-adapters.py
|
|
3
|
+
import type { Component } from "@earendil-works/pi-tui";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generic prop-driven component contract. Every renderable owns its own `P` shape;
|
|
7
|
+
* the adapter computes `P` from canonical state via per-component selectors and
|
|
8
|
+
* pushes it via `setProps`. `focused: boolean` is a field on `P` only where the
|
|
9
|
+
* component needs it.
|
|
10
|
+
*/
|
|
11
|
+
export interface StatefulView<P> extends Component {
|
|
12
|
+
setProps(props: P): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Discriminated focus union — encodes the three-cell focus invariant
|
|
17
|
+
* (`notesVisible`, submit-tab, options). Dispatcher cascade (`key-router.ts`)
|
|
18
|
+
* and reducer's defensive clears (`state-reducer.ts`) enforce mutual exclusion;
|
|
19
|
+
* this type makes it explicit so per-component `focused: boolean` flags
|
|
20
|
+
* derive from one equality check against this discriminant rather than
|
|
21
|
+
* parallel boolean reads.
|
|
22
|
+
*
|
|
23
|
+
* Priority order: notes > submit > options. Matches the dispatcher
|
|
24
|
+
* cascade exactly so the union is observably equivalent to today's reads.
|
|
25
|
+
*/
|
|
26
|
+
export type ActiveView = "notes" | "options" | "submit";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/view/tab-components.ts
|
|
2
|
+
// Regenerate with: python3 scripts/build-adapters.py
|
|
3
|
+
import type { MultiSelectView } from "./components/multi-select-view.js";
|
|
4
|
+
import type { OptionListViewProps } from "./components/option-list-view.js";
|
|
5
|
+
import type { PreviewPane } from "./components/preview/preview-pane.js";
|
|
6
|
+
import type { StatefulView } from "./stateful-view.js";
|
|
7
|
+
|
|
8
|
+
export interface TabBodyHeights {
|
|
9
|
+
current: number;
|
|
10
|
+
max: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface TabComponents {
|
|
14
|
+
optionList: StatefulView<OptionListViewProps>;
|
|
15
|
+
preview: PreviewPane;
|
|
16
|
+
multiSelect?: MultiSelectView;
|
|
17
|
+
bodyHeights: (width: number) => TabBodyHeights;
|
|
18
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// GENERATED — DO NOT EDIT. Source: adapter-src/pi/extensions/ask-user-question/view/tab-content-strategy.ts
|
|
2
|
+
// Regenerate with: python3 scripts/build-adapters.py
|
|
3
|
+
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { type Component, Container, type Input, Spacer, Text, truncateToWidth } from "@earendil-works/pi-tui";
|
|
5
|
+
import { t } from "../state/i18n-bridge.js";
|
|
6
|
+
import { formatAnswerScalar } from "../tool/format-answer.js";
|
|
7
|
+
import type { QuestionData } from "../tool/types.js";
|
|
8
|
+
import type { PreviewPane, PreviewPaneProps } from "./components/preview/preview-pane.js";
|
|
9
|
+
import {
|
|
10
|
+
type DialogState,
|
|
11
|
+
HINT_PART_CANCEL,
|
|
12
|
+
HINT_PART_COLLAPSE,
|
|
13
|
+
HINT_PART_ENTER,
|
|
14
|
+
HINT_PART_NAV,
|
|
15
|
+
HINT_PART_NOTES,
|
|
16
|
+
HINT_PART_TAB,
|
|
17
|
+
HINT_PART_TOGGLE,
|
|
18
|
+
INCOMPLETE_WARNING_PREFIX,
|
|
19
|
+
READY_PROMPT,
|
|
20
|
+
REVIEW_HEADING,
|
|
21
|
+
} from "./dialog-builder.js";
|
|
22
|
+
import type { StatefulView } from "./stateful-view.js";
|
|
23
|
+
import type { TabComponents } from "./tab-components.js";
|
|
24
|
+
|
|
25
|
+
const NOTES_HEADER = "Notes:";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Single-row, width-clipped chrome cell. The footer row count is invariant
|
|
29
|
+
* (`QuestionTabStrategy.footerRowCount = 2`) — pi-tui's `Text` word-wraps when
|
|
30
|
+
* the styled hint exceeds `width`, inflating that row count and desyncing the
|
|
31
|
+
* `bodyHeight + footerRowCount` math in `DialogView.render`. Clipping with
|
|
32
|
+
* `truncateToWidth` (ANSI-aware, matches `multi-select-view.ts` usage) keeps
|
|
33
|
+
* the hint on one line; the collapse affordance falls off the right edge with
|
|
34
|
+
* `…` on terminals too narrow to advertise it.
|
|
35
|
+
*/
|
|
36
|
+
class OneLineClippedText implements Component {
|
|
37
|
+
constructor(
|
|
38
|
+
private readonly text: string,
|
|
39
|
+
private readonly paddingLeft: number = 0,
|
|
40
|
+
) {}
|
|
41
|
+
|
|
42
|
+
render(width: number): string[] {
|
|
43
|
+
const pad = " ".repeat(this.paddingLeft);
|
|
44
|
+
const avail = Math.max(0, width - this.paddingLeft);
|
|
45
|
+
return [pad + truncateToWidth(this.text, avail, "…", false)];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
invalidate(): void {}
|
|
49
|
+
|
|
50
|
+
handleInput(_data: string): void {}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Per-tab content provider. Pure functional — closes over construction-time
|
|
55
|
+
* config; per-tick state threads through method args. The chrome wrapper
|
|
56
|
+
* enforces height equality across tabs via `bodyHeight + footerRowCount`.
|
|
57
|
+
*/
|
|
58
|
+
export interface TabContentStrategy {
|
|
59
|
+
/** Total RENDERED footer rows — MUST equal what `footerRows()` actually emits. Drives residual math. */
|
|
60
|
+
readonly footerRowCount: number;
|
|
61
|
+
|
|
62
|
+
/** Variable rows above the body, after top chrome (border + tabBar + Spacer). */
|
|
63
|
+
headingRows(state: DialogState): Component[];
|
|
64
|
+
|
|
65
|
+
/** Body Component placed at the body slot. */
|
|
66
|
+
bodyComponent(state: DialogState): Component;
|
|
67
|
+
|
|
68
|
+
/** Natural rendered height of `bodyComponent(state)` at given width. */
|
|
69
|
+
bodyHeight(width: number, state: DialogState): number;
|
|
70
|
+
|
|
71
|
+
/** Optional rows between body's trailing Spacer and the bottom border. */
|
|
72
|
+
midRows(state: DialogState): Component[];
|
|
73
|
+
|
|
74
|
+
/** Footer rows below the bottom border. Rendered row count MUST equal `footerRowCount`. */
|
|
75
|
+
footerRows(state: DialogState): Component[];
|
|
76
|
+
|
|
77
|
+
/** Row range of the focused item within the body's rendered output, or undefined if no interactive focus. */
|
|
78
|
+
focusedItemRowRange(width: number, state: DialogState): [number, number] | undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface QuestionTabStrategyConfig {
|
|
82
|
+
theme: Theme;
|
|
83
|
+
questions: readonly QuestionData[];
|
|
84
|
+
getPreviewPane: () => StatefulView<PreviewPaneProps>;
|
|
85
|
+
tabsByIndex: ReadonlyArray<TabComponents>;
|
|
86
|
+
notesInput: Input;
|
|
87
|
+
isMulti: boolean;
|
|
88
|
+
getCurrentBodyHeight: (width: number) => number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export class QuestionTabStrategy implements TabContentStrategy {
|
|
92
|
+
/** Spacer(1) + OneLineClippedText(hint, 1) = 2 rendered rows. */
|
|
93
|
+
readonly footerRowCount = 2;
|
|
94
|
+
|
|
95
|
+
constructor(private readonly config: QuestionTabStrategyConfig) {}
|
|
96
|
+
|
|
97
|
+
headingRows(state: DialogState): Component[] {
|
|
98
|
+
const out: Component[] = [];
|
|
99
|
+
const question = this.config.questions[state.currentTab];
|
|
100
|
+
// In multi-question mode the tab bar already shows the header; suppress the inline badge.
|
|
101
|
+
if (!this.config.isMulti && question?.header && question.header.length > 0) {
|
|
102
|
+
out.push(new Text(this.config.theme.bg("selectedBg", ` ${question.header} `), 1, 0));
|
|
103
|
+
out.push(new Spacer(1));
|
|
104
|
+
}
|
|
105
|
+
if (question) {
|
|
106
|
+
out.push(new Text(this.config.theme.bold(question.question), 1, 0));
|
|
107
|
+
out.push(new Spacer(1));
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
bodyComponent(state: DialogState): Component {
|
|
113
|
+
const question = this.config.questions[state.currentTab];
|
|
114
|
+
const mso = this.config.tabsByIndex[state.currentTab]?.multiSelect;
|
|
115
|
+
if (question?.multiSelect === true && mso) return mso;
|
|
116
|
+
return this.config.getPreviewPane();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
bodyHeight(width: number, _state: DialogState): number {
|
|
120
|
+
return this.config.getCurrentBodyHeight(width);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
midRows(state: DialogState): Component[] {
|
|
124
|
+
if (!state.notesVisible) return [];
|
|
125
|
+
return [
|
|
126
|
+
new Text(this.config.theme.fg("muted", t("notes.header", NOTES_HEADER)), 1, 0),
|
|
127
|
+
this.config.notesInput,
|
|
128
|
+
new Spacer(1),
|
|
129
|
+
];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
footerRows(state: DialogState): Component[] {
|
|
133
|
+
const question = this.config.questions[state.currentTab];
|
|
134
|
+
// OneLineClippedText (not pi-tui `Text`) — `buildHintText` includes the collapse
|
|
135
|
+
// affordance, pushing the rendered string past 80 columns; `Text` would wrap and
|
|
136
|
+
// break the strategy's `footerRowCount = 2` invariant. Clipping on narrow terminals
|
|
137
|
+
// drops the trailing parts (collapse hint first, then cancel) with `…`.
|
|
138
|
+
return [
|
|
139
|
+
new Spacer(1),
|
|
140
|
+
new OneLineClippedText(this.config.theme.fg("dim", buildHintText(question, this.config.isMulti, state)), 1),
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
focusedItemRowRange(width: number, state: DialogState): [number, number] | undefined {
|
|
145
|
+
const question = this.config.questions[state.currentTab];
|
|
146
|
+
const mso = this.config.tabsByIndex[state.currentTab]?.multiSelect;
|
|
147
|
+
if (question?.multiSelect === true && mso) return mso.focusedItemRowRange(width);
|
|
148
|
+
return (this.config.getPreviewPane() as unknown as PreviewPane).focusedItemRowRange(width);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface SubmitTabStrategyConfig {
|
|
153
|
+
theme: Theme;
|
|
154
|
+
questions: readonly QuestionData[];
|
|
155
|
+
submitPicker: Component | undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export class SubmitTabStrategy implements TabContentStrategy {
|
|
159
|
+
/** Spacer(1) + Text(prompt, 1) + Spacer(1) + submitPicker(2) = 5 rendered rows. Fallback path lands at 5 via 2 trailing Spacer(1)s. */
|
|
160
|
+
readonly footerRowCount = 5;
|
|
161
|
+
|
|
162
|
+
constructor(private readonly config: SubmitTabStrategyConfig) {}
|
|
163
|
+
|
|
164
|
+
headingRows(_state: DialogState): Component[] {
|
|
165
|
+
return [
|
|
166
|
+
new Text(this.config.theme.bold(this.config.theme.fg("accent", t("review.heading", REVIEW_HEADING))), 1, 0),
|
|
167
|
+
new Spacer(1),
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
bodyComponent(state: DialogState): Component {
|
|
172
|
+
const c = new Container();
|
|
173
|
+
for (let i = 0; i < this.config.questions.length; i++) {
|
|
174
|
+
const q = this.config.questions[i];
|
|
175
|
+
const a = state.answers.get(i);
|
|
176
|
+
if (!a) continue;
|
|
177
|
+
const label = q.header && q.header.length > 0 ? q.header : `Q${i + 1}`;
|
|
178
|
+
const answerText = formatAnswerScalar(a, "summary");
|
|
179
|
+
c.addChild(new Text(this.config.theme.fg("muted", ` ● ${label}`), 1, 0));
|
|
180
|
+
c.addChild(
|
|
181
|
+
new Text(` ${this.config.theme.fg("muted", "→")} ${this.config.theme.fg("text", answerText)}`, 1, 0),
|
|
182
|
+
);
|
|
183
|
+
if (a.notes && a.notes.length > 0) {
|
|
184
|
+
c.addChild(new Text(this.config.theme.fg("dim", ` notes: ${a.notes}`), 1, 0));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return c;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
bodyHeight(width: number, state: DialogState): number {
|
|
191
|
+
return this.bodyComponent(state).render(width).length;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
midRows(_state: DialogState): Component[] {
|
|
195
|
+
return [];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
footerRows(state: DialogState): Component[] {
|
|
199
|
+
const missing: string[] = [];
|
|
200
|
+
for (let i = 0; i < this.config.questions.length; i++) {
|
|
201
|
+
const q = this.config.questions[i];
|
|
202
|
+
if (!state.answers.has(i)) {
|
|
203
|
+
missing.push(q.header && q.header.length > 0 ? q.header : `Q${i + 1}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const promptText =
|
|
207
|
+
missing.length === 0
|
|
208
|
+
? this.config.theme.fg("muted", t("review.ready", READY_PROMPT))
|
|
209
|
+
: this.config.theme.fg(
|
|
210
|
+
"warning",
|
|
211
|
+
`${t("review.incomplete", INCOMPLETE_WARNING_PREFIX)} ${missing.join(", ")}`,
|
|
212
|
+
);
|
|
213
|
+
const out: Component[] = [new Spacer(1), new Text(promptText, 1, 0), new Spacer(1)];
|
|
214
|
+
if (this.config.submitPicker) {
|
|
215
|
+
out.push(this.config.submitPicker);
|
|
216
|
+
} else {
|
|
217
|
+
// Padding when the picker isn't wired — keeps rendered row count at footerRowCount=5.
|
|
218
|
+
out.push(new Spacer(1));
|
|
219
|
+
out.push(new Spacer(1));
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
focusedItemRowRange(_width: number, _state: DialogState): [number, number] | undefined {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Build the controls hint line. Order:
|
|
231
|
+
* Enter · ↑/↓ [· Space toggle] [· n notes] [· Tab switch] · Esc · Ctrl+] collapse
|
|
232
|
+
*
|
|
233
|
+
* `NOTES` is now part of the resting (notes-closed) core — it renders on every
|
|
234
|
+
* question tab and drops when `state.notesVisible` flips the editor open OR while
|
|
235
|
+
* `state.inputMode` has the "Type something." row capturing text (`n` would insert
|
|
236
|
+
* a literal character there, so advertising it would lie). The collapse affordance
|
|
237
|
+
* is appended last so the resting core stays a contiguous substring even when the
|
|
238
|
+
* trailing part is clipped by `OneLineClippedText` on terminals < ~95 cols. This
|
|
239
|
+
* is the trade we picked over wrapping (which would inflate `footerRowCount` and
|
|
240
|
+
* desync the height math in `DialogView.render`).
|
|
241
|
+
*/
|
|
242
|
+
export function buildHintText(question: QuestionData | undefined, isMulti: boolean, state: DialogState): string {
|
|
243
|
+
const parts: string[] = [t("hint.enter", HINT_PART_ENTER), t("hint.navigate", HINT_PART_NAV)];
|
|
244
|
+
if (question?.multiSelect === true) parts.push(t("hint.toggle", HINT_PART_TOGGLE));
|
|
245
|
+
if (question && !state.notesVisible && !state.inputMode) {
|
|
246
|
+
parts.push(t("hint.notes", HINT_PART_NOTES));
|
|
247
|
+
}
|
|
248
|
+
if (isMulti) parts.push(t("hint.tab", HINT_PART_TAB));
|
|
249
|
+
parts.push(t("hint.cancel", HINT_PART_CANCEL));
|
|
250
|
+
parts.push(t("hint.collapse", HINT_PART_COLLAPSE));
|
|
251
|
+
return parts.join(" · ");
|
|
252
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "feature-forge-pi-adapter",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi-package"
|
|
7
|
+
],
|
|
8
|
+
"pi": {
|
|
9
|
+
"skills": [
|
|
10
|
+
"./skills"
|
|
11
|
+
],
|
|
12
|
+
"extensions": [
|
|
13
|
+
"./extensions/ask-user-question/index.ts"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"pi-subagents": {
|
|
17
|
+
"agents": [
|
|
18
|
+
"./agents"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
23
|
+
"@earendil-works/pi-tui": "*",
|
|
24
|
+
"typebox": "*"
|
|
25
|
+
}
|
|
26
|
+
}
|