@fenglimg/fabric-cli 2.2.0-rc.4 → 2.2.0-rc.9

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 (73) hide show
  1. package/README.md +8 -5
  2. package/dist/{chunk-5JG4QJLO.js → chunk-27HK6H5Y.js} +10 -5
  3. package/dist/{chunk-F6ITRM7T.js → chunk-2KBCTMID.js} +29 -6
  4. package/dist/chunk-3D7B2UAZ.js +149 -0
  5. package/dist/{chunk-XC5RUHLK.js → chunk-3IOLS5EK.js} +23 -38
  6. package/dist/{chunk-XHHCRDIR.js → chunk-7ZDXBOOU.js} +174 -211
  7. package/dist/{doctor-U5W4CX5I.js → chunk-E7HJUU34.js} +103 -51
  8. package/dist/{chunk-XCBVSGCS.js → chunk-FNHDQTPC.js} +1 -10
  9. package/dist/{chunk-2CY4BMTH.js → chunk-HORSMSZL.js} +9 -5
  10. package/dist/{chunk-BO4XIZWZ.js → chunk-NLNH64A3.js} +5 -18
  11. package/dist/{chunk-H3FE6VIK.js → chunk-PTGQAZEW.js} +13 -3
  12. package/dist/chunk-QFIVFZRH.js +13 -0
  13. package/dist/{chunk-5SSNE5GM.js → chunk-QPAW6IYT.js} +125 -39
  14. package/dist/{chunk-COI5VDFU.js → chunk-WA3DYGSY.js} +1 -2
  15. package/dist/{plan-context-hint-CHVZGOZ5.js → chunk-YM4XATJF.js} +29 -4
  16. package/dist/{config-VJMXCLXW.js → config-A3LTECAY.js} +4 -3
  17. package/dist/context-7NUKXDB6.js +117 -0
  18. package/dist/doctor-MDTZWKBK.js +24 -0
  19. package/dist/index.d.ts +2 -2
  20. package/dist/index.js +131 -21
  21. package/dist/info-7FKBTMVO.js +139 -0
  22. package/dist/install-v2-I6PJ6IFT.js +3279 -0
  23. package/dist/{metrics-RER6NLFC.js → metrics-HMFH4YHK.js} +1 -1
  24. package/dist/{onboard-coverage-JWQWDZW7.js → onboard-coverage-XSG77LL3.js} +48 -27
  25. package/dist/plan-context-hint-G75R4P4J.js +12 -0
  26. package/dist/{scope-explain-BWRWBCCP.js → scope-explain-HLJZ2M33.js} +3 -2
  27. package/dist/{status-7UFLWRX7.js → status-4R3TM4FJ.js} +8 -5
  28. package/dist/{store-ZEZMQVG7.js → store-HOCORVL3.js} +96 -350
  29. package/dist/{sync-EA5HZMXM.js → sync-DT5UJMMR.js} +36 -13
  30. package/dist/{uninstall-F75MPKQC.js → uninstall-IFN2KYBK.js} +71 -140
  31. package/dist/{whoami-3FRWYGML.js → whoami-ITGEFWH4.js} +9 -7
  32. package/package.json +7 -5
  33. package/templates/hooks/cite-policy-evict.cjs +5 -5
  34. package/templates/hooks/configs/README.md +14 -27
  35. package/templates/hooks/configs/claude-code.json +1 -1
  36. package/templates/hooks/configs/codex-hooks.json +3 -3
  37. package/templates/hooks/fabric-hint.cjs +326 -161
  38. package/templates/hooks/knowledge-hint-broad.cjs +431 -271
  39. package/templates/hooks/knowledge-hint-narrow.cjs +64 -77
  40. package/templates/hooks/lib/banner-i18n.cjs +31 -0
  41. package/templates/hooks/lib/bindings-snapshot-reader.cjs +118 -7
  42. package/templates/hooks/lib/cite-line-parser.cjs +12 -20
  43. package/templates/hooks/lib/client-adapter.cjs +66 -7
  44. package/templates/hooks/lib/nudge-policy.cjs +117 -0
  45. package/templates/hooks/lib/state-store.cjs +60 -0
  46. package/templates/hooks/post-tooluse-mutation.cjs +112 -11
  47. package/templates/skills/fabric/SKILL.md +100 -0
  48. package/templates/skills/fabric-archive/SKILL.md +29 -26
  49. package/templates/skills/fabric-archive/ref/dry-run-scope.md +1 -1
  50. package/templates/skills/fabric-archive/ref/i18n-policy.md +2 -3
  51. package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +2 -3
  52. package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +1 -1
  53. package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +1 -1
  54. package/templates/skills/fabric-archive/ref/phase-3-6-related-edges.md +18 -0
  55. package/templates/skills/fabric-archive/ref/phase-3-7-semantic-scope.md +47 -0
  56. package/templates/skills/fabric-audit/SKILL.md +13 -3
  57. package/templates/skills/fabric-connect/SKILL.md +3 -3
  58. package/templates/skills/fabric-import/SKILL.md +7 -7
  59. package/templates/skills/fabric-import/ref/i18n-policy.md +2 -3
  60. package/templates/skills/fabric-import/ref/state-recovery.md +1 -2
  61. package/templates/skills/fabric-review/SKILL.md +5 -5
  62. package/templates/skills/fabric-review/ref/cite-contract.md +1 -1
  63. package/templates/skills/fabric-review/ref/i18n-policy.md +2 -3
  64. package/templates/skills/fabric-review/ref/output-contract.md +1 -1
  65. package/templates/skills/fabric-review/ref/per-mode-flows.md +2 -2
  66. package/templates/skills/fabric-review/ref/worked-examples.md +1 -1
  67. package/templates/skills/fabric-store/SKILL.md +1 -1
  68. package/templates/skills/fabric-sync/SKILL.md +1 -1
  69. package/templates/skills/lib/shared-policy.md +2 -2
  70. package/dist/install-7XJ64WSC.js +0 -2743
  71. package/templates/hooks/configs/cursor-hooks.json +0 -30
  72. package/templates/hooks/lib/cite-contract-reminder.cjs +0 -179
  73. package/templates/hooks/lib/summary-fallback.cjs +0 -210
