@toolbaux/guardian 0.2.0 → 0.2.2
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/README.md +49 -16
- package/dist/cli.js +1 -5
- package/dist/commands/init.js +0 -4
- package/dist/db/fts-builder.js +23 -0
- package/dist/db/sqlite-specs-store.js +10 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,21 +41,24 @@ Developer writes code
|
|
|
41
41
|
↓ (save)
|
|
42
42
|
VSCode extension (5s debounce)
|
|
43
43
|
↓
|
|
44
|
-
guardian extract → .specs/
|
|
45
|
-
guardian generate --ai-context → .
|
|
46
|
-
guardian context → CLAUDE.md (between markers)
|
|
44
|
+
guardian extract → .specs/ + guardian.db (BM25 search index)
|
|
45
|
+
guardian generate --ai-context → reads from guardian.db, writes CLAUDE.md
|
|
47
46
|
Status bar: "✓ Guardian: stable · 35 ep · 8 pg"
|
|
48
47
|
↓ (git commit)
|
|
49
48
|
Pre-commit hook: extract + context → auto-staged
|
|
50
49
|
↓
|
|
51
|
-
Claude Code
|
|
50
|
+
Claude Code reads CLAUDE.md at session start
|
|
51
|
+
Claude Code calls MCP tools (guardian_search, guardian_grep, guardian_glob…)
|
|
52
|
+
↓ fresh, indexed context on every query
|
|
52
53
|
```
|
|
53
54
|
|
|
54
55
|
After `guardian init`, your project gets:
|
|
55
|
-
- `.specs/` directory with architecture snapshots
|
|
56
|
+
- `.specs/` directory with architecture snapshots + `guardian.db` (SQLite search index)
|
|
56
57
|
- `CLAUDE.md` with auto-injected context (refreshed on every save and commit)
|
|
57
58
|
- Pre-commit hook that keeps context fresh automatically
|
|
58
|
-
-
|
|
59
|
+
- `.mcp.json` wiring Claude Code and Cursor to Guardian's MCP server
|
|
60
|
+
- `guardian.config.json` with a unique `project_id` and auto-detected roots
|
|
61
|
+
- MCP-first hook: Claude Code is nudged to call `guardian_search` before reading source files
|
|
59
62
|
|
|
60
63
|
## Claude Code / Cursor Integration
|
|
61
64
|
|
|
@@ -115,6 +118,19 @@ All responses are compact JSON — no pretty-printing, no verbose keys. Repeated
|
|
|
115
118
|
|
|
116
119
|
> **Note:** After `.mcp.json` is created or modified, you must **restart your Claude Code / Cursor session** (or reload the VSCode window) for the MCP server to connect. MCP config is only read at session start.
|
|
117
120
|
|
|
121
|
+
### MCP-First Hook
|
|
122
|
+
|
|
123
|
+
`guardian init` also installs a Claude Code hook that encourages AI tools to call Guardian before reading source files directly. The hook is session-scoped — once any `guardian_*` tool is called, file reads are unblocked for the rest of the session. No repeated interruptions.
|
|
124
|
+
|
|
125
|
+
The block message tells Claude exactly what to call:
|
|
126
|
+
```
|
|
127
|
+
Call one of these first:
|
|
128
|
+
guardian_search("your query") — find files/symbols/endpoints by keyword
|
|
129
|
+
guardian_grep("pattern") — semantic grep (replaces Grep tool)
|
|
130
|
+
guardian_glob("src/auth/**") — semantic file discovery (replaces Glob tool)
|
|
131
|
+
guardian_orient() — get codebase overview
|
|
132
|
+
```
|
|
133
|
+
|
|
118
134
|
## VSCode Extension
|
|
119
135
|
|
|
120
136
|
Install from [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=toolbaux.toolbaux-guardian):
|
|
@@ -139,14 +155,21 @@ Cmd+Shift+P → "Extensions: Install from VSIX"
|
|
|
139
155
|
## Key Commands
|
|
140
156
|
|
|
141
157
|
```bash
|
|
142
|
-
# One-time setup —
|
|
158
|
+
# One-time setup — config, .specs/, guardian.db, pre-commit hook, .mcp.json, CLAUDE.md
|
|
143
159
|
guardian init
|
|
144
160
|
|
|
145
|
-
# Extract architecture
|
|
161
|
+
# Extract architecture + build search index (guardian.db built automatically)
|
|
146
162
|
guardian extract
|
|
147
163
|
|
|
148
|
-
#
|
|
164
|
+
# Extract without DB (CI environments that don't need search)
|
|
165
|
+
guardian extract --backend file
|
|
166
|
+
|
|
167
|
+
# Search your codebase by concept (uses guardian.db when available)
|
|
149
168
|
guardian search --query "session"
|
|
169
|
+
guardian search --query "auth" --types functions,endpoints
|
|
170
|
+
|
|
171
|
+
# Inject fresh context into CLAUDE.md
|
|
172
|
+
guardian context --output CLAUDE.md
|
|
150
173
|
|
|
151
174
|
# Compute architectural drift
|
|
152
175
|
guardian drift
|
|
@@ -239,11 +262,12 @@ guardian generate --ai-context # compact ~3K token AI context only
|
|
|
239
262
|
### Search & Context
|
|
240
263
|
|
|
241
264
|
```bash
|
|
242
|
-
guardian search --query "session"
|
|
243
|
-
guardian search --query "auth" --types models,endpoints
|
|
244
|
-
guardian
|
|
245
|
-
guardian context --
|
|
246
|
-
guardian
|
|
265
|
+
guardian search --query "session" # search models, endpoints, components, functions
|
|
266
|
+
guardian search --query "auth" --types models,endpoints # filter by type
|
|
267
|
+
guardian search --query "validate token" --types functions # function-level search (uses guardian.db)
|
|
268
|
+
guardian context --focus "auth" # focused AI context block
|
|
269
|
+
guardian context --output CLAUDE.md # inject between auto-context markers
|
|
270
|
+
guardian summary # executive summary
|
|
247
271
|
```
|
|
248
272
|
|
|
249
273
|
### Architectural Metrics
|
|
@@ -285,11 +309,16 @@ guardian feature-context --spec feature-specs/billing.yaml
|
|
|
285
309
|
|
|
286
310
|
```json
|
|
287
311
|
{
|
|
312
|
+
"project_id": "auto-generated-uuid",
|
|
288
313
|
"project": {
|
|
289
314
|
"description": "Short product description for generated docs",
|
|
290
315
|
"backendRoot": "./backend",
|
|
291
316
|
"frontendRoot": "./frontend"
|
|
292
317
|
},
|
|
318
|
+
"ignore": {
|
|
319
|
+
"directories": ["bench-repos", "fixtures", "vendor"],
|
|
320
|
+
"paths": ["src/generated"]
|
|
321
|
+
},
|
|
293
322
|
"frontend": {
|
|
294
323
|
"routeDirs": ["app"],
|
|
295
324
|
"aliases": { "@": "./frontend" }
|
|
@@ -311,6 +340,8 @@ guardian feature-context --spec feature-specs/billing.yaml
|
|
|
311
340
|
}
|
|
312
341
|
```
|
|
313
342
|
|
|
343
|
+
> **Tip:** Use `ignore.directories` to exclude directories that Guardian indexes but aren't part of your project (e.g. benchmark repos, vendor directories, generated code). Guardian scans all source files under the project root by design — configure ignores to keep the search index clean.
|
|
344
|
+
|
|
314
345
|
</details>
|
|
315
346
|
|
|
316
347
|
<details>
|
|
@@ -318,13 +349,15 @@ guardian feature-context --spec feature-specs/billing.yaml
|
|
|
318
349
|
|
|
319
350
|
```
|
|
320
351
|
.specs/
|
|
352
|
+
├── guardian.db ← SQLite search index (BM25 + function call graph)
|
|
321
353
|
├── machine/
|
|
322
354
|
│ ├── architecture-context.md ← AI context (~3K tokens)
|
|
323
355
|
│ ├── architecture.snapshot.yaml ← full architecture snapshot
|
|
324
356
|
│ ├── ux.snapshot.yaml ← frontend components + pages
|
|
325
357
|
│ ├── codebase-intelligence.json ← unified registry
|
|
326
|
-
│ ├──
|
|
327
|
-
│ ├──
|
|
358
|
+
│ ├── function-intelligence.json ← function call graph + literal index
|
|
359
|
+
│ ├── structural-intelligence.json ← depth/complexity per module
|
|
360
|
+
│ ├── drift.heatmap.json ← file-level change frequency
|
|
328
361
|
│ └── docs/ ← generated markdown docs
|
|
329
362
|
├── human/
|
|
330
363
|
│ ├── product-document.md ← LLM-powered product doc
|
package/dist/cli.js
CHANGED
|
@@ -349,16 +349,12 @@ program
|
|
|
349
349
|
.command("init")
|
|
350
350
|
.description("Initialize guardian for a project (config, .specs dir, pre-commit hook, CLAUDE.md)")
|
|
351
351
|
.argument("[projectRoot]", "Repo or project root", process.cwd())
|
|
352
|
-
.option("--backend-root <path>", "Path to backend root")
|
|
353
|
-
.option("--frontend-root <path>", "Path to frontend root")
|
|
354
352
|
.option("--output <path>", "Output directory", DEFAULT_SPECS_DIR)
|
|
355
353
|
.option("--skip-hook", "Skip pre-commit hook installation", false)
|
|
356
|
-
.option("--backend <backend>", "Storage backend: '
|
|
354
|
+
.option("--backend <backend>", "Storage backend: 'sqlite' (default) or 'file'")
|
|
357
355
|
.action(async (projectRoot, options) => {
|
|
358
356
|
await runInit({
|
|
359
357
|
projectRoot,
|
|
360
|
-
backendRoot: options.backendRoot,
|
|
361
|
-
frontendRoot: options.frontendRoot,
|
|
362
358
|
output: options.output,
|
|
363
359
|
skipHook: options.skipHook ?? false,
|
|
364
360
|
backend: options.backend,
|
package/dist/commands/init.js
CHANGED
|
@@ -204,8 +204,6 @@ export async function runInit(options) {
|
|
|
204
204
|
const { runExtract } = await import("./extract.js");
|
|
205
205
|
await runExtract({
|
|
206
206
|
projectRoot: root,
|
|
207
|
-
backendRoot: options.backendRoot,
|
|
208
|
-
frontendRoot: options.frontendRoot,
|
|
209
207
|
output: specsDir,
|
|
210
208
|
includeFileGraph: true,
|
|
211
209
|
backend: options.backend,
|
|
@@ -213,8 +211,6 @@ export async function runInit(options) {
|
|
|
213
211
|
const { runGenerate } = await import("./generate.js");
|
|
214
212
|
await runGenerate({
|
|
215
213
|
projectRoot: root,
|
|
216
|
-
backendRoot: options.backendRoot,
|
|
217
|
-
frontendRoot: options.frontendRoot,
|
|
218
214
|
output: specsDir,
|
|
219
215
|
aiContext: true,
|
|
220
216
|
});
|
package/dist/db/fts-builder.js
CHANGED
|
@@ -296,6 +296,29 @@ export function populateFTSIndex(store, intel, arch, funcIntel) {
|
|
|
296
296
|
mergeArchitectureRows(rowMap, arch);
|
|
297
297
|
if (funcIntel)
|
|
298
298
|
mergeFunctionIntelRows(rowMap, funcIntel);
|
|
299
|
+
// Deduplicate paths where one is a suffix of another — caused when the snapshot
|
|
300
|
+
// stores some paths relative to backendRoot ("db/store.ts") and others relative
|
|
301
|
+
// to the workspace root ("src/db/store.ts"). Keep the longer (workspace-relative)
|
|
302
|
+
// path and merge any unique tokens from the shorter entry into it.
|
|
303
|
+
const paths = Array.from(rowMap.keys());
|
|
304
|
+
for (const shorter of paths) {
|
|
305
|
+
for (const longer of paths) {
|
|
306
|
+
if (shorter === longer)
|
|
307
|
+
continue;
|
|
308
|
+
if (longer.endsWith("/" + shorter) && rowMap.has(shorter) && rowMap.has(longer)) {
|
|
309
|
+
const s = rowMap.get(shorter);
|
|
310
|
+
const l = rowMap.get(longer);
|
|
311
|
+
// Merge any tokens the shorter row had that the longer lacks
|
|
312
|
+
for (const field of ["symbol_name", "endpoint", "body", "module"]) {
|
|
313
|
+
if (s[field] && !l[field].includes(s[field])) {
|
|
314
|
+
l[field] = l[field] ? l[field] + " " + s[field] : s[field];
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
rowMap.delete(shorter);
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
299
322
|
store.rebuildSearchIndex(Array.from(rowMap.values()));
|
|
300
323
|
// Per-function index — enables symbol-level search results with line numbers.
|
|
301
324
|
if (funcIntel?.functions?.length) {
|
|
@@ -592,6 +592,16 @@ export class SqliteSpecsStore {
|
|
|
592
592
|
return [];
|
|
593
593
|
}
|
|
594
594
|
}
|
|
595
|
+
/** Return all file_path values currently in the search index. Useful for tests and diagnostics. */
|
|
596
|
+
listIndexedFilePaths() {
|
|
597
|
+
try {
|
|
598
|
+
const rows = this.db.prepare("SELECT file_path FROM search_fts").all();
|
|
599
|
+
return rows.map(r => r.file_path);
|
|
600
|
+
}
|
|
601
|
+
catch {
|
|
602
|
+
return [];
|
|
603
|
+
}
|
|
604
|
+
}
|
|
595
605
|
/**
|
|
596
606
|
* Store API endpoint facts.
|
|
597
607
|
* Called from populateFTSIndex() after reading intel/arch objects.
|
package/package.json
CHANGED