cqs 1.26.0

Code intelligence and RAG for AI agents. Semantic search, call graphs, impact analysis, type dependencies, and smart context assembly — in single tool calls. 54 languages + L5X/L5K PLC exports, 91.2% Recall@1 (BGE-large), 0.951 MRR (296 queries). Local ML, GPU-accelerated.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
//! Task planning with template classification.
//!
//! Classifies a task description into one of 11 task-type templates,
//! runs scout for relevant code, and produces an implementation checklist.

use std::path::Path;

use serde::Serialize;

use crate::scout::{scout, ScoutResult};
use crate::{Embedder, Store};

/// A task-type template for implementation planning.
#[derive(Debug, Clone, Serialize)]
pub struct TaskTemplate {
    pub name: &'static str,
    pub description: &'static str,
    pub checklist: &'static [&'static str],
    pub patterns: &'static [&'static str],
}

/// Result of a plan operation.
#[derive(Debug, Clone, Serialize)]
pub struct PlanResult {
    pub template: String,
    pub template_description: String,
    pub checklist: Vec<String>,
    pub patterns: Vec<String>,
    pub scout: ScoutResult,
}

// ---------------------------------------------------------------------------
// Template definitions
// ---------------------------------------------------------------------------

struct TemplateEntry {
    template: TaskTemplate,
    keywords: &'static [(&'static str, f32)],
}

