scopecairn 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.
Files changed (2) hide show
  1. package/dist/cli.js +143 -28
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { Command } from "commander";
5
- import path15 from "path";
5
+ import path16 from "path";
6
6
 
7
7
  // src/scanner.ts
8
8
  import fs7 from "fs";
@@ -2544,6 +2544,116 @@ ${tests.map((t) => `- ${t}`).join("\n") || "-"}
2544
2544
  return { markdown: md, high: highFiles, medium: medFiles, tests, dependencies: deps };
2545
2545
  }
2546
2546
 
2547
+ // src/commands/orientasi.ts
2548
+ import fs12 from "fs";
2549
+ import path13 from "path";
2550
+ function isPathLike(task) {
2551
+ const t = task.trim().replace(/^["']|["']$/g, "");
2552
+ return t === "." || t === "./" || t === "/" || t.startsWith("./") || t.startsWith("../") || t.startsWith("/") || t.startsWith("~");
2553
+ }
2554
+ function looksLikeRepoPath(db, repoRoot2, task) {
2555
+ const t = task.trim().replace(/^["']|["']$/g, "");
2556
+ if (/\s/.test(t)) return false;
2557
+ if (isPathLike(task)) return true;
2558
+ try {
2559
+ fs12.statSync(path13.resolve(repoRoot2, t));
2560
+ return true;
2561
+ } catch {
2562
+ }
2563
+ try {
2564
+ const row = db.prepare(`SELECT 1 AS ok FROM files WHERE path = ? OR path LIKE ? LIMIT 1`).get(t, `%/${t}`);
2565
+ return !!row;
2566
+ } catch {
2567
+ return false;
2568
+ }
2569
+ }
2570
+ function topCentral(db, limit = 10) {
2571
+ try {
2572
+ return db.prepare(
2573
+ `SELECT s.name, s.type, f.path AS file, COUNT(*) AS fanin
2574
+ FROM relationships r
2575
+ JOIN symbols s ON s.id = r.target_id
2576
+ JOIN files f ON f.id = s.file_id
2577
+ WHERE s.type IN ('function','class','method','component','interface','type')
2578
+ GROUP BY r.target_id ORDER BY fanin DESC LIMIT ${limit}`
2579
+ ).all();
2580
+ } catch {
2581
+ return [];
2582
+ }
2583
+ }
2584
+ function cmdOrientasi(repoRoot2, task) {
2585
+ const db = openDb(repoRoot2);
2586
+ try {
2587
+ const files = db.prepare(`SELECT COUNT(*) AS n FROM files`).get().n;
2588
+ if (files === 0) {
2589
+ console.log("Index kosong. Jalankan `scopecairn scan` dulu.");
2590
+ return;
2591
+ }
2592
+ const byLang = db.prepare(`SELECT language, COUNT(*) AS n FROM files GROUP BY language ORDER BY n DESC`).all();
2593
+ const symbols = db.prepare(`SELECT COUNT(*) AS n FROM symbols`).get().n;
2594
+ const t = task.trim().replace(/^["']|["']$/g, "");
2595
+ const abs = path13.resolve(repoRoot2, t);
2596
+ let rel = null;
2597
+ try {
2598
+ const stat = fs12.statSync(abs);
2599
+ if (stat.isFile()) rel = path13.relative(repoRoot2, abs).split(path13.sep).join("/");
2600
+ } catch {
2601
+ rel = null;
2602
+ }
2603
+ if (rel) {
2604
+ const row = db.prepare(`SELECT id FROM files WHERE path = ?`).get(rel);
2605
+ if (row) {
2606
+ const syms = db.prepare(`SELECT name, type FROM symbols WHERE file_id = ? AND type != 'file' LIMIT 20`).all(row.id);
2607
+ const fileSym = db.prepare(`SELECT id FROM symbols WHERE file_id = ? AND type = 'file'`).get(row.id);
2608
+ const linked = /* @__PURE__ */ new Set();
2609
+ if (fileSym) {
2610
+ for (const e of neighbors(db, fileSym.id, 30)) {
2611
+ if (e.other.file !== rel) linked.add(e.other.file);
2612
+ }
2613
+ }
2614
+ console.log(
2615
+ `# Task Context (orientasi)
2616
+
2617
+ ## Task
2618
+ ${task}
2619
+
2620
+ ## File
2621
+ ${rel}
2622
+
2623
+ ## Simbol (${syms.length})
2624
+ ${syms.map((s) => `- ${s.name} (${s.type})`).join("\n") || "-"}
2625
+
2626
+ ## Terkait langsung
2627
+ ${[...linked].slice(0, 10).map((f) => `- ${f}`).join("\n") || "-"}
2628
+
2629
+ Butuh konteks kerja? Jalankan \`scopecairn context "<deskripsi task>"\`.
2630
+ `
2631
+ );
2632
+ return;
2633
+ }
2634
+ }
2635
+ const central = topCentral(db);
2636
+ console.log(
2637
+ `# Task Context (orientasi)
2638
+
2639
+ ## Task
2640
+ ${task}
2641
+
2642
+ ## Ringkasan index
2643
+ - ${files} file (${byLang.map((l) => `${l.language} ${l.n}`).join(", ")})
2644
+ - ${symbols} simbol
2645
+
2646
+ ## Simbol paling dipakai
2647
+ ${central.map((c) => `- ${c.name} (${c.type}, ${c.file}) \u2190 ${c.fanin}x`).join("\n") || "-"}
2648
+
2649
+ Mulai dari simbol di atas, atau jalankan \`scopecairn context "<deskripsi task>"\` untuk konteks kerja.
2650
+ `
2651
+ );
2652
+ } finally {
2653
+ db.close();
2654
+ }
2655
+ }
2656
+
2547
2657
  // src/commands/context.ts
2548
2658
  function cmdContext(repoRoot2, task, opts = {}) {
2549
2659
  if (!opts.noRefresh) {
@@ -2562,6 +2672,11 @@ function cmdContext(repoRoot2, task, opts = {}) {
2562
2672
  console.log("Index kosong. Jalankan `scopecairn scan` dulu.");
2563
2673
  return;
2564
2674
  }
2675
+ if (looksLikeRepoPath(db, repoRoot2, task)) {
2676
+ logInvocation(db, "context");
2677
+ cmdOrientasi(repoRoot2, task);
2678
+ return;
2679
+ }
2565
2680
  const result = retrieve(db, repoRoot2, task);
2566
2681
  if (result.ranked.length === 0) {
2567
2682
  logInvocation(db, "context");
@@ -2731,29 +2846,29 @@ function cmdImpact(repoRoot2, target, opts = {}) {
2731
2846
  }
2732
2847
 
2733
2848
  // src/commands/init.ts
2734
- import fs12 from "fs";
2735
- import path13 from "path";
2849
+ import fs13 from "fs";
2850
+ import path14 from "path";
2736
2851
  function writeTarget(repoRoot2, target, prefix) {
2737
- const abs = path13.join(repoRoot2, target.rel);
2852
+ const abs = path14.join(repoRoot2, target.rel);
2738
2853
  const block = target.render(prefix);
2739
2854
  if (target.mode === "new") {
2740
- const existed = fs12.existsSync(abs);
2741
- fs12.mkdirSync(path13.dirname(abs), { recursive: true });
2742
- fs12.writeFileSync(abs, block.endsWith("\n") ? block : block + "\n");
2855
+ const existed = fs13.existsSync(abs);
2856
+ fs13.mkdirSync(path14.dirname(abs), { recursive: true });
2857
+ fs13.writeFileSync(abs, block.endsWith("\n") ? block : block + "\n");
2743
2858
  return existed ? "updated" : "generated";
2744
2859
  }
2745
- if (fs12.existsSync(abs)) {
2746
- const cur = fs12.readFileSync(abs, "utf8");
2860
+ if (fs13.existsSync(abs)) {
2861
+ const cur = fs13.readFileSync(abs, "utf8");
2747
2862
  if (cur.includes(MARKER)) {
2748
2863
  const re = new RegExp(`${MARKER}[\\s\\S]*?(?=\\n#[^#]|$)`, "");
2749
- fs12.writeFileSync(abs, cur.replace(re, block.trimEnd()).trimEnd() + "\n");
2864
+ fs13.writeFileSync(abs, cur.replace(re, block.trimEnd()).trimEnd() + "\n");
2750
2865
  return "updated";
2751
2866
  }
2752
- fs12.writeFileSync(abs, cur.trimEnd() + "\n\n" + block);
2867
+ fs13.writeFileSync(abs, cur.trimEnd() + "\n\n" + block);
2753
2868
  return "extended";
2754
2869
  }
2755
- fs12.mkdirSync(path13.dirname(abs), { recursive: true });
2756
- fs12.writeFileSync(abs, block.endsWith("\n") ? block : block + "\n");
2870
+ fs13.mkdirSync(path14.dirname(abs), { recursive: true });
2871
+ fs13.writeFileSync(abs, block.endsWith("\n") ? block : block + "\n");
2757
2872
  return "generated";
2758
2873
  }
2759
2874
  function cmdInit(repoRoot2, opts = {}) {
@@ -2784,29 +2899,29 @@ function cmdInit(repoRoot2, opts = {}) {
2784
2899
  } else {
2785
2900
  targets = [AGENT_MATRIX[0]];
2786
2901
  for (const t of AGENT_MATRIX.slice(1)) {
2787
- if (fs12.existsSync(path13.join(repoRoot2, t.presentMarker))) targets.push(t);
2902
+ if (fs13.existsSync(path14.join(repoRoot2, t.presentMarker))) targets.push(t);
2788
2903
  }
2789
2904
  }
2790
2905
  for (const t of targets) {
2791
2906
  const how = writeTarget(repoRoot2, t, prefix);
2792
2907
  console.log(`\u2713 ${t.label}: ${t.rel} (${how})`);
2793
2908
  }
2794
- const skillPath = path13.join(repoRoot2, agentDir, "skills", "scopecairn", "SKILL.md");
2795
- fs12.mkdirSync(path13.dirname(skillPath), { recursive: true });
2796
- fs12.writeFileSync(skillPath, skillMd(prefix));
2797
- console.log(`\u2713 Skill installed (${path13.relative(repoRoot2, skillPath)})`);
2798
- const wfPath = path13.join(repoRoot2, "workflows", "scopecairn.md");
2799
- fs12.mkdirSync(path13.dirname(wfPath), { recursive: true });
2800
- fs12.writeFileSync(wfPath, workflowMd(prefix));
2801
- console.log(`\u2713 Workflow installed (${path13.relative(repoRoot2, wfPath)})`);
2909
+ const skillPath = path14.join(repoRoot2, agentDir, "skills", "scopecairn", "SKILL.md");
2910
+ fs13.mkdirSync(path14.dirname(skillPath), { recursive: true });
2911
+ fs13.writeFileSync(skillPath, skillMd(prefix));
2912
+ console.log(`\u2713 Skill installed (${path14.relative(repoRoot2, skillPath)})`);
2913
+ const wfPath = path14.join(repoRoot2, "workflows", "scopecairn.md");
2914
+ fs13.mkdirSync(path14.dirname(wfPath), { recursive: true });
2915
+ fs13.writeFileSync(wfPath, workflowMd(prefix));
2916
+ console.log(`\u2713 Workflow installed (${path14.relative(repoRoot2, wfPath)})`);
2802
2917
  console.log("Ready.");
2803
2918
  console.log("");
2804
2919
  console.log(allowlistGuide(prefix));
2805
2920
  }
2806
2921
 
2807
2922
  // src/benchmark/run.ts
2808
- import fs13 from "fs";
2809
- import path14 from "path";
2923
+ import fs14 from "fs";
2924
+ import path15 from "path";
2810
2925
 
2811
2926
  // src/benchmark/dataset.ts
2812
2927
  var BENCHMARK_TASKS = [
@@ -2863,7 +2978,7 @@ var BENCHMARK_TASKS = [
2863
2978
  // src/benchmark/run.ts
2864
2979
  function fileChars(repoRoot2, rel) {
2865
2980
  try {
2866
- return fs13.readFileSync(path14.join(repoRoot2, rel), "utf8").length;
2981
+ return fs14.readFileSync(path15.join(repoRoot2, rel), "utf8").length;
2867
2982
  } catch {
2868
2983
  return 0;
2869
2984
  }
@@ -3015,7 +3130,7 @@ function cmdBenchmark(repoRoot2, opts = {}) {
3015
3130
  // package.json
3016
3131
  var package_default = {
3017
3132
  name: "scopecairn",
3018
- version: "0.2.0",
3133
+ version: "0.2.1",
3019
3134
  description: "ScopeCairn \u2014 Codebase Intelligence & Agent Optimization Layer (local-first)",
3020
3135
  license: "MIT",
3021
3136
  type: "module",
@@ -3067,11 +3182,11 @@ var package_default = {
3067
3182
  var program = new Command();
3068
3183
  program.name("scopecairn").description("ScopeCairn \u2014 local-first codebase intelligence layer").version(package_default.version);
3069
3184
  function repoRoot() {
3070
- return path15.resolve(process.cwd());
3185
+ return path16.resolve(process.cwd());
3071
3186
  }
3072
3187
  function resolveRoot(given) {
3073
3188
  if (!given) return repoRoot();
3074
- return path15.resolve(process.cwd(), given);
3189
+ return path16.resolve(process.cwd(), given);
3075
3190
  }
3076
3191
  program.command("scan").description("Index repository (Fase 1: files + hash + language)").action(() => cmdScan(repoRoot()));
3077
3192
  program.command("status").description("Show index status").action(() => cmdStatus(repoRoot()));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scopecairn",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "ScopeCairn — Codebase Intelligence & Agent Optimization Layer (local-first)",
5
5
  "license": "MIT",
6
6
  "type": "module",