@fenglimg/fabric-shared 2.3.0-rc.12 → 2.3.0-rc.16

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.
@@ -709,7 +709,18 @@ var _fabReviewModifyChangesSchema = z3.object({
709
709
  // impact are flow-arrays.
710
710
  must_read_if: z3.string().optional(),
711
711
  intent_clues: z3.array(z3.string()).optional(),
712
- impact: z3.array(z3.string()).optional()
712
+ impact: z3.array(z3.string()).optional(),
713
+ // ISS-20260711-180: propose writes these; without them here zod .strip()
714
+ // silently drops modify patches (KT-PIT-0005 recurrence).
715
+ tech_stack: z3.array(z3.string()).optional(),
716
+ evidence_paths: z3.array(z3.string()).optional(),
717
+ onboard_slot: z3.enum([
718
+ "tech-stack-decision",
719
+ "architecture-pattern",
720
+ "code-style-tone",
721
+ "build-system-idiom",
722
+ "domain-vocabulary"
723
+ ]).optional()
713
724
  });
714
725
  var FabReviewInputSchema = z3.discriminatedUnion("action", [
715
726
  z3.object({
@@ -863,6 +874,8 @@ var _fabReviewListItemSchema = z3.object({
863
874
  tags: z3.array(z3.string()).optional(),
864
875
  title: z3.string().optional(),
865
876
  summary: z3.string().optional(),
877
+ // ISS-20260712-011: triage UI needs proposed_reason without a second Read.
878
+ proposed_reason: z3.string().optional(),
866
879
  // Store-only cutover: origin reflects the resolved store audience where the
867
880
  // pending file lives; layer reflects the declared classification that will
868
881
  // drive approval semantics.
@@ -902,10 +915,16 @@ var _fabReviewSearchItemSchema = z3.object({
902
915
  // hits in the same array.
903
916
  stable_id: z3.string().optional()
904
917
  });
918
+ var _fabReviewFailedItemSchema = z3.object({
919
+ pending_path: z3.string(),
920
+ reason: z3.string()
921
+ });
905
922
  var FabReviewOutputSchema = z3.discriminatedUnion("action", [
906
923
  z3.object({
907
924
  action: z3.literal("approve"),
908
925
  approved: z3.array(z3.object({ pending_path: z3.string(), stable_id: z3.string() })),
926
+ // ISS-20260712-012: partial-failure surface so empty approved[] is not false-success.
927
+ failed: z3.array(_fabReviewFailedItemSchema).optional(),
909
928
  warnings: z3.array(structuredWarningSchema).optional()
910
929
  }),
911
930
  z3.object({
@@ -953,6 +972,8 @@ var FabReviewOutputSchema = z3.discriminatedUnion("action", [
953
972
  superseded_by: z3.string().optional()
954
973
  })
955
974
  ),
975
+ // ISS-20260712-012: partial-failure surface for skipped/unresolvable paths.
976
+ failed: z3.array(_fabReviewFailedItemSchema).optional(),
956
977
  warnings: z3.array(structuredWarningSchema).optional()
957
978
  })
958
979
  ]);
@@ -984,6 +1005,9 @@ var FabReviewOutputShape = {
984
1005
  approved: z3.array(z3.object({ pending_path: z3.string(), stable_id: z3.string() })).optional().describe(
985
1006
  "Allocated stable ids paired with their original pending paths. Present when action=approve."
986
1007
  ),
1008
+ failed: z3.array(z3.object({ pending_path: z3.string(), reason: z3.string() })).optional().describe(
1009
+ "Paths that did not apply (approve/retire). Present when any path was skipped or failed."
1010
+ ),
987
1011
  rejected: z3.array(z3.string()).optional().describe(
988
1012
  "Pending paths that were rejected (files retained on disk; doctor owns vacuum). Present when action=reject."
989
1013
  ),
@@ -1,3 +1,8 @@
1
+ import {
2
+ atomicWriteJson,
3
+ withFileLock
4
+ } from "./chunk-C7WZPYZE.js";
5
+
1
6
  // src/i18n/normalize-locale.ts
2
7
  function normalizeLocale(raw) {
3
8
  if (typeof raw !== "string") {
@@ -189,7 +194,8 @@ var globalConfigSchema = z.object({
189
194
  }).passthrough();
190
195
 
191
196
  // src/store/global-config-io.ts
192
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
197
+ import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "fs";
198
+ import { mkdir } from "fs/promises";
193
199
  import { homedir } from "os";
194
200
  import { join } from "path";
195
201
  function isTestRuntime() {
@@ -210,6 +216,9 @@ function resolveGlobalRoot() {
210
216
  function globalConfigPath(globalRoot = resolveGlobalRoot()) {
211
217
  return join(globalRoot, "fabric-global.json");
212
218
  }
219
+ function globalConfigLockPath(globalRoot) {
220
+ return `${globalConfigPath(globalRoot)}.lock`;
221
+ }
213
222
  function loadGlobalConfig(globalRoot = resolveGlobalRoot()) {
214
223
  const path = globalConfigPath(globalRoot);
215
224
  if (!existsSync(path)) {
@@ -217,11 +226,30 @@ function loadGlobalConfig(globalRoot = resolveGlobalRoot()) {
217
226
  }
218
227
  return globalConfigSchema.parse(JSON.parse(readFileSync(path, "utf8")));
219
228
  }
229
+ async function saveGlobalConfigAsync(config, globalRoot = resolveGlobalRoot()) {
230
+ const validated = globalConfigSchema.parse(config);
231
+ await mkdir(globalRoot, { recursive: true });
232
+ const path = globalConfigPath(globalRoot);
233
+ await withFileLock(globalConfigLockPath(globalRoot), async () => {
234
+ await atomicWriteJson(path, validated, { indent: 2 });
235
+ });
236
+ }
220
237
  function saveGlobalConfig(config, globalRoot = resolveGlobalRoot()) {
221
238
  const validated = globalConfigSchema.parse(config);
222
239
  mkdirSync(globalRoot, { recursive: true });
223
- writeFileSync(globalConfigPath(globalRoot), `${JSON.stringify(validated, null, 2)}
240
+ const path = globalConfigPath(globalRoot);
241
+ const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
242
+ try {
243
+ writeFileSync(tmpPath, `${JSON.stringify(validated, null, 2)}
224
244
  `, "utf8");
245
+ renameSync(tmpPath, path);
246
+ } catch (error) {
247
+ try {
248
+ unlinkSync(tmpPath);
249
+ } catch {
250
+ }
251
+ throw error;
252
+ }
225
253
  }
226
254
 
227
255
  // src/i18n/resolve-global-locale.ts
@@ -269,6 +297,7 @@ export {
269
297
  resolveGlobalRoot,
270
298
  globalConfigPath,
271
299
  loadGlobalConfig,
300
+ saveGlobalConfigAsync,
272
301
  saveGlobalConfig,
273
302
  resolveGlobalLocale
274
303
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveGlobalLocale
3
- } from "./chunk-ASS2KBB7.js";
3
+ } from "./chunk-OX6DWUK4.js";
4
4
 
5
5
  // src/templates/bootstrap-canonical.ts
6
6
  var BOOTSTRAP_MARKER_BEGIN = "<!-- fabric:bootstrap:begin -->";
@@ -36,8 +36,9 @@ var BOOTSTRAP_CANONICAL_ZH = `# Fabric Bootstrap
36
36
  \u5B8C\u6574 maintainer \u7248\u89C1 \`docs/USER-QUICKSTART.md\`\u3002
37
37
 
38
38
  ## \u884C\u4E3A\u89C4\u5219
39
- - **\u4FEE\u6539\u4EFB\u4F55\u6587\u4EF6\u524D**:\u5148 \`fab_recall(paths=[<\u88AB\u6539\u6587\u4EF6>])\` \u2014\u2014 \u4E00\u6B21\u8C03\u7528\u62FF\u56DE\u76F8\u5173 KB \u7684\u63CF\u8FF0 + \u539F\u751F\u8BFB\u53D6\u8DEF\u5F84(\`entries[].read_path\`)\u3002\`fab_recall\` \u4E0D\u518D\u6295\u9012\u6B63\u6587;\u9700\u8981\u67D0\u6761\u6B63\u6587\u65F6\u76F4\u63A5\u5BF9\u5176 \`entries[].read_path\` \u505A\u539F\u751F Read(\`Read <store>/knowledge/<type>/<id>--*.md\`),\u8FD9\u4F1A\u88AB PostToolUse hook \u8BB0\u4E3A \`knowledge_body_read\`\u3002lean \u9ED8\u8BA4:\u63CF\u8FF0+\u7D22\u5F15\u5DF2\u591F\u53D1\u73B0\u6761\u76EE,\u6B63\u6587\u6309\u9700\u8BFB\u4E00\u6B21,\u4E0D\u6BCF\u8F6E\u91CD\u704C(KT-GLD-0005)\u3002
40
- - **\`.fabric/agents.meta.json\` \u4E25\u7981\u624B\u52A8\u7F16\u8F91**;engine \u4F1A\u81EA\u52A8\u540C\u6B65\u6D3E\u751F\u72B6\u6001,\u663E\u5F0F reconcile \u8DD1 \`fabric doctor --fix\`\u3002
39
+ - **Pre-action gating (\u4FEE\u6539\u4EFB\u4F55\u6587\u4EF6\u524D MUST)**: \u2460 \u5148 \`fab_recall(paths=[<\u88AB\u6539\u6587\u4EF6>], session_id=<client-session-id>)\`\uFF1B\u2461 \u6309\u9700\u539F\u751F Read \u547D\u4E2D\u7684 \`entries[].read_path\` \u6B63\u6587(PostToolUse \u8BB0\u4E3A \`knowledge_body_read\`)\uFF1B\u2462 \u8DF3\u8FC7\u4E0D\u9002\u7528\u547D\u4E2D\u65F6\u8BF4 \`dismissed: <id> (<reason>)\`\u3002\`fab_recall\` \u4E0D\u518D\u6295\u9012\u6B63\u6587;lean \u9ED8\u8BA4\u63CF\u8FF0+\u7D22\u5F15\u5DF2\u591F\u53D1\u73B0\u6761\u76EE(KT-GLD-0005)\u3002PreToolUse \u4EC5\u8F6F nudge,\u5B88 KT-DEC-0007(\u975E permanent gate / \u65E0 \`decision:block\`)\u3002
40
+ - **Archive-as-truth**: \u8FDB canonical **\u552F\u4E00\u8DEF\u5F84** \u662F \`fab_propose\` \u2192 pending \u2192 \`fabric-review\`/\`fab_review\` \u5BA1\u6279\uFF1B\u7981\u6B62\u628A chat/log \u81EA\u52A8 promote \u8FDB canonical\u3002
41
+ - **\`.fabric/agents.meta.json\` \u4E25\u7981\u624B\u52A8\u7F16\u8F91**\uFF08co-location index \u5DF2\u9000\u5F79;\u77E5\u8BC6\u5728 mounted stores\uFF09\u3002\u5F02\u5E38\u65F6\u8DD1 \`fabric doctor\` / \`fabric doctor --fix\` \u81EA\u6108\u53EF\u4FEE\u590D\u9879\u3002
41
42
 
42
43
  ## \u77E5\u8BC6\u5E93(KB)
43
44
  - **Discovery**:SessionStart hook \u5217 broad-scoped \u6761\u76EE(\u6761\u76EE\u6309 \`semantic_scope\` \u5206\u4E09\u5C42:\`team\` \u56E2\u961F\u901A\u7528 / \`project:<id>\` \u672C\u9879\u76EE\u4E13\u5C5E(\u4EC5\u5728\u7ED1\u5B9A\u8BE5\u9879\u76EE\u7684\u4ED3\u5E93\u6D6E\u73B0)/ \`personal\` \u4E2A\u4EBA \`KP-*\`,\u4E09\u8005\u5F15\u7528\u65B9\u5F0F\u76F8\u540C);edit \u6587\u4EF6\u65F6 PreToolUse hook \u53EF\u80FD\u89E6\u53D1 narrow hint\u3002
@@ -46,7 +47,7 @@ var BOOTSTRAP_CANONICAL_ZH = `# Fabric Bootstrap
46
47
  - **session_id**: \u8C03\u7528 \`fab_recall\` \u65F6, \u52A1\u5FC5\u628A\u5F53\u524D client session id \u4F5C\u4E3A \`session_id\` \u53C2\u6570\u4F20\u5165(Claude Code \u7684 session id \u5728 stdin payload \u4E2D, Codex \u7684\u5BF9\u5E94 identifier \u540C\u7406)\u3002\u8FD9\u80FD\u8BA9 \`fabric doctor --archive-history\` \u4E0E \`fabric-hint.cjs\` Stop hook \u51C6\u786E\u8BC6\u522B\u8DE8\u4F1A\u8BDD debt \u72B6\u6001\u3002
47
48
  - **Skills (4)**:\u5199\u6D41\u7A0B \`fabric-archive\`(\u542B source mode \u51B7\u542F\u52A8\u4ECE git/docs \u56DE\u704C)/ \`fabric-review\`(\u542B retire \u8BED\u4E49\u6DD8\u6C70 + relate \u5173\u8054\u5EFA\u8FB9 \u5B50\u6D41\u7A0B);store \u8FD0\u7EF4 \`fabric-store\` / \`fabric-sync\`\u3002
48
49
  - **Language**:\u6E32\u67D3\u6309 \`~/.fabric/fabric-global.json\` \u7684 \`language\` \u5B57\u6BB5(machine-wide tone)\u3002
49
- - **Archive cadence nudge** (rc.36): \u6BCF\u5B8C\u6210\u4E00\u6279 Edit(\u9ED8\u8BA4 ~20 \u6B21, \u4E0E Stop hook \u9608\u503C config \`archive_edit_threshold\` \u4E00\u81F4)/ \u663E\u8457 decision \u540E,\u5728\u5408\u9002\u56DE\u5408\u4E3B\u52A8 propose \u8C03 \`fabric-archive\` skill \u2014 archive \u6CA1\u5EFA\u7ACB\u9891\u7387\u4F1A\u8BA9 KB \u6162\u901F\u6B7B\u6389\u3002
50
+ - **Archive cadence nudge** (rc.36 / finish\u2192archive): \u663E\u8457 decision \u6536\u53E3\u6216\u4E00\u6279 Edit \u8FBE\u5230 config \`archive_edit_threshold\`(\u9ED8\u8BA4 20) \u540E,\u5728\u5408\u9002\u56DE\u5408\u8F7B\u91CF\u81EA\u8C03 \`fabric-archive\`(\u540C turn \u6700\u591A 1 \u6B21;\u975E task engine / \u975E Stop-hook flood)\u3002Stop hook \u4EC5 soft threshold nudge,\u5B88 KT-DEC-0007 \u2014 archive \u6CA1\u5EFA\u7ACB\u9891\u7387\u4F1A\u8BA9 KB \u6162\u901F\u6B7B\u6389\u3002
50
51
  - **Review backlog nudge** (rc.36): \u9700\u8981\u5224\u65AD pending backlog \u65F6\u8D70 \`fab_pending action="list"\` \u6216 \`fabric-review\` \u8FD4\u56DE\u7684 \`pending_path\`;\u4E0D\u8981 glob \u9879\u76EE\u672C\u5730 \`.fabric/knowledge/pending\`\u3002\u5F53\u53EF\u89C1 pending \u7D2F\u79EF >10 \u6761\u65F6,\u5728\u5408\u9002\u56DE\u5408\u4E3B\u52A8 propose \u8C03 \`fabric-review\` skill \u6279\u91CF\u5BA1,\u907F\u514D draft \u5361\u6B7B\u3002
51
52
 
52
53
  ## Self-archive policy (v2.2 C1: \u7CBE\u7B80\u8BF4\u660E\u4E66)
@@ -95,8 +96,9 @@ As a dev you only need to: run \`fabric install\` once per repo, use \`fabric st
95
96
  See \`docs/USER-QUICKSTART.md\` for the full maintainer version.
96
97
 
97
98
  ## Behavior Rules
98
- - **Before modifying any file**: first \`fab_recall(paths=[<file-being-edited>])\` \u2014\u2014 a single call returns the relevant KB descriptions + native read paths (\`entries[].read_path\`). \`fab_recall\` no longer delivers bodies; when you need a body, do a native Read of its \`entries[].read_path\` (\`Read <store>/knowledge/<type>/<id>--*.md\`), which the PostToolUse hook records as \`knowledge_body_read\`. Lean default: descriptions + index already suffice to discover entries; read a body once on demand, don't re-inject it every turn (KT-GLD-0005).
99
- - **Never hand-edit \`.fabric/agents.meta.json\`**; the engine syncs derived state automatically \u2014 run \`fabric doctor --fix\` for an explicit reconcile.
99
+ - **Pre-action gating (MUST before Edit/Write)**: (1) call \`fab_recall(paths=[<file-being-edited>], session_id=<client-session-id>)\`; (2) optionally native-Read selected \`entries[].read_path\` bodies (PostToolUse records \`knowledge_body_read\`); (3) when skipping a hit, say \`dismissed: <id> (<reason>)\`. \`fab_recall\` no longer delivers bodies; lean default is description+index discovery (KT-GLD-0005). PreToolUse is soft nudge only per KT-DEC-0007 (never permanent gate / no \`decision:block\`).
100
+ - **Archive-as-truth**: the **only** path into canonical is \`fab_propose\` \u2192 pending \u2192 \`fabric-review\`/\`fab_review\` approve; never auto-promote chat/logs into canonical.
101
+ - **Never hand-edit \`.fabric/agents.meta.json\`** (co-location index retired; knowledge lives in mounted stores). For anomalies run \`fabric doctor\` / \`fabric doctor --fix\` on fixable state.
100
102
 
101
103
  ## Knowledge Base (KB)
102
104
  - **Discovery**: the SessionStart hook lists broad-scoped entries (scoped by \`semantic_scope\` across three tiers: \`team\` team-wide / \`project:<id>\` this-project-only (surfaces only in repos bound to that project) / \`personal\` \`KP-*\`, all three referenced the same way); editing a file may trigger a narrow hint via the PreToolUse hook.
@@ -105,7 +107,7 @@ See \`docs/USER-QUICKSTART.md\` for the full maintainer version.
105
107
  - **session_id**: when calling \`fab_recall\`, always pass the current client session id as the \`session_id\` argument (Claude Code's session id is in the stdin payload; Codex's corresponding identifier likewise). This lets \`fabric doctor --archive-history\` and the \`fabric-hint.cjs\` Stop hook track cross-session debt accurately.
106
108
  - **Skills (4)**: write flow \`fabric-archive\` (with source-mode cold-start backfill from git/docs) / \`fabric-review\` (with retire-deprecation + relate-edge sub-flows); store ops \`fabric-store\` / \`fabric-sync\`.
107
109
  - **Language**: rendered per the \`language\` field in \`~/.fabric/fabric-global.json\` (machine-wide tone).
108
- - **Archive cadence nudge** (rc.36): after each batch of edits (default ~20, matching the Stop hook threshold config \`archive_edit_threshold\`) / a significant decision, proactively propose the \`fabric-archive\` skill at a suitable turn \u2014 without an archive cadence the KB slowly dies.
110
+ - **Archive cadence nudge** (rc.36 / finish\u2192archive): after a significant decision lands or an edit batch reaches config \`archive_edit_threshold\` (default 20), lightly self-trigger \`fabric-archive\` at a suitable turn (max 1 per turn; not a task engine / not Stop-hook flood). Stop hook remains a soft threshold nudge per KT-DEC-0007 \u2014 without an archive cadence the KB slowly dies.
109
111
  - **Review backlog nudge** (rc.36): to judge the pending backlog, go through \`fab_pending action="list"\` or the \`pending_path\` returned by \`fabric-review\`; don't glob the project-local \`.fabric/knowledge/pending\`. When the visible pending count exceeds 10, proactively propose the \`fabric-review\` skill at a suitable turn to batch-review and avoid draft deadlock.
110
112
 
111
113
  ## Self-archive policy (v2.2 C1: lean spec)