const TEMPLATES: &[TemplateEntry] = &[
    TemplateEntry {
        template: TaskTemplate {
            name: "Add/Replace a CLI Flag",
            description: "Adding a new flag, renaming a flag, changing a flag's type",
            checklist: &[
                "src/cli/definitions.rs — Commands enum variant: add/modify #[arg] field",
                "src/cli/dispatch.rs — run_with() match arm: update destructuring",
                "src/cli/commands/<category>/<name>.rs — cmd_<name>() signature and branching logic",
                "src/cli/commands/<category>/<name>.rs — Display functions if flag affects output",
                "src/store/*.rs / src/lib.rs — Only if flag affects query behavior",
                "tests/<name>_test.rs — Add case for new value",
                ".claude/skills/cqs/SKILL.md — Update argument-hint and usage",
                "README.md — Update examples if command is featured",
            ],
            patterns: &[
                "Output format flags: #[arg(long, value_enum, default_value_t)]",
                "Display functions: display_<name>_text(), display_<name>_json()",
                "JSON output: serde_json::to_string_pretty on #[derive(Serialize)] structs",
            ],
        },
        keywords: &[
            ("flag", 2.0), ("arg", 1.5), ("--", 2.0), ("clap", 1.5),
            ("option", 1.0), ("parameter", 0.5),
        ],
    },
    TemplateEntry {
        template: TaskTemplate {
            name: "Add a New CLI Command",
            description: "Adding an entirely new cqs <command>",
            checklist: &[
                "src/cli/definitions.rs — Add variant to Commands enum with args",
                "src/cli/dispatch.rs — Add match arm in run_with()",
                "src/cli/commands/<category>/<name>.rs — New file: cmd_<name>() following command pattern",
                "src/cli/commands/<category>/mod.rs — Add mod + pub(crate) use",
                "src/lib.rs or src/<module>.rs — Library function if logic is non-trivial",
                "tests/<name>_test.rs — Integration tests",
                ".claude/skills/cqs/SKILL.md — Add to commands list",
                "CLAUDE.md — Add to key commands list",
                "README.md — Add to command reference",
                "CONTRIBUTING.md — Update Architecture Overview",
            ],
            patterns: &[
                "Command files are ~50-150 lines: store/library calls, then display",
                "Boilerplate: find_project_root() + resolve_index_dir() + Store::open()",
                "JSON output with --json flag, text output respects --quiet",
                "Tracing span at entry: let _span = tracing::info_span!(\"cmd_<name>\").entered()",
            ],
        },
        keywords: &[
            ("new command", 3.0), ("add command", 3.0), ("subcommand", 2.0),
            ("command", 1.0), ("cli command", 2.0),
        ],
    },
    TemplateEntry {
        template: TaskTemplate {
            name: "Fix a Bug",
            description: "Something produces wrong results, panics, or misbehaves",
            checklist: &[
                "Reproduce: understand exact failure (input → actual → expected)",
                "Locate: cqs scout to find relevant code",
                "Trace callers: cqs callers <function> — who calls the buggy code?",
                "Check tests: cqs test-map <function> — do tests cover the failing case?",
                "Fix: minimal change in the library layer, not the CLI layer",
                "Add test: regression test that would have caught this bug",
                "Check impact: cqs impact <function> — did the fix change behavior for other callers?",
            ],
            patterns: &[
                "Fix in src/*.rs (library), test in tests/*.rs or inline #[cfg(test)]",
                "Use tracing::warn! for recoverable errors, bail! for unrecoverable",
                "Never .unwrap() in library code — use ? or match + tracing::warn!",
            ],
        },
        keywords: &[
            ("bug", 2.0), ("fix", 1.5), ("broken", 2.0), ("wrong", 1.5),
            ("crash", 2.0), ("panic", 2.0), ("error", 0.5), ("fail", 1.0),
            ("incorrect", 1.5), ("regression", 1.5),
        ],
    },
    TemplateEntry {
        template: TaskTemplate {
            name: "Add Language Support",
            description: "Adding a new programming language to the parser",
            checklist: &[
                "Cargo.toml — Add tree-sitter grammar dependency (optional)",
                "Cargo.toml features — Add lang-<name> feature, add to default and lang-all",
                "src/language/mod.rs — Add to define_languages! macro invocation",
                "src/language/<lang>.rs — New file: LanguageDef with chunk_query, call_query, extensions",
                "tests/fixtures/sample.<ext> — Sample file for parser tests",
                "tests/parser_test.rs — Parser tests for the new language",
            ],
            patterns: &[
                "One-liner in define_languages! handles registration",
                "Chunk query captures must use names from capture_types: function, struct, class, enum, trait, interface, const",
                "Call query uses @callee capture",
                "Look at similar languages for query patterns (e.g., Ruby for dynamic, Haskell for functional)",
            ],
        },
        keywords: &[
            ("language", 2.0), ("parser", 1.5), ("lang-", 1.5),
            ("language support", 3.0), ("add language", 3.0),
        ],
    },
    TemplateEntry {
        template: TaskTemplate {
            name: "Add ChunkType Variant",
            description: "Adding a new chunk type (e.g., Extension, Protocol)",
            checklist: &[
                "src/language/mod.rs — Add one line to define_chunk_types! macro (Display, FromStr auto-generated)",
                "src/language/mod.rs — Update is_callable() and human_name() if needed",
                "src/language/mod.rs — Add capture name mapping in capture_name_to_chunk_type()",
                "src/nl.rs — Add natural language label for the variant",
                "src/language/<lang>.rs — Add capture using the new variant name in chunk_query",
                "tests/parser_test.rs — Parser tests for each language using the variant",
                "ROADMAP.md — Update ChunkType Variant Status table",
            ],
            patterns: &[
                "is_callable() returns true for Function, Method, Macro — most others false",
                "define_chunk_types! generates Display (lowercase), FromStr (snake_case + spaces), all_names()",
                "capture_name_to_chunk_type() maps tree-sitter capture names to ChunkType (may differ from Display)",
                "Container extraction uses capture_types to decide container vs leaf",
            ],
        },
        keywords: &[
            ("chunk type", 3.0), ("ChunkType", 3.0), ("variant", 1.5),
            ("chunk variant", 3.0),
        ],
    },
    TemplateEntry {
        template: TaskTemplate {
            name: "Add Injection Rule",
            description: "Adding multi-grammar parsing (e.g., HTML→JS, PHP→HTML)",
            checklist: &[
                "src/language/<host>.rs — Add InjectionRule to LanguageDef::injection_rules()",
                "src/language/<target>.rs — Ensure target LanguageDef exists and parses in isolation",
                "src/parser/injection.rs — Only if new detection logic needed",
                "tests/fixtures/sample.<ext> — Sample file with embedded content",
                "tests/parser_test.rs — Verify chunks from both host and injected language",
                "ROADMAP.md — Update Multi-Grammar Parsing section",
            ],
            patterns: &[
                "content_scoped_lines prevents container-spans-file problem",
                "detect_language callbacks inspect attributes (e.g., lang=\"ts\")",
                "set_included_ranges() for byte-range isolation",
                "Recursive injections must respect depth limit (default 3)",
            ],
        },
        keywords: &[
            ("injection", 2.5), ("embedded", 1.5), ("multi-grammar", 3.0),
            ("inject", 2.0), ("injection rule", 3.0),
        ],
    },
    TemplateEntry {
        template: TaskTemplate {
            name: "Performance Optimization",
            description: "Improving speed or reducing resource usage",
            checklist: &[
                "Benchmark before: cargo bench or manual timing. Record baseline",
                "Profile: cqs scout to find hot path, cqs callers to trace call chain",
                "Identify approach: lazy loading, caching, reduced allocations, parallelism",
                "Implement: minimal change, prefer data structure changes over algorithmic rewrites",
                "Benchmark after: same benchmark as before. Quantify improvement",
                "Regression test: same inputs produce same outputs",
                "Check callers: cqs impact — did the optimization change the API surface?",
            ],
            patterns: &[
                "HNSW candidate fetch: load only (id, embedding) for scoring, full content for top-k",
                "Rayon par_iter for embarrassingly parallel work — check for shared mutable state",
                "tracing::info_span! around hot paths for flame graph visibility",
            ],
        },
        keywords: &[
            ("performance", 2.0), ("speed", 1.5), ("slow", 2.0),
            ("memory", 1.0), ("optimize", 2.0), ("perf", 1.5), ("fast", 1.0),
            ("benchmark", 1.5), ("latency", 1.5),
        ],
    },
    TemplateEntry {
        template: TaskTemplate {
            name: "Audit Finding Fix",
            description: "Fixing an issue identified during a code audit",
            checklist: &[
                "Read triage entry: priority, category, description from docs/audit-triage.md",
                "Locate: cqs scout — verify the issue still exists",
                "Assess scope: cqs impact — how many callers are affected?",
                "Fix: follow the triage entry's suggested approach",
                "Add test: cover the specific scenario from the finding",
                "Update triage: mark entry as fixed with PR reference",
                "Check related findings: same category may have related issues",
            ],
            patterns: &[
                "P1 findings: fix immediately, standalone PR",
                "P2-P3: batch by category into single PR",
                "P4: fix opportunistically when touching nearby code",
            ],
        },
        keywords: &[
            ("audit", 2.5), ("finding", 2.0), ("triage", 2.0),
            ("P1", 2.0), ("P2", 2.0), ("P3", 1.5), ("P4", 1.0),
        ],
    },
    TemplateEntry {
        template: TaskTemplate {
            name: "Add Tree-Sitter Grammar",
            description: "Adding a new tree-sitter grammar dependency",
            checklist: &[
                "Cargo.toml — Add grammar crate as optional dependency (prefer crates.io, git dep with rev pin if unpublished)",
                "src/language/<lang>.rs — Wire grammar via tree_sitter_<lang>::LANGUAGE or language()",
                "Cargo.toml features — Add to lang-<name> feature gate, default, and lang-all",
                "Verify compatibility: grammar must target tree-sitter >=0.24, <0.27",
                "Tests: cargo test --features lang-<name>",
                "If forked: document fork reason in Cargo.toml comment",
            ],
            patterns: &[
                "Git deps need rev pin, not branch — branches break reproducibility",
                "Some grammars export LANGUAGE (static), others language() (function)",
                "Monolithic grammars (Razor, VB.NET) don't need injection",
            ],
        },
        keywords: &[
            ("grammar", 2.0), ("tree-sitter", 2.5), ("tree_sitter", 2.5),
        ],
    },
    TemplateEntry {
        template: TaskTemplate {
            name: "Schema Migration",
            description: "Bumping the SQLite schema version",
            checklist: &[
                "src/store/helpers/mod.rs — Bump CURRENT_SCHEMA_VERSION",
                "src/store/migrations.rs — Add migrate_vN_to_vM() function",
                "src/store/migrations.rs — Register in run_migration() match arms",
                "src/schema.sql — Update schema comment and add column/table",
                "src/store/*.rs — Update queries that read/write affected tables",
                "tests/store_test.rs — Update schema version assertion",
                "src/store/migrations.rs — Add migration test",
                "PROJECT_CONTINUITY.md — Update schema version in Architecture section",
            ],
            patterns: &[
                "Migrations must be idempotent: IF NOT EXISTS guards",
                "For new columns with NOT NULL, use DEFAULT or populate from existing data",
                "We use a metadata table for schema_version, not PRAGMA user_version",
                "Test with real old-version database if available",
            ],
        },
        keywords: &[
            ("schema", 2.5), ("migration", 2.5), ("column", 1.5),
            ("table", 1.0), ("ALTER", 2.0), ("schema version", 3.0),
        ],
    },
    TemplateEntry {
        template: TaskTemplate {
            name: "Refactor / Extract",
            description: "Moving code, splitting files, extracting shared helpers",
            checklist: &[
                "Find all call sites: cqs callers <function> for each function being moved",
                "Check similar code: cqs similar <function> to find duplicates to consolidate",
                "Plan visibility: pub(crate) for cross-module, pub for public API, private for same-module",
                "Move tests with code: #[cfg(test)] mod tests works in submodules",
                "Update imports: each file needs its own use statements",
                "Verify callers compile: all callers must update their use paths",
                "CONTRIBUTING.md — Update Architecture Overview for structural changes",
            ],
            patterns: &[
                "impl Foo blocks can live in separate files (Rust allows multiple)",
                "Trait method imports don't carry over to submodule files",
                "Use pub(crate) for types/constants shared across submodules",
            ],
        },
        keywords: &[
            ("refactor", 2.5), ("extract", 2.0), ("move", 1.0),
            ("split", 2.0), ("rename", 1.5), ("reorganize", 2.0),
        ],
    },
];

