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.
- package/dist/cli.js +143 -28
- 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
|
|
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
|
|
2735
|
-
import
|
|
2849
|
+
import fs13 from "fs";
|
|
2850
|
+
import path14 from "path";
|
|
2736
2851
|
function writeTarget(repoRoot2, target, prefix) {
|
|
2737
|
-
const abs =
|
|
2852
|
+
const abs = path14.join(repoRoot2, target.rel);
|
|
2738
2853
|
const block = target.render(prefix);
|
|
2739
2854
|
if (target.mode === "new") {
|
|
2740
|
-
const existed =
|
|
2741
|
-
|
|
2742
|
-
|
|
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 (
|
|
2746
|
-
const cur =
|
|
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
|
-
|
|
2864
|
+
fs13.writeFileSync(abs, cur.replace(re, block.trimEnd()).trimEnd() + "\n");
|
|
2750
2865
|
return "updated";
|
|
2751
2866
|
}
|
|
2752
|
-
|
|
2867
|
+
fs13.writeFileSync(abs, cur.trimEnd() + "\n\n" + block);
|
|
2753
2868
|
return "extended";
|
|
2754
2869
|
}
|
|
2755
|
-
|
|
2756
|
-
|
|
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 (
|
|
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 =
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
console.log(`\u2713 Skill installed (${
|
|
2798
|
-
const wfPath =
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
console.log(`\u2713 Workflow installed (${
|
|
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
|
|
2809
|
-
import
|
|
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
|
|
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.
|
|
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
|
|
3185
|
+
return path16.resolve(process.cwd());
|
|
3071
3186
|
}
|
|
3072
3187
|
function resolveRoot(given) {
|
|
3073
3188
|
if (!given) return repoRoot();
|
|
3074
|
-
return
|
|
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()));
|