@toolbaux/guardian 0.2.0 → 0.2.1

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 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 → .specs/
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 / Cursor reads CLAUDE.md fresh architecture context
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
- - `guardian.config.json` for project settings (roots auto-detected at runtime)
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 — creates config, .specs/, pre-commit hook, CLAUDE.md
158
+ # One-time setup — config, .specs/, guardian.db, pre-commit hook, .mcp.json, CLAUDE.md
143
159
  guardian init
144
160
 
145
- # Extract architecture (run after major changes, or let the hook do it)
161
+ # Extract architecture + build search index (guardian.db built automatically)
146
162
  guardian extract
147
163
 
148
- # Search your codebase by concept
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" # search models, endpoints, components
243
- guardian search --query "auth" --types models,endpoints
244
- guardian context --focus "auth" # focused AI context block
245
- guardian context --output CLAUDE.md # inject between auto-context markers
246
- guardian summary # executive summary
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
- │ ├── drift.report.json drift metrics
327
- │ ├── constraints.json duplicates, cycles
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: 'file' (default) or 'sqlite' (builds guardian.db + FTS index)")
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,
@@ -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
  });
@@ -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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolbaux/guardian",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "Architectural intelligence for codebases. Verify that AI-generated code matches your architectural intent.",
6
6
  "keywords": [