scopecairn 0.10.0 → 0.12.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/README.md CHANGED
@@ -24,15 +24,15 @@ No MCP server, no embeddings, no network calls, no telemetry.
24
24
  Definition of Done, so the agent changes only what matters.
25
25
  - **Change impact** — direct, indirect (2-hop), tests, and UI components
26
26
  affected by a file or symbol.
27
- - **Call path tracing** — `scopecairn path <A> <B>` shows the shortest
27
+ - **Call path tracing** — `scopecairn graph <A> --to <B>` shows the shortest
28
28
  multi-hop call/dependency chain between two symbols.
29
29
  - **Centrality ranking** — weighted PageRank + betweenness cached per
30
- symbol; `context` surfaces critical hotspots, `doctor` flags
30
+ symbol; `context` surfaces critical hotspots, `status --doctor` flags
31
31
  single points of failure.
32
32
  - **Community detection** — Louvain clustering finds real modules by
33
33
  link density (not just folders); `graph` shows each symbol's community.
34
34
  - **Cycle detection** — Tarjan SCC finds circular dependencies;
35
- reported by `doctor`, warned in `context`, listed in `GRAPH.md`.
35
+ reported by `status --doctor` and `verify`, warned in `context`, listed in `GRAPH.md`.
36
36
  - **Auto-refresh** — `context` and `impact` re-index changed files first;
37
37
  run `scan` after editing to close the loop.
38
38
  - **Framework adapters** — Next.js (App + Pages Router routes),
@@ -40,8 +40,8 @@ No MCP server, no embeddings, no network calls, no telemetry.
40
40
  - **Multi-language** — TypeScript, JavaScript, Python, Java, Go, Rust,
41
41
  PHP, C#, C/C++, Ruby, HTML, Vue SFC (plus config/schema files as
42
42
  graph nodes).
43
- - **Multi-agent setup** — skills for Claude Code (plugin + marketplace
44
- ready), Antigravity, Cursor, Windsurf, Copilot, Kiro, and an OpenCode
43
+ - **Multi-agent setup** — skills for Claude Code (project skill),
44
+ Antigravity, Cursor, Windsurf, Copilot, Kiro, and an OpenCode
45
45
  slash command. ScopeCairn never touches your `AGENTS.md`/`CLAUDE.md` —
46
46
  those hold your repo's details, not tool instructions.
47
47
  - **`GRAPH.md` knowledge file** — `.scopecairn/GRAPH.md` is a budgeted
@@ -58,7 +58,7 @@ No MCP server, no embeddings, no network calls, no telemetry.
58
58
  click-for-details side panel (callers, callees, centrality scores).
59
59
  Nodes sized by symbols/PageRank, colored by Louvain community.
60
60
  Pan, zoom, hover inspect. Double-click to open, no server.
61
- - **`export` for external tools** — `scopecairn export --format
61
+ - **Graph export for external tools** — `scopecairn graph --export
62
62
  mermaid|graphml|dot|json` writes `.scopecairn/graph.<ext>` for PR
63
63
  diagrams (Mermaid), Gephi/Cytoscape (GraphML), Graphviz (DOT),
64
64
  or custom scripts (JSON).
@@ -74,8 +74,7 @@ Requires Node.js ≥ 20.
74
74
  Upgrade later with:
75
75
 
76
76
  ```bash
77
- scopecairn update # or: npm install -g scopecairn@latest
78
- scopecairn update --check # only check for a new version
77
+ npm install -g scopecairn@latest
79
78
  ```
80
79
 
81
80
  ## Agent-driven setup
@@ -88,8 +87,8 @@ it runs project setup itself.
88
87
  npx skills add <github-user>/scopecairn -g
89
88
  ```
90
89
  (fallback without a skill: add one line to your global agent
91
- instructions — `~/.claude/CLAUDE.md` or equivalent: *When the user
92
- writes "scopecairn \<path\>", run `scopecairn init \<path\>`.*)
90
+ instructions — `~/.claude/CLAUDE.md` or equivalent: _When the user
91
+ writes "scopecairn \<path\>", run `scopecairn init \<path\>`._)
93
92
  2. In any project chat, type:
