claude-all-hands 1.0.2 → 1.0.3

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 (35) hide show
  1. package/.claude/agents/curator.md +1 -5
  2. package/.claude/agents/documentation-taxonomist.md +255 -0
  3. package/.claude/agents/documentation-writer.md +366 -0
  4. package/.claude/agents/surveyor.md +1 -1
  5. package/.claude/commands/continue.md +12 -10
  6. package/.claude/commands/create-skill.md +2 -2
  7. package/.claude/commands/create-specialist.md +3 -3
  8. package/.claude/commands/debug.md +5 -5
  9. package/.claude/commands/docs-adjust.md +214 -0
  10. package/.claude/commands/docs-audit.md +172 -0
  11. package/.claude/commands/docs-init.md +210 -0
  12. package/.claude/commands/plan.md +6 -6
  13. package/.claude/commands/whats-next.md +2 -2
  14. package/.claude/envoy/README.md +5 -5
  15. package/.claude/envoy/package-lock.json +216 -10
  16. package/.claude/envoy/package.json +9 -0
  17. package/.claude/envoy/src/commands/docs.ts +881 -0
  18. package/.claude/envoy/src/commands/knowledge.ts +33 -42
  19. package/.claude/envoy/src/lib/ast-queries.ts +261 -0
  20. package/.claude/envoy/src/lib/knowledge.ts +176 -124
  21. package/.claude/envoy/src/lib/tree-sitter-utils.ts +301 -0
  22. package/.claude/envoy/src/types/tree-sitter.d.ts +76 -0
  23. package/.claude/hooks/scripts/enforce_research_fetch.py +1 -1
  24. package/.claude/protocols/bug-discovery.yaml +1 -1
  25. package/.claude/protocols/discovery.yaml +1 -1
  26. package/.claude/settings.json +4 -3
  27. package/.claude/skills/discovery-mode/SKILL.md +7 -7
  28. package/.claude/skills/documentation-taxonomy/SKILL.md +287 -0
  29. package/.claude/skills/implementation-mode/SKILL.md +7 -7
  30. package/.claude/skills/knowledge-discovery/SKILL.md +178 -0
  31. package/bin/cli.js +41 -1
  32. package/package.json +1 -1
  33. package/.claude/agents/documentor.md +0 -147
  34. package/.claude/commands/audit-docs.md +0 -94
  35. package/.claude/commands/create-docs.md +0 -100
