scopecairn 0.3.0 → 0.4.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
@@ -28,8 +28,10 @@ No MCP server, no embeddings, no network calls, no telemetry.
28
28
  Prisma models, and Drizzle tables/queries detected automatically.
29
29
  - **Multi-language** — TypeScript, JavaScript, Python, Java, Go, Rust,
30
30
  PHP, C#, C/C++, Ruby, HTML (plus config/schema files as graph nodes).
31
- - **Multi-agent setup** — one `init` writes rules for AGENTS.md,
32
- Claude Code, Gemini, Cursor, Windsurf, Copilot, Kiro, or all at once.
31
+ - **Multi-agent setup** — skills for Claude Code (plugin + marketplace
32
+ ready), Antigravity, Cursor, Windsurf, Copilot, Kiro, and an OpenCode
33
+ slash command. ScopeCairn never touches your `AGENTS.md`/`CLAUDE.md` —
34
+ those hold your repo's details, not tool instructions.
33
35
 
34
36
  ## Install
35
37
 
@@ -63,7 +65,20 @@ it runs project setup itself.
63
65
 
64
66
  ```bash
65
67
  cd your-repo
66
- scopecairn init # index + generate AGENTS.md, Skill, Workflow
68
+ scopecairn init # index + Antigravity Skill + Workflow
69
+ ```
70
+
71
+ Claude Code (project skill, committed with the repo):
72
+
73
+ ```bash
74
+ scopecairn claude install
75
+ ```
76
+
77
+ Or as a plugin from the bundled marketplace (this repo):
78
+
79
+ ```
80
+ /plugin marketplace add <github-user>/scopecairn
81
+ /plugin install scopecairn@scopecairn
67
82
  ```
68
83
 
69
84
  After setup, your agent calls ScopeCairn automatically on every coding task.
@@ -82,7 +97,8 @@ Per-agent installers (instead of `init --agents …`):
82
97
 
83
98
  ```bash
84
99
  scopecairn antigravity install # Skill + Workflow + allowlist guide
85
- scopecairn claude install # CLAUDE.md (also: gemini, cursor, windsurf, copilot, kiro)
100
+ scopecairn claude install # project skill .claude/skills (also: cursor, windsurf, copilot, kiro)
101
+ scopecairn opencode install # /scopecairn slash command + permission snippet
86
102
  scopecairn claude uninstall # clean removal, user content preserved
87
103
  scopecairn agents list
88
104
  ```
@@ -91,7 +107,7 @@ scopecairn agents list
91
107
 
92
108
  | Command | Description |
93
109
  |---|---|
94
- | `init [path]` | Initial indexing + `AGENTS.md`, Skill, Workflow, agent matrix (`--agents …\|all`) |
110
+ | `init [path]` | Initial indexing + Skill, Workflow, detected agent skills (`--agents …\|all`; never writes `AGENTS.md`) |
95
111
  | `scan` / `rebuild` | Manual index management (`scan` inkremental; `rebuild` dari nol) |
96
112
  | `status` / `doctor` | Index status (incl. adapters, invocations) and health checks |
97
113
  | `context "<task>"` | Main entry point: context + scope (`--escalate`, `--no-refresh`); a path task returns an orientation map |
package/dist/cli.js CHANGED
@@ -2021,9 +2021,28 @@ Jalankan \`${prefix} scan\` agar indeks ScopeCairn mencerminkan perubahanmu.
2021
2021
  Lewati ScopeCairn hanya untuk pertanyaan non-coding.
2022
2022
  `;
2023
2023
  }