94
93
  ```
95
94
  scopecairn ./
@@ -111,82 +110,75 @@ Claude Code (project skill, committed with the repo):
111
110
  scopecairn claude install
112
111
  ```
113
112
 
114
- Or as a plugin from the bundled marketplace (this repo):
115
-
116
- ```
117
- /plugin marketplace add <github-user>/scopecairn
118
- /plugin install scopecairn@scopecairn
119
- ```
120
-
121
113
  After setup, your agent calls ScopeCairn automatically on every coding task.
122
- To query manually:
114
+ To query manually (replace `src/indexer.ts` or `buildContext` with files/symbols from your codebase):
123
115
 
124
116
  ```bash
125
- scopecairn context "add approval workflow"
126
- scopecairn context ./ # repository orientation map
127
- scopecairn impact src/request/RequestService.ts
128
- scopecairn read symbol approveRequest
129
- scopecairn graph RequestService
130
- scopecairn path handleLogin dbQuery
131
- scopecairn export --format mermaid
132
- scopecairn doctor
117
+ scopecairn scan # index repository (--rebuild, --clean, --watch)
118
+ scopecairn context "tambah fitur auth" # AI agent task context + scope
119
+ scopecairn impact src/indexer.ts # blast radius & affected tests (--run-tests)
120
+ scopecairn graph buildContext # inspect relations, path (--to), export (--export)
121
+ scopecairn verify # pre-commit safety guard (--diff, --arch)
122
+ scopecairn status # index statistics & health checks (--doctor, --html)
123
+ scopecairn read symbol indexFileSymbols # symbol-level source excerpt
133
124
  ```
134
125
 
135
126
  Per-agent installers (instead of `init --agents …`):
136
127
 
137
128
  ```bash
138
129
  scopecairn antigravity install # Skill + Workflow + allowlist guide
139
- scopecairn claude install # project skill .claude/skills (also: cursor, windsurf, copilot, kiro)
130
+ scopecairn claude install # project skill .claude/skills (also: cursor, windsurf, copilot)
131
+ scopecairn kiro install # steering .kiro/steering + knowledge skill .kiro/skills/scopecairn-knowledge
140
132
  scopecairn opencode install # /scopecairn slash command + permission snippet
141
133
  scopecairn claude uninstall # clean removal, user content preserved
142
134
  scopecairn agents list
143
135
  ```
144
136
 
145
- ## Commands
137
+ ## 6 Core Commands
138
+
139
+ ScopeCairn organizes its capabilities into **6 core commands** aligned with codebase architecture:
140
+
141
+ | Core Command | Description & Key Options |
142
+ | ------------ | ------------------------- |
143
+ | `scan` | **Index & Database Lifecycle:** Index repository. Options: `--rebuild` (rebuild from scratch), `--clean` (remove `.scopecairn/`), `--watch` (auto-refresh daemon on file save). |
144
+ | `context "<task>"` | **AI Agent Context & Scope:** Main agent entry point with auto-refresh, task classification, relevant context, task scope, and Task Guard. Options: `--escalate`, `--mode NORMAL\|FAST\|SAFE\|AUDIT`, `--max-tokens N`, `--no-history`, `--with-diff`, `--json`. |
145
+ | `impact <target>` | **Blast Radius & Test Selection:** Change impact analysis across direct, indirect, tests, UI components, and API clients. Options: `--run-tests` (run affected tests directly), `--tests` (list affected tests only), `--repos [list]` (cross-repo blast radius). |
146
+ | `graph [symbol]` | **Knowledge Graph & Navigation:** Symbol relations, PageRank centrality, community clusters, and cycle status. Options: `--to <target>` (shortest multi-hop call path), `--export <fmt>` (`mermaid\|graphml\|dot\|json`). |
147
+ | `verify` | **Pre-Commit Safety Guard:** Working tree check against protected files, task scope creep, and circular dependency regressions. Options: `--diff` (structural symbol-level diff vs HEAD), `--arch` (architectural layer rules), `--strict`, `--task <t>`. |
148
+ | `status` | **Health & Diagnostics:** Index statistics and metadata. Options: `--doctor` (integration and adapter health check), `--verbose`, `--html` (generate local HTML dashboard). |
149
+
150
+ ### Helper & Setup Commands
146
151
 
