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,551 @@
1
+ /**
2
+ * Observability Hooks
3
+ *
4
+ * Centralized event tracking for debugging and analysis.
5
+ * All events are logged to SQLite + JSONL via trace-store.
6
+ *
7
+ * Events captured:
8
+ * - session-start: Agent session initialized
9
+ * - prompt-submit: User prompt submitted
10
+ * - tool-pre: Pre-tool use (filtered by inclusion list)
11
+ * - tool-post: Post-tool use (filtered by inclusion list)
12
+ * - task-spawn: Task/subagent spawned (special handling for rich metadata)
13
+ * - agent-stop: Agent stopped
14
+ *
15
+ * Filtering:
16
+ * - Bash commands are filtered to only log valuable commands (tldr, ast-grep, git, etc.)
17
+ * - Bash(ah*) is excluded to avoid recursion
18
+ * - Low-value tools (Glob, Grep, Read) are excluded
19
+ * - Task spawns are always logged with full metadata
20
+ */
21
+
22
+ import type { Command } from 'commander';
23
+ import {
24
+ HookInput,
25
+ HookCategory,
26
+ RegisterFn,
27
+ allowTool,
28
+ registerCategory,
29
+ registerCategoryForDaemon,
30
+ } from './shared.js';
31
+ import { logEvent } from '../lib/trace-store.js';
32
+
33
+ // ─────────────────────────────────────────────────────────────────────────────
34
+ // Filtering Configuration
35
+ // ─────────────────────────────────────────────────────────────────────────────
36
+
37
+ /**
38
+ * Tools to ALWAYS log (high-value orchestration)
39
+ */
40
+ const ALWAYS_LOG_TOOLS = new Set([
41
+ 'Task',
42
+ 'EnterPlanMode',
43
+ 'ExitPlanMode',
44
+ 'AskUserQuestion',
45
+ 'Write',
46
+ 'Edit',
47
+ 'NotebookEdit',
48
+ ]);
49
+
50
+ /**
51
+ * Tools to NEVER log (high-frequency, low-value)
52
+ */
53
+ const NEVER_LOG_TOOLS = new Set([
54
+ 'Glob',
55
+ 'Grep',
56
+ 'Read',
57
+ 'TaskList',
58
+ 'TaskGet',
59
+ 'TaskCreate',
60
+ 'TaskUpdate',
61
+ 'TaskOutput',
62
+ 'TaskStop',
63
+ ]);
64
+
65
+ /**
66
+ * Bash command prefixes to log (valuable for debugging)
67
+ */
68
+ const BASH_LOG_PREFIXES = [
69
+ 'tldr',
70
+ 'ast-grep',
71
+ 'sg', // ast-grep alias
72
+ 'git',
73
+ 'npm',
74
+ 'pnpm',
75
+ 'yarn',
76
+ 'pytest',
77
+ 'vitest',
78
+ 'jest',
79
+ 'docker',
80
+ 'make',
81
+ 'cargo',
82
+ 'go ',
83
+ 'python',
84
+ 'node',
85
+ 'uv',
86
+ 'pip',
87
+ ];
88
+
89
+ /**
90
+ * Bash command prefixes to NEVER log (avoid recursion)
91
+ */
92
+ const BASH_EXCLUDE_PREFIXES = [
93
+ 'ah ',
94
+ 'ah\t',
95
+ 'echo ',
96
+ 'cat ',
97
+ 'ls ',
98
+ 'cd ',
99
+ 'pwd',
100
+ ];
101
+
102
+ // ─────────────────────────────────────────────────────────────────────────────
103
+ // Filtering Logic
104
+ // ─────────────────────────────────────────────────────────────────────────────
105
+
106
+ /**
107
+ * Determine if a tool use should be logged
108
+ */
109
+ function shouldLogTool(toolName: string, toolInput: Record<string, unknown>): boolean {
110
+ // Always log these
111
+ if (ALWAYS_LOG_TOOLS.has(toolName)) {
112
+ return true;
113
+ }
114
+
115
+ // Never log these
116
+ if (NEVER_LOG_TOOLS.has(toolName)) {
117
+ return false;
118
+ }
119
+
120
+ // Special handling for Bash
121
+ if (toolName === 'Bash') {
122
+ const command = String(toolInput.command || '').trim().toLowerCase();
123
+
124
+ // Exclude certain prefixes
125
+ for (const prefix of BASH_EXCLUDE_PREFIXES) {
126
+ if (command.startsWith(prefix.toLowerCase())) {
127
+ return false;
128
+ }
129
+ }
130
+
131
+ // Include if matches valuable prefixes
132
+ for (const prefix of BASH_LOG_PREFIXES) {
133
+ if (command.startsWith(prefix.toLowerCase())) {
134
+ return true;
135
+ }
136
+ }
137
+
138
+ // Default: don't log unknown bash commands
139
+ return false;
140
+ }
141
+
142
+ // Default: log other tools (WebFetch, WebSearch, etc.)
143
+ return true;
144
+ }
145
+
146
+ /**
147
+ * Extract a summary of the bash command for logging
148
+ */
149
+ function summarizeBashCommand(command: string): string {
150
+ // Get first line only
151
+ const firstLine = command.split('\n')[0].trim();
152
+
153
+ // Truncate if too long
154
+ if (firstLine.length > 200) {
155
+ return firstLine.slice(0, 200) + '...';
156
+ }
157
+
158
+ return firstLine;
159
+ }
160
+
161
+ // ─────────────────────────────────────────────────────────────────────────────
162
+ // Hook Handlers
163
+ // ─────────────────────────────────────────────────────────────────────────────
164
+
165
+ /**
166
+ * Handle session start event
167
+ */
168
+ function handleSessionStart(input: HookInput): void {
169
+ logEvent('session.start', {
170
+ session_id: input.session_id,
171
+ transcript_path: input.transcript_path,
172
+ });
173
+
174
+ // Silent exit - don't modify session start
175
+ process.exit(0);
176
+ }
177
+
178
+ /**
179
+ * Handle user prompt submit event
180
+ */
181
+ function handlePromptSubmit(input: HookInput): void {
182
+ // Extract prompt from tool_input if present
183
+ const prompt = input.tool_input?.prompt || input.tool_input?.message || '(no prompt captured)';
184
+
185
+ logEvent('prompt.submit', {
186
+ session_id: input.session_id,
187
+ prompt,
188
+ });
189
+
190
+ // Silent exit
191
+ process.exit(0);
192
+ }
193
+
194
+ /**
195
+ * Handle pre-tool use event (filtered)
196
+ */
197
+ function handleToolPre(input: HookInput): void {
198
+ const toolName = input.tool_name || 'unknown';
199
+ const toolInput = input.tool_input || {};
200
+
201
+ if (!shouldLogTool(toolName, toolInput)) {
202
+ // Silent exit - don't log this tool
203
+ allowTool();
204
+ }
205
+
206
+ // Build payload
207
+ const payload: Record<string, unknown> = {
208
+ session_id: input.session_id,
209
+ };
210
+
211
+ // Add tool-specific info
212
+ if (toolName === 'Bash') {
213
+ payload.command_summary = summarizeBashCommand(String(toolInput.command || ''));
214
+ payload.timeout = toolInput.timeout;
215
+ } else if (toolName === 'Task') {
216
+ // Rich metadata for Task spawns
217
+ payload.subagent_type = toolInput.subagent_type;
218
+ payload.description = toolInput.description;
219
+ payload.prompt_preview = String(toolInput.prompt || '').slice(0, 200);
220
+ payload.model = toolInput.model;
221
+ payload.run_in_background = toolInput.run_in_background;
222
+ } else if (toolName === 'Write' || toolName === 'Edit') {
223
+ payload.file_path = toolInput.file_path;
224
+ } else if (toolName === 'AskUserQuestion') {
225
+ payload.questions = toolInput.questions;
226
+ } else {
227
+ // Generic: include a subset of input keys
228
+ const inputKeys = Object.keys(toolInput).slice(0, 5);
229
+ for (const key of inputKeys) {
230
+ payload[key] = toolInput[key];
231
+ }
232
+ }
233
+
234
+ logEvent('tool.pre', payload, toolName);
235
+
236
+ // Allow the tool
237
+ allowTool();
238
+ }
239
+
240
+ /**
241
+ * Detect if a bash result indicates an error
242
+ */
243
+ function isBashError(toolResult: unknown): { isError: boolean; exitCode?: number; stderr?: string } {
244
+ if (typeof toolResult === 'string') {
245
+ // Check for common error patterns in output
246
+ const lowerResult = toolResult.toLowerCase();
247
+ if (
248
+ lowerResult.includes('error:') ||
249
+ lowerResult.includes('command not found') ||
250
+ lowerResult.includes('no such file or directory') ||
251
+ lowerResult.includes('permission denied') ||
252
+ lowerResult.includes('fatal:') ||
253
+ lowerResult.includes('traceback (most recent call last)') ||
254
+ lowerResult.includes('syntaxerror:') ||
255
+ lowerResult.includes('typeerror:') ||
256
+ lowerResult.includes('exception:')
257
+ ) {
258
+ return { isError: true, stderr: toolResult.slice(0, 500) };
259
+ }
260
+ }
261
+
262
+ // Check if result is an object with error info
263
+ if (typeof toolResult === 'object' && toolResult !== null) {
264
+ const result = toolResult as Record<string, unknown>;
265
+ if (result.exitCode && result.exitCode !== 0) {
266
+ return {
267
+ isError: true,
268
+ exitCode: result.exitCode as number,
269
+ stderr: result.stderr ? String(result.stderr).slice(0, 500) : undefined,
270
+ };
271
+ }
272
+ if (result.error) {
273
+ return { isError: true, stderr: String(result.error).slice(0, 500) };
274
+ }
275
+ }
276
+
277
+ return { isError: false };
278
+ }
279
+
280
+ /**
281
+ * Handle post-tool use event (filtered)
282
+ */
283
+ function handleToolPost(input: HookInput): void {
284
+ const toolName = input.tool_name || 'unknown';
285
+ const toolInput = input.tool_input || {};
286
+
287
+ if (!shouldLogTool(toolName, toolInput)) {
288
+ // Silent exit
289
+ process.exit(0);
290
+ }
291
+
292
+ // Check for bash errors
293
+ if (toolName === 'Bash') {
294
+ const errorCheck = isBashError(input.tool_result);
295
+ if (errorCheck.isError) {
296
+ // Log as bash.error instead of tool.post
297
+ logEvent('bash.error', {
298
+ session_id: input.session_id,
299
+ command_summary: summarizeBashCommand(String(toolInput.command || '')),
300
+ exit_code: errorCheck.exitCode,
301
+ stderr: errorCheck.stderr,
302
+ }, 'Bash');
303
+
304
+ process.exit(0);
305
+ }
306
+ }
307
+
308
+ // Build payload for successful execution
309
+ const payload: Record<string, unknown> = {
310
+ session_id: input.session_id,
311
+ };
312
+
313
+ // Add result summary (truncated)
314
+ if (input.tool_result !== undefined) {
315
+ const resultStr = typeof input.tool_result === 'string'
316
+ ? input.tool_result
317
+ : JSON.stringify(input.tool_result);
318
+
319
+ payload.result_preview = resultStr.slice(0, 300);
320
+ payload.result_length = resultStr.length;
321
+ }
322
+
323
+ // Add tool-specific info
324
+ if (toolName === 'Write' || toolName === 'Edit') {
325
+ payload.file_path = toolInput.file_path;
326
+ } else if (toolName === 'Task') {
327
+ payload.subagent_type = toolInput.subagent_type;
328
+ payload.description = toolInput.description;
329
+ } else if (toolName === 'Bash') {
330
+ payload.command_summary = summarizeBashCommand(String(toolInput.command || ''));
331
+ }
332
+
333
+ logEvent('tool.post', payload, toolName);
334
+
335
+ // Silent exit
336
+ process.exit(0);
337
+ }
338
+
339
+ /**
340
+ * Handle tool failure event (PostToolUseFailure hook)
341
+ * This fires when a tool returns an error result
342
+ */
343
+ function handleToolFailure(input: HookInput): void {
344
+ const toolName = input.tool_name || 'unknown';
345
+ const toolInput = input.tool_input || {};
346
+
347
+ // Log ALL failures (important for debugging)
348
+ const payload: Record<string, unknown> = {
349
+ session_id: input.session_id,
350
+ };
351
+
352
+ // Add tool-specific context
353
+ if (toolName === 'Bash') {
354
+ payload.command_summary = summarizeBashCommand(String(toolInput.command || ''));
355
+ } else if (toolName === 'Write' || toolName === 'Edit' || toolName === 'Read') {
356
+ payload.file_path = toolInput.file_path;
357
+ } else if (toolName === 'Task') {
358
+ payload.subagent_type = toolInput.subagent_type;
359
+ payload.description = toolInput.description;
360
+ } else {
361
+ // For other tools, include a subset of input
362
+ const inputKeys = Object.keys(toolInput).slice(0, 5);
363
+ for (const key of inputKeys) {
364
+ payload[`input_${key}`] = toolInput[key];
365
+ }
366
+ }
367
+
368
+ // Add error from result
369
+ if (input.tool_result !== undefined) {
370
+ const resultStr = typeof input.tool_result === 'string'
371
+ ? input.tool_result
372
+ : JSON.stringify(input.tool_result);
373
+
374
+ payload.error = resultStr.slice(0, 500);
375
+ }
376
+
377
+ logEvent('tool.failure', payload, toolName);
378
+
379
+ // Silent exit
380
+ process.exit(0);
381
+ }
382
+
383
+ /**
384
+ * Handle tool denial event (when a PreToolUse hook denies the tool)
385
+ */
386
+ function handleToolDenied(input: HookInput, reason: string): void {
387
+ const toolName = input.tool_name || 'unknown';
388
+ const toolInput = input.tool_input || {};
389
+
390
+ const payload: Record<string, unknown> = {
391
+ session_id: input.session_id,
392
+ denial_reason: reason,
393
+ };
394
+
395
+ // Add tool-specific context
396
+ if (toolName === 'Bash') {
397
+ payload.command_summary = summarizeBashCommand(String(toolInput.command || ''));
398
+ } else if (toolName === 'Write' || toolName === 'Edit' || toolName === 'Read') {
399
+ payload.file_path = toolInput.file_path;
400
+ } else if (toolName === 'Task') {
401
+ payload.subagent_type = toolInput.subagent_type;
402
+ payload.description = toolInput.description;
403
+ }
404
+
405
+ logEvent('tool.denied', payload, toolName);
406
+ }
407
+
408
+ /**
409
+ * Handle Task spawn event (special - always logged with rich metadata)
410
+ *
411
+ * This is separate from tool-pre because Task spawns are critical
412
+ * for understanding agent orchestration and should capture all metadata.
413
+ */
414
+ function handleTaskSpawn(input: HookInput): void {
415
+ const toolInput = input.tool_input || {};
416
+
417
+ const payload: Record<string, unknown> = {
418
+ session_id: input.session_id,
419
+ subagent_type: toolInput.subagent_type,
420
+ description: toolInput.description,
421
+ prompt: toolInput.prompt, // Full prompt for Task spawns
422
+ model: toolInput.model,
423
+ run_in_background: toolInput.run_in_background,
424
+ allowed_tools: toolInput.allowed_tools,
425
+ max_turns: toolInput.max_turns,
426
+ };
427
+
428
+ logEvent('agent.spawn', payload, 'Task');
429
+
430
+ // Allow the task
431
+ allowTool();
432
+ }
433
+
434
+ /**
435
+ * Handle agent stop event
436
+ */
437
+ function handleAgentStop(input: HookInput): void {
438
+ try {
439
+ logEvent('agent.stop', {
440
+ session_id: input.session_id,
441
+ transcript_path: input.transcript_path,
442
+ stop_hook_active: input.stop_hook_active,
443
+ });
444
+ } catch {
445
+ // Silent failure - don't break the stop hook
446
+ }
447
+
448
+ // Let the lifecycle hook handle the actual stop
449
+ process.exit(0);
450
+ }
451
+
452
+ /**
453
+ * Handle agent compact event
454
+ */
455
+ function handleAgentCompact(input: HookInput): void {
456
+ try {
457
+ logEvent('agent.compact', {
458
+ session_id: input.session_id,
459
+ transcript_path: input.transcript_path,
460
+ });
461
+ } catch {
462
+ // Silent failure - don't break the compact hook
463
+ }
464
+
465
+ // Let the lifecycle hook handle the actual compaction
466
+ process.exit(0);
467
+ }
468
+
469
+ /**
470
+ * Handle pre-tool use (combined handler with Task routing)
471
+ */
472
+ function handleToolPreWithTaskRouting(input: HookInput): void {
473
+ if (input.tool_name === 'Task') {
474
+ handleTaskSpawn(input);
475
+ } else {
476
+ handleToolPre(input);
477
+ }
478
+ }
479
+
480
+ // ─────────────────────────────────────────────────────────────────────────────
481
+ // Hook Category Definition
482
+ // ─────────────────────────────────────────────────────────────────────────────
483
+
484
+ /** Observability hooks category */
485
+ export const category: HookCategory = {
486
+ name: 'observability',
487
+ description: 'Observability hooks for event tracking',
488
+ hooks: [
489
+ {
490
+ name: 'session-start',
491
+ description: 'Log session start event',
492
+ handler: handleSessionStart,
493
+ errorFallback: { type: 'silent' },
494
+ },
495
+ {
496
+ name: 'prompt-submit',
497
+ description: 'Log user prompt submit event',
498
+ handler: handlePromptSubmit,
499
+ errorFallback: { type: 'silent' },
500
+ },
501
+ {
502
+ name: 'tool-pre',
503
+ description: 'Log pre-tool use event (filtered)',
504
+ handler: handleToolPreWithTaskRouting,
505
+ errorFallback: { type: 'silent' },
506
+ },
507
+ {
508
+ name: 'tool-post',
509
+ description: 'Log post-tool use event (filtered)',
510
+ handler: handleToolPost,
511
+ errorFallback: { type: 'silent' },
512
+ },
513
+ {
514
+ name: 'tool-failure',
515
+ description: 'Log tool failure event',
516
+ handler: handleToolFailure,
517
+ errorFallback: { type: 'silent' },
518
+ },
519
+ {
520
+ name: 'agent-stop',
521
+ description: 'Log agent stop event',
522
+ handler: handleAgentStop,
523
+ errorFallback: { type: 'silent' },
524
+ },
525
+ {
526
+ name: 'agent-compact',
527
+ description: 'Log agent compact event',
528
+ handler: handleAgentCompact,
529
+ errorFallback: { type: 'silent' },
530
+ },
531
+ ],
532
+ };
533
+
534
+ // ─────────────────────────────────────────────────────────────────────────────
535
+ // Command Registration
536
+ // ─────────────────────────────────────────────────────────────────────────────
537
+
538
+ export function register(parent: Command): void {
539
+ registerCategory(parent, category);
540
+ }
541
+
542
+ // ─────────────────────────────────────────────────────────────────────────────
543
+ // Daemon Handler Registration
544
+ // ─────────────────────────────────────────────────────────────────────────────
545
+
546
+ /**
547
+ * Register handlers for daemon mode.
548
+ */
549
+ export function registerDaemonHandlers(register: RegisterFn): void {
550
+ registerCategoryForDaemon(category, register);
551
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Session Hooks - Session lifecycle management
3
+ *
4
+ * Hooks for session start/resume events:
5
+ * - TLDR cache warming (async, non-blocking)
6
+ */
7
+
8
+ import type { Command } from 'commander';
9
+ import { readHookInput, getProjectDir } from './shared.js';
10
+ import { isTldrInstalled, isTldrDaemonRunning, warmIndex } from '../lib/tldr.js';
11
+ import { logHookStart, logHookSuccess } from '../lib/trace-store.js';
12
+
13
+ const HOOK_TLDR_WARM = 'session tldr-warm';
14
+
15
+ // ─────────────────────────────────────────────────────────────────────────────
16
+ // SessionStart: tldr-warm
17
+ // ─────────────────────────────────────────────────────────────────────────────
18
+
19
+ /**
20
+ * Warm TLDR cache on session start/resume.
21
+ *
22
+ * This hook runs asynchronously and non-blocking:
23
+ * - If TLDR is not installed, exits silently
24
+ * - If daemon is already running, exits silently
25
+ * - Otherwise, starts daemon in background
26
+ *
27
+ * Triggered by: SessionStart matcher "*"
28
+ */
29
+ async function tldrWarm(): Promise<void> {
30
+ const projectDir = getProjectDir();
31
+
32
+ // Skip if TLDR not installed
33
+ if (!isTldrInstalled()) {
34
+ logHookSuccess(HOOK_TLDR_WARM, { action: 'skip', reason: 'not_installed' });
35
+ console.log('{}');
36
+ process.exit(0);
37
+ }
38
+
39
+ // Skip if daemon already running
40
+ if (isTldrDaemonRunning(projectDir)) {
41
+ logHookSuccess(HOOK_TLDR_WARM, { action: 'skip', reason: 'daemon_running' });
42
+ console.log('{}');
43
+ process.exit(0);
44
+ }
45
+
46
+ // Start warming in background (non-blocking)
47
+ try {
48
+ await warmIndex(projectDir);
49
+ logHookSuccess(HOOK_TLDR_WARM, { action: 'warmed' });
50
+ } catch {
51
+ // Ignore errors - best effort warming
52
+ logHookSuccess(HOOK_TLDR_WARM, { action: 'skip', reason: 'error' });
53
+ }
54
+
55
+ // Always succeed - don't block session start
56
+ console.log('{}');
57
+ process.exit(0);
58
+ }
59
+
60
+ // ─────────────────────────────────────────────────────────────────────────────
61
+ // Command Registration
62
+ // ─────────────────────────────────────────────────────────────────────────────
63
+
64
+ /**
65
+ * Register session hook subcommands.
66
+ */
67
+ export function register(parent: Command): void {
68
+ const session = parent
69
+ .command('session')
70
+ .description('Session lifecycle hooks');
71
+
72
+ session
73
+ .command('tldr-warm')
74
+ .description('Warm TLDR cache on session start (SessionStart)')
75
+ .action(async () => {
76
+ try {
77
+ // Read input but don't require it
78
+ await readHookInput().catch(() => ({}));
79
+ logHookStart(HOOK_TLDR_WARM, {});
80
+ await tldrWarm();
81
+ } catch {
82
+ // On any error, exit cleanly
83
+ logHookSuccess(HOOK_TLDR_WARM, { action: 'skip', reason: 'error' });
84
+ console.log('{}');
85
+ process.exit(0);
86
+ }
87
+ });
88
+ }