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,714 @@
1
+ /**
2
+ * Trace Store - Dual storage for observability events
3
+ *
4
+ * Provides:
5
+ * - SQLite database for structured queries
6
+ * - JSONL file for greppable backup
7
+ * - Payload trimming to prevent log bloat
8
+ *
9
+ * All events include agent context from environment variables:
10
+ * - AGENT_ID, AGENT_TYPE, PROMPT_NUMBER, SPEC_NAME, BRANCH
11
+ */
12
+
13
+ import { mkdirSync, appendFileSync, existsSync, writeFileSync } from 'fs';
14
+ import { join, dirname } from 'path';
15
+ import { createRequire } from 'module';
16
+ import type BetterSqlite3 from 'better-sqlite3';
17
+ const require = createRequire(import.meta.url);
18
+ const Database = require('better-sqlite3') as typeof BetterSqlite3;
19
+
20
+ // ============================================================================
21
+ // Configuration (env-configurable)
22
+ // ============================================================================
23
+
24
+ const MAX_STRING_LENGTH = parseInt(process.env.TRACE_MAX_STRING_LENGTH ?? '200', 10);
25
+ const MAX_DEPTH = parseInt(process.env.TRACE_MAX_DEPTH ?? '3', 10);
26
+ const MAX_ARRAY_ITEMS = parseInt(process.env.TRACE_MAX_ARRAY_ITEMS ?? '5', 10);
27
+ const MAX_OBJECT_KEYS = parseInt(process.env.TRACE_MAX_OBJECT_KEYS ?? '8', 10);
28
+
29
+ // ============================================================================
30
+ // Types
31
+ // ============================================================================
32
+
33
+ export type TraceEventType =
34
+ | 'session.start'
35
+ | 'session.end'
36
+ | 'prompt.submit'
37
+ | 'tool.pre'
38
+ | 'tool.post'
39
+ | 'tool.failure' // Tool returned an error result
40
+ | 'tool.denied' // Hook denied the tool use
41
+ | 'bash.error' // Bash command non-zero exit
42
+ | 'hook.start' // Hook execution started
43
+ | 'hook.success' // Hook execution succeeded
44
+ | 'hook.error' // Hook execution failed
45
+ | 'harness.error' // Internal harness error
46
+ | 'tui.action' // TUI user action
47
+ | 'tui.lifecycle' // TUI lifecycle event
48
+ | 'tui.error' // TUI runtime error
49
+ | 'command.start' // CLI command started
50
+ | 'command.success' // CLI command succeeded
51
+ | 'command.error' // CLI command failed
52
+ | 'agent.spawn'
53
+ | 'agent.stop'
54
+ | 'agent.compact';
55
+
56
+ /** Error event types for filtering */
57
+ export const ERROR_EVENT_TYPES: TraceEventType[] = [
58
+ 'tool.failure',
59
+ 'tool.denied',
60
+ 'bash.error',
61
+ 'hook.error',
62
+ 'harness.error',
63
+ 'tui.error',
64
+ 'command.error',
65
+ ];
66
+
67
+ export interface TraceEvent {
68
+ id?: number;
69
+ timestamp: string;
70
+ eventType: TraceEventType;
71
+ agentId: string | null;
72
+ agentType: string | null;
73
+ promptNumber: string | null;
74
+ specName: string | null;
75
+ branch: string | null;
76
+ toolName: string | null;
77
+ viaDaemon: boolean; // true if executed via CLI daemon, false if via tsx
78
+ payload: Record<string, unknown>;
79
+ }
80
+
81
+ export interface TraceQueryOptions {
82
+ agentId?: string;
83
+ agentType?: string;
84
+ eventType?: TraceEventType;
85
+ toolName?: string;
86
+ since?: string; // ISO timestamp or relative like '1h', '30m'
87
+ limit?: number;
88
+ offset?: number;
89
+ errorsOnly?: boolean; // Only return error events
90
+ }
91
+
92
+ export interface TraceStats {
93
+ totalEvents: number;
94
+ totalErrors: number;
95
+ byEventType: Record<string, number>;
96
+ byAgentType: Record<string, number>;
97
+ byToolName: Record<string, number>;
98
+ }
99
+
100
+ // ============================================================================
101
+ // Trimming Logic (similar to envoy observability)
102
+ // ============================================================================
103
+
104
+ /**
105
+ * Truncate strings that exceed MAX_STRING_LENGTH
106
+ */
107
+ function trimStrings(obj: unknown, seen = new WeakSet()): unknown {
108
+ if (obj === null || obj === undefined) return obj;
109
+
110
+ if (typeof obj === 'string') {
111
+ if (obj.length > MAX_STRING_LENGTH) {
112
+ return obj.slice(0, MAX_STRING_LENGTH) + '...';
113
+ }
114
+ return obj;
115
+ }
116
+
117
+ if (typeof obj !== 'object') return obj;
118
+
119
+ // Circular reference protection
120
+ if (seen.has(obj as object)) return '[Circular]';
121
+ seen.add(obj as object);
122
+
123
+ if (Array.isArray(obj)) {
124
+ return obj.map(item => trimStrings(item, seen));
125
+ }
126
+
127
+ const result: Record<string, unknown> = {};
128
+ for (const [key, value] of Object.entries(obj)) {
129
+ result[key] = trimStrings(value, seen);
130
+ }
131
+ return result;
132
+ }
133
+
134
+ /**
135
+ * Truncate structure depth and breadth
136
+ */
137
+ function truncateStructure(obj: unknown, depth = 0, seen = new WeakSet()): unknown {
138
+ if (obj === null || obj === undefined) return obj;
139
+ if (typeof obj !== 'object') return obj;
140
+
141
+ // Circular reference protection
142
+ if (seen.has(obj as object)) return '[Circular]';
143
+ seen.add(obj as object);
144
+
145
+ if (depth >= MAX_DEPTH) {
146
+ if (Array.isArray(obj)) {
147
+ return `[Array(${obj.length})]`;
148
+ }
149
+ return `[Object(${Object.keys(obj).length} keys)]`;
150
+ }
151
+
152
+ if (Array.isArray(obj)) {
153
+ const truncated = obj.slice(0, MAX_ARRAY_ITEMS).map(
154
+ item => truncateStructure(item, depth + 1, seen)
155
+ );
156
+ if (obj.length > MAX_ARRAY_ITEMS) {
157
+ truncated.push(`... +${obj.length - MAX_ARRAY_ITEMS} more`);
158
+ }
159
+ return truncated;
160
+ }
161
+
162
+ const keys = Object.keys(obj);
163
+ const result: Record<string, unknown> = {};
164
+ const selectedKeys = keys.slice(0, MAX_OBJECT_KEYS);
165
+
166
+ for (const key of selectedKeys) {
167
+ result[key] = truncateStructure((obj as Record<string, unknown>)[key], depth + 1, seen);
168
+ }
169
+
170
+ if (keys.length > MAX_OBJECT_KEYS) {
171
+ result['...'] = `+${keys.length - MAX_OBJECT_KEYS} more keys`;
172
+ }
173
+
174
+ return result;
175
+ }
176
+
177
+ /**
178
+ * Sanitize payload for logging
179
+ */
180
+ export function sanitizePayload(payload: unknown): Record<string, unknown> {
181
+ const truncated = truncateStructure(payload);
182
+ const trimmed = trimStrings(truncated);
183
+ return (trimmed as Record<string, unknown>) ?? {};
184
+ }
185
+
186
+ // ============================================================================
187
+ // Agent Context
188
+ // ============================================================================
189
+
190
+ /**
191
+ * Get agent context from environment variables
192
+ */
193
+ export function getAgentContext(): Pick<TraceEvent, 'agentId' | 'agentType' | 'promptNumber' | 'specName' | 'branch' | 'viaDaemon'> {
194
+ return {
195
+ agentId: process.env.AGENT_ID || null,
196
+ agentType: process.env.AGENT_TYPE || null,
197
+ promptNumber: process.env.PROMPT_NUMBER || null,
198
+ specName: process.env.SPEC_NAME || null,
199
+ branch: process.env.BRANCH || null,
200
+ viaDaemon: process.env.AH_VIA_DAEMON === '1',
201
+ };
202
+ }
203
+
204
+ // ============================================================================
205
+ // Storage Paths
206
+ // ============================================================================
207
+
208
+ /**
209
+ * Get the project root directory.
210
+ * Uses CLAUDE_PROJECT_DIR if set (by Claude Code), otherwise falls back to cwd.
211
+ */
212
+ function getProjectRoot(): string {
213
+ return process.env.CLAUDE_PROJECT_DIR || process.cwd();
214
+ }
215
+
216
+ function getStoragePaths(cwd?: string): { dbPath: string; jsonlPath: string } {
217
+ const base = cwd || getProjectRoot();
218
+ const cacheDir = join(base, '.allhands', 'harness', '.cache', 'trace');
219
+
220
+ return {
221
+ dbPath: join(cacheDir, 'trace.db'),
222
+ jsonlPath: join(cacheDir, 'trace.jsonl'),
223
+ };
224
+ }
225
+
226
+ // ============================================================================
227
+ // SQLite Database
228
+ // ============================================================================
229
+
230
+ // Cache databases by path to support multiple projects in same process
231
+ const dbCache = new Map<string, BetterSqlite3.Database>();
232
+
233
+ function getDb(cwd?: string): BetterSqlite3.Database {
234
+ const { dbPath } = getStoragePaths(cwd);
235
+
236
+ // Return cached connection for this path
237
+ const cached = dbCache.get(dbPath);
238
+ if (cached) return cached;
239
+
240
+ mkdirSync(dirname(dbPath), { recursive: true });
241
+
242
+ const db = new Database(dbPath);
243
+
244
+ // Create tables
245
+ db.exec(`
246
+ CREATE TABLE IF NOT EXISTS events (
247
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
248
+ timestamp TEXT NOT NULL,
249
+ event_type TEXT NOT NULL,
250
+ agent_id TEXT,
251
+ agent_type TEXT,
252
+ prompt_number TEXT,
253
+ spec_name TEXT,
254
+ branch TEXT,
255
+ tool_name TEXT,
256
+ is_error INTEGER DEFAULT 0,
257
+ via_daemon INTEGER DEFAULT 0,
258
+ payload TEXT NOT NULL
259
+ );
260
+
261
+ CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
262
+ CREATE INDEX IF NOT EXISTS idx_events_event_type ON events(event_type);
263
+ CREATE INDEX IF NOT EXISTS idx_events_agent_type ON events(agent_type);
264
+ CREATE INDEX IF NOT EXISTS idx_events_tool_name ON events(tool_name);
265
+ CREATE INDEX IF NOT EXISTS idx_events_is_error ON events(is_error);
266
+ `);
267
+
268
+ dbCache.set(dbPath, db);
269
+ return db;
270
+ }
271
+
272
+ // ============================================================================
273
+ // Write Operations
274
+ // ============================================================================
275
+
276
+ /**
277
+ * Check if an event type is an error type
278
+ */
279
+ function isErrorEvent(eventType: TraceEventType): boolean {
280
+ return ERROR_EVENT_TYPES.includes(eventType);
281
+ }
282
+
283
+ /**
284
+ * Log a trace event to both SQLite and JSONL
285
+ */
286
+ export function logEvent(
287
+ eventType: TraceEventType,
288
+ payload: Record<string, unknown>,
289
+ toolName?: string,
290
+ cwd?: string
291
+ ): void {
292
+ const context = getAgentContext();
293
+ const timestamp = new Date().toISOString();
294
+ const sanitizedPayload = sanitizePayload(payload);
295
+ const isError = isErrorEvent(eventType) ? 1 : 0;
296
+
297
+ const event = {
298
+ timestamp,
299
+ eventType,
300
+ ...context,
301
+ toolName: toolName || null,
302
+ isError: isError === 1,
303
+ payload: sanitizedPayload,
304
+ };
305
+
306
+ // Write to SQLite
307
+ try {
308
+ const database = getDb(cwd);
309
+ const stmt = database.prepare(`
310
+ INSERT INTO events (timestamp, event_type, agent_id, agent_type, prompt_number, spec_name, branch, tool_name, is_error, via_daemon, payload)
311
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
312
+ `);
313
+ stmt.run(
314
+ event.timestamp,
315
+ event.eventType,
316
+ event.agentId,
317
+ event.agentType,
318
+ event.promptNumber,
319
+ event.specName,
320
+ event.branch,
321
+ event.toolName,
322
+ isError,
323
+ event.viaDaemon ? 1 : 0,
324
+ JSON.stringify(event.payload)
325
+ );
326
+ } catch (err) {
327
+ // Silent failure - don't break the hook
328
+ console.error(`[trace-store] SQLite write error: ${err}`);
329
+ }
330
+
331
+ // Write to JSONL
332
+ try {
333
+ const { jsonlPath } = getStoragePaths(cwd);
334
+ mkdirSync(dirname(jsonlPath), { recursive: true });
335
+ appendFileSync(jsonlPath, JSON.stringify(event) + '\n');
336
+ } catch (err) {
337
+ // Silent failure
338
+ console.error(`[trace-store] JSONL write error: ${err}`);
339
+ }
340
+ }
341
+
342
+ /**
343
+ * Log a harness internal error
344
+ * Use this for errors that occur within the harness itself (not tool failures)
345
+ */
346
+ export function logHarnessError(
347
+ error: Error | string,
348
+ context: Record<string, unknown> = {},
349
+ cwd?: string
350
+ ): void {
351
+ const errorMessage = error instanceof Error ? error.message : error;
352
+ const errorStack = error instanceof Error ? error.stack : undefined;
353
+
354
+ logEvent('harness.error', {
355
+ error: errorMessage,
356
+ stack: errorStack,
357
+ ...context,
358
+ }, undefined, cwd);
359
+ }
360
+
361
+ /**
362
+ * Log a hook execution error
363
+ */
364
+ export function logHookError(
365
+ hookName: string,
366
+ error: Error | string,
367
+ input?: Record<string, unknown>,
368
+ cwd?: string
369
+ ): void {
370
+ const errorMessage = error instanceof Error ? error.message : error;
371
+
372
+ logEvent('hook.error', {
373
+ hook: hookName,
374
+ error: errorMessage,
375
+ input: input ? sanitizePayload(input) : undefined,
376
+ }, undefined, cwd);
377
+ }
378
+
379
+ /**
380
+ * Log a TUI runtime error
381
+ * Use this for errors that occur within the TUI (e.g., rendering, modal handling, agent spawning)
382
+ */
383
+ export function logTuiError(
384
+ component: string,
385
+ error: Error | string,
386
+ context: Record<string, unknown> = {},
387
+ cwd?: string
388
+ ): void {
389
+ const errorMessage = error instanceof Error ? error.message : error;
390
+ const errorStack = error instanceof Error ? error.stack : undefined;
391
+
392
+ logEvent('tui.error', {
393
+ component,
394
+ error: errorMessage,
395
+ stack: errorStack,
396
+ ...context,
397
+ }, undefined, cwd);
398
+ }
399
+
400
+ /**
401
+ * Log CLI command start
402
+ * Use this at the beginning of important CLI commands (specs, prompts, etc.)
403
+ */
404
+ export function logCommandStart(
405
+ command: string,
406
+ args: Record<string, unknown> = {},
407
+ cwd?: string
408
+ ): void {
409
+ logEvent('command.start', {
410
+ command,
411
+ args: sanitizePayload(args),
412
+ }, undefined, cwd);
413
+ }
414
+
415
+ /**
416
+ * Log CLI command success
417
+ * Use this when a CLI command completes successfully
418
+ */
419
+ export function logCommandSuccess(
420
+ command: string,
421
+ result: Record<string, unknown> = {},
422
+ cwd?: string
423
+ ): void {
424
+ logEvent('command.success', {
425
+ command,
426
+ result: sanitizePayload(result),
427
+ }, undefined, cwd);
428
+ }
429
+
430
+ /**
431
+ * Log CLI command error
432
+ * Use this when a CLI command fails
433
+ */
434
+ export function logCommandError(
435
+ command: string,
436
+ error: Error | string,
437
+ args: Record<string, unknown> = {},
438
+ cwd?: string
439
+ ): void {
440
+ const errorMessage = error instanceof Error ? error.message : error;
441
+ const errorStack = error instanceof Error ? error.stack : undefined;
442
+
443
+ logEvent('command.error', {
444
+ command,
445
+ error: errorMessage,
446
+ stack: errorStack,
447
+ args: sanitizePayload(args),
448
+ }, undefined, cwd);
449
+ }
450
+
451
+ // ============================================================================
452
+ // Hook Logging
453
+ // ============================================================================
454
+
455
+ /**
456
+ * Log hook execution start
457
+ * Use this at the beginning of hook handlers
458
+ */
459
+ export function logHookStart(
460
+ hookName: string,
461
+ input: Record<string, unknown> = {},
462
+ cwd?: string
463
+ ): void {
464
+ logEvent('hook.start', {
465
+ hook: hookName,
466
+ input: sanitizePayload(input),
467
+ }, undefined, cwd);
468
+ }
469
+
470
+ /**
471
+ * Log hook execution success
472
+ * Use this when a hook completes successfully
473
+ */
474
+ export function logHookSuccess(
475
+ hookName: string,
476
+ result: Record<string, unknown> = {},
477
+ cwd?: string
478
+ ): void {
479
+ logEvent('hook.success', {
480
+ hook: hookName,
481
+ result: sanitizePayload(result),
482
+ }, undefined, cwd);
483
+ }
484
+
485
+ // ============================================================================
486
+ // TUI Logging
487
+ // ============================================================================
488
+
489
+ /**
490
+ * Log TUI user action
491
+ * Use this when user performs an action in the TUI
492
+ */
493
+ export function logTuiAction(
494
+ action: string,
495
+ data: Record<string, unknown> = {},
496
+ cwd?: string
497
+ ): void {
498
+ logEvent('tui.action', {
499
+ action,
500
+ ...sanitizePayload(data),
501
+ }, undefined, cwd);
502
+ }
503
+
504
+ /**
505
+ * Log TUI lifecycle event
506
+ * Use this for TUI state changes and lifecycle events
507
+ */
508
+ export function logTuiLifecycle(
509
+ event: string,
510
+ data: Record<string, unknown> = {},
511
+ cwd?: string
512
+ ): void {
513
+ logEvent('tui.lifecycle', {
514
+ event,
515
+ ...sanitizePayload(data),
516
+ }, undefined, cwd);
517
+ }
518
+
519
+ // ============================================================================
520
+ // Read Operations
521
+ // ============================================================================
522
+
523
+ /**
524
+ * Parse relative time strings like '1h', '30m', '2d'
525
+ */
526
+ function parseRelativeTime(input: string): Date {
527
+ const now = new Date();
528
+ const match = input.match(/^(\d+)([smhd])$/);
529
+
530
+ if (!match) {
531
+ // Try parsing as ISO timestamp
532
+ const date = new Date(input);
533
+ if (!isNaN(date.getTime())) return date;
534
+ throw new Error(`Invalid time format: ${input}`);
535
+ }
536
+
537
+ const [, num, unit] = match;
538
+ const value = parseInt(num, 10);
539
+
540
+ switch (unit) {
541
+ case 's': return new Date(now.getTime() - value * 1000);
542
+ case 'm': return new Date(now.getTime() - value * 60 * 1000);
543
+ case 'h': return new Date(now.getTime() - value * 60 * 60 * 1000);
544
+ case 'd': return new Date(now.getTime() - value * 24 * 60 * 60 * 1000);
545
+ default: throw new Error(`Invalid time unit: ${unit}`);
546
+ }
547
+ }
548
+
549
+ /**
550
+ * Query events with filters
551
+ */
552
+ export function queryEvents(options: TraceQueryOptions = {}, cwd?: string): (TraceEvent & { isError?: boolean })[] {
553
+ const database = getDb(cwd);
554
+
555
+ const conditions: string[] = [];
556
+ const params: unknown[] = [];
557
+
558
+ if (options.agentId) {
559
+ conditions.push('agent_id = ?');
560
+ params.push(options.agentId);
561
+ }
562
+
563
+ if (options.agentType) {
564
+ conditions.push('agent_type = ?');
565
+ params.push(options.agentType);
566
+ }
567
+
568
+ if (options.eventType) {
569
+ conditions.push('event_type = ?');
570
+ params.push(options.eventType);
571
+ }
572
+
573
+ if (options.toolName) {
574
+ conditions.push('tool_name = ?');
575
+ params.push(options.toolName);
576
+ }
577
+
578
+ if (options.since) {
579
+ const sinceDate = parseRelativeTime(options.since);
580
+ conditions.push('timestamp >= ?');
581
+ params.push(sinceDate.toISOString());
582
+ }
583
+
584
+ if (options.errorsOnly) {
585
+ conditions.push('is_error = 1');
586
+ }
587
+
588
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
589
+ const limit = options.limit ?? 100;
590
+ const offset = options.offset ?? 0;
591
+
592
+ const sql = `
593
+ SELECT * FROM events
594
+ ${whereClause}
595
+ ORDER BY timestamp DESC
596
+ LIMIT ? OFFSET ?
597
+ `;
598
+
599
+ params.push(limit, offset);
600
+
601
+ const rows = database.prepare(sql).all(...params) as Array<{
602
+ id: number;
603
+ timestamp: string;
604
+ event_type: string;
605
+ agent_id: string | null;
606
+ agent_type: string | null;
607
+ prompt_number: string | null;
608
+ spec_name: string | null;
609
+ branch: string | null;
610
+ tool_name: string | null;
611
+ is_error: number;
612
+ via_daemon: number;
613
+ payload: string;
614
+ }>;
615
+
616
+ return rows.map(row => ({
617
+ id: row.id,
618
+ timestamp: row.timestamp,
619
+ eventType: row.event_type as TraceEventType,
620
+ agentId: row.agent_id,
621
+ agentType: row.agent_type,
622
+ promptNumber: row.prompt_number,
623
+ specName: row.spec_name,
624
+ branch: row.branch,
625
+ toolName: row.tool_name,
626
+ isError: row.is_error === 1,
627
+ viaDaemon: row.via_daemon === 1,
628
+ payload: JSON.parse(row.payload),
629
+ }));
630
+ }
631
+
632
+ /**
633
+ * Get aggregate statistics
634
+ */
635
+ export function getStats(since?: string, cwd?: string): TraceStats {
636
+ const database = getDb(cwd);
637
+
638
+ let whereClause = '';
639
+ const params: unknown[] = [];
640
+
641
+ if (since) {
642
+ const sinceDate = parseRelativeTime(since);
643
+ whereClause = 'WHERE timestamp >= ?';
644
+ params.push(sinceDate.toISOString());
645
+ }
646
+
647
+ // Total count
648
+ const totalRow = database.prepare(`SELECT COUNT(*) as count FROM events ${whereClause}`).get(...params) as { count: number };
649
+
650
+ // Error count
651
+ const errorWhereClause = whereClause ? `${whereClause} AND is_error = 1` : 'WHERE is_error = 1';
652
+ const errorRow = database.prepare(`SELECT COUNT(*) as count FROM events ${errorWhereClause}`).get(...params) as { count: number };
653
+
654
+ // By event type
655
+ const eventTypeRows = database.prepare(`
656
+ SELECT event_type, COUNT(*) as count FROM events ${whereClause} GROUP BY event_type
657
+ `).all(...params) as Array<{ event_type: string; count: number }>;
658
+
659
+ // By agent type
660
+ const agentTypeWhereClause = whereClause
661
+ ? `${whereClause} AND agent_type IS NOT NULL`
662
+ : 'WHERE agent_type IS NOT NULL';
663
+ const agentTypeRows = database.prepare(`
664
+ SELECT agent_type, COUNT(*) as count FROM events ${agentTypeWhereClause} GROUP BY agent_type
665
+ `).all(...params) as Array<{ agent_type: string; count: number }>;
666
+
667
+ // By tool name
668
+ const toolNameWhereClause = whereClause
669
+ ? `${whereClause} AND tool_name IS NOT NULL`
670
+ : 'WHERE tool_name IS NOT NULL';
671
+ const toolNameRows = database.prepare(`
672
+ SELECT tool_name, COUNT(*) as count FROM events ${toolNameWhereClause} GROUP BY tool_name
673
+ `).all(...params) as Array<{ tool_name: string; count: number }>;
674
+
675
+ return {
676
+ totalEvents: totalRow.count,
677
+ totalErrors: errorRow.count,
678
+ byEventType: Object.fromEntries(eventTypeRows.map(r => [r.event_type, r.count])),
679
+ byAgentType: Object.fromEntries(agentTypeRows.map(r => [r.agent_type, r.count])),
680
+ byToolName: Object.fromEntries(toolNameRows.map(r => [r.tool_name, r.count])),
681
+ };
682
+ }
683
+
684
+ /**
685
+ * Close all database connections (for cleanup)
686
+ */
687
+ export function closeDb(): void {
688
+ Array.from(dbCache.values()).forEach((db) => db.close());
689
+ dbCache.clear();
690
+ }
691
+
692
+ /**
693
+ * Clear all trace logs (both SQLite database and JSONL file)
694
+ */
695
+ export function clearLogs(cwd?: string): void {
696
+ const { jsonlPath } = getStoragePaths(cwd);
697
+
698
+ // Clear SQLite database
699
+ try {
700
+ const database = getDb(cwd);
701
+ database.exec('DELETE FROM events');
702
+ } catch (err) {
703
+ console.error(`[trace-store] SQLite clear error: ${err}`);
704
+ }
705
+
706
+ // Clear JSONL file by truncating it
707
+ try {
708
+ if (existsSync(jsonlPath)) {
709
+ writeFileSync(jsonlPath, '');
710
+ }
711
+ } catch (err) {
712
+ console.error(`[trace-store] JSONL clear error: ${err}`);
713
+ }
714
+ }