@opengsd/gsd-pi 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/headless.js +2 -1
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +2 -2
- package/dist/resources/extensions/gsd/artifact-verification.js +3 -4
- package/dist/resources/extensions/gsd/auto/loop.js +24 -4
- package/dist/resources/extensions/gsd/auto/run-unit.js +19 -1
- package/dist/resources/extensions/gsd/auto/unit-phase.js +37 -5
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +14 -8
- package/dist/resources/extensions/gsd/auto-dispatch.js +2 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +102 -21
- package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
- package/dist/resources/extensions/gsd/auto-runtime-state.js +5 -5
- package/dist/resources/extensions/gsd/auto-start.js +29 -9
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +40 -0
- package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +4 -0
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +7 -3
- package/dist/resources/extensions/gsd/auto-worktree-teardown.js +1 -1
- package/dist/resources/extensions/gsd/auto.js +15 -5
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +33 -7
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +10 -3
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +4 -24
- package/dist/resources/extensions/gsd/commands-verdict.js +10 -9
- package/dist/resources/extensions/gsd/compat/compat-marker.js +80 -5
- package/dist/resources/extensions/gsd/constants.js +12 -13
- package/dist/resources/extensions/gsd/context-masker.js +97 -21
- package/dist/resources/extensions/gsd/db/engine.js +5 -2
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +22 -0
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +24 -8
- package/dist/resources/extensions/gsd/error-classifier.js +12 -2
- package/dist/resources/extensions/gsd/exec-sandbox.js +1 -0
- package/dist/resources/extensions/gsd/flat-phase-migration.js +12 -0
- package/dist/resources/extensions/gsd/git-service.js +19 -7
- package/dist/resources/extensions/gsd/gsd-db.js +75 -31
- package/dist/resources/extensions/gsd/guided-flow.js +11 -5
- package/dist/resources/extensions/gsd/markdown-renderer.js +5 -21
- package/dist/resources/extensions/gsd/md-importer.js +5 -6
- package/dist/resources/extensions/gsd/model-cost-table.js +2 -0
- package/dist/resources/extensions/gsd/model-router.js +11 -0
- package/dist/resources/extensions/gsd/paths.js +66 -15
- package/dist/resources/extensions/gsd/planning-subagent-policy.js +37 -0
- package/dist/resources/extensions/gsd/planning-subagent-registry.js +25 -0
- package/dist/resources/extensions/gsd/preferences-types.js +5 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +48 -1
- package/dist/resources/extensions/gsd/preferences.js +17 -0
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +5 -5
- package/dist/resources/extensions/gsd/provider-payload-policy.js +16 -5
- package/dist/resources/extensions/gsd/safety/destructive-guard.js +1 -1
- package/dist/resources/extensions/gsd/skill-activation.js +17 -3
- package/dist/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.js +98 -2
- package/dist/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.js +4 -1
- package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +5 -2
- package/dist/resources/extensions/gsd/stop-notice.js +9 -0
- package/dist/resources/extensions/gsd/templates/PREFERENCES.md +5 -0
- package/dist/resources/extensions/gsd/tools/complete-task.js +12 -5
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +14 -5
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +15 -5
- package/dist/resources/extensions/gsd/tools/reopen-task.js +16 -10
- package/dist/resources/extensions/gsd/unit-context-composer.js +28 -5
- package/dist/resources/extensions/gsd/workflow-projections.js +2 -3
- package/dist/resources/extensions/gsd/workspace.js +2 -1
- package/dist/resources/extensions/gsd/worktree-state-projection.js +20 -18
- package/dist/resources/extensions/shared/interview-ui.js +43 -6
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
- package/dist/web/standalone/.next/server/chunks/1991.js +2 -2
- package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
- package/integrations/hermes/pyproject.toml +1 -1
- package/package.json +7 -6
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/compaction/compaction.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/compaction/compaction.js +36 -4
- package/packages/gsd-agent-core/dist/compaction/compaction.js.map +1 -1
- package/packages/gsd-agent-core/dist/sdk.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/sdk.js +1 -0
- package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/gsd-cloud/README.md +9 -3
- package/packages/gsd-cloud/dist/background-cli.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/background-cli.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/background-cli.test.js +248 -0
- package/packages/gsd-cloud/dist/background-cli.test.js.map +1 -0
- package/packages/gsd-cloud/dist/cli.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cli.js +121 -15
- package/packages/gsd-cloud/dist/cli.js.map +1 -1
- package/packages/gsd-cloud/dist/cloud-config.d.ts +1 -1
- package/packages/gsd-cloud/dist/cloud-config.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cloud-config.js +7 -1
- package/packages/gsd-cloud/dist/cloud-config.js.map +1 -1
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts +1 -3
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cloud-runtime.js +9 -7
- package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -1
- package/packages/gsd-cloud/dist/runtime-process.d.ts +27 -0
- package/packages/gsd-cloud/dist/runtime-process.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.js +272 -0
- package/packages/gsd-cloud/dist/runtime-process.js.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/runtime-process.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.test.js +97 -0
- package/packages/gsd-cloud/dist/runtime-process.test.js.map +1 -0
- package/packages/gsd-cloud/package.json +2 -2
- package/packages/mcp-server/README.md +7 -2
- package/packages/mcp-server/dist/cli-runner.d.ts +3 -0
- package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -1
- package/packages/mcp-server/dist/cli-runner.js +119 -33
- package/packages/mcp-server/dist/cli-runner.js.map +1 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +5 -5
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +4 -3
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +5 -1
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +76 -0
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +70 -0
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +6 -1
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +103 -0
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/capability-patches.js +1 -1
- package/packages/pi-coding-agent/dist/core/capability-patches.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +11 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +54 -0
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/GSD-WORKFLOW.md +2 -2
- package/src/resources/extensions/gsd/artifact-verification.ts +3 -8
- package/src/resources/extensions/gsd/auto/loop.ts +48 -4
- package/src/resources/extensions/gsd/auto/run-unit.ts +20 -1
- package/src/resources/extensions/gsd/auto/unit-phase.ts +41 -5
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +14 -7
- package/src/resources/extensions/gsd/auto-dispatch.ts +1 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +118 -19
- package/src/resources/extensions/gsd/auto-recovery.ts +10 -8
- package/src/resources/extensions/gsd/auto-runtime-state.ts +12 -7
- package/src/resources/extensions/gsd/auto-start.ts +31 -8
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +56 -0
- package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +4 -0
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +10 -3
- package/src/resources/extensions/gsd/auto-worktree-teardown.ts +1 -1
- package/src/resources/extensions/gsd/auto.ts +20 -7
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +1 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +41 -12
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -3
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +7 -29
- package/src/resources/extensions/gsd/commands-verdict.ts +16 -9
- package/src/resources/extensions/gsd/compat/compat-marker.ts +99 -5
- package/src/resources/extensions/gsd/constants.ts +14 -13
- package/src/resources/extensions/gsd/context-masker.ts +98 -2
- package/src/resources/extensions/gsd/db/engine.ts +5 -2
- package/src/resources/extensions/gsd/docs/preferences-reference.md +22 -0
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +30 -7
- package/src/resources/extensions/gsd/error-classifier.ts +13 -2
- package/src/resources/extensions/gsd/exec-sandbox.ts +1 -0
- package/src/resources/extensions/gsd/flat-phase-migration.ts +14 -0
- package/src/resources/extensions/gsd/git-service.ts +19 -6
- package/src/resources/extensions/gsd/gsd-db.ts +85 -33
- package/src/resources/extensions/gsd/guided-flow.ts +11 -4
- package/src/resources/extensions/gsd/markdown-renderer.ts +5 -23
- package/src/resources/extensions/gsd/md-importer.ts +5 -5
- package/src/resources/extensions/gsd/model-cost-table.ts +2 -0
- package/src/resources/extensions/gsd/model-router.ts +12 -0
- package/src/resources/extensions/gsd/paths.ts +73 -15
- package/src/resources/extensions/gsd/planning-subagent-policy.ts +58 -0
- package/src/resources/extensions/gsd/planning-subagent-registry.ts +31 -0
- package/src/resources/extensions/gsd/preferences-types.ts +16 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +62 -0
- package/src/resources/extensions/gsd/preferences.ts +25 -0
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +5 -5
- package/src/resources/extensions/gsd/provider-payload-policy.ts +18 -4
- package/src/resources/extensions/gsd/safety/destructive-guard.ts +1 -1
- package/src/resources/extensions/gsd/skill-activation.ts +17 -3
- package/src/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.ts +132 -2
- package/src/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.ts +4 -1
- package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +4 -2
- package/src/resources/extensions/gsd/stop-notice.ts +11 -0
- package/src/resources/extensions/gsd/templates/PREFERENCES.md +5 -0
- package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +264 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/auto-start-index-lock.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +74 -1
- package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/compat-marker.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +76 -1
- package/src/resources/extensions/gsd/tests/context-masker.test.ts +204 -8
- package/src/resources/extensions/gsd/tests/db-engine-cjs-compat.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/db-tools-alias-suppression.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/destructive-guard.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/external-markdown-edit.test.ts +38 -1
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +8 -9
- package/src/resources/extensions/gsd/tests/flat-phase-validation-path.test.ts +20 -1
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/integration/paths.test.ts +22 -4
- package/src/resources/extensions/gsd/tests/integration-branch-meta-location.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +5 -5
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
- package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +7 -5
- package/src/resources/extensions/gsd/tests/preferences.test.ts +84 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +121 -1
- package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +105 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +33 -7
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -16
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/stop-notice.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +13 -7
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +34 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +20 -5
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +24 -5
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +25 -5
- package/src/resources/extensions/gsd/tools/reopen-task.ts +23 -9
- package/src/resources/extensions/gsd/unit-context-composer.ts +34 -8
- package/src/resources/extensions/gsd/workflow-projections.ts +2 -3
- package/src/resources/extensions/gsd/workspace.ts +2 -1
- package/src/resources/extensions/gsd/worktree-state-projection.ts +21 -24
- package/src/resources/extensions/shared/interview-ui.ts +47 -8
- package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +29 -0
- /package/dist/web/standalone/.next/static/{BE5XoklToI6lZjRzpXih8 → EAhnhYMt5UVOiG9-iPDjc}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{BE5XoklToI6lZjRzpXih8 → EAhnhYMt5UVOiG9-iPDjc}/_ssgManifest.js +0 -0
|
@@ -22,7 +22,15 @@ import { logWarning } from "../workflow-logger.js";
|
|
|
22
22
|
import { debugLog } from "../debug-logger.js";
|
|
23
23
|
import { existsSync, unlinkSync } from "node:fs";
|
|
24
24
|
import { join } from "node:path";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
buildFlatTaskFileName,
|
|
27
|
+
buildTaskFileName,
|
|
28
|
+
legacyMilestonesDir,
|
|
29
|
+
resolveMilestonePath,
|
|
30
|
+
resolveSlicePath,
|
|
31
|
+
resolveTasksDir,
|
|
32
|
+
clearPathCache,
|
|
33
|
+
} from "../paths.js";
|
|
26
34
|
|
|
27
35
|
export interface ReopenMilestoneParams {
|
|
28
36
|
milestoneId: string;
|
|
@@ -69,6 +77,10 @@ export async function handleReopenMilestone(
|
|
|
69
77
|
// auto-corrects entities back to "complete", making reopen a no-op (#3161).
|
|
70
78
|
try {
|
|
71
79
|
const milestoneDir = resolveMilestonePath(basePath, params.milestoneId);
|
|
80
|
+
const legacyBase = legacyMilestonesDir(basePath);
|
|
81
|
+
const isLegacy = !!milestoneDir && (
|
|
82
|
+
milestoneDir.startsWith(legacyBase + "/") || milestoneDir.startsWith(legacyBase + "\\")
|
|
83
|
+
);
|
|
72
84
|
if (milestoneDir) {
|
|
73
85
|
const milestoneSummary = join(milestoneDir, `${params.milestoneId}-SUMMARY.md`);
|
|
74
86
|
if (existsSync(milestoneSummary)) unlinkSync(milestoneSummary);
|
|
@@ -85,10 +97,17 @@ export async function handleReopenMilestone(
|
|
|
85
97
|
}
|
|
86
98
|
|
|
87
99
|
const tasksDir = resolveTasksDir(basePath, params.milestoneId, slice.id);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
100
|
+
const tasks = getSliceTasks(params.milestoneId, slice.id);
|
|
101
|
+
for (const task of tasks) {
|
|
102
|
+
const taskSummaries = isLegacy
|
|
103
|
+
? (tasksDir ? [join(tasksDir, buildTaskFileName(task.id, "SUMMARY"))] : [])
|
|
104
|
+
: milestoneDir
|
|
105
|
+
? [
|
|
106
|
+
join(milestoneDir, buildFlatTaskFileName(slice.id, task.id, "SUMMARY")),
|
|
107
|
+
join(milestoneDir, buildTaskFileName(task.id, "SUMMARY")),
|
|
108
|
+
]
|
|
109
|
+
: [];
|
|
110
|
+
for (const taskSummary of taskSummaries) {
|
|
92
111
|
if (existsSync(taskSummary)) unlinkSync(taskSummary);
|
|
93
112
|
}
|
|
94
113
|
}
|
|
@@ -27,7 +27,15 @@ import { appendEvent } from "../workflow-events.js";
|
|
|
27
27
|
import { logWarning } from "../workflow-logger.js";
|
|
28
28
|
import { existsSync, unlinkSync } from "node:fs";
|
|
29
29
|
import { join } from "node:path";
|
|
30
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
buildFlatTaskFileName,
|
|
32
|
+
buildTaskFileName,
|
|
33
|
+
legacyMilestonesDir,
|
|
34
|
+
resolveMilestonePath,
|
|
35
|
+
resolveTasksDir,
|
|
36
|
+
resolveSlicePath,
|
|
37
|
+
clearPathCache,
|
|
38
|
+
} from "../paths.js";
|
|
31
39
|
|
|
32
40
|
export interface ReopenSliceParams {
|
|
33
41
|
milestoneId: string;
|
|
@@ -80,11 +88,23 @@ export async function handleReopenSlice(
|
|
|
80
88
|
// Without this, the DB-filesystem reconciler sees SUMMARY.md files and
|
|
81
89
|
// auto-corrects tasks back to "complete", making reopen a no-op (#3161).
|
|
82
90
|
try {
|
|
91
|
+
const milestoneDir = resolveMilestonePath(basePath, params.milestoneId);
|
|
92
|
+
const legacyBase = legacyMilestonesDir(basePath);
|
|
93
|
+
const isLegacy = !!milestoneDir && (
|
|
94
|
+
milestoneDir.startsWith(legacyBase + "/") || milestoneDir.startsWith(legacyBase + "\\")
|
|
95
|
+
);
|
|
83
96
|
const tasksDir = resolveTasksDir(basePath, params.milestoneId, params.sliceId);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
97
|
+
const tasks = getSliceTasks(params.milestoneId, params.sliceId);
|
|
98
|
+
for (const task of tasks) {
|
|
99
|
+
const summaryPaths = isLegacy
|
|
100
|
+
? (tasksDir ? [join(tasksDir, buildTaskFileName(task.id, "SUMMARY"))] : [])
|
|
101
|
+
: milestoneDir
|
|
102
|
+
? [
|
|
103
|
+
join(milestoneDir, buildFlatTaskFileName(params.sliceId, task.id, "SUMMARY")),
|
|
104
|
+
join(milestoneDir, buildTaskFileName(task.id, "SUMMARY")),
|
|
105
|
+
]
|
|
106
|
+
: [];
|
|
107
|
+
for (const summaryPath of summaryPaths) {
|
|
88
108
|
if (existsSync(summaryPath)) unlinkSync(summaryPath);
|
|
89
109
|
}
|
|
90
110
|
}
|
|
@@ -25,8 +25,16 @@ import { appendEvent } from "../workflow-events.js";
|
|
|
25
25
|
import { logWarning } from "../workflow-logger.js";
|
|
26
26
|
import { writeReopenReason } from "../reopen-reason.js";
|
|
27
27
|
import { existsSync, unlinkSync } from "node:fs";
|
|
28
|
-
import { join
|
|
29
|
-
import {
|
|
28
|
+
import { join } from "node:path";
|
|
29
|
+
import {
|
|
30
|
+
buildFlatTaskFileName,
|
|
31
|
+
buildTaskFileName,
|
|
32
|
+
legacyMilestonesDir,
|
|
33
|
+
resolveMilestonePath,
|
|
34
|
+
resolveTasksDir,
|
|
35
|
+
resolveSlicePath,
|
|
36
|
+
clearPathCache,
|
|
37
|
+
} from "../paths.js";
|
|
30
38
|
|
|
31
39
|
export interface ReopenTaskParams {
|
|
32
40
|
milestoneId: string;
|
|
@@ -113,13 +121,19 @@ export async function handleReopenTask(
|
|
|
113
121
|
// summary cleanup into tasks/ (#1208).
|
|
114
122
|
try {
|
|
115
123
|
const slicePath = resolveSlicePath(basePath, params.milestoneId, params.sliceId);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
const milestonePath = resolveMilestonePath(basePath, params.milestoneId);
|
|
125
|
+
if (milestonePath) {
|
|
126
|
+
const legacyBase = legacyMilestonesDir(basePath);
|
|
127
|
+
const isLegacy = milestonePath.startsWith(legacyBase + "/") || milestonePath.startsWith(legacyBase + "\\");
|
|
128
|
+
const summaryPaths = isLegacy
|
|
129
|
+
? [join(resolveTasksDir(basePath, params.milestoneId, params.sliceId) ?? slicePath ?? join(milestonePath, "slices", params.sliceId, "tasks"), buildTaskFileName(params.taskId, "SUMMARY"))]
|
|
130
|
+
: [
|
|
131
|
+
join(milestonePath, buildFlatTaskFileName(params.sliceId, params.taskId, "SUMMARY")),
|
|
132
|
+
join(milestonePath, buildTaskFileName(params.taskId, "SUMMARY")),
|
|
133
|
+
];
|
|
134
|
+
for (const summaryPath of summaryPaths) {
|
|
135
|
+
if (existsSync(summaryPath)) unlinkSync(summaryPath);
|
|
136
|
+
}
|
|
123
137
|
}
|
|
124
138
|
} catch (cleanupErr) {
|
|
125
139
|
logWarning("tool", `reopen-task artifact cleanup warning: ${(cleanupErr as Error).message}`);
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
type ToolsPolicy,
|
|
47
47
|
type UnitContextManifest,
|
|
48
48
|
} from "./unit-context-manifest.js";
|
|
49
|
+
import { resolveEffectivePlanningToolsPolicy } from "./planning-subagent-policy.js";
|
|
49
50
|
import { getUnitToolSurfaceContract } from "./unit-tool-contracts.js";
|
|
50
51
|
import type { UnitPromptContextContract } from "./tool-contract.js";
|
|
51
52
|
|
|
@@ -207,6 +208,7 @@ export function composeContextModeInstructions(
|
|
|
207
208
|
|
|
208
209
|
export interface ComposeToolSurfaceInstructionOptions {
|
|
209
210
|
readonly renderMode: ContextModeRenderMode;
|
|
211
|
+
readonly basePath?: string;
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
const TOOL_SURFACE_GUIDANCE_BY_UNIT: Record<string, string> = {
|
|
@@ -226,16 +228,39 @@ const TOOL_SURFACE_GUIDANCE_BY_UNIT: Record<string, string> = {
|
|
|
226
228
|
"Persist completion only through `gsd_complete_milestone` after verification passes. Do not query `.gsd/gsd.db` directly. Do not write `.gsd/PROJECT.md` or `.gsd/REQUIREMENTS.md` by hand — use `gsd_summary_save` and `gsd_requirement_update`.",
|
|
227
229
|
"replan-slice":
|
|
228
230
|
"Persist replans through `gsd_replan_slice` only. Do not edit `PLAN.md` or task plans directly.",
|
|
229
|
-
"plan-slice":
|
|
230
|
-
"Persist planning through `gsd_plan_slice` only. Dispatch subagents only to **scout** or **planner** for reconnaissance — not implementation agents. Do not edit user source files outside `.gsd/**`.",
|
|
231
|
-
"refine-slice":
|
|
232
|
-
"Persist refinements through `gsd_plan_slice` only. Dispatch subagents only to **scout** or **planner**. Do not edit user source files outside `.gsd/**`.",
|
|
233
|
-
"plan-milestone":
|
|
234
|
-
"Persist milestone planning through `gsd_plan_milestone` / `gsd_plan_slice`. Do not edit user source files outside `.gsd/**`.",
|
|
235
231
|
"research-slice":
|
|
236
232
|
"Dispatch subagents only to **scout** or **planner** for reconnaissance. Do not edit user source files outside `.gsd/**`.",
|
|
237
233
|
};
|
|
238
234
|
|
|
235
|
+
function formatAllowedAgents(agents: readonly string[]): string {
|
|
236
|
+
return agents.map((agent) => `**${agent}**`).join(", ");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function guidanceForUnitToolsPolicy(unitType: string, policy: ToolsPolicy): string | undefined {
|
|
240
|
+
if (unitType === "plan-slice") {
|
|
241
|
+
const dispatch = policy.mode === "planning-dispatch"
|
|
242
|
+
? ` Dispatch subagents only to ${formatAllowedAgents(policy.allowedSubagents)} for reconnaissance — not implementation agents.`
|
|
243
|
+
: " Do not dispatch subagents.";
|
|
244
|
+
return `Persist planning through \`gsd_plan_slice\` only.${dispatch} Do not edit user source files outside \`.gsd/**\`.`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (unitType === "refine-slice") {
|
|
248
|
+
const dispatch = policy.mode === "planning-dispatch"
|
|
249
|
+
? ` Dispatch subagents only to ${formatAllowedAgents(policy.allowedSubagents)}.`
|
|
250
|
+
: " Do not dispatch subagents.";
|
|
251
|
+
return `Persist refinements through \`gsd_plan_slice\` only.${dispatch} Do not edit user source files outside \`.gsd/**\`.`;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (unitType === "plan-milestone") {
|
|
255
|
+
const dispatch = policy.mode === "planning-dispatch"
|
|
256
|
+
? ` Dispatch subagents only to ${formatAllowedAgents(policy.allowedSubagents)}.`
|
|
257
|
+
: "";
|
|
258
|
+
return `Persist milestone planning through \`gsd_plan_milestone\` / \`gsd_plan_slice\`.${dispatch} Do not edit user source files outside \`.gsd/**\`.`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
|
|
239
264
|
function guidanceForToolsPolicy(policy: ToolsPolicy): string | null {
|
|
240
265
|
switch (policy.mode) {
|
|
241
266
|
case "planning":
|
|
@@ -280,8 +305,9 @@ export function composeToolSurfaceInstructions(
|
|
|
280
305
|
const manifest = resolveManifest(unitType);
|
|
281
306
|
if (!manifest) return "";
|
|
282
307
|
|
|
283
|
-
const
|
|
284
|
-
const
|
|
308
|
+
const effectiveTools = resolveEffectivePlanningToolsPolicy(unitType, manifest.tools, opts.basePath) ?? manifest.tools;
|
|
309
|
+
const unitGuidance = guidanceForUnitToolsPolicy(unitType, effectiveTools) ?? TOOL_SURFACE_GUIDANCE_BY_UNIT[unitType];
|
|
310
|
+
const policyGuidance = unitGuidance ? null : guidanceForToolsPolicy(effectiveTools);
|
|
285
311
|
const forbiddenLine = formatForbiddenWorkflowToolsLine(unitType, unitGuidance);
|
|
286
312
|
const parts = [unitGuidance, policyGuidance, forbiddenLine].filter(
|
|
287
313
|
(part): part is string => typeof part === "string" && part.length > 0,
|
|
@@ -26,7 +26,7 @@ import { deriveState } from "./state.js";
|
|
|
26
26
|
import type { GSDState } from "./types.js";
|
|
27
27
|
import { renderPlanFromDb, renderRoadmapFromDb } from "./markdown-renderer.js";
|
|
28
28
|
import { readManifest } from "./workflow-manifest.js";
|
|
29
|
-
import { gsdRoot, resolveMilestoneFile, resolveSliceFile, resolveTaskFile,
|
|
29
|
+
import { gsdRoot, resolveMilestoneFile, resolveSliceFile, resolveTaskFile, targetTaskFile } from "./paths.js";
|
|
30
30
|
|
|
31
31
|
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
32
32
|
|
|
@@ -329,8 +329,7 @@ export function renderSummaryProjection(basePath: string, milestoneId: string, s
|
|
|
329
329
|
const evidenceRows = getVerificationEvidence(milestoneId, sliceId, taskId);
|
|
330
330
|
const content = renderSummaryContent(taskRow, sliceId, milestoneId, evidenceRows);
|
|
331
331
|
|
|
332
|
-
const summaryPath =
|
|
333
|
-
?? join(basePath, relTaskFile(basePath, milestoneId, sliceId, taskId, "SUMMARY", getMilestone(milestoneId)?.title));
|
|
332
|
+
const summaryPath = targetTaskFile(basePath, milestoneId, sliceId, taskId, "SUMMARY", getMilestone(milestoneId)?.title);
|
|
334
333
|
mkdirSync(dirname(summaryPath), { recursive: true });
|
|
335
334
|
atomicWriteSync(summaryPath, content);
|
|
336
335
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
normalizeRealPath,
|
|
11
11
|
} from "./paths.js";
|
|
12
12
|
import { isGsdWorktreePath, resolveWorktreeProjectRoot } from "./worktree-root.js";
|
|
13
|
+
import { milestoneMetaPath } from "./git-service.js";
|
|
13
14
|
|
|
14
15
|
export type GsdWorkspaceMode = "project" | "worktree";
|
|
15
16
|
|
|
@@ -107,7 +108,7 @@ export function scopeMilestone(workspace: GsdWorkspace, milestoneId: string): Mi
|
|
|
107
108
|
stateFile: () => join(gsd, "STATE.md"),
|
|
108
109
|
dbPath: () => contract.projectDb,
|
|
109
110
|
milestoneDir: () => scopeMilestoneDir(basePath, milestoneId),
|
|
110
|
-
metaJson: () =>
|
|
111
|
+
metaJson: () => milestoneMetaPath(basePath, milestoneId),
|
|
111
112
|
});
|
|
112
113
|
|
|
113
114
|
return scope;
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
import { join } from "node:path";
|
|
33
33
|
|
|
34
34
|
import { reconcileWorktreeDb } from "./gsd-db.js";
|
|
35
|
-
import { resolveGsdPathContract } from "./paths.js";
|
|
35
|
+
import { dirIsContentBearingLegacyMilestone, resolveGsdPathContract } from "./paths.js";
|
|
36
36
|
import { safeCopy, safeCopyRecursive } from "./safe-fs.js";
|
|
37
37
|
import type { MilestoneScope } from "./workspace.js";
|
|
38
38
|
import { logError, logWarning } from "./workflow-logger.js";
|
|
@@ -161,6 +161,7 @@ function syncOtherMilestoneArtifacts(
|
|
|
161
161
|
// additive safeCopyRecursive; skip it here to avoid redundant work.
|
|
162
162
|
if (milestoneEntry.name === currentMilestoneId) continue;
|
|
163
163
|
const srcMilestoneDir = join(srcMilestonesDir, milestoneEntry.name);
|
|
164
|
+
if (!dirIsContentBearingLegacyMilestone(srcMilestoneDir)) continue;
|
|
164
165
|
const dstMilestoneDir = join(dstMilestonesDir, milestoneEntry.name);
|
|
165
166
|
|
|
166
167
|
// Additively project the entire milestone subtree (force:false), not just
|
|
@@ -271,36 +272,32 @@ export function _projectRootToWorktreeImpl(
|
|
|
271
272
|
// Without this, worktree-local files (e.g. VALIDATION.md written
|
|
272
273
|
// by validate-milestone) get clobbered by stale project root copies,
|
|
273
274
|
// causing an infinite re-validation loop (#1886).
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
{ force: false }
|
|
278
|
-
|
|
275
|
+
const prMilestoneDir = join(prGsd, "milestones", milestoneId);
|
|
276
|
+
const wtMilestoneDir = join(wtGsd, "milestones", milestoneId);
|
|
277
|
+
if (dirIsContentBearingLegacyMilestone(prMilestoneDir)) {
|
|
278
|
+
safeCopyRecursive(prMilestoneDir, wtMilestoneDir, { force: false });
|
|
279
|
+
|
|
280
|
+
// Force-sync ASSESSMENT files that have a verdict from project root (#2821).
|
|
281
|
+
// The additive-only copy above preserves worktree-local files, but
|
|
282
|
+
// ASSESSMENT files are special: after run-uat writes a verdict and post-unit
|
|
283
|
+
// syncs it to the project root, the worktree may retain a stale copy (e.g.
|
|
284
|
+
// verdict:fail while the project root has verdict:pass from a retry). On
|
|
285
|
+
// session resume the DB is rebuilt from disk, and if the stale ASSESSMENT
|
|
286
|
+
// persists, checkNeedsRunUat finds no passing verdict → re-dispatches
|
|
287
|
+
// run-uat indefinitely (stuck-loop ×9).
|
|
288
|
+
forceOverwriteValidationWithVerdict(prMilestoneDir, wtMilestoneDir, milestoneId);
|
|
289
|
+
forceOverwriteAssessmentsWithVerdict(prMilestoneDir, wtMilestoneDir);
|
|
290
|
+
}
|
|
279
291
|
|
|
280
|
-
// Additively project the full subtree of every OTHER
|
|
281
|
-
// worktree-bound units can read prior-milestone context artifacts
|
|
282
|
-
//
|
|
283
|
-
// UAT.md on the worktree filesystem (otherwise the stale-render detector
|
|
284
|
-
// flags them as "complete in DB but missing on disk").
|
|
292
|
+
// Additively project the full subtree of every OTHER content-bearing legacy
|
|
293
|
+
// milestone so worktree-bound units can read prior-milestone context artifacts
|
|
294
|
+
// without recreating empty/meta-only milestones/ scaffolds in flat-phase projects.
|
|
285
295
|
syncOtherMilestoneArtifacts(
|
|
286
296
|
join(prGsd, "milestones"),
|
|
287
297
|
join(wtGsd, "milestones"),
|
|
288
298
|
milestoneId,
|
|
289
299
|
);
|
|
290
300
|
|
|
291
|
-
// Force-sync ASSESSMENT files that have a verdict from project root (#2821).
|
|
292
|
-
// The additive-only copy above preserves worktree-local files, but
|
|
293
|
-
// ASSESSMENT files are special: after run-uat writes a verdict and post-unit
|
|
294
|
-
// syncs it to the project root, the worktree may retain a stale copy (e.g.
|
|
295
|
-
// verdict:fail while the project root has verdict:pass from a retry). On
|
|
296
|
-
// session resume the DB is rebuilt from disk, and if the stale ASSESSMENT
|
|
297
|
-
// persists, checkNeedsRunUat finds no passing verdict → re-dispatches
|
|
298
|
-
// run-uat indefinitely (stuck-loop ×9).
|
|
299
|
-
const prMilestoneDir = join(prGsd, "milestones", milestoneId);
|
|
300
|
-
const wtMilestoneDir = join(wtGsd, "milestones", milestoneId);
|
|
301
|
-
forceOverwriteValidationWithVerdict(prMilestoneDir, wtMilestoneDir, milestoneId);
|
|
302
|
-
forceOverwriteAssessmentsWithVerdict(prMilestoneDir, wtMilestoneDir);
|
|
303
|
-
|
|
304
301
|
// Forward-sync completed-units.json from project root to worktree.
|
|
305
302
|
// Project root is authoritative for completion state after crash recovery;
|
|
306
303
|
// without this, the worktree re-dispatches already-completed units (#1886).
|
|
@@ -252,6 +252,11 @@ export async function showInterviewRound(
|
|
|
252
252
|
let previewTotal = 0;
|
|
253
253
|
function resetPreviewScroll() { previewScroll = 0; }
|
|
254
254
|
|
|
255
|
+
let optionsScroll = 0;
|
|
256
|
+
let optionsViewport = 0;
|
|
257
|
+
let optionsTotal = 0;
|
|
258
|
+
function resetOptionsScroll() { optionsScroll = 0; }
|
|
259
|
+
|
|
255
260
|
function finish(result: RoundResult) {
|
|
256
261
|
if (completed) return;
|
|
257
262
|
completed = true;
|
|
@@ -318,6 +323,7 @@ export async function showInterviewRound(
|
|
|
318
323
|
saveEditorToState();
|
|
319
324
|
currentIdx = newIdx;
|
|
320
325
|
resetPreviewScroll();
|
|
326
|
+
resetOptionsScroll();
|
|
321
327
|
loadStateToEditor();
|
|
322
328
|
focusNotes = states[currentIdx].notesVisible && states[currentIdx].notes.length > 0;
|
|
323
329
|
refresh();
|
|
@@ -464,20 +470,39 @@ export async function showInterviewRound(
|
|
|
464
470
|
if (matchesKey(data, Key.right)) { switchQuestion((currentIdx + 1) % questions.length); return; }
|
|
465
471
|
}
|
|
466
472
|
|
|
467
|
-
// ──
|
|
473
|
+
// ── Side-by-side panel scrolling ─────────────────────────────
|
|
468
474
|
if (matchesKey(data, Key.pageUp)) {
|
|
469
|
-
|
|
475
|
+
let didScroll = false;
|
|
476
|
+
if (optionsScroll > 0) {
|
|
477
|
+
optionsScroll = Math.max(0, optionsScroll - optionsViewport);
|
|
478
|
+
didScroll = true;
|
|
479
|
+
}
|
|
480
|
+
if (previewScroll > 0) {
|
|
481
|
+
previewScroll = Math.max(0, previewScroll - previewViewport);
|
|
482
|
+
didScroll = true;
|
|
483
|
+
}
|
|
484
|
+
if (didScroll) refresh();
|
|
470
485
|
return;
|
|
471
486
|
}
|
|
472
487
|
if (matchesKey(data, Key.pageDown)) {
|
|
488
|
+
let didScroll = false;
|
|
489
|
+
const maxOptionsScroll = Math.max(0, optionsTotal - optionsViewport);
|
|
490
|
+
if (optionsScroll < maxOptionsScroll) {
|
|
491
|
+
optionsScroll = Math.min(maxOptionsScroll, optionsScroll + optionsViewport);
|
|
492
|
+
didScroll = true;
|
|
493
|
+
}
|
|
473
494
|
const maxScroll = Math.max(0, previewTotal - previewViewport);
|
|
474
|
-
if (previewScroll < maxScroll) {
|
|
495
|
+
if (previewScroll < maxScroll) {
|
|
496
|
+
previewScroll = Math.min(maxScroll, previewScroll + previewViewport);
|
|
497
|
+
didScroll = true;
|
|
498
|
+
}
|
|
499
|
+
if (didScroll) refresh();
|
|
475
500
|
return;
|
|
476
501
|
}
|
|
477
502
|
|
|
478
503
|
// ── Cursor navigation ────────────────────────────────────────
|
|
479
|
-
if (matchesKey(data, Key.up)) { st.cursorIndex = (st.cursorIndex - 1 + optCount) % optCount; resetPreviewScroll(); refresh(); return; }
|
|
480
|
-
if (matchesKey(data, Key.down)) { st.cursorIndex = (st.cursorIndex + 1) % optCount; resetPreviewScroll(); refresh(); return; }
|
|
504
|
+
if (matchesKey(data, Key.up)) { st.cursorIndex = (st.cursorIndex - 1 + optCount) % optCount; resetPreviewScroll(); resetOptionsScroll(); refresh(); return; }
|
|
505
|
+
if (matchesKey(data, Key.down)) { st.cursorIndex = (st.cursorIndex + 1) % optCount; resetPreviewScroll(); resetOptionsScroll(); refresh(); return; }
|
|
481
506
|
|
|
482
507
|
if (multiSel) {
|
|
483
508
|
const doneI = noneOrDoneIdx(currentIdx);
|
|
@@ -695,6 +720,8 @@ export async function showInterviewRound(
|
|
|
695
720
|
// No scrollable preview unless the side-by-side path sets these below.
|
|
696
721
|
previewTotal = 0;
|
|
697
722
|
previewViewport = 0;
|
|
723
|
+
optionsTotal = 0;
|
|
724
|
+
optionsViewport = 0;
|
|
698
725
|
|
|
699
726
|
if (useSideBySide) {
|
|
700
727
|
// ── Preview path ──────────────────────────────────────
|
|
@@ -734,10 +761,21 @@ export async function showInterviewRound(
|
|
|
734
761
|
const leftWidth = Math.max(MIN_OPTIONS_WIDTH, contentWidth - previewWidth - DIVIDER_WIDTH);
|
|
735
762
|
|
|
736
763
|
const fullLeft = renderOptionsColumn(leftWidth);
|
|
737
|
-
|
|
764
|
+
optionsTotal = fullLeft.length;
|
|
765
|
+
optionsViewport = maxBody;
|
|
766
|
+
const maxOptionsScroll = Math.max(0, fullLeft.length - maxBody);
|
|
767
|
+
if (optionsScroll > maxOptionsScroll) optionsScroll = maxOptionsScroll;
|
|
768
|
+
|
|
769
|
+
const leftLines = fullLeft.slice(optionsScroll, optionsScroll + maxBody);
|
|
738
770
|
if (fullLeft.length > maxBody) {
|
|
739
|
-
const
|
|
740
|
-
|
|
771
|
+
const hiddenAbove = optionsScroll;
|
|
772
|
+
const hiddenBelow = fullLeft.length - (optionsScroll + maxBody);
|
|
773
|
+
if (hiddenAbove > 0) {
|
|
774
|
+
leftLines[0] = scrollIndicator(`▲ ${hiddenAbove} more`, leftWidth);
|
|
775
|
+
}
|
|
776
|
+
if (hiddenBelow > 0) {
|
|
777
|
+
leftLines[leftLines.length - 1] = scrollIndicator(`▼ ${hiddenBelow} more · PgUp/PgDn`, leftWidth);
|
|
778
|
+
}
|
|
741
779
|
}
|
|
742
780
|
|
|
743
781
|
const preview = getCurrentPreview();
|
|
@@ -791,6 +829,7 @@ export async function showInterviewRound(
|
|
|
791
829
|
hints.push(isLast && allAnswered() ? "enter to review" : "enter to next");
|
|
792
830
|
}
|
|
793
831
|
if (previewTotal > previewViewport) hints.push("pgup/pgdn scroll preview");
|
|
832
|
+
if (optionsTotal > optionsViewport) hints.push("pgup/pgdn scroll options");
|
|
794
833
|
hints.push("esc to exit");
|
|
795
834
|
const footer = ui.hints(hints)[0] ?? "";
|
|
796
835
|
|
|
@@ -192,4 +192,33 @@ describe("interview-ui dialog borders", () => {
|
|
|
192
192
|
const back = stripAnsi(widget.render(100).join("\n"));
|
|
193
193
|
assert.doesNotMatch(back, /▲ \d+ more/, "top indicator should disappear after scrolling back to top");
|
|
194
194
|
});
|
|
195
|
+
|
|
196
|
+
it("makes overflowing side-by-side options scrollable with PgUp/PgDn", async () => {
|
|
197
|
+
const overflowOptions = Array.from({ length: 8 }, (_, i) => ({
|
|
198
|
+
label: `Overflow Option ${i + 1}`,
|
|
199
|
+
description: `Detailed explanation for option ${i + 1} that is intentionally long enough to wrap in the options column and force vertical overflow.`,
|
|
200
|
+
...(i === 0 ? { preview: "Short preview" } : {}),
|
|
201
|
+
}));
|
|
202
|
+
const widget = await captureInterviewWidget([{
|
|
203
|
+
...questions[0],
|
|
204
|
+
options: overflowOptions,
|
|
205
|
+
}]);
|
|
206
|
+
|
|
207
|
+
const initial = stripAnsi(widget.render(100).join("\n"));
|
|
208
|
+
assertFullOuterBorder(widget.render(100), 100);
|
|
209
|
+
assert.match(initial, /▼ \d+ more/, "bottom scroll indicator should appear when options overflow");
|
|
210
|
+
assert.match(initial, /pgup\/pgdn scroll options/, "footer should hint at options scrolling");
|
|
211
|
+
assert.doesNotMatch(initial, /\+\d+ lines hidden/, "options overflow should not be a dead-end hidden-lines marker");
|
|
212
|
+
assert.doesNotMatch(initial, /▲ \d+ more/, "top indicator should be absent before scrolling");
|
|
213
|
+
|
|
214
|
+
widget.handleInput(PAGE_DOWN);
|
|
215
|
+
const scrolled = stripAnsi(widget.render(100).join("\n"));
|
|
216
|
+
assertFullOuterBorder(widget.render(100), 100);
|
|
217
|
+
assert.match(scrolled, /▲ \d+ more/, "top scroll indicator should appear after scrolling down");
|
|
218
|
+
assert.ok(scrolled !== initial, "scrolling should change the rendered options");
|
|
219
|
+
|
|
220
|
+
widget.handleInput(PAGE_UP);
|
|
221
|
+
const back = stripAnsi(widget.render(100).join("\n"));
|
|
222
|
+
assert.doesNotMatch(back, /▲ \d+ more/, "top indicator should disappear after scrolling back to top");
|
|
223
|
+
});
|
|
195
224
|
});
|
|
File without changes
|
|
File without changes
|