all-hands-cli 0.1.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/.allhands/README.md +75 -0
- package/.allhands/agents/compounder.yaml +15 -0
- package/.allhands/agents/coordinator.yaml +17 -0
- package/.allhands/agents/documentor.yaml +15 -0
- package/.allhands/agents/e2e-test-planner.yaml +17 -0
- package/.allhands/agents/emergent.yaml +22 -0
- package/.allhands/agents/executor.yaml +14 -0
- package/.allhands/agents/ideation.yaml +11 -0
- package/.allhands/agents/initiative-steering.yaml +19 -0
- package/.allhands/agents/judge.yaml +13 -0
- package/.allhands/agents/planner.yaml +19 -0
- package/.allhands/agents/pr-reviewer.yaml +15 -0
- package/.allhands/docs.json +5 -0
- package/.allhands/docs.local.json +26 -0
- package/.allhands/flows/COMPOUNDING.md +203 -0
- package/.allhands/flows/COORDINATION.md +89 -0
- package/.allhands/flows/CORE.md +87 -0
- package/.allhands/flows/DOCUMENTATION.md +218 -0
- package/.allhands/flows/E2E_TEST_PLAN_BUILDING.md +140 -0
- package/.allhands/flows/EMERGENT_PLANNING.md +57 -0
- package/.allhands/flows/IDEATION_SCOPING.md +154 -0
- package/.allhands/flows/INITIATIVE_STEERING.md +110 -0
- package/.allhands/flows/JUDGE_REVIEWING.md +79 -0
- package/.allhands/flows/PROMPT_TASK_EXECUTION.md +68 -0
- package/.allhands/flows/PR_REVIEWING.md +43 -0
- package/.allhands/flows/SPEC_PLANNING.md +216 -0
- package/.allhands/flows/harness/WRITING_HARNESS_FLOWS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_KNOWLEDGE.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_ORCHESTRATION.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_SKILLS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_TOOLS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_VALIDATION_TOOLING.md +27 -0
- package/.allhands/flows/shared/CODEBASE_UNDERSTANDING.md +72 -0
- package/.allhands/flows/shared/CREATE_HARNESS_SPEC.md +48 -0
- package/.allhands/flows/shared/CREATE_SPEC.md +41 -0
- package/.allhands/flows/shared/CREATE_VALIDATION_TOOLING_SPEC.md +70 -0
- package/.allhands/flows/shared/DOCUMENTATION_DISCOVERY.md +123 -0
- package/.allhands/flows/shared/DOCUMENTATION_WRITER.md +101 -0
- package/.allhands/flows/shared/EMERGENT_REFINEMENT_ANALYSIS.md +76 -0
- package/.allhands/flows/shared/EXTERNAL_TECH_GUIDANCE.md +97 -0
- package/.allhands/flows/shared/IDEATION_CODEBASE_GROUNDING.md +49 -0
- package/.allhands/flows/shared/PLAN_DEEPENING.md +152 -0
- package/.allhands/flows/shared/PROMPT_TASKS_CURATION.md +113 -0
- package/.allhands/flows/shared/PROMPT_VALIDATION_REVIEW.MD +99 -0
- package/.allhands/flows/shared/QUICK_PREMORTEM.md +70 -0
- package/.allhands/flows/shared/RESEARCH_GUIDANCE.md +38 -0
- package/.allhands/flows/shared/REVIEW_OPTIONS_BREAKDOWN.md +68 -0
- package/.allhands/flows/shared/SKILL_EXTRACTION.md +84 -0
- package/.allhands/flows/shared/SPEC_FLOW_ANALYSIS.md +119 -0
- package/.allhands/flows/shared/TDD_WORKFLOW.md +109 -0
- package/.allhands/flows/shared/UTILIZE_VALIDATION_TOOLING.md +84 -0
- package/.allhands/flows/shared/WRITING_HARNESS_FLOWS.md +11 -0
- package/.allhands/flows/shared/WRITING_HARNESS_MCP_TOOLS.md +84 -0
- package/.allhands/flows/shared/jury/ARCHITECTURE_REVIEW.md +91 -0
- package/.allhands/flows/shared/jury/BEST_PRACTICES_REVIEW.md +80 -0
- package/.allhands/flows/shared/jury/CLAIM_VERIFICATION_REVIEW.md +101 -0
- package/.allhands/flows/shared/jury/EXPECTATIONS_FIT_REVIEW.md +78 -0
- package/.allhands/flows/shared/jury/MAINTAINABILITY_REVIEW.md +110 -0
- package/.allhands/flows/shared/jury/PROMPTS_EXPECTATIONS_FIT.md +74 -0
- package/.allhands/flows/shared/jury/PROMPTS_FLOW_ANALYSIS.md +92 -0
- package/.allhands/flows/shared/jury/PROMPTS_YAGNI.md +78 -0
- package/.allhands/flows/shared/jury/PROMPT_PREMORTEM.md +125 -0
- package/.allhands/flows/shared/jury/SECURITY_REVIEW.md +86 -0
- package/.allhands/flows/shared/jury/YAGNI_REVIEW.md +82 -0
- package/.allhands/flows/wip/DEBUG_INVESTIGATION.md +162 -0
- package/.allhands/flows/wip/MEMORY_RECALL.md +62 -0
- package/.allhands/harness/ah +131 -0
- package/.allhands/harness/package-lock.json +5292 -0
- package/.allhands/harness/package.json +52 -0
- package/.allhands/harness/src/__tests__/e2e/commands.test.ts +307 -0
- package/.allhands/harness/src/__tests__/e2e/event-loop.test.ts +539 -0
- package/.allhands/harness/src/__tests__/e2e/hooks.test.ts +427 -0
- package/.allhands/harness/src/__tests__/e2e/new-initiative-routing.test.ts +137 -0
- package/.allhands/harness/src/__tests__/e2e/run-e2e.ts +109 -0
- package/.allhands/harness/src/__tests__/e2e/specs-type.test.ts +210 -0
- package/.allhands/harness/src/__tests__/e2e/validation-hooks.test.ts +669 -0
- package/.allhands/harness/src/__tests__/e2e/validation-path-consistency.test.ts +354 -0
- package/.allhands/harness/src/__tests__/e2e/validation.test.ts +528 -0
- package/.allhands/harness/src/__tests__/harness/assertions.ts +318 -0
- package/.allhands/harness/src/__tests__/harness/cli-runner.ts +359 -0
- package/.allhands/harness/src/__tests__/harness/fixture.ts +384 -0
- package/.allhands/harness/src/__tests__/harness/hook-runner.ts +411 -0
- package/.allhands/harness/src/__tests__/harness/index.ts +122 -0
- package/.allhands/harness/src/cli.ts +36 -0
- package/.allhands/harness/src/commands/complexity.ts +177 -0
- package/.allhands/harness/src/commands/context7.ts +202 -0
- package/.allhands/harness/src/commands/docs.ts +557 -0
- package/.allhands/harness/src/commands/hooks.ts +24 -0
- package/.allhands/harness/src/commands/index.ts +51 -0
- package/.allhands/harness/src/commands/knowledge.ts +382 -0
- package/.allhands/harness/src/commands/memories.ts +302 -0
- package/.allhands/harness/src/commands/notify.ts +61 -0
- package/.allhands/harness/src/commands/oracle.ts +158 -0
- package/.allhands/harness/src/commands/perplexity.ts +220 -0
- package/.allhands/harness/src/commands/planning.ts +245 -0
- package/.allhands/harness/src/commands/schema.ts +73 -0
- package/.allhands/harness/src/commands/skills.ts +128 -0
- package/.allhands/harness/src/commands/solutions.ts +353 -0
- package/.allhands/harness/src/commands/spawn.ts +158 -0
- package/.allhands/harness/src/commands/specs.ts +532 -0
- package/.allhands/harness/src/commands/tavily.ts +226 -0
- package/.allhands/harness/src/commands/tools.ts +579 -0
- package/.allhands/harness/src/commands/trace.ts +327 -0
- package/.allhands/harness/src/commands/tui.ts +960 -0
- package/.allhands/harness/src/commands/validate.ts +143 -0
- package/.allhands/harness/src/commands/validation-tools.ts +108 -0
- package/.allhands/harness/src/hooks/context.ts +1442 -0
- package/.allhands/harness/src/hooks/enforcement.ts +170 -0
- package/.allhands/harness/src/hooks/index.ts +54 -0
- package/.allhands/harness/src/hooks/lifecycle.ts +229 -0
- package/.allhands/harness/src/hooks/notification.ts +104 -0
- package/.allhands/harness/src/hooks/observability.ts +551 -0
- package/.allhands/harness/src/hooks/session.ts +88 -0
- package/.allhands/harness/src/hooks/shared.ts +815 -0
- package/.allhands/harness/src/hooks/transcript-parser.ts +208 -0
- package/.allhands/harness/src/hooks/validation.ts +617 -0
- package/.allhands/harness/src/lib/__tests__/ctags.test.ts +244 -0
- package/.allhands/harness/src/lib/__tests__/docs-validation.test.ts +344 -0
- package/.allhands/harness/src/lib/__tests__/mcp-runtime.test.ts +190 -0
- package/.allhands/harness/src/lib/__tests__/schema.test.ts +861 -0
- package/.allhands/harness/src/lib/base-command.ts +198 -0
- package/.allhands/harness/src/lib/cli-daemon.ts +343 -0
- package/.allhands/harness/src/lib/compaction.ts +313 -0
- package/.allhands/harness/src/lib/ctags.ts +497 -0
- package/.allhands/harness/src/lib/docs-validation.ts +907 -0
- package/.allhands/harness/src/lib/event-loop.ts +662 -0
- package/.allhands/harness/src/lib/flows.ts +155 -0
- package/.allhands/harness/src/lib/git.ts +276 -0
- package/.allhands/harness/src/lib/knowledge-worker.ts +72 -0
- package/.allhands/harness/src/lib/knowledge.ts +810 -0
- package/.allhands/harness/src/lib/llm.ts +255 -0
- package/.allhands/harness/src/lib/mcp-client.ts +432 -0
- package/.allhands/harness/src/lib/mcp-daemon.ts +486 -0
- package/.allhands/harness/src/lib/mcp-runtime.ts +418 -0
- package/.allhands/harness/src/lib/notification.ts +115 -0
- package/.allhands/harness/src/lib/opencode/index.ts +70 -0
- package/.allhands/harness/src/lib/opencode/profiles.ts +300 -0
- package/.allhands/harness/src/lib/opencode/prompts/codesearch.md +98 -0
- package/.allhands/harness/src/lib/opencode/prompts/knowledge-aggregator.md +67 -0
- package/.allhands/harness/src/lib/opencode/runner.ts +281 -0
- package/.allhands/harness/src/lib/oracle.ts +926 -0
- package/.allhands/harness/src/lib/planning-utils.ts +150 -0
- package/.allhands/harness/src/lib/planning.ts +605 -0
- package/.allhands/harness/src/lib/pr-review.ts +225 -0
- package/.allhands/harness/src/lib/prompts.ts +522 -0
- package/.allhands/harness/src/lib/schema.ts +418 -0
- package/.allhands/harness/src/lib/schemas/agent-profile.ts +141 -0
- package/.allhands/harness/src/lib/schemas/template-vars.ts +138 -0
- package/.allhands/harness/src/lib/session.ts +164 -0
- package/.allhands/harness/src/lib/specs.ts +348 -0
- package/.allhands/harness/src/lib/tldr.ts +829 -0
- package/.allhands/harness/src/lib/tmux.ts +1051 -0
- package/.allhands/harness/src/lib/trace-store.ts +714 -0
- package/.allhands/harness/src/mcp/__tests__/index.test.ts +46 -0
- package/.allhands/harness/src/mcp/_template.ts +47 -0
- package/.allhands/harness/src/mcp/filesystem.ts +33 -0
- package/.allhands/harness/src/mcp/index.ts +69 -0
- package/.allhands/harness/src/mcp/playwright.ts +34 -0
- package/.allhands/harness/src/mcp/xcodebuild.ts +29 -0
- package/.allhands/harness/src/schemas/docs.schema.json +44 -0
- package/.allhands/harness/src/schemas/settings.schema.json +214 -0
- package/.allhands/harness/src/tui/actions.ts +227 -0
- package/.allhands/harness/src/tui/file-viewer-modal.ts +270 -0
- package/.allhands/harness/src/tui/index.ts +1574 -0
- package/.allhands/harness/src/tui/modal.ts +232 -0
- package/.allhands/harness/src/tui/prompts-pane.ts +186 -0
- package/.allhands/harness/src/tui/status-pane.ts +434 -0
- package/.allhands/harness/tsconfig.json +22 -0
- package/.allhands/harness/vitest.config.ts +13 -0
- package/.allhands/pillars.md +33 -0
- package/.allhands/principles.md +88 -0
- package/.allhands/schemas/alignment.yaml +51 -0
- package/.allhands/schemas/documentation.yaml +10 -0
- package/.allhands/schemas/prompt.yaml +92 -0
- package/.allhands/schemas/skill.yaml +34 -0
- package/.allhands/schemas/solution.yaml +131 -0
- package/.allhands/schemas/spec.yaml +67 -0
- package/.allhands/schemas/validation-suite.yaml +49 -0
- package/.allhands/schemas/workflow.yaml +51 -0
- package/.allhands/settings.json +57 -0
- package/.allhands/skills/claude-code-patterns/SKILL.md +60 -0
- package/.allhands/skills/claude-code-patterns/docs/context-hygiene.md +19 -0
- package/.allhands/skills/harness-maintenance/SKILL.md +449 -0
- package/.allhands/skills/harness-maintenance/references/core-architecture.md +187 -0
- package/.allhands/skills/harness-maintenance/references/harness-skills.md +87 -0
- package/.allhands/skills/harness-maintenance/references/knowledge-compounding.md +78 -0
- package/.allhands/skills/harness-maintenance/references/tools-commands-mcp-hooks.md +115 -0
- package/.allhands/skills/harness-maintenance/references/validation-tooling.md +77 -0
- package/.allhands/skills/harness-maintenance/references/writing-flows.md +84 -0
- package/.allhands/validation/browser-automation.md +109 -0
- package/.allhands/validation/xcode-automation.md +195 -0
- package/.allhands/workflows/documentation.md +86 -0
- package/.allhands/workflows/investigation.md +81 -0
- package/.allhands/workflows/milestone.md +91 -0
- package/.allhands/workflows/optimization.md +85 -0
- package/.allhands/workflows/refactor.md +99 -0
- package/.allhands/workflows/triage.md +81 -0
- package/.claude/README.md +1 -0
- package/.claude/agents/explorer.md +10 -0
- package/.claude/agents/researcher.md +11 -0
- package/.claude/agents/task-runner.md +8 -0
- package/.claude/settings.json +231 -0
- package/.env.ai.example +7 -0
- package/.github/workflows/npm-publish.yml +69 -0
- package/.internal.json +45 -0
- package/.tldr/config.json +11 -0
- package/.tldrignore +90 -0
- package/CLAUDE.md +6 -0
- package/README.md +98 -0
- package/bin/sync-cli.js +7552 -0
- package/concerns.md +7 -0
- package/docs/README.md +41 -0
- package/docs/agents/README.md +24 -0
- package/docs/agents/agent-configuration-system.md +86 -0
- package/docs/agents/execution-agents.md +50 -0
- package/docs/agents/knowledge-agents.md +61 -0
- package/docs/agents/orchestration-agent.md +57 -0
- package/docs/agents/planning-agents.md +84 -0
- package/docs/agents/quality-review-agents.md +67 -0
- package/docs/agents/workflow-agent-orchestration.md +69 -0
- package/docs/flows/README.md +44 -0
- package/docs/flows/compounding.md +126 -0
- package/docs/flows/coordination.md +72 -0
- package/docs/flows/core-harness-integration.md +63 -0
- package/docs/flows/documentation-orchestration.md +98 -0
- package/docs/flows/e2e-test-plan-building.md +83 -0
- package/docs/flows/emergent-refinement.md +104 -0
- package/docs/flows/flow-authoring-and-mcp-tools.md +89 -0
- package/docs/flows/judge-reviewing.md +112 -0
- package/docs/flows/plan-deepening-and-research.md +107 -0
- package/docs/flows/plan-review-jury.md +114 -0
- package/docs/flows/pr-reviewing.md +54 -0
- package/docs/flows/prompt-task-execution.md +119 -0
- package/docs/flows/spec-planning.md +162 -0
- package/docs/flows/type-specific-scoping-flows.md +49 -0
- package/docs/flows/validation-and-skills-integration.md +145 -0
- package/docs/flows/wip/wip-flows.md +102 -0
- package/docs/harness/README.md +23 -0
- package/docs/harness/agent-profiles.md +84 -0
- package/docs/harness/cli/README.md +24 -0
- package/docs/harness/cli/cli-entry-and-command-discovery.md +91 -0
- package/docs/harness/cli/docs-command.md +87 -0
- package/docs/harness/cli/knowledge-command.md +91 -0
- package/docs/harness/cli/minor-cli-commands.md +65 -0
- package/docs/harness/cli/oracle-command.md +113 -0
- package/docs/harness/cli/planning-command.md +95 -0
- package/docs/harness/cli/schema-and-validation-commands.md +154 -0
- package/docs/harness/cli/search-commands.md +97 -0
- package/docs/harness/cli/spawn-command.md +136 -0
- package/docs/harness/cli/specs-command.md +102 -0
- package/docs/harness/cli/tools-command.md +122 -0
- package/docs/harness/cli/trace-command.md +122 -0
- package/docs/harness/cli-daemon.md +92 -0
- package/docs/harness/event-loop.md +184 -0
- package/docs/harness/hooks/README.md +15 -0
- package/docs/harness/hooks/context-hooks.md +96 -0
- package/docs/harness/hooks/lifecycle-and-observability-hooks.md +135 -0
- package/docs/harness/hooks/validation-hooks.md +97 -0
- package/docs/harness/test-harness.md +149 -0
- package/docs/harness/tui.md +176 -0
- package/docs/memories.md +20 -0
- package/docs/solutions/agentic-issues/premature-agent-deletion-tui-action-dependency-20260130.md +49 -0
- package/docs/solutions/agentic-issues/ref-anchor-scope-mismatch-skill-references-20260131.md +55 -0
- package/docs/solutions/agentic-issues/tautological-tests-routing-20260131.md +52 -0
- package/docs/solutions/integration_issue/blocktool-output-format-mismatch-hook-runner-20260130.md +52 -0
- package/docs/solutions/integration_issue/dual-validation-path-divergence-schema-20260130.md +66 -0
- package/docs/solutions/security-issues/unsanitized-domain-path-join-20260131.md +52 -0
- package/docs/solutions/test-failures/event-loop-mock-ordering-checkAgentWindows-20260130.md +63 -0
- package/docs/sync-cli/README.md +19 -0
- package/docs/sync-cli/cli-entrypoint-and-commands.md +39 -0
- package/docs/sync-cli/commands/README.md +11 -0
- package/docs/sync-cli/commands/pull-manifest-command.md +36 -0
- package/docs/sync-cli/commands/push-command.md +84 -0
- package/docs/sync-cli/commands/sync-command.md +71 -0
- package/docs/sync-cli/systems/README.md +14 -0
- package/docs/sync-cli/systems/git-and-github-integration.md +49 -0
- package/docs/sync-cli/systems/interactive-ui.md +43 -0
- package/docs/sync-cli/systems/manifest-and-distribution.md +51 -0
- package/docs/sync-cli/systems/path-resolution.md +42 -0
- package/package.json +46 -0
- package/scripts/install-shim.sh +40 -0
- package/scripts/pre-pack.sh +25 -0
- package/specs/harness-maintenance-skill.spec.md +138 -0
- package/specs/roadmap/git-spec-lifecycle-management.spec.md +113 -0
- package/specs/sync-init-flag.spec.md +117 -0
- package/specs/unified-workflow-orchestration.spec.md +250 -0
- package/specs/validation-tooling-practice.spec.md +98 -0
- package/specs/workflow-domain-configuration.spec.md +265 -0
- package/src/commands/pull-manifest.ts +31 -0
- package/src/commands/push.ts +344 -0
- package/src/commands/sync.ts +289 -0
- package/src/lib/constants.ts +10 -0
- package/src/lib/dotfiles.ts +36 -0
- package/src/lib/fs-utils.ts +18 -0
- package/src/lib/gh.ts +40 -0
- package/src/lib/git.ts +63 -0
- package/src/lib/gitignore.ts +167 -0
- package/src/lib/manifest.ts +121 -0
- package/src/lib/marker-sync.ts +39 -0
- package/src/lib/paths.ts +38 -0
- package/src/lib/target-lines.ts +66 -0
- package/src/lib/ui.ts +78 -0
- package/src/sync-cli.ts +120 -0
- package/target-lines.json +23 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Actions Pane - Left sidebar with agent spawners, toggles, and controls
|
|
3
|
+
*
|
|
4
|
+
* All actions are always visible — agents exit early if nothing to do.
|
|
5
|
+
*
|
|
6
|
+
* Layout (vertical):
|
|
7
|
+
* [1] Coordinator [2] New Initiative [3] Planner
|
|
8
|
+
* [4] Review Jury [5] E2E Test Plan [6] PR Action
|
|
9
|
+
* [7] Address PR Review [8] Compound [9] Complete
|
|
10
|
+
* [0] Switch Workspace [-] Custom Flow
|
|
11
|
+
* ━━ Toggles ━━
|
|
12
|
+
* [O] Loop [P] Parallel
|
|
13
|
+
* ━━ Controls ━━
|
|
14
|
+
* [V] View Logs [C] Clear Logs [R] Refresh
|
|
15
|
+
* [Q] Quit
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import blessed from 'blessed';
|
|
19
|
+
import type { PRActionState } from './index.js';
|
|
20
|
+
|
|
21
|
+
export interface ActionItem {
|
|
22
|
+
id: string;
|
|
23
|
+
label: string;
|
|
24
|
+
key?: string;
|
|
25
|
+
type: 'action' | 'toggle' | 'separator';
|
|
26
|
+
highlight?: boolean;
|
|
27
|
+
checked?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ToggleState {
|
|
31
|
+
loopEnabled: boolean;
|
|
32
|
+
parallelEnabled: boolean;
|
|
33
|
+
prActionState: PRActionState;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const PANE_WIDTH = 24;
|
|
37
|
+
const HEADER_HEIGHT = 3;
|
|
38
|
+
|
|
39
|
+
export function createActionsPane(
|
|
40
|
+
screen: blessed.Widgets.Screen,
|
|
41
|
+
toggleState: ToggleState,
|
|
42
|
+
selectedIndex?: number
|
|
43
|
+
): blessed.Widgets.BoxElement {
|
|
44
|
+
// Create outer container (non-scrollable, holds border and help text)
|
|
45
|
+
const container = blessed.box({
|
|
46
|
+
parent: screen,
|
|
47
|
+
top: HEADER_HEIGHT,
|
|
48
|
+
left: 0,
|
|
49
|
+
width: PANE_WIDTH,
|
|
50
|
+
height: `100%-${HEADER_HEIGHT}`,
|
|
51
|
+
border: {
|
|
52
|
+
type: 'line',
|
|
53
|
+
},
|
|
54
|
+
label: ' Actions ',
|
|
55
|
+
tags: true,
|
|
56
|
+
style: {
|
|
57
|
+
border: {
|
|
58
|
+
fg: '#4A34C5',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Calculate content area height (container minus borders minus help text lines)
|
|
64
|
+
const containerHeight = typeof container.height === 'number' ? container.height : (screen.height as number) - HEADER_HEIGHT;
|
|
65
|
+
const contentHeight = containerHeight - 4; // 2 for borders, 2 for help text lines
|
|
66
|
+
|
|
67
|
+
// Create scrollable content area inside the container
|
|
68
|
+
const scrollArea = blessed.box({
|
|
69
|
+
parent: container,
|
|
70
|
+
top: 0,
|
|
71
|
+
left: 0,
|
|
72
|
+
width: '100%-2',
|
|
73
|
+
height: contentHeight,
|
|
74
|
+
tags: true,
|
|
75
|
+
scrollable: true,
|
|
76
|
+
alwaysScroll: true,
|
|
77
|
+
scrollbar: {
|
|
78
|
+
ch: '┃',
|
|
79
|
+
track: {
|
|
80
|
+
bg: 'black',
|
|
81
|
+
},
|
|
82
|
+
style: {
|
|
83
|
+
fg: '#4A34C5',
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const items = buildActionItems(toggleState);
|
|
89
|
+
const { content, selectedLineNumber } = formatActionsContent(items, selectedIndex);
|
|
90
|
+
|
|
91
|
+
scrollArea.setContent(content);
|
|
92
|
+
|
|
93
|
+
// Scroll to ensure selected item is visible
|
|
94
|
+
if (selectedLineNumber !== undefined && selectedLineNumber >= 0) {
|
|
95
|
+
const visibleHeight = contentHeight;
|
|
96
|
+
|
|
97
|
+
// Only scroll if selected line would be outside visible area
|
|
98
|
+
if (selectedLineNumber >= visibleHeight) {
|
|
99
|
+
// Scroll to put selected line in the middle of visible area when possible
|
|
100
|
+
const scrollOffset = Math.max(0, selectedLineNumber - Math.floor(visibleHeight / 2));
|
|
101
|
+
scrollArea.scrollTo(scrollOffset);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Add help text at bottom of container (fixed position, outside scroll area)
|
|
106
|
+
blessed.text({
|
|
107
|
+
parent: container,
|
|
108
|
+
bottom: 1,
|
|
109
|
+
left: 1,
|
|
110
|
+
content: '{#5c6370-fg}Tab: Switch Pane{/#5c6370-fg}',
|
|
111
|
+
tags: true,
|
|
112
|
+
});
|
|
113
|
+
blessed.text({
|
|
114
|
+
parent: container,
|
|
115
|
+
bottom: 0,
|
|
116
|
+
left: 1,
|
|
117
|
+
content: '{#5c6370-fg}j/k: Navigate{/#5c6370-fg}',
|
|
118
|
+
tags: true,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return container;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
interface ActionsContentResult {
|
|
125
|
+
content: string;
|
|
126
|
+
selectedLineNumber?: number;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function formatActionsContent(items: ActionItem[], selectedIndex?: number): ActionsContentResult {
|
|
130
|
+
const lines: string[] = [];
|
|
131
|
+
let selectableIndex = 0;
|
|
132
|
+
let selectedLineNumber: number | undefined;
|
|
133
|
+
|
|
134
|
+
for (const item of items) {
|
|
135
|
+
if (item.type === 'separator') {
|
|
136
|
+
lines.push(`{#6366f1-fg}${item.label}{/#6366f1-fg}`);
|
|
137
|
+
} else {
|
|
138
|
+
const isSelected = selectedIndex === selectableIndex;
|
|
139
|
+
|
|
140
|
+
if (isSelected) {
|
|
141
|
+
selectedLineNumber = lines.length;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const content = formatItemContent(item, isSelected);
|
|
145
|
+
lines.push(content);
|
|
146
|
+
selectableIndex++;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return { content: lines.join('\n'), selectedLineNumber };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function buildActionItems(toggleState: ToggleState): ActionItem[] {
|
|
154
|
+
const prLabel = getPRActionLabel(toggleState.prActionState);
|
|
155
|
+
|
|
156
|
+
return [
|
|
157
|
+
// Agent spawners — all always visible
|
|
158
|
+
{ id: 'coordinator', label: 'Coordinator', key: '1', type: 'action' },
|
|
159
|
+
{ id: 'new-initiative', label: 'New Initiative', key: '2', type: 'action' },
|
|
160
|
+
{ id: 'planner', label: 'Planner', key: '3', type: 'action' },
|
|
161
|
+
{ id: 'review-jury', label: 'Review Jury', key: '4', type: 'action' },
|
|
162
|
+
{ id: 'e2e-test-planner', label: 'E2E Test Plan', key: '5', type: 'action' },
|
|
163
|
+
{ id: 'pr-action', label: prLabel, key: '6', type: 'action' },
|
|
164
|
+
{ id: 'review-pr', label: 'Address PR Review', key: '7', type: 'action' },
|
|
165
|
+
{ id: 'compound', label: 'Compound', key: '8', type: 'action' },
|
|
166
|
+
{ id: 'mark-completed', label: 'Complete', key: '9', type: 'action' },
|
|
167
|
+
{ id: 'switch-spec', label: 'Switch Workspace', key: '0', type: 'action' },
|
|
168
|
+
{ id: 'custom-flow', label: 'Custom Flow', key: '-', type: 'action' },
|
|
169
|
+
{ id: 'initiative-steering', label: 'Steer Initiative', key: '=', type: 'action' },
|
|
170
|
+
// Spacing before toggles
|
|
171
|
+
{ id: 'spacer-1', label: '', type: 'separator' },
|
|
172
|
+
{ id: 'separator-toggles', label: '━━ Toggles ━━', type: 'separator' },
|
|
173
|
+
{ id: 'toggle-loop', label: 'Loop', key: 'O', type: 'toggle', checked: toggleState.loopEnabled },
|
|
174
|
+
{ id: 'toggle-parallel', label: 'Parallel', key: 'P', type: 'toggle', checked: toggleState.parallelEnabled },
|
|
175
|
+
// Spacing before controls
|
|
176
|
+
{ id: 'spacer-2', label: '', type: 'separator' },
|
|
177
|
+
{ id: 'separator-bottom', label: '━━ Controls ━━', type: 'separator' },
|
|
178
|
+
{ id: 'view-logs', label: 'View Logs', key: 'V', type: 'action' },
|
|
179
|
+
{ id: 'clear-logs', label: 'Clear Logs', key: 'C', type: 'action' },
|
|
180
|
+
{ id: 'refresh', label: 'Refresh', key: 'R', type: 'action' },
|
|
181
|
+
{ id: 'quit', label: 'Quit', key: 'Q', type: 'action' },
|
|
182
|
+
];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function getPRActionLabel(state: PRActionState): string {
|
|
186
|
+
switch (state) {
|
|
187
|
+
case 'create-pr':
|
|
188
|
+
return 'Create PR';
|
|
189
|
+
case 'awaiting-review':
|
|
190
|
+
return 'Awaiting Review...';
|
|
191
|
+
case 'rerun-pr-review':
|
|
192
|
+
return 'Rerun PR Review';
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function formatItemContent(item: ActionItem, isSelected: boolean): string {
|
|
197
|
+
const prefix = item.key ? `{#818cf8-fg}[${item.key}]{/#818cf8-fg} ` : ' ';
|
|
198
|
+
let label = item.label;
|
|
199
|
+
|
|
200
|
+
// Toggle checkbox
|
|
201
|
+
if (item.type === 'toggle') {
|
|
202
|
+
const checkbox = item.checked ? '{#10b981-fg}[x]{/#10b981-fg}' : '{#5c6370-fg}[ ]{/#5c6370-fg}';
|
|
203
|
+
label = `${checkbox} ${label}`;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Apply styling
|
|
207
|
+
let style = '';
|
|
208
|
+
let endStyle = '';
|
|
209
|
+
|
|
210
|
+
if (isSelected) {
|
|
211
|
+
style = '{#a78bfa-fg}{bold}▸ ';
|
|
212
|
+
endStyle = '{/bold}{/#a78bfa-fg}';
|
|
213
|
+
// For selected items, use plain prefix without colors
|
|
214
|
+
return `${style}${item.key ? `[${item.key}] ` : ''}${label}${endStyle}`;
|
|
215
|
+
} else if (item.highlight) {
|
|
216
|
+
style = '{#f59e0b-fg}';
|
|
217
|
+
endStyle = '{/#f59e0b-fg}';
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return `${style}${prefix}${label}${endStyle}`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function getSelectableItems(toggleState: ToggleState): ActionItem[] {
|
|
224
|
+
return buildActionItems(toggleState).filter(
|
|
225
|
+
(item) => item.type !== 'separator'
|
|
226
|
+
);
|
|
227
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Viewer Modal - Scrollable modal for viewing markdown files
|
|
3
|
+
*
|
|
4
|
+
* Used for viewing:
|
|
5
|
+
* - Spec files
|
|
6
|
+
* - Alignment documents
|
|
7
|
+
* - E2E test plans
|
|
8
|
+
* - Prompt files
|
|
9
|
+
*
|
|
10
|
+
* Navigation:
|
|
11
|
+
* - j/k: Scroll one line up/down
|
|
12
|
+
* - u/d: Page up/down
|
|
13
|
+
* - g: Jump to top
|
|
14
|
+
* - G: Jump to bottom
|
|
15
|
+
* - Esc: Close modal
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import blessed from 'blessed';
|
|
19
|
+
import { readFileSync, existsSync } from 'fs';
|
|
20
|
+
import { join } from 'path';
|
|
21
|
+
import { parse as parseYaml } from 'yaml';
|
|
22
|
+
|
|
23
|
+
export interface FileViewerOptions {
|
|
24
|
+
title: string;
|
|
25
|
+
filePath: string;
|
|
26
|
+
onClose: () => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface FileViewer {
|
|
30
|
+
box: blessed.Widgets.BoxElement;
|
|
31
|
+
destroy: () => void;
|
|
32
|
+
scrollUp: (lines?: number) => void;
|
|
33
|
+
scrollDown: (lines?: number) => void;
|
|
34
|
+
scrollToTop: () => void;
|
|
35
|
+
scrollToBottom: () => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function createFileViewer(
|
|
39
|
+
screen: blessed.Widgets.Screen,
|
|
40
|
+
options: FileViewerOptions
|
|
41
|
+
): FileViewer | null {
|
|
42
|
+
const { title, filePath, onClose } = options;
|
|
43
|
+
|
|
44
|
+
// Check if file exists
|
|
45
|
+
if (!existsSync(filePath)) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Read file content
|
|
50
|
+
let content: string;
|
|
51
|
+
try {
|
|
52
|
+
content = readFileSync(filePath, 'utf-8');
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Calculate modal size (80% of screen)
|
|
58
|
+
const width = Math.floor((screen.width as number) * 0.8);
|
|
59
|
+
const height = Math.floor((screen.height as number) * 0.8);
|
|
60
|
+
|
|
61
|
+
// Create outer container (non-scrollable, holds border and help text)
|
|
62
|
+
const container = blessed.box({
|
|
63
|
+
parent: screen,
|
|
64
|
+
top: 'center',
|
|
65
|
+
left: 'center',
|
|
66
|
+
width,
|
|
67
|
+
height,
|
|
68
|
+
border: 'line',
|
|
69
|
+
label: ` ${title} `,
|
|
70
|
+
tags: true,
|
|
71
|
+
style: {
|
|
72
|
+
border: {
|
|
73
|
+
fg: '#c4b5fd',
|
|
74
|
+
bold: true,
|
|
75
|
+
},
|
|
76
|
+
fg: '#e0e7ff',
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Calculate content area height (container minus borders minus help text)
|
|
81
|
+
// Container inner area = height - 2 (borders), minus 1 for help text = height - 3
|
|
82
|
+
const contentHeight = height - 4; // Be more conservative to ensure we don't clip
|
|
83
|
+
|
|
84
|
+
// Create scrollable content area inside the container
|
|
85
|
+
const scrollArea = blessed.box({
|
|
86
|
+
parent: container,
|
|
87
|
+
top: 0,
|
|
88
|
+
left: 0,
|
|
89
|
+
width: '100%-2', // Fill width minus scrollbar space
|
|
90
|
+
height: contentHeight,
|
|
91
|
+
tags: true,
|
|
92
|
+
scrollable: true,
|
|
93
|
+
alwaysScroll: true,
|
|
94
|
+
keys: false,
|
|
95
|
+
vi: false,
|
|
96
|
+
scrollbar: {
|
|
97
|
+
ch: '┃',
|
|
98
|
+
track: {
|
|
99
|
+
bg: 'black',
|
|
100
|
+
},
|
|
101
|
+
style: {
|
|
102
|
+
fg: '#4A34C5',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
style: {
|
|
106
|
+
fg: '#e0e7ff',
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Set content
|
|
111
|
+
scrollArea.setContent(content);
|
|
112
|
+
|
|
113
|
+
// Focus the container for key events
|
|
114
|
+
container.focus();
|
|
115
|
+
|
|
116
|
+
// Track scroll position
|
|
117
|
+
let scrollPosition = 0;
|
|
118
|
+
const contentLines = content.split('\n').length;
|
|
119
|
+
const visibleLines = contentHeight;
|
|
120
|
+
// Allow scrolling to show the last line at the top of the view
|
|
121
|
+
// This ensures we can always reach the true bottom of the file
|
|
122
|
+
const maxScroll = Math.max(0, contentLines);
|
|
123
|
+
|
|
124
|
+
function updateScroll(): void {
|
|
125
|
+
scrollArea.scrollTo(scrollPosition);
|
|
126
|
+
screen.render();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function scrollUp(lines: number = 1): void {
|
|
130
|
+
scrollPosition = Math.max(0, scrollPosition - lines);
|
|
131
|
+
updateScroll();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function scrollDown(lines: number = 1): void {
|
|
135
|
+
scrollPosition = Math.min(maxScroll, scrollPosition + lines);
|
|
136
|
+
updateScroll();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function scrollToTop(): void {
|
|
140
|
+
scrollPosition = 0;
|
|
141
|
+
updateScroll();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function scrollToBottom(): void {
|
|
145
|
+
scrollPosition = maxScroll;
|
|
146
|
+
updateScroll();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Set up key bindings on container
|
|
150
|
+
container.key(['j'], () => scrollDown(1));
|
|
151
|
+
container.key(['k'], () => scrollUp(1));
|
|
152
|
+
container.key(['u'], () => scrollUp(Math.floor(visibleLines / 2)));
|
|
153
|
+
container.key(['d'], () => scrollDown(Math.floor(visibleLines / 2)));
|
|
154
|
+
container.key(['g'], () => scrollToTop());
|
|
155
|
+
container.key(['S-g'], () => scrollToBottom()); // Shift+G
|
|
156
|
+
container.key(['escape'], () => onClose());
|
|
157
|
+
|
|
158
|
+
// Add help text at bottom of container (fixed position, outside scroll area)
|
|
159
|
+
blessed.text({
|
|
160
|
+
parent: container,
|
|
161
|
+
bottom: 0,
|
|
162
|
+
left: 1,
|
|
163
|
+
content: '{#5c6370-fg}j/k:scroll u/d:page g/G:top/bottom Esc:close{/#5c6370-fg}',
|
|
164
|
+
tags: true,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Initial render
|
|
168
|
+
screen.render();
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
box: container,
|
|
172
|
+
destroy: () => container.destroy(),
|
|
173
|
+
scrollUp,
|
|
174
|
+
scrollDown,
|
|
175
|
+
scrollToTop,
|
|
176
|
+
scrollToBottom,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Check if a planning file exists for the given spec and file type
|
|
182
|
+
* Tries multiple folder name formats to handle branch name variations
|
|
183
|
+
*/
|
|
184
|
+
export function getPlanningFilePath(
|
|
185
|
+
cwd: string,
|
|
186
|
+
spec: string,
|
|
187
|
+
fileType: 'alignment' | 'e2e_test_plan'
|
|
188
|
+
): string | null {
|
|
189
|
+
// Try multiple filename variations (underscore vs hyphen)
|
|
190
|
+
const filenames = fileType === 'alignment'
|
|
191
|
+
? ['alignment.md']
|
|
192
|
+
: ['e2e-test-plan.md', 'e2e_test_plan.md'];
|
|
193
|
+
|
|
194
|
+
// Try multiple folder name variations to handle branch naming differences
|
|
195
|
+
// e.g., "feature/core-taskflow" might be stored as "feature-core-taskflow"
|
|
196
|
+
const folderVariations = [
|
|
197
|
+
spec, // Original: feature/core-taskflow
|
|
198
|
+
spec.replace(/\//g, '-'), // Slashes to hyphens: feature-core-taskflow
|
|
199
|
+
spec.replace(/[/\\]/g, '-'), // All path separators to hyphens
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
for (const folderName of folderVariations) {
|
|
203
|
+
for (const filename of filenames) {
|
|
204
|
+
const filePath = join(cwd, '.planning', folderName, filename);
|
|
205
|
+
if (existsSync(filePath)) {
|
|
206
|
+
return filePath;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Get the spec file path for a spec
|
|
216
|
+
* First tries to read from status.yaml, then falls back to common locations
|
|
217
|
+
*/
|
|
218
|
+
export function getSpecFilePath(cwd: string, specId: string): string | null {
|
|
219
|
+
// Try variations of the spec ID (slashes to hyphens, etc.)
|
|
220
|
+
const specIdVariations = [
|
|
221
|
+
specId,
|
|
222
|
+
specId.replace(/\//g, '-'),
|
|
223
|
+
specId.replace(/[/\\]/g, '-'),
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
// First, try to read the spec path from status.yaml in the planning directory
|
|
227
|
+
for (const id of specIdVariations) {
|
|
228
|
+
const statusPath = join(cwd, '.planning', id, 'status.yaml');
|
|
229
|
+
if (existsSync(statusPath)) {
|
|
230
|
+
try {
|
|
231
|
+
const content = readFileSync(statusPath, 'utf-8');
|
|
232
|
+
const status = parseYaml(content) as { spec?: string };
|
|
233
|
+
if (status?.spec) {
|
|
234
|
+
// The spec path in status.yaml might be relative or absolute
|
|
235
|
+
const specPath = status.spec.startsWith('/')
|
|
236
|
+
? status.spec
|
|
237
|
+
: join(cwd, status.spec);
|
|
238
|
+
if (existsSync(specPath)) {
|
|
239
|
+
return specPath;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} catch {
|
|
243
|
+
// Ignore parse errors, continue to fallback
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Fallback: try common spec file locations
|
|
249
|
+
const locations: string[] = [];
|
|
250
|
+
|
|
251
|
+
for (const id of specIdVariations) {
|
|
252
|
+
// Check specs/ folder
|
|
253
|
+
locations.push(join(cwd, 'specs', `${id}.spec.md`));
|
|
254
|
+
locations.push(join(cwd, 'specs', `${id}.md`));
|
|
255
|
+
// Check .specs/ folder
|
|
256
|
+
locations.push(join(cwd, '.specs', `${id}.spec.md`));
|
|
257
|
+
locations.push(join(cwd, '.specs', `${id}.md`));
|
|
258
|
+
// Check .planning/ folder (spec.md inside the planning dir)
|
|
259
|
+
locations.push(join(cwd, '.planning', id, 'spec.md'));
|
|
260
|
+
locations.push(join(cwd, '.planning', id, `${id}.spec.md`));
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
for (const filePath of locations) {
|
|
264
|
+
if (existsSync(filePath)) {
|
|
265
|
+
return filePath;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return null;
|
|
270
|
+
}
|