147
152
  | Command | Description |
148
- |---|---|
149
- | `init [path]` | Initial indexing + Skill, Workflow, detected agent skills (`--agents …\|all`; never writes `AGENTS.md`) |
150
- | `scan` / `rebuild` / `clean` | Manual index management (`scan` incremental; `rebuild` from scratch; `clean` removes `.scopecairn/`) |
151
- | `status` / `doctor` | Index status (incl. adapters, invocations) and health checks |
152
- | `context "<task>"` | Main entry point: context + scope (`--escalate`, `--no-refresh`, `--mode NORMAL|FAST|SAFE|AUDIT`, `--max-tokens N`, `--no-history`, `--with-diff`); a path task returns an orientation map |
153
- | `history "<task>" [--limit N] [--json] [--with-diff]` | Analog commit lama paling mirip (BM25 + recency half-life 180 hari + co-change); co-change set ikut ke Optional di `context` |
154
- | `impact <path\|symbol>` | Change impact: direct, indirect, tests (via TESTS edges + static CALLS backward reachability), UI, queries, routes. Edge evidence tagged EXTRACTED/INFERRED/AMBIGUOUS |
155
- | `graph <symbol>` | Symbol relations (+ PageRank, community, cycle status) |
156
- | `path <from> <to>` | Shortest multi-hop call/dependency path between two symbols |
157
- | `export --format <fmt>` | Graph export: `mermaid`, `graphml`, `dot`, or `json` (default `.scopecairn/graph.<ext>`, override with `--out`) |
158
- | `read symbol <name>` | Symbol-level source excerpt (not whole files) |
159
- | `<agent> install` | Per-agent setup: `antigravity`, `claude`, `gemini`, `cursor`, `windsurf`, `copilot`, `kiro` (each also `uninstall`; `agents list` to see all) |
160
- | `dashboard` | Generate local HTML dashboard at `.scopecairn/dashboard.html` |
161
- | `test-select <target> [--run]` | List affected test files via graph dependency traversal & runner detection (`--run` executes runner) |
162
- | `verify [--task <t>] [--strict]` | Post-edit guard: check modified protected files, task scope creep, and circular dependency regressions |
163
- | `diff` | Structural symbol-level diff of working tree vs HEAD with breaking impact alerts |
164
- | `arch-check [--init]` | Check architectural layer rules & disallowed import boundaries (`--init` scaffolds `.scopecairn/arch.json`) |
165
- | `watch [--debounce <ms>]` | Daemon mode: auto-refresh index in background on file saves |
166
- | `doctor --verbose` | Also surface adapter/graph errors from `.scopecairn/last-run.log` to stderr |
167
- | `benchmark [--tune]` | Retrieval recall, irrelevant ratio, context reduction + weight calibration |
168
- | `update [--check]` | Update ScopeCairn to the latest npm release (`--check` only checks) |
169
-
170
- Agent commands (`context`, `impact`, `read`, `graph`, `status`, `doctor`, `verify`, `diff`, `test-select`, `arch-check`)
171
- are read-only toward source and only write to `.scopecairn/` — safe to
172
- allowlist. `init`, `scan`, `rebuild`, and `watch` are manual/user-side commands.
153
+ | ------- | ----------- |
154
+ | `init [path]` | Initial indexing + Skill, Workflow, detected agent skills (`--agents …\|all`). |
155
+ | `read symbol <name>` | Symbol-level source excerpt (not whole files). |
156
+ | `benchmark [--tune]` | Retrieval recall, irrelevant ratio, context reduction + weight tuning (dev-only, hidden from `--help`). |
157
+ | `<agent> install` | Agent installers: `antigravity`, `claude`, `cursor`, `windsurf`, `copilot`, `kiro`, `opencode`. |
158
+
159
+ > [!NOTE]
160
+ > **Backward Compatibility:** All previous standalone commands (`rebuild`, `clean`, `watch`, `doctor`, `dashboard`, `test-select`, `path`, `export`, `diff`, `arch-check`, `history`) are preserved as hidden aliases mapped directly into these 6 core commands — they still work but no longer appear in `--help`. `update` is deprecated (use `npm install -g scopecairn@latest`).
161
+
162
+ Agent commands (`context`, `impact`, `read`, `graph`, `status`, `verify`)
163
+ are read-only toward source code and safe to allowlist. `init` and `scan`
164
+ are user-side setup/lifecycle commands.
173
165
 
