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,427 @@
1
+ /**
2
+ * E2E Tests - Hook Contracts
3
+ *
4
+ * Tests hook I/O contracts to ensure hooks behave correctly
5
+ * with various inputs. These tests run hooks as separate processes
6
+ * with mock stdin and validate their stdout JSON.
7
+ */
8
+
9
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
10
+ import {
11
+ createFixture,
12
+ createMilestoneFixture,
13
+ runContextHook,
14
+ runEditInject,
15
+ runReadEnforcer,
16
+ runSearchRouter,
17
+ runValidationHook,
18
+ runHook,
19
+ testHookContracts,
20
+ assertHookAllowed,
21
+ assertHookDenied,
22
+ assertHookInjectedContext,
23
+ assertHookContextContains,
24
+ assertContractsPassed,
25
+ type TestFixture,
26
+ type HookContract,
27
+ PYTHON_SAMPLE,
28
+ TYPESCRIPT_SAMPLE,
29
+ } from '../harness/index.js';
30
+
31
+ // ─────────────────────────────────────────────────────────────────────────────
32
+ // Test Setup
33
+ // ─────────────────────────────────────────────────────────────────────────────
34
+
35
+ describe('Hook Contracts E2E', () => {
36
+ let fixture: TestFixture;
37
+
38
+ beforeAll(() => {
39
+ fixture = createFixture({
40
+ name: 'hook-contracts-test',
41
+ files: {
42
+ 'src/sample.py': PYTHON_SAMPLE,
43
+ 'src/sample.ts': TYPESCRIPT_SAMPLE,
44
+ 'src/small.py': '# Small file\nx = 1\n',
45
+ '.planning/test/prompts/01.md': `---
46
+ status: pending
47
+ ---
48
+
49
+ # Task 1
50
+ `,
51
+ },
52
+ });
53
+ });
54
+
55
+ afterAll(() => {
56
+ fixture.cleanup();
57
+ });
58
+
59
+ // ───────────────────────────────────────────────────────────────────────────
60
+ // Context Hooks - PreToolUse
61
+ // ───────────────────────────────────────────────────────────────────────────
62
+
63
+ describe('context hooks', () => {
64
+ describe('edit-inject', () => {
65
+ it('allows Edit tool and may inject context for Python files', async () => {
66
+ const result = await runEditInject(
67
+ `${fixture.root}/src/sample.py`,
68
+ fixture
69
+ );
70
+ // Hook should allow the edit
71
+ assertHookAllowed(result);
72
+ });
73
+
74
+ it('allows Edit tool for TypeScript files', async () => {
75
+ const result = await runEditInject(
76
+ `${fixture.root}/src/sample.ts`,
77
+ fixture
78
+ );
79
+ assertHookAllowed(result);
80
+ });
81
+
82
+ it('allows Edit tool for non-existent files', async () => {
83
+ const result = await runEditInject(
84
+ `${fixture.root}/src/new-file.py`,
85
+ fixture
86
+ );
87
+ // Should allow creating new files
88
+ assertHookAllowed(result);
89
+ });
90
+ });
91
+
92
+ describe('search-router', () => {
93
+ it('allows Grep with literal pattern', async () => {
94
+ const result = await runSearchRouter('hello', fixture);
95
+ assertHookAllowed(result);
96
+ });
97
+
98
+ it('allows Grep with regex pattern', async () => {
99
+ const result = await runSearchRouter('def\\s+\\w+', fixture);
100
+ assertHookAllowed(result);
101
+ });
102
+
103
+ it('allows Grep with function search pattern', async () => {
104
+ const result = await runSearchRouter('function add', fixture);
105
+ assertHookAllowed(result);
106
+ });
107
+ });
108
+
109
+ describe('read-enforcer', () => {
110
+ it('allows Read for small files without interception', async () => {
111
+ const result = await runReadEnforcer(
112
+ `${fixture.root}/src/small.py`,
113
+ fixture
114
+ );
115
+ assertHookAllowed(result);
116
+ });
117
+
118
+ it('allows Read with offset/limit (explicit range)', async () => {
119
+ const result = await runReadEnforcer(
120
+ `${fixture.root}/src/sample.py`,
121
+ fixture,
122
+ { offset: 1, limit: 10 }
123
+ );
124
+ // Offset/limit reads are allowed without interception
125
+ assertHookAllowed(result);
126
+ });
127
+
128
+ it('handles Read for non-existent file', async () => {
129
+ const result = await runReadEnforcer(
130
+ `${fixture.root}/src/nonexistent.py`,
131
+ fixture
132
+ );
133
+ // Should allow (file not found error comes later)
134
+ assertHookAllowed(result);
135
+ });
136
+ });
137
+
138
+ describe('tldr-inject', () => {
139
+ it('handles Task tool for debugging intent', async () => {
140
+ const result = await runContextHook(
141
+ 'tldr-inject',
142
+ {
143
+ tool_name: 'Task',
144
+ tool_input: {
145
+ prompt: 'Debug why the function fails',
146
+ description: 'Investigate bug',
147
+ },
148
+ },
149
+ fixture
150
+ );
151
+ // Should allow, may inject context if TLDR available
152
+ assertHookAllowed(result);
153
+ });
154
+
155
+ it('handles Task tool for dataflow intent', async () => {
156
+ const result = await runContextHook(
157
+ 'tldr-inject',
158
+ {
159
+ tool_name: 'Task',
160
+ tool_input: {
161
+ prompt: 'Trace where variable x comes from',
162
+ description: 'Dataflow analysis',
163
+ },
164
+ },
165
+ fixture
166
+ );
167
+ assertHookAllowed(result);
168
+ });
169
+ });
170
+ });
171
+
172
+ // ───────────────────────────────────────────────────────────────────────────
173
+ // Validation Hooks (schema-pre hook)
174
+ // ───────────────────────────────────────────────────────────────────────────
175
+
176
+ describe('validation hooks', () => {
177
+ describe('schema-pre (PreWrite validation)', () => {
178
+ it('runs without crashing on valid prompt', async () => {
179
+ const result = await runHook(
180
+ 'validation',
181
+ 'schema-pre',
182
+ {
183
+ tool_name: 'Write',
184
+ tool_input: {
185
+ file_path: `${fixture.root}/.planning/test/prompts/02.md`,
186
+ content: `---
187
+ number: 2
188
+ title: New Task
189
+ type: planned
190
+ planning_session: 1
191
+ status: pending
192
+ ---
193
+
194
+ # Tasks
195
+
196
+ - New Task
197
+
198
+ # Acceptance Criteria
199
+
200
+ - Works
201
+ `,
202
+ },
203
+ },
204
+ fixture
205
+ );
206
+ // Should complete without crashing
207
+ expect(result.exitCode).toBeDefined();
208
+ });
209
+
210
+ it('runs without crashing on non-schema file', async () => {
211
+ const result = await runHook(
212
+ 'validation',
213
+ 'schema-pre',
214
+ {
215
+ tool_name: 'Write',
216
+ tool_input: {
217
+ file_path: `${fixture.root}/src/new-file.py`,
218
+ content: '# New Python file\n',
219
+ },
220
+ },
221
+ fixture
222
+ );
223
+ expect(result.exitCode).toBeDefined();
224
+ });
225
+ });
226
+ });
227
+
228
+ // ───────────────────────────────────────────────────────────────────────────
229
+ // Contract Testing
230
+ // ───────────────────────────────────────────────────────────────────────────
231
+
232
+ describe('contract compliance', () => {
233
+ const contracts: HookContract[] = [
234
+ {
235
+ name: 'edit-inject allows Python edit',
236
+ hookType: 'context',
237
+ hookName: 'edit-inject',
238
+ input: {
239
+ tool_name: 'Edit',
240
+ tool_input: { file_path: '/tmp/test.py', old_string: '', new_string: '' },
241
+ },
242
+ expect: {
243
+ success: true,
244
+ allowed: true,
245
+ },
246
+ },
247
+ {
248
+ name: 'search-router allows grep',
249
+ hookType: 'context',
250
+ hookName: 'search-router',
251
+ input: {
252
+ tool_name: 'Grep',
253
+ tool_input: { pattern: 'test' },
254
+ },
255
+ expect: {
256
+ success: true,
257
+ allowed: true,
258
+ },
259
+ },
260
+ {
261
+ name: 'read-enforcer allows small file read',
262
+ hookType: 'context',
263
+ hookName: 'read-enforcer',
264
+ input: {
265
+ tool_name: 'Read',
266
+ tool_input: { file_path: '/tmp/small.txt' },
267
+ },
268
+ expect: {
269
+ success: true,
270
+ allowed: true,
271
+ },
272
+ },
273
+ ];
274
+
275
+ it('all core contracts pass', async () => {
276
+ const results = await testHookContracts(contracts, fixture);
277
+ assertContractsPassed(results);
278
+ });
279
+ });
280
+
281
+ // ───────────────────────────────────────────────────────────────────────────
282
+ // Edge Cases
283
+ // ───────────────────────────────────────────────────────────────────────────
284
+
285
+ describe('edge cases', () => {
286
+ it('handles empty hook input gracefully', async () => {
287
+ const result = await runContextHook(
288
+ 'edit-inject',
289
+ {
290
+ tool_name: 'Edit',
291
+ tool_input: {},
292
+ },
293
+ fixture
294
+ );
295
+ // Should not crash
296
+ expect(result.exitCode).toBeDefined();
297
+ });
298
+
299
+ it('handles missing tool_name gracefully', async () => {
300
+ const result = await runContextHook(
301
+ 'edit-inject',
302
+ {
303
+ tool_input: { file_path: '/tmp/test.py' },
304
+ } as any,
305
+ fixture
306
+ );
307
+ expect(result.exitCode).toBeDefined();
308
+ });
309
+
310
+ it('handles very long file paths', async () => {
311
+ const longPath = '/tmp/' + 'a'.repeat(200) + '.py';
312
+ const result = await runEditInject(longPath, fixture);
313
+ expect(result.exitCode).toBeDefined();
314
+ });
315
+
316
+ it('handles unicode in patterns', async () => {
317
+ const result = await runSearchRouter('函数.*定义', fixture);
318
+ expect(result.exitCode).toBeDefined();
319
+ });
320
+ });
321
+ });
322
+
323
+ // ─────────────────────────────────────────────────────────────────────────────
324
+ // Hook Chain Tests
325
+ // ─────────────────────────────────────────────────────────────────────────────
326
+
327
+ describe('Hook Chain E2E', () => {
328
+ let fixture: TestFixture;
329
+
330
+ beforeAll(() => {
331
+ fixture = createMilestoneFixture('hook-chain-test', 2);
332
+ });
333
+
334
+ afterAll(() => {
335
+ fixture.cleanup();
336
+ });
337
+
338
+ describe('search → edit chain', () => {
339
+ it('search-router then edit-inject work in sequence', async () => {
340
+ // First, search for something
341
+ const searchResult = await runSearchRouter('Calculator', fixture);
342
+ assertHookAllowed(searchResult);
343
+
344
+ // Then, edit the file
345
+ const editResult = await runEditInject(
346
+ `${fixture.root}/src/sample.py`,
347
+ fixture
348
+ );
349
+ assertHookAllowed(editResult);
350
+ });
351
+ });
352
+
353
+ describe('read → edit chain', () => {
354
+ it('read-enforcer then edit-inject work in sequence', async () => {
355
+ // Read a file
356
+ const readResult = await runReadEnforcer(
357
+ `${fixture.root}/src/sample.py`,
358
+ fixture
359
+ );
360
+ assertHookAllowed(readResult);
361
+
362
+ // Edit the file
363
+ const editResult = await runEditInject(
364
+ `${fixture.root}/src/sample.py`,
365
+ fixture
366
+ );
367
+ assertHookAllowed(editResult);
368
+ });
369
+ });
370
+ });
371
+
372
+ // ─────────────────────────────────────────────────────────────────────────────
373
+ // Performance Tests
374
+ // ─────────────────────────────────────────────────────────────────────────────
375
+
376
+ describe('Hook Performance E2E', () => {
377
+ let fixture: TestFixture;
378
+
379
+ beforeAll(() => {
380
+ fixture = createFixture({ name: 'hook-perf-test' });
381
+ });
382
+
383
+ afterAll(() => {
384
+ fixture.cleanup();
385
+ });
386
+
387
+ it('edit-inject completes within 3 seconds', async () => {
388
+ const result = await runEditInject(`${fixture.root}/src/test.py`, fixture);
389
+ expect(result.duration).toBeLessThan(3000);
390
+ });
391
+
392
+ it('search-router completes within 2 seconds', async () => {
393
+ const result = await runSearchRouter('test', fixture);
394
+ expect(result.duration).toBeLessThan(2000);
395
+ });
396
+
397
+ it('schema-pre completes within 2 seconds', async () => {
398
+ const result = await runHook(
399
+ 'validation',
400
+ 'schema-pre',
401
+ {
402
+ tool_name: 'Write',
403
+ tool_input: {
404
+ file_path: `${fixture.root}/.planning/test/prompts/01.md`,
405
+ content: `---
406
+ number: 1
407
+ title: Test
408
+ type: planned
409
+ planning_session: 1
410
+ status: pending
411
+ ---
412
+
413
+ # Tasks
414
+
415
+ - Task
416
+
417
+ # Acceptance Criteria
418
+
419
+ - Works
420
+ `,
421
+ },
422
+ },
423
+ fixture
424
+ );
425
+ expect(result.duration).toBeLessThan(2000);
426
+ });
427
+ });
@@ -0,0 +1,137 @@
1
+ /**
2
+ * New Initiative Routing Integration Tests
3
+ *
4
+ * Validates:
5
+ * - Unified scoping flow routing (all spec types → IDEATION_SCOPING.md)
6
+ * - WORKFLOW_DOMAIN_PATH resolution for each spec type
7
+ * - buildActionItems() always-visible guarantee for new-initiative and initiative-steering
8
+ */
9
+
10
+ import { describe, it, expect } from 'vitest';
11
+ import { existsSync } from 'fs';
12
+ import { join, dirname } from 'path';
13
+ import type { SpecType } from '../../lib/specs.js';
14
+ import { UNIFIED_SCOPING_FLOW } from '../../commands/tui.js';
15
+ import { buildActionItems, type ToggleState } from '../../tui/actions.js';
16
+ import { getFlowsDirectory } from '../../lib/flows.js';
17
+
18
+ /** Resolve .allhands/workflows/ relative to .allhands/flows/ */
19
+ const workflowsDir = join(dirname(getFlowsDirectory()), 'workflows');
20
+
21
+ const ALL_SPEC_TYPES: SpecType[] = [
22
+ 'milestone',
23
+ 'investigation',
24
+ 'optimization',
25
+ 'refactor',
26
+ 'documentation',
27
+ 'triage',
28
+ ];
29
+
30
+ // ─── Task 1: Unified scoping flow routing ────────────────────────────────────
31
+
32
+ describe('Unified scoping flow routing', () => {
33
+ it('UNIFIED_SCOPING_FLOW is IDEATION_SCOPING.md', () => {
34
+ expect(UNIFIED_SCOPING_FLOW).toBe('IDEATION_SCOPING.md');
35
+ });
36
+
37
+ it('unified scoping flow file exists on disk', () => {
38
+ const flowsDir = getFlowsDirectory();
39
+ const fullPath = join(flowsDir, UNIFIED_SCOPING_FLOW);
40
+ expect(existsSync(fullPath)).toBe(true);
41
+ });
42
+
43
+ it.each(ALL_SPEC_TYPES)(
44
+ '%s has a corresponding workflow domain config file on disk',
45
+ (specType) => {
46
+ const domainPath = join(workflowsDir, `${specType}.md`);
47
+ expect(existsSync(domainPath)).toBe(true);
48
+ }
49
+ );
50
+ });
51
+
52
+ // ─── Task 2: WORKFLOW_DOMAIN_PATH resolution per spec type ───────────────────
53
+
54
+ describe('WORKFLOW_DOMAIN_PATH resolution', () => {
55
+ it.each(ALL_SPEC_TYPES)(
56
+ '%s workflow domain config exists at .allhands/workflows/%s.md',
57
+ (specType) => {
58
+ const domainPath = join(workflowsDir, `${specType}.md`);
59
+ expect(existsSync(domainPath)).toBe(true);
60
+ }
61
+ );
62
+
63
+ it('initiative-steering action is present in buildActionItems', () => {
64
+ const items = buildActionItems({
65
+ loopEnabled: false,
66
+ parallelEnabled: false,
67
+ prActionState: 'create-pr',
68
+ });
69
+ const steering = items.find((item) => item.id === 'initiative-steering');
70
+ expect(steering).toBeDefined();
71
+ expect(steering!.type).toBe('action');
72
+ expect(steering!.key).toBe('=');
73
+ });
74
+ });
75
+
76
+ // ─── Task 3: buildActionItems() always-visible guarantee ─────────────────────
77
+
78
+ describe('buildActionItems always-visible guarantee', () => {
79
+ const prActionStates = ['create-pr', 'awaiting-review', 'rerun-pr-review'] as const;
80
+
81
+ const toggleCombinations: ToggleState[] = [];
82
+ for (const loop of [true, false]) {
83
+ for (const parallel of [true, false]) {
84
+ for (const pr of prActionStates) {
85
+ toggleCombinations.push({
86
+ loopEnabled: loop,
87
+ parallelEnabled: parallel,
88
+ prActionState: pr,
89
+ });
90
+ }
91
+ }
92
+ }
93
+
94
+ it.each(toggleCombinations)(
95
+ 'new-initiative is present with loop=$loopEnabled, parallel=$parallelEnabled, pr=$prActionState',
96
+ (toggleState) => {
97
+ const items = buildActionItems(toggleState);
98
+ const newInitiative = items.find((item) => item.id === 'new-initiative');
99
+ expect(newInitiative).toBeDefined();
100
+ expect(newInitiative!.type).toBe('action');
101
+ }
102
+ );
103
+
104
+ it.each(toggleCombinations)(
105
+ 'initiative-steering is present with loop=$loopEnabled, parallel=$parallelEnabled, pr=$prActionState',
106
+ (toggleState) => {
107
+ const items = buildActionItems(toggleState);
108
+ const steering = items.find((item) => item.id === 'initiative-steering');
109
+ expect(steering).toBeDefined();
110
+ expect(steering!.type).toBe('action');
111
+ expect(steering!.key).toBe('=');
112
+ }
113
+ );
114
+
115
+ it('no action items have hidden or disabled properties', () => {
116
+ const items = buildActionItems({
117
+ loopEnabled: false,
118
+ parallelEnabled: false,
119
+ prActionState: 'create-pr',
120
+ });
121
+
122
+ for (const item of items) {
123
+ // ActionItem interface at actions.ts:21 has: id, label, key?, type, highlight?, checked?
124
+ // No hidden or disabled fields exist
125
+ expect(item).not.toHaveProperty('hidden');
126
+ expect(item).not.toHaveProperty('disabled');
127
+ }
128
+ });
129
+
130
+ it('returns consistent action count across all toggle states', () => {
131
+ const counts = toggleCombinations.map(
132
+ (ts) => buildActionItems(ts).length
133
+ );
134
+ const uniqueCounts = new Set(counts);
135
+ expect(uniqueCounts.size).toBe(1);
136
+ });
137
+ });
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * E2E Test Runner
4
+ *
5
+ * Standalone script to run E2E tests with proper setup.
6
+ * Can be run directly: npx tsx src/__tests__/e2e/run-e2e.ts
7
+ *
8
+ * Options:
9
+ * --verbose Show detailed output
10
+ * --filter Filter tests by name pattern
11
+ * --list List available test suites without running
12
+ */
13
+
14
+ import { spawn } from 'child_process';
15
+ import { join, dirname } from 'path';
16
+ import { fileURLToPath } from 'url';
17
+ import { readdirSync } from 'fs';
18
+
19
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
+ const harnessRoot = join(__dirname, '..', '..', '..');
21
+
22
+ interface RunOptions {
23
+ verbose: boolean;
24
+ filter?: string;
25
+ list: boolean;
26
+ }
27
+
28
+ function parseArgs(): RunOptions {
29
+ const args = process.argv.slice(2);
30
+ return {
31
+ verbose: args.includes('--verbose') || args.includes('-v'),
32
+ filter: args.find((a) => a.startsWith('--filter='))?.split('=')[1],
33
+ list: args.includes('--list'),
34
+ };
35
+ }
36
+
37
+ function listTestSuites(): void {
38
+ console.log('\nAvailable E2E Test Suites:\n');
39
+
40
+ const e2eDir = __dirname;
41
+ const files = readdirSync(e2eDir).filter((f) => f.endsWith('.test.ts'));
42
+
43
+ for (const file of files) {
44
+ console.log(` • ${file.replace('.test.ts', '')}`);
45
+ }
46
+
47
+ console.log('\nRun with: npm run test:e2e');
48
+ console.log('Or: npx vitest run src/__tests__/e2e/');
49
+ console.log('Filter: npx vitest run src/__tests__/e2e/ -t "pattern"');
50
+ }
51
+
52
+ async function runTests(options: RunOptions): Promise<number> {
53
+ console.log('\n🧪 Running E2E Tests\n');
54
+ console.log('━'.repeat(60));
55
+
56
+ const args = ['vitest', 'run', 'src/__tests__/e2e/'];
57
+
58
+ if (options.verbose) {
59
+ args.push('--reporter=verbose');
60
+ }
61
+
62
+ if (options.filter) {
63
+ args.push('-t', options.filter);
64
+ }
65
+
66
+ return new Promise((resolve) => {
67
+ const child = spawn('npx', args, {
68
+ cwd: harnessRoot,
69
+ stdio: 'inherit',
70
+ env: {
71
+ ...process.env,
72
+ // Ensure consistent output
73
+ FORCE_COLOR: '1',
74
+ },
75
+ });
76
+
77
+ child.on('close', (code) => {
78
+ console.log('\n' + '━'.repeat(60));
79
+ if (code === 0) {
80
+ console.log('āœ… All E2E tests passed\n');
81
+ } else {
82
+ console.log(`āŒ E2E tests failed with code ${code}\n`);
83
+ }
84
+ resolve(code ?? 1);
85
+ });
86
+
87
+ child.on('error', (err) => {
88
+ console.error('Failed to run tests:', err);
89
+ resolve(1);
90
+ });
91
+ });
92
+ }
93
+
94
+ async function main(): Promise<void> {
95
+ const options = parseArgs();
96
+
97
+ if (options.list) {
98
+ listTestSuites();
99
+ process.exit(0);
100
+ }
101
+
102
+ const exitCode = await runTests(options);
103
+ process.exit(exitCode);
104
+ }
105
+
106
+ main().catch((err) => {
107
+ console.error(err);
108
+ process.exit(1);
109
+ });