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,669 @@
1
+ /**
2
+ * E2E Tests - Validation Hook Contracts
3
+ *
4
+ * Integration tests for the validation hook pipeline:
5
+ * schema-pre (PreToolUse) → deny/allow before write
6
+ * schema (PostToolUse) → block/allow after write
7
+ * validation-tools list → CLI command output contract
8
+ *
9
+ * These tests exercise the full hook→schema→response pipeline using the
10
+ * hook-runner test harness, verifying that the actual enforcement mechanism
11
+ * agents encounter behaves correctly.
12
+ */
13
+
14
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
15
+ import {
16
+ createFixture,
17
+ runHook,
18
+ runInFixture,
19
+ testHookContracts,
20
+ assertHookAllowed,
21
+ assertHookDenied,
22
+ assertDenialReasonContains,
23
+ assertContractsPassed,
24
+ assertJsonOutput,
25
+ PROMPT_TEMPLATE,
26
+ type TestFixture,
27
+ type HookContract,
28
+ } from '../harness/index.js';
29
+
30
+ // ─────────────────────────────────────────────────────────────────────────────
31
+ // Fixtures
32
+ // ─────────────────────────────────────────────────────────────────────────────
33
+
34
+ const VALID_PROMPT_CONTENT = PROMPT_TEMPLATE('pending', 'Hook Test Task', 1);
35
+
36
+ const VALID_PROMPT_IN_PROGRESS = PROMPT_TEMPLATE('in_progress', 'Active Task', 2);
37
+
38
+ const INVALID_PROMPT_BAD_STATUS = `---
39
+ number: 1
40
+ title: "Bad Status Task"
41
+ type: planned
42
+ status: garbage_value
43
+ dependencies: []
44
+ ---
45
+
46
+ # Tasks
47
+
48
+ - This has an invalid status
49
+
50
+ # Acceptance Criteria
51
+
52
+ - N/A
53
+ `;
54
+
55
+ const INVALID_PROMPT_MISSING_STATUS = `---
56
+ number: 1
57
+ title: "Missing Status"
58
+ type: planned
59
+ dependencies: []
60
+ ---
61
+
62
+ # Tasks
63
+
64
+ - Missing required status field
65
+
66
+ # Acceptance Criteria
67
+
68
+ - N/A
69
+ `;
70
+
71
+ const INVALID_PROMPT_NO_FRONTMATTER = `# No Frontmatter
72
+
73
+ This file has no YAML frontmatter at all.
74
+ `;
75
+
76
+ // ─────────────────────────────────────────────────────────────────────────────
77
+ // schema-pre (PreToolUse) Hook Contracts
78
+ // ─────────────────────────────────────────────────────────────────────────────
79
+
80
+ describe('Validation Hook Contracts E2E', () => {
81
+ let fixture: TestFixture;
82
+
83
+ beforeAll(() => {
84
+ fixture = createFixture({
85
+ name: 'validation-hook-contracts',
86
+ files: {
87
+ // Seed valid prompt for Edit tests
88
+ '.planning/test/prompts/01.md': PROMPT_TEMPLATE('pending', 'Seeded Task', 1),
89
+ '.planning/test/prompts/02.md': PROMPT_TEMPLATE('in_progress', 'Active Seeded', 2),
90
+ // Non-schema file
91
+ 'src/app.ts': 'export const x = 1;\n',
92
+ },
93
+ });
94
+ });
95
+
96
+ afterAll(() => {
97
+ fixture.cleanup();
98
+ });
99
+
100
+ // ───────────────────────────────────────────────────────────────────────────
101
+ // schema-pre: PreToolUse (deny/allow before write)
102
+ // ───────────────────────────────────────────────────────────────────────────
103
+
104
+ describe('schema-pre hook (PreToolUse)', () => {
105
+ describe('Write tool', () => {
106
+ it('allows valid prompt Write', async () => {
107
+ const result = await runHook(
108
+ 'validation',
109
+ 'schema-pre',
110
+ {
111
+ tool_name: 'Write',
112
+ tool_input: {
113
+ file_path: `${fixture.root}/.planning/test/prompts/new-valid.md`,
114
+ content: VALID_PROMPT_CONTENT,
115
+ },
116
+ },
117
+ fixture
118
+ );
119
+ assertHookAllowed(result);
120
+ });
121
+
122
+ it('denies Write with invalid status enum', async () => {
123
+ const result = await runHook(
124
+ 'validation',
125
+ 'schema-pre',
126
+ {
127
+ tool_name: 'Write',
128
+ tool_input: {
129
+ file_path: `${fixture.root}/.planning/test/prompts/bad-enum.md`,
130
+ content: INVALID_PROMPT_BAD_STATUS,
131
+ },
132
+ },
133
+ fixture
134
+ );
135
+ assertHookDenied(result);
136
+ });
137
+
138
+ it('denies Write with invalid status enum and reason contains Schema Validation', async () => {
139
+ const result = await runHook(
140
+ 'validation',
141
+ 'schema-pre',
142
+ {
143
+ tool_name: 'Write',
144
+ tool_input: {
145
+ file_path: `${fixture.root}/.planning/test/prompts/bad-enum-2.md`,
146
+ content: INVALID_PROMPT_BAD_STATUS,
147
+ },
148
+ },
149
+ fixture
150
+ );
151
+ assertDenialReasonContains(result, 'Schema Validation');
152
+ });
153
+
154
+ it('denies Write missing required status field', async () => {
155
+ const result = await runHook(
156
+ 'validation',
157
+ 'schema-pre',
158
+ {
159
+ tool_name: 'Write',
160
+ tool_input: {
161
+ file_path: `${fixture.root}/.planning/test/prompts/no-status.md`,
162
+ content: INVALID_PROMPT_MISSING_STATUS,
163
+ },
164
+ },
165
+ fixture
166
+ );
167
+ assertHookDenied(result);
168
+ });
169
+
170
+ it('denies Write without frontmatter', async () => {
171
+ const result = await runHook(
172
+ 'validation',
173
+ 'schema-pre',
174
+ {
175
+ tool_name: 'Write',
176
+ tool_input: {
177
+ file_path: `${fixture.root}/.planning/test/prompts/no-fm.md`,
178
+ content: INVALID_PROMPT_NO_FRONTMATTER,
179
+ },
180
+ },
181
+ fixture
182
+ );
183
+ assertHookDenied(result);
184
+ });
185
+
186
+ it('denies Write without frontmatter and reason mentions frontmatter', async () => {
187
+ const result = await runHook(
188
+ 'validation',
189
+ 'schema-pre',
190
+ {
191
+ tool_name: 'Write',
192
+ tool_input: {
193
+ file_path: `${fixture.root}/.planning/test/prompts/no-fm-2.md`,
194
+ content: INVALID_PROMPT_NO_FRONTMATTER,
195
+ },
196
+ },
197
+ fixture
198
+ );
199
+ assertDenialReasonContains(result, 'frontmatter');
200
+ });
201
+
202
+ it('allows Write to non-schema-managed file', async () => {
203
+ const result = await runHook(
204
+ 'validation',
205
+ 'schema-pre',
206
+ {
207
+ tool_name: 'Write',
208
+ tool_input: {
209
+ file_path: `${fixture.root}/src/new-file.ts`,
210
+ content: 'export const y = 2;\n',
211
+ },
212
+ },
213
+ fixture
214
+ );
215
+ assertHookAllowed(result);
216
+ });
217
+
218
+ it('allows when file_path is missing from tool_input', async () => {
219
+ const result = await runHook(
220
+ 'validation',
221
+ 'schema-pre',
222
+ {
223
+ tool_name: 'Write',
224
+ tool_input: {},
225
+ },
226
+ fixture
227
+ );
228
+ assertHookAllowed(result);
229
+ });
230
+ });
231
+
232
+ describe('Edit tool', () => {
233
+ it('allows Edit that produces valid schema result', async () => {
234
+ const result = await runHook(
235
+ 'validation',
236
+ 'schema-pre',
237
+ {
238
+ tool_name: 'Edit',
239
+ tool_input: {
240
+ file_path: `${fixture.root}/.planning/test/prompts/01.md`,
241
+ old_string: 'status: pending',
242
+ new_string: 'status: in_progress',
243
+ },
244
+ },
245
+ fixture
246
+ );
247
+ assertHookAllowed(result);
248
+ });
249
+
250
+ it('denies Edit that breaks schema (invalid status enum)', async () => {
251
+ const result = await runHook(
252
+ 'validation',
253
+ 'schema-pre',
254
+ {
255
+ tool_name: 'Edit',
256
+ tool_input: {
257
+ file_path: `${fixture.root}/.planning/test/prompts/01.md`,
258
+ old_string: 'status: pending',
259
+ new_string: 'status: garbage_value',
260
+ },
261
+ },
262
+ fixture
263
+ );
264
+ assertHookDenied(result);
265
+ });
266
+
267
+ it('allows Edit on nonexistent file (early return)', async () => {
268
+ const result = await runHook(
269
+ 'validation',
270
+ 'schema-pre',
271
+ {
272
+ tool_name: 'Edit',
273
+ tool_input: {
274
+ file_path: `${fixture.root}/.planning/test/prompts/ghost.md`,
275
+ old_string: 'status: pending',
276
+ new_string: 'status: done',
277
+ },
278
+ },
279
+ fixture
280
+ );
281
+ assertHookAllowed(result);
282
+ });
283
+
284
+ it('allows Edit when old_string/new_string missing (early return)', async () => {
285
+ const result = await runHook(
286
+ 'validation',
287
+ 'schema-pre',
288
+ {
289
+ tool_name: 'Edit',
290
+ tool_input: {
291
+ file_path: `${fixture.root}/.planning/test/prompts/01.md`,
292
+ },
293
+ },
294
+ fixture
295
+ );
296
+ assertHookAllowed(result);
297
+ });
298
+
299
+ it('allows Edit with replace_all that produces valid result', async () => {
300
+ const result = await runHook(
301
+ 'validation',
302
+ 'schema-pre',
303
+ {
304
+ tool_name: 'Edit',
305
+ tool_input: {
306
+ file_path: `${fixture.root}/.planning/test/prompts/01.md`,
307
+ old_string: 'pending',
308
+ new_string: 'in_progress',
309
+ replace_all: true,
310
+ },
311
+ },
312
+ fixture
313
+ );
314
+ assertHookAllowed(result);
315
+ });
316
+
317
+ it('allows Edit on non-schema-managed file', async () => {
318
+ const result = await runHook(
319
+ 'validation',
320
+ 'schema-pre',
321
+ {
322
+ tool_name: 'Edit',
323
+ tool_input: {
324
+ file_path: `${fixture.root}/src/app.ts`,
325
+ old_string: 'export const x = 1;',
326
+ new_string: 'export const x = 2;',
327
+ },
328
+ },
329
+ fixture
330
+ );
331
+ assertHookAllowed(result);
332
+ });
333
+ });
334
+ });
335
+
336
+ // ───────────────────────────────────────────────────────────────────────────
337
+ // schema: PostToolUse (block/allow after write)
338
+ // ───────────────────────────────────────────────────────────────────────────
339
+
340
+ describe('schema hook (PostToolUse)', () => {
341
+ it('allows after valid prompt file exists', async () => {
342
+ // Seed valid file
343
+ fixture.writeFile('.planning/post/prompts/valid.md', VALID_PROMPT_CONTENT);
344
+
345
+ const result = await runHook(
346
+ 'validation',
347
+ 'schema',
348
+ {
349
+ tool_name: 'Write',
350
+ tool_input: {
351
+ file_path: `${fixture.root}/.planning/post/prompts/valid.md`,
352
+ },
353
+ },
354
+ fixture
355
+ );
356
+ assertHookAllowed(result);
357
+ });
358
+
359
+ it('allows after valid in_progress prompt file exists', async () => {
360
+ fixture.writeFile('.planning/post/prompts/active.md', VALID_PROMPT_IN_PROGRESS);
361
+
362
+ const result = await runHook(
363
+ 'validation',
364
+ 'schema',
365
+ {
366
+ tool_name: 'Write',
367
+ tool_input: {
368
+ file_path: `${fixture.root}/.planning/post/prompts/active.md`,
369
+ },
370
+ },
371
+ fixture
372
+ );
373
+ assertHookAllowed(result);
374
+ });
375
+
376
+ it('blocks after invalid prompt file exists (bad enum)', async () => {
377
+ fixture.writeFile('.planning/post/prompts/bad-status.md', INVALID_PROMPT_BAD_STATUS);
378
+
379
+ const result = await runHook(
380
+ 'validation',
381
+ 'schema',
382
+ {
383
+ tool_name: 'Write',
384
+ tool_input: {
385
+ file_path: `${fixture.root}/.planning/post/prompts/bad-status.md`,
386
+ },
387
+ },
388
+ fixture
389
+ );
390
+ // blockTool outputs { decision: 'block', reason } format
391
+ const json = result.json as { decision?: string; reason?: string } | undefined;
392
+ expect(json?.decision).toBe('block');
393
+ expect(json?.reason).toContain('Schema Validation');
394
+ });
395
+
396
+ it('blocks after prompt file missing frontmatter', async () => {
397
+ fixture.writeFile('.planning/post/prompts/no-fm.md', INVALID_PROMPT_NO_FRONTMATTER);
398
+
399
+ const result = await runHook(
400
+ 'validation',
401
+ 'schema',
402
+ {
403
+ tool_name: 'Write',
404
+ tool_input: {
405
+ file_path: `${fixture.root}/.planning/post/prompts/no-fm.md`,
406
+ },
407
+ },
408
+ fixture
409
+ );
410
+ // blockTool outputs { decision: 'block', reason } format
411
+ const json = result.json as { decision?: string; reason?: string } | undefined;
412
+ expect(json?.decision).toBe('block');
413
+ expect(json?.reason).toContain('frontmatter');
414
+ });
415
+
416
+ it('allows for non-schema-managed file', async () => {
417
+ const result = await runHook(
418
+ 'validation',
419
+ 'schema',
420
+ {
421
+ tool_name: 'Write',
422
+ tool_input: {
423
+ file_path: `${fixture.root}/src/app.ts`,
424
+ },
425
+ },
426
+ fixture
427
+ );
428
+ assertHookAllowed(result);
429
+ });
430
+
431
+ it('allows when file_path is missing from tool_input', async () => {
432
+ const result = await runHook(
433
+ 'validation',
434
+ 'schema',
435
+ {
436
+ tool_name: 'Write',
437
+ tool_input: {},
438
+ },
439
+ fixture
440
+ );
441
+ assertHookAllowed(result);
442
+ });
443
+ });
444
+
445
+ // ───────────────────────────────────────────────────────────────────────────
446
+ // Declarative Contract Compliance Suite
447
+ // ───────────────────────────────────────────────────────────────────────────
448
+
449
+ describe('validation hook contract compliance', () => {
450
+ let contractFixture: TestFixture;
451
+
452
+ beforeAll(() => {
453
+ contractFixture = createFixture({
454
+ name: 'validation-contracts',
455
+ files: {
456
+ '.planning/c/prompts/01.md': PROMPT_TEMPLATE('pending', 'Contract Task', 1),
457
+ '.planning/c/prompts/bad.md': INVALID_PROMPT_BAD_STATUS,
458
+ },
459
+ });
460
+ });
461
+
462
+ afterAll(() => {
463
+ contractFixture.cleanup();
464
+ });
465
+
466
+ it('all schema-pre contracts pass', async () => {
467
+ const contracts: HookContract[] = [
468
+ {
469
+ name: 'schema-pre allows valid prompt Write',
470
+ hookType: 'validation',
471
+ hookName: 'schema-pre',
472
+ input: {
473
+ tool_name: 'Write',
474
+ tool_input: {
475
+ file_path: `${contractFixture.root}/.planning/c/prompts/new.md`,
476
+ content: VALID_PROMPT_CONTENT,
477
+ },
478
+ },
479
+ expect: {
480
+ success: true,
481
+ allowed: true,
482
+ },
483
+ },
484
+ {
485
+ name: 'schema-pre denies invalid enum Write',
486
+ hookType: 'validation',
487
+ hookName: 'schema-pre',
488
+ input: {
489
+ tool_name: 'Write',
490
+ tool_input: {
491
+ file_path: `${contractFixture.root}/.planning/c/prompts/bad-write.md`,
492
+ content: INVALID_PROMPT_BAD_STATUS,
493
+ },
494
+ },
495
+ expect: {
496
+ denied: true,
497
+ denialReasonContains: 'Schema Validation',
498
+ },
499
+ },
500
+ {
501
+ name: 'schema-pre allows non-schema Write',
502
+ hookType: 'validation',
503
+ hookName: 'schema-pre',
504
+ input: {
505
+ tool_name: 'Write',
506
+ tool_input: {
507
+ file_path: `${contractFixture.root}/src/utils.ts`,
508
+ content: 'export const z = 3;\n',
509
+ },
510
+ },
511
+ expect: {
512
+ success: true,
513
+ allowed: true,
514
+ },
515
+ },
516
+ ];
517
+
518
+ const results = await testHookContracts(contracts, contractFixture);
519
+ assertContractsPassed(results);
520
+ });
521
+
522
+ it('schema allows valid prompt file (PostToolUse)', async () => {
523
+ const result = await runHook(
524
+ 'validation',
525
+ 'schema',
526
+ {
527
+ tool_name: 'Write',
528
+ tool_input: {
529
+ file_path: `${contractFixture.root}/.planning/c/prompts/01.md`,
530
+ },
531
+ },
532
+ contractFixture
533
+ );
534
+ assertHookAllowed(result);
535
+ });
536
+
537
+ it('schema blocks invalid prompt file (PostToolUse)', async () => {
538
+ const result = await runHook(
539
+ 'validation',
540
+ 'schema',
541
+ {
542
+ tool_name: 'Write',
543
+ tool_input: {
544
+ file_path: `${contractFixture.root}/.planning/c/prompts/bad.md`,
545
+ },
546
+ },
547
+ contractFixture
548
+ );
549
+ // blockTool uses { decision: 'block', reason } format
550
+ const json = result.json as { decision?: string } | undefined;
551
+ expect(json?.decision).toBe('block');
552
+ });
553
+ });
554
+ });
555
+
556
+ // ─────────────────────────────────────────────────────────────────────────────
557
+ // validation-tools list Command Output Contract
558
+ // ─────────────────────────────────────────────────────────────────────────────
559
+
560
+ describe('validation-tools list E2E', () => {
561
+ describe('with validation suites present', () => {
562
+ let fixture: TestFixture;
563
+
564
+ beforeAll(() => {
565
+ fixture = createFixture({
566
+ name: 'validation-tools-list-populated',
567
+ copyHarness: true,
568
+ });
569
+ });
570
+
571
+ afterAll(() => {
572
+ fixture.cleanup();
573
+ });
574
+
575
+ it('returns JSON with success and suites array', async () => {
576
+ const result = await runInFixture(
577
+ fixture,
578
+ ['validation-tools', 'list'],
579
+ { expectJson: true }
580
+ );
581
+
582
+ assertJsonOutput(result, (json: { success: boolean; suites: unknown[] }) => {
583
+ return json.success === true && Array.isArray(json.suites);
584
+ });
585
+ });
586
+
587
+ it('suite entries contain required fields', async () => {
588
+ const result = await runInFixture(
589
+ fixture,
590
+ ['validation-tools', 'list'],
591
+ { expectJson: true }
592
+ );
593
+
594
+ if (result.json) {
595
+ const data = result.json as { suites: Record<string, unknown>[] };
596
+ for (const suite of data.suites) {
597
+ expect(suite).toHaveProperty('name');
598
+ expect(suite).toHaveProperty('description');
599
+ expect(suite).toHaveProperty('globs');
600
+ expect(suite).toHaveProperty('tools');
601
+ expect(suite).toHaveProperty('file');
602
+ }
603
+ }
604
+ });
605
+
606
+ it('tools field is a string array on each suite', async () => {
607
+ const result = await runInFixture(
608
+ fixture,
609
+ ['validation-tools', 'list'],
610
+ { expectJson: true }
611
+ );
612
+
613
+ if (result.json) {
614
+ const data = result.json as { suites: { tools: unknown }[] };
615
+ for (const suite of data.suites) {
616
+ expect(Array.isArray(suite.tools)).toBe(true);
617
+ for (const tool of suite.tools as unknown[]) {
618
+ expect(typeof tool).toBe('string');
619
+ }
620
+ }
621
+ }
622
+ });
623
+ });
624
+
625
+ describe('output structure invariants', () => {
626
+ let fixture: TestFixture;
627
+
628
+ beforeAll(() => {
629
+ fixture = createFixture({
630
+ name: 'validation-tools-list-structure',
631
+ copyHarness: true,
632
+ });
633
+ });
634
+
635
+ afterAll(() => {
636
+ fixture.cleanup();
637
+ });
638
+
639
+ it('count field matches suites array length', async () => {
640
+ const result = await runInFixture(
641
+ fixture,
642
+ ['validation-tools', 'list'],
643
+ { expectJson: true }
644
+ );
645
+
646
+ if (result.json) {
647
+ const data = result.json as { suites: unknown[]; count?: number };
648
+ if (data.count !== undefined) {
649
+ expect(data.count).toBe(data.suites.length);
650
+ }
651
+ }
652
+ });
653
+
654
+ it('file field follows .allhands/validation/ path pattern', async () => {
655
+ const result = await runInFixture(
656
+ fixture,
657
+ ['validation-tools', 'list'],
658
+ { expectJson: true }
659
+ );
660
+
661
+ if (result.json) {
662
+ const data = result.json as { suites: { file: string }[] };
663
+ for (const suite of data.suites) {
664
+ expect(suite.file).toMatch(/^\.allhands\/validation\/.*\.md$/);
665
+ }
666
+ }
667
+ });
668
+ });
669
+ });