174
166
  ## Framework adapters
175
167
 
176
168
  Detected automatically during `scan` (shown in output and `status`).
177
169
  No configuration needed.
178
170
 
179
- | Adapter | Detects | Produces |
180
- |---|---|---|
181
- | `nextjs` | `app/`, `pages/`, `next.config.*` | Route symbols (`GET /api/x`, `PAGE /y` incl. route groups, dynamic segments), `ROUTES_TO` handler edges, `QUERIES` handler→model edges |
182
- | `prisma` | `schema.prisma` | `model` symbols for `QUERIES` resolution |
183
- | `drizzle` | `drizzle*` paths/config, `db/` schemas | `model` symbols from `pgTable`/`sqliteTable`/`mysqlTable`; `QUERIES` from `db.query.*` relational and `db.select/insert/update/delete` builder calls |
184
- | `express` | `package.json` deps | Route symbols from `app/router.METHOD(path)`, handler edges |
185
- | `fastapi` | `.py` routes, requirements/pyproject | Route symbols from `@app.METHOD("/path")` decorators |
186
- | `nestjs` | `*.controller.ts`, `*.service.ts`, `nest-cli.json` | Route symbols from `@Controller` and `@Get/@Post/...` decorators, `ROUTES_TO` handler edges, `@Injectable()` service providers |
187
- | `django` | `manage.py`, `urls.py`, `models.py` | Route symbols from `path()`/`re_path()` in `urls.py`, `ROUTES_TO` handler edges, `model` symbols from `models.Model` |
188
- | `sqlalchemy` | requirements/pyproject | `model` symbols from `class X(Base)`/`__tablename__`; `QUERIES` from `session.query/select` |
189
- | `vue` | `.vue` SFCs, `package.json` deps | `component` symbols per SFC; `IMPORTS` edges from used components in template |
171
+ | Adapter | Detects | Produces |
172
+ | ------------ | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
173
+ | `nextjs` | `app/`, `pages/`, `next.config.*` | Route symbols (`GET /api/x`, `PAGE /y` incl. route groups, dynamic segments), `ROUTES_TO` handler edges, `QUERIES` handler→model edges |
174
+ | `prisma` | `schema.prisma` | `model` symbols for `QUERIES` resolution |
175
+ | `drizzle` | `drizzle*` paths/config, `db/` schemas | `model` symbols from `pgTable`/`sqliteTable`/`mysqlTable`; `QUERIES` from `db.query.*` relational and `db.select/insert/update/delete` builder calls |
176
+ | `express` | `package.json` deps | Route symbols from `app/router.METHOD(path)`, handler edges |
177
+ | `fastapi` | `.py` routes, requirements/pyproject | Route symbols from `@app.METHOD("/path")` decorators |
178
+ | `nestjs` | `*.controller.ts`, `*.service.ts`, `nest-cli.json` | Route symbols from `@Controller` and `@Get/@Post/...` decorators, `ROUTES_TO` handler edges, `@Injectable()` service providers |
179
+ | `django` | `manage.py`, `urls.py`, `models.py` | Route symbols from `path()`/`re_path()` in `urls.py`, `ROUTES_TO` handler edges, `model` symbols from `models.Model` |
180
+ | `sqlalchemy` | requirements/pyproject | `model` symbols from `class X(Base)`/`__tablename__`; `QUERIES` from `session.query/select` |
181
+ | `vue` | `.vue` SFCs, `package.json` deps | `component` symbols per SFC; `IMPORTS` edges from used components in template |
190
182
 
