@fenglimg/fabric-shared 2.3.0-rc.8 → 2.3.0
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/{chunk-CVNJCAC7.js → chunk-5M3HB554.js} +190 -37
- package/dist/{chunk-ASS2KBB7.js → chunk-OX6DWUK4.js} +31 -2
- package/dist/{chunk-KFFBQRL5.js → chunk-V7IUN5U2.js} +9 -7
- package/dist/{chunk-P6INRP3A.js → chunk-WXVFJBL5.js} +194 -39
- package/dist/i18n/index.js +3 -2
- package/dist/{index-B2e0wKJi.d.ts → index-KvxqMZV1.d.ts} +6 -0
- package/dist/index.d.ts +474 -25
- package/dist/index.js +310 -43
- package/dist/schemas/api-contracts.d.ts +608 -151
- package/dist/schemas/api-contracts.js +1 -1
- package/dist/templates/bootstrap-canonical.d.ts +2 -2
- package/dist/templates/bootstrap-canonical.js +3 -2
- package/dist/theme.d.ts +6 -3
- package/dist/theme.js +27 -5
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -49,16 +49,24 @@ var _knowledgeTypeEnum = z3.enum(["models", "decisions", "guidelines", "pitfalls
|
|
|
49
49
|
var _maturityEnum = z3.enum(["draft", "verified", "proven"]);
|
|
50
50
|
var _ruleDescriptionSchema = z3.object({
|
|
51
51
|
summary: z3.string(),
|
|
52
|
-
|
|
52
|
+
// TASK-005 wire thinning: intent_clues dropped from recall wire (0 hook
|
|
53
|
+
// consumers grep-verified). Selection signal is summary + must_read_if
|
|
54
|
+
// (when distinct) + knowledge_type; intent_clues is preserved in the on-disk
|
|
55
|
+
// .md frontmatter (KB source-of-truth) and reachable via read_path.
|
|
56
|
+
intent_clues: z3.array(z3.string()).optional(),
|
|
53
57
|
// wire-slim (payload): fab_recall projects a LEAN description (summary +
|
|
54
|
-
// must_read_if +
|
|
55
|
-
//
|
|
58
|
+
// must_read_if + knowledge_type — the selection signal), leaving tech_stack/
|
|
59
|
+
// impact/intent_clues to be Read on demand via read_path (KT-DEC-0026 lean
|
|
56
60
|
// contract at the field level). So they are optional on the wire. plan-context
|
|
57
|
-
// still returns them in full — zod keeps optional-present values, only ABSENT
|
|
58
|
-
// now allowed, so no plan-context consumer regresses.
|
|
61
|
+
// still returns them in full — zod keeps optional-present values, only ABSENT
|
|
62
|
+
// is now allowed, so no plan-context consumer regresses.
|
|
59
63
|
tech_stack: z3.array(z3.string()).optional(),
|
|
60
64
|
impact: z3.array(z3.string()).optional(),
|
|
61
|
-
|
|
65
|
+
// TASK-002 wire dedup: omitted when identical to `summary` (~40% of KB entries
|
|
66
|
+
// — knowledge-meta-builder.ts:212/:248 `?? summary` fallback). Consumers may
|
|
67
|
+
// fall back to `summary` when absent. KB source-of-truth (the .md frontmatter)
|
|
68
|
+
// stays unchanged; this optionality is a WIRE-only projection.
|
|
69
|
+
must_read_if: z3.string().optional(),
|
|
62
70
|
// v2.0: optional knowledge-entry fields. Absent for v1.x rules; present for
|
|
63
71
|
// entries that declare frontmatter `id/type/maturity`. W4/Track1: the redundant
|
|
64
72
|
// `knowledge_layer` field was removed — a candidate's layer is derived from its
|
|
@@ -310,28 +318,40 @@ var recallInputSchema = z3.object({
|
|
|
310
318
|
// W1-3 / KT-DEC-0031: graph expansion (surface related read paths, no body).
|
|
311
319
|
include_related: z3.boolean().optional().describe(
|
|
312
320
|
"When true, also surface the one-hop `related` graph neighbours (of the surfaced entries) that are present in the candidate set \u2014 their descriptions and read paths, NOT their bodies."
|
|
321
|
+
),
|
|
322
|
+
// TASK-006 (KT-PIT-0036 observability opt-in): score_breakdown carries the
|
|
323
|
+
// numbers-only signal decomposition (bm25/vector/salience/recency/locality/
|
|
324
|
+
// proximity/credibility → final). Emitted per-entry ONLY when this flag is
|
|
325
|
+
// true — the debug/tuning surface. Steady-state recall omits it to stay lean
|
|
326
|
+
// (~4.8KB saved on a 24-entry sample). final===score invariant still enforced
|
|
327
|
+
// at the plan-context service layer (candidate_scores Map) regardless.
|
|
328
|
+
include_score_breakdown: z3.boolean().optional().describe(
|
|
329
|
+
"When true, populate `entry.score_breakdown` (numbers-only signal decomposition \u2014 bm25/vector/salience/recency/locality/proximity/credibility \u2192 final). Off by default for wire efficiency. Enable when debugging ranking or tuning scoring weights."
|
|
313
330
|
)
|
|
314
331
|
});
|
|
332
|
+
var _recallEntryDescriptionSchema = z3.object({
|
|
333
|
+
summary: z3.string(),
|
|
334
|
+
// TASK-002: omitted when identical to summary (~40% dedup).
|
|
335
|
+
must_read_if: z3.string().optional(),
|
|
336
|
+
// Semantic-preservation (PLN-002 F1 restored): knowledge-hint-narrow.cjs
|
|
337
|
+
// consumes this for the "⚠️ 后果" narrow-hint line.
|
|
338
|
+
impact: z3.array(z3.string()).optional(),
|
|
339
|
+
// Semantic-preservation (PLN-002): cite-contract-reminder.cjs consumes it.
|
|
340
|
+
knowledge_type: _knowledgeTypeEnum.optional()
|
|
341
|
+
});
|
|
315
342
|
var _recallEntrySchema = z3.object({
|
|
316
343
|
stable_id: z3.string(),
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
|
|
320
|
-
// The DESCRIPTION (summary / intent_clues / must_read_if / related ...). No body.
|
|
321
|
-
description: _ruleDescriptionSchema,
|
|
344
|
+
// The projected DESCRIPTION (recall-specific slim contract, not the shared
|
|
345
|
+
// frontmatter shape). Codex review F6: dedicated schema locks the wire contract.
|
|
346
|
+
description: _recallEntryDescriptionSchema,
|
|
322
347
|
// on-disk knowledge file to Read for the full body. Omitted when the entry has
|
|
323
348
|
// no resolvable file (description-only discovery) or was scoped out by `ids`.
|
|
324
349
|
read_path: z3.string().optional(),
|
|
325
350
|
// originating store alias (omitted for unqualified / single-store entries).
|
|
326
|
-
|
|
351
|
+
store_alias: z3.string().optional(),
|
|
327
352
|
// true when this entry's body is ALSO injected at SessionStart (broad
|
|
328
353
|
// model/guideline "ALWAYS-ACTIVE") — skip the Read, it is already in context.
|
|
329
354
|
body_in_context: z3.boolean().optional(),
|
|
330
|
-
// P1 recall-observability: the fused relevance score this entry scored during
|
|
331
|
-
// the plan-context sort (was computed internally but dropped before this wave).
|
|
332
|
-
// Optional + additive — backward-compatible. MUST be declared here or zod
|
|
333
|
-
// .strip() silently drops it at the MCP boundary (KT-PIT-0005).
|
|
334
|
-
score: z3.number().optional(),
|
|
335
355
|
// P1 recall-observability: numbers-only decomposition of `score` into its
|
|
336
356
|
// weighted signal contributions. NEVER carries body/description text — preserves
|
|
337
357
|
// the lean read_path contract (KT-DEC-0019 / KT-GLD-0005). bm25_rank/vector_rank
|
|
@@ -355,25 +375,24 @@ var _recallEntrySchema = z3.object({
|
|
|
355
375
|
}).optional()
|
|
356
376
|
});
|
|
357
377
|
var recallOutputSchema = z3.object({
|
|
378
|
+
// Retained: client hook cache key (packages/cli/.claude/hooks/knowledge-hint-narrow.cjs)
|
|
358
379
|
revision_hash: z3.string(),
|
|
359
|
-
stale: z3.boolean(),
|
|
360
380
|
// ux-w2-4: single unified entry list (was candidates[] + paths[] + per-path
|
|
361
381
|
// requirement-profile entries[]). Each item carries description + read_path +
|
|
362
382
|
// rank + body_in_context, so the agent never joins two arrays on stable_id.
|
|
363
383
|
entries: z3.array(_recallEntrySchema),
|
|
364
|
-
// v2.2 payload de-dup: single top-level echo of the caller's `intent`.
|
|
365
|
-
// Omitted when no intent.
|
|
366
|
-
intent: z3.string().optional(),
|
|
367
384
|
// K6 (W3-K): structured list of lower-ranked candidates dropped by the
|
|
368
|
-
// retrieval pipeline
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
385
|
+
// retrieval pipeline. dropped_ids preserves per-id transparency (KT-DEC-0028);
|
|
386
|
+
// dropped_reasons hoists the reason to a top-level count map (68/68 same-reason
|
|
387
|
+
// observation from ANL-002). Present ONLY when truncation fired.
|
|
388
|
+
dropped_ids: z3.array(z3.string()).optional(),
|
|
389
|
+
dropped_reasons: z3.object({
|
|
390
|
+
retrieval_budget: z3.number().int().nonnegative().optional(),
|
|
391
|
+
payload_budget: z3.number().int().nonnegative().optional()
|
|
392
|
+
}).optional(),
|
|
393
|
+
preflight_diagnostics: z3.array(_preflightDiagnosticSchema).optional(),
|
|
376
394
|
warnings: z3.array(structuredWarningSchema).optional(),
|
|
395
|
+
// Auto-heal banner pair (consumed by knowledge-hint-broad.cjs:711-729).
|
|
377
396
|
auto_healed: z3.boolean().optional(),
|
|
378
397
|
previous_revision_hash: z3.string().optional(),
|
|
379
398
|
// v2.0.0-rc.37 NEW-24: parallel to planContextOutputSchema.redirects — stale
|
|
@@ -384,9 +403,6 @@ var recallOutputSchema = z3.object({
|
|
|
384
403
|
// (appended id → surfaced source id). Present only when include_related
|
|
385
404
|
// appended an in-corpus neighbour. Omitted on the steady-state path.
|
|
386
405
|
related_appended: z3.record(z3.string()).optional(),
|
|
387
|
-
// v2.2 MC1-recall-pack: standing behavioral directive (cite-before-edit) +
|
|
388
|
-
// dynamic discovery hints, so the one-call recall is self-describing.
|
|
389
|
-
directive: z3.string(),
|
|
390
406
|
next_steps: z3.array(z3.string()).optional()
|
|
391
407
|
});
|
|
392
408
|
var recallAnnotations = {
|
|
@@ -681,7 +697,30 @@ var _fabReviewModifyChangesSchema = z3.object({
|
|
|
681
697
|
// edges via fab_recall and sends the merged set. Absent this field the modify
|
|
682
698
|
// path silently dropped `related` via zod .strip() (KT-PIT-0005 recurrence),
|
|
683
699
|
// leaving the only programmatic related-write path non-functional.
|
|
684
|
-
related: z3.array(z3.string()).optional()
|
|
700
|
+
related: z3.array(z3.string()).optional(),
|
|
701
|
+
// rc.9 (2026-07-06): discovery-signal scalar patches — must_read_if triggers
|
|
702
|
+
// Reference-type entry surfacing; intent_clues drives the AI's "should I Read
|
|
703
|
+
// the body?" judgment; impact enumerates consequence prose surfaced in the
|
|
704
|
+
// BM25F body slot. Before rc.9 these three fields were undeclared here, so
|
|
705
|
+
// fab_review modify silently .strip()'d them (KT-PIT-0005 recurrence) and the
|
|
706
|
+
// only path to fix a bad-shape must_read_if / missing intent_clues was direct
|
|
707
|
+
// Edit — bypassing the skill audit trail. All three are REPLACE semantics
|
|
708
|
+
// (mirror tags/related). must_read_if is a scalar string; intent_clues +
|
|
709
|
+
// impact are flow-arrays.
|
|
710
|
+
must_read_if: z3.string().optional(),
|
|
711
|
+
intent_clues: 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()
|
|
685
724
|
});
|
|
686
725
|
var FabReviewInputSchema = z3.discriminatedUnion("action", [
|
|
687
726
|
z3.object({
|
|
@@ -716,11 +755,45 @@ var FabReviewInputSchema = z3.discriminatedUnion("action", [
|
|
|
716
755
|
layer: z3.enum(["team", "personal"])
|
|
717
756
|
})
|
|
718
757
|
}),
|
|
758
|
+
// v2.3 batch content-modify: array-native flush for the fabric-review maintain
|
|
759
|
+
// loop. Each item is an INDEPENDENT content edit — unlike approve/reject/defer
|
|
760
|
+
// (which share one reason/until over pending_paths[]), modify needs its OWN
|
|
761
|
+
// changes per entry, so the shape is items[] not paths[]. Layer is stripped
|
|
762
|
+
// per item (content-only; layer-flips stay on the single interactive
|
|
763
|
+
// modify-layer path). Per-item failure is isolated: a bad item reports
|
|
764
|
+
// {ok:false, error} in modified[] without aborting its siblings.
|
|
765
|
+
z3.object({
|
|
766
|
+
action: z3.literal("modify-content-batch"),
|
|
767
|
+
items: z3.array(
|
|
768
|
+
z3.object({
|
|
769
|
+
pending_path: z3.string().min(1),
|
|
770
|
+
changes: _fabReviewModifyChangesSchema
|
|
771
|
+
})
|
|
772
|
+
).min(1)
|
|
773
|
+
}),
|
|
719
774
|
z3.object({
|
|
720
775
|
action: z3.literal("defer"),
|
|
721
776
|
pending_paths: z3.array(z3.string()).min(1),
|
|
722
777
|
until: z3.string().datetime().optional(),
|
|
723
778
|
reason: z3.string().optional()
|
|
779
|
+
}),
|
|
780
|
+
// retire (W3-C: fabric-review retire-mode landing surface). Semantically
|
|
781
|
+
// deprecates one or more CANONICAL knowledge entries so they stop surfacing in
|
|
782
|
+
// recall candidates / broad SessionStart indexes — WITHOUT deleting the file
|
|
783
|
+
// (red line: deprecate-over-delete). The service writes `deprecated: true`
|
|
784
|
+
// (+ `superseded_by: <id>` when the entry is replaced) into the entry's
|
|
785
|
+
// frontmatter via the same in-place merge path modify uses; body + stable_id
|
|
786
|
+
// are preserved so the "当时为什么这么决策" rationale stays inspectable.
|
|
787
|
+
z3.object({
|
|
788
|
+
action: z3.literal("retire"),
|
|
789
|
+
// Canonical entry paths (store-absolute, from fab_pending list/search).
|
|
790
|
+
pending_paths: z3.array(z3.string()).min(1),
|
|
791
|
+
// Optional stable_id of the entry that supersedes these (bare `KT-DEC-0001`
|
|
792
|
+
// or store-qualified `alias:KT-DEC-0001`). Written as `superseded_by`
|
|
793
|
+
// frontmatter so the supersession chain is recoverable.
|
|
794
|
+
superseded_by: z3.string().optional(),
|
|
795
|
+
// Optional human reason recorded on the knowledge_modified ledger event.
|
|
796
|
+
reason: z3.string().optional()
|
|
724
797
|
})
|
|
725
798
|
]);
|
|
726
799
|
var FabPendingInputSchema = z3.discriminatedUnion("action", [
|
|
@@ -746,11 +819,20 @@ var FabPendingInputShape = {
|
|
|
746
819
|
)
|
|
747
820
|
};
|
|
748
821
|
var FabReviewInputShape = {
|
|
749
|
-
action: z3.enum([
|
|
750
|
-
"
|
|
822
|
+
action: z3.enum([
|
|
823
|
+
"approve",
|
|
824
|
+
"reject",
|
|
825
|
+
"modify",
|
|
826
|
+
"modify-content",
|
|
827
|
+
"modify-layer",
|
|
828
|
+
"modify-content-batch",
|
|
829
|
+
"defer",
|
|
830
|
+
"retire"
|
|
831
|
+
]).describe(
|
|
832
|
+
"Action selector. Discriminates the per-action fields below; required. modify-content edits scalars (no layer); modify-layer is the layer-flip path (changes.layer required); modify is the legacy combined alias; modify-content-batch flushes an array of independent content edits (items[]) in one call; retire marks canonical entries deprecated (deprecate-over-delete) so they stop surfacing. (list/search moved to the read-only fab_pending tool.)"
|
|
751
833
|
),
|
|
752
834
|
pending_paths: z3.array(z3.string()).min(1).optional().describe(
|
|
753
|
-
"Workspace-relative pending entry paths. Required when action=approve|reject|defer (non-empty array)."
|
|
835
|
+
"Workspace-relative pending entry paths (or canonical entry paths for action=retire). Required when action=approve|reject|defer|retire (non-empty array)."
|
|
754
836
|
),
|
|
755
837
|
pending_path: z3.string().min(1).optional().describe(
|
|
756
838
|
"Workspace-relative pending OR canonical entry path. Required when action=modify."
|
|
@@ -761,8 +843,19 @@ var FabReviewInputShape = {
|
|
|
761
843
|
changes: _fabReviewModifyChangesSchema.optional().describe(
|
|
762
844
|
"Frontmatter scalar patches (title/summary/layer/maturity/tags/relevance_*/semantic_scope/related). Required when action=modify. semantic_scope re-scopes the entry's resolution coordinate in place (e.g. team \u2192 project:<id>) without moving stores; personal-root coordinates are rejected (use modify-layer)."
|
|
763
845
|
),
|
|
846
|
+
items: z3.array(
|
|
847
|
+
z3.object({
|
|
848
|
+
pending_path: z3.string().min(1),
|
|
849
|
+
changes: _fabReviewModifyChangesSchema
|
|
850
|
+
})
|
|
851
|
+
).min(1).optional().describe(
|
|
852
|
+
"Batch of independent content edits \u2014 required (non-empty) when action=modify-content-batch. Each item {pending_path, changes} is applied content-only (layer stripped); per-item failures surface in modified[] without aborting siblings."
|
|
853
|
+
),
|
|
764
854
|
until: z3.string().datetime().optional().describe(
|
|
765
855
|
"ISO-8601 datetime upper bound for the deferral. Optional; used only when action=defer."
|
|
856
|
+
),
|
|
857
|
+
superseded_by: z3.string().optional().describe(
|
|
858
|
+
"Stable_id (bare or store-qualified) of the entry that supersedes the retired one, written as `superseded_by` frontmatter. Optional; used only when action=retire."
|
|
766
859
|
)
|
|
767
860
|
};
|
|
768
861
|
var _fabReviewListItemSchema = z3.object({
|
|
@@ -781,6 +874,8 @@ var _fabReviewListItemSchema = z3.object({
|
|
|
781
874
|
tags: z3.array(z3.string()).optional(),
|
|
782
875
|
title: z3.string().optional(),
|
|
783
876
|
summary: z3.string().optional(),
|
|
877
|
+
// ISS-20260712-011: triage UI needs proposed_reason without a second Read.
|
|
878
|
+
proposed_reason: z3.string().optional(),
|
|
784
879
|
// Store-only cutover: origin reflects the resolved store audience where the
|
|
785
880
|
// pending file lives; layer reflects the declared classification that will
|
|
786
881
|
// drive approval semantics.
|
|
@@ -820,10 +915,16 @@ var _fabReviewSearchItemSchema = z3.object({
|
|
|
820
915
|
// hits in the same array.
|
|
821
916
|
stable_id: z3.string().optional()
|
|
822
917
|
});
|
|
918
|
+
var _fabReviewFailedItemSchema = z3.object({
|
|
919
|
+
pending_path: z3.string(),
|
|
920
|
+
reason: z3.string()
|
|
921
|
+
});
|
|
823
922
|
var FabReviewOutputSchema = z3.discriminatedUnion("action", [
|
|
824
923
|
z3.object({
|
|
825
924
|
action: z3.literal("approve"),
|
|
826
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(),
|
|
827
928
|
warnings: z3.array(structuredWarningSchema).optional()
|
|
828
929
|
}),
|
|
829
930
|
z3.object({
|
|
@@ -839,10 +940,41 @@ var FabReviewOutputSchema = z3.discriminatedUnion("action", [
|
|
|
839
940
|
new_stable_id: z3.string().optional(),
|
|
840
941
|
warnings: z3.array(structuredWarningSchema).optional()
|
|
841
942
|
}),
|
|
943
|
+
// v2.3 batch content-modify result: per-item {pending_path, ok, error?}. No
|
|
944
|
+
// prior/new_stable_id — content edits strip layer, so no id reallocation can
|
|
945
|
+
// occur. ok=false items carry the failure message; siblings still applied
|
|
946
|
+
// (partial failure is reported per-item, never thrown for the whole batch).
|
|
947
|
+
z3.object({
|
|
948
|
+
action: z3.literal("modify-content-batch"),
|
|
949
|
+
modified: z3.array(
|
|
950
|
+
z3.object({
|
|
951
|
+
pending_path: z3.string(),
|
|
952
|
+
ok: z3.boolean(),
|
|
953
|
+
error: z3.string().optional()
|
|
954
|
+
})
|
|
955
|
+
),
|
|
956
|
+
warnings: z3.array(structuredWarningSchema).optional()
|
|
957
|
+
}),
|
|
842
958
|
z3.object({
|
|
843
959
|
action: z3.literal("defer"),
|
|
844
960
|
deferred: z3.array(z3.string()),
|
|
845
961
|
warnings: z3.array(structuredWarningSchema).optional()
|
|
962
|
+
}),
|
|
963
|
+
z3.object({
|
|
964
|
+
action: z3.literal("retire"),
|
|
965
|
+
// Each retired canonical entry: its echoed path + (when the frontmatter
|
|
966
|
+
// carried one) its stable_id, plus the superseded_by id when supplied. The
|
|
967
|
+
// file is NOT deleted — only marked `deprecated: true` in place.
|
|
968
|
+
retired: z3.array(
|
|
969
|
+
z3.object({
|
|
970
|
+
path: z3.string(),
|
|
971
|
+
stable_id: z3.string().optional(),
|
|
972
|
+
superseded_by: z3.string().optional()
|
|
973
|
+
})
|
|
974
|
+
),
|
|
975
|
+
// ISS-20260712-012: partial-failure surface for skipped/unresolvable paths.
|
|
976
|
+
failed: z3.array(_fabReviewFailedItemSchema).optional(),
|
|
977
|
+
warnings: z3.array(structuredWarningSchema).optional()
|
|
846
978
|
})
|
|
847
979
|
]);
|
|
848
980
|
var FabPendingOutputSchema = z3.discriminatedUnion("action", [
|
|
@@ -867,12 +999,15 @@ var FabPendingOutputShape = {
|
|
|
867
999
|
warnings: z3.array(structuredWarningSchema).optional()
|
|
868
1000
|
};
|
|
869
1001
|
var FabReviewOutputShape = {
|
|
870
|
-
action: z3.enum(["approve", "reject", "modify", "defer"]).describe(
|
|
1002
|
+
action: z3.enum(["approve", "reject", "modify", "modify-content-batch", "defer", "retire"]).describe(
|
|
871
1003
|
"Echoes the input action; clients can switch on it for per-variant fields below. (list/search results moved to the read-only fab_pending tool.)"
|
|
872
1004
|
),
|
|
873
1005
|
approved: z3.array(z3.object({ pending_path: z3.string(), stable_id: z3.string() })).optional().describe(
|
|
874
1006
|
"Allocated stable ids paired with their original pending paths. Present when action=approve."
|
|
875
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
|
+
),
|
|
876
1011
|
rejected: z3.array(z3.string()).optional().describe(
|
|
877
1012
|
"Pending paths that were rejected (files retained on disk; doctor owns vacuum). Present when action=reject."
|
|
878
1013
|
),
|
|
@@ -885,9 +1020,27 @@ var FabReviewOutputShape = {
|
|
|
885
1020
|
new_stable_id: z3.string().optional().describe(
|
|
886
1021
|
"New stable id after reallocation. Present when action=modify AND a layer-flip reallocated the id."
|
|
887
1022
|
),
|
|
1023
|
+
modified: z3.array(
|
|
1024
|
+
z3.object({
|
|
1025
|
+
pending_path: z3.string(),
|
|
1026
|
+
ok: z3.boolean(),
|
|
1027
|
+
error: z3.string().optional()
|
|
1028
|
+
})
|
|
1029
|
+
).optional().describe(
|
|
1030
|
+
"Per-item results for action=modify-content-batch: {pending_path, ok, error?}. ok=false items carry the error; siblings still applied."
|
|
1031
|
+
),
|
|
888
1032
|
deferred: z3.array(z3.string()).optional().describe(
|
|
889
1033
|
"Pending paths that were deferred (files retained on disk). Present when action=defer."
|
|
890
1034
|
),
|
|
1035
|
+
retired: z3.array(
|
|
1036
|
+
z3.object({
|
|
1037
|
+
path: z3.string(),
|
|
1038
|
+
stable_id: z3.string().optional(),
|
|
1039
|
+
superseded_by: z3.string().optional()
|
|
1040
|
+
})
|
|
1041
|
+
).optional().describe(
|
|
1042
|
+
"Canonical entries marked deprecated in place (files retained \u2014 deprecate-over-delete). Present when action=retire."
|
|
1043
|
+
),
|
|
891
1044
|
// v2.0.0-rc.23 TASK-009 (d): optional warnings surface for the first-reconcile
|
|
892
1045
|
// gate (`meta_stale` / `reconcile_failed`). Absent on the steady-state path.
|
|
893
1046
|
warnings: z3.array(structuredWarningSchema).optional()
|
|
@@ -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
|
-
|
|
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-
|
|
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
|
-
-
|
|
40
|
-
-
|
|
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): \
|
|
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
|
-
- **
|
|
99
|
-
- **
|
|
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
|
|
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)
|