@fenglimg/fabric-cli 2.0.0 → 2.1.0-rc.2

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 (86) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -5
  3. package/dist/chunk-BATF4PEJ.js +361 -0
  4. package/dist/{chunk-OBQU6NHO.js → chunk-COI5VDFU.js} +0 -18
  5. package/dist/chunk-F46ORPOA.js +903 -0
  6. package/dist/chunk-HFQVXY6P.js +86 -0
  7. package/dist/chunk-L4Q55UC4.js +52 -0
  8. package/dist/chunk-LFIKMVY7.js +27 -0
  9. package/dist/chunk-MF3OTILQ.js +544 -0
  10. package/dist/chunk-PWLW3B57.js +18 -0
  11. package/dist/chunk-RYAFBNES.js +33 -0
  12. package/dist/chunk-T5RPGCCM.js +40 -0
  13. package/dist/chunk-WU6GAPKH.js +36 -0
  14. package/dist/config-XJIPZNUP.js +13 -0
  15. package/dist/doctor-QVNPHLJK.js +920 -0
  16. package/dist/index.js +23 -8
  17. package/dist/{init-BIRSIOXO.js → install-2HDO5FTQ.js} +807 -705
  18. package/dist/metrics-ACEQFPDU.js +122 -0
  19. package/dist/onboard-coverage-MFCAEBDO.js +220 -0
  20. package/dist/{plan-context-hint-QMUPAXIB.js → plan-context-hint-FC6P3WFE.js} +34 -28
  21. package/dist/scope-explain-2F2R5URO.js +33 -0
  22. package/dist/status-GLQWLWH6.js +23 -0
  23. package/dist/store-XTSE5TY6.js +105 -0
  24. package/dist/sync-BJCWDPNC.js +245 -0
  25. package/dist/uninstall-TAXSUSKH.js +1073 -0
  26. package/dist/whoami-B6AEMSEV.js +31 -0
  27. package/package.json +30 -5
  28. package/templates/hooks/cite-policy-evict.cjs +231 -0
  29. package/templates/hooks/configs/README.md +29 -6
  30. package/templates/hooks/configs/claude-code.json +14 -3
  31. package/templates/hooks/configs/codex-hooks.json +6 -3
  32. package/templates/hooks/configs/cursor-hooks.json +8 -10
  33. package/templates/hooks/fabric-hint.cjs +873 -105
  34. package/templates/hooks/knowledge-hint-broad.cjs +549 -135
  35. package/templates/hooks/knowledge-hint-narrow.cjs +830 -26
  36. package/templates/hooks/lib/banner-i18n.cjs +309 -0
  37. package/templates/hooks/lib/bindings-snapshot-reader.cjs +81 -0
  38. package/templates/hooks/lib/cite-contract-reminder.cjs +179 -0
  39. package/templates/hooks/lib/cite-line-parser.cjs +180 -0
  40. package/templates/hooks/lib/client-adapter.cjs +106 -0
  41. package/templates/hooks/lib/config-cache.cjs +107 -0
  42. package/templates/hooks/lib/state-store.cjs +84 -0
  43. package/templates/hooks/lib/summary-fallback.cjs +210 -0
  44. package/templates/skills/fabric-archive/SKILL.md +97 -419
  45. package/templates/skills/fabric-archive/ref/dry-run-scope.md +16 -0
  46. package/templates/skills/fabric-archive/ref/e5-cron-recap.md +58 -0
  47. package/templates/skills/fabric-archive/ref/i18n-policy.md +86 -0
  48. package/templates/skills/fabric-archive/ref/phase-0-range-resolution.md +156 -0
  49. package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +218 -0
  50. package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +62 -0
  51. package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +68 -0
  52. package/templates/skills/fabric-archive/ref/phase-3-5-scope.md +108 -0
  53. package/templates/skills/fabric-archive/ref/phase-3-classify.md +63 -0
  54. package/templates/skills/fabric-archive/ref/phase-4-5-emit.md +78 -0
  55. package/templates/skills/fabric-archive/ref/phase-4-mcp-persist.md +89 -0
  56. package/templates/skills/fabric-archive/ref/rc-history.md +38 -0
  57. package/templates/skills/fabric-archive/ref/worked-examples.md +78 -0
  58. package/templates/skills/fabric-import/SKILL.md +77 -514
  59. package/templates/skills/fabric-import/ref/checkpoint-state.md +85 -0
  60. package/templates/skills/fabric-import/ref/i18n-policy.md +79 -0
  61. package/templates/skills/fabric-import/ref/output-contract.md +61 -0
  62. package/templates/skills/fabric-import/ref/phase-2-mining.md +213 -0
  63. package/templates/skills/fabric-import/ref/phase-3-dedup.md +75 -0
  64. package/templates/skills/fabric-import/ref/state-recovery.md +57 -0
  65. package/templates/skills/fabric-import/ref/worked-examples.md +127 -0
  66. package/templates/skills/fabric-review/SKILL.md +90 -284
  67. package/templates/skills/fabric-review/ref/askuserquestion-policy.md +66 -0
  68. package/templates/skills/fabric-review/ref/i18n-policy.md +111 -0
  69. package/templates/skills/fabric-review/ref/modify-flow.md +103 -0
  70. package/templates/skills/fabric-review/ref/output-contract.md +58 -0
  71. package/templates/skills/fabric-review/ref/per-mode-flows.md +155 -0
  72. package/templates/skills/fabric-review/ref/semantic-check.md +26 -0
  73. package/templates/skills/fabric-review/ref/worked-examples.md +95 -0
  74. package/templates/skills/fabric-sync/SKILL.md +46 -0
  75. package/templates/skills/lib/shared-policy.md +69 -0
  76. package/dist/chunk-6ICJICVU.js +0 -10
  77. package/dist/chunk-74SZWYPH.js +0 -658
  78. package/dist/chunk-EYIDD2YS.js +0 -1000
  79. package/dist/doctor-T7JWODKG.js +0 -282
  80. package/dist/hooks-Y74Y5LQS.js +0 -12
  81. package/dist/scan-LMK3UCWL.js +0 -22
  82. package/dist/serve-H554BHLG.js +0 -124
  83. package/templates/agents-md/AGENTS.md.template +0 -59
  84. package/templates/bootstrap/CLAUDE.md +0 -8
  85. package/templates/bootstrap/codex-AGENTS-header.md +0 -6
  86. package/templates/bootstrap/cursor-fabric-bootstrap.mdc +0 -10
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/commands/metrics.ts
4
+ import { resolve } from "path";
5
+ import { defineCommand } from "citty";
6
+ import { readMetrics } from "@fenglimg/fabric-server";
7
+ function parseSinceArg(raw) {
8
+ if (raw === void 0 || raw.length === 0) return 0;
9
+ const match = /^(\d+)([smhd]?)$/u.exec(raw);
10
+ if (match === null) {
11
+ throw new Error(`--since: invalid duration "${raw}" (expected e.g. 24h, 7d, 30m)`);
12
+ }
13
+ const n = Number.parseInt(match[1], 10);
14
+ const unit = match[2] ?? "s";
15
+ const multipliers = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
16
+ return n * (multipliers[unit] ?? 1e3);
17
+ }
18
+ function aggregate(rows, sinceMs, now) {
19
+ const cutoff = sinceMs > 0 ? now.getTime() - sinceMs : 0;
20
+ const filtered = rows.filter((r) => {
21
+ if (cutoff === 0) return true;
22
+ const ts = Date.parse(r.timestamp);
23
+ return Number.isFinite(ts) && ts >= cutoff;
24
+ });
25
+ const totals = {};
26
+ const perEntryConsumed = {};
27
+ for (const row of filtered) {
28
+ for (const [name, count] of Object.entries(row.counters)) {
29
+ if (name.startsWith("knowledge_consumed:")) {
30
+ const id = name.slice("knowledge_consumed:".length);
31
+ perEntryConsumed[id] = (perEntryConsumed[id] ?? 0) + count;
32
+ totals["knowledge_consumed"] = (totals["knowledge_consumed"] ?? 0) + count;
33
+ continue;
34
+ }
35
+ totals[name] = (totals[name] ?? 0) + count;
36
+ }
37
+ }
38
+ return {
39
+ windowDescription: sinceMs > 0 ? formatDuration(sinceMs) : "all-time",
40
+ rowCount: filtered.length,
41
+ totals,
42
+ perEntryConsumed,
43
+ rangeStart: filtered[0]?.timestamp ?? null,
44
+ rangeEnd: filtered[filtered.length - 1]?.timestamp ?? null
45
+ };
46
+ }
47
+ function formatDuration(ms) {
48
+ if (ms >= 864e5) return `${Math.round(ms / 864e5)}d`;
49
+ if (ms >= 36e5) return `${Math.round(ms / 36e5)}h`;
50
+ if (ms >= 6e4) return `${Math.round(ms / 6e4)}m`;
51
+ return `${Math.round(ms / 1e3)}s`;
52
+ }
53
+ function renderText(agg) {
54
+ const lines = [];
55
+ lines.push(`Fabric metrics \u2014 window: ${agg.windowDescription}`);
56
+ if (agg.rangeStart && agg.rangeEnd) {
57
+ lines.push(` rows: ${agg.rowCount} (${agg.rangeStart} \u2192 ${agg.rangeEnd})`);
58
+ } else {
59
+ lines.push(` rows: ${agg.rowCount}`);
60
+ }
61
+ lines.push("");
62
+ if (Object.keys(agg.totals).length === 0) {
63
+ lines.push(" (no counter activity in window \u2014 server may be idle or just started)");
64
+ return lines.join("\n");
65
+ }
66
+ lines.push(" counter total");
67
+ lines.push(" ------------------------------------ ----------");
68
+ const sorted = Object.entries(agg.totals).sort((a, b) => b[1] - a[1]);
69
+ for (const [name, count] of sorted) {
70
+ lines.push(` ${name.padEnd(36)} ${String(count).padStart(10)}`);
71
+ }
72
+ const perEntrySorted = Object.entries(agg.perEntryConsumed).sort((a, b) => b[1] - a[1]).slice(0, 10);
73
+ if (perEntrySorted.length > 0) {
74
+ lines.push("");
75
+ lines.push(" Top per-entry consumed (knowledge_consumed:<id>)");
76
+ lines.push(" ------------------------------------ ----------");
77
+ for (const [id, count] of perEntrySorted) {
78
+ lines.push(` ${id.padEnd(36)} ${String(count).padStart(10)}`);
79
+ }
80
+ }
81
+ return lines.join("\n");
82
+ }
83
+ var metricsCommand = defineCommand({
84
+ meta: {
85
+ name: "metrics",
86
+ description: "Print a text dashboard of Fabric counter activity from .fabric/metrics.jsonl",
87
+ hidden: true
88
+ },
89
+ args: {
90
+ json: {
91
+ type: "boolean",
92
+ description: "Emit machine-readable JSON instead of the text table.",
93
+ default: false
94
+ },
95
+ target: {
96
+ type: "string",
97
+ description: "Project root (defaults to the current working directory)."
98
+ },
99
+ since: {
100
+ type: "string",
101
+ description: "Limit to rows within a recent window. Examples: 24h, 7d, 30m, 90s. Omit for all-time."
102
+ }
103
+ },
104
+ async run({ args }) {
105
+ const projectRoot = resolve(args.target ?? process.cwd());
106
+ const sinceMs = parseSinceArg(args.since);
107
+ const rows = await readMetrics(projectRoot);
108
+ const aggregated = aggregate(rows, sinceMs, /* @__PURE__ */ new Date());
109
+ if (args.json === true) {
110
+ process.stdout.write(`${JSON.stringify(aggregated)}
111
+ `);
112
+ } else {
113
+ process.stdout.write(`${renderText(aggregated)}
114
+ `);
115
+ }
116
+ }
117
+ });
118
+ var metrics_default = metricsCommand;
119
+ export {
120
+ metrics_default as default,
121
+ metricsCommand
122
+ };
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ t
4
+ } from "./chunk-PWLW3B57.js";
5
+
6
+ // src/commands/onboard-coverage.ts
7
+ import { existsSync, readdirSync, readFileSync } from "fs";
8
+ import { join, resolve } from "path";
9
+ import { defineCommand } from "citty";
10
+ import {
11
+ ONBOARD_SLOT_NAMES,
12
+ ONBOARD_SLOT_TOTAL
13
+ } from "@fenglimg/fabric-shared";
14
+ var KNOWLEDGE_TYPE_DIRS = [
15
+ "decisions",
16
+ "pitfalls",
17
+ "guidelines",
18
+ "models",
19
+ "processes"
20
+ ];
21
+ var FABRIC_CONFIG_PATH = [".fabric", "fabric-config.json"];
22
+ function emptyFilled() {
23
+ const filled = {};
24
+ for (const slot of ONBOARD_SLOT_NAMES) {
25
+ filled[slot] = [];
26
+ }
27
+ return filled;
28
+ }
29
+ function readOnboardSlotFrontmatter(filePath) {
30
+ let content;
31
+ try {
32
+ content = readFileSync(filePath, "utf8");
33
+ } catch {
34
+ return void 0;
35
+ }
36
+ const match = /^---\n([\s\S]*?)\n---/u.exec(content);
37
+ if (match === null) return void 0;
38
+ const block = match[1];
39
+ if (block === void 0) return void 0;
40
+ for (const rawLine of block.split(/\r?\n/u)) {
41
+ const line = rawLine.trim();
42
+ const sep = line.indexOf(":");
43
+ if (sep === -1) continue;
44
+ const key = line.slice(0, sep).trim();
45
+ if (key !== "onboard_slot") continue;
46
+ let value = line.slice(sep + 1).trim();
47
+ if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
48
+ value = value.slice(1, -1);
49
+ }
50
+ return value;
51
+ }
52
+ return void 0;
53
+ }
54
+ function readStableIdFrontmatter(filePath, fallbackName) {
55
+ let content;
56
+ try {
57
+ content = readFileSync(filePath, "utf8");
58
+ } catch {
59
+ return fallbackName.replace(/\.md$/u, "");
60
+ }
61
+ const match = /^---\n([\s\S]*?)\n---/u.exec(content);
62
+ if (match === null) return fallbackName.replace(/\.md$/u, "");
63
+ const block = match[1];
64
+ if (block === void 0) return fallbackName.replace(/\.md$/u, "");
65
+ for (const rawLine of block.split(/\r?\n/u)) {
66
+ const line = rawLine.trim();
67
+ const sep = line.indexOf(":");
68
+ if (sep === -1) continue;
69
+ if (line.slice(0, sep).trim() !== "id") continue;
70
+ return line.slice(sep + 1).trim();
71
+ }
72
+ return fallbackName.replace(/\.md$/u, "");
73
+ }
74
+ function readOptedOutSlots(projectRoot) {
75
+ const path = join(projectRoot, ...FABRIC_CONFIG_PATH);
76
+ if (!existsSync(path)) return [];
77
+ let raw;
78
+ try {
79
+ raw = readFileSync(path, "utf8");
80
+ } catch {
81
+ return [];
82
+ }
83
+ let parsed;
84
+ try {
85
+ parsed = JSON.parse(raw);
86
+ } catch (err) {
87
+ process.stderr.write(
88
+ `onboard-coverage: ignoring malformed fabric-config.json (${err instanceof Error ? err.message : String(err)})
89
+ `
90
+ );
91
+ return [];
92
+ }
93
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
94
+ return [];
95
+ }
96
+ const slots = parsed.onboard_slots_opted_out;
97
+ if (!Array.isArray(slots)) return [];
98
+ return slots.filter((v) => typeof v === "string");
99
+ }
100
+ function runOnboardCoverage(projectRoot) {
101
+ const filled = emptyFilled();
102
+ const knowledgeRoot = join(projectRoot, ".fabric", "knowledge");
103
+ if (existsSync(knowledgeRoot)) {
104
+ for (const typeDir of KNOWLEDGE_TYPE_DIRS) {
105
+ const dir = join(knowledgeRoot, typeDir);
106
+ if (!existsSync(dir)) continue;
107
+ let entries;
108
+ try {
109
+ entries = readdirSync(dir, { withFileTypes: true });
110
+ } catch {
111
+ continue;
112
+ }
113
+ for (const entry of entries) {
114
+ if (!entry.isFile()) continue;
115
+ if (!entry.name.endsWith(".md")) continue;
116
+ const filePath = join(dir, entry.name);
117
+ const slot = readOnboardSlotFrontmatter(filePath);
118
+ if (slot === void 0) continue;
119
+ if (!ONBOARD_SLOT_NAMES.includes(slot)) continue;
120
+ const stableId = readStableIdFrontmatter(filePath, entry.name);
121
+ filled[slot].push(stableId);
122
+ }
123
+ }
124
+ }
125
+ const optedOut = readOptedOutSlots(projectRoot);
126
+ const missing = ONBOARD_SLOT_NAMES.filter((slot) => {
127
+ if (filled[slot].length > 0) return false;
128
+ if (optedOut.includes(slot)) return false;
129
+ return true;
130
+ });
131
+ for (const slot of ONBOARD_SLOT_NAMES) {
132
+ filled[slot].sort();
133
+ }
134
+ return {
135
+ filled,
136
+ missing,
137
+ opted_out: optedOut,
138
+ total: ONBOARD_SLOT_TOTAL
139
+ };
140
+ }
141
+ function renderHumanReadable(report) {
142
+ const filledCount = ONBOARD_SLOT_NAMES.filter(
143
+ (slot) => report.filled[slot].length > 0
144
+ ).length;
145
+ const optedOutCount = report.opted_out.length;
146
+ const missingCount = report.missing.length;
147
+ const lines = [];
148
+ lines.push(
149
+ `Onboard coverage: ${filledCount}/${report.total} filled, ${optedOutCount} opted-out, ${missingCount} missing`
150
+ );
151
+ lines.push("");
152
+ lines.push(" slot status entries");
153
+ lines.push(" -------------------------- ----------- -------------------------");
154
+ for (const slot of ONBOARD_SLOT_NAMES) {
155
+ const entries = report.filled[slot];
156
+ let status;
157
+ let detail;
158
+ if (entries.length > 0) {
159
+ status = "filled";
160
+ detail = entries.join(", ");
161
+ } else if (report.opted_out.includes(slot)) {
162
+ status = "opted-out";
163
+ detail = "(user-dismissed; run `fabric config onboard-reset` to re-open)";
164
+ } else {
165
+ status = "missing";
166
+ detail = "(run /fabric-archive to onboard)";
167
+ }
168
+ lines.push(` ${slot.padEnd(26)} ${status.padEnd(11)} ${detail}`);
169
+ }
170
+ return lines.join("\n");
171
+ }
172
+ var onboardCoverageCommand = defineCommand({
173
+ meta: {
174
+ name: "onboard-coverage",
175
+ // v2.0.0-rc.29 TASK-008 (BUG-L2): route description strings through t()
176
+ // (mirrors serve.ts pattern). Previously this command was English-only
177
+ // even when the rest of `fabric --help` rendered zh-CN, so Chinese-locale
178
+ // users saw an isolated English block under --help.
179
+ description: t("cli.onboard-coverage.description"),
180
+ // Mirrors `plan-context-hint`: hidden from `fabric --help` so the top-level
181
+ // banner stays focused on install/doctor/serve/config. The command stays
182
+ // callable directly from Skills via `fabric onboard-coverage --json`.
183
+ hidden: true
184
+ },
185
+ args: {
186
+ json: {
187
+ type: "boolean",
188
+ description: t("cli.onboard-coverage.args.json.description"),
189
+ default: false
190
+ },
191
+ target: {
192
+ type: "string",
193
+ description: t("cli.onboard-coverage.args.target.description")
194
+ }
195
+ },
196
+ async run({ args }) {
197
+ try {
198
+ const projectRoot = resolve(args.target ?? process.cwd());
199
+ const report = runOnboardCoverage(projectRoot);
200
+ if (args.json === true) {
201
+ process.stdout.write(`${JSON.stringify(report)}
202
+ `);
203
+ } else {
204
+ process.stdout.write(`${renderHumanReadable(report)}
205
+ `);
206
+ }
207
+ } catch (error) {
208
+ const message = error instanceof Error ? error.message : String(error);
209
+ process.stderr.write(`onboard-coverage failed: ${message}
210
+ `);
211
+ process.exitCode = 1;
212
+ }
213
+ }
214
+ });
215
+ var onboard_coverage_default = onboardCoverageCommand;
216
+ export {
217
+ onboard_coverage_default as default,
218
+ onboardCoverageCommand,
219
+ runOnboardCoverage
220
+ };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  resolveDevMode
4
- } from "./chunk-OBQU6NHO.js";
4
+ } from "./chunk-COI5VDFU.js";
5
5
 
