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,411 @@
1
+ /**
2
+ * Hook Runner - Execute hooks with mock stdin and capture output
3
+ *
4
+ * Hooks communicate via stdin JSON and stdout JSON. This runner
5
+ * provides utilities for testing hook I/O contracts.
6
+ */
7
+
8
+ import { runCli, type RunOptions, type RunResult } from './cli-runner.js';
9
+ import type { TestFixture } from './fixture.js';
10
+ import type {
11
+ HookInput,
12
+ PreToolUseOutput,
13
+ PostToolUseOutput,
14
+ StopHookOutput,
15
+ PreCompactOutput,
16
+ } from '../../hooks/shared.js';
17
+
18
+ // ─────────────────────────────────────────────────────────────────────────────
19
+ // Types
20
+ // ─────────────────────────────────────────────────────────────────────────────
21
+
22
+ export type HookType = 'context' | 'validation' | 'lifecycle';
23
+
24
+ export interface HookResult extends RunResult {
25
+ /** Parsed hook output (PreToolUse, PostToolUse, Stop, or PreCompact format) */
26
+ hookOutput?: PreToolUseOutput | PostToolUseOutput | StopHookOutput | PreCompactOutput;
27
+ /** Whether the hook allowed the tool (for PreToolUse) */
28
+ allowed?: boolean;
29
+ /** Whether the hook denied the tool (for PreToolUse) */
30
+ denied?: boolean;
31
+ /** Whether the hook blocked the tool (for PostToolUse) */
32
+ blocked?: boolean;
33
+ /** The system message if present */
34
+ systemMessage?: string;
35
+ /** Denial reason if denied */
36
+ denialReason?: string;
37
+ }
38
+
39
+ export interface PreToolUseInput extends HookInput {
40
+ tool_name: string;
41
+ tool_input: Record<string, unknown>;
42
+ }
43
+
44
+ export interface PostToolUseInput extends HookInput {
45
+ tool_name: string;
46
+ tool_input: Record<string, unknown>;
47
+ tool_result: unknown;
48
+ }
49
+
50
+ // ─────────────────────────────────────────────────────────────────────────────
51
+ // Hook Execution
52
+ // ─────────────────────────────────────────────────────────────────────────────
53
+
54
+ /**
55
+ * Run a hook with the given input.
56
+ */
57
+ export async function runHook(
58
+ hookType: HookType,
59
+ hookName: string,
60
+ input: HookInput,
61
+ fixture?: TestFixture,
62
+ options: Omit<RunOptions, 'stdin' | 'expectJson'> = {}
63
+ ): Promise<HookResult> {
64
+ const args = ['hooks', hookType, hookName];
65
+ const stdin = JSON.stringify(input);
66
+
67
+ const runOptions: RunOptions = {
68
+ ...options,
69
+ stdin,
70
+ expectJson: true,
71
+ cwd: fixture?.root ?? options.cwd,
72
+ env: {
73
+ ...fixture?.env,
74
+ ...options.env,
75
+ },
76
+ };
77
+
78
+ const result = await runCli(args, runOptions);
79
+ const hookResult: HookResult = { ...result };
80
+
81
+ // Parse hook output if present
82
+ if (result.json) {
83
+ hookResult.hookOutput = result.json as
84
+ | PreToolUseOutput
85
+ | PostToolUseOutput
86
+ | StopHookOutput
87
+ | PreCompactOutput;
88
+
89
+ // Extract common fields
90
+ const output = hookResult.hookOutput as Record<string, unknown>;
91
+
92
+ if ('systemMessage' in output) {
93
+ hookResult.systemMessage = output.systemMessage as string;
94
+ }
95
+
96
+ // PreToolUse output parsing
97
+ if ('hookSpecificOutput' in output) {
98
+ const specific = output.hookSpecificOutput as Record<string, unknown>;
99
+ if (specific.permissionDecision === 'allow') {
100
+ hookResult.allowed = true;
101
+ hookResult.denied = false;
102
+ } else if (specific.permissionDecision === 'deny') {
103
+ hookResult.allowed = false;
104
+ hookResult.denied = true;
105
+ hookResult.denialReason = specific.permissionDecisionReason as string | undefined;
106
+ }
107
+ }
108
+
109
+ // PostToolUse output parsing
110
+ if ('continue' in output) {
111
+ hookResult.blocked = output.continue === false;
112
+ } else if ('decision' in output && output.decision === 'block') {
113
+ hookResult.blocked = true;
114
+ }
115
+ } else if (result.success && !result.stdout.trim()) {
116
+ // Empty output = allow (for PreToolUse hooks)
117
+ hookResult.allowed = true;
118
+ hookResult.denied = false;
119
+ }
120
+
121
+ return hookResult;
122
+ }
123
+
124
+ // ─────────────────────────────────────────────────────────────────────────────
125
+ // PreToolUse Hooks
126
+ // ─────────────────────────────────────────────────────────────────────────────
127
+
128
+ /**
129
+ * Run a context hook (PreToolUse).
130
+ */
131
+ export async function runContextHook(
132
+ hookName: string,
133
+ input: PreToolUseInput,
134
+ fixture?: TestFixture,
135
+ options: Omit<RunOptions, 'stdin' | 'expectJson'> = {}
136
+ ): Promise<HookResult> {
137
+ return runHook('context', hookName, input, fixture, options);
138
+ }
139
+
140
+ /**
141
+ * Test the edit-inject hook.
142
+ */
143
+ export async function runEditInject(
144
+ filePath: string,
145
+ fixture?: TestFixture
146
+ ): Promise<HookResult> {
147
+ return runContextHook(
148
+ 'edit-inject',
149
+ {
150
+ tool_name: 'Edit',
151
+ tool_input: { file_path: filePath, old_string: '', new_string: '' },
152
+ },
153
+ fixture
154
+ );
155
+ }
156
+
157
+ /**
158
+ * Test the read-enforcer hook.
159
+ */
160
+ export async function runReadEnforcer(
161
+ filePath: string,
162
+ fixture?: TestFixture,
163
+ options: { offset?: number; limit?: number } = {}
164
+ ): Promise<HookResult> {
165
+ return runContextHook(
166
+ 'read-enforcer',
167
+ {
168
+ tool_name: 'Read',
169
+ tool_input: { file_path: filePath, ...options },
170
+ },
171
+ fixture,
172
+ { timeout: 20000 } // read-enforcer has longer timeout
173
+ );
174
+ }
175
+
176
+ /**
177
+ * Test the search-router hook.
178
+ */
179
+ export async function runSearchRouter(
180
+ pattern: string,
181
+ fixture?: TestFixture
182
+ ): Promise<HookResult> {
183
+ return runContextHook(
184
+ 'search-router',
185
+ {
186
+ tool_name: 'Grep',
187
+ tool_input: { pattern },
188
+ },
189
+ fixture
190
+ );
191
+ }
192
+
193
+ /**
194
+ * Test the tldr-inject hook for Task tool.
195
+ */
196
+ export async function runTldrInject(
197
+ prompt: string,
198
+ fixture?: TestFixture
199
+ ): Promise<HookResult> {
200
+ return runContextHook(
201
+ 'tldr-inject',
202
+ {
203
+ tool_name: 'Task',
204
+ tool_input: { prompt, description: 'Test task' },
205
+ },
206
+ fixture
207
+ );
208
+ }
209
+
210
+ // ─────────────────────────────────────────────────────────────────────────────
211
+ // Validation Hooks
212
+ // ─────────────────────────────────────────────────────────────────────────────
213
+
214
+ /**
215
+ * Run a validation hook (PreWrite or PostWrite).
216
+ */
217
+ export async function runValidationHook(
218
+ hookName: string,
219
+ input: PreToolUseInput | PostToolUseInput,
220
+ fixture?: TestFixture
221
+ ): Promise<HookResult> {
222
+ return runHook('validation', hookName, input, fixture);
223
+ }
224
+
225
+ /**
226
+ * Test the schema-check hook (PreWrite validation).
227
+ */
228
+ export async function runSchemaCheck(
229
+ filePath: string,
230
+ content: string,
231
+ fixture?: TestFixture
232
+ ): Promise<HookResult> {
233
+ return runValidationHook(
234
+ 'schema-check',
235
+ {
236
+ tool_name: 'Write',
237
+ tool_input: { file_path: filePath, content },
238
+ },
239
+ fixture
240
+ );
241
+ }
242
+
243
+ // ─────────────────────────────────────────────────────────────────────────────
244
+ // Lifecycle Hooks
245
+ // ─────────────────────────────────────────────────────────────────────────────
246
+
247
+ /**
248
+ * Run a lifecycle hook (Stop, PreCompact).
249
+ */
250
+ export async function runLifecycleHook(
251
+ hookName: string,
252
+ input: HookInput,
253
+ fixture?: TestFixture
254
+ ): Promise<HookResult> {
255
+ return runHook('lifecycle', hookName, input, fixture);
256
+ }
257
+
258
+ /**
259
+ * Test the stop hook.
260
+ */
261
+ export async function runStopHook(
262
+ sessionId: string,
263
+ fixture?: TestFixture
264
+ ): Promise<HookResult> {
265
+ return runLifecycleHook(
266
+ 'stop',
267
+ {
268
+ session_id: sessionId,
269
+ stop_hook_active: true,
270
+ },
271
+ fixture
272
+ );
273
+ }
274
+
275
+ // ─────────────────────────────────────────────────────────────────────────────
276
+ // Hook Contract Testing
277
+ // ─────────────────────────────────────────────────────────────────────────────
278
+
279
+ export interface HookContract {
280
+ name: string;
281
+ hookType: HookType;
282
+ hookName: string;
283
+ input: HookInput;
284
+ expect: {
285
+ success?: boolean;
286
+ allowed?: boolean;
287
+ denied?: boolean;
288
+ blocked?: boolean;
289
+ hasSystemMessage?: boolean;
290
+ systemMessageContains?: string[];
291
+ denialReasonContains?: string;
292
+ };
293
+ }
294
+
295
+ export interface ContractResult {
296
+ contract: HookContract;
297
+ result: HookResult;
298
+ passed: boolean;
299
+ failures: string[];
300
+ }
301
+
302
+ /**
303
+ * Test a hook against its contract.
304
+ */
305
+ export async function testHookContract(
306
+ contract: HookContract,
307
+ fixture?: TestFixture
308
+ ): Promise<ContractResult> {
309
+ const result = await runHook(
310
+ contract.hookType,
311
+ contract.hookName,
312
+ contract.input,
313
+ fixture
314
+ );
315
+
316
+ const failures: string[] = [];
317
+ const { expect: exp } = contract;
318
+
319
+ if (exp.success !== undefined && result.success !== exp.success) {
320
+ failures.push(`Expected success=${exp.success}, got ${result.success}`);
321
+ }
322
+
323
+ if (exp.allowed !== undefined && result.allowed !== exp.allowed) {
324
+ failures.push(`Expected allowed=${exp.allowed}, got ${result.allowed}`);
325
+ }
326
+
327
+ if (exp.denied !== undefined && result.denied !== exp.denied) {
328
+ failures.push(`Expected denied=${exp.denied}, got ${result.denied}`);
329
+ }
330
+
331
+ if (exp.blocked !== undefined && result.blocked !== exp.blocked) {
332
+ failures.push(`Expected blocked=${exp.blocked}, got ${result.blocked}`);
333
+ }
334
+
335
+ if (exp.hasSystemMessage !== undefined) {
336
+ const hasMsg = !!result.systemMessage;
337
+ if (hasMsg !== exp.hasSystemMessage) {
338
+ failures.push(`Expected hasSystemMessage=${exp.hasSystemMessage}, got ${hasMsg}`);
339
+ }
340
+ }
341
+
342
+ if (exp.systemMessageContains && result.systemMessage) {
343
+ for (const expected of exp.systemMessageContains) {
344
+ if (!result.systemMessage.includes(expected)) {
345
+ failures.push(`Expected systemMessage to contain "${expected}"`);
346
+ }
347
+ }
348
+ }
349
+
350
+ if (exp.denialReasonContains && result.denialReason) {
351
+ if (!result.denialReason.includes(exp.denialReasonContains)) {
352
+ failures.push(`Expected denialReason to contain "${exp.denialReasonContains}"`);
353
+ }
354
+ }
355
+
356
+ return {
357
+ contract,
358
+ result,
359
+ passed: failures.length === 0,
360
+ failures,
361
+ };
362
+ }
363
+
364
+ /**
365
+ * Test multiple hook contracts.
366
+ */
367
+ export async function testHookContracts(
368
+ contracts: HookContract[],
369
+ fixture?: TestFixture
370
+ ): Promise<ContractResult[]> {
371
+ const results: ContractResult[] = [];
372
+
373
+ for (const contract of contracts) {
374
+ results.push(await testHookContract(contract, fixture));
375
+ }
376
+
377
+ return results;
378
+ }
379
+
380
+ // ─────────────────────────────────────────────────────────────────────────────
381
+ // Debug Helpers
382
+ // ─────────────────────────────────────────────────────────────────────────────
383
+
384
+ /**
385
+ * Print a hook result for debugging.
386
+ */
387
+ export function debugHookResult(result: HookResult, label?: string): void {
388
+ console.log('\n' + '='.repeat(60));
389
+ if (label) {
390
+ console.log(`HOOK DEBUG: ${label}`);
391
+ console.log('-'.repeat(60));
392
+ }
393
+ console.log(`Exit Code: ${result.exitCode} (${result.success ? 'success' : 'failure'})`);
394
+ console.log(`Allowed: ${result.allowed}, Denied: ${result.denied}, Blocked: ${result.blocked}`);
395
+ if (result.denialReason) {
396
+ console.log(`Denial Reason: ${result.denialReason}`);
397
+ }
398
+ if (result.systemMessage) {
399
+ console.log(`\n--- SYSTEM MESSAGE ---`);
400
+ console.log(result.systemMessage.substring(0, 500) + (result.systemMessage.length > 500 ? '...' : ''));
401
+ }
402
+ if (result.hookOutput) {
403
+ console.log(`\n--- RAW HOOK OUTPUT ---`);
404
+ console.log(JSON.stringify(result.hookOutput, null, 2).substring(0, 1000));
405
+ }
406
+ if (result.stderr) {
407
+ console.log(`\n--- STDERR ---`);
408
+ console.log(result.stderr);
409
+ }
410
+ console.log('='.repeat(60) + '\n');
411
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Harness Test Utilities - Main Export
3
+ *
4
+ * Provides everything needed for headless E2E testing of the ah CLI.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import {
9
+ * createFixture,
10
+ * createSpecFixture,
11
+ * runInFixture,
12
+ * runHook,
13
+ * assertSuccess,
14
+ * assertHookAllowed,
15
+ * } from '../harness/index.js';
16
+ *
17
+ * describe('My E2E Tests', () => {
18
+ * let fixture: TestFixture;
19
+ *
20
+ * beforeAll(() => {
21
+ * fixture = createSpecFixture('test-spec');
22
+ * });
23
+ *
24
+ * afterAll(() => {
25
+ * fixture.cleanup();
26
+ * });
27
+ *
28
+ * it('runs a command', async () => {
29
+ * const result = await runInFixture(fixture, ['validate', 'file.md']);
30
+ * assertSuccess(result);
31
+ * });
32
+ * });
33
+ * ```
34
+ */
35
+
36
+ // Fixture creation and management
37
+ export {
38
+ createFixture,
39
+ createSpecFixture,
40
+ createMilestoneFixture,
41
+ createMultiSpecFixture,
42
+ getPooledFixture,
43
+ cleanupPool,
44
+ PROMPT_TEMPLATE,
45
+ ALIGNMENT_TEMPLATE,
46
+ SPEC_TEMPLATE,
47
+ PYTHON_SAMPLE,
48
+ TYPESCRIPT_SAMPLE,
49
+ type TestFixture,
50
+ type FixtureOptions,
51
+ } from './fixture.js';
52
+
53
+ // CLI runner
54
+ export {
55
+ runCli,
56
+ runInFixture,
57
+ runKnowledgeSearch,
58
+ runValidate,
59
+ runCodeSearch,
60
+ runToolsList,
61
+ runSpecsList,
62
+ runBatch,
63
+ debugResult,
64
+ type RunOptions,
65
+ type RunResult,
66
+ type BatchCommand,
67
+ type BatchResult,
68
+ } from './cli-runner.js';
69
+
70
+ // Hook runner
71
+ export {
72
+ runHook,
73
+ runContextHook,
74
+ runEditInject,
75
+ runReadEnforcer,
76
+ runSearchRouter,
77
+ runTldrInject,
78
+ runValidationHook,
79
+ runSchemaCheck,
80
+ runLifecycleHook,
81
+ runStopHook,
82
+ testHookContract,
83
+ testHookContracts,
84
+ debugHookResult,
85
+ type HookType,
86
+ type HookResult,
87
+ type PreToolUseInput,
88
+ type PostToolUseInput,
89
+ type HookContract,
90
+ type ContractResult,
91
+ } from './hook-runner.js';
92
+
93
+ // Assertions
94
+ export {
95
+ // CLI assertions
96
+ assertSuccess,
97
+ assertFailure,
98
+ assertStdoutContains,
99
+ assertStderrContains,
100
+ assertStdoutMatches,
101
+ assertJsonOutput,
102
+ assertTimedWithin,
103
+ // Hook assertions
104
+ assertHookAllowed,
105
+ assertHookDenied,
106
+ assertHookBlocked,
107
+ assertHookInjectedContext,
108
+ assertHookContextContains,
109
+ assertDenialReasonContains,
110
+ // Fixture assertions
111
+ assertFileExists,
112
+ assertFileNotExists,
113
+ assertFileContains,
114
+ assertFileMatches,
115
+ assertValidFrontmatter,
116
+ // Git assertions
117
+ assertGitTracked,
118
+ assertGitDirty,
119
+ // Composite assertions
120
+ assertWorkflowSuccess,
121
+ assertContractsPassed,
122
+ } from './assertions.js';
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * All Hands CLI - Main Entry Point
4
+ *
5
+ * Running `ah` with no command launches the TUI.
6
+ * Commands are auto-discovered from the commands/ directory.
7
+ * Each command module exports a `register` function.
8
+ */
9
+
10
+ import { Command } from 'commander';
11
+ import { discoverAndRegister } from './commands/index.js';
12
+ import { launchTUI } from './commands/tui.js';
13
+
14
+ async function main(): Promise<void> {
15
+ const program = new Command();
16
+
17
+ program
18
+ .name('ah')
19
+ .description('All Hands - Agentic harness for model-first software development')
20
+ .version('0.1.0')
21
+ .option('-s, --use-spec <spec>', 'Spec to use for TUI (defaults to active)')
22
+ .action(async (options: { useSpec?: string }) => {
23
+ // Default action when no subcommand - launch TUI
24
+ await launchTUI({ spec: options.useSpec });
25
+ });
26
+
27
+ // Auto-discover and register all commands
28
+ await discoverAndRegister(program);
29
+
30
+ await program.parseAsync();
31
+ }
32
+
33
+ main().catch((e) => {
34
+ console.error(e);
35
+ process.exit(1);
36
+ });