@@ -1,28 +1,40 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- loadGlobalConfig,
4
3
  loadProjectConfig,
5
- resolveGlobalRoot,
6
- saveGlobalConfig,
7
4
  saveProjectConfig
8
- } from "./chunk-XCBVSGCS.js";
5
+ } from "./chunk-QFIVFZRH.js";
6
+ import {
7
+ loadGlobalConfig,
8
+ resolveGlobalRoot,
9
+ saveGlobalConfig
10
+ } from "./chunk-FNHDQTPC.js";
9
11
 
10
12
  // src/store/store-ops.ts
11
13
  import { execFileSync } from "child_process";
12
14
  import { randomUUID } from "crypto";
13
- import { existsSync, lstatSync, mkdirSync, readdirSync, readlinkSync, rmSync, symlinkSync } from "fs";
15
+ import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, readlinkSync, rmSync, symlinkSync, writeFileSync } from "fs";
14
16
  import { join } from "path";
15
17
  import {
16
18
  addMountedStore,
17
19
  addStoreProject,
18
20
  bindRequiredStore,
21
+ deriveMountLabel,
19
22
  detachMountedStore,
20
23
  explainStore,
21
24
  initStore,
22
- readStoreProjects,
25
+ STORE_GITIGNORE,
26
+ STORE_KNOWLEDGE_TYPE_DIRS,
27
+ STORE_LAYOUT,
28
+ STORE_PENDING_DIR,
23
29
  STORES_ROOT_DIR,
24
30
  storeHasProject,
25
- storeRelativePath
31
+ storeIdentitySchema,
32
+ storeProjectsFileSchema,
33
+ storeMountSubPath,
34
+ storeRelativePath,
35
+ storeRelativePathForMount,
36
+ storeMountNameSchema,
37
+ writeRouteSchema
26
38
  } from "@fenglimg/fabric-shared";
27
39
  var NO_GLOBAL_CONFIG = "no global Fabric config found \u2014 run `fabric install --global <url>` first";
