savepoint 1.0.4 → 1.0.6
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/package.json +10 -3
- package/savepoint.exe +0 -0
- package/.claude/settings.local.json +0 -38
- package/.github/workflows/ci.yml +0 -20
- package/.golangci.yml +0 -11
- package/.prettierignore +0 -4
- package/.savepoint/Design.md +0 -207
- package/.savepoint/PRD.md +0 -58
- package/.savepoint/config.yml +0 -27
- package/.savepoint/releases/v1/epics/E01-go-setup/E01-Detail.md +0 -39
- package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T001-init-module.md +0 -42
- package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T002-entrypoint.md +0 -23
- package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T003-directory-structure.md +0 -24
- package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T004-makefile.md +0 -23
- package/.savepoint/releases/v1/epics/E02-data-readers/E02-Detail.md +0 -61
- package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T001-task-struct.md +0 -29
- package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T002-frontmatter-parser.md +0 -30
- package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T003-router-reader.md +0 -29
- package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T004-config-reader.md +0 -29
- package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T005-discovery.md +0 -30
- package/.savepoint/releases/v1/epics/E03-board-tui-core/E03-Detail.md +0 -38
- package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T001-model.md +0 -29
- package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T002-update-loop.md +0 -30
- package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T003-view.md +0 -34
- package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T004-styles.md +0 -29
- package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T005-layout.md +0 -42
- package/.savepoint/releases/v1/epics/E04-board-components/E04-Detail.md +0 -44
- package/.savepoint/releases/v1/epics/E04-board-components/tasks/T001-column.md +0 -34
- package/.savepoint/releases/v1/epics/E04-board-components/tasks/T002-card.md +0 -33
- package/.savepoint/releases/v1/epics/E04-board-components/tasks/T003-epic-panel.md +0 -49
- package/.savepoint/releases/v1/epics/E04-board-components/tasks/T004-detail-overlay.md +0 -40
- package/.savepoint/releases/v1/epics/E04-board-components/tasks/T005-release-dropdown.md +0 -33
- package/.savepoint/releases/v1/epics/E04-board-components/tasks/T006-help-overlay.md +0 -34
- package/.savepoint/releases/v1/epics/E05-phase-transitions/E05-Detail.md +0 -38
- package/.savepoint/releases/v1/epics/E05-phase-transitions/tasks/T001-phase-stepping.md +0 -29
- package/.savepoint/releases/v1/epics/E05-phase-transitions/tasks/T002-gates.md +0 -31
- package/.savepoint/releases/v1/epics/E05-phase-transitions/tasks/T003-write-task.md +0 -31
- package/.savepoint/releases/v1/epics/E05-phase-transitions/tasks/T004-write-router.md +0 -31
- package/.savepoint/releases/v1/epics/E06-atari-noir-layout/E06-Detail.md +0 -62
- package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T001-color-system.md +0 -39
- package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T002-header-and-dividers.md +0 -52
- package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T003-footer-status-bar.md +0 -52
- package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T004-component-refinement.md +0 -53
- package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T005-restore-nav-hints.md +0 -39
- package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T007-detail-card-fixes.md +0 -36
- package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T008-checkbox-states.md +0 -40
- package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T009-router-priority-marker.md +0 -48
- package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T010-auto-refresh-watcher.md +0 -66
- package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/Design.md +0 -39
- package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/tasks/T001-archive-epics.md +0 -20
- package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/tasks/T002-rewrite-prd.md +0 -22
- package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/tasks/T003-create-epic-stubs.md +0 -24
- package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/tasks/T004-update-router.md +0 -22
- package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/Design.md +0 -118
- package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/handoff.md +0 -9
- package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T001-package-baseline.md +0 -45
- package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T002-typescript-build.md +0 -48
- package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T003-vitest-smoke.md +0 -43
- package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T004-lint-format-gates.md +0 -45
- package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T005-scaffold-verification.md +0 -40
- package/.savepoint/releases/v1/epics/_archived/E02-data-model/Design.md +0 -142
- package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T001-domain-ids-status.md +0 -27
- package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T002-markdown-frontmatter-boundary.md +0 -28
- package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T003-task-documents.md +0 -29
- package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T004-release-epic-router-config-readers.md +0 -30
- package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T005-dependency-validation.md +0 -29
- package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T006-epic-task-set-reader.md +0 -29
- package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T007-quality-gates.md +0 -31
- package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/Design.md +0 -40
- package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/tasks/T001-phase-types.md +0 -27
- package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/tasks/T002-phase-frontmatter.md +0 -25
- package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/tasks/T003-simplify-config.md +0 -26
- package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/tasks/T004-simplify-router-domain.md +0 -24
- package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/Design.md +0 -122
- package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T001-argument-parser-contract.md +0 -28
- package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T002-help-text-generation.md +0 -28
- package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T003-terminal-environment-detection.md +0 -27
- package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T004-command-stub-modules.md +0 -29
- package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T005-cli-runner-dispatch.md +0 -34
- package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T006-entrypoint-quality-gates.md +0 -32
- package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/Design.md +0 -43
- package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T001-strip-args.md +0 -26
- package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T002-strip-help.md +0 -23
- package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T003-strip-run.md +0 -23
- package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T004-delete-commands.md +0 -24
- package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T005-update-cli-tests.md +0 -22
- package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/Design.md +0 -48
- package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T001-board-data-phases.md +0 -26
- package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T002-phase-rendering.md +0 -28
- package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T003-detail-pane-phases.md +0 -27
- package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T004-phase-transitions.md +0 -42
- package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T005-phase-gates.md +0 -24
- package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T006-phase-write-back.md +0 -24
- package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T007-remove-audit-flow.md +0 -27
- package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T008-board-tests.md +0 -25
- package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/Design.md +0 -85
- package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T001-project-template-assets.md +0 -17
- package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T002-release-and-prompt-assets.md +0 -20
- package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T003-template-registry-renderer.md +0 -22
- package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T004-template-integrity-tests.md +0 -17
- package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T005-template-closeout-quality-gates.md +0 -16
- package/.savepoint/releases/v1/epics/_archived/E05-init-command/Design.md +0 -88
- package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T001-init-cli-contract.md +0 -22
- package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T002-target-validation.md +0 -23
- package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T003-scaffold-writer.md +0 -24
- package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T004-magic-prompt-and-clipboard.md +0 -23
- package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T005-dev-deps-install-option.md +0 -24
- package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T006-init-command-integration.md +0 -28
- package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/Design.md +0 -53
- package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T001-delete-dead-src.md +0 -23
- package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T002-delete-dead-tests.md +0 -26
- package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T003-delete-assets.md +0 -25
- package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T004-clean-savepoint.md +0 -28
- package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T005-rewrite-agents-md.md +0 -28
- package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T006-clean-package-json.md +0 -23
- package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T007-verify.md +0 -25
- package/.savepoint/releases/v1/epics/_archived/E06-tui-board/Design.md +0 -104
- package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T001-board-command-data.md +0 -23
- package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T002-board-view-state.md +0 -24
- package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T003-transition-gates-and-writes.md +0 -25
- package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T004-terminal-theme.md +0 -23
- package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T005-ink-board-ui.md +0 -26
- package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T006-board-integration-audit-entry.md +0 -24
- package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/Design.md +0 -88
- package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T001-audit-cli-contract.md +0 -23
- package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T002-quality-gate-runner.md +0 -23
- package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T003-snapshot-and-prompt.md +0 -23
- package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T004-audit-orchestration-router.md +0 -27
- package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T005-proposal-validation-apply.md +0 -25
- package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T006-audit-review-state.md +0 -24
- package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T007-audit-review-ui.md +0 -26
- package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T008-audit-pipeline-integration.md +0 -24
- package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/Design.md +0 -103
- package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T001-acceptance-criteria-model.md +0 -30
- package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T002-release-task-set-reader.md +0 -33
- package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T003-board-data-and-plain-output.md +0 -34
- package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T004-board-selection-state.md +0 -33
- package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T005-ink-board-layout-cleanup.md +0 -37
- package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T006-task-detail-popup.md +0 -36
- package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T007-templates-acceptance-criteria.md +0 -34
- package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T008-board-workflow-integration.md +0 -41
- package/.savepoint/releases/v1/epics/_archived/E09-doctor-command/Design.md +0 -70
- package/.savepoint/releases/v1/epics/_archived/E10-docs-and-packaging/Design.md +0 -68
- package/.savepoint/releases/v1/epics/_archived/E11-release-validation/Design.md +0 -68
- package/.savepoint/releases/v1/v1-PRD.md +0 -66
- package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/E01-Detail.md +0 -40
- package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T001-next-activity-header.md +0 -56
- package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T002-rename-epic-design-files.md +0 -38
- package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T003-rename-release-prd.md +0 -28
- package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T004-update-instruction-files.md +0 -51
- package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T005-update-cross-references.md +0 -45
- package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T006-column-and-detail-scrolling.md +0 -68
- package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T007-column-focus-border-stability.md +0 -57
- package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/E02-Audit.md +0 -124
- package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/E02-Detail.md +0 -49
- package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T001-fix-makefile.md +0 -37
- package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T002-linux-build-target.md +0 -38
- package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T003-macos-build-target.md +0 -36
- package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T004-smoke-tests-and-artifacts.md +0 -59
- package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/E03-Audit.md +0 -195
- package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/E03-Detail.md +0 -45
- package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T001-border-resize-fix.md +0 -40
- package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T002-next-activity-below-header.md +0 -64
- package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T003-checkbox-rendering-fix.md +0 -56
- package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T005-unify-status-glyphs.md +0 -65
- package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T006-forced-256-color-profile.md +0 -36
- package/.savepoint/releases/v1.1/epics/E04-epic-navigation/E04-Audit.md +0 -167
- package/.savepoint/releases/v1.1/epics/E04-epic-navigation/E04-Detail.md +0 -51
- package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T001-sidebar-focusable-navigation.md +0 -65
- package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T002-epic-detail-overlay.md +0 -73
- package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T003-epic-status-glyphs.md +0 -73
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/E05-Audit.md +0 -237
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/E05-Detail.md +0 -54
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T001-update-agents-md.md +0 -45
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T002-update-router-md.md +0 -40
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T003-update-design-md.md +0 -47
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T004-implement-m-hotkey.md +0 -98
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T005-update-help-overlay.md +0 -33
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T006-tests-and-quality-gates.md +0 -62
- package/.savepoint/releases/v1.1/epics/E06-audit-command/E06-Audit.md +0 -56
- package/.savepoint/releases/v1.1/epics/E06-audit-command/E06-Detail.md +0 -63
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T005-proposals.md +0 -44
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T007-apply-close.md +0 -35
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T009-integration.md +0 -40
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T010-audit-file-migration.md +0 -45
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T011-model-tab-state.md +0 -26
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T012-epic-audit-render.md +0 -33
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T013-handle-tab-keys.md +0 -34
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T014-tab-indicator.md +0 -33
- package/.savepoint/releases/v1.1/epics/E07-init-command/E07-Audit.md +0 -336
- package/.savepoint/releases/v1.1/epics/E07-init-command/E07-Detail.md +0 -61
- package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T001-cli-entrypoint.md +0 -37
- package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T002-target-validation.md +0 -28
- package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T003-scaffold-writer.md +0 -46
- package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T004-atomic-writes.md +0 -27
- package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T005-magic-prompt.md +0 -25
- package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T006-clipboard.md +0 -26
- package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T007-integration-test.md +0 -26
- package/.savepoint/releases/v1.1/epics/E08-board-command/E08-Audit.md +0 -333
- package/.savepoint/releases/v1.1/epics/E08-board-command/E08-Detail.md +0 -68
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T001-cli-entrypoint.md +0 -26
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T002-non-tty-fallback.md +0 -27
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T003-tui-app-shell.md +0 -28
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T004-board-model.md +0 -29
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T005-detail-pane.md +0 -27
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T006-status-transitions.md +0 -29
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T007-theme-fallbacks.md +0 -29
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T008-integration-test.md +0 -27
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/E09-Audit.md +0 -207
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/E09-Detail.md +0 -65
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T001-cli-entrypoint.md +0 -24
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T002-config-router-validation.md +0 -28
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T003-structure-checks.md +0 -29
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T004-dependency-checks.md +0 -27
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T005-audit-orphan-checks.md +0 -28
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T006-quality-gates-report.md +0 -31
- package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/E11-Detail.md +0 -36
- package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T001-debug-logging.md +0 -25
- package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T002-increase-debounce.md +0 -21
- package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T003-error-handling.md +0 -22
- package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T004-test-verify.md +0 -29
- package/.savepoint/releases/v1.1/epics/E12-validation-fix/E12-Audit.md +0 -444
- package/.savepoint/releases/v1.1/epics/E12-validation-fix/E12-Detail.md +0 -45
- package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T001-default-phase.md +0 -35
- package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T002-default-status.md +0 -19
- package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T003-better-errors.md +0 -29
- package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T004-validate-on-write.md +0 -25
- package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T005-tests.md +0 -37
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/E13-Audit.md +0 -118
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/E13-Detail.md +0 -73
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T001-safe-cleanup.md +0 -66
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T002-bug-fixes.md +0 -35
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T003-centralize-duplication.md +0 -60
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T004-infrastructure.md +0 -33
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T005-decompose-update.md +0 -37
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T006-async-io.md +0 -40
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T007-test-coverage.md +0 -37
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/E14-Audit.md +0 -267
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/E14-Detail.md +0 -54
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T001-group-model.md +0 -39
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T002-data-interfaces.md +0 -42
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T003-discover-orphans.md +0 -33
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T004-epic-panel-headings.md +0 -35
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T005-shell-tokenization.md +0 -27
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T006-unify-enums.md +0 -29
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T007-testutil-package.md +0 -28
- package/.savepoint/releases/v1.1/epics/E15-hardening/E15-Audit.md +0 -272
- package/.savepoint/releases/v1.1/epics/E15-hardening/E15-Detail.md +0 -60
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T001-benchmarks.md +0 -31
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T002-fuzz-targets.md +0 -34
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T003-debug-flag.md +0 -30
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T004-dist-checksums.md +0 -27
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T005-windows-targets.md +0 -28
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T006-abbreviation-splitting.md +0 -26
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T007-root-test-allowlist.md +0 -33
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T008-ci-and-release-automation.md +0 -46
- package/.savepoint/releases/v1.1/epics/_archived/T001-cli-entrypoint.md +0 -25
- package/.savepoint/releases/v1.1/epics/_archived/T002-quality-gates.md +0 -27
- package/.savepoint/releases/v1.1/epics/_archived/T003-snapshot.md +0 -27
- package/.savepoint/releases/v1.1/epics/_archived/T004-ai-reconcile.md +0 -29
- package/.savepoint/releases/v1.1/epics/_archived/T006-tui-review.md +0 -31
- package/.savepoint/releases/v1.1/epics/_archived/T008-skip-handling.md +0 -34
- package/.savepoint/releases/v1.1/v1.1-PRD.md +0 -139
- package/.savepoint/router.md +0 -57
- package/.savepoint/visual-identity.md +0 -125
- package/AGENTS.md +0 -99
- package/CLAUDE.md +0 -1
- package/GEMINI.md +0 -1
- package/Makefile +0 -31
- package/agent-skills/ink-tui-design/SKILL.md +0 -309
- package/agent-skills/ink-tui-design/references/component-patterns.md +0 -371
- package/agent-skills/ink-tui-design/references/hooks-guide.md +0 -436
- package/agent-skills/ink-tui-design/references/ink-gotchas.md +0 -330
- package/agent-skills/ink-tui-design/references/testing-patterns.md +0 -384
- package/agent-skills/savepoint-audit/SKILL.md +0 -87
- package/agent-skills/savepoint-build-task/SKILL.md +0 -44
- package/agent-skills/savepoint-create-plan/SKILL.md +0 -33
- package/agent-skills/savepoint-create-task/SKILL.md +0 -44
- package/agent-skills/savepoint-draft-prd/SKILL.md +0 -37
- package/agent-skills/savepoint-system-design/SKILL.md +0 -38
- package/agent-skills/superpowers/brainstorming/SKILL.md +0 -165
- package/agent-skills/superpowers/brainstorming/visual-companion.md +0 -304
- package/agent-skills/superpowers/dispatching-parallel-agents/SKILL.md +0 -193
- package/agent-skills/superpowers/executing-plans/SKILL.md +0 -77
- package/agent-skills/superpowers/finishing-a-development-branch/SKILL.md +0 -213
- package/agent-skills/superpowers/receiving-code-review/SKILL.md +0 -226
- package/agent-skills/superpowers/requesting-code-review/SKILL.md +0 -115
- package/agent-skills/superpowers/requesting-code-review/code-reviewer.md +0 -160
- package/agent-skills/superpowers/subagent-driven-development/SKILL.md +0 -292
- package/agent-skills/superpowers/subagent-driven-development/code-quality-reviewer-prompt.md +0 -27
- package/agent-skills/superpowers/subagent-driven-development/implementer-prompt.md +0 -113
- package/agent-skills/superpowers/subagent-driven-development/spec-reviewer-prompt.md +0 -61
- package/agent-skills/superpowers/systematic-debugging/SKILL.md +0 -305
- package/agent-skills/superpowers/systematic-debugging/condition-based-waiting.md +0 -122
- package/agent-skills/superpowers/systematic-debugging/defense-in-depth.md +0 -130
- package/agent-skills/superpowers/systematic-debugging/root-cause-tracing.md +0 -183
- package/agent-skills/superpowers/test-driven-development/SKILL.md +0 -389
- package/agent-skills/superpowers/test-driven-development/testing-anti-patterns.md +0 -317
- package/agent-skills/superpowers/verification-before-completion/SKILL.md +0 -147
- package/agent-skills/superpowers/writing-plans/SKILL.md +0 -159
- package/agent-skills/superpowers/writing-plans/plan-document-reviewer-prompt.md +0 -49
- package/agent_skills_test.go +0 -91
- package/assets/banner.png +0 -0
- package/assets/logo.png +0 -0
- package/assets/strawman.png +0 -0
- package/cmd/board.go +0 -59
- package/cmd/board_test.go +0 -137
- package/cmd/doctor.go +0 -53
- package/cmd/doctor_test.go +0 -146
- package/cmd/init.go +0 -63
- package/cmd/init_test.go +0 -104
- package/go.mod +0 -36
- package/go.sum +0 -75
- package/internal/board/board.go +0 -181
- package/internal/board/board_test.go +0 -168
- package/internal/board/card.go +0 -129
- package/internal/board/card_test.go +0 -287
- package/internal/board/column.go +0 -156
- package/internal/board/column_test.go +0 -210
- package/internal/board/debug.go +0 -26
- package/internal/board/debug_test.go +0 -108
- package/internal/board/detail.go +0 -218
- package/internal/board/detail_test.go +0 -388
- package/internal/board/epic_panel.go +0 -264
- package/internal/board/epic_panel_test.go +0 -869
- package/internal/board/help.go +0 -41
- package/internal/board/help_test.go +0 -86
- package/internal/board/integration_test.go +0 -266
- package/internal/board/interfaces.go +0 -65
- package/internal/board/interfaces_test.go +0 -114
- package/internal/board/io.go +0 -93
- package/internal/board/layout.go +0 -68
- package/internal/board/layout_test.go +0 -106
- package/internal/board/model.go +0 -235
- package/internal/board/model_test.go +0 -67
- package/internal/board/plain.go +0 -88
- package/internal/board/plain_test.go +0 -117
- package/internal/board/release.go +0 -34
- package/internal/board/release_test.go +0 -177
- package/internal/board/render_policy_test.go +0 -77
- package/internal/board/status.go +0 -23
- package/internal/board/theme.go +0 -24
- package/internal/board/theme_test.go +0 -31
- package/internal/board/transitions.go +0 -113
- package/internal/board/transitions_test.go +0 -164
- package/internal/board/tui.go +0 -32
- package/internal/board/update.go +0 -575
- package/internal/board/update_test.go +0 -602
- package/internal/board/util.go +0 -76
- package/internal/board/view.go +0 -317
- package/internal/board/view_test.go +0 -377
- package/internal/board/watch.go +0 -136
- package/internal/buildtool/main.go +0 -249
- package/internal/buildtool/main_test.go +0 -224
- package/internal/data/config.go +0 -101
- package/internal/data/config_test.go +0 -122
- package/internal/data/discover.go +0 -178
- package/internal/data/discover_test.go +0 -130
- package/internal/data/errors.go +0 -13
- package/internal/data/fuzz_test.go +0 -75
- package/internal/data/lifecycle.go +0 -44
- package/internal/data/lifecycle_test.go +0 -41
- package/internal/data/parser.go +0 -243
- package/internal/data/parser_test.go +0 -281
- package/internal/data/router.go +0 -52
- package/internal/data/router_test.go +0 -35
- package/internal/data/task.go +0 -57
- package/internal/data/task_test.go +0 -51
- package/internal/data/testdata/fuzz/FuzzSplitFrontmatterBody/68eb66b0fe91e7e3 +0 -2
- package/internal/data/write.go +0 -221
- package/internal/data/write_test.go +0 -623
- package/internal/doctor/checks.go +0 -567
- package/internal/doctor/checks_test.go +0 -716
- package/internal/doctor/gates.go +0 -193
- package/internal/doctor/gates_test.go +0 -166
- package/internal/doctor/interfaces.go +0 -64
- package/internal/doctor/interfaces_test.go +0 -104
- package/internal/doctor/repairs.go +0 -80
- package/internal/doctor/repairs_test.go +0 -81
- package/internal/doctor/report.go +0 -157
- package/internal/doctor/report_test.go +0 -89
- package/internal/init/clipboard.go +0 -146
- package/internal/init/clipboard_test.go +0 -74
- package/internal/init/install.go +0 -16
- package/internal/init/integration_test.go +0 -197
- package/internal/init/prompt.go +0 -14
- package/internal/init/prompt_test.go +0 -77
- package/internal/init/scaffold.go +0 -59
- package/internal/init/scaffold_test.go +0 -179
- package/internal/init/template_freshness_test.go +0 -56
- package/internal/init/validate.go +0 -85
- package/internal/init/validate_test.go +0 -141
- package/internal/init/write.go +0 -73
- package/internal/init/write_test.go +0 -91
- package/internal/styles/palette.go +0 -49
- package/internal/styles/styles.go +0 -139
- package/internal/styles/styles_test.go +0 -133
- package/internal/testutil/fixture.go +0 -113
- package/internal/testutil/fs.go +0 -26
- package/main.go +0 -136
- package/project-audit/audit_report_glm_5.1.md +0 -411
- package/project-audit/audit_report_opus_4.6.md +0 -406
- package/project-audit/consolidated-audit-report.md +0 -456
- package/scripts/vitest-preload.cjs +0 -95
- package/templates/project/.savepoint/Design.md +0 -47
- package/templates/project/.savepoint/PRD.md +0 -34
- package/templates/project/.savepoint/config.yml +0 -27
- package/templates/project/.savepoint/router.md +0 -153
- package/templates/project/.savepoint/visual-identity.md +0 -122
- package/templates/project/AGENTS.md +0 -88
- package/templates/project/agent-skills/savepoint-audit/SKILL.md +0 -87
- package/templates/project/agent-skills/savepoint-build-task/SKILL.md +0 -44
- package/templates/project/agent-skills/savepoint-create-plan/SKILL.md +0 -33
- package/templates/project/agent-skills/savepoint-create-task/SKILL.md +0 -44
- package/templates/project/agent-skills/savepoint-draft-prd/SKILL.md +0 -37
- package/templates/project/agent-skills/savepoint-system-design/SKILL.md +0 -38
- package/templates/prompts/audit-reconciliation.prompt.md +0 -72
- package/templates/prompts/design.prompt.md +0 -45
- package/templates/prompts/epic-design.prompt.md +0 -43
- package/templates/prompts/magic-prompt.prompt.md +0 -7
- package/templates/prompts/prd.prompt.md +0 -42
- package/templates/prompts/task-breakdown.prompt.md +0 -54
- package/templates/prompts/task-building.prompt.md +0 -38
- package/templates/prompts/task-planning.prompt.md +0 -53
- package/templates/release/v1/PRD.md +0 -37
package/internal/doctor/gates.go
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
package doctor
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"context"
|
|
5
|
-
"fmt"
|
|
6
|
-
"os"
|
|
7
|
-
"os/exec"
|
|
8
|
-
"path/filepath"
|
|
9
|
-
"strings"
|
|
10
|
-
"time"
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
// GateResult holds the outcome of a single quality gate.
|
|
14
|
-
type GateResult struct {
|
|
15
|
-
Name string
|
|
16
|
-
Command string
|
|
17
|
-
Passed bool
|
|
18
|
-
ExitCode int
|
|
19
|
-
Output string
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// RunQualityGates executes configured quality gates (lint, typecheck, test).
|
|
23
|
-
func RunQualityGates(root string, overrides ...DoctorDependencies) []GateResult {
|
|
24
|
-
deps := doctorDependencies(overrides)
|
|
25
|
-
configPath := filepath.Join(root, "config.yml")
|
|
26
|
-
cfg, err := deps.ConfigReader.Read(configPath)
|
|
27
|
-
if err != nil {
|
|
28
|
-
return []GateResult{{
|
|
29
|
-
Name: "config",
|
|
30
|
-
Command: "",
|
|
31
|
-
Passed: false,
|
|
32
|
-
Output: fmt.Sprintf("cannot read config: %v", err),
|
|
33
|
-
}}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
timeout := 60 * time.Second
|
|
37
|
-
if cfg.QualityGates.Timeout != "" {
|
|
38
|
-
if d, err := time.ParseDuration(cfg.QualityGates.Timeout); err == nil {
|
|
39
|
-
timeout = d
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
var results []GateResult
|
|
44
|
-
|
|
45
|
-
if cfg.QualityGates.Lint != nil && *cfg.QualityGates.Lint != "" {
|
|
46
|
-
results = append(results, runGate("lint", *cfg.QualityGates.Lint, root, timeout))
|
|
47
|
-
}
|
|
48
|
-
if cfg.QualityGates.Typecheck != nil && *cfg.QualityGates.Typecheck != "" {
|
|
49
|
-
results = append(results, runGate("typecheck", *cfg.QualityGates.Typecheck, root, timeout))
|
|
50
|
-
}
|
|
51
|
-
if cfg.QualityGates.Test != nil && *cfg.QualityGates.Test != "" {
|
|
52
|
-
results = append(results, runGate("test", *cfg.QualityGates.Test, root, timeout))
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if len(results) == 0 {
|
|
56
|
-
results = append(results, GateResult{
|
|
57
|
-
Name: "quality_gates",
|
|
58
|
-
Command: "",
|
|
59
|
-
Passed: true,
|
|
60
|
-
Output: "no quality gates configured",
|
|
61
|
-
})
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return results
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
func runGate(name, command string, root string, timeout time.Duration) GateResult {
|
|
68
|
-
parts := splitCommand(command)
|
|
69
|
-
if len(parts) == 0 {
|
|
70
|
-
return GateResult{
|
|
71
|
-
Name: name,
|
|
72
|
-
Command: command,
|
|
73
|
-
Passed: false,
|
|
74
|
-
Output: "empty command",
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
79
|
-
defer cancel()
|
|
80
|
-
|
|
81
|
-
cmd := exec.CommandContext(ctx, parts[0], parts[1:]...)
|
|
82
|
-
cmd.Dir = root
|
|
83
|
-
cmd.Env = os.Environ()
|
|
84
|
-
|
|
85
|
-
output, err := cmd.CombinedOutput()
|
|
86
|
-
outStr := strings.TrimSpace(string(output))
|
|
87
|
-
|
|
88
|
-
switch {
|
|
89
|
-
case err != nil && ctx.Err() == context.DeadlineExceeded:
|
|
90
|
-
return GateResult{
|
|
91
|
-
Name: name,
|
|
92
|
-
Command: command,
|
|
93
|
-
Passed: false,
|
|
94
|
-
Output: fmt.Sprintf("timed out after %v", timeout),
|
|
95
|
-
}
|
|
96
|
-
case err != nil:
|
|
97
|
-
if exitErr, ok := err.(*exec.ExitError); ok {
|
|
98
|
-
return GateResult{
|
|
99
|
-
Name: name,
|
|
100
|
-
Command: command,
|
|
101
|
-
Passed: false,
|
|
102
|
-
ExitCode: exitErr.ExitCode(),
|
|
103
|
-
Output: outStr,
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return GateResult{
|
|
107
|
-
Name: name,
|
|
108
|
-
Command: command,
|
|
109
|
-
Passed: false,
|
|
110
|
-
Output: fmt.Sprintf("failed to execute: %v", err),
|
|
111
|
-
}
|
|
112
|
-
default:
|
|
113
|
-
return GateResult{
|
|
114
|
-
Name: name,
|
|
115
|
-
Command: command,
|
|
116
|
-
Passed: true,
|
|
117
|
-
ExitCode: 0,
|
|
118
|
-
Output: outStr,
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
func splitCommand(command string) []string {
|
|
124
|
-
var parts []string
|
|
125
|
-
current := strings.Builder{}
|
|
126
|
-
inDoubleQuote := false
|
|
127
|
-
inSingleQuote := false
|
|
128
|
-
tokenStarted := false
|
|
129
|
-
|
|
130
|
-
flush := func() {
|
|
131
|
-
if tokenStarted || current.Len() > 0 {
|
|
132
|
-
parts = append(parts, current.String())
|
|
133
|
-
current.Reset()
|
|
134
|
-
tokenStarted = false
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
for i := 0; i < len(command); i++ {
|
|
139
|
-
c := command[i]
|
|
140
|
-
|
|
141
|
-
if inSingleQuote {
|
|
142
|
-
tokenStarted = true
|
|
143
|
-
if c == '\'' {
|
|
144
|
-
inSingleQuote = false
|
|
145
|
-
} else {
|
|
146
|
-
current.WriteByte(c)
|
|
147
|
-
}
|
|
148
|
-
continue
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if inDoubleQuote {
|
|
152
|
-
tokenStarted = true
|
|
153
|
-
if c == '\\' && i+1 < len(command) {
|
|
154
|
-
next := command[i+1]
|
|
155
|
-
if next == '"' || next == '\\' || next == '$' || next == '`' {
|
|
156
|
-
i++
|
|
157
|
-
current.WriteByte(command[i])
|
|
158
|
-
continue
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
if c == '"' {
|
|
162
|
-
inDoubleQuote = false
|
|
163
|
-
continue
|
|
164
|
-
}
|
|
165
|
-
current.WriteByte(c)
|
|
166
|
-
continue
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
switch c {
|
|
170
|
-
case '\\':
|
|
171
|
-
tokenStarted = true
|
|
172
|
-
if i+1 < len(command) {
|
|
173
|
-
i++
|
|
174
|
-
current.WriteByte(command[i])
|
|
175
|
-
} else {
|
|
176
|
-
current.WriteByte(c)
|
|
177
|
-
}
|
|
178
|
-
case '"':
|
|
179
|
-
tokenStarted = true
|
|
180
|
-
inDoubleQuote = true
|
|
181
|
-
case '\'':
|
|
182
|
-
tokenStarted = true
|
|
183
|
-
inSingleQuote = true
|
|
184
|
-
case ' ', '\t', '\n', '\r':
|
|
185
|
-
flush()
|
|
186
|
-
default:
|
|
187
|
-
tokenStarted = true
|
|
188
|
-
current.WriteByte(c)
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
flush()
|
|
192
|
-
return parts
|
|
193
|
-
}
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
package doctor
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"os"
|
|
5
|
-
"path/filepath"
|
|
6
|
-
"strings"
|
|
7
|
-
"testing"
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
func writeConfig(t *testing.T, root, content string) {
|
|
11
|
-
t.Helper()
|
|
12
|
-
if err := os.WriteFile(filepath.Join(root, "config.yml"), []byte(content), 0644); err != nil {
|
|
13
|
-
t.Fatal(err)
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
func TestRunQualityGates_NoConfig(t *testing.T) {
|
|
18
|
-
root := t.TempDir()
|
|
19
|
-
results := RunQualityGates(root)
|
|
20
|
-
if len(results) != 1 {
|
|
21
|
-
t.Fatalf("RunQualityGates() = %d results, want 1", len(results))
|
|
22
|
-
}
|
|
23
|
-
if !results[0].Passed {
|
|
24
|
-
t.Fatalf("RunQualityGates() = %v, want passed (default config has no gates)", results[0])
|
|
25
|
-
}
|
|
26
|
-
if !strings.Contains(results[0].Output, "no quality gates configured") {
|
|
27
|
-
t.Fatalf("RunQualityGates() output = %q, want 'no quality gates configured'", results[0].Output)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
func TestRunQualityGates_AllNull(t *testing.T) {
|
|
32
|
-
root := t.TempDir()
|
|
33
|
-
writeConfig(t, root, "quality_gates:\n lint: null\n typecheck: null\n test: null\ntheme:\n bg: \"#000\"\n")
|
|
34
|
-
|
|
35
|
-
results := RunQualityGates(root)
|
|
36
|
-
if len(results) != 1 {
|
|
37
|
-
t.Fatalf("RunQualityGates() = %d results, want 1 (no gates configured)", len(results))
|
|
38
|
-
}
|
|
39
|
-
if !results[0].Passed {
|
|
40
|
-
t.Fatalf("RunQualityGates() = %v, want passed with 'no gates configured'", results[0])
|
|
41
|
-
}
|
|
42
|
-
if !strings.Contains(results[0].Output, "no quality gates configured") {
|
|
43
|
-
t.Fatalf("RunQualityGates() output = %q, want 'no quality gates configured'", results[0].Output)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
func TestRunQualityGates_LintOnly(t *testing.T) {
|
|
48
|
-
root := t.TempDir()
|
|
49
|
-
writeConfig(t, root, "quality_gates:\n lint: \"go version\"\n typecheck: null\n test: null\ntheme:\n bg: \"#000\"\n")
|
|
50
|
-
|
|
51
|
-
results := RunQualityGates(root)
|
|
52
|
-
if len(results) != 1 {
|
|
53
|
-
t.Fatalf("RunQualityGates() = %d results, want 1 (lint only)", len(results))
|
|
54
|
-
}
|
|
55
|
-
if !results[0].Passed {
|
|
56
|
-
t.Fatalf("RunQualityGates() lint should pass: %v", results[0])
|
|
57
|
-
}
|
|
58
|
-
if results[0].Name != "lint" {
|
|
59
|
-
t.Fatalf("RunQualityGates()[0].Name = %q, want \"lint\"", results[0].Name)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
func TestRunQualityGates_AllThree(t *testing.T) {
|
|
64
|
-
root := t.TempDir()
|
|
65
|
-
writeConfig(t, root, "quality_gates:\n lint: \"go version\"\n typecheck: \"go version\"\n test: \"go version\"\ntheme:\n bg: \"#000\"\n")
|
|
66
|
-
|
|
67
|
-
results := RunQualityGates(root)
|
|
68
|
-
if len(results) != 3 {
|
|
69
|
-
t.Fatalf("RunQualityGates() = %d results, want 3", len(results))
|
|
70
|
-
}
|
|
71
|
-
for _, r := range results {
|
|
72
|
-
if !r.Passed {
|
|
73
|
-
t.Fatalf("RunQualityGates() %s should pass: %v", r.Name, r)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
func TestRunQualityGates_FailingCommand(t *testing.T) {
|
|
79
|
-
root := t.TempDir()
|
|
80
|
-
writeConfig(t, root, "quality_gates:\n lint: \"cmd-that-does-not-exist-12345\"\n typecheck: null\n test: null\ntheme:\n bg: \"#000\"\n")
|
|
81
|
-
|
|
82
|
-
results := RunQualityGates(root)
|
|
83
|
-
if len(results) != 1 {
|
|
84
|
-
t.Fatalf("RunQualityGates() = %d results, want 1", len(results))
|
|
85
|
-
}
|
|
86
|
-
if results[0].Passed {
|
|
87
|
-
t.Fatal("RunQualityGates() should fail for non-existent command")
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
func TestRunQualityGates_ExitCodeNonZero(t *testing.T) {
|
|
92
|
-
root := t.TempDir()
|
|
93
|
-
writeConfig(t, root, "quality_gates:\n lint: \"go vet\"\n typecheck: null\n test: null\ntheme:\n bg: \"#000\"\n")
|
|
94
|
-
// Write a bad Go file so go vet fails
|
|
95
|
-
badPath := filepath.Join(root, "bad.go")
|
|
96
|
-
os.WriteFile(badPath, []byte("package x\n\nfunc f() { return 1 }\n"), 0644)
|
|
97
|
-
|
|
98
|
-
results := RunQualityGates(root)
|
|
99
|
-
if len(results) != 1 {
|
|
100
|
-
t.Fatalf("RunQualityGates() = %d results, want 1", len(results))
|
|
101
|
-
}
|
|
102
|
-
if results[0].Passed {
|
|
103
|
-
t.Fatal("RunQualityGates() should fail when go vet fails")
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
func TestRunQualityGates_Timeout(t *testing.T) {
|
|
108
|
-
root := t.TempDir()
|
|
109
|
-
writeConfig(t, root, "quality_gates:\n lint: \"go test -test.run TestNonexistent -count=1 ./...\"\n gate_timeout: \"1ns\"\ntheme:\n bg: \"#000\"\n")
|
|
110
|
-
|
|
111
|
-
results := RunQualityGates(root)
|
|
112
|
-
if len(results) != 1 {
|
|
113
|
-
t.Fatalf("RunQualityGates() = %d results, want 1", len(results))
|
|
114
|
-
}
|
|
115
|
-
if results[0].Passed {
|
|
116
|
-
t.Fatal("RunQualityGates() should fail due to timeout")
|
|
117
|
-
}
|
|
118
|
-
if !strings.Contains(results[0].Output, "timed out") {
|
|
119
|
-
t.Fatalf("RunQualityGates() output = %q, want 'timed out'", results[0].Output)
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
func TestRunQualityGates_DefaultTimeout(t *testing.T) {
|
|
124
|
-
root := t.TempDir()
|
|
125
|
-
writeConfig(t, root, "quality_gates:\n lint: \"go version\"\n # no gate_timeout set — uses 60s default\ntheme:\n bg: \"#000\"\n")
|
|
126
|
-
|
|
127
|
-
results := RunQualityGates(root)
|
|
128
|
-
if len(results) != 1 {
|
|
129
|
-
t.Fatalf("RunQualityGates() = %d results, want 1", len(results))
|
|
130
|
-
}
|
|
131
|
-
if !results[0].Passed {
|
|
132
|
-
t.Fatalf("RunQualityGates() lint should pass with default timeout: %v", results[0])
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
func TestSplitCommand(t *testing.T) {
|
|
137
|
-
tests := []struct {
|
|
138
|
-
input string
|
|
139
|
-
want []string
|
|
140
|
-
}{
|
|
141
|
-
{"echo hello", []string{"echo", "hello"}},
|
|
142
|
-
{"go test ./...", []string{"go", "test", "./..."}},
|
|
143
|
-
{"\"c:\\program files\\go\\bin\\go\"", []string{"c:\\program files\\go\\bin\\go"}},
|
|
144
|
-
{"echo 'hello world'", []string{"echo", "hello world"}},
|
|
145
|
-
{"echo \"hello \\\"world\\\"\"", []string{"echo", "hello \"world\""}},
|
|
146
|
-
{"echo hello\\ world", []string{"echo", "hello world"}},
|
|
147
|
-
{"echo 'it''s'", []string{"echo", "its"}},
|
|
148
|
-
{"go test -run \"\" ./...", []string{"go", "test", "-run", "", "./..."}},
|
|
149
|
-
{"printf ''", []string{"printf", ""}},
|
|
150
|
-
{"echo trailing\\", []string{"echo", "trailing\\"}},
|
|
151
|
-
{"", nil},
|
|
152
|
-
{" ", nil},
|
|
153
|
-
}
|
|
154
|
-
for _, tt := range tests {
|
|
155
|
-
got := splitCommand(tt.input)
|
|
156
|
-
if len(got) != len(tt.want) {
|
|
157
|
-
t.Errorf("splitCommand(%q) = %v, want %v", tt.input, got, tt.want)
|
|
158
|
-
continue
|
|
159
|
-
}
|
|
160
|
-
for i := range got {
|
|
161
|
-
if got[i] != tt.want[i] {
|
|
162
|
-
t.Errorf("splitCommand(%q)[%d] = %q, want %q", tt.input, i, got[i], tt.want[i])
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
package doctor
|
|
2
|
-
|
|
3
|
-
import "github.com/opencode/savepoint/internal/data"
|
|
4
|
-
|
|
5
|
-
// taskDiscoverer provides project traversal for doctor checks.
|
|
6
|
-
type taskDiscoverer interface {
|
|
7
|
-
ListRootDirs(root string) ([]string, error)
|
|
8
|
-
ListReleases(root string) ([]data.ReleaseInfo, error)
|
|
9
|
-
ListEpics(root, release string) ([]data.EpicInfo, error)
|
|
10
|
-
ListTasks(root, release, epic string) ([]data.TaskInfo, error)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// taskParser parses Savepoint frontmatter for doctor checks.
|
|
14
|
-
type taskParser interface {
|
|
15
|
-
ParseFrontmatter(content string) (map[string]any, error)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// configReader reads quality gate configuration.
|
|
19
|
-
type configReader interface {
|
|
20
|
-
Read(path string) (*data.Config, error)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// routerReader parses router state from router.md content.
|
|
24
|
-
type routerReader interface {
|
|
25
|
-
ReadState(content string) (*data.RouterState, error)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// DoctorDependencies contains doctor data-access dependencies.
|
|
29
|
-
type DoctorDependencies struct {
|
|
30
|
-
Discoverer taskDiscoverer
|
|
31
|
-
Parser taskParser
|
|
32
|
-
ConfigReader configReader
|
|
33
|
-
RouterReader routerReader
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
func defaultDoctorDependencies() DoctorDependencies {
|
|
37
|
-
return DoctorDependencies{
|
|
38
|
-
Discoverer: data.NewDiscover(),
|
|
39
|
-
Parser: data.NewParser(),
|
|
40
|
-
ConfigReader: data.NewConfigReader(),
|
|
41
|
-
RouterReader: data.NewRouterReader(),
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
func doctorDependencies(overrides []DoctorDependencies) DoctorDependencies {
|
|
46
|
-
deps := defaultDoctorDependencies()
|
|
47
|
-
if len(overrides) == 0 {
|
|
48
|
-
return deps
|
|
49
|
-
}
|
|
50
|
-
override := overrides[0]
|
|
51
|
-
if override.Discoverer != nil {
|
|
52
|
-
deps.Discoverer = override.Discoverer
|
|
53
|
-
}
|
|
54
|
-
if override.Parser != nil {
|
|
55
|
-
deps.Parser = override.Parser
|
|
56
|
-
}
|
|
57
|
-
if override.ConfigReader != nil {
|
|
58
|
-
deps.ConfigReader = override.ConfigReader
|
|
59
|
-
}
|
|
60
|
-
if override.RouterReader != nil {
|
|
61
|
-
deps.RouterReader = override.RouterReader
|
|
62
|
-
}
|
|
63
|
-
return deps
|
|
64
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
package doctor
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"path/filepath"
|
|
5
|
-
"testing"
|
|
6
|
-
|
|
7
|
-
"github.com/opencode/savepoint/internal/data"
|
|
8
|
-
"github.com/opencode/savepoint/internal/testutil"
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
type stubDoctorRouterReader struct {
|
|
12
|
-
state *data.RouterState
|
|
13
|
-
calls int
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
func (r *stubDoctorRouterReader) ReadState(content string) (*data.RouterState, error) {
|
|
17
|
-
r.calls++
|
|
18
|
-
return r.state, nil
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
type stubDoctorDiscoverer struct {
|
|
22
|
-
releases []data.ReleaseInfo
|
|
23
|
-
epics map[string][]data.EpicInfo
|
|
24
|
-
tasks map[string][]data.TaskInfo
|
|
25
|
-
calls int
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
func (d *stubDoctorDiscoverer) ListRootDirs(root string) ([]string, error) {
|
|
29
|
-
d.calls++
|
|
30
|
-
return nil, nil
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
func (d *stubDoctorDiscoverer) ListReleases(root string) ([]data.ReleaseInfo, error) {
|
|
34
|
-
d.calls++
|
|
35
|
-
return d.releases, nil
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
func (d *stubDoctorDiscoverer) ListEpics(root, release string) ([]data.EpicInfo, error) {
|
|
39
|
-
d.calls++
|
|
40
|
-
return d.epics[release], nil
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
func (d *stubDoctorDiscoverer) ListTasks(root, release, epic string) ([]data.TaskInfo, error) {
|
|
44
|
-
d.calls++
|
|
45
|
-
return d.tasks[release+"/"+epic], nil
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
type countingDoctorParser struct {
|
|
49
|
-
parser *data.Parser
|
|
50
|
-
calls int
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
func (p *countingDoctorParser) ParseFrontmatter(content string) (map[string]any, error) {
|
|
54
|
-
p.calls++
|
|
55
|
-
return p.parser.ParseFrontmatter(content)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
func TestCheckRouterUsesInjectedRouterReader(t *testing.T) {
|
|
59
|
-
root := t.TempDir()
|
|
60
|
-
testutil.WriteFile(t, filepath.Join(root, "router.md"), "# intentionally not a router state block")
|
|
61
|
-
testutil.MkdirAll(t, filepath.Join(root, "releases", "v9", "epics", "E01-mock"))
|
|
62
|
-
|
|
63
|
-
reader := &stubDoctorRouterReader{state: &data.RouterState{
|
|
64
|
-
State: "task-building",
|
|
65
|
-
Release: "v9",
|
|
66
|
-
Epic: "E01-mock",
|
|
67
|
-
}}
|
|
68
|
-
|
|
69
|
-
if err := CheckRouter(root, "", DoctorDependencies{RouterReader: reader}); err != nil {
|
|
70
|
-
t.Fatalf("CheckRouter() with injected reader = %v, want nil", err)
|
|
71
|
-
}
|
|
72
|
-
if reader.calls != 1 {
|
|
73
|
-
t.Fatalf("ReadState calls = %d, want 1", reader.calls)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
func TestCheckDependenciesUsesInjectedDiscovererAndParser(t *testing.T) {
|
|
78
|
-
root := t.TempDir()
|
|
79
|
-
taskPath := filepath.Join(root, "virtual", "T001-task.md")
|
|
80
|
-
testutil.MkdirAll(t, filepath.Dir(taskPath))
|
|
81
|
-
testutil.WriteFile(t, taskPath, "---\nid: E01-mock/T001-task\nstatus: planned\nobjective: Mock\ndepends_on: []\n---\n")
|
|
82
|
-
|
|
83
|
-
discoverer := &stubDoctorDiscoverer{
|
|
84
|
-
releases: []data.ReleaseInfo{{ID: "v9", Path: filepath.Join(root, "virtual-release")}},
|
|
85
|
-
epics: map[string][]data.EpicInfo{
|
|
86
|
-
"v9": {{ID: "E01-mock", Path: filepath.Join(root, "virtual-epic")}},
|
|
87
|
-
},
|
|
88
|
-
tasks: map[string][]data.TaskInfo{
|
|
89
|
-
"v9/E01-mock": {{ID: "T001-task", Path: taskPath}},
|
|
90
|
-
},
|
|
91
|
-
}
|
|
92
|
-
parser := &countingDoctorParser{parser: data.NewParser()}
|
|
93
|
-
|
|
94
|
-
problems := CheckDependencies(root, "", DoctorDependencies{Discoverer: discoverer, Parser: parser})
|
|
95
|
-
if len(problems) > 0 {
|
|
96
|
-
t.Fatalf("CheckDependencies() = %v, want no problems", problems)
|
|
97
|
-
}
|
|
98
|
-
if discoverer.calls == 0 {
|
|
99
|
-
t.Fatal("injected discoverer was not used")
|
|
100
|
-
}
|
|
101
|
-
if parser.calls != 1 {
|
|
102
|
-
t.Fatalf("ParseFrontmatter calls = %d, want 1", parser.calls)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
package doctor
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"errors"
|
|
5
|
-
"fmt"
|
|
6
|
-
"strings"
|
|
7
|
-
|
|
8
|
-
"github.com/opencode/savepoint/internal/data"
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
func SuggestRepair(err error) string {
|
|
12
|
-
switch {
|
|
13
|
-
case errors.Is(err, data.ErrConfigNotFound):
|
|
14
|
-
return "Run `savepoint init` to scaffold a new project"
|
|
15
|
-
case errors.Is(err, data.ErrInvalidStatus):
|
|
16
|
-
return "Set router state to a recognized workflow state (see router.md State → action section)"
|
|
17
|
-
case errors.Is(err, data.ErrMissingFrontmatter):
|
|
18
|
-
return "Fix the YAML frontmatter between the --- delimiters"
|
|
19
|
-
case errors.Is(err, data.ErrStructureProblem):
|
|
20
|
-
return "Review the file and fix the reported issue"
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
msg := err.Error()
|
|
24
|
-
switch {
|
|
25
|
-
case strings.Contains(msg, "config.yml not found"):
|
|
26
|
-
return "Run `savepoint init` to scaffold a new project"
|
|
27
|
-
case strings.Contains(msg, "config.yml missing required field"):
|
|
28
|
-
return "Add the missing field to config.yml — see the project template for reference"
|
|
29
|
-
case strings.Contains(msg, "invalid YAML"):
|
|
30
|
-
return "Fix the YAML syntax error at the indicated line"
|
|
31
|
-
case strings.Contains(msg, "router.md not found"):
|
|
32
|
-
return "Run `savepoint init` to scaffold a new project"
|
|
33
|
-
case strings.Contains(msg, "unknown state"):
|
|
34
|
-
return "Set router state to a recognized workflow state (see router.md State → action section)"
|
|
35
|
-
case strings.Contains(msg, "release PRD file not found"):
|
|
36
|
-
return "Create a {release}-PRD.md file with frontmatter for the release"
|
|
37
|
-
case strings.Contains(msg, "release"):
|
|
38
|
-
return "Create the release directory at releases/<release-id>/"
|
|
39
|
-
case strings.Contains(msg, "epic") && strings.Contains(msg, "directory not found"):
|
|
40
|
-
return "Create the epic directory at releases/<release>/epics/<epic-id>/"
|
|
41
|
-
case strings.Contains(msg, "epic detail file not found"):
|
|
42
|
-
return "Create an E##-Detail.md with frontmatter for the epic"
|
|
43
|
-
case strings.Contains(msg, "invalid frontmatter"):
|
|
44
|
-
return "Fix the YAML frontmatter between the --- delimiters"
|
|
45
|
-
case strings.Contains(msg, "task missing required frontmatter field"):
|
|
46
|
-
return "Add the missing field to the task frontmatter"
|
|
47
|
-
case strings.Contains(msg, "missing ## Acceptance Criteria"):
|
|
48
|
-
return "Add an ## Acceptance Criteria section with checkable items"
|
|
49
|
-
case strings.Contains(msg, "depends_on must be a list"):
|
|
50
|
-
return "Change depends_on to a YAML list format"
|
|
51
|
-
case strings.Contains(msg, "references non-existent"):
|
|
52
|
-
return "Create the referenced task or remove the dependency"
|
|
53
|
-
case strings.Contains(msg, "duplicate task ID"):
|
|
54
|
-
return "Rename one of the tasks to have a unique ID"
|
|
55
|
-
case strings.Contains(msg, "dependency cycle"):
|
|
56
|
-
return "Break the circular dependency chain between tasks"
|
|
57
|
-
case strings.Contains(msg, "audit proposal exists"):
|
|
58
|
-
return "Set router state to audit-pending for the matching epic, or remove stale audit files"
|
|
59
|
-
case strings.Contains(msg, "orphaned"):
|
|
60
|
-
return "Move the task directory to the correct epic or create the referenced epic"
|
|
61
|
-
case strings.Contains(msg, "quality gate"):
|
|
62
|
-
return "Fix the issue reported by the quality gate tool"
|
|
63
|
-
default:
|
|
64
|
-
return "Review the file and fix the reported issue"
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// GateSuggestion returns a command-specific repair hint.
|
|
69
|
-
func GateSuggestion(name string) string {
|
|
70
|
-
switch name {
|
|
71
|
-
case "lint":
|
|
72
|
-
return "Run `make lint` locally and fix reported issues"
|
|
73
|
-
case "typecheck":
|
|
74
|
-
return "Run `make typecheck` locally and fix type errors"
|
|
75
|
-
case "test":
|
|
76
|
-
return "Run `make test` locally and fix failing tests"
|
|
77
|
-
default:
|
|
78
|
-
return fmt.Sprintf("Run %q locally and fix reported issues", name)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
package doctor
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"errors"
|
|
5
|
-
"fmt"
|
|
6
|
-
"strings"
|
|
7
|
-
"testing"
|
|
8
|
-
|
|
9
|
-
"github.com/opencode/savepoint/internal/data"
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
func TestSuggestRepair(t *testing.T) {
|
|
13
|
-
tests := []struct {
|
|
14
|
-
err error
|
|
15
|
-
contains string
|
|
16
|
-
}{
|
|
17
|
-
{Problem{Message: "config.yml not found"}, "savepoint init"},
|
|
18
|
-
{Problem{Message: "config.yml missing required field: theme"}, "Add the missing field"},
|
|
19
|
-
{Problem{Message: "config.yml invalid YAML: yaml: line 3: could not find expected"}, "Fix the YAML syntax"},
|
|
20
|
-
{Problem{Message: "router.md not found"}, "savepoint init"},
|
|
21
|
-
{Problem{Message: "router.md unknown state \"bogus\""}, "Set router state to a recognized"},
|
|
22
|
-
{Problem{Message: "router.md release \"v99\" directory not found"}, "Create the release directory"},
|
|
23
|
-
{Problem{Message: "router.md epic \"E99-foo\" directory not found"}, "Create the epic directory"},
|
|
24
|
-
{Problem{Message: "release PRD file not found"}, "Create a {release}-PRD.md"},
|
|
25
|
-
{Problem{Message: "epic detail file not found"}, "Create an E##-Detail.md"},
|
|
26
|
-
{Problem{Message: "invalid frontmatter: yaml: line 5:"}, "Fix the YAML frontmatter"},
|
|
27
|
-
{Problem{Message: "task missing required frontmatter field: status"}, "Add the missing field"},
|
|
28
|
-
{Problem{Message: "task missing ## Acceptance Criteria section"}, "Add an ## Acceptance Criteria section"},
|
|
29
|
-
{Problem{Message: "task frontmatter field depends_on must be a list"}, "Change depends_on to a YAML list"},
|
|
30
|
-
{Problem{Message: "depends_on references non-existent task \"E99/T999\""}, "Create the referenced task"},
|
|
31
|
-
{Problem{Message: "duplicate task ID \"E01-foo/T001-task\" (first seen in"}, "Rename one of the tasks"},
|
|
32
|
-
{Problem{Message: "dependency cycle detected:"}, "Break the circular dependency chain"},
|
|
33
|
-
{Problem{Message: "audit proposal exists but router state is"}, "Set router state to audit-pending"},
|
|
34
|
-
{Problem{Message: "orphaned task: epic \"E99-ghost\" does not exist"}, "Move the task directory"},
|
|
35
|
-
{Problem{Message: "quality gate \"lint\" failed"}, "Fix the issue reported by the quality gate"},
|
|
36
|
-
{Problem{Message: "some random unknown problem"}, "Review the file and fix"},
|
|
37
|
-
}
|
|
38
|
-
for _, tt := range tests {
|
|
39
|
-
got := SuggestRepair(tt.err)
|
|
40
|
-
if !strings.Contains(got, tt.contains) {
|
|
41
|
-
t.Errorf("SuggestRepair(%q) = %q, want containing %q", tt.err.Error(), got, tt.contains)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
func TestGateSuggestion(t *testing.T) {
|
|
47
|
-
tests := []struct {
|
|
48
|
-
name string
|
|
49
|
-
contains string
|
|
50
|
-
}{
|
|
51
|
-
{"lint", "make lint"},
|
|
52
|
-
{"typecheck", "make typecheck"},
|
|
53
|
-
{"test", "make test"},
|
|
54
|
-
{"custom", "Run \"custom\" locally"},
|
|
55
|
-
}
|
|
56
|
-
for _, tt := range tests {
|
|
57
|
-
got := GateSuggestion(tt.name)
|
|
58
|
-
if !strings.Contains(got, tt.contains) {
|
|
59
|
-
t.Errorf("GateSuggestion(%q) = %q, want containing %q", tt.name, got, tt.contains)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
func TestSuggestRepair_typedErrors(t *testing.T) {
|
|
65
|
-
tests := []struct {
|
|
66
|
-
err error
|
|
67
|
-
contains string
|
|
68
|
-
}{
|
|
69
|
-
{fmt.Errorf("config.yml not found: %w", data.ErrConfigNotFound), "savepoint init"},
|
|
70
|
-
{fmt.Errorf("router.md not found: %w", data.ErrConfigNotFound), "savepoint init"},
|
|
71
|
-
}
|
|
72
|
-
for _, tt := range tests {
|
|
73
|
-
if !errors.Is(tt.err, data.ErrConfigNotFound) {
|
|
74
|
-
t.Fatalf("test error should wrap %v", data.ErrConfigNotFound)
|
|
75
|
-
}
|
|
76
|
-
got := SuggestRepair(tt.err)
|
|
77
|
-
if !strings.Contains(got, tt.contains) {
|
|
78
|
-
t.Errorf("SuggestRepair(%q) = %q, want containing %q", tt.err.Error(), got, tt.contains)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|