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,579 @@
1
+ /**
2
+ * Tools Command - MCP server integrations with session management.
3
+ *
4
+ * Commands:
5
+ * - ah tools list - List all available MCP servers
6
+ * - ah tools <server> - List tools on a server
7
+ * - ah tools <server>:<tool> [args] - Call a specific tool
8
+ * - ah tools <server> --help-tool - Show help for server
9
+ * - ah tools <server>:<tool> --help-tool - Show help for specific tool
10
+ *
11
+ * Session management (stateful servers only):
12
+ * - ah tools <server> --restart - Restart session (recovery from bad state)
13
+ * - ah tools --sessions - List all active sessions
14
+ * - ah tools --shutdown-daemon - Shutdown the daemon for this AGENT_ID
15
+ *
16
+ * Session Lifecycle:
17
+ * - Sessions are auto-started on first tool call (no --start needed)
18
+ * - Sessions auto-cleanup after inactivity timeout (no --stop needed)
19
+ * - Use --restart only if a server gets into a bad state
20
+ *
21
+ * Session isolation via AGENT_ID:
22
+ * - AGENT_ID=<id> ah tools ... - Use specific agent session
23
+ * - Default AGENT_ID is "default"
24
+ */
25
+
26
+ import { Command } from 'commander';
27
+ import {
28
+ callTool,
29
+ discoverTools,
30
+ getAgentId,
31
+ getDaemonInfo,
32
+ isDaemonRunning,
33
+ listSessions,
34
+ restartServer,
35
+ shutdownDaemon,
36
+ } from '../lib/mcp-client.js';
37
+ import { tracedAction } from '../lib/base-command.js';
38
+ import {
39
+ DAEMON_DEFAULT_MCP_TIMEOUT,
40
+ formatToolHelp,
41
+ type McpServerConfig,
42
+ type McpToolSchema,
43
+ } from '../lib/mcp-runtime.js';
44
+ import { discoverServers, getServer } from '../mcp/index.js';
45
+
46
+ export function register(program: Command): void {
47
+ program
48
+ .command('tools [target]')
49
+ .description('MCP tool integrations with session management')
50
+ .option('--json', 'Output as JSON')
51
+ .option('--help-tool', 'Show help for the target')
52
+ .option('--list', 'List all available MCP servers')
53
+ .option('--sessions', 'List all active sessions')
54
+ .option('--restart', 'Restart server session (recovery from bad state)')
55
+ .option('--shutdown-daemon', 'Shutdown the daemon for this AGENT_ID')
56
+ .allowUnknownOption(true)
57
+ .action(tracedAction('tools', async (target: string | undefined, options: {
58
+ json?: boolean;
59
+ helpTool?: boolean;
60
+ list?: boolean;
61
+ sessions?: boolean;
62
+ restart?: boolean;
63
+ shutdownDaemon?: boolean;
64
+ }, cmd: Command) => {
65
+ const agentId = getAgentId();
66
+
67
+ // Shutdown daemon
68
+ if (options.shutdownDaemon) {
69
+ await handleShutdownDaemon(agentId, options.json);
70
+ return;
71
+ }
72
+
73
+ // List all active sessions
74
+ if (options.sessions) {
75
+ await handleListSessions(agentId, options.json);
76
+ return;
77
+ }
78
+
79
+ // List all servers
80
+ if (options.list || !target) {
81
+ await handleListServers(agentId, options.json);
82
+ return;
83
+ }
84
+
85
+ // Parse target: "server" or "server:tool"
86
+ const [serverName, toolName] = target.split(':');
87
+
88
+ const config = await getServer(serverName);
89
+ if (!config) {
90
+ const servers = await discoverServers();
91
+ const available = Array.from(servers.keys()).join(', ');
92
+ const msg = `Unknown server: ${serverName}. Available: ${available || 'none'}`;
93
+ if (options.json) {
94
+ console.log(JSON.stringify({ success: false, error: msg }));
95
+ } else {
96
+ console.error(`Error: ${msg}`);
97
+ }
98
+ process.exit(1);
99
+ }
100
+
101
+ // Restart command (stateful servers only)
102
+ if (options.restart) {
103
+ if (!config.stateful) {
104
+ const msg = `Server ${serverName} is stateless. --restart only works with stateful servers.`;
105
+ if (options.json) {
106
+ console.log(JSON.stringify({ success: false, error: msg }));
107
+ } else {
108
+ console.error(`Error: ${msg}`);
109
+ }
110
+ process.exit(1);
111
+ }
112
+
113
+ await handleSessionRestart(config, agentId, options.json);
114
+ return;
115
+ }
116
+
117
+ // Get tools
118
+ let allTools: McpToolSchema[];
119
+ try {
120
+ allTools = await discoverTools(config, agentId);
121
+ } catch (e) {
122
+ const error = e instanceof Error ? e.message : String(e);
123
+ if (options.json) {
124
+ console.log(JSON.stringify({ success: false, error }));
125
+ } else {
126
+ console.error(`Error discovering tools: ${error}`);
127
+ }
128
+ process.exit(1);
129
+ }
130
+
131
+ // List tools on server (no tool specified)
132
+ if (!toolName) {
133
+ if (options.helpTool) {
134
+ // Full help with session management info
135
+ if (options.json) {
136
+ console.log(JSON.stringify({
137
+ success: true,
138
+ server: config.name,
139
+ description: config.description,
140
+ stateful: config.stateful ?? false,
141
+ stateful_session_timeout: config.stateful_session_timeout ?? DAEMON_DEFAULT_MCP_TIMEOUT,
142
+ agentId,
143
+ tools: allTools,
144
+ }, null, 2));
145
+ } else {
146
+ const helpText = await formatServerHelpWithSession(config, allTools, agentId);
147
+ console.log(helpText);
148
+ }
149
+ } else {
150
+ // Brief tool list
151
+ await handleListTools(config, allTools, options.json);
152
+ }
153
+ return;
154
+ }
155
+
156
+ // Find the specific tool
157
+ const tool = allTools.find((t) => t.name === toolName);
158
+ if (!tool) {
159
+ const available = allTools.map((t) => t.name).join(', ');
160
+ const msg = `Unknown tool: ${toolName} on ${serverName}. Available: ${available}`;
161
+ if (options.json) {
162
+ console.log(JSON.stringify({ success: false, error: msg }));
163
+ } else {
164
+ console.error(`Error: ${msg}`);
165
+ }
166
+ process.exit(1);
167
+ }
168
+
169
+ // Help for specific tool
170
+ if (options.helpTool) {
171
+ if (options.json) {
172
+ console.log(JSON.stringify({
173
+ success: true,
174
+ server: config.name,
175
+ tool,
176
+ hint: config.toolHints?.[toolName],
177
+ }, null, 2));
178
+ } else {
179
+ console.log(formatToolHelp(tool, config.toolHints?.[toolName]));
180
+ }
181
+ return;
182
+ }
183
+
184
+ // Call the tool - parse remaining args as params
185
+ const params = parseToolArgs(cmd.args.slice(1)); // Skip the target arg
186
+
187
+ try {
188
+ const result = await callTool(config, toolName, params, agentId);
189
+ if (options.json) {
190
+ console.log(JSON.stringify({ success: true, result }, null, 2));
191
+ } else {
192
+ if (typeof result === 'string') {
193
+ console.log(result);
194
+ } else {
195
+ console.log(JSON.stringify(result, null, 2));
196
+ }
197
+ }
198
+ } catch (e) {
199
+ const error = e instanceof Error ? e.message : String(e);
200
+ if (options.json) {
201
+ console.log(JSON.stringify({ success: false, error }));
202
+ } else {
203
+ console.error(`Error: ${error}`);
204
+ }
205
+ process.exit(1);
206
+ }
207
+ }));
208
+ }
209
+
210
+ /**
211
+ * Format server help with session management info.
212
+ */
213
+ async function formatServerHelpWithSession(
214
+ config: McpServerConfig,
215
+ tools: McpToolSchema[],
216
+ agentId: string
217
+ ): Promise<string> {
218
+ const lines: string[] = [];
219
+
220
+ // Header
221
+ lines.push(`${config.name} - ${config.description}`);
222
+
223
+ if (config.stateful) {
224
+ const timeoutMs = config.stateful_session_timeout ?? DAEMON_DEFAULT_MCP_TIMEOUT;
225
+ const timeoutSec = Math.round(timeoutMs / 1000);
226
+
227
+ lines.push('');
228
+ lines.push(' [STATEFUL] This server maintains session state between calls.');
229
+ lines.push('');
230
+ lines.push(' Session Lifecycle:');
231
+ lines.push(' - Auto-starts on first tool call');
232
+ lines.push(` - Auto-closes after ${timeoutSec}s of inactivity`);
233
+ lines.push(' - Use --restart if server gets into a bad state');
234
+ lines.push('');
235
+ lines.push(' Commands:');
236
+ lines.push(' --restart Restart session (for recovery)');
237
+ lines.push('');
238
+ lines.push(' Session Isolation (set AGENT_ID env var for parallel sessions):');
239
+ lines.push(` Current AGENT_ID: ${agentId}`);
240
+
241
+ const daemonInfo = await getDaemonInfo(agentId);
242
+ if (daemonInfo.running) {
243
+ lines.push(` Daemon: running (PID: ${daemonInfo.pid})`);
244
+ if (daemonInfo.sessions && daemonInfo.sessions.includes(config.name)) {
245
+ lines.push(` Session: active`);
246
+ } else {
247
+ lines.push(' Session: not started (will start on first call)');
248
+ }
249
+ } else {
250
+ lines.push(' Daemon: not running (will start on first call)');
251
+ }
252
+ } else {
253
+ lines.push('');
254
+ lines.push(' [STATELESS] Each tool call is independent.');
255
+ }
256
+
257
+ lines.push('');
258
+ lines.push(`Tools (${tools.length}):`);
259
+
260
+ for (const tool of tools) {
261
+ lines.push('');
262
+ lines.push(formatToolHelp(tool, config.toolHints?.[tool.name]));
263
+ }
264
+
265
+ return lines.join('\n');
266
+ }
267
+
268
+ /**
269
+ * List all available MCP servers.
270
+ */
271
+ async function handleListServers(agentId: string, json?: boolean): Promise<void> {
272
+ const servers = await discoverServers();
273
+
274
+ if (servers.size === 0) {
275
+ if (json) {
276
+ console.log(JSON.stringify({ success: true, servers: [] }));
277
+ } else {
278
+ console.log('No MCP servers configured.');
279
+ console.log('Add servers in .allhands/harness/src/mcp/ (copy _template.ts)');
280
+ }
281
+ return;
282
+ }
283
+
284
+ const daemonRunning = isDaemonRunning(agentId);
285
+
286
+ if (json) {
287
+ const serverList = Array.from(servers.values()).map((s) => ({
288
+ name: s.name,
289
+ description: s.description,
290
+ type: s.type ?? 'stdio',
291
+ stateful: s.stateful ?? false,
292
+ stateful_session_timeout: s.stateful_session_timeout ?? DAEMON_DEFAULT_MCP_TIMEOUT,
293
+ }));
294
+ console.log(JSON.stringify({
295
+ success: true,
296
+ agentId,
297
+ daemonRunning,
298
+ servers: serverList,
299
+ }, null, 2));
300
+ return;
301
+ }
302
+
303
+ console.log(`Available MCP servers (AGENT_ID: ${agentId}):`);
304
+ console.log('');
305
+ for (const config of servers.values()) {
306
+ const stateLabel = config.stateful ? '[STATEFUL]' : '[STATELESS]';
307
+ console.log(` ${config.name} ${stateLabel}`);
308
+ console.log(` ${config.description}`);
309
+ }
310
+ console.log('');
311
+ console.log('Usage:');
312
+ console.log(' ah tools <server> List tools on server');
313
+ console.log(' ah tools <server>:<tool> Call a tool');
314
+ console.log(' ah tools <server> --help-tool Show detailed help');
315
+ console.log(' ah tools --sessions List active sessions');
316
+ console.log('');
317
+ console.log('Session isolation: AGENT_ID=<id> ah tools ...');
318
+ }
319
+
320
+ /**
321
+ * List all active sessions.
322
+ */
323
+ async function handleListSessions(agentId: string, json?: boolean): Promise<void> {
324
+ const sessions = await listSessions(agentId);
325
+ const daemonInfo = await getDaemonInfo(agentId);
326
+
327
+ if (json) {
328
+ console.log(JSON.stringify({
329
+ success: true,
330
+ agentId,
331
+ daemon: daemonInfo,
332
+ sessions: sessions.map((s) => ({
333
+ server: s.serverName,
334
+ startedAt: s.startedAt.toISOString(),
335
+ lastUsedAt: s.lastUsedAt.toISOString(),
336
+ timeoutMs: s.timeoutMs,
337
+ pid: s.pid,
338
+ })),
339
+ }, null, 2));
340
+ return;
341
+ }
342
+
343
+ console.log(`Sessions for AGENT_ID: ${agentId}`);
344
+ console.log('');
345
+
346
+ if (!daemonInfo.running) {
347
+ console.log(' Daemon: not running');
348
+ console.log(' No active sessions.');
349
+ return;
350
+ }
351
+
352
+ console.log(` Daemon: running (PID: ${daemonInfo.pid})`);
353
+ console.log('');
354
+
355
+ if (sessions.length === 0) {
356
+ console.log(' No active MCP sessions.');
357
+ return;
358
+ }
359
+
360
+ console.log(' Active sessions:');
361
+ for (const session of sessions) {
362
+ const idleMs = Date.now() - session.lastUsedAt.getTime();
363
+ const idleSec = Math.floor(idleMs / 1000);
364
+ const timeoutSec = Math.floor(session.timeoutMs / 1000);
365
+ const remaining = Math.max(0, timeoutSec - idleSec);
366
+ console.log(` ${session.serverName}`);
367
+ console.log(` PID: ${session.pid ?? 'unknown'}, Idle: ${idleSec}s, Timeout in: ${remaining}s`);
368
+ }
369
+ }
370
+
371
+ /**
372
+ * List tools available on a server.
373
+ */
374
+ async function handleListTools(
375
+ config: McpServerConfig,
376
+ tools: McpToolSchema[],
377
+ json?: boolean
378
+ ): Promise<void> {
379
+ if (json) {
380
+ console.log(JSON.stringify({
381
+ success: true,
382
+ server: config.name,
383
+ stateful: config.stateful ?? false,
384
+ tools: tools.map((t) => ({
385
+ name: t.name,
386
+ description: t.description,
387
+ })),
388
+ }, null, 2));
389
+ return;
390
+ }
391
+
392
+ const stateLabel = config.stateful ? '[STATEFUL]' : '[STATELESS]';
393
+ console.log(`${config.name} - ${config.description} ${stateLabel}`);
394
+ console.log('');
395
+ console.log(`Tools (${tools.length}):`);
396
+
397
+ for (const tool of tools) {
398
+ // Build parameter signature
399
+ const params: string[] = [];
400
+ const props = tool.inputSchema?.properties ?? {};
401
+ const required = new Set(tool.inputSchema?.required ?? []);
402
+
403
+ for (const [name, schema] of Object.entries(props)) {
404
+ let typeStr = schema.type;
405
+ if (schema.items?.type) {
406
+ typeStr = `${schema.items.type}[]`;
407
+ }
408
+
409
+ if (required.has(name)) {
410
+ params.push(`${name}:${typeStr}`);
411
+ } else {
412
+ params.push(`[${name}:${typeStr}]`);
413
+ }
414
+ }
415
+
416
+ console.log(` ${tool.name}(${params.join(', ')})`);
417
+ if (tool.description) {
418
+ console.log(` ${tool.description}`);
419
+ }
420
+ }
421
+
422
+ console.log('');
423
+ console.log('Usage: ah tools ' + config.name + ':<tool> --<param>=<value>');
424
+
425
+ if (config.stateful) {
426
+ console.log('');
427
+ console.log('Session: auto-starts on first call, use --restart for recovery');
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Handle daemon shutdown command.
433
+ */
434
+ async function handleShutdownDaemon(
435
+ agentId: string,
436
+ json?: boolean
437
+ ): Promise<void> {
438
+ const daemonInfo = await getDaemonInfo(agentId);
439
+
440
+ if (!daemonInfo.running) {
441
+ if (json) {
442
+ console.log(JSON.stringify({
443
+ success: true,
444
+ agentId,
445
+ wasRunning: false,
446
+ }, null, 2));
447
+ } else {
448
+ console.log(`Daemon not running (AGENT_ID: ${agentId}).`);
449
+ }
450
+ return;
451
+ }
452
+
453
+ await shutdownDaemon(agentId);
454
+
455
+ if (json) {
456
+ console.log(JSON.stringify({
457
+ success: true,
458
+ agentId,
459
+ wasRunning: true,
460
+ pid: daemonInfo.pid,
461
+ }, null, 2));
462
+ } else {
463
+ console.log(`Daemon shutdown (AGENT_ID: ${agentId}, PID: ${daemonInfo.pid}).`);
464
+ }
465
+ }
466
+
467
+ /**
468
+ * Handle session restart command.
469
+ */
470
+ async function handleSessionRestart(
471
+ config: McpServerConfig,
472
+ agentId: string,
473
+ json?: boolean
474
+ ): Promise<void> {
475
+ const result = await restartServer(config, agentId);
476
+
477
+ if (json) {
478
+ console.log(JSON.stringify({
479
+ success: result.success,
480
+ server: config.name,
481
+ agentId,
482
+ pid: result.pid,
483
+ error: result.error,
484
+ }, null, 2));
485
+ return;
486
+ }
487
+
488
+ if (result.success) {
489
+ console.log(`Session ${config.name} restarted (AGENT_ID: ${agentId}, MCP PID: ${result.pid ?? 'unknown'}).`);
490
+ } else {
491
+ console.error(`Failed to restart ${config.name}: ${result.error}`);
492
+ process.exit(1);
493
+ }
494
+ }
495
+
496
+ /**
497
+ * Parse tool arguments from command line.
498
+ *
499
+ * Supports:
500
+ * - --param=value
501
+ * - --param value
502
+ * - --flag (boolean true)
503
+ * - JSON string as single argument
504
+ */
505
+ function parseToolArgs(args: string[]): Record<string, unknown> {
506
+ // Filter out our own flags
507
+ const toolArgs = args.filter((arg) =>
508
+ arg !== '--restart' &&
509
+ arg !== '--json' &&
510
+ arg !== '--help-tool' &&
511
+ arg !== '--list' &&
512
+ arg !== '--sessions' &&
513
+ arg !== '--shutdown-daemon'
514
+ );
515
+
516
+ // Check if first arg is JSON
517
+ if (toolArgs.length === 1 && toolArgs[0].startsWith('{')) {
518
+ try {
519
+ return JSON.parse(toolArgs[0]);
520
+ } catch {
521
+ // Not JSON, parse as flags
522
+ }
523
+ }
524
+
525
+ const params: Record<string, unknown> = {};
526
+ let i = 0;
527
+
528
+ while (i < toolArgs.length) {
529
+ const arg = toolArgs[i];
530
+
531
+ if (arg.startsWith('--')) {
532
+ const withoutDashes = arg.slice(2);
533
+
534
+ if (withoutDashes.includes('=')) {
535
+ // --param=value
536
+ const [key, ...valueParts] = withoutDashes.split('=');
537
+ const value = valueParts.join('=');
538
+ params[key] = parseValue(value);
539
+ } else if (i + 1 < toolArgs.length && !toolArgs[i + 1].startsWith('--')) {
540
+ // --param value
541
+ params[withoutDashes] = parseValue(toolArgs[i + 1]);
542
+ i++;
543
+ } else {
544
+ // --flag (boolean)
545
+ params[withoutDashes] = true;
546
+ }
547
+ }
548
+
549
+ i++;
550
+ }
551
+
552
+ return params;
553
+ }
554
+
555
+ /**
556
+ * Parse a string value to appropriate type.
557
+ */
558
+ function parseValue(value: string): unknown {
559
+ // Boolean
560
+ if (value === 'true') return true;
561
+ if (value === 'false') return false;
562
+
563
+ // Number
564
+ if (/^-?\d+(\.\d+)?$/.test(value)) {
565
+ return parseFloat(value);
566
+ }
567
+
568
+ // JSON array or object
569
+ if ((value.startsWith('[') && value.endsWith(']')) ||
570
+ (value.startsWith('{') && value.endsWith('}'))) {
571
+ try {
572
+ return JSON.parse(value);
573
+ } catch {
574
+ // Return as string
575
+ }
576
+ }
577
+
578
+ return value;
579
+ }