2024
- function agentsMd(prefix) {
2025
- return `${MARKER}
2026
- ${ruleBody(prefix)}`;
2024
+ function claudeSkillMd(prefix) {
2025
+ return `---
2026
+ name: scopecairn
2027
+ description: ScopeCairn codebase context + scope. Use for ALL coding tasks
2028
+ (feature, bugfix, refactor, code questions) before exploring the repo,
2029
+ and when the user says "scopecairn", asks for a repo overview, or asks
2030
+ to set up ScopeCairn.
2031
+ allowed-tools: Bash
2032
+ ---
2033
+
2034
+ # ScopeCairn
2035
+
2036
+ 1. Jika \`.scopecairn/scopecairn.db\` belum ada: jalankan \`${prefix} init\`
2037
+ (bila CLI belum ada: \`npm install -g scopecairn\`), laporkan file/simbol/relasi, berhenti.
2038
+ 2. Jika sudah ada: jalankan \`${prefix} context "<task user>"\`.
2039
+ Mulai dari Relevant Files. Jangan menjelajah dari nol.
2040
+ 3. Ubah hanya Required/Optional; Protected perlu persetujuan user.
2041
+ Simbol bersama \u2192 \`${prefix} impact <path>\`.
2042
+ Butuh isi fungsi \u2192 \`${prefix} read symbol <nama>\`, bukan baca file penuh.
2043
+ 4. Task membesar (>2 file)? Panggil ulang \`${prefix} context --escalate\`.
2044
+ 5. Setelah mengedit: \`${prefix} scan\`. Berhenti saat acceptance criteria terpenuhi.
2045
+ `;
2027
2046
  }
2028
2047
  function genericRuleMd(prefix) {
2029
2048
  return `${MARKER}
@@ -2064,6 +2083,37 @@ Shortcut manual bila agent lupa memanggil ScopeCairn otomatis.
2064
2083
  4. Perlu source simbol? \`${prefix} read symbol <nama>\`.
2065
2084
  `;
2066
2085
  }
2086
+ function opencodeCommandMd(prefix) {
2087
+ return `---
2088
+ description: ScopeCairn codebase context + scope for a task
2089
+ ---
2090
+
2091
+ Run \`${prefix} context "$ARGUMENTS"\` with the bash tool, then:
2092
+
2093
+ 1. Use Relevant Files as the starting point \u2014 do not explore from zero.
2094
+ 2. Change only Required and Optional files; Protected needs user approval.
2095
+ 3. If shared symbols change, run \`${prefix} impact <path>\`.
2096
+ 4. Need a function body? Use \`${prefix} read symbol <nama>\`, not full reads.
2097
+ 5. After editing, run \`${prefix} scan\` so the ScopeCairn index stays fresh.
2098
+ <!-- scopecairn:managed -->
2099
+ `;
2100
+ }
2101
+ function opencodePermissionGuide(prefix) {
2102
+ const cmds = ["context", "impact", "read", "graph", "status", "doctor"];
2103
+ const rules = cmds.map((c) => ` "${prefix} ${c} *": "allow"`).join(",\n");
2104
+ return `Agar command di atas jalan tanpa persetujuan, gabungkan ke opencode.json
2105
+ (project root atau ~/.config/opencode/opencode.json):
2106
+
2107
+ {
2108
+ "permission": {
2109
+ "bash": {
2110
+ ${rules}
2111
+ }
2112
+ }
2113
+ }
2114
+
2115
+ Catatan: ScopeCairn tidak mengubah opencode.json Anda secara otomatis.`;
2116
+ }
2067
2117
  var ALLOWLIST_COMMANDS = [
2068
2118
  "context",
2069
2119
  "impact",
@@ -2085,29 +2135,13 @@ Catatan: ScopeCairn tidak mengubah pengaturan IDE Anda secara otomatis.`;
2085
2135
 
2086
2136
  // src/integrate/matrix.ts
2087
2137
  var AGENT_MATRIX = [
2088
- {
2089
- id: "agents",
2090
- label: "AGENTS.md (universal: Codex, Aider, Opencode, Amp, Jules, ...)",
2091
- rel: "AGENTS.md",
2092
- mode: "merge",
2093
- render: agentsMd,
2094
- presentMarker: "AGENTS.md"
2095
- },
2096
2138
  {
2097
2139
  id: "claude",
2098
2140
  label: "Claude Code",
2099
- rel: "CLAUDE.md",
2100
- mode: "merge",
2101
- render: genericRuleMd,
2102
- presentMarker: "CLAUDE.md"
2103
- },
2104
- {
2105
- id: "gemini",
2106
- label: "Gemini / Antigravity CLI",
2107
- rel: "GEMINI.md",
2108
- mode: "merge",
2109
- render: genericRuleMd,
2110
- presentMarker: "GEMINI.md"
2141
+ rel: ".claude/skills/scopecairn/SKILL.md",
2142
+ mode: "new",
2143
+ render: claudeSkillMd,
2144
+ presentMarker: ".claude"
2111
2145
  },
2112
2146
  {
2113
2147
  id: "cursor",
@@ -2140,6 +2174,14 @@ var AGENT_MATRIX = [
2140
2174
  mode: "new",
2141
2175
  render: genericRuleMd,
2142
2176
  presentMarker: ".kiro"
2177
+ },
2178
+ {
2179
+ id: "opencode",
2180
+ label: "OpenCode",
2181
+ rel: ".opencode/commands/scopecairn.md",
2182
+ mode: "new",
2183
+ render: opencodeCommandMd,
2184
+ presentMarker: ".opencode"
2143
2185
  }
2144
2186
  ];
2145
2187
  function findTarget(id) {
@@ -2194,7 +2236,7 @@ async function cmdDoctor(repoRoot2) {
2194
2236
  const abs = path11.join(repoRoot2, t.rel);
2195
2237
  if (!fs11.existsSync(abs)) continue;
2196
2238
  const text = fs11.readFileSync(abs, "utf8");
2197
- if (!text.includes(MARKER)) continue;
2239
+ if (t.mode === "merge" && !text.includes(MARKER)) continue;
2198
2240
  matrixOk++;
2199
2241
  const used = extractPrefixUsed(text);
2200
2242
  if (used && used !== expectedPrefix) {
@@ -2216,7 +2258,7 @@ async function cmdDoctor(repoRoot2) {
2216
2258
  const wfPath = path11.join(repoRoot2, "workflows", "scopecairn.md");
2217
2259
  console.log(`${tick(fs11.existsSync(wfPath))} Workflow (workflows/scopecairn.md, opsional)`);
2218
2260
  console.log(`i Strict Mode mengabaikan allowlist \u2014 semua perintah tetap perlu persetujuan.`);
2219
- const healthy = nodeOk && sqliteOk && ts.ok && dbOk && matrixOk > 0 && skillOk && prefixOk;
2261
+ const healthy = nodeOk && sqliteOk && ts.ok && dbOk && (matrixOk > 0 || skillOk) && prefixOk;
2220
2262
  console.log(`Status: ${healthy ? "HEALTHY" : "DEGRADED (see \u2717 above)"}`);
2221
2263
  }
2222
2264
 
@@ -2464,29 +2506,36 @@ function retrieve(db, repoRoot2, task, opts) {
2464
2506
  proximity.set(s.symbolId, Math.max(proximity.get(s.symbolId) ?? 0, s.bm25));
2465
2507
  visited.add(s.symbolId);
2466
2508
  });
2467
- const edgeStmt = db.prepare(
2468
- `SELECT source_id, target_id, weight FROM relationships`
2469
- );
2470
- const adj = /* @__PURE__ */ new Map();
2471
- for (const r of edgeStmt.all()) {
2472
- if (!adj.has(r.source_id)) adj.set(r.source_id, []);
2473
- adj.get(r.source_id).push({ to: r.target_id, w: r.weight });
2474
- if (!adj.has(r.target_id)) adj.set(r.target_id, []);
2475
- adj.get(r.target_id).push({ to: r.source_id, w: r.weight * 0.8 });
2509
+ const CHUNK = 2e3;
2510
+ function neighborsOf(ids) {
2511
+ const out = [];
2512
+ for (let i = 0; i < ids.length; i += CHUNK) {
2513
+ const c = ids.slice(i, i + CHUNK);
2514
+ const ph = c.map(() => "?").join(",");
2515
+ for (const r of db.prepare(
2516
+ `SELECT source_id AS f, target_id AS t, weight AS w FROM relationships WHERE source_id IN (${ph})`
2517
+ ).all(...c)) {
2518
+ out.push({ from: r.f, to: r.t, w: r.w });
2519
+ }
2520
+ for (const r of db.prepare(
2521
+ `SELECT target_id AS f, source_id AS t, weight * 0.8 AS w FROM relationships WHERE target_id IN (${ph})`
2522
+ ).all(...c)) {
2523
+ out.push({ from: r.f, to: r.t, w: r.w });
2524
+ }
2525
+ }
2526
+ return out;
2476
2527
  }
2477
2528
  let hopScore = 1;
2478
2529
  for (let depth = 1; depth <= config.maxDepth; depth++) {
2479
2530
  hopScore *= config.decayPerHop;
2480
2531
  const next = [];
2481
- for (const id of frontier) {
2482
- const base = proximity.get(id) ?? 0;
2483
- for (const e of adj.get(id) ?? []) {
2484
- const cand = base * e.w * hopScore;
2485
- if (cand > (proximity.get(e.to) ?? 0)) proximity.set(e.to, cand);
2486
- if (!visited.has(e.to)) {
2487
- visited.add(e.to);
2488
- next.push(e.to);
2489
- }
2532
+ for (const e of neighborsOf(frontier)) {
2533
+ const base = proximity.get(e.from) ?? 0;
2534
+ const cand = base * e.w * hopScore;
2535
+ if (cand > (proximity.get(e.to) ?? 0)) proximity.set(e.to, cand);
2536
+ if (!visited.has(e.to)) {
2537
+ visited.add(e.to);
2538
+ next.push(e.to);
2490
2539
  }
2491
2540
  }
2492
2541
  frontier = next;
@@ -3256,14 +3305,13 @@ function withCore(ids) {
3256
3305
  console.log(`! Unknown agent "${id}" (pilihan: ${AGENT_MATRIX.map((x) => x.id).join(", ")}, all)`);
3257
3306
  continue;
3258
3307
  }
3259
- out.push(t);
3308
+ if (!out.includes(t)) out.push(t);
3260
3309
  }
3261
- if (!out.includes(AGENT_MATRIX[0])) out.unshift(AGENT_MATRIX[0]);
3262
3310
  return out;
3263
3311
  }
3264
3312
  function withDetected(repoRoot2) {
3265
- const targets = [AGENT_MATRIX[0]];
3266
- for (const t of AGENT_MATRIX.slice(1)) {
3313
+ const targets = [];
3314
+ for (const t of AGENT_MATRIX) {
3267
3315
  if (fs15.existsSync(path15.join(repoRoot2, t.presentMarker))) targets.push(t);
3268
3316
  }
3269
3317
  return targets;
@@ -3499,8 +3547,13 @@ function registerAgentCommands(program2, repoRoot2) {
3499
3547
  const cmd = program2.command(t.id).description(`${t.label} integration`);
3500
3548
  cmd.command("install").description(`Write ${t.rel}`).action(() => {
3501
3549
  const root = repoRoot2();
3502
- const r = installTarget(root, t, detectPrefix(root));
3550
+ const prefix = detectPrefix(root);
3551
+ const r = installTarget(root, t, prefix);
3503
3552
  console.log(`\u2713 ${t.label}: ${r.rel} (${r.how})`);
3553
+ if (t.id === "opencode") {
3554
+ console.log("");
3555
+ console.log(opencodePermissionGuide(prefix));
3556
+ }
3504
3557
  });
3505
3558
  cmd.command("uninstall").description(`Remove ScopeCairn block from ${t.rel}`).action(() => {
3506
3559
  const r = uninstallTarget(repoRoot2(), t);
@@ -3512,7 +3565,7 @@ function registerAgentCommands(program2, repoRoot2) {
3512
3565
  // package.json
3513
3566
  var package_default = {
3514
3567
  name: "scopecairn",
3515
- version: "0.3.0",
3568
+ version: "0.4.0",
3516
3569
  description: "ScopeCairn \u2014 Codebase Intelligence & Agent Optimization Layer (local-first)",
3517
3570
  license: "MIT",
3518
3571
  type: "module",
@@ -3599,7 +3652,7 @@ program.command("context <task>").description("Main entry point: auto-refresh +
3599
3652
  program.command("impact <target>").description("Change impact analysis: file, symbol, function, or class").option("--no-refresh", "skip incremental refresh (debugging/benchmark)").action(
3600
3653
  (target, opts) => cmdImpact(repoRoot(), target, opts)
3601
3654
  );
3602
- program.command("init [path]").description("Initial indexing + generate AGENTS.md, Skill, Workflow, agent matrix").option("--agents <list>", "agent targets: comma list from agents,claude,gemini,cursor,windsurf,copilot,kiro, or 'all'").action(
3655
+ program.command("init [path]").description("Initial indexing + Skill, Workflow, agent skills (never touches AGENTS.md)").option("--agents <list>", "agent targets: comma list from claude,cursor,windsurf,copilot,kiro,opencode, or 'all'").action(
3603
3656
  (targetPath, opts) => cmdInit(resolveRoot(targetPath), opts)
3604
3657
  );
3605
3658
  program.command("benchmark").description("Retrieval recall, irrelevant ratio, context reduction + weight tuning").option("--tune", "grid search bobot FR-05").option("--no-refresh", "skip incremental refresh (debugging/benchmark)").action(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scopecairn",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "ScopeCairn — Codebase Intelligence & Agent Optimization Layer (local-first)",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,31 +1,30 @@
1
1
  ---
2
2
  name: scopecairn
3
- description: Set up ScopeCairn codebase intelligence in a repository. Use when the user says "scopecairn", "scopecairn ./", "scopecairn <path>", asks to index a repo for an AI agent, or wants the agent to understand a codebase with ScopeCairn.
3
+ description: Set up ScopeCairn codebase intelligence, then use it for coding
4
+ tasks. Use when the user says "scopecairn", "scopecairn ./",
5
+ "scopecairn <path>", asks to index a repo for an AI agent, asks for a
6
+ repo overview, or for ALL coding tasks (feature, bugfix, refactor)
7
+ before exploring the repo.
8
+ allowed-tools: Bash
4
9
  ---
5
10
 
6
- # ScopeCairn setup
11
+ # ScopeCairn
7
12
 
8
- The user invoked ScopeCairn with a path (e.g. `scopecairn ./`).
9
- Your job: run project setup so this repo becomes agent-readable.
13
+ ## Setup (bila `.scopecairn/scopecairn.db` belum ada)
10
14
 
11
- ## Steps
15
+ 1. Resolve path: `.`/`./` = direktori proyek saat ini.
16
+ 2. Jalankan `scopecairn init <path>` (`npx -y scopecairn init <path>`
17
+ bila CLI belum ada; atau `npm install -g scopecairn` dulu).
18
+ 3. Laporkan singkat: file terindex, simbol, relasi, adapter,
19
+ file integrasi yang ditulis. Berhenti setelah setup.
12
20
 
13
- 1. Resolve the path: `.` or `./` means the current project directory.
14
- A relative or absolute path means that directory.
15
- 2. Run setup in that directory:
16
- `scopecairn init <path>` (or `npx -y scopecairn init <path>` if it is not installed).
17
- 3. If the command is not found, install globally first
18
- (`npm install -g scopecairn`), then retry step 2.
19
- 4. Report back, briefly: files indexed, symbols, relationships,
20
- detected adapters, and which integration files were written
21
- (AGENTS.md, Skill, Workflow, agent matrix).
21
+ ## Tiap task coding (bila index sudah ada)
22
22
 
23
- ## Rules
24
-
25
- - Setup is read-only toward source code. `init` only adds/updates
26
- `AGENTS.md` (marker block), the agent Skill, the Workflow file,
27
- and the `.scopecairn/` index directory. Never refactor user code.
28
- - If a terminal command needs approval, ask once — after setup,
29
- recommend the user allowlist the read-only commands printed by `init`.
30
- - After setup, coding tasks go through `scopecairn context "<task>"`,
31
- never by re-exploring the repository from zero.
23
+ 1. Jalankan `scopecairn context "<task user>"`.
24
+ Mulai dari Relevant Files. Jangan menjelajah dari nol.
25
+ 2. Ubah hanya Required/Optional; Protected perlu persetujuan user.
26
+ Simbol bersama → `scopecairn impact <path>`.
27
+ Butuh isi fungsi → `scopecairn read symbol <nama>`, bukan baca file penuh.
28
+ 3. Task membesar (>2 file)? Panggil ulang `scopecairn context --escalate`.
29
+ 4. Setelah mengedit: `scopecairn scan`.
30
+ Berhenti saat acceptance criteria terpenuhi.