191
183
  Limits (honest): non-Prisma/Drizzle/SQLAlchemy ORMs, inter-table
192
184
  references, raw SQL strings, and fully dynamic table names are not mapped
@@ -210,11 +202,11 @@ Measure retrieval quality anytime: `scopecairn benchmark [--tune]`.
210
202
 
211
203
  `scan` mengindeks pesan commit git lokal ke tabel `commits`/`commit_files` di
212
204
  `.scopecairn/scopecairn.db` (dilewati commit >40 file, lockfile, dan generated).
213
- Isi commit **tidak** masuk `GRAPH.md`/`GRAPH.html`/`export` (sering di-commit dan
214
- dibagikan) — riwayat hanya hidup di database lokal dan output `context`/`history`.
205
+ Isi commit **tidak** masuk `GRAPH.md`/`GRAPH.html`/`graph --export` (sering di-commit dan
206
+ dibagikan) — riwayat hanya hidup di database lokal dan output `context`.
215
207
  Pesan commit bisa memuat info sensitif; karena tidak keluar dari mesin, risikonya
216
208
  kecil. Repo tanpa git atau clone dangkal tetap didukung (riwayat parsial/absen,
217
- dilaporkan `doctor`).
209
+ dilaporkan `status --doctor`).
218
210
 
219
211
  ## License
220
212
 
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ PROJECT_RULES_MAX_CHARS,
4
+ demoteHeadings,
5
+ findAgentsMd,
6
+ loadProjectRules
7
+ } from "./chunk-ZV4BSIQM.js";
8
+ export {
9
+ PROJECT_RULES_MAX_CHARS,
10
+ demoteHeadings,
11
+ findAgentsMd,
12
+ loadProjectRules
13
+ };
@@ -9,6 +9,9 @@ import {
9
9
  coChangeSet,
10
10
  groupHistoryFiles
11
11
  } from "./chunk-2BIPX6YJ.js";
12
+ import {
13
+ demoteHeadings
14
+ } from "./chunk-ZV4BSIQM.js";
12
15
 
13
16
  // src/scope/guard.ts
14
17
  var TASK_GUARD_RULES = [
@@ -214,6 +217,46 @@ Task sederhana. Gunakan pencarian langsung. Kandidat: ${cand}
214
217
  if (analogTestSha && !requiredHasTest) {
215
218
  extraDoD.push(`ubah atau tambah test, seperti pada \`${analogTestSha.shortSha}\``);
216
219
  }
220
+ let rulesBlock = "";
221
+ const rules = opts?.projectRules;
222
+ if (rules && rules.content) {
223
+ rulesBlock = `## Project Rules
224
+
225
+ > Sumber: \`${rules.path}\` \u2014 konvensi proyek, berlaku untuk task ini.
226
+ ` + (rules.truncated ? `> (dipotong ke anggaran \u2014 baca file langsung untuk lengkapnya)
227
+ ` : ``) + `
228
+ ${demoteHeadings(rules.content)}
229
+
230
+ `;
231
+ }
232
+ if (opts?.knowledge) {
233
+ const mdKnowledge = `# Task Context (Knowledge)
234
+
235
+ ## Task
236
+ ${task}
237
+
238
+ ## Complexity
239
+ ${cls.complexity} (${cls.reasons.join("; ")})
240
+
241
+ ` + rulesBlock + `## Ringkasan
242
+ File relevan: ${highFiles.slice(0, 5).join(", ") || "-"}${medFiles.length > 0 ? ` (lihat juga: ${medFiles.slice(0, 5).join(", ")})` : ""}
243
+ Simbol kunci: ${hi.slice(0, 5).map((r) => `\`${r.name}\``).join(", ") || "-"}
244
+
245
+ ## Kenapa (trace 2-hop)
246
+ ${deps.map((d) => `- ${d}`).join("\n") || "-"}
247
+
248
+ ` + (hotspots.length > 0 ? `## Hotspot arsitektur
249
+ ${hotspots.join("\n")}
250
+
251
+ ` : ``) + `## Cara pakai
252
+ ${hi.slice(0, 8).map(symLine).join("\n") || "-"}
253
+
254
+ ## Related Tests
255
+ ${tests.map((t) => `- ${t}`).join("\n") || "-"}
256
+
257
+ ` + historyBlock + cycleWarn;
258
+ return { markdown: mdKnowledge, high: highFiles, medium: medFiles, tests, dependencies: deps };
259
+ }
217
260
  const md = `# Task Context