// ---------------------------------------------------------------------------
// Classification
// ---------------------------------------------------------------------------

/// Classify a task description into the best-matching template.
///
/// Uses keyword scoring. Returns the template index (into TEMPLATES).
/// Falls back to "Fix a Bug" (index 2) if no keywords match.
fn classify(description: &str) -> usize {
    let lower = description.to_lowercase();
    let mut best_idx = TEMPLATES
        .iter()
        .position(|e| e.template.name == "Fix a Bug")
        .unwrap_or(0);
    let mut best_score = 0.0f32;

    for (i, entry) in TEMPLATES.iter().enumerate() {
        let mut score = 0.0f32;
        for &(keyword, weight) in entry.keywords {
            if lower.contains(keyword) {
                score += weight;
            }
        }
        if score > best_score {
            best_score = score;
            best_idx = i;
        }
    }

    best_idx
}

/// Retrieves a task template by index.
///
/// # Arguments
///
/// * `idx` - The index of the template to retrieve from the templates collection.
///
/// # Returns
///
/// A reference to the `TaskTemplate` at the specified index.
///
/// # Panics
///
/// Panics if `idx` is out of bounds for the `TEMPLATES` array.
pub fn get_template(idx: usize) -> &'static TaskTemplate {
    &TEMPLATES[idx].template
}