@@ -0,0 +1,301 @@
1
+ /**
2
+ * Tree-sitter utilities for AST parsing and symbol resolution.
3
+ * Used by docs commands for symbol lookup and validation.
4
+ */
5
+
6
+ import { readFileSync, existsSync } from "fs";
7
+ import { extname } from "path";
8
+ import {
9
+ getLanguageForExtension,
10
+ getQueriesForLanguage,
11
+ type LanguageQueries,
12
+ } from "./ast-queries.js";
13
+
14
+ export interface SymbolLocation {
15
+ name: string;
16
+ startLine: number;
17
+ endLine: number;
18
+ type: string;
19
+ }
20
+
21
+ export interface ParseResult {
22
+ success: boolean;
23
+ language?: string;
24
+ symbols?: SymbolLocation[];
25
+ error?: string;
26
+ }
27
+
28
+ // Tree-sitter Query types
29
+ interface QueryCapture {
30
+ name: string;
31
+ node: {
32
+ text: string;
33
+ startPosition: { row: number; column: number };
34
+ endPosition: { row: number; column: number };
35
+ };
36
+ }
37
+
38
+ interface QueryMatch {
39
+ pattern: number;
40
+ captures: QueryCapture[];
41
+ }
42
+
43
+ interface Query {
44
+ matches(node: unknown): QueryMatch[];
45
+ }
46
+
47
+ interface QueryConstructor {
48
+ new (grammar: unknown, queryString: string): Query;
49
+ }
50
+
51
+ // Parser data type including Query constructor
52
+ interface ParserData {
53
+ parser: unknown;
54
+ grammar: unknown;
55
+ QueryClass: QueryConstructor;
56
+ }
57
+
58
+ // Lazy-loaded parsers cache - stores parser, grammar, and Query class
59
+ const parserCache = new Map<string, ParserData>();
60
+
61
+ /**
62
+ * Get or create a tree-sitter parser for a language.
63
+ * Lazily loads grammars to avoid startup cost.
64
+ * Returns parser, grammar, and Query class (needed for Query API).
65
+ */
66
+ async function getParser(language: string): Promise<ParserData | null> {
67
+ if (parserCache.has(language)) {
68
+ return parserCache.get(language)!;
69
+ }
70
+
71
+ try {
72
+ // Dynamic import tree-sitter
73
+ const TreeSitter = await import("tree-sitter");
74
+ const Parser = TreeSitter.default;
75
+ const QueryClass = (TreeSitter as unknown as { Query: QueryConstructor }).Query;
76
+
77
+ // Load language grammar
78
+ let grammar: unknown;
79
+ switch (language) {
80
+ case "typescript":
81
+ grammar = (await import("tree-sitter-typescript")).default.typescript;
82
+ break;
83
+ case "javascript":
84
+ grammar = (await import("tree-sitter-javascript")).default;
85
+ break;
86
+ case "python":
87
+ grammar = (await import("tree-sitter-python")).default;
88
+ break;
89
+ case "go":
90
+ grammar = (await import("tree-sitter-go")).default;
91
+ break;
92
+ case "rust":
93
+ grammar = (await import("tree-sitter-rust")).default;
94
+ break;
95
+ case "java":
96
+ grammar = (await import("tree-sitter-java")).default;
97
+ break;
98
+ case "ruby":
99
+ grammar = (await import("tree-sitter-ruby")).default;
100
+ break;
101
+ case "swift":
102
+ grammar = (await import("tree-sitter-swift")).default;
103
+ break;
104
+ default:
105
+ return null;
106
+ }
107
+
108
+ const parser = new Parser();
109
+ parser.setLanguage(grammar as Parameters<typeof parser.setLanguage>[0]);
110
+ const cached: ParserData = { parser, grammar, QueryClass };
111
+ parserCache.set(language, cached);
112
+ return cached;
113
+ } catch (e) {
114
+ console.error(`Failed to load parser for ${language}:`, e);
115
+ return null;
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Parse a file and extract all symbol definitions.
121
+ */
122
+ export async function parseFile(filePath: string): Promise<ParseResult> {
123
+ const ext = extname(filePath);
124
+ const language = getLanguageForExtension(ext);
125
+
126
+ if (!language) {
127
+ return {
128
+ success: false,
129
+ error: `Unsupported file extension: ${ext}`,
130
+ };
131
+ }
132
+
133
+ if (!existsSync(filePath)) {
134
+ return {
135
+ success: false,
136
+ error: `File not found: ${filePath}`,
137
+ };
138
+ }
139
+
140
+ const parserData = await getParser(language);
141
+ if (!parserData) {
142
+ return {
143
+ success: false,
144
+ error: `No parser available for ${language}`,
145
+ };
146
+ }
147
+
148
+ try {
149
+ const sourceCode = readFileSync(filePath, "utf-8");
150
+ const tree = (parserData.parser as { parse(source: string): unknown }).parse(sourceCode);
151
+ const queries = getQueriesForLanguage(language);
152
+
153
+ if (!queries) {
154
+ return {
155
+ success: false,
156
+ error: `No queries defined for ${language}`,
157
+ };
158
+ }
159
+
160
+ const symbols = extractSymbols(tree, queries, parserData.grammar, parserData.QueryClass);
161
+
162
+ return {
163
+ success: true,
164
+ language,
165
+ symbols,
166
+ };
167
+ } catch (e) {
168
+ return {
169
+ success: false,
170
+ error: e instanceof Error ? e.message : String(e),
171
+ };
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Find a specific symbol in a file.
177
+ */
178
+ export async function findSymbol(
179
+ filePath: string,
180
+ symbolName: string
181
+ ): Promise<SymbolLocation | null> {
182
+ const result = await parseFile(filePath);
183
+
184
+ if (!result.success || !result.symbols) {
185
+ return null;
186
+ }
187
+
188
+ return result.symbols.find((s) => s.name === symbolName) || null;
189
+ }
190
+
191
+ /**
192
+ * Check if a symbol exists in a file.
193
+ */
194
+ export async function symbolExists(
195
+ filePath: string,
196
+ symbolName: string
197
+ ): Promise<boolean> {
198
+ const symbol = await findSymbol(filePath, symbolName);
199
+ return symbol !== null;
200
+ }
201
+
202
+ // Cache compiled queries to avoid recompilation
203
+ const queryCache = new Map<string, Query>();
204
+
205
+ /**
206
+ * Extract symbols from a parsed tree using tree-sitter's Query API.
207
+ * This uses the declarative query patterns from ast-queries.ts.
208
+ */
209
+ function extractSymbols(
210
+ tree: unknown,
211
+ queries: LanguageQueries,
212
+ grammar: unknown,
213
+ QueryClass: QueryConstructor
214
+ ): SymbolLocation[] {
215
+ const symbols: SymbolLocation[] = [];
216
+ const root = (tree as { rootNode: unknown }).rootNode;
217
+
218
+ for (const [symbolType, queryDef] of Object.entries(queries)) {
219
+ const cacheKey = `${symbolType}:${queryDef.query}`;
220
+
221
+ let query = queryCache.get(cacheKey);
222
+ if (!query) {
223
+ try {
224
+ query = new QueryClass(grammar, queryDef.query);
225
+ queryCache.set(cacheKey, query);
226
+ } catch (e) {
227
+ // Query compilation failed - skip this symbol type
228
+ console.error(`Failed to compile query for ${symbolType}:`, e);
229
+ continue;
230
+ }
231
+ }
232
+
233
+ try {
234
+ const matches = query.matches(root);
235
+ for (const match of matches) {
236
+ const nameCapture = match.captures.find(c => c.name === queryDef.nameCapture);
237
+ if (nameCapture) {
238
+ symbols.push({
239
+ name: nameCapture.node.text,
240
+ startLine: nameCapture.node.startPosition.row + 1,
241
+ endLine: nameCapture.node.endPosition.row + 1,
242
+ type: symbolType,
243
+ });
244
+ }
245
+ }
246
+ } catch (e) {
247
+ // Query execution failed - skip this symbol type
248
+ console.error(`Failed to execute query for ${symbolType}:`, e);
249
+ continue;
250
+ }
251
+ }
252
+
253
+ return symbols;
254
+ }
255
+
256
+ /**
257
+ * Get complexity metrics for a file.
258
+ */
259
+ export async function getFileComplexity(filePath: string): Promise<{
260
+ lines: number;
261
+ imports: number;
262
+ exports: number;
263
+ functions: number;
264
+ classes: number;
265
+ } | null> {
266
+ if (!existsSync(filePath)) {
267
+ return null;
268
+ }
269
+
270
+ try {
271
+ const content = readFileSync(filePath, "utf-8");
272
+ const lines = content.split("\n").length;
273
+
274
+ const result = await parseFile(filePath);
275
+ if (!result.success || !result.symbols) {
276
+ // Fallback to regex-based counting for unsupported files
277
+ return {
278
+ lines,
279
+ imports: (content.match(/^import\s/gm) || []).length,
280
+ exports: (content.match(/^export\s/gm) || []).length,
281
+ functions: (content.match(/function\s+\w+/g) || []).length,
282
+ classes: (content.match(/class\s+\w+/g) || []).length,
283
+ };
284
+ }
285
+
286
+ const functions = result.symbols.filter(
287
+ (s) => s.type === "function" || s.type === "method" || s.type === "arrowFunction"
288
+ ).length;
289
+ const classes = result.symbols.filter(
290
+ (s) => s.type === "class" || s.type === "struct" || s.type === "interface"
291
+ ).length;
292
+
293
+ // Count imports/exports via regex (simpler than AST for this)
294
+ const imports = (content.match(/^import\s/gm) || []).length;
295
+ const exports = (content.match(/^export\s/gm) || []).length;
296
+
297
+ return { lines, imports, exports, functions, classes };
298
+ } catch {
299
+ return null;
300
+ }
301
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Type declarations for tree-sitter and language grammars.
3
+ * These packages don't have bundled types, so we declare them here.
4
+ */
5
+
6
+ declare module "tree-sitter" {
7
+ export interface SyntaxNode {
8
+ type: string;
9
+ text: string;
10
+ startPosition: { row: number; column: number };
11
+ endPosition: { row: number; column: number };
12
+ children: SyntaxNode[];
13
+ namedChildren: SyntaxNode[];
14
+ childForFieldName(fieldName: string): SyntaxNode | null;
15
+ parent: SyntaxNode | null;
16
+ }
17
+
18
+ export interface Tree {
19
+ rootNode: SyntaxNode;
20
+ }
21
+
22
+ export interface Language {}
23
+
24
+ export default class Parser {
25
+ setLanguage(language: Language): void;
26
+ parse(input: string): Tree;
27
+ }
28
+ }
29
+
30
+ declare module "tree-sitter-typescript" {
31
+ import { Language } from "tree-sitter";
32
+ const grammar: { typescript: Language; tsx: Language };
33
+ export default grammar;
34
+ }
35
+
36
+ declare module "tree-sitter-javascript" {
37
+ import { Language } from "tree-sitter";
38
+ const grammar: Language;
39
+ export default grammar;
40
+ }
41
+
42
+ declare module "tree-sitter-python" {
43
+ import { Language } from "tree-sitter";
44
+ const grammar: Language;
45
+ export default grammar;
46
+ }
47
+
48
+ declare module "tree-sitter-go" {
49
+ import { Language } from "tree-sitter";
50
+ const grammar: Language;
51
+ export default grammar;
52
+ }
53
+
54
+ declare module "tree-sitter-rust" {
55
+ import { Language } from "tree-sitter";
56
+ const grammar: Language;
57
+ export default grammar;
58
+ }
59
+
60
+ declare module "tree-sitter-java" {
61
+ import { Language } from "tree-sitter";
62
+ const grammar: Language;
63
+ export default grammar;
64
+ }
65
+
66
+ declare module "tree-sitter-ruby" {
67
+ import { Language } from "tree-sitter";
68
+ const grammar: Language;
69
+ export default grammar;
70
+ }
71
+
72
+ declare module "tree-sitter-swift" {
73
+ import { Language } from "tree-sitter";
74
+ const grammar: Language;
75
+ export default grammar;
76
+ }
@@ -12,6 +12,6 @@ if not url:
12
12
  # JSON output with additionalContext for Claude self-correction
13
13
  print(json.dumps({
14
14
  "continue": False,
15
- "additionalContext": "WebFetch blocked.\n\nMain agent: delegate to researcher agent.\nSubagent: use `.claude/envoy/envoy tavily extract \"<url>\"` instead."
15
+ "additionalContext": "WebFetch blocked.\n\nMain agent: delegate to researcher agent.\nSubagent: use `envoy tavily extract \"<url>\"` instead."
16
16
  }))
17
17
  sys.exit(0)
@@ -19,7 +19,7 @@ outputs:
19
19
  description: bug hypotheses written to file via envoy commands
20
20
  steps:
21
21
  1: |
22
- **Query documentation first**: Call `envoy knowledge search docs "<suspected area + symptoms as descriptive phrase>"` (semantic search - full phrases, not keywords)
22
+ **Query documentation first**: Call `envoy knowledge search "<suspected area + symptoms as descriptive phrase>"` (semantic search - full phrases, not keywords)
23
23
  * Check for documented anti-patterns that might explain the bug
24
24
  * Note any constraints that must be preserved in the fix
25
25
  2: |
@@ -19,7 +19,7 @@ outputs:
19
19
  description: findings written to file via envoy commands
20
20
  steps:
21
21
  1: |
22
- **Query documentation first**: Call `envoy knowledge search docs "<focused requirement area as descriptive request>"` (semantic search - full phrases, not keywords)
22
+ **Query documentation first**: Call `envoy knowledge search "<focused requirement area as descriptive request>"` (semantic search - full phrases, not keywords)
23
23
  * May run multiple searches if requirements span distinct focus areas
24
24
  * Use returned docs as context for approach building - reference existing patterns rather than reinventing
25
25
  * Note any constraints or anti-patterns documented for this area
@@ -3,8 +3,8 @@
3
3
  "env": {
4
4
  "PARALLEL_MAX_WORKERS": "3",
5
5
  "PROJECT_NAME": "test-target",
6
- "SEARCH_SIMILARITY_THRESHOLD": "0.64",
7
- "SEARCH_FULL_CONTEXT_SIMILARITY_THRESHOLD": "0.72",
6
+ "SEARCH_SIMILARITY_THRESHOLD": "0.65",
7
+ "SEARCH_FULL_CONTEXT_SIMILARITY_THRESHOLD": "0.82",
8
8
  "SEARCH_CONTEXT_TOKEN_LIMIT": "5000",
9
9
  "MAX_LOGS_TOKENS": "10000",
10
10
  "BASH_MAX_TIMEOUT_MS": "3600000",
@@ -21,9 +21,10 @@
21
21
  "Bash(ls:*)",
22
22
  "Bash(rg:*)",
23
23
  "Bash(mkdir:*)",
24
+ "Bash(mkdir -p:*)",
24
25
  "Bash(npx repomix@latest:*)",
25
26
  "Read(~/.claude-code-docs/docs/*)",
26
- "Bash(.claude/envoy/envoy:*)",
27
+ "Bash(envoy:*)",
27
28
  "SlashCommand(/plan:*)"
28
29
  ],
29
30
  "deny": [],
@@ -10,14 +10,14 @@ Enable agents to perform read-only codebase analysis during planning phase. Agen
10
10
  <quick_start>
11
11
  ```bash
12
12
  # Write approach (required)
13
- .claude/envoy/envoy plans write-approach \
13
+ envoy plans write-approach \
14
14
  --specialist "<your-name>" \
15
15
  --summary "2-3 sentence summary" \
16
16
  --files '[{"path": "src/auth/service.ts", "purpose": "Core auth logic"}]' \
17
17
  --content "Full analysis..."
18
18
 
19
19
  # Write options (when alternatives exist)
20
- .claude/envoy/envoy plans write-option \
20
+ envoy plans write-option \
21
21
  --specialist "<your-name>" \
22
22
  --id "1A" --group "Token storage" --name "httpOnly cookies" \
23
23
  --summary "Store tokens in httpOnly cookies..." \
@@ -44,7 +44,7 @@ Use: Glob, Grep, Read
44
44
 
45
45
  **Approach (required)**:
46
46
  ```bash
47
- .claude/envoy/envoy plans write-approach \
47
+ envoy plans write-approach \
48
48
  --specialist "<name>" \
49
49
  --summary "Brief summary for main agent" \
50
50
  --files '[{"path": "...", "purpose": "..."}]' \
@@ -54,7 +54,7 @@ Use: Glob, Grep, Read
54
54
 
55
55
  **Options (only if alternatives exist that the approach file mentions)**:
56
56
  ```bash
57
- .claude/envoy/envoy plans write-option \
57
+ envoy plans write-option \
58
58
  --specialist "<name>" \
59
59
  --id "1A" \
60
60
  --group "Token storage" \
@@ -83,13 +83,13 @@ When re-invoked after user feedback (re-discovery loop):
83
83
 
84
84
  ```bash
85
85
  # Check for existing findings
86
- .claude/envoy/envoy plans has-findings --specialist "<name>"
86
+ envoy plans has-findings --specialist "<name>"
87
87
 
88
88
  # Read previous approach
89
- .claude/envoy/envoy plans read-finding --specialist "<name>" --type approach
89
+ envoy plans read-finding --specialist "<name>" --type approach
90
90
 
91
91
  # Write new approach with --replace (clears existing options)
92
- .claude/envoy/envoy plans write-approach \
92
+ envoy plans write-approach \
93
93
  --specialist "<name>" \
94
94
  --replace \
95
95
  --summary "..." \