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.
Files changed (305) hide show
  1. package/.allhands/README.md +75 -0
  2. package/.allhands/agents/compounder.yaml +15 -0
  3. package/.allhands/agents/coordinator.yaml +17 -0
  4. package/.allhands/agents/documentor.yaml +15 -0
  5. package/.allhands/agents/e2e-test-planner.yaml +17 -0
  6. package/.allhands/agents/emergent.yaml +22 -0
  7. package/.allhands/agents/executor.yaml +14 -0
  8. package/.allhands/agents/ideation.yaml +11 -0
  9. package/.allhands/agents/initiative-steering.yaml +19 -0
  10. package/.allhands/agents/judge.yaml +13 -0
  11. package/.allhands/agents/planner.yaml +19 -0
  12. package/.allhands/agents/pr-reviewer.yaml +15 -0
  13. package/.allhands/docs.json +5 -0
  14. package/.allhands/docs.local.json +26 -0
  15. package/.allhands/flows/COMPOUNDING.md +203 -0
  16. package/.allhands/flows/COORDINATION.md +89 -0
  17. package/.allhands/flows/CORE.md +87 -0
  18. package/.allhands/flows/DOCUMENTATION.md +218 -0
  19. package/.allhands/flows/E2E_TEST_PLAN_BUILDING.md +140 -0
  20. package/.allhands/flows/EMERGENT_PLANNING.md +57 -0
  21. package/.allhands/flows/IDEATION_SCOPING.md +154 -0
  22. package/.allhands/flows/INITIATIVE_STEERING.md +110 -0
  23. package/.allhands/flows/JUDGE_REVIEWING.md +79 -0
  24. package/.allhands/flows/PROMPT_TASK_EXECUTION.md +68 -0
  25. package/.allhands/flows/PR_REVIEWING.md +43 -0
  26. package/.allhands/flows/SPEC_PLANNING.md +216 -0
  27. package/.allhands/flows/harness/WRITING_HARNESS_FLOWS.md +27 -0
  28. package/.allhands/flows/harness/WRITING_HARNESS_KNOWLEDGE.md +27 -0
  29. package/.allhands/flows/harness/WRITING_HARNESS_ORCHESTRATION.md +27 -0
  30. package/.allhands/flows/harness/WRITING_HARNESS_SKILLS.md +27 -0
  31. package/.allhands/flows/harness/WRITING_HARNESS_TOOLS.md +27 -0
  32. package/.allhands/flows/harness/WRITING_HARNESS_VALIDATION_TOOLING.md +27 -0
  33. package/.allhands/flows/shared/CODEBASE_UNDERSTANDING.md +72 -0
  34. package/.allhands/flows/shared/CREATE_HARNESS_SPEC.md +48 -0
  35. package/.allhands/flows/shared/CREATE_SPEC.md +41 -0
  36. package/.allhands/flows/shared/CREATE_VALIDATION_TOOLING_SPEC.md +70 -0
  37. package/.allhands/flows/shared/DOCUMENTATION_DISCOVERY.md +123 -0
  38. package/.allhands/flows/shared/DOCUMENTATION_WRITER.md +101 -0
  39. package/.allhands/flows/shared/EMERGENT_REFINEMENT_ANALYSIS.md +76 -0
  40. package/.allhands/flows/shared/EXTERNAL_TECH_GUIDANCE.md +97 -0
  41. package/.allhands/flows/shared/IDEATION_CODEBASE_GROUNDING.md +49 -0
  42. package/.allhands/flows/shared/PLAN_DEEPENING.md +152 -0
  43. package/.allhands/flows/shared/PROMPT_TASKS_CURATION.md +113 -0
  44. package/.allhands/flows/shared/PROMPT_VALIDATION_REVIEW.MD +99 -0
  45. package/.allhands/flows/shared/QUICK_PREMORTEM.md +70 -0
  46. package/.allhands/flows/shared/RESEARCH_GUIDANCE.md +38 -0
  47. package/.allhands/flows/shared/REVIEW_OPTIONS_BREAKDOWN.md +68 -0
  48. package/.allhands/flows/shared/SKILL_EXTRACTION.md +84 -0
  49. package/.allhands/flows/shared/SPEC_FLOW_ANALYSIS.md +119 -0
  50. package/.allhands/flows/shared/TDD_WORKFLOW.md +109 -0
  51. package/.allhands/flows/shared/UTILIZE_VALIDATION_TOOLING.md +84 -0
  52. package/.allhands/flows/shared/WRITING_HARNESS_FLOWS.md +11 -0
  53. package/.allhands/flows/shared/WRITING_HARNESS_MCP_TOOLS.md +84 -0
  54. package/.allhands/flows/shared/jury/ARCHITECTURE_REVIEW.md +91 -0
  55. package/.allhands/flows/shared/jury/BEST_PRACTICES_REVIEW.md +80 -0
  56. package/.allhands/flows/shared/jury/CLAIM_VERIFICATION_REVIEW.md +101 -0
  57. package/.allhands/flows/shared/jury/EXPECTATIONS_FIT_REVIEW.md +78 -0
  58. package/.allhands/flows/shared/jury/MAINTAINABILITY_REVIEW.md +110 -0
  59. package/.allhands/flows/shared/jury/PROMPTS_EXPECTATIONS_FIT.md +74 -0
  60. package/.allhands/flows/shared/jury/PROMPTS_FLOW_ANALYSIS.md +92 -0
  61. package/.allhands/flows/shared/jury/PROMPTS_YAGNI.md +78 -0
  62. package/.allhands/flows/shared/jury/PROMPT_PREMORTEM.md +125 -0
  63. package/.allhands/flows/shared/jury/SECURITY_REVIEW.md +86 -0
  64. package/.allhands/flows/shared/jury/YAGNI_REVIEW.md +82 -0
  65. package/.allhands/flows/wip/DEBUG_INVESTIGATION.md +162 -0
  66. package/.allhands/flows/wip/MEMORY_RECALL.md +62 -0
  67. package/.allhands/harness/ah +131 -0
  68. package/.allhands/harness/package-lock.json +5292 -0
  69. package/.allhands/harness/package.json +52 -0
  70. package/.allhands/harness/src/__tests__/e2e/commands.test.ts +307 -0
  71. package/.allhands/harness/src/__tests__/e2e/event-loop.test.ts +539 -0
  72. package/.allhands/harness/src/__tests__/e2e/hooks.test.ts +427 -0
  73. package/.allhands/harness/src/__tests__/e2e/new-initiative-routing.test.ts +137 -0
  74. package/.allhands/harness/src/__tests__/e2e/run-e2e.ts +109 -0
  75. package/.allhands/harness/src/__tests__/e2e/specs-type.test.ts +210 -0
  76. package/.allhands/harness/src/__tests__/e2e/validation-hooks.test.ts +669 -0
  77. package/.allhands/harness/src/__tests__/e2e/validation-path-consistency.test.ts +354 -0
  78. package/.allhands/harness/src/__tests__/e2e/validation.test.ts +528 -0
  79. package/.allhands/harness/src/__tests__/harness/assertions.ts +318 -0
  80. package/.allhands/harness/src/__tests__/harness/cli-runner.ts +359 -0
  81. package/.allhands/harness/src/__tests__/harness/fixture.ts +384 -0
  82. package/.allhands/harness/src/__tests__/harness/hook-runner.ts +411 -0
  83. package/.allhands/harness/src/__tests__/harness/index.ts +122 -0
  84. package/.allhands/harness/src/cli.ts +36 -0
  85. package/.allhands/harness/src/commands/complexity.ts +177 -0
  86. package/.allhands/harness/src/commands/context7.ts +202 -0
  87. package/.allhands/harness/src/commands/docs.ts +557 -0
  88. package/.allhands/harness/src/commands/hooks.ts +24 -0
  89. package/.allhands/harness/src/commands/index.ts +51 -0
  90. package/.allhands/harness/src/commands/knowledge.ts +382 -0
  91. package/.allhands/harness/src/commands/memories.ts +302 -0
  92. package/.allhands/harness/src/commands/notify.ts +61 -0
  93. package/.allhands/harness/src/commands/oracle.ts +158 -0
  94. package/.allhands/harness/src/commands/perplexity.ts +220 -0
  95. package/.allhands/harness/src/commands/planning.ts +245 -0
  96. package/.allhands/harness/src/commands/schema.ts +73 -0
  97. package/.allhands/harness/src/commands/skills.ts +128 -0
  98. package/.allhands/harness/src/commands/solutions.ts +353 -0
  99. package/.allhands/harness/src/commands/spawn.ts +158 -0
  100. package/.allhands/harness/src/commands/specs.ts +532 -0
  101. package/.allhands/harness/src/commands/tavily.ts +226 -0
  102. package/.allhands/harness/src/commands/tools.ts +579 -0
  103. package/.allhands/harness/src/commands/trace.ts +327 -0
  104. package/.allhands/harness/src/commands/tui.ts +960 -0
  105. package/.allhands/harness/src/commands/validate.ts +143 -0
  106. package/.allhands/harness/src/commands/validation-tools.ts +108 -0
  107. package/.allhands/harness/src/hooks/context.ts +1442 -0
  108. package/.allhands/harness/src/hooks/enforcement.ts +170 -0
  109. package/.allhands/harness/src/hooks/index.ts +54 -0
  110. package/.allhands/harness/src/hooks/lifecycle.ts +229 -0
  111. package/.allhands/harness/src/hooks/notification.ts +104 -0
  112. package/.allhands/harness/src/hooks/observability.ts +551 -0
  113. package/.allhands/harness/src/hooks/session.ts +88 -0
  114. package/.allhands/harness/src/hooks/shared.ts +815 -0
  115. package/.allhands/harness/src/hooks/transcript-parser.ts +208 -0
  116. package/.allhands/harness/src/hooks/validation.ts +617 -0
  117. package/.allhands/harness/src/lib/__tests__/ctags.test.ts +244 -0
  118. package/.allhands/harness/src/lib/__tests__/docs-validation.test.ts +344 -0
  119. package/.allhands/harness/src/lib/__tests__/mcp-runtime.test.ts +190 -0
  120. package/.allhands/harness/src/lib/__tests__/schema.test.ts +861 -0
  121. package/.allhands/harness/src/lib/base-command.ts +198 -0
  122. package/.allhands/harness/src/lib/cli-daemon.ts +343 -0
  123. package/.allhands/harness/src/lib/compaction.ts +313 -0
  124. package/.allhands/harness/src/lib/ctags.ts +497 -0
  125. package/.allhands/harness/src/lib/docs-validation.ts +907 -0
  126. package/.allhands/harness/src/lib/event-loop.ts +662 -0
  127. package/.allhands/harness/src/lib/flows.ts +155 -0
  128. package/.allhands/harness/src/lib/git.ts +276 -0
  129. package/.allhands/harness/src/lib/knowledge-worker.ts +72 -0
  130. package/.allhands/harness/src/lib/knowledge.ts +810 -0
  131. package/.allhands/harness/src/lib/llm.ts +255 -0
  132. package/.allhands/harness/src/lib/mcp-client.ts +432 -0
  133. package/.allhands/harness/src/lib/mcp-daemon.ts +486 -0
  134. package/.allhands/harness/src/lib/mcp-runtime.ts +418 -0
  135. package/.allhands/harness/src/lib/notification.ts +115 -0
  136. package/.allhands/harness/src/lib/opencode/index.ts +70 -0
  137. package/.allhands/harness/src/lib/opencode/profiles.ts +300 -0
  138. package/.allhands/harness/src/lib/opencode/prompts/codesearch.md +98 -0
  139. package/.allhands/harness/src/lib/opencode/prompts/knowledge-aggregator.md +67 -0
  140. package/.allhands/harness/src/lib/opencode/runner.ts +281 -0
  141. package/.allhands/harness/src/lib/oracle.ts +926 -0
  142. package/.allhands/harness/src/lib/planning-utils.ts +150 -0
  143. package/.allhands/harness/src/lib/planning.ts +605 -0
  144. package/.allhands/harness/src/lib/pr-review.ts +225 -0
  145. package/.allhands/harness/src/lib/prompts.ts +522 -0
  146. package/.allhands/harness/src/lib/schema.ts +418 -0
  147. package/.allhands/harness/src/lib/schemas/agent-profile.ts +141 -0
  148. package/.allhands/harness/src/lib/schemas/template-vars.ts +138 -0
  149. package/.allhands/harness/src/lib/session.ts +164 -0
  150. package/.allhands/harness/src/lib/specs.ts +348 -0
  151. package/.allhands/harness/src/lib/tldr.ts +829 -0
  152. package/.allhands/harness/src/lib/tmux.ts +1051 -0
  153. package/.allhands/harness/src/lib/trace-store.ts +714 -0
  154. package/.allhands/harness/src/mcp/__tests__/index.test.ts +46 -0
  155. package/.allhands/harness/src/mcp/_template.ts +47 -0
  156. package/.allhands/harness/src/mcp/filesystem.ts +33 -0
  157. package/.allhands/harness/src/mcp/index.ts +69 -0
  158. package/.allhands/harness/src/mcp/playwright.ts +34 -0
  159. package/.allhands/harness/src/mcp/xcodebuild.ts +29 -0
  160. package/.allhands/harness/src/schemas/docs.schema.json +44 -0
  161. package/.allhands/harness/src/schemas/settings.schema.json +214 -0
  162. package/.allhands/harness/src/tui/actions.ts +227 -0
  163. package/.allhands/harness/src/tui/file-viewer-modal.ts +270 -0
  164. package/.allhands/harness/src/tui/index.ts +1574 -0
  165. package/.allhands/harness/src/tui/modal.ts +232 -0
  166. package/.allhands/harness/src/tui/prompts-pane.ts +186 -0
  167. package/.allhands/harness/src/tui/status-pane.ts +434 -0
  168. package/.allhands/harness/tsconfig.json +22 -0
  169. package/.allhands/harness/vitest.config.ts +13 -0
  170. package/.allhands/pillars.md +33 -0
  171. package/.allhands/principles.md +88 -0
  172. package/.allhands/schemas/alignment.yaml +51 -0
  173. package/.allhands/schemas/documentation.yaml +10 -0
  174. package/.allhands/schemas/prompt.yaml +92 -0
  175. package/.allhands/schemas/skill.yaml +34 -0
  176. package/.allhands/schemas/solution.yaml +131 -0
  177. package/.allhands/schemas/spec.yaml +67 -0
  178. package/.allhands/schemas/validation-suite.yaml +49 -0
  179. package/.allhands/schemas/workflow.yaml +51 -0
  180. package/.allhands/settings.json +57 -0
  181. package/.allhands/skills/claude-code-patterns/SKILL.md +60 -0
  182. package/.allhands/skills/claude-code-patterns/docs/context-hygiene.md +19 -0
  183. package/.allhands/skills/harness-maintenance/SKILL.md +449 -0
  184. package/.allhands/skills/harness-maintenance/references/core-architecture.md +187 -0
  185. package/.allhands/skills/harness-maintenance/references/harness-skills.md +87 -0
  186. package/.allhands/skills/harness-maintenance/references/knowledge-compounding.md +78 -0
  187. package/.allhands/skills/harness-maintenance/references/tools-commands-mcp-hooks.md +115 -0
  188. package/.allhands/skills/harness-maintenance/references/validation-tooling.md +77 -0
  189. package/.allhands/skills/harness-maintenance/references/writing-flows.md +84 -0
  190. package/.allhands/validation/browser-automation.md +109 -0
  191. package/.allhands/validation/xcode-automation.md +195 -0
  192. package/.allhands/workflows/documentation.md +86 -0
  193. package/.allhands/workflows/investigation.md +81 -0
  194. package/.allhands/workflows/milestone.md +91 -0
  195. package/.allhands/workflows/optimization.md +85 -0
  196. package/.allhands/workflows/refactor.md +99 -0
  197. package/.allhands/workflows/triage.md +81 -0
  198. package/.claude/README.md +1 -0
  199. package/.claude/agents/explorer.md +10 -0
  200. package/.claude/agents/researcher.md +11 -0
  201. package/.claude/agents/task-runner.md +8 -0
  202. package/.claude/settings.json +231 -0
  203. package/.env.ai.example +7 -0
  204. package/.github/workflows/npm-publish.yml +69 -0
  205. package/.internal.json +45 -0
  206. package/.tldr/config.json +11 -0
  207. package/.tldrignore +90 -0
  208. package/CLAUDE.md +6 -0
  209. package/README.md +98 -0
  210. package/bin/sync-cli.js +7552 -0
  211. package/concerns.md +7 -0
  212. package/docs/README.md +41 -0
  213. package/docs/agents/README.md +24 -0
  214. package/docs/agents/agent-configuration-system.md +86 -0
  215. package/docs/agents/execution-agents.md +50 -0
  216. package/docs/agents/knowledge-agents.md +61 -0
  217. package/docs/agents/orchestration-agent.md +57 -0
  218. package/docs/agents/planning-agents.md +84 -0
  219. package/docs/agents/quality-review-agents.md +67 -0
  220. package/docs/agents/workflow-agent-orchestration.md +69 -0
  221. package/docs/flows/README.md +44 -0
  222. package/docs/flows/compounding.md +126 -0
  223. package/docs/flows/coordination.md +72 -0
  224. package/docs/flows/core-harness-integration.md +63 -0
  225. package/docs/flows/documentation-orchestration.md +98 -0
  226. package/docs/flows/e2e-test-plan-building.md +83 -0
  227. package/docs/flows/emergent-refinement.md +104 -0
  228. package/docs/flows/flow-authoring-and-mcp-tools.md +89 -0
  229. package/docs/flows/judge-reviewing.md +112 -0
  230. package/docs/flows/plan-deepening-and-research.md +107 -0
  231. package/docs/flows/plan-review-jury.md +114 -0
  232. package/docs/flows/pr-reviewing.md +54 -0
  233. package/docs/flows/prompt-task-execution.md +119 -0
  234. package/docs/flows/spec-planning.md +162 -0
  235. package/docs/flows/type-specific-scoping-flows.md +49 -0
  236. package/docs/flows/validation-and-skills-integration.md +145 -0
  237. package/docs/flows/wip/wip-flows.md +102 -0
  238. package/docs/harness/README.md +23 -0
  239. package/docs/harness/agent-profiles.md +84 -0
  240. package/docs/harness/cli/README.md +24 -0
  241. package/docs/harness/cli/cli-entry-and-command-discovery.md +91 -0
  242. package/docs/harness/cli/docs-command.md +87 -0
  243. package/docs/harness/cli/knowledge-command.md +91 -0
  244. package/docs/harness/cli/minor-cli-commands.md +65 -0
  245. package/docs/harness/cli/oracle-command.md +113 -0
  246. package/docs/harness/cli/planning-command.md +95 -0
  247. package/docs/harness/cli/schema-and-validation-commands.md +154 -0
  248. package/docs/harness/cli/search-commands.md +97 -0
  249. package/docs/harness/cli/spawn-command.md +136 -0
  250. package/docs/harness/cli/specs-command.md +102 -0
  251. package/docs/harness/cli/tools-command.md +122 -0
  252. package/docs/harness/cli/trace-command.md +122 -0
  253. package/docs/harness/cli-daemon.md +92 -0
  254. package/docs/harness/event-loop.md +184 -0
  255. package/docs/harness/hooks/README.md +15 -0
  256. package/docs/harness/hooks/context-hooks.md +96 -0
  257. package/docs/harness/hooks/lifecycle-and-observability-hooks.md +135 -0
  258. package/docs/harness/hooks/validation-hooks.md +97 -0
  259. package/docs/harness/test-harness.md +149 -0
  260. package/docs/harness/tui.md +176 -0
  261. package/docs/memories.md +20 -0
  262. package/docs/solutions/agentic-issues/premature-agent-deletion-tui-action-dependency-20260130.md +49 -0
  263. package/docs/solutions/agentic-issues/ref-anchor-scope-mismatch-skill-references-20260131.md +55 -0
  264. package/docs/solutions/agentic-issues/tautological-tests-routing-20260131.md +52 -0
  265. package/docs/solutions/integration_issue/blocktool-output-format-mismatch-hook-runner-20260130.md +52 -0
  266. package/docs/solutions/integration_issue/dual-validation-path-divergence-schema-20260130.md +66 -0
  267. package/docs/solutions/security-issues/unsanitized-domain-path-join-20260131.md +52 -0
  268. package/docs/solutions/test-failures/event-loop-mock-ordering-checkAgentWindows-20260130.md +63 -0
  269. package/docs/sync-cli/README.md +19 -0
  270. package/docs/sync-cli/cli-entrypoint-and-commands.md +39 -0
  271. package/docs/sync-cli/commands/README.md +11 -0
  272. package/docs/sync-cli/commands/pull-manifest-command.md +36 -0
  273. package/docs/sync-cli/commands/push-command.md +84 -0
  274. package/docs/sync-cli/commands/sync-command.md +71 -0
  275. package/docs/sync-cli/systems/README.md +14 -0
  276. package/docs/sync-cli/systems/git-and-github-integration.md +49 -0
  277. package/docs/sync-cli/systems/interactive-ui.md +43 -0
  278. package/docs/sync-cli/systems/manifest-and-distribution.md +51 -0
  279. package/docs/sync-cli/systems/path-resolution.md +42 -0
  280. package/package.json +46 -0
  281. package/scripts/install-shim.sh +40 -0
  282. package/scripts/pre-pack.sh +25 -0
  283. package/specs/harness-maintenance-skill.spec.md +138 -0
  284. package/specs/roadmap/git-spec-lifecycle-management.spec.md +113 -0
  285. package/specs/sync-init-flag.spec.md +117 -0
  286. package/specs/unified-workflow-orchestration.spec.md +250 -0
  287. package/specs/validation-tooling-practice.spec.md +98 -0
  288. package/specs/workflow-domain-configuration.spec.md +265 -0
  289. package/src/commands/pull-manifest.ts +31 -0
  290. package/src/commands/push.ts +344 -0
  291. package/src/commands/sync.ts +289 -0
  292. package/src/lib/constants.ts +10 -0
  293. package/src/lib/dotfiles.ts +36 -0
  294. package/src/lib/fs-utils.ts +18 -0
  295. package/src/lib/gh.ts +40 -0
  296. package/src/lib/git.ts +63 -0
  297. package/src/lib/gitignore.ts +167 -0
  298. package/src/lib/manifest.ts +121 -0
  299. package/src/lib/marker-sync.ts +39 -0
  300. package/src/lib/paths.ts +38 -0
  301. package/src/lib/target-lines.ts +66 -0
  302. package/src/lib/ui.ts +78 -0
  303. package/src/sync-cli.ts +120 -0
  304. package/target-lines.json +23 -0
  305. package/tsconfig.json +20 -0
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Flow File Management
3
+ *
4
+ * Loads flow files from .allhands/flows/ directory and converts them
5
+ * to modal items for the TUI custom flow selection.
6
+ */
7
+
8
+ import { existsSync, readdirSync, statSync } from 'fs';
9
+ import { join, dirname } from 'path';
10
+ import { fileURLToPath } from 'url';
11
+ import type { ModalItem } from '../tui/modal.js';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+
16
+ export interface FlowFile {
17
+ /** Display name (filename without .md) */
18
+ name: string;
19
+ /** Relative path from flows directory (e.g., "shared/jury/BEST_PRACTICES_REVIEW.md") */
20
+ relativePath: string;
21
+ /** Absolute path to the flow file */
22
+ absolutePath: string;
23
+ /** Directory group (e.g., "", "shared", "shared/jury") */
24
+ directory: string;
25
+ }
26
+
27
+ export interface FlowGroup {
28
+ /** Directory path (empty string for root) */
29
+ directory: string;
30
+ /** Display label for the group */
31
+ label: string;
32
+ /** Flow files in this group */
33
+ flows: FlowFile[];
34
+ }
35
+
36
+ /**
37
+ * Get the flows directory path
38
+ */
39
+ function getFlowsDir(): string {
40
+ // Path: harness/src/lib/ -> harness/src/ -> harness/ -> .allhands/ -> flows/
41
+ return join(__dirname, '..', '..', '..', 'flows');
42
+ }
43
+
44
+ /**
45
+ * Recursively collect all .md files from a directory
46
+ */
47
+ function collectFlowFiles(baseDir: string, currentDir: string, relativePath: string = ''): FlowFile[] {
48
+ const flows: FlowFile[] = [];
49
+
50
+ if (!existsSync(currentDir)) {
51
+ return flows;
52
+ }
53
+
54
+ const entries = readdirSync(currentDir);
55
+
56
+ for (const entry of entries) {
57
+ const fullPath = join(currentDir, entry);
58
+ const relPath = relativePath ? join(relativePath, entry) : entry;
59
+ const stat = statSync(fullPath);
60
+
61
+ if (stat.isDirectory()) {
62
+ // Recurse into subdirectories
63
+ flows.push(...collectFlowFiles(baseDir, fullPath, relPath));
64
+ } else if (entry.endsWith('.md')) {
65
+ flows.push({
66
+ name: entry.replace('.md', ''),
67
+ relativePath: relPath,
68
+ absolutePath: fullPath,
69
+ directory: relativePath,
70
+ });
71
+ }
72
+ }
73
+
74
+ return flows;
75
+ }
76
+
77
+ /**
78
+ * Load all flow files from the flows directory
79
+ *
80
+ * Returns flows grouped by their directory structure.
81
+ */
82
+ export function loadAllFlows(): FlowGroup[] {
83
+ const flowsDir = getFlowsDir();
84
+ const allFlows = collectFlowFiles(flowsDir, flowsDir);
85
+
86
+ // Group flows by directory
87
+ const groupMap = new Map<string, FlowFile[]>();
88
+
89
+ for (const flow of allFlows) {
90
+ const dir = flow.directory;
91
+ const existing = groupMap.get(dir) ?? [];
92
+ existing.push(flow);
93
+ groupMap.set(dir, existing);
94
+ }
95
+
96
+ // Convert to FlowGroup array, sorted by directory depth then name
97
+ const groups: FlowGroup[] = [];
98
+ const sortedDirs = Array.from(groupMap.keys()).sort((a, b) => {
99
+ // Root first, then by depth, then alphabetically
100
+ if (a === '' && b !== '') return -1;
101
+ if (b === '' && a !== '') return 1;
102
+ const depthA = a.split('/').length;
103
+ const depthB = b.split('/').length;
104
+ if (depthA !== depthB) return depthA - depthB;
105
+ return a.localeCompare(b);
106
+ });
107
+
108
+ for (const dir of sortedDirs) {
109
+ const flows = groupMap.get(dir) ?? [];
110
+ // Sort flows by name within each group
111
+ flows.sort((a, b) => a.name.localeCompare(b.name));
112
+
113
+ groups.push({
114
+ directory: dir,
115
+ label: dir === '' ? 'Root Flows' : dir.replace(/\//g, ' / '),
116
+ flows,
117
+ });
118
+ }
119
+
120
+ return groups;
121
+ }
122
+
123
+ /**
124
+ * Convert flow groups to modal items for display
125
+ */
126
+ export function flowsToModalItems(groups: FlowGroup[]): ModalItem[] {
127
+ const items: ModalItem[] = [];
128
+
129
+ for (const group of groups) {
130
+ // Add header for this group
131
+ items.push({
132
+ id: `header-${group.directory || 'root'}`,
133
+ label: group.label,
134
+ type: 'header',
135
+ });
136
+
137
+ // Add flow items
138
+ for (const flow of group.flows) {
139
+ items.push({
140
+ id: flow.absolutePath, // Use absolute path as ID for easy lookup
141
+ label: flow.name,
142
+ type: 'item',
143
+ });
144
+ }
145
+ }
146
+
147
+ return items;
148
+ }
149
+
150
+ /**
151
+ * Get the absolute path to the flows directory
152
+ */
153
+ export function getFlowsDirectory(): string {
154
+ return getFlowsDir();
155
+ }
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Git utilities for All Hands CLI.
3
+ *
4
+ * Provides git context for notifications and other features.
5
+ */
6
+
7
+ import { spawnSync } from "child_process";
8
+ import { basename } from "path";
9
+ import { getBaseBranch, getLocalBaseBranch } from '../hooks/shared.js';
10
+
11
+ // Re-export getBaseBranch and getLocalBaseBranch for consumers
12
+ export { getBaseBranch, getLocalBaseBranch };
13
+
14
+ // ── Safe git execution wrapper ──────────────────────────────────────────────
15
+
16
+ export interface GitExecResult {
17
+ success: boolean;
18
+ stdout: string;
19
+ stderr: string;
20
+ exitCode: number;
21
+ }
22
+
23
+ /**
24
+ * Safe git command execution using spawnSync with argument arrays.
25
+ * No shell involved — immune to command injection via branch names, paths, etc.
26
+ */
27
+ export function gitExec(args: string[], cwd?: string): GitExecResult {
28
+ const workingDir = cwd || process.cwd();
29
+ const result = spawnSync('git', args, {
30
+ encoding: 'utf-8',
31
+ cwd: workingDir,
32
+ maxBuffer: 10 * 1024 * 1024,
33
+ });
34
+
35
+ return {
36
+ success: result.status === 0,
37
+ stdout: result.stdout?.trim() || '',
38
+ stderr: result.stderr?.trim() || '',
39
+ exitCode: result.status ?? 1,
40
+ };
41
+ }
42
+
43
+ // ── Input validation ────────────────────────────────────────────────────────
44
+
45
+ const GIT_REF_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9/_.-]*$/;
46
+
47
+ /**
48
+ * Validate a git ref (branch name, tag, etc.) against a safe allowlist pattern.
49
+ * Throws a descriptive error if the ref contains unsafe characters.
50
+ *
51
+ * Apply at the boundary where user-derived values (spec frontmatter `branch`,
52
+ * settings `baseBranch`) enter git operations — not deep inside helpers.
53
+ */
54
+ export function validateGitRef(ref: string, label: string): void {
55
+ if (!ref || !GIT_REF_PATTERN.test(ref)) {
56
+ throw new Error(
57
+ `Invalid ${label}: "${ref}". Git refs must start with an alphanumeric character and contain only alphanumerics, slashes, underscores, dots, and hyphens.`
58
+ );
59
+ }
60
+ }
61
+
62
+ // Protected branches - no planning required
63
+ const PROTECTED_BRANCHES = new Set([
64
+ "main",
65
+ "master",
66
+ "develop",
67
+ "development",
68
+ "dev",
69
+ "staging",
70
+ "stage",
71
+ "production",
72
+ "prod",
73
+ ]);
74
+
75
+ // Prefixes that indicate direct mode (no planning)
76
+ const DIRECT_MODE_PREFIXES = ["quick/", "curator/"];
77
+
78
+ /**
79
+ * Get current git branch name.
80
+ * Uses CLAUDE_PROJECT_DIR if available (for hooks), else current directory.
81
+ */
82
+ export function getBranch(): string {
83
+ try {
84
+ const cwd = process.env.CLAUDE_PROJECT_DIR || process.cwd();
85
+ const result = spawnSync("git", ["branch", "--show-current"], {
86
+ encoding: "utf-8",
87
+ cwd,
88
+ });
89
+ return result.status === 0 ? result.stdout.trim() : "";
90
+ } catch {
91
+ return "";
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Convert branch name to safe directory name (feat/auth -> feat-auth).
97
+ */
98
+ export function sanitizeBranch(branch: string): string {
99
+ return branch.replace(/[^a-zA-Z0-9_-]/g, "-");
100
+ }
101
+
102
+ /**
103
+ * Check if branch should skip planning.
104
+ */
105
+ export function isDirectModeBranch(branch: string): boolean {
106
+ if (PROTECTED_BRANCHES.has(branch)) {
107
+ return true;
108
+ }
109
+ return DIRECT_MODE_PREFIXES.some((prefix) => branch.startsWith(prefix));
110
+ }
111
+
112
+ /**
113
+ * Get git diff against a reference.
114
+ */
115
+ export function getDiff(ref: string): string {
116
+ try {
117
+ const result = spawnSync("git", ["diff", ref], {
118
+ encoding: "utf-8",
119
+ maxBuffer: 10 * 1024 * 1024, // 10MB
120
+ });
121
+
122
+ if (result.status !== 0) {
123
+ // Fallback to empty tree if ref doesn't exist (fresh repo)
124
+ const emptyTree = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
125
+ const fallback = spawnSync("git", ["diff", emptyTree], {
126
+ encoding: "utf-8",
127
+ maxBuffer: 10 * 1024 * 1024,
128
+ });
129
+ return fallback.stdout || "(No changes)";
130
+ }
131
+
132
+ return result.stdout || "(No changes)";
133
+ } catch {
134
+ return "(Unable to get diff)";
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Get the project root directory (where .git is located).
140
+ * Uses CLAUDE_PROJECT_DIR if available (for hooks), else current directory.
141
+ */
142
+ export function getProjectRoot(): string {
143
+ const cwd = process.env.CLAUDE_PROJECT_DIR || process.cwd();
144
+ const result = gitExec(['rev-parse', '--show-toplevel'], cwd);
145
+ return result.success ? result.stdout : cwd;
146
+ }
147
+
148
+ /**
149
+ * Get the repo's root directory name (e.g., "claude-agents").
150
+ */
151
+ export function getRepoName(): string {
152
+ return basename(getProjectRoot()) || "";
153
+ }
154
+
155
+ /**
156
+ * Get the plan directory path for current branch.
157
+ */
158
+ export function getPlanDir(cwd?: string): string {
159
+ const root = cwd ?? getProjectRoot();
160
+ const branch = getBranch();
161
+ const planId = sanitizeBranch(branch);
162
+ return `${root}/.allhands/plans/${planId}`;
163
+ }
164
+
165
+ /**
166
+ * Get the current HEAD commit hash.
167
+ */
168
+ export function getHeadCommit(cwd?: string): string {
169
+ const workingDir = cwd || process.cwd();
170
+
171
+ try {
172
+ const result = spawnSync("git", ["rev-parse", "HEAD"], {
173
+ encoding: "utf-8",
174
+ cwd: workingDir,
175
+ });
176
+
177
+ return result.status === 0 ? result.stdout.trim() : "";
178
+ } catch {
179
+ return "";
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Get short commit hash (7 chars).
185
+ */
186
+ export function getShortCommit(commitHash: string): string {
187
+ return commitHash.substring(0, 7);
188
+ }
189
+
190
+ /**
191
+ * Check if there are uncommitted changes in the working directory.
192
+ * Returns true if there are staged or unstaged changes.
193
+ *
194
+ * Fail-safe: returns true when git itself fails (non-zero exit or exception).
195
+ * When we can't determine repo state, assume dirty to prevent destructive
196
+ * operations (checkout, clean, branch delete) from proceeding on false data.
197
+ */
198
+ export function hasUncommittedChanges(cwd?: string): boolean {
199
+ const workingDir = cwd || process.cwd();
200
+ try {
201
+ const result = spawnSync("git", ["status", "--porcelain"], {
202
+ encoding: "utf-8",
203
+ cwd: workingDir,
204
+ });
205
+ // Fail-safe: if git status itself fails, assume changes exist
206
+ if (result.status !== 0) {
207
+ return true;
208
+ }
209
+ return result.stdout.trim().length > 0;
210
+ } catch {
211
+ // Fail-safe: exception means we can't determine state — assume dirty
212
+ return true;
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Checkout a git branch.
218
+ * Returns true on success, false on failure.
219
+ */
220
+ export function checkoutBranch(branch: string, cwd?: string): boolean {
221
+ const workingDir = cwd || process.cwd();
222
+ try {
223
+ const result = spawnSync("git", ["checkout", branch], {
224
+ encoding: "utf-8",
225
+ cwd: workingDir,
226
+ });
227
+ return result.status === 0;
228
+ } catch {
229
+ return false;
230
+ }
231
+ }
232
+
233
+ // ── Remote sync ─────────────────────────────────────────────────────────────
234
+
235
+ export interface SyncResult {
236
+ success: boolean;
237
+ conflicts: string[];
238
+ }
239
+
240
+ /**
241
+ * Sync the current branch with origin/main.
242
+ * Used at three lifecycle points: activation, PR creation, completion.
243
+ *
244
+ * Flow: fetch origin main → merge origin/main --no-edit →
245
+ * on conflict: parse conflicted files, abort merge, return conflicts list
246
+ * on success: return { success: true, conflicts: [] }
247
+ */
248
+ export function syncWithOriginMain(cwd?: string): SyncResult {
249
+ const workingDir = cwd || process.cwd();
250
+
251
+ // 1. Fetch latest main from origin
252
+ const fetch = gitExec(['fetch', 'origin', 'main'], workingDir);
253
+ if (!fetch.success) {
254
+ return { success: false, conflicts: [] };
255
+ }
256
+
257
+ // 2. Attempt merge
258
+ const merge = gitExec(['merge', 'origin/main', '--no-edit'], workingDir);
259
+ if (merge.success) {
260
+ return { success: true, conflicts: [] };
261
+ }
262
+
263
+ // 3. Merge failed — detect conflicts
264
+ const diffResult = gitExec(
265
+ ['diff', '--name-only', '--diff-filter=U'],
266
+ workingDir,
267
+ );
268
+ const conflicts = diffResult.stdout
269
+ ? diffResult.stdout.split('\n').filter(Boolean)
270
+ : [];
271
+
272
+ // 4. Abort the failed merge to restore clean state
273
+ gitExec(['merge', '--abort'], workingDir);
274
+
275
+ return { success: false, conflicts };
276
+ }
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Knowledge Worker - standalone child process for embedding operations.
4
+ *
5
+ * Isolates the ONNX Runtime + gtr-t5-quant model (~300-600MB) from the
6
+ * main TUI process. The model loads, runs, and dies entirely within this
7
+ * child process, preventing heap pressure in the TUI.
8
+ *
9
+ * Usage:
10
+ * npx tsx knowledge-worker.ts reindexAll <indexName> <projectRoot>
11
+ * npx tsx knowledge-worker.ts reindexFromChanges <indexName> <projectRoot>
12
+ * (reads changes JSON from stdin for reindexFromChanges)
13
+ *
14
+ * Output:
15
+ * stderr: Progress messages (line-buffered)
16
+ * stdout: Final JSON result { success, files_indexed, total_tokens } or
17
+ * { success, message, files }
18
+ */
19
+
20
+ import { KnowledgeService, type IndexName } from "./knowledge.js";
21
+
22
+ async function main(): Promise<void> {
23
+ const [command, indexName, projectRoot] = process.argv.slice(2);
24
+
25
+ if (!command || !indexName || !projectRoot) {
26
+ process.stderr.write("Usage: knowledge-worker.ts <reindexAll|reindexFromChanges> <indexName> <projectRoot>\n");
27
+ process.exit(1);
28
+ }
29
+
30
+ // Create service with quiet=false so log() writes to stderr
31
+ const service = new KnowledgeService(projectRoot, { quiet: false });
32
+
33
+ try {
34
+ if (command === "reindexAll") {
35
+ const result = await service.reindexAll(indexName as IndexName);
36
+ // Write final result to stdout
37
+ process.stdout.write(JSON.stringify({
38
+ success: true,
39
+ files_indexed: result.files_indexed,
40
+ total_tokens: result.total_tokens,
41
+ }) + "\n");
42
+ } else if (command === "reindexFromChanges") {
43
+ // Read changes from stdin
44
+ const chunks: Buffer[] = [];
45
+ for await (const chunk of process.stdin) {
46
+ chunks.push(chunk);
47
+ }
48
+ const changesJson = Buffer.concat(chunks).toString("utf-8");
49
+ const changes = JSON.parse(changesJson);
50
+
51
+ const result = await service.reindexFromChanges(indexName as IndexName, changes);
52
+ process.stdout.write(JSON.stringify({
53
+ success: result.success,
54
+ message: result.message,
55
+ files: result.files,
56
+ }) + "\n");
57
+ } else {
58
+ process.stderr.write(`Unknown command: ${command}\n`);
59
+ process.exit(1);
60
+ }
61
+ } catch (err) {
62
+ const message = err instanceof Error ? err.message : String(err);
63
+ process.stderr.write(`[knowledge-worker] Error: ${message}\n`);
64
+ process.stdout.write(JSON.stringify({ success: false, error: message }) + "\n");
65
+ process.exit(1);
66
+ }
67
+
68
+ // Clean exit to release ONNX thread pools
69
+ process.exit(0);
70
+ }
71
+
72
+ main();