6
6
  // src/commands/plan-context-hint.ts
7
7
  import { defineCommand } from "citty";
@@ -10,7 +10,12 @@ var ALL_PATHS_SENTINEL = "**";
10
10
  var planContextHintCommand = defineCommand({
11
11
  meta: {
12
12
  name: "plan-context-hint",
13
- description: "Emit versioned knowledge hint JSON to stdout. Used by rc.6 hooks and the fabric-import skill."
13
+ description: "Emit versioned knowledge hint JSON to stdout. Used by rc.6 hooks and the fabric-import skill.",
14
+ // rc.15 TASK-004 (C8): hidden from `fabric --help` listing. The command stays
15
+ // callable so hook scripts and the fabric-import skill can still invoke
16
+ // it via `fabric plan-context-hint ...`; it just no longer appears in the
17
+ // top-level usage banner alongside install/doctor/serve/uninstall/config.
18
+ hidden: true
14
19
  },
15
20
  args: {
16
21
  paths: {
@@ -53,27 +58,38 @@ async function runPlanContextHint(opts) {
53
58
  const result = await planContext(resolution.target, {
54
59
  paths: targetPaths
55
60
  });
56
- const sharedIndex = result.shared.description_index;
57
- const narrowSource = all ? sharedIndex : (
58
- // Path mode: union of per-entry description_index across requested
59
- // paths, deduped by stable_id. This is identical to `shared` for L0/L1
60
- // entries (always included) and additionally captures L2 entries whose
61
- // scope_glob matches the requested path.
62
- dedupeByStableId(result.entries.flatMap((entry) => entry.description_index))
63
- );
64
- const narrow = narrowSource.map((item) => ({
61
+ const candidates = result.candidates;
62
+ const entries = candidates.map((item) => ({
65
63
  id: item.stable_id,
66
- type: item.type ?? item.description.knowledge_type ?? "",
67
- maturity: item.maturity ?? item.description.maturity ?? "",
68
- summary: item.description.summary
64
+ type: item.description.knowledge_type ?? "",
65
+ maturity: item.description.maturity ?? "",
66
+ summary: item.description.summary,
67
+ relevance_scope: item.description.relevance_scope ?? "broad"
69
68
  }));
70
- return {
71
- version: 1,
69
+ let narrow_count = 0;
70
+ let broad_only_count = 0;
71
+ for (const e of entries) {
72
+ if (e.relevance_scope === "narrow") narrow_count += 1;
73
+ else broad_only_count += 1;
74
+ }
75
+ const output = {
76
+ version: 2,
72
77
  revision_hash: result.revision_hash,
73
78
  target_paths: targetPaths,
74
- narrow,
75
- broad_count: sharedIndex.length
79
+ entries,
80
+ // Legacy field — preserved for v2 consumers that haven't migrated. Value
81
+ // semantics unchanged from rc.18 (total candidate count).
82
+ broad_count: candidates.length,
83
+ narrow_count,
84
+ broad_only_count
76
85
  };
86
+ if (result.auto_healed === true) {
87
+ output.auto_healed = true;
88
+ if (typeof result.previous_revision_hash === "string") {
89
+ output.previous_revision_hash = result.previous_revision_hash;
90
+ }
91
+ }
92
+ return output;
77
93
  }
78
94
  function parsePathsArg(raw) {
79
95
  if (raw === void 0 || raw.length === 0) {
@@ -81,16 +97,6 @@ function parsePathsArg(raw) {
81
97
  }
82
98
  return raw.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
83
99
  }
84
- function dedupeByStableId(items) {
85
- const seen = /* @__PURE__ */ new Set();
86
- const result = [];
87
- for (const item of items) {
88
- if (seen.has(item.stable_id)) continue;
89
- seen.add(item.stable_id);
90
- result.push(item);
91
- }
92
- return result;
93
- }
94
100
  export {
95
101
  plan_context_hint_default as default,
96
102
  planContextHintCommand,
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ scopeExplain
4
+ } from "./chunk-L4Q55UC4.js";
5
+ import "./chunk-LFIKMVY7.js";
6
+ import "./chunk-RYAFBNES.js";
7
+
8
+ // src/commands/scope-explain.ts
9
+ import { defineCommand } from "citty";
10
+ var scope_explain_default = defineCommand({
11
+ meta: {
12
+ name: "scope-explain",
13
+ description: "Explain the resolved read-set and write target for a scope"
14
+ },
15
+ args: {
16
+ scope: {
17
+ type: "positional",
18
+ required: true,
19
+ description: "Scope coordinate (e.g. team, project:x, personal)"
20
+ }
21
+ },
22
+ run({ args }) {
23
+ const result = scopeExplain(process.cwd(), args.scope);
24
+ if (result === null) {
25
+ console.log("no global Fabric config \u2014 run `fabric install --global <url>` first");
26
+ return;
27
+ }
28
+ console.log(JSON.stringify(result, null, 2));
29
+ }
30
+ });
31
+ export {
32
+ scope_explain_default as default
33
+ };
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ projectStatus
4
+ } from "./chunk-T5RPGCCM.js";
5
+ import "./chunk-LFIKMVY7.js";
6
+ import "./chunk-RYAFBNES.js";
7
+
8
+ // src/commands/status.ts
9
+ import { defineCommand } from "citty";
10
+ var status_default = defineCommand({
11
+ meta: { name: "status", description: "Show this project's Fabric store status" },
12
+ run() {
13
+ const status = projectStatus(process.cwd());
14
+ console.log(`uid: ${status.uid ?? "(no global config)"}`);
15
+ console.log(`project_id: ${status.project_id ?? "(not a Fabric project)"}`);
16
+ console.log(`mounted stores: ${status.mounted.length > 0 ? status.mounted.join(", ") : "(none)"}`);
17
+ console.log(`required: ${status.required.length > 0 ? status.required.join(", ") : "(none)"}`);
18
+ console.log(`active write: ${status.active_write_store ?? "(none \u2014 personal scope only)"}`);
19
+ }
20
+ });
21
+ export {
22
+ status_default as default
23
+ };
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ storeAdd,
4
+ storeBind,
5
+ storeExplain,
6
+ storeList,
7
+ storeRemove,
8
+ storeSwitchWrite
9
+ } from "./chunk-HFQVXY6P.js";
10
+ import {
11
+ regenerateBindingsSnapshot
12
+ } from "./chunk-WU6GAPKH.js";
13
+ import "./chunk-L4Q55UC4.js";
14
+ import "./chunk-LFIKMVY7.js";
15
+ import "./chunk-RYAFBNES.js";
16
+
17
+ // src/commands/store.ts
18
+ import { defineCommand } from "citty";
19
+ var listCommand = defineCommand({
20
+ meta: { name: "list", description: "List mounted knowledge stores" },
21
+ run() {
22
+ const stores = storeList();
23
+ if (stores.length === 0) {
24
+ console.log("(no stores mounted)");
25
+ return;
26
+ }
27
+ for (const store of stores) {
28
+ console.log(`${store.alias} ${store.store_uuid} ${store.remote ?? "(local-only)"}`);
29
+ }
30
+ }
31
+ });
32
+ var addCommand = defineCommand({
33
+ meta: { name: "add", description: "Mount a knowledge store into the global registry" },
34
+ args: {
35
+ uuid: { type: "string", required: true, description: "Intrinsic store UUID" },
36
+ alias: { type: "string", required: true, description: "Local alias for this store" },
37
+ remote: { type: "string", description: "Git remote locator (omit for local-only)" }
38
+ },
39
+ run({ args }) {
40
+ const store = args.remote === void 0 ? { store_uuid: args.uuid, alias: args.alias } : { store_uuid: args.uuid, alias: args.alias, remote: args.remote };
41
+ const next = storeAdd(store);
42
+ console.log(`mounted '${args.alias}' (${next.stores.length} store(s) total)`);
43
+ }
44
+ });
45
+ var removeCommand = defineCommand({
46
+ meta: { name: "remove", description: "Detach a store from the registry (does NOT delete it)" },
47
+ args: {
48
+ alias: { type: "positional", required: true, description: "Alias to detach" }
49
+ },
50
+ run({ args }) {
51
+ const { detached } = storeRemove(args.alias);
52
+ console.log(
53
+ detached === null ? `no store aliased '${args.alias}'` : `detached '${args.alias}' \u2014 on-disk store tree left intact (detach \u2260 delete)`
54
+ );
55
+ }
56
+ });
57
+ var explainCommand = defineCommand({
58
+ meta: { name: "explain", description: "Explain how a store alias resolves" },
59
+ args: {
60
+ alias: { type: "positional", required: true, description: "Alias to explain" }
61
+ },
62
+ run({ args }) {
63
+ const explanation = storeExplain(args.alias);
64
+ console.log(
65
+ explanation === null ? `no store aliased '${args.alias}'` : JSON.stringify(explanation, null, 2)
66
+ );
67
+ }
68
+ });
69
+ var bindCommand = defineCommand({
70
+ meta: { name: "bind", description: "Declare a required store on this project's config" },
71
+ args: {
72
+ id: { type: "positional", required: true, description: "Store alias/UUID to require" },
73
+ remote: { type: "string", description: "Suggested remote for clone onboarding" }
74
+ },
75
+ run({ args }) {
76
+ const entry = args.remote === void 0 ? { id: args.id } : { id: args.id, suggested_remote: args.remote };
77
+ const next = storeBind(process.cwd(), entry);
78
+ console.log(`bound required store '${args.id}' (${next.required_stores?.length ?? 0} required)`);
79
+ regenerateBindingsSnapshot(process.cwd(), { now: (/* @__PURE__ */ new Date()).toISOString() });
80
+ }
81
+ });
82
+ var switchWriteCommand = defineCommand({
83
+ meta: { name: "switch-write", description: "Set the active write store for non-personal scopes" },
84
+ args: {
85
+ alias: { type: "positional", required: true, description: "Alias of the store to write to" }
86
+ },
87
+ run({ args }) {
88
+ storeSwitchWrite(process.cwd(), args.alias);
89
+ console.log(`active write store set to '${args.alias}' for this project`);
90
+ }
91
+ });
92
+ var store_default = defineCommand({
93
+ meta: { name: "store", description: "Manage mounted Fabric knowledge stores" },
94
+ subCommands: {
95
+ list: listCommand,
96
+ add: addCommand,
97
+ remove: removeCommand,
98
+ explain: explainCommand,
99
+ bind: bindCommand,
100
+ "switch-write": switchWriteCommand
101
+ }
102
+ });
103
+ export {
104
+ store_default as default
105
+ };