/// List all available template names.
pub fn template_names() -> Vec<&'static str> {
    TEMPLATES.iter().map(|t| t.template.name).collect()
}

// ---------------------------------------------------------------------------
// Plan
// ---------------------------------------------------------------------------

/// Generate an implementation plan for a task.
///
/// Classifies the task, runs scout, and returns a structured plan
/// combining the template checklist with scout results.
pub fn plan<Mode>(
    store: &Store<Mode>,
    embedder: &Embedder,
    description: &str,
    root: &Path,
    limit: usize,
) -> Result<PlanResult, crate::AnalysisError> {
    let _span = tracing::info_span!("plan", %description).entered();
    let idx = classify(description);
    let tmpl = &TEMPLATES[idx].template;

    let scout_result = scout(store, embedder, description, root, limit)?;

    Ok(PlanResult {
        template: tmpl.name.to_string(),
        template_description: tmpl.description.to_string(),
        checklist: tmpl.checklist.iter().map(|s| s.to_string()).collect(),
        patterns: tmpl.patterns.iter().map(|s| s.to_string()).collect(),
        scout: scout_result,
    })
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_classify_language() {
        assert_eq!(classify("add dart language support"), 3);
        assert_eq!(TEMPLATES[3].template.name, "Add Language Support");
    }

    #[test]
    fn test_classify_bug() {
        assert_eq!(classify("fix broken search results"), 2);
        assert_eq!(TEMPLATES[2].template.name, "Fix a Bug");
    }

    #[test]
    fn test_classify_flag() {
        assert_eq!(classify("add --format flag to search"), 0);
        assert_eq!(TEMPLATES[0].template.name, "Add/Replace a CLI Flag");
    }

    #[test]
    fn test_classify_command() {
        assert_eq!(classify("add a new command for blame"), 1);
        assert_eq!(TEMPLATES[1].template.name, "Add a New CLI Command");
    }

    #[test]
    fn test_classify_injection() {
        assert_eq!(classify("add injection rule for Vue templates"), 5);
        assert_eq!(TEMPLATES[5].template.name, "Add Injection Rule");
    }

    #[test]
    fn test_classify_schema() {
        assert_eq!(classify("add schema migration for new column"), 9);
        assert_eq!(TEMPLATES[9].template.name, "Schema Migration");
    }

    #[test]
    fn test_classify_refactor() {
        assert_eq!(classify("refactor the store module"), 10);
        assert_eq!(TEMPLATES[10].template.name, "Refactor / Extract");
    }

    #[test]
    fn test_classify_performance() {
        assert_eq!(classify("optimize search performance"), 6);
        assert_eq!(TEMPLATES[6].template.name, "Performance Optimization");
    }

    #[test]
    fn test_classify_default() {
        // Ambiguous input falls back to Bug Fix
        assert_eq!(classify("improve the code quality"), 2);
    }

    #[test]
    fn test_template_names() {
        let names = template_names();
        assert_eq!(names.len(), 11);
        assert_eq!(names[0], "Add/Replace a CLI Flag");
    }
}