28
40
  function requireConfig(globalRoot) {
@@ -35,6 +47,18 @@ function requireConfig(globalRoot) {
35
47
  function storeList(globalRoot = resolveGlobalRoot()) {
36
48
  return requireConfig(globalRoot).stores;
37
49
  }
50
+ function mountedStoreDir(store, globalRoot) {
51
+ return join(globalRoot, storeRelativePathForMount(store));
52
+ }
53
+ function resolveStoreByAliasOrUuid(aliasOrUuid, globalRoot = resolveGlobalRoot()) {
54
+ const config = loadGlobalConfig(globalRoot);
55
+ if (config === null) {
56
+ return null;
57
+ }
58
+ return config.stores.find(
59
+ (s) => s.alias === aliasOrUuid || s.store_uuid === aliasOrUuid || s.mount_name === aliasOrUuid
60
+ ) ?? null;
61
+ }
38
62
  var STORE_BY_ALIAS_DIR = "by-alias";
39
63
  function syncStoreAliasLinks(globalRoot = resolveGlobalRoot()) {
40
64
  const result = { created: [], removed: [], errors: [] };
@@ -43,7 +67,7 @@ function syncStoreAliasLinks(globalRoot = resolveGlobalRoot()) {
43
67
  return result;
44
68
  }
45
69
  const byAliasDir = join(globalRoot, STORES_ROOT_DIR, STORE_BY_ALIAS_DIR);
46
- const desired = new Map(config.stores.map((s) => [s.alias, s.store_uuid]));
70
+ const desired = new Map(config.stores.map((s) => [s.alias, storeMountSubPath(s)]));
47
71
  try {
48
72
  mkdirSync(byAliasDir, { recursive: true });
49
73
  } catch {
@@ -66,9 +90,9 @@ function syncStoreAliasLinks(globalRoot = resolveGlobalRoot()) {
66
90
  result.errors.push(name);
67
91
  }
68
92
  }
69
- for (const [alias, uuid] of desired) {
93
+ for (const [alias, mountName] of desired) {
70
94
  const link = join(byAliasDir, alias);
71
- const target = join("..", uuid);
95
+ const target = join("..", mountName);
72
96
  try {
73
97
  let current = null;
74
98
  try {
@@ -102,7 +126,7 @@ function detectAliasLinkDrift(globalRoot = resolveGlobalRoot()) {
102
126
  const drifted = [];
103
127
  for (const store of config.stores) {
104
128
  const link = join(byAliasDir, store.alias);
105
- const target = join("..", store.store_uuid);
129
+ const target = join("..", storeMountSubPath(store));
106
130
  try {
107
131
  if (!lstatSync(link).isSymbolicLink() || readlinkSync(link) !== target) {
108
132
  drifted.push(store.alias);
@@ -119,25 +143,47 @@ function storeAdd(store, globalRoot = resolveGlobalRoot()) {
119
143
  syncStoreAliasLinks(globalRoot);
120
144
  return next;
121
145
  }
122
- function storeCreate(alias, now, options = {}) {
146
+ async function storeCreate(alias, now, options = {}) {
123
147
  const globalRoot = options.globalRoot ?? resolveGlobalRoot();
124
148
  const config = requireConfig(globalRoot);
125
149
  const uuid = options.uuid ?? randomUUID();
126
- const storeDir = join(globalRoot, storeRelativePath(uuid));
127
- initStore(
128
- storeDir,
129
- { store_uuid: uuid, created_at: now, canonical_alias: alias },
130
- { git: options.git }
131
- );
150
+ const mount_name = options.mountName !== void 0 ? storeMountNameSchema.parse(options.mountName) : deriveMountLabel({ remote: options.remote, alias, store_uuid: uuid });
151
+ const mountedBase = { store_uuid: uuid, alias, mount_name };
152
+ const storeDir = mountedStoreDir(mountedBase, globalRoot);
153
+ const identity = { store_uuid: uuid, created_at: now, canonical_alias: alias };
154
+ if (options.git === false) {
155
+ initStoreSync(storeDir, identity);
156
+ } else {
157
+ await initStore(storeDir, identity, { git: options.git });
158
+ }
132
159
  if (options.remote !== void 0 && options.git !== false) {
133
160
  gitRemoteAdd(storeDir, options.remote);
134
161
  }
135
- const mounted = options.remote === void 0 ? { store_uuid: uuid, alias } : { store_uuid: uuid, alias, remote: options.remote };
162
+ const mounted = options.remote === void 0 ? mountedBase : { ...mountedBase, remote: options.remote };
136
163
  const next = addMountedStore(config, mounted);
137
164
  saveGlobalConfig(next, globalRoot);
138
165
  syncStoreAliasLinks(globalRoot);
139
166
  return { config: next, store_uuid: uuid, storeDir };
140
167
  }
168
+ function initStoreSync(absDir, identity) {
169
+ const parsed = storeIdentitySchema.parse(identity);
170
+ const identityFile = join(absDir, STORE_LAYOUT.identityFile);
171
+ if (existsSync(identityFile)) {
172
+ throw new Error(`store already initialized at ${absDir} (store.json exists)`);
173
+ }
174
+ for (const type of STORE_KNOWLEDGE_TYPE_DIRS) {
175
+ const typeDir = join(absDir, STORE_LAYOUT.knowledgeDir, type);
176
+ mkdirSync(typeDir, { recursive: true });
177
+ writeFileSync(join(typeDir, ".gitkeep"), "", "utf8");
178
+ }
179
+ mkdirSync(join(absDir, STORE_LAYOUT.knowledgeDir, STORE_PENDING_DIR), { recursive: true });
180
+ mkdirSync(join(absDir, STORE_LAYOUT.bindingsDir), { recursive: true });
181
+ mkdirSync(join(absDir, STORE_LAYOUT.stateDir), { recursive: true });
182
+ writeFileSync(identityFile, `${JSON.stringify(parsed, null, 2)}
183
+ `, "utf8");
184
+ writeFileSync(join(absDir, ".gitignore"), STORE_GITIGNORE, "utf8");
185
+ return parsed;
186
+ }
141
187
  function gitRemoteAdd(storeDir, remote) {
142
188
  try {
143
189
  execFileSync("git", ["remote", "add", "origin", remote], {
@@ -154,8 +200,8 @@ function gitRemoteAdd(storeDir, remote) {
154
200
  }
155
201
  }
156
202
  }
157
- function storeGitRemote(uuid, globalRoot = resolveGlobalRoot()) {
158
- const storeDir = join(globalRoot, storeRelativePath(uuid));
203
+ function storeGitRemote(aliasOrUuid, globalRoot = resolveGlobalRoot()) {
204
+ const storeDir = resolveStoreDir(aliasOrUuid, globalRoot) ?? join(globalRoot, storeRelativePath(aliasOrUuid));
159
205
  if (!existsSync(storeDir)) {
160
206
  return void 0;
161
207
  }
@@ -171,8 +217,20 @@ function storeGitRemote(uuid, globalRoot = resolveGlobalRoot()) {
171
217
  return void 0;
172
218
  }
173
219
  }
174
- function assertStoreMountable(uuid, globalRoot = resolveGlobalRoot()) {
175
- const storeDir = join(globalRoot, storeRelativePath(uuid));
220
+ function assertStoreMountable(uuid, globalRoot = resolveGlobalRoot(), mountName) {
221
+ const registered = resolveStoreByAliasOrUuid(uuid, globalRoot);
222
+ const candidates = mountName === void 0 && registered === null ? [join(globalRoot, storeRelativePath(uuid))] : [
223
+ join(
224
+ globalRoot,
225
+ storeRelativePathForMount({
226
+ store_uuid: uuid,
227
+ mount_name: mountName ?? registered?.mount_name,
228
+ personal: registered?.personal
229
+ })
230
+ ),
231
+ join(globalRoot, storeRelativePath(uuid))
232
+ ];
233
+ const storeDir = candidates.find((dir) => existsSync(join(dir, "store.json"))) ?? candidates[0];
176
234
  if (!existsSync(join(storeDir, "store.json"))) {
177
235
  throw new Error(
178
236
  `cannot mount store ${uuid}: no store tree at ${storeDir} \u2014 clone it first (\`fabric install --global --url <remote>\`) or create it locally, then re-run \`fabric store add\`. Refusing to register a phantom store.`
@@ -197,36 +255,40 @@ function requireProjectConfig(projectRoot) {
197
255
  return config;
198
256
  }
199
257
  function resolveStoreDir(aliasOrUuid, globalRoot = resolveGlobalRoot()) {
200
- const config = loadGlobalConfig(globalRoot);
201
- if (config === null) {
258
+ const store = resolveStoreByAliasOrUuid(aliasOrUuid, globalRoot);
259
+ if (store === null) {
202
260
  return null;
203
261
  }
204
- const store = config.stores.find(
205
- (s) => s.alias === aliasOrUuid || s.store_uuid === aliasOrUuid
206
- );
207
- if (store === void 0) {
208
- return null;
209
- }
210
- return join(globalRoot, storeRelativePath(store.store_uuid));
262
+ return mountedStoreDir(store, globalRoot);
211
263
  }
212
264
  function storeProjectList(aliasOrUuid, globalRoot = resolveGlobalRoot()) {
213
265
  const storeDir = resolveStoreDir(aliasOrUuid, globalRoot);
214
266
  if (storeDir === null) {
215
267
  throw new Error(`no mounted store '${aliasOrUuid}' \u2014 run \`fabric store list\` to see mounts`);
216
268
  }
217
- return readStoreProjects(storeDir);
269
+ return readStoreProjectsSync(storeDir);
270
+ }
271
+ function readStoreProjectsSync(storeDir) {
272
+ try {
273
+ const parsed = storeProjectsFileSchema.safeParse(
274
+ JSON.parse(readFileSync(join(storeDir, STORE_LAYOUT.projectsFile), "utf8"))
275
+ );
276
+ return parsed.success ? parsed.data.projects : [];
277
+ } catch {
278
+ return [];
279
+ }
218
280
  }
219
- function storeProjectCreate(aliasOrUuid, id, now, options = {}) {
281
+ async function storeProjectCreate(aliasOrUuid, id, now, options = {}) {
220
282
  const globalRoot = options.globalRoot ?? resolveGlobalRoot();
221
283
  const storeDir = resolveStoreDir(aliasOrUuid, globalRoot);
222
284
  if (storeDir === null) {
223
285
  throw new Error(`no mounted store '${aliasOrUuid}' \u2014 run \`fabric store list\` to see mounts`);
224
286
  }
225
287
  const project = options.name === void 0 ? { id, created_at: now } : { id, name: options.name, created_at: now };
226
- addStoreProject(storeDir, project);
288
+ await addStoreProject(storeDir, project);
227
289
  return project;
228
290
  }
229
- function storeBind(projectRoot, entry, options = {}) {
291
+ async function storeBind(projectRoot, entry, options = {}) {
230
292
  const config = requireProjectConfig(projectRoot);
231
293
  let activeProject = config.active_project;
232
294
  if (options.project !== void 0) {
@@ -237,7 +299,7 @@ function storeBind(projectRoot, entry, options = {}) {
237
299
  `cannot bind project '${options.project}': store '${entry.id}' is not mounted \u2014 mount it first (\`fabric store add\` / \`fabric install --global --url <remote>\`)`
238
300
  );
239
301
  }
240
- if (!storeHasProject(storeDir, options.project)) {
302
+ if (!await storeHasProject(storeDir, options.project)) {
241
303
  throw new Error(
242
304
  `cannot bind to project '${options.project}': not registered in store '${entry.id}' \u2014 create it first with \`fabric store project create ${entry.id} ${options.project}\``
243
305
  );
@@ -252,9 +314,32 @@ function storeBind(projectRoot, entry, options = {}) {
252
314
  saveProjectConfig(next, projectRoot);
253
315
  return next;
254
316
  }
255
- function storeSwitchWrite(projectRoot, alias) {
317
+ function storeSwitchWrite(projectRoot, alias, options = {}) {
256
318
  const config = requireProjectConfig(projectRoot);
257
- const next = { ...config, active_write_store: alias };
319
+ const store = resolveStoreByAliasOrUuid(alias, options.globalRoot ?? resolveGlobalRoot());
320
+ if (store === null || store.personal === true || store.writable === false) {
321
+ throw new Error(`cannot set default write store '${alias}': mount a writable shared store first`);
322
+ }
323
+ const next = {
324
+ ...config,
325
+ active_write_store: alias,
326
+ default_write_store: alias
327
+ };
328
+ saveProjectConfig(next, projectRoot);
329
+ return next;
330
+ }
331
+ function storeSetWriteRoute(projectRoot, scope, alias, options = {}) {
332
+ const config = requireProjectConfig(projectRoot);
333
+ const route = writeRouteSchema.parse({ scope, store: alias });
334
+ const store = resolveStoreByAliasOrUuid(alias, options.globalRoot ?? resolveGlobalRoot());
335
+ if (store === null || store.personal === true || store.writable === false) {
336
+ throw new Error(`cannot route scope '${scope}' to '${alias}': mount a writable shared store first`);
337
+ }
338
+ const routes = [
339
+ ...(config.write_routes ?? []).filter((existing) => existing.scope !== route.scope),
340
+ route
341
+ ];
342
+ const next = { ...config, write_routes: routes };
258
343
  saveProjectConfig(next, projectRoot);
259
344
  return next;
260
345
  }
@@ -296,6 +381,7 @@ export {
296
381
  storeProjectCreate,
297
382
  storeBind,
298
383
  storeSwitchWrite,
384
+ storeSetWriteRoute,
299
385
  missingRequiredStores,
300
386
  unboundAvailableStores
301
387
  };
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/dev-mode.ts
4
- import { existsSync, readFileSync } from "fs";
5
- import { isAbsolute, join, resolve } from "path";
4
+ import { isAbsolute, resolve } from "path";
6
5
  function resolveDevMode(cliTarget, workspaceRoot = process.cwd()) {
7
6
  const envTarget = normalizeTarget(process.env.EXTERNAL_FIXTURE_PATH, workspaceRoot);
8
7
  const directTarget = normalizeTarget(cliTarget, workspaceRoot);
@@ -1,11 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  resolveDevMode
4
- } from "./chunk-COI5VDFU.js";
4
+ } from "./chunk-WA3DYGSY.js";
5
5
 
6
6
  // src/commands/plan-context-hint.ts
7
7
  import { defineCommand } from "citty";
8
- import { planContext } from "@fenglimg/fabric-server";
8
+ import {
9
+ buildAlwaysActiveBodies,
10
+ buildKnowledgeCensus,
11
+ planContext
12
+ } from "@fenglimg/fabric-server";
9
13
  var ALL_PATHS_SENTINEL = "**";
10
14
  var planContextHintCommand = defineCommand({
11
15
  meta: {
@@ -74,6 +78,9 @@ async function runPlanContextHint(opts) {
74
78
  maturity: item.description.maturity ?? "",
75
79
  summary: item.description.summary,
76
80
  relevance_scope: item.description.relevance_scope ?? "broad",
81
+ // W2-2 (KT-DEC-0027): forward the must_read_if trigger hook for the
82
+ // SessionStart REFERENCE rendering. Omitted when absent/empty.
83
+ ...typeof item.description.must_read_if === "string" && item.description.must_read_if.length > 0 ? { must_read_if: item.description.must_read_if } : {},
77
84
  // Only set when this entry was pulled in via a graph edge — its presence
78
85
  // is the honest signal, never synthesized for ordinarily-ranked entries.
79
86
  ...typeof relatedTo === "string" ? { related_to: relatedTo } : {}
@@ -85,6 +92,15 @@ async function runPlanContextHint(opts) {
85
92
  if (e.relevance_scope === "narrow") narrow_count += 1;
86
93
  else broad_only_count += 1;
87
94
  }
95
+ const alwaysBodies = await buildAlwaysActiveBodies(resolution.target).catch(() => []);
96
+ const census = await buildKnowledgeCensus(resolution.target).catch(
97
+ () => ({
98
+ by_type: {},
99
+ by_layer: { team: 0, personal: 0, project: 0 },
100
+ dropped_other_project: 0,
101
+ total: 0
102
+ })
103
+ );
88
104
  const output = {
89
105
  version: 2,
90
106
  revision_hash: result.revision_hash,
@@ -94,7 +110,15 @@ async function runPlanContextHint(opts) {
94
110
  // semantics unchanged from rc.18 (total candidate count).
95
111
  broad_count: candidates.length,
96
112
  narrow_count,
97
- broad_only_count
113
+ broad_only_count,
114
+ always_bodies: alwaysBodies.map((b) => ({
115
+ id: b.stable_id,
116
+ type: b.type,
117
+ layer: b.layer,
118
+ summary: b.summary,
119
+ body: b.body
120
+ })),
121
+ census
98
122
  };
99
123
  if (result.auto_healed === true) {
100
124
  output.auto_healed = true;
@@ -110,8 +134,9 @@ function parsePathsArg(raw) {
110
134
  }
111
135
  return raw.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
112
136
  }
137
+
113
138
  export {
114
- plan_context_hint_default as default,
115
139
  planContextHintCommand,
140
+ plan_context_hint_default,
116
141
  runPlanContextHint
117
142
  };
@@ -3,9 +3,10 @@ import {
3
3
  configCmd,
4
4
  config_default,
5
5
  installMcpClients
6
- } from "./chunk-F6ITRM7T.js";
7
- import "./chunk-XC5RUHLK.js";
8
- import "./chunk-2CY4BMTH.js";
6
+ } from "./chunk-2KBCTMID.js";
7
+ import "./chunk-3IOLS5EK.js";
8
+ import "./chunk-FNHDQTPC.js";
9
+ import "./chunk-HORSMSZL.js";
9
10
  export {
10
11
  configCmd,
11
12
  config_default as default,
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runPlanContextHint
4
+ } from "./chunk-YM4XATJF.js";
5
+ import "./chunk-WA3DYGSY.js";
6
+
7
+ // src/commands/context.ts
8
+ import { existsSync } from "fs";
9
+ import { createRequire } from "module";
10
+ import { dirname, join, parse, resolve } from "path";
11
+ import { fileURLToPath } from "url";
12
+ import { defineCommand } from "citty";
13
+ function findTemplatePath(relativePath) {
14
+ const startDir = dirname(fileURLToPath(import.meta.url));
15
+ let current = resolve(startDir);
16
+ for (; ; ) {
17
+ const candidate = join(current, "templates", relativePath);
18
+ if (existsSync(candidate)) return candidate;
19
+ const parent = dirname(current);
20
+ if (parent === current || parse(current).root === current) {
21
+ throw new Error(`Template not found: templates/${relativePath} (searched up from ${startDir})`);
22
+ }
23
+ current = parent;
24
+ }
25
+ }
26
+ function loadHookRenderer() {
27
+ const require2 = createRequire(import.meta.url);
28
+ return require2(findTemplatePath("hooks/knowledge-hint-broad.cjs"));
29
+ }
30
+ function renderExplain(sinks) {
31
+ const payload = sinks.resolvedPayload;
32
+ const lines = ["", "\u2014 explain (provenance; not injected) \u2014"];
33
+ const bodies = Array.isArray(payload?.always_bodies) ? payload.always_bodies : [];
34
+ if (bodies.length > 0) {
35
+ lines.push("always-active (body injected):");
36
+ for (const b of bodies) {
37
+ lines.push(` [${b.type}] ${b.id} (${b.layer}) \xB7 ${b.summary}`);
38
+ }
39
+ }
40
+ const entries = Array.isArray(payload?.entries) ? payload.entries : [];
41
+ if (entries.length > 0) {
42
+ lines.push("reference / candidates:");
43
+ for (const e of entries) {
44
+ const provenance = typeof e.related_to === "string" ? ` \u2190related-to:${e.related_to}` : "";
45
+ lines.push(
46
+ ` [${e.type}] ${e.id} (${e.maturity || "?"}, ${e.relevance_scope})${provenance} \xB7 ${e.summary}`
47
+ );
48
+ if (typeof e.must_read_if === "string" && e.must_read_if.length > 0) {
49
+ lines.push(` must_read_if: ${e.must_read_if}`);
50
+ }
51
+ }
52
+ }
53
+ const census = payload?.census;
54
+ if (census) {
55
+ const layer = census.by_layer ?? { team: 0, personal: 0, project: 0 };
56
+ lines.push(
57
+ `census: total ${census.total} \xB7 [team]${layer.team ?? 0} [project]${layer.project ?? 0} [personal]${layer.personal ?? 0}`
58
+ );
59
+ }
60
+ return lines.join("\n");
61
+ }
62
+ async function runContext(opts) {
63
+ const cwd = opts.target ? resolve(opts.target) : process.cwd();
64
+ const payload = opts.payload !== void 0 ? opts.payload : await runPlanContextHint({ all: true, target: opts.target });
65
+ const renderer = loadHookRenderer();
66
+ const sinks = renderer.buildSessionStartSinks(cwd, payload, {});
67
+ let base;
68
+ if (opts.render === "ai") {
69
+ base = sinks.ai ?? "";
70
+ } else if (opts.render === "human") {
71
+ base = sinks.human ?? "";
72
+ } else {
73
+ base = [sinks.human, sinks.ai].filter((s) => typeof s === "string" && s.length > 0).join("\n\n");
74
+ }
75
+ if (base.length === 0 && !sinks.hasRenderedContent) return "";
76
+ return opts.explain === true ? base + "\n" + renderExplain(sinks) : base;
77
+ }
78
+ var contextCommand = defineCommand({
79
+ meta: {
80
+ name: "context",
81
+ description: "Show what Fabric injects at SessionStart (the knowledge spine). --explain for per-entry provenance."
82
+ },
83
+ args: {
84
+ render: {
85
+ type: "string",
86
+ description: "Which sink to show: 'human' (systemMessage) or 'ai' (additionalContext). Default: both."
87
+ },
88
+ explain: {
89
+ type: "boolean",
90
+ description: "Append a per-entry provenance section (id \xB7 type \xB7 maturity \xB7 scope \xB7 why-surfaced).",
91
+ default: false
92
+ },
93
+ target: {
94
+ type: "string",
95
+ description: "Override the project root (defaults to cwd / dev-mode resolution)."
96
+ }
97
+ },
98
+ async run({ args }) {
99
+ try {
100
+ const render = args.render === "human" || args.render === "ai" ? args.render : void 0;
101
+ const out = await runContext({ render, explain: args.explain === true, target: args.target });
102
+ if (out.length > 0) process.stdout.write(`${out}
103
+ `);
104
+ } catch (error) {
105
+ const message = error instanceof Error ? error.message : String(error);
106
+ process.stderr.write(`context failed: ${message}
107
+ `);
108
+ process.exitCode = 1;
109
+ }
110
+ }
111
+ });
112
+ var context_default = contextCommand;
113
+ export {
114
+ contextCommand,
115
+ context_default as default,
116
+ runContext
117
+ };
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ doctorCommand,
4
+ doctor_default,
5
+ parseSinceDuration,
6
+ renderDoctorFilteredHelp,
7
+ renderTldrHeader
8
+ } from "./chunk-E7HJUU34.js";
9
+ import "./chunk-3D7B2UAZ.js";
10
+ import "./chunk-WA3DYGSY.js";
11
+ import "./chunk-NLNH64A3.js";
12
+ import "./chunk-PTGQAZEW.js";
13
+ import "./chunk-EOT63RDH.js";
14
+ import "./chunk-QPAW6IYT.js";
15
+ import "./chunk-QFIVFZRH.js";
16
+ import "./chunk-FNHDQTPC.js";
17
+ import "./chunk-HORSMSZL.js";
18
+ export {
19
+ doctor_default as default,
20
+ doctorCommand,
21
+ parseSinceDuration,
22
+ renderDoctorFilteredHelp,
23
+ renderTldrHeader
24
+ };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import * as citty from 'citty';
1
+ import { CommandDef, ArgsDef } from 'citty';
2
2
 
3
- declare const main: citty.CommandDef<citty.ArgsDef>;
3
+ declare const main: CommandDef<ArgsDef>;
4
4
  declare function run(): Promise<void>;
5
5
 
6
6
  export { main, run };