218
261
 
219
262
  ## Task
@@ -222,7 +265,7 @@ ${task}
222
265
  ## Complexity
223
266
  ${cls.complexity} (${cls.reasons.join("; ")})
224
267
 
225
- ## Relevant Files
268
+ ` + rulesBlock + `## Relevant Files
226
269
  ### HIGH
227
270
  ${highFiles.map((f) => `- ${f}`).join("\n") || "-"}
228
271
  ### MEDIUM
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/scope/agentsmd.ts
4
+ import fs from "fs";
5
+ import path from "path";
6
+ var CANDIDATES = ["AGENTS.md", "agents.md"];
7
+ var PROJECT_RULES_MAX_CHARS = 4e3;
8
+ function findAgentsMd(repoRoot) {
9
+ for (const name of CANDIDATES) {
10
+ try {
11
+ if (fs.statSync(path.join(repoRoot, name)).isFile()) return name;
12
+ } catch {
13
+ }
14
+ }
15
+ return null;
16
+ }
17
+ function loadProjectRules(repoRoot, maxChars = PROJECT_RULES_MAX_CHARS) {
18
+ if (typeof maxChars === "number" && maxChars <= 0) return null;
19
+ const rel = findAgentsMd(repoRoot);
20
+ if (!rel) return null;
21
+ const cap = Math.max(
22
+ 500,
23
+ Number.isFinite(maxChars) ? Math.floor(maxChars) : PROJECT_RULES_MAX_CHARS
24
+ );
25
+ const fullPath = path.join(repoRoot, rel);
26
+ let text;
27
+ let fileExceedsCap = false;
28
+ try {
29
+ const stat = fs.statSync(fullPath);
30
+ const bytesToRead = Math.min(stat.size, cap * 4);
31
+ fileExceedsCap = stat.size > bytesToRead;
32
+ const fd = fs.openSync(fullPath, "r");
33
+ try {
34
+ const buf = Buffer.alloc(bytesToRead);
35
+ const bytesRead = fs.readSync(fd, buf, 0, bytesToRead, 0);
36
+ text = buf.subarray(0, bytesRead).toString("utf8");
37
+ } finally {
38
+ fs.closeSync(fd);
39
+ }
40
+ } catch {
41
+ return null;
42
+ }
43
+ text = text.replace(/\r\n/g, "\n").trim();
44
+ if (!text) return null;
45
+ if (fileExceedsCap || text.length > cap) {
46
+ return { path: rel, content: text.slice(0, cap).trimEnd(), truncated: true };
47
+ }
48
+ return { path: rel, content: text, truncated: false };
49
+ }
50
+ function demoteHeadings(markdown) {
51
+ let inCodeBlock = false;
52
+ return markdown.split("\n").map((line) => {
53
+ const trimmed = line.trimStart();
54
+ if (trimmed.startsWith("```") || trimmed.startsWith("~~~")) {
55
+ inCodeBlock = !inCodeBlock;
56
+ return line;
57
+ }
58
+ if (!inCodeBlock && /^#{1,6}\s/.test(line)) {
59
+ return line.replace(/^(#{1,6})\s/, (_, hashes) => {
60
+ const nextLevel = Math.min(6, hashes.length + 2);
61
+ return "#".repeat(nextLevel) + " ";
62
+ });
63
+ }
64
+ return line;
65
+ }).join("\n");
66
+ }
67
+
68
+ export {
69
+ PROJECT_RULES_MAX_CHARS,
70
+ findAgentsMd,
71
+ loadProjectRules,
72
+ demoteHeadings
73
+ };