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.
- package/.allhands/README.md +75 -0
- package/.allhands/agents/compounder.yaml +15 -0
- package/.allhands/agents/coordinator.yaml +17 -0
- package/.allhands/agents/documentor.yaml +15 -0
- package/.allhands/agents/e2e-test-planner.yaml +17 -0
- package/.allhands/agents/emergent.yaml +22 -0
- package/.allhands/agents/executor.yaml +14 -0
- package/.allhands/agents/ideation.yaml +11 -0
- package/.allhands/agents/initiative-steering.yaml +19 -0
- package/.allhands/agents/judge.yaml +13 -0
- package/.allhands/agents/planner.yaml +19 -0
- package/.allhands/agents/pr-reviewer.yaml +15 -0
- package/.allhands/docs.json +5 -0
- package/.allhands/docs.local.json +26 -0
- package/.allhands/flows/COMPOUNDING.md +203 -0
- package/.allhands/flows/COORDINATION.md +89 -0
- package/.allhands/flows/CORE.md +87 -0
- package/.allhands/flows/DOCUMENTATION.md +218 -0
- package/.allhands/flows/E2E_TEST_PLAN_BUILDING.md +140 -0
- package/.allhands/flows/EMERGENT_PLANNING.md +57 -0
- package/.allhands/flows/IDEATION_SCOPING.md +154 -0
- package/.allhands/flows/INITIATIVE_STEERING.md +110 -0
- package/.allhands/flows/JUDGE_REVIEWING.md +79 -0
- package/.allhands/flows/PROMPT_TASK_EXECUTION.md +68 -0
- package/.allhands/flows/PR_REVIEWING.md +43 -0
- package/.allhands/flows/SPEC_PLANNING.md +216 -0
- package/.allhands/flows/harness/WRITING_HARNESS_FLOWS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_KNOWLEDGE.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_ORCHESTRATION.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_SKILLS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_TOOLS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_VALIDATION_TOOLING.md +27 -0
- package/.allhands/flows/shared/CODEBASE_UNDERSTANDING.md +72 -0
- package/.allhands/flows/shared/CREATE_HARNESS_SPEC.md +48 -0
- package/.allhands/flows/shared/CREATE_SPEC.md +41 -0
- package/.allhands/flows/shared/CREATE_VALIDATION_TOOLING_SPEC.md +70 -0
- package/.allhands/flows/shared/DOCUMENTATION_DISCOVERY.md +123 -0
- package/.allhands/flows/shared/DOCUMENTATION_WRITER.md +101 -0
- package/.allhands/flows/shared/EMERGENT_REFINEMENT_ANALYSIS.md +76 -0
- package/.allhands/flows/shared/EXTERNAL_TECH_GUIDANCE.md +97 -0
- package/.allhands/flows/shared/IDEATION_CODEBASE_GROUNDING.md +49 -0
- package/.allhands/flows/shared/PLAN_DEEPENING.md +152 -0
- package/.allhands/flows/shared/PROMPT_TASKS_CURATION.md +113 -0
- package/.allhands/flows/shared/PROMPT_VALIDATION_REVIEW.MD +99 -0
- package/.allhands/flows/shared/QUICK_PREMORTEM.md +70 -0
- package/.allhands/flows/shared/RESEARCH_GUIDANCE.md +38 -0
- package/.allhands/flows/shared/REVIEW_OPTIONS_BREAKDOWN.md +68 -0
- package/.allhands/flows/shared/SKILL_EXTRACTION.md +84 -0
- package/.allhands/flows/shared/SPEC_FLOW_ANALYSIS.md +119 -0
- package/.allhands/flows/shared/TDD_WORKFLOW.md +109 -0
- package/.allhands/flows/shared/UTILIZE_VALIDATION_TOOLING.md +84 -0
- package/.allhands/flows/shared/WRITING_HARNESS_FLOWS.md +11 -0
- package/.allhands/flows/shared/WRITING_HARNESS_MCP_TOOLS.md +84 -0
- package/.allhands/flows/shared/jury/ARCHITECTURE_REVIEW.md +91 -0
- package/.allhands/flows/shared/jury/BEST_PRACTICES_REVIEW.md +80 -0
- package/.allhands/flows/shared/jury/CLAIM_VERIFICATION_REVIEW.md +101 -0
- package/.allhands/flows/shared/jury/EXPECTATIONS_FIT_REVIEW.md +78 -0
- package/.allhands/flows/shared/jury/MAINTAINABILITY_REVIEW.md +110 -0
- package/.allhands/flows/shared/jury/PROMPTS_EXPECTATIONS_FIT.md +74 -0
- package/.allhands/flows/shared/jury/PROMPTS_FLOW_ANALYSIS.md +92 -0
- package/.allhands/flows/shared/jury/PROMPTS_YAGNI.md +78 -0
- package/.allhands/flows/shared/jury/PROMPT_PREMORTEM.md +125 -0
- package/.allhands/flows/shared/jury/SECURITY_REVIEW.md +86 -0
- package/.allhands/flows/shared/jury/YAGNI_REVIEW.md +82 -0
- package/.allhands/flows/wip/DEBUG_INVESTIGATION.md +162 -0
- package/.allhands/flows/wip/MEMORY_RECALL.md +62 -0
- package/.allhands/harness/ah +131 -0
- package/.allhands/harness/package-lock.json +5292 -0
- package/.allhands/harness/package.json +52 -0
- package/.allhands/harness/src/__tests__/e2e/commands.test.ts +307 -0
- package/.allhands/harness/src/__tests__/e2e/event-loop.test.ts +539 -0
- package/.allhands/harness/src/__tests__/e2e/hooks.test.ts +427 -0
- package/.allhands/harness/src/__tests__/e2e/new-initiative-routing.test.ts +137 -0
- package/.allhands/harness/src/__tests__/e2e/run-e2e.ts +109 -0
- package/.allhands/harness/src/__tests__/e2e/specs-type.test.ts +210 -0
- package/.allhands/harness/src/__tests__/e2e/validation-hooks.test.ts +669 -0
- package/.allhands/harness/src/__tests__/e2e/validation-path-consistency.test.ts +354 -0
- package/.allhands/harness/src/__tests__/e2e/validation.test.ts +528 -0
- package/.allhands/harness/src/__tests__/harness/assertions.ts +318 -0
- package/.allhands/harness/src/__tests__/harness/cli-runner.ts +359 -0
- package/.allhands/harness/src/__tests__/harness/fixture.ts +384 -0
- package/.allhands/harness/src/__tests__/harness/hook-runner.ts +411 -0
- package/.allhands/harness/src/__tests__/harness/index.ts +122 -0
- package/.allhands/harness/src/cli.ts +36 -0
- package/.allhands/harness/src/commands/complexity.ts +177 -0
- package/.allhands/harness/src/commands/context7.ts +202 -0
- package/.allhands/harness/src/commands/docs.ts +557 -0
- package/.allhands/harness/src/commands/hooks.ts +24 -0
- package/.allhands/harness/src/commands/index.ts +51 -0
- package/.allhands/harness/src/commands/knowledge.ts +382 -0
- package/.allhands/harness/src/commands/memories.ts +302 -0
- package/.allhands/harness/src/commands/notify.ts +61 -0
- package/.allhands/harness/src/commands/oracle.ts +158 -0
- package/.allhands/harness/src/commands/perplexity.ts +220 -0
- package/.allhands/harness/src/commands/planning.ts +245 -0
- package/.allhands/harness/src/commands/schema.ts +73 -0
- package/.allhands/harness/src/commands/skills.ts +128 -0
- package/.allhands/harness/src/commands/solutions.ts +353 -0
- package/.allhands/harness/src/commands/spawn.ts +158 -0
- package/.allhands/harness/src/commands/specs.ts +532 -0
- package/.allhands/harness/src/commands/tavily.ts +226 -0
- package/.allhands/harness/src/commands/tools.ts +579 -0
- package/.allhands/harness/src/commands/trace.ts +327 -0
- package/.allhands/harness/src/commands/tui.ts +960 -0
- package/.allhands/harness/src/commands/validate.ts +143 -0
- package/.allhands/harness/src/commands/validation-tools.ts +108 -0
- package/.allhands/harness/src/hooks/context.ts +1442 -0
- package/.allhands/harness/src/hooks/enforcement.ts +170 -0
- package/.allhands/harness/src/hooks/index.ts +54 -0
- package/.allhands/harness/src/hooks/lifecycle.ts +229 -0
- package/.allhands/harness/src/hooks/notification.ts +104 -0
- package/.allhands/harness/src/hooks/observability.ts +551 -0
- package/.allhands/harness/src/hooks/session.ts +88 -0
- package/.allhands/harness/src/hooks/shared.ts +815 -0
- package/.allhands/harness/src/hooks/transcript-parser.ts +208 -0
- package/.allhands/harness/src/hooks/validation.ts +617 -0
- package/.allhands/harness/src/lib/__tests__/ctags.test.ts +244 -0
- package/.allhands/harness/src/lib/__tests__/docs-validation.test.ts +344 -0
- package/.allhands/harness/src/lib/__tests__/mcp-runtime.test.ts +190 -0
- package/.allhands/harness/src/lib/__tests__/schema.test.ts +861 -0
- package/.allhands/harness/src/lib/base-command.ts +198 -0
- package/.allhands/harness/src/lib/cli-daemon.ts +343 -0
- package/.allhands/harness/src/lib/compaction.ts +313 -0
- package/.allhands/harness/src/lib/ctags.ts +497 -0
- package/.allhands/harness/src/lib/docs-validation.ts +907 -0
- package/.allhands/harness/src/lib/event-loop.ts +662 -0
- package/.allhands/harness/src/lib/flows.ts +155 -0
- package/.allhands/harness/src/lib/git.ts +276 -0
- package/.allhands/harness/src/lib/knowledge-worker.ts +72 -0
- package/.allhands/harness/src/lib/knowledge.ts +810 -0
- package/.allhands/harness/src/lib/llm.ts +255 -0
- package/.allhands/harness/src/lib/mcp-client.ts +432 -0
- package/.allhands/harness/src/lib/mcp-daemon.ts +486 -0
- package/.allhands/harness/src/lib/mcp-runtime.ts +418 -0
- package/.allhands/harness/src/lib/notification.ts +115 -0
- package/.allhands/harness/src/lib/opencode/index.ts +70 -0
- package/.allhands/harness/src/lib/opencode/profiles.ts +300 -0
- package/.allhands/harness/src/lib/opencode/prompts/codesearch.md +98 -0
- package/.allhands/harness/src/lib/opencode/prompts/knowledge-aggregator.md +67 -0
- package/.allhands/harness/src/lib/opencode/runner.ts +281 -0
- package/.allhands/harness/src/lib/oracle.ts +926 -0
- package/.allhands/harness/src/lib/planning-utils.ts +150 -0
- package/.allhands/harness/src/lib/planning.ts +605 -0
- package/.allhands/harness/src/lib/pr-review.ts +225 -0
- package/.allhands/harness/src/lib/prompts.ts +522 -0
- package/.allhands/harness/src/lib/schema.ts +418 -0
- package/.allhands/harness/src/lib/schemas/agent-profile.ts +141 -0
- package/.allhands/harness/src/lib/schemas/template-vars.ts +138 -0
- package/.allhands/harness/src/lib/session.ts +164 -0
- package/.allhands/harness/src/lib/specs.ts +348 -0
- package/.allhands/harness/src/lib/tldr.ts +829 -0
- package/.allhands/harness/src/lib/tmux.ts +1051 -0
- package/.allhands/harness/src/lib/trace-store.ts +714 -0
- package/.allhands/harness/src/mcp/__tests__/index.test.ts +46 -0
- package/.allhands/harness/src/mcp/_template.ts +47 -0
- package/.allhands/harness/src/mcp/filesystem.ts +33 -0
- package/.allhands/harness/src/mcp/index.ts +69 -0
- package/.allhands/harness/src/mcp/playwright.ts +34 -0
- package/.allhands/harness/src/mcp/xcodebuild.ts +29 -0
- package/.allhands/harness/src/schemas/docs.schema.json +44 -0
- package/.allhands/harness/src/schemas/settings.schema.json +214 -0
- package/.allhands/harness/src/tui/actions.ts +227 -0
- package/.allhands/harness/src/tui/file-viewer-modal.ts +270 -0
- package/.allhands/harness/src/tui/index.ts +1574 -0
- package/.allhands/harness/src/tui/modal.ts +232 -0
- package/.allhands/harness/src/tui/prompts-pane.ts +186 -0
- package/.allhands/harness/src/tui/status-pane.ts +434 -0
- package/.allhands/harness/tsconfig.json +22 -0
- package/.allhands/harness/vitest.config.ts +13 -0
- package/.allhands/pillars.md +33 -0
- package/.allhands/principles.md +88 -0
- package/.allhands/schemas/alignment.yaml +51 -0
- package/.allhands/schemas/documentation.yaml +10 -0
- package/.allhands/schemas/prompt.yaml +92 -0
- package/.allhands/schemas/skill.yaml +34 -0
- package/.allhands/schemas/solution.yaml +131 -0
- package/.allhands/schemas/spec.yaml +67 -0
- package/.allhands/schemas/validation-suite.yaml +49 -0
- package/.allhands/schemas/workflow.yaml +51 -0
- package/.allhands/settings.json +57 -0
- package/.allhands/skills/claude-code-patterns/SKILL.md +60 -0
- package/.allhands/skills/claude-code-patterns/docs/context-hygiene.md +19 -0
- package/.allhands/skills/harness-maintenance/SKILL.md +449 -0
- package/.allhands/skills/harness-maintenance/references/core-architecture.md +187 -0
- package/.allhands/skills/harness-maintenance/references/harness-skills.md +87 -0
- package/.allhands/skills/harness-maintenance/references/knowledge-compounding.md +78 -0
- package/.allhands/skills/harness-maintenance/references/tools-commands-mcp-hooks.md +115 -0
- package/.allhands/skills/harness-maintenance/references/validation-tooling.md +77 -0
- package/.allhands/skills/harness-maintenance/references/writing-flows.md +84 -0
- package/.allhands/validation/browser-automation.md +109 -0
- package/.allhands/validation/xcode-automation.md +195 -0
- package/.allhands/workflows/documentation.md +86 -0
- package/.allhands/workflows/investigation.md +81 -0
- package/.allhands/workflows/milestone.md +91 -0
- package/.allhands/workflows/optimization.md +85 -0
- package/.allhands/workflows/refactor.md +99 -0
- package/.allhands/workflows/triage.md +81 -0
- package/.claude/README.md +1 -0
- package/.claude/agents/explorer.md +10 -0
- package/.claude/agents/researcher.md +11 -0
- package/.claude/agents/task-runner.md +8 -0
- package/.claude/settings.json +231 -0
- package/.env.ai.example +7 -0
- package/.github/workflows/npm-publish.yml +69 -0
- package/.internal.json +45 -0
- package/.tldr/config.json +11 -0
- package/.tldrignore +90 -0
- package/CLAUDE.md +6 -0
- package/README.md +98 -0
- package/bin/sync-cli.js +7552 -0
- package/concerns.md +7 -0
- package/docs/README.md +41 -0
- package/docs/agents/README.md +24 -0
- package/docs/agents/agent-configuration-system.md +86 -0
- package/docs/agents/execution-agents.md +50 -0
- package/docs/agents/knowledge-agents.md +61 -0
- package/docs/agents/orchestration-agent.md +57 -0
- package/docs/agents/planning-agents.md +84 -0
- package/docs/agents/quality-review-agents.md +67 -0
- package/docs/agents/workflow-agent-orchestration.md +69 -0
- package/docs/flows/README.md +44 -0
- package/docs/flows/compounding.md +126 -0
- package/docs/flows/coordination.md +72 -0
- package/docs/flows/core-harness-integration.md +63 -0
- package/docs/flows/documentation-orchestration.md +98 -0
- package/docs/flows/e2e-test-plan-building.md +83 -0
- package/docs/flows/emergent-refinement.md +104 -0
- package/docs/flows/flow-authoring-and-mcp-tools.md +89 -0
- package/docs/flows/judge-reviewing.md +112 -0
- package/docs/flows/plan-deepening-and-research.md +107 -0
- package/docs/flows/plan-review-jury.md +114 -0
- package/docs/flows/pr-reviewing.md +54 -0
- package/docs/flows/prompt-task-execution.md +119 -0
- package/docs/flows/spec-planning.md +162 -0
- package/docs/flows/type-specific-scoping-flows.md +49 -0
- package/docs/flows/validation-and-skills-integration.md +145 -0
- package/docs/flows/wip/wip-flows.md +102 -0
- package/docs/harness/README.md +23 -0
- package/docs/harness/agent-profiles.md +84 -0
- package/docs/harness/cli/README.md +24 -0
- package/docs/harness/cli/cli-entry-and-command-discovery.md +91 -0
- package/docs/harness/cli/docs-command.md +87 -0
- package/docs/harness/cli/knowledge-command.md +91 -0
- package/docs/harness/cli/minor-cli-commands.md +65 -0
- package/docs/harness/cli/oracle-command.md +113 -0
- package/docs/harness/cli/planning-command.md +95 -0
- package/docs/harness/cli/schema-and-validation-commands.md +154 -0
- package/docs/harness/cli/search-commands.md +97 -0
- package/docs/harness/cli/spawn-command.md +136 -0
- package/docs/harness/cli/specs-command.md +102 -0
- package/docs/harness/cli/tools-command.md +122 -0
- package/docs/harness/cli/trace-command.md +122 -0
- package/docs/harness/cli-daemon.md +92 -0
- package/docs/harness/event-loop.md +184 -0
- package/docs/harness/hooks/README.md +15 -0
- package/docs/harness/hooks/context-hooks.md +96 -0
- package/docs/harness/hooks/lifecycle-and-observability-hooks.md +135 -0
- package/docs/harness/hooks/validation-hooks.md +97 -0
- package/docs/harness/test-harness.md +149 -0
- package/docs/harness/tui.md +176 -0
- package/docs/memories.md +20 -0
- package/docs/solutions/agentic-issues/premature-agent-deletion-tui-action-dependency-20260130.md +49 -0
- package/docs/solutions/agentic-issues/ref-anchor-scope-mismatch-skill-references-20260131.md +55 -0
- package/docs/solutions/agentic-issues/tautological-tests-routing-20260131.md +52 -0
- package/docs/solutions/integration_issue/blocktool-output-format-mismatch-hook-runner-20260130.md +52 -0
- package/docs/solutions/integration_issue/dual-validation-path-divergence-schema-20260130.md +66 -0
- package/docs/solutions/security-issues/unsanitized-domain-path-join-20260131.md +52 -0
- package/docs/solutions/test-failures/event-loop-mock-ordering-checkAgentWindows-20260130.md +63 -0
- package/docs/sync-cli/README.md +19 -0
- package/docs/sync-cli/cli-entrypoint-and-commands.md +39 -0
- package/docs/sync-cli/commands/README.md +11 -0
- package/docs/sync-cli/commands/pull-manifest-command.md +36 -0
- package/docs/sync-cli/commands/push-command.md +84 -0
- package/docs/sync-cli/commands/sync-command.md +71 -0
- package/docs/sync-cli/systems/README.md +14 -0
- package/docs/sync-cli/systems/git-and-github-integration.md +49 -0
- package/docs/sync-cli/systems/interactive-ui.md +43 -0
- package/docs/sync-cli/systems/manifest-and-distribution.md +51 -0
- package/docs/sync-cli/systems/path-resolution.md +42 -0
- package/package.json +46 -0
- package/scripts/install-shim.sh +40 -0
- package/scripts/pre-pack.sh +25 -0
- package/specs/harness-maintenance-skill.spec.md +138 -0
- package/specs/roadmap/git-spec-lifecycle-management.spec.md +113 -0
- package/specs/sync-init-flag.spec.md +117 -0
- package/specs/unified-workflow-orchestration.spec.md +250 -0
- package/specs/validation-tooling-practice.spec.md +98 -0
- package/specs/workflow-domain-configuration.spec.md +265 -0
- package/src/commands/pull-manifest.ts +31 -0
- package/src/commands/push.ts +344 -0
- package/src/commands/sync.ts +289 -0
- package/src/lib/constants.ts +10 -0
- package/src/lib/dotfiles.ts +36 -0
- package/src/lib/fs-utils.ts +18 -0
- package/src/lib/gh.ts +40 -0
- package/src/lib/git.ts +63 -0
- package/src/lib/gitignore.ts +167 -0
- package/src/lib/manifest.ts +121 -0
- package/src/lib/marker-sync.ts +39 -0
- package/src/lib/paths.ts +38 -0
- package/src/lib/target-lines.ts +66 -0
- package/src/lib/ui.ts +78 -0
- package/src/sync-cli.ts +120 -0
- package/target-lines.json +23 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ctags utilities for symbol lookup and validation.
|
|
3
|
+
*
|
|
4
|
+
* Uses universal-ctags to generate a symbol index for fast O(1) lookups.
|
|
5
|
+
* This enables documentation reference validation without AST parsing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawn, spawnSync } from "child_process";
|
|
9
|
+
import { existsSync } from "fs";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Single ctags entry representing a symbol in a file.
|
|
13
|
+
*/
|
|
14
|
+
export interface CtagsEntry {
|
|
15
|
+
name: string;
|
|
16
|
+
path: string;
|
|
17
|
+
line: number;
|
|
18
|
+
kind: string;
|
|
19
|
+
signature?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Indexed ctags data for O(1) lookups.
|
|
24
|
+
* Structure: Map<file, Map<symbol, CtagsEntry[]>>
|
|
25
|
+
* Multiple entries per symbol possible (overloads, same name in different scopes).
|
|
26
|
+
*/
|
|
27
|
+
export type CtagsIndex = Map<string, Map<string, CtagsEntry[]>>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Common paths where universal-ctags might be installed.
|
|
31
|
+
* Checked in order before falling back to PATH.
|
|
32
|
+
*/
|
|
33
|
+
const CTAGS_PATHS = [
|
|
34
|
+
"/opt/homebrew/bin/ctags", // macOS ARM homebrew
|
|
35
|
+
"/usr/local/bin/ctags", // macOS Intel homebrew / Linux
|
|
36
|
+
"/usr/bin/ctags", // System PATH (may be BSD ctags)
|
|
37
|
+
"ctags", // Fall back to PATH
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
/** Cached path to universal-ctags binary */
|
|
41
|
+
let cachedCtagsPath: string | null = null;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Find the universal-ctags binary, checking common paths first.
|
|
45
|
+
*/
|
|
46
|
+
function findUniversalCtags(): { path: string; version: string } | null {
|
|
47
|
+
for (const ctagsPath of CTAGS_PATHS) {
|
|
48
|
+
// Skip absolute paths that don't exist
|
|
49
|
+
if (ctagsPath.startsWith("/") && !existsSync(ctagsPath)) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const result = spawnSync(ctagsPath, ["--version"], { encoding: "utf-8" });
|
|
54
|
+
|
|
55
|
+
if (result.status !== 0) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const output = result.stdout || "";
|
|
60
|
+
if (output.includes("Universal Ctags") || output.includes("universal-ctags")) {
|
|
61
|
+
const versionMatch = output.match(/Universal Ctags\s+([\d.]+)/i);
|
|
62
|
+
const version = versionMatch ? versionMatch[1] : "unknown";
|
|
63
|
+
return { path: ctagsPath, version };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get the path to universal-ctags (cached).
|
|
72
|
+
*/
|
|
73
|
+
export function getCtagsPath(): string | null {
|
|
74
|
+
if (cachedCtagsPath !== null) {
|
|
75
|
+
return cachedCtagsPath;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const found = findUniversalCtags();
|
|
79
|
+
if (found) {
|
|
80
|
+
cachedCtagsPath = found.path;
|
|
81
|
+
return cachedCtagsPath;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Check if ctags (universal-ctags) is available.
|
|
89
|
+
*/
|
|
90
|
+
export function checkCtagsAvailable(): { available: boolean; version?: string; error?: string; path?: string } {
|
|
91
|
+
const found = findUniversalCtags();
|
|
92
|
+
|
|
93
|
+
if (!found) {
|
|
94
|
+
return {
|
|
95
|
+
available: false,
|
|
96
|
+
error:
|
|
97
|
+
"Universal Ctags not found. Install with: brew install universal-ctags\n" +
|
|
98
|
+
"Note: The BSD ctags that ships with macOS is not compatible.",
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
cachedCtagsPath = found.path;
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
available: true,
|
|
106
|
+
version: found.version,
|
|
107
|
+
path: found.path,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Parse a single line of ctags JSON output.
|
|
113
|
+
*/
|
|
114
|
+
function parseCtagsLine(line: string): CtagsEntry | null {
|
|
115
|
+
try {
|
|
116
|
+
const entry = JSON.parse(line);
|
|
117
|
+
|
|
118
|
+
// Skip ptag entries (pseudo-tags with metadata)
|
|
119
|
+
if (entry._type === "ptag") {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (entry._type !== "tag" || !entry.name || !entry.path) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
name: entry.name,
|
|
129
|
+
path: entry.path,
|
|
130
|
+
line: entry.line || 0,
|
|
131
|
+
kind: entry.kind || "unknown",
|
|
132
|
+
signature: entry.signature,
|
|
133
|
+
};
|
|
134
|
+
} catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Generate a ctags index for a directory.
|
|
141
|
+
*
|
|
142
|
+
* Uses ctags with JSON output format for reliable parsing.
|
|
143
|
+
* Excludes common non-source directories.
|
|
144
|
+
*/
|
|
145
|
+
export function generateCtagsIndex(
|
|
146
|
+
cwd: string,
|
|
147
|
+
options?: {
|
|
148
|
+
/** Additional exclude patterns */
|
|
149
|
+
exclude?: string[];
|
|
150
|
+
/** Specific file or directory to index (relative to cwd) */
|
|
151
|
+
target?: string;
|
|
152
|
+
}
|
|
153
|
+
): { index: CtagsIndex; success: boolean; error?: string; entryCount: number } {
|
|
154
|
+
const check = checkCtagsAvailable();
|
|
155
|
+
if (!check.available) {
|
|
156
|
+
return {
|
|
157
|
+
index: new Map(),
|
|
158
|
+
success: false,
|
|
159
|
+
error: check.error,
|
|
160
|
+
entryCount: 0,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const excludes = [
|
|
165
|
+
"node_modules",
|
|
166
|
+
".git",
|
|
167
|
+
"dist",
|
|
168
|
+
"build",
|
|
169
|
+
".next",
|
|
170
|
+
"coverage",
|
|
171
|
+
"__pycache__",
|
|
172
|
+
".venv",
|
|
173
|
+
"venv",
|
|
174
|
+
...(options?.exclude || []),
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
const args = [
|
|
178
|
+
"-R",
|
|
179
|
+
"--output-format=json",
|
|
180
|
+
"--fields=+nKS", // +n=line number, +K=kind, +S=signature
|
|
181
|
+
"-o",
|
|
182
|
+
"-", // Output to stdout
|
|
183
|
+
];
|
|
184
|
+
|
|
185
|
+
// Add exclude patterns
|
|
186
|
+
for (const exclude of excludes) {
|
|
187
|
+
args.push(`--exclude=${exclude}`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Add target if specified, otherwise index current directory
|
|
191
|
+
const target = options?.target || ".";
|
|
192
|
+
args.push(target);
|
|
193
|
+
|
|
194
|
+
const ctagsPath = getCtagsPath()!;
|
|
195
|
+
const result = spawnSync(ctagsPath, args, {
|
|
196
|
+
encoding: "utf-8",
|
|
197
|
+
cwd,
|
|
198
|
+
maxBuffer: 50 * 1024 * 1024, // 50MB buffer for large repos
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
if (result.status !== 0) {
|
|
202
|
+
return {
|
|
203
|
+
index: new Map(),
|
|
204
|
+
success: false,
|
|
205
|
+
error: `ctags failed: ${result.stderr || "unknown error"}`,
|
|
206
|
+
entryCount: 0,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Parse JSON output into index
|
|
211
|
+
const index: CtagsIndex = new Map();
|
|
212
|
+
let entryCount = 0;
|
|
213
|
+
|
|
214
|
+
const lines = result.stdout.split("\n");
|
|
215
|
+
for (const line of lines) {
|
|
216
|
+
if (!line.trim()) continue;
|
|
217
|
+
|
|
218
|
+
const entry = parseCtagsLine(line);
|
|
219
|
+
if (!entry) continue;
|
|
220
|
+
|
|
221
|
+
entryCount++;
|
|
222
|
+
|
|
223
|
+
// Get or create file map
|
|
224
|
+
let fileMap = index.get(entry.path);
|
|
225
|
+
if (!fileMap) {
|
|
226
|
+
fileMap = new Map();
|
|
227
|
+
index.set(entry.path, fileMap);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Get or create symbol array
|
|
231
|
+
let symbols = fileMap.get(entry.name);
|
|
232
|
+
if (!symbols) {
|
|
233
|
+
symbols = [];
|
|
234
|
+
fileMap.set(entry.name, symbols);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
symbols.push(entry);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return { index, success: true, entryCount };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Look up a symbol in a specific file.
|
|
245
|
+
*
|
|
246
|
+
* Returns all matching entries (may have multiple for overloads).
|
|
247
|
+
*/
|
|
248
|
+
export function lookupSymbol(
|
|
249
|
+
index: CtagsIndex,
|
|
250
|
+
filePath: string,
|
|
251
|
+
symbolName: string
|
|
252
|
+
): CtagsEntry[] {
|
|
253
|
+
const fileMap = index.get(filePath);
|
|
254
|
+
if (!fileMap) {
|
|
255
|
+
return [];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return fileMap.get(symbolName) || [];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Look up a symbol in any file (for searching).
|
|
263
|
+
*
|
|
264
|
+
* Returns all matching entries across all files.
|
|
265
|
+
*/
|
|
266
|
+
export function searchSymbol(
|
|
267
|
+
index: CtagsIndex,
|
|
268
|
+
symbolName: string
|
|
269
|
+
): Array<CtagsEntry & { file: string }> {
|
|
270
|
+
const results: Array<CtagsEntry & { file: string }> = [];
|
|
271
|
+
|
|
272
|
+
for (const [file, fileMap] of index) {
|
|
273
|
+
const entries = fileMap.get(symbolName);
|
|
274
|
+
if (entries) {
|
|
275
|
+
for (const entry of entries) {
|
|
276
|
+
results.push({ ...entry, file });
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return results;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Get all symbols in a file.
|
|
286
|
+
*/
|
|
287
|
+
export function getFileSymbols(index: CtagsIndex, filePath: string): CtagsEntry[] {
|
|
288
|
+
const fileMap = index.get(filePath);
|
|
289
|
+
if (!fileMap) {
|
|
290
|
+
return [];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const symbols: CtagsEntry[] = [];
|
|
294
|
+
for (const entries of fileMap.values()) {
|
|
295
|
+
symbols.push(...entries);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Sort by line number
|
|
299
|
+
return symbols.sort((a, b) => a.line - b.line);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Generate ctags for a single file (faster for format-reference command).
|
|
304
|
+
*/
|
|
305
|
+
export function generateFileCtags(
|
|
306
|
+
filePath: string,
|
|
307
|
+
cwd: string
|
|
308
|
+
): { entries: CtagsEntry[]; success: boolean; error?: string } {
|
|
309
|
+
if (!existsSync(filePath)) {
|
|
310
|
+
return { entries: [], success: false, error: "File not found" };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const check = checkCtagsAvailable();
|
|
314
|
+
if (!check.available) {
|
|
315
|
+
return { entries: [], success: false, error: check.error };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const args = [
|
|
319
|
+
"--output-format=json",
|
|
320
|
+
"--fields=+nKS",
|
|
321
|
+
"-o",
|
|
322
|
+
"-",
|
|
323
|
+
filePath,
|
|
324
|
+
];
|
|
325
|
+
|
|
326
|
+
const ctagsPath = getCtagsPath()!;
|
|
327
|
+
const result = spawnSync(ctagsPath, args, {
|
|
328
|
+
encoding: "utf-8",
|
|
329
|
+
cwd,
|
|
330
|
+
maxBuffer: 10 * 1024 * 1024, // 10MB buffer
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
if (result.status !== 0) {
|
|
334
|
+
return {
|
|
335
|
+
entries: [],
|
|
336
|
+
success: false,
|
|
337
|
+
error: `ctags failed: ${result.stderr || "unknown error"}`,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const entries: CtagsEntry[] = [];
|
|
342
|
+
const lines = result.stdout.split("\n");
|
|
343
|
+
|
|
344
|
+
for (const line of lines) {
|
|
345
|
+
if (!line.trim()) continue;
|
|
346
|
+
|
|
347
|
+
const entry = parseCtagsLine(line);
|
|
348
|
+
if (entry) {
|
|
349
|
+
entries.push(entry);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return { entries, success: true };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Find a specific symbol in a file (convenience function).
|
|
358
|
+
* Uses single-file ctags for efficiency.
|
|
359
|
+
*/
|
|
360
|
+
export function findSymbolInFile(
|
|
361
|
+
filePath: string,
|
|
362
|
+
symbolName: string,
|
|
363
|
+
cwd: string
|
|
364
|
+
): CtagsEntry | null {
|
|
365
|
+
const { entries, success } = generateFileCtags(filePath, cwd);
|
|
366
|
+
if (!success) {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Return first match (usually there's only one)
|
|
371
|
+
return entries.find((e) => e.name === symbolName) || null;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Generate a ctags index asynchronously (non-blocking).
|
|
376
|
+
*
|
|
377
|
+
* Same behavior as generateCtagsIndex but uses spawn instead of spawnSync,
|
|
378
|
+
* keeping the event loop free during the ctags process.
|
|
379
|
+
*/
|
|
380
|
+
export async function generateCtagsIndexAsync(
|
|
381
|
+
cwd: string,
|
|
382
|
+
options?: {
|
|
383
|
+
/** Additional exclude patterns */
|
|
384
|
+
exclude?: string[];
|
|
385
|
+
/** Specific file or directory to index (relative to cwd) */
|
|
386
|
+
target?: string;
|
|
387
|
+
}
|
|
388
|
+
): Promise<{ index: CtagsIndex; success: boolean; error?: string; entryCount: number }> {
|
|
389
|
+
const check = checkCtagsAvailable();
|
|
390
|
+
if (!check.available) {
|
|
391
|
+
return {
|
|
392
|
+
index: new Map(),
|
|
393
|
+
success: false,
|
|
394
|
+
error: check.error,
|
|
395
|
+
entryCount: 0,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const excludes = [
|
|
400
|
+
"node_modules",
|
|
401
|
+
".git",
|
|
402
|
+
"dist",
|
|
403
|
+
"build",
|
|
404
|
+
".next",
|
|
405
|
+
"coverage",
|
|
406
|
+
"__pycache__",
|
|
407
|
+
".venv",
|
|
408
|
+
"venv",
|
|
409
|
+
...(options?.exclude || []),
|
|
410
|
+
];
|
|
411
|
+
|
|
412
|
+
const args = [
|
|
413
|
+
"-R",
|
|
414
|
+
"--output-format=json",
|
|
415
|
+
"--fields=+nKS",
|
|
416
|
+
"-o",
|
|
417
|
+
"-",
|
|
418
|
+
];
|
|
419
|
+
|
|
420
|
+
for (const exclude of excludes) {
|
|
421
|
+
args.push(`--exclude=${exclude}`);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const target = options?.target || ".";
|
|
425
|
+
args.push(target);
|
|
426
|
+
|
|
427
|
+
const ctagsPath = getCtagsPath()!;
|
|
428
|
+
|
|
429
|
+
return new Promise((resolve) => {
|
|
430
|
+
const child = spawn(ctagsPath, args, {
|
|
431
|
+
cwd,
|
|
432
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
const chunks: Buffer[] = [];
|
|
436
|
+
let stderrOutput = "";
|
|
437
|
+
|
|
438
|
+
child.stdout?.on("data", (data: Buffer) => {
|
|
439
|
+
chunks.push(data);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
child.stderr?.on("data", (data: Buffer) => {
|
|
443
|
+
stderrOutput += data.toString();
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
child.on("close", (code) => {
|
|
447
|
+
if (code !== 0) {
|
|
448
|
+
resolve({
|
|
449
|
+
index: new Map(),
|
|
450
|
+
success: false,
|
|
451
|
+
error: `ctags failed: ${stderrOutput || "unknown error"}`,
|
|
452
|
+
entryCount: 0,
|
|
453
|
+
});
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const stdout = Buffer.concat(chunks).toString("utf-8");
|
|
458
|
+
const index: CtagsIndex = new Map();
|
|
459
|
+
let entryCount = 0;
|
|
460
|
+
|
|
461
|
+
const lines = stdout.split("\n");
|
|
462
|
+
for (const line of lines) {
|
|
463
|
+
if (!line.trim()) continue;
|
|
464
|
+
|
|
465
|
+
const entry = parseCtagsLine(line);
|
|
466
|
+
if (!entry) continue;
|
|
467
|
+
|
|
468
|
+
entryCount++;
|
|
469
|
+
|
|
470
|
+
let fileMap = index.get(entry.path);
|
|
471
|
+
if (!fileMap) {
|
|
472
|
+
fileMap = new Map();
|
|
473
|
+
index.set(entry.path, fileMap);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
let symbols = fileMap.get(entry.name);
|
|
477
|
+
if (!symbols) {
|
|
478
|
+
symbols = [];
|
|
479
|
+
fileMap.set(entry.name, symbols);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
symbols.push(entry);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
resolve({ index, success: true, entryCount });
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
child.on("error", (err) => {
|
|
489
|
+
resolve({
|
|
490
|
+
index: new Map(),
|
|
491
|
+
success: false,
|
|
492
|
+
error: `ctags spawn error: ${err.message}`,
|
|
493
|
+
entryCount: 0,
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
}
|