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,177 @@
1
+ /**
2
+ * Complexity command - Get complexity metrics for files or directories.
3
+ *
4
+ * Uses ctags to count symbols for broader language support.
5
+ *
6
+ * Command:
7
+ * ah complexity <path> - Get complexity metrics
8
+ */
9
+
10
+ import { Command } from "commander";
11
+ import { existsSync, readFileSync, readdirSync, statSync } from "fs";
12
+ import { join, relative, extname } from "path";
13
+ import {
14
+ executeCommand,
15
+ parseContext,
16
+ addCommonOptions,
17
+ CommandResult,
18
+ } from "../lib/base-command.js";
19
+ import { getProjectRoot } from "../lib/git.js";
20
+ import {
21
+ checkCtagsAvailable,
22
+ generateCtagsIndex,
23
+ getFileSymbols,
24
+ } from "../lib/ctags.js";
25
+
26
+ /**
27
+ * Get complexity metrics for a file or directory.
28
+ * Uses ctags to count symbols instead of AST parsing.
29
+ */
30
+ async function complexity(pathArg: string): Promise<CommandResult> {
31
+ const projectRoot = getProjectRoot();
32
+ const absolutePath = pathArg.startsWith("/")
33
+ ? pathArg
34
+ : join(projectRoot, pathArg);
35
+ const relativePath = relative(projectRoot, absolutePath);
36
+
37
+ if (!existsSync(absolutePath)) {
38
+ return {
39
+ success: false,
40
+ error: `path_not_found: Path not found: ${relativePath}`,
41
+ };
42
+ }
43
+
44
+ // Check ctags availability
45
+ const ctagsCheck = checkCtagsAvailable();
46
+ if (!ctagsCheck.available) {
47
+ return {
48
+ success: false,
49
+ error: `ctags_unavailable: ${ctagsCheck.error}`,
50
+ };
51
+ }
52
+
53
+ const stat = statSync(absolutePath);
54
+
55
+ if (stat.isFile()) {
56
+ // Single file complexity
57
+ const content = readFileSync(absolutePath, "utf-8");
58
+ const lines = content.split("\n").length;
59
+
60
+ // Get symbols via ctags
61
+ const { index } = generateCtagsIndex(projectRoot, { target: relativePath });
62
+ const symbols = getFileSymbols(index, relativePath);
63
+
64
+ const functions = symbols.filter(
65
+ (s) => s.kind === "function" || s.kind === "method"
66
+ ).length;
67
+ const classes = symbols.filter((s) => s.kind === "class").length;
68
+ const interfaces = symbols.filter(
69
+ (s) => s.kind === "interface" || s.kind === "type"
70
+ ).length;
71
+
72
+ // Count imports/exports with regex (simple heuristic)
73
+ const importMatches = content.match(/^import\s/gm);
74
+ const exportMatches = content.match(/^export\s/gm);
75
+
76
+ return {
77
+ success: true,
78
+ data: {
79
+ path: relativePath,
80
+ type: "file",
81
+ metrics: {
82
+ lines,
83
+ functions,
84
+ classes,
85
+ interfaces,
86
+ imports: importMatches?.length || 0,
87
+ exports: exportMatches?.length || 0,
88
+ total_symbols: symbols.length,
89
+ },
90
+ estimated_tokens: Math.ceil(lines * 10),
91
+ },
92
+ };
93
+ }
94
+
95
+ // Directory complexity
96
+ const { index, entryCount } = generateCtagsIndex(projectRoot, {
97
+ target: relativePath,
98
+ });
99
+
100
+ // Aggregate stats
101
+ let totalLines = 0;
102
+ let totalFunctions = 0;
103
+ let totalClasses = 0;
104
+ let totalInterfaces = 0;
105
+ let fileCount = 0;
106
+
107
+ // Find source files and count lines
108
+ const countDir = (dir: string): void => {
109
+ const entries = readdirSync(dir);
110
+ for (const entry of entries) {
111
+ if (entry.startsWith(".") || entry === "node_modules") continue;
112
+ const fullPath = join(dir, entry);
113
+ const entryStat = statSync(fullPath);
114
+ if (entryStat.isDirectory()) {
115
+ countDir(fullPath);
116
+ } else {
117
+ const ext = extname(entry);
118
+ if ([".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".rs", ".java"].includes(ext)) {
119
+ fileCount++;
120
+ const content = readFileSync(fullPath, "utf-8");
121
+ totalLines += content.split("\n").length;
122
+ }
123
+ }
124
+ }
125
+ };
126
+
127
+ countDir(absolutePath);
128
+
129
+ // Count symbol types from index
130
+ for (const fileMap of Array.from(index.values())) {
131
+ for (const entries of Array.from(fileMap.values())) {
132
+ for (const entry of entries) {
133
+ if (entry.kind === "function" || entry.kind === "method") {
134
+ totalFunctions++;
135
+ } else if (entry.kind === "class") {
136
+ totalClasses++;
137
+ } else if (entry.kind === "interface" || entry.kind === "type") {
138
+ totalInterfaces++;
139
+ }
140
+ }
141
+ }
142
+ }
143
+
144
+ return {
145
+ success: true,
146
+ data: {
147
+ path: relativePath,
148
+ type: "directory",
149
+ file_count: fileCount,
150
+ metrics: {
151
+ lines: totalLines,
152
+ functions: totalFunctions,
153
+ classes: totalClasses,
154
+ interfaces: totalInterfaces,
155
+ total_symbols: entryCount,
156
+ },
157
+ estimated_tokens: Math.ceil(totalLines * 10),
158
+ },
159
+ };
160
+ }
161
+
162
+ /**
163
+ * Register complexity command.
164
+ */
165
+ export function register(program: Command): void {
166
+ const complexityCmd = program
167
+ .command("complexity")
168
+ .description("Get complexity metrics for file or directory")
169
+ .argument("<path>", "File or directory path");
170
+
171
+ addCommonOptions(complexityCmd);
172
+
173
+ complexityCmd.action(async (path: string, options) => {
174
+ const context = parseContext(options);
175
+ await executeCommand("complexity", context, () => complexity(path));
176
+ });
177
+ }
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Context7 Commands (Agent-Facing)
3
+ *
4
+ * Library documentation search and context retrieval.
5
+ * Flow: search (find library) → context (get docs for known library)
6
+ *
7
+ * Commands:
8
+ * - ah context7 search <library> [query] - Search for libraries by name
9
+ * - ah context7 context <libraryId> <query> - Get documentation context
10
+ */
11
+
12
+ import { Command } from 'commander';
13
+ import { Context7, type Library } from '@upstash/context7-sdk';
14
+ import { tracedAction } from '../lib/base-command.js';
15
+
16
+ const DEFAULT_TIMEOUT = 120000;
17
+
18
+ export function register(program: Command): void {
19
+ const context7 = program
20
+ .command('context7')
21
+ .description('Library documentation search and context retrieval');
22
+
23
+ // ah context7 search
24
+ context7
25
+ .command('search <library>')
26
+ .argument('[query]', 'Optional query for relevance ranking')
27
+ .description('Search for libraries by name, returns IDs for context command')
28
+ .option('--limit <n>', 'Max results (default: 5)', parseInt)
29
+ .option('--json', 'Output as JSON')
30
+ .action(tracedAction('context7 search', async (library: string, query: string | undefined, options: { limit?: number; json?: boolean }) => {
31
+ const apiKey = process.env.CONTEXT7_API_KEY;
32
+ if (!apiKey) {
33
+ if (options.json) {
34
+ console.log(JSON.stringify({ success: false, error: 'CONTEXT7_API_KEY not set' }));
35
+ } else {
36
+ console.error('Error: CONTEXT7_API_KEY not set in environment');
37
+ }
38
+ process.exit(1);
39
+ }
40
+
41
+ const client = new Context7({ apiKey });
42
+ const searchQuery = query ?? `How to use ${library}`;
43
+ const limit = options.limit ?? 5;
44
+
45
+ try {
46
+ const libraries = await withTimeout(() => client.searchLibrary(searchQuery, library));
47
+
48
+ if (!Array.isArray(libraries)) {
49
+ throw new Error('Unexpected response format from Context7');
50
+ }
51
+
52
+ const results = libraries.slice(0, limit).map((lib: Library) => ({
53
+ id: lib.id,
54
+ name: lib.name,
55
+ description: lib.description,
56
+ snippets: lib.totalSnippets,
57
+ trust: lib.trustScore,
58
+ }));
59
+
60
+ if (options.json) {
61
+ console.log(JSON.stringify({
62
+ success: true,
63
+ query: library,
64
+ results,
65
+ usage: results.length > 0
66
+ ? `Use: ah context7 context "${results[0].id}" "your question"`
67
+ : undefined,
68
+ }, null, 2));
69
+ return;
70
+ }
71
+
72
+ console.log(`Search results for: ${library}`);
73
+ console.log();
74
+
75
+ if (results.length === 0) {
76
+ console.log('No libraries found. Try a different search term.');
77
+ return;
78
+ }
79
+
80
+ for (const lib of results) {
81
+ console.log(` ID: ${lib.id}`);
82
+ console.log(` Name: ${lib.name}`);
83
+ if (lib.description) {
84
+ console.log(` Description: ${lib.description}`);
85
+ }
86
+ console.log(` Snippets: ${lib.snippets}, Trust: ${lib.trust}`);
87
+ console.log();
88
+ }
89
+
90
+ console.log(`Usage: ah context7 context "${results[0].id}" "your question"`);
91
+ } catch (e) {
92
+ const error = e instanceof Error ? e.message : String(e);
93
+ if (options.json) {
94
+ console.log(JSON.stringify({ success: false, error }));
95
+ } else {
96
+ console.error(`Error: ${error}`);
97
+ }
98
+ process.exit(1);
99
+ }
100
+ }));
101
+
102
+ // ah context7 context
103
+ context7
104
+ .command('context <libraryId> <query>')
105
+ .description('Get documentation context for a known library (use search first)')
106
+ .option('--text', 'Return plain text instead of JSON (better for direct LLM use)')
107
+ .option('--json', 'Output as JSON')
108
+ .action(tracedAction('context7 context', async (libraryId: string, query: string, options: { text?: boolean; json?: boolean }) => {
109
+ const apiKey = process.env.CONTEXT7_API_KEY;
110
+ if (!apiKey) {
111
+ if (options.json) {
112
+ console.log(JSON.stringify({ success: false, error: 'CONTEXT7_API_KEY not set' }));
113
+ } else {
114
+ console.error('Error: CONTEXT7_API_KEY not set in environment');
115
+ }
116
+ process.exit(1);
117
+ }
118
+
119
+ const client = new Context7({ apiKey });
120
+
121
+ try {
122
+ if (options.text) {
123
+ // Plain text mode - directly usable in LLM prompts
124
+ const content = await withTimeout(() =>
125
+ client.getContext(query, libraryId, { type: 'txt' })
126
+ );
127
+
128
+ if (options.json) {
129
+ console.log(JSON.stringify({
130
+ success: true,
131
+ library: libraryId,
132
+ query,
133
+ content,
134
+ format: 'text',
135
+ }, null, 2));
136
+ return;
137
+ }
138
+
139
+ console.log(content);
140
+ return;
141
+ }
142
+
143
+ // JSON mode - structured docs
144
+ const docs = await withTimeout(() =>
145
+ client.getContext(query, libraryId, { type: 'json' })
146
+ );
147
+
148
+ if (!Array.isArray(docs)) {
149
+ throw new Error('Unexpected response format from Context7');
150
+ }
151
+
152
+ const documentation = docs.map((doc) => ({
153
+ title: doc.title,
154
+ content: doc.content,
155
+ source: doc.source,
156
+ }));
157
+
158
+ if (options.json) {
159
+ console.log(JSON.stringify({
160
+ success: true,
161
+ library: libraryId,
162
+ query,
163
+ docs: documentation,
164
+ doc_count: documentation.length,
165
+ }, null, 2));
166
+ return;
167
+ }
168
+
169
+ console.log(`Documentation for: ${libraryId}`);
170
+ console.log(`Query: ${query}`);
171
+ console.log();
172
+
173
+ for (const doc of documentation) {
174
+ console.log(`--- ${doc.title} ---`);
175
+ console.log(doc.content);
176
+ if (doc.source) {
177
+ console.log(`Source: ${doc.source}`);
178
+ }
179
+ console.log();
180
+ }
181
+ } catch (e) {
182
+ const error = e instanceof Error ? e.message : String(e);
183
+ const suggestion = 'Ensure libraryId is valid (from search results)';
184
+ if (options.json) {
185
+ console.log(JSON.stringify({ success: false, error, suggestion }));
186
+ } else {
187
+ console.error(`Error: ${error}`);
188
+ console.error(suggestion);
189
+ }
190
+ process.exit(1);
191
+ }
192
+ }));
193
+ }
194
+
195
+ async function withTimeout<T>(fn: () => Promise<T>): Promise<T> {
196
+ let timeoutId: NodeJS.Timeout;
197
+ const timeout = new Promise<never>((_, reject) => {
198
+ timeoutId = setTimeout(() => reject(new Error('Request timed out')), DEFAULT_TIMEOUT);
199
+ timeoutId.unref(); // Don't keep process alive after main work completes
200
+ });
201
+ return Promise.race([fn(), timeout]);
202
+ }