kibi-cli 2.21.2 → 2.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-register-foundation.d.ts.map +1 -1
- package/dist/cli-register-foundation.js +1 -0
- package/dist/commands/check.d.ts +0 -1
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +47 -111
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +108 -6
- package/dist/commands/init-helpers.d.ts +16 -2
- package/dist/commands/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +52 -10
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +12 -1
- package/dist/commands/legacy-storage-migration.d.ts +46 -1
- package/dist/commands/legacy-storage-migration.d.ts.map +1 -1
- package/dist/commands/legacy-storage-migration.js +219 -9
- package/dist/commands/migrate.d.ts +37 -0
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +72 -188
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +9 -0
- package/dist/extractors/symbols-coordinator.js +1 -0
- package/dist/intent-search.d.ts.map +1 -1
- package/dist/intent-search.js +28 -6
- package/dist/operations/migration/inventory-rederive.d.ts +40 -1
- package/dist/operations/migration/inventory-rederive.d.ts.map +1 -1
- package/dist/operations/migration/inventory-rederive.js +99 -8
- package/dist/operations/migration/requirement-status.d.ts +25 -0
- package/dist/operations/migration/requirement-status.d.ts.map +1 -0
- package/dist/operations/migration/requirement-status.js +105 -0
- package/dist/operations/migration/schema6.d.ts +25 -0
- package/dist/operations/migration/schema6.d.ts.map +1 -1
- package/dist/operations/migration/schema6.js +265 -7
- package/dist/operations/migration/symbol-legacy-links.d.ts +51 -0
- package/dist/operations/migration/symbol-legacy-links.d.ts.map +1 -0
- package/dist/operations/migration/symbol-legacy-links.js +319 -0
- package/dist/operations/mutation/context-warning.d.ts +12 -2
- package/dist/operations/mutation/context-warning.d.ts.map +1 -1
- package/dist/operations/mutation/context-warning.js +6 -6
- package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
- package/dist/operations/mutation/source-authoring.js +130 -35
- package/dist/operations/mutation/types.d.ts +2 -1
- package/dist/operations/mutation/types.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.d.ts +17 -2
- package/dist/operations/mutation/upsert.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.js +28 -11
- package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
- package/dist/operations/mutation/validate-upsert.js +40 -8
- package/dist/operations/mutation/validation.d.ts.map +1 -1
- package/dist/operations/mutation/validation.js +43 -1
- package/dist/operations/planning/apply-plan.d.ts.map +1 -1
- package/dist/operations/planning/apply-plan.js +34 -10
- package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/ingestion-boundary.js +25 -1
- package/dist/operations/semantic-advisor/stored-ledger-drift.d.ts +19 -0
- package/dist/operations/semantic-advisor/stored-ledger-drift.d.ts.map +1 -0
- package/dist/operations/semantic-advisor/stored-ledger-drift.js +78 -0
- package/dist/public/impact/diagnostics.d.ts.map +1 -1
- package/dist/public/impact/diagnostics.js +24 -14
- package/dist/public/operations/catalog.d.ts +3 -3
- package/dist/public/operations/check-executor.d.ts.map +1 -1
- package/dist/public/operations/check-executor.js +3 -0
- package/dist/public/operations/contracts.d.ts.map +1 -1
- package/dist/public/operations/contracts.js +6 -1
- package/dist/public/operations/decision-firmness.d.ts +3 -1
- package/dist/public/operations/decision-firmness.d.ts.map +1 -1
- package/dist/public/operations/decision-firmness.js +13 -3
- package/dist/public/operations/discovery-executors.d.ts +2 -0
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +24 -2
- package/dist/public/operations/migration-plan.d.ts +15 -0
- package/dist/public/operations/migration-plan.d.ts.map +1 -1
- package/dist/public/operations/migration-plan.js +130 -17
- package/dist/public/operations/result-envelope.d.ts.map +1 -1
- package/dist/public/operations/result-envelope.js +27 -1
- package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
- package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
- package/dist/public/operations/specs/check-rules.generated.js +2 -1
- package/dist/public/operations/specs/check.d.ts +1 -1
- package/dist/public/operations/specs/planning.d.ts +2 -2
- package/dist/public/operations/specs/planning.js +2 -2
- package/dist/public/operations/specs/semantic.d.ts.map +1 -1
- package/dist/public/operations/specs/semantic.js +26 -1
- package/dist/public/operations/symbol-granularity-check.d.ts +18 -0
- package/dist/public/operations/symbol-granularity-check.d.ts.map +1 -0
- package/dist/public/operations/symbol-granularity-check.js +175 -0
- package/dist/public/skills/kibi-usage/SKILL.md +8 -3
- package/dist/public/symbol-granularity.d.ts +17 -0
- package/dist/public/symbol-granularity.d.ts.map +1 -1
- package/dist/public/symbol-granularity.js +21 -0
- package/dist/traceability/evidence-model.d.ts +7 -3
- package/dist/traceability/evidence-model.d.ts.map +1 -1
- package/dist/traceability/staged-symbols-manifest.js +6 -5
- package/dist/traceability/validate.d.ts +14 -1
- package/dist/traceability/validate.d.ts.map +1 -1
- package/dist/traceability/validate.js +41 -0
- package/dist/utils/rule-registry.generated.d.ts +5 -0
- package/dist/utils/rule-registry.generated.d.ts.map +1 -1
- package/dist/utils/rule-registry.generated.js +6 -0
- package/package.json +3 -3
- package/src/public/impact/diagnostics.ts +29 -17
- package/src/public/operations/check-executor.ts +7 -0
- package/src/public/operations/contracts.ts +6 -1
- package/src/public/operations/decision-firmness.ts +18 -2
- package/src/public/operations/discovery-executors.ts +28 -2
- package/src/public/operations/migration-plan.ts +147 -20
- package/src/public/operations/result-envelope.ts +34 -1
- package/src/public/operations/specs/check-rules.generated.ts +2 -1
- package/src/public/operations/specs/planning.ts +2 -2
- package/src/public/operations/specs/semantic.ts +37 -2
- package/src/public/operations/symbol-granularity-check.ts +233 -0
- package/src/public/skills/kibi-usage/SKILL.md +8 -3
- package/src/public/symbol-granularity.ts +37 -0
|
@@ -9,7 +9,10 @@ import { type EntityLane } from "../utils/kb-paths.js";
|
|
|
9
9
|
* - legacy `.kb/config.json` is read (never trusted blindly);
|
|
10
10
|
* - configured or default legacy paths are discovered on disk;
|
|
11
11
|
* - files move into `.kb/<lane>/` without content changes;
|
|
12
|
-
* - destination
|
|
12
|
+
* - a legacy copy identical to its destination is removed instead of moved;
|
|
13
|
+
* - other destination conflicts abort before any file moves, each with the
|
|
14
|
+
* resolution Kibi can tell (a stale subset of the symbols manifest, a
|
|
15
|
+
* generated coordinates file, or a real conflict);
|
|
13
16
|
* - partial migrations resume safely (already-moved files are no-ops);
|
|
14
17
|
* - on success the legacy config is retired.
|
|
15
18
|
*/
|
|
@@ -22,9 +25,34 @@ export interface StorageMigrationPlan {
|
|
|
22
25
|
/** Lifecycle state preserved into the canonical manifest. */
|
|
23
26
|
schemaVersion: number | null;
|
|
24
27
|
semanticAdvisorBackfill: SemanticAdvisorBackfill | null;
|
|
28
|
+
/**
|
|
29
|
+
* Legacy copies byte-identical to their `.kb/` destination: the migration
|
|
30
|
+
* removes them instead of moving them.
|
|
31
|
+
*/
|
|
32
|
+
removals: StorageMigrationRemoval[];
|
|
33
|
+
/** Existing destinations that block a move, with how to resolve each. */
|
|
34
|
+
conflicts: StorageMigrationConflict[];
|
|
25
35
|
/** Why the migration cannot proceed automatically, if blocked. */
|
|
26
36
|
blockers: string[];
|
|
27
37
|
}
|
|
38
|
+
export interface StorageMigrationRemoval {
|
|
39
|
+
/** The legacy copy to remove. */
|
|
40
|
+
path: string;
|
|
41
|
+
/** The `.kb/` file it duplicates. */
|
|
42
|
+
duplicateOf: string;
|
|
43
|
+
}
|
|
44
|
+
export interface StorageMigrationConflict {
|
|
45
|
+
from: string;
|
|
46
|
+
to: string;
|
|
47
|
+
/**
|
|
48
|
+
* `stale_subset`: every symbol of the legacy manifest is already in the
|
|
49
|
+
* `.kb/` one. `generated`: a symbol-coordinates file, which sync
|
|
50
|
+
* regenerates. `ambiguous`: Kibi cannot tell which legacy file is meant.
|
|
51
|
+
* `conflict`: different content Kibi cannot classify.
|
|
52
|
+
*/
|
|
53
|
+
kind: "stale_subset" | "generated" | "ambiguous" | "conflict";
|
|
54
|
+
resolution: string;
|
|
55
|
+
}
|
|
28
56
|
export interface StorageMigrationMove {
|
|
29
57
|
from: string;
|
|
30
58
|
to: string;
|
|
@@ -34,6 +62,13 @@ export interface StorageMigrationMove {
|
|
|
34
62
|
export declare function planLegacyStorageMigration(cwd: string): StorageMigrationPlan;
|
|
35
63
|
export interface StorageMigrationResult {
|
|
36
64
|
movedFiles: string[];
|
|
65
|
+
/** Legacy copies removed because they were identical to their destination. */
|
|
66
|
+
removedFiles: string[];
|
|
67
|
+
/**
|
|
68
|
+
* Moved files Git does not track yet. Each got a pending-source receipt, so
|
|
69
|
+
* `kibi sync` compiles them before they are staged; empty outside Git.
|
|
70
|
+
*/
|
|
71
|
+
untrackedMovedFiles: string[];
|
|
37
72
|
retiredLegacyConfig: boolean;
|
|
38
73
|
manifestPath: string;
|
|
39
74
|
schemaVersion: number;
|
|
@@ -47,6 +82,16 @@ export interface StorageMigrationResult {
|
|
|
47
82
|
export declare function applyLegacyStorageMigration(cwd: string, plan: StorageMigrationPlan): StorageMigrationResult;
|
|
48
83
|
/** Rewrite recovery pending-source receipts after a storage cutover. */
|
|
49
84
|
export declare function rewritePendingSourceReceiptPaths(cwd: string, moves: readonly StorageMigrationMove[]): number;
|
|
85
|
+
/**
|
|
86
|
+
* Bind every moved file Git does not track to its bytes with a
|
|
87
|
+
* pending-source receipt, the record kb_upsert writes for the files it
|
|
88
|
+
* creates. `kibi sync` compiles tracked sources plus receipted ones, so the
|
|
89
|
+
* moved knowledge compiles at once; Kibi never stages anything itself, and
|
|
90
|
+
* each receipt retires when Git starts tracking its file. Later migration
|
|
91
|
+
* rewrites rebind the receipt they find. Covers files a previous,
|
|
92
|
+
* interrupted run moved too. Returns the untracked destinations.
|
|
93
|
+
*/
|
|
94
|
+
export declare function writeMovedSourceReceipts(cwd: string, moves: readonly StorageMigrationMove[]): string[];
|
|
50
95
|
/** True when the workspace still needs the legacy storage migration. */
|
|
51
96
|
export declare function needsLegacyStorageMigration(cwd: string): boolean;
|
|
52
97
|
/** Entity lanes that must exist for the canonical layout to be complete. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"legacy-storage-migration.d.ts","sourceRoot":"","sources":["../../src/commands/legacy-storage-migration.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"legacy-storage-migration.d.ts","sourceRoot":"","sources":["../../src/commands/legacy-storage-migration.ts"],"names":[],"mappings":"AAwCA,OAAO,EACL,KAAK,uBAAuB,EAI7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAGL,KAAK,UAAU,EAGhB,MAAM,sBAAsB,CAAC;AAI9B;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,4CAA4C;IAC5C,YAAY,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACxD;;;OAGG;IACH,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IACpC,yEAAyE;IACzE,SAAS,EAAE,wBAAwB,EAAE,CAAC;IACtC,kEAAkE;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX;;;;;OAKG;IACH,IAAI,EAAE,cAAc,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,CAAC;IAC9D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;CAC9B;AA2BD,6DAA6D;AAC7D,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAkJ5E;AAoID,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,8EAA8E;IAC9E,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;;;OAGG;IACH,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,uBAAuB,CAAC;CAClD;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,oBAAoB,GACzB,sBAAsB,CAyExB;AAkCD,wEAAwE;AACxE,wBAAgB,gCAAgC,CAC9C,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,SAAS,oBAAoB,EAAE,GACrC,MAAM,CAwCR;AAqBD;;;;;;;;GAQG;AAEH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,SAAS,oBAAoB,EAAE,GACrC,MAAM,EAAE,CAqBV;AA8BD,wEAAwE;AACxE,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAShE;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,CAI/D"}
|
|
@@ -15,13 +15,16 @@
|
|
|
15
15
|
* You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
|
+
import { execFileSync } from "node:child_process";
|
|
18
19
|
import { createHash } from "node:crypto";
|
|
19
20
|
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
20
21
|
import * as path from "node:path";
|
|
21
22
|
import fg from "fast-glob";
|
|
23
|
+
import { load as parseYaml } from "js-yaml";
|
|
24
|
+
import { writePendingSourceReceipt } from "../operations/mutation/source-authoring.js";
|
|
22
25
|
import { readLegacyKbConfig, resolveLegacyEntityPaths, } from "../utils/config.js";
|
|
23
26
|
import { defaultKbManifest, readKbManifest, writeKbManifest, } from "../utils/kb-manifest.js";
|
|
24
|
-
import { CANONICAL_ENTITY_PATHS, ENTITY_LANES, KB_PATHS, } from "../utils/kb-paths.js";
|
|
27
|
+
import { CANONICAL_ENTITY_PATHS, ENTITY_LANES, KB_PATHS, LEGACY_DEFAULT_ENTITY_PATHS, } from "../utils/kb-paths.js";
|
|
25
28
|
import { LATEST_KB_SCHEMA_VERSION } from "../utils/schema-version.js";
|
|
26
29
|
import { updateGitIgnore } from "./init-helpers.js";
|
|
27
30
|
const LANE_BY_CONFIG_KEY = {
|
|
@@ -57,6 +60,8 @@ export function planLegacyStorageMigration(cwd) {
|
|
|
57
60
|
legacyConfigError: legacyConfigResult.error,
|
|
58
61
|
schemaVersion: null,
|
|
59
62
|
semanticAdvisorBackfill: null,
|
|
63
|
+
removals: [],
|
|
64
|
+
conflicts: [],
|
|
60
65
|
blockers: [
|
|
61
66
|
`Legacy .kb/config.json is malformed (${legacyConfigResult.error}). Repair the JSON or provide an explicit recovery/migration override; Kibi will not infer default legacy paths.`,
|
|
62
67
|
],
|
|
@@ -96,7 +101,11 @@ export function planLegacyStorageMigration(cwd) {
|
|
|
96
101
|
}
|
|
97
102
|
}
|
|
98
103
|
// Symbols manifest + coordinates.
|
|
99
|
-
const
|
|
104
|
+
const conflicts = [];
|
|
105
|
+
const symbolsSource = resolveLegacySymbolsSource(cwd, config, legacyPaths.symbols);
|
|
106
|
+
if (symbolsSource.conflict !== undefined)
|
|
107
|
+
conflicts.push(symbolsSource.conflict);
|
|
108
|
+
const legacySymbols = symbolsSource.path;
|
|
100
109
|
if (typeof legacySymbols === "string" && legacySymbols.trim().length > 0) {
|
|
101
110
|
const legacyManifest = path.resolve(cwd, legacySymbols);
|
|
102
111
|
if (existsSync(legacyManifest) &&
|
|
@@ -122,17 +131,33 @@ export function planLegacyStorageMigration(cwd) {
|
|
|
122
131
|
});
|
|
123
132
|
}
|
|
124
133
|
}
|
|
125
|
-
// Destination
|
|
126
|
-
//
|
|
134
|
+
// Destination classification: an identical legacy copy is removed, an
|
|
135
|
+
// empty seeded symbols manifest is replaced, and anything else is an
|
|
136
|
+
// operator decision with the resolution Kibi can tell.
|
|
137
|
+
const removals = [];
|
|
138
|
+
const pending = [];
|
|
127
139
|
for (const move of moves) {
|
|
128
140
|
const destinationAbsolute = path.resolve(cwd, move.to);
|
|
129
|
-
if (!existsSync(destinationAbsolute))
|
|
141
|
+
if (!existsSync(destinationAbsolute)) {
|
|
142
|
+
pending.push(move);
|
|
130
143
|
continue;
|
|
144
|
+
}
|
|
131
145
|
// 'kibi init' seeds an empty symbols manifest; the legacy one replaces it.
|
|
132
|
-
if (move.lane === "symbols" && isEmptySymbolsStub(destinationAbsolute))
|
|
146
|
+
if (move.lane === "symbols" && isEmptySymbolsStub(destinationAbsolute)) {
|
|
147
|
+
pending.push(move);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const conflict = classifyExistingDestination(cwd, move);
|
|
151
|
+
if (conflict === null) {
|
|
152
|
+
removals.push({ path: move.from, duplicateOf: move.to });
|
|
133
153
|
continue;
|
|
134
|
-
|
|
154
|
+
}
|
|
155
|
+
pending.push(move);
|
|
156
|
+
conflicts.push(conflict);
|
|
135
157
|
}
|
|
158
|
+
moves.splice(0, moves.length, ...pending);
|
|
159
|
+
for (const conflict of conflicts)
|
|
160
|
+
blockers.push(conflict.resolution);
|
|
136
161
|
// Duplicate destinations (two legacy sources collapsing onto one target).
|
|
137
162
|
const destinations = new Map();
|
|
138
163
|
for (const move of moves) {
|
|
@@ -148,9 +173,126 @@ export function planLegacyStorageMigration(cwd) {
|
|
|
148
173
|
legacyConfig: legacyConfigResult.kind === "none" ? "absent" : "present",
|
|
149
174
|
schemaVersion: typeof config?.schemaVersion === "number" ? config.schemaVersion : null,
|
|
150
175
|
semanticAdvisorBackfill: config?.semanticAdvisorBackfill ?? null,
|
|
176
|
+
removals,
|
|
177
|
+
conflicts,
|
|
151
178
|
blockers,
|
|
152
179
|
};
|
|
153
180
|
}
|
|
181
|
+
function hashFile(filePath) {
|
|
182
|
+
try {
|
|
183
|
+
return createHash("sha256").update(readFileSync(filePath)).digest("hex");
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/** Symbol ids a manifest declares, or null when it does not parse. */
|
|
190
|
+
function manifestSymbolIds(filePath) {
|
|
191
|
+
try {
|
|
192
|
+
const parsed = parseYaml(readFileSync(filePath, "utf8"));
|
|
193
|
+
if (!Array.isArray(parsed?.symbols))
|
|
194
|
+
return null;
|
|
195
|
+
return new Set(parsed.symbols.flatMap((symbol) => symbol !== null &&
|
|
196
|
+
typeof symbol === "object" &&
|
|
197
|
+
typeof symbol.id === "string"
|
|
198
|
+
? [symbol.id]
|
|
199
|
+
: []));
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* How a move whose destination already exists is resolved: null when the
|
|
207
|
+
* legacy copy is identical (it is removed), otherwise the conflict and the
|
|
208
|
+
* resolution Kibi can tell.
|
|
209
|
+
*/
|
|
210
|
+
// implements REQ-cli-schema-migration
|
|
211
|
+
function classifyExistingDestination(cwd, move) {
|
|
212
|
+
const from = path.resolve(cwd, move.from);
|
|
213
|
+
const to = path.resolve(cwd, move.to);
|
|
214
|
+
const legacyHash = hashFile(from);
|
|
215
|
+
if (legacyHash !== null && legacyHash === hashFile(to))
|
|
216
|
+
return null;
|
|
217
|
+
if (move.to === CANONICAL_ENTITY_PATHS.symbols) {
|
|
218
|
+
const legacyIds = manifestSymbolIds(from);
|
|
219
|
+
const currentIds = manifestSymbolIds(to);
|
|
220
|
+
if (legacyIds !== null &&
|
|
221
|
+
currentIds !== null &&
|
|
222
|
+
[...legacyIds].every((id) => currentIds.has(id))) {
|
|
223
|
+
return {
|
|
224
|
+
from: move.from,
|
|
225
|
+
to: move.to,
|
|
226
|
+
kind: "stale_subset",
|
|
227
|
+
resolution: `The legacy copy ${move.from} is a stale subset of ${move.to}: every symbol it declares (${legacyIds.size}) is already there. Remove it (git rm ${move.from}); Kibi removes only a copy identical to its destination.`,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (move.to === KB_PATHS.symbolCoordinates) {
|
|
232
|
+
return {
|
|
233
|
+
from: move.from,
|
|
234
|
+
to: move.to,
|
|
235
|
+
kind: "generated",
|
|
236
|
+
resolution: `${move.from} is a generated coordinates file and ${move.to} already exists. Keep ${move.to} and remove the legacy copy (git rm ${move.from}); kibi sync --refresh-symbol-coordinates regenerates coordinates.`,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
from: move.from,
|
|
241
|
+
to: move.to,
|
|
242
|
+
kind: "conflict",
|
|
243
|
+
resolution: `Destination ${move.to} already exists with different content than ${move.from}. Compare them and keep the current one: replace ${move.to} when the legacy copy is newer, or remove (git rm) ${move.from} when it is a stale duplicate.`,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/** The parent folders of the lane roots a legacy config names explicitly. */
|
|
247
|
+
function configuredLaneParents(config) {
|
|
248
|
+
const parents = new Set();
|
|
249
|
+
for (const key of Object.keys(LANE_BY_CONFIG_KEY)) {
|
|
250
|
+
const value = config?.paths?.[key];
|
|
251
|
+
if (typeof value !== "string" || value.trim().length === 0)
|
|
252
|
+
continue;
|
|
253
|
+
const { root } = normalizeLegacySource(value.trim());
|
|
254
|
+
const parent = path.posix.dirname(root.replace(/^\.\//, ""));
|
|
255
|
+
if (parent !== "." && parent !== "")
|
|
256
|
+
parents.add(parent);
|
|
257
|
+
}
|
|
258
|
+
return [...parents].sort();
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* The legacy symbols manifest to move. A configured path wins. Without one,
|
|
262
|
+
* the historical `documentation/symbols.yaml` default applies only when the
|
|
263
|
+
* config does not place its lanes under another folder: there the manifest
|
|
264
|
+
* sits beside the lanes, and a `documentation/` copy may be a stale leftover.
|
|
265
|
+
* When Kibi cannot tell which file is meant, it moves none and reports the
|
|
266
|
+
* ambiguity instead.
|
|
267
|
+
*/
|
|
268
|
+
// implements REQ-cli-schema-migration
|
|
269
|
+
function resolveLegacySymbolsSource(cwd, config, resolved) {
|
|
270
|
+
const configured = config?.paths?.symbols ?? config?.symbolsManifest;
|
|
271
|
+
if (typeof configured === "string" && configured.trim().length > 0)
|
|
272
|
+
return { path: resolved };
|
|
273
|
+
const parents = configuredLaneParents(config).filter((parent) => parent !== path.posix.dirname(LEGACY_DEFAULT_ENTITY_PATHS.symbols));
|
|
274
|
+
if (parents.length === 0)
|
|
275
|
+
return { path: resolved };
|
|
276
|
+
const candidates = parents
|
|
277
|
+
.map((parent) => `${parent}/symbols.yaml`)
|
|
278
|
+
.filter((candidate) => existsSync(path.resolve(cwd, candidate)));
|
|
279
|
+
const fallback = LEGACY_DEFAULT_ENTITY_PATHS.symbols;
|
|
280
|
+
const fallbackExists = existsSync(path.resolve(cwd, fallback));
|
|
281
|
+
if (candidates.length === 1 && !fallbackExists)
|
|
282
|
+
return { path: candidates[0] ?? null };
|
|
283
|
+
if (candidates.length === 0 && !fallbackExists)
|
|
284
|
+
return { path: null };
|
|
285
|
+
const all = [...candidates, ...(fallbackExists ? [fallback] : [])];
|
|
286
|
+
return {
|
|
287
|
+
path: null,
|
|
288
|
+
conflict: {
|
|
289
|
+
from: all.join(", "),
|
|
290
|
+
to: CANONICAL_ENTITY_PATHS.symbols,
|
|
291
|
+
kind: "ambiguous",
|
|
292
|
+
resolution: `The legacy .kb/config.json places its lanes under ${parents.join(", ")} but names no symbols manifest, and ${all.length === 1 ? `${all[0]} exists outside that folder` : `${all.join(" and ")} all exist`}; Kibi will not guess which one is current. Set paths.symbols in .kb/config.json to the current manifest, or remove (git rm) the stale one, then run kibi migrate --yes.`,
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
}
|
|
154
296
|
/**
|
|
155
297
|
* Apply the legacy storage migration. Moves every planned file, writes the
|
|
156
298
|
* canonical lifecycle manifest, retires the legacy config, and prunes empty
|
|
@@ -160,6 +302,18 @@ export function applyLegacyStorageMigration(cwd, plan) {
|
|
|
160
302
|
if (plan.blockers.length > 0) {
|
|
161
303
|
throw new Error(`Legacy storage migration is blocked: ${plan.blockers.join(" ")}`);
|
|
162
304
|
}
|
|
305
|
+
// A legacy copy identical to its destination carries nothing the
|
|
306
|
+
// destination lacks; it is removed rather than left to block a later run.
|
|
307
|
+
const removedFiles = [];
|
|
308
|
+
for (const removal of plan.removals) {
|
|
309
|
+
const absolute = path.resolve(cwd, removal.path);
|
|
310
|
+
if (!existsSync(absolute))
|
|
311
|
+
continue;
|
|
312
|
+
if (hashFile(absolute) !== hashFile(path.resolve(cwd, removal.duplicateOf)))
|
|
313
|
+
throw new Error(`Legacy storage migration is blocked: ${removal.path} changed since planning and no longer matches ${removal.duplicateOf}.`);
|
|
314
|
+
rmSync(absolute);
|
|
315
|
+
removedFiles.push(removal.path);
|
|
316
|
+
}
|
|
163
317
|
const movedFiles = [];
|
|
164
318
|
for (const move of plan.moves) {
|
|
165
319
|
const sourceAbsolute = path.resolve(cwd, move.from);
|
|
@@ -196,9 +350,12 @@ export function applyLegacyStorageMigration(cwd, plan) {
|
|
|
196
350
|
}
|
|
197
351
|
pruneEmptyLegacyDirs(cwd, plan);
|
|
198
352
|
rewritePendingSourceReceiptPaths(cwd, plan.moves);
|
|
353
|
+
const untrackedMovedFiles = writeMovedSourceReceipts(cwd, plan.moves);
|
|
199
354
|
updateGitIgnore(cwd);
|
|
200
355
|
return {
|
|
201
356
|
movedFiles,
|
|
357
|
+
removedFiles,
|
|
358
|
+
untrackedMovedFiles,
|
|
202
359
|
retiredLegacyConfig,
|
|
203
360
|
manifestPath: path.relative(cwd, manifestPath).replaceAll(path.sep, "/"),
|
|
204
361
|
schemaVersion: manifest.schemaVersion,
|
|
@@ -207,8 +364,11 @@ export function applyLegacyStorageMigration(cwd, plan) {
|
|
|
207
364
|
}
|
|
208
365
|
function pruneEmptyLegacyDirs(cwd, plan) {
|
|
209
366
|
const roots = new Set();
|
|
210
|
-
for (const
|
|
211
|
-
|
|
367
|
+
for (const from of [
|
|
368
|
+
...plan.moves.map((move) => move.from),
|
|
369
|
+
...plan.removals.map((removal) => removal.path),
|
|
370
|
+
]) {
|
|
371
|
+
const absolute = path.resolve(cwd, from);
|
|
212
372
|
let dir = path.dirname(absolute);
|
|
213
373
|
while (dir.startsWith(path.resolve(cwd)) &&
|
|
214
374
|
dir !== path.resolve(cwd) &&
|
|
@@ -270,6 +430,54 @@ export function rewritePendingSourceReceiptPaths(cwd, moves) {
|
|
|
270
430
|
}
|
|
271
431
|
return rewritten;
|
|
272
432
|
}
|
|
433
|
+
/** Workspace-relative paths Git tracks, or null outside a repository. */
|
|
434
|
+
function gitTrackedFiles(cwd) {
|
|
435
|
+
try {
|
|
436
|
+
return new Set(execFileSync("git", ["-C", cwd, "ls-files", "--cached", "-z"], {
|
|
437
|
+
encoding: "utf8",
|
|
438
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
439
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
440
|
+
timeout: 30_000,
|
|
441
|
+
})
|
|
442
|
+
.split("\0")
|
|
443
|
+
.filter((file) => file.length > 0)
|
|
444
|
+
.map((file) => file.replaceAll("\\", "/")));
|
|
445
|
+
}
|
|
446
|
+
catch {
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Bind every moved file Git does not track to its bytes with a
|
|
452
|
+
* pending-source receipt, the record kb_upsert writes for the files it
|
|
453
|
+
* creates. `kibi sync` compiles tracked sources plus receipted ones, so the
|
|
454
|
+
* moved knowledge compiles at once; Kibi never stages anything itself, and
|
|
455
|
+
* each receipt retires when Git starts tracking its file. Later migration
|
|
456
|
+
* rewrites rebind the receipt they find. Covers files a previous,
|
|
457
|
+
* interrupted run moved too. Returns the untracked destinations.
|
|
458
|
+
*/
|
|
459
|
+
// implements REQ-cli-schema-migration, REQ-core-atomic-upsert-persistence
|
|
460
|
+
export function writeMovedSourceReceipts(cwd, moves) {
|
|
461
|
+
const tracked = gitTrackedFiles(cwd);
|
|
462
|
+
if (tracked === null)
|
|
463
|
+
return [];
|
|
464
|
+
const untracked = [];
|
|
465
|
+
for (const move of moves) {
|
|
466
|
+
if (tracked.has(move.to))
|
|
467
|
+
continue;
|
|
468
|
+
const absolute = path.resolve(cwd, move.to);
|
|
469
|
+
let bytes;
|
|
470
|
+
try {
|
|
471
|
+
bytes = readFileSync(absolute);
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
writePendingSourceReceipt(cwd, move.to, createHash("sha256").update(bytes).digest("hex"));
|
|
477
|
+
untracked.push(move.to);
|
|
478
|
+
}
|
|
479
|
+
return untracked;
|
|
480
|
+
}
|
|
273
481
|
// implements REQ-cli-schema-migration
|
|
274
482
|
function isEmptySymbolsStub(filePath) {
|
|
275
483
|
try {
|
|
@@ -293,6 +501,8 @@ function sourceSchemaVersion(legacyVersion, existing) {
|
|
|
293
501
|
export function needsLegacyStorageMigration(cwd) {
|
|
294
502
|
const plan = planLegacyStorageMigration(cwd);
|
|
295
503
|
return (plan.moves.length > 0 ||
|
|
504
|
+
plan.removals.length > 0 ||
|
|
505
|
+
plan.blockers.length > 0 ||
|
|
296
506
|
plan.legacyConfig === "present" ||
|
|
297
507
|
plan.legacyConfig === "malformed");
|
|
298
508
|
}
|
|
@@ -11,12 +11,49 @@ interface MigrateOptions {
|
|
|
11
11
|
/** Internal migration-plan executor: create only the canonical baseline manifest. */
|
|
12
12
|
initializeMissingConfig?: boolean;
|
|
13
13
|
}
|
|
14
|
+
export interface MigrationAuditRecord {
|
|
15
|
+
auditVersion: number;
|
|
16
|
+
branch: string;
|
|
17
|
+
manifestPath: string;
|
|
18
|
+
fromVersion: number | null;
|
|
19
|
+
migratedAt: string;
|
|
20
|
+
semanticAdvisorBackfill: "pending" | "completed" | "not_applicable" | null;
|
|
21
|
+
status: "applied";
|
|
22
|
+
symbolGranularityLegacyLinks: number;
|
|
23
|
+
/** Entities carrying origin {kind: migration, ref: 'kibi migrate v5->v6'}. */
|
|
24
|
+
entityOriginBackfill: number;
|
|
25
|
+
/** Superseded requirements this run set to status: closed. */
|
|
26
|
+
supersededRequirementsClosed: number;
|
|
27
|
+
/** Pre-canonical source paths this run pointed at their .kb/ file. */
|
|
28
|
+
sourcePathsRewritten: number;
|
|
29
|
+
/** Authored source values this run removed: they named the entity's own file or nothing. */
|
|
30
|
+
sourcePathsRemoved: number;
|
|
31
|
+
toVersion: number;
|
|
32
|
+
/** The schema warning the KB showed before this run migrated it. */
|
|
33
|
+
preMigrationWarning: string | null;
|
|
34
|
+
steps: readonly string[];
|
|
35
|
+
}
|
|
36
|
+
/** What one applied `kibi migrate --yes` run changed, for plan apply results. */
|
|
37
|
+
export interface MigrationRunSummary {
|
|
38
|
+
/** Workspace-relative path of the audit record this run wrote. */
|
|
39
|
+
auditPath: string;
|
|
40
|
+
audit: MigrationAuditRecord;
|
|
41
|
+
/** Whether this run added the canonical derived entries to .gitignore. */
|
|
42
|
+
gitignoreUpdated: boolean;
|
|
43
|
+
}
|
|
14
44
|
export declare function formatSchemaVersion(rawSchemaVersion: unknown, normalized: number | null): string;
|
|
15
45
|
export declare function warnMigrationRequiredWithoutYes(): {
|
|
16
46
|
exitCode: number;
|
|
17
47
|
};
|
|
18
48
|
export declare function migrateCommand(options?: MigrateOptions): Promise<{
|
|
19
49
|
exitCode: number;
|
|
50
|
+
summary?: MigrationRunSummary;
|
|
20
51
|
}>;
|
|
52
|
+
/**
|
|
53
|
+
* One line naming what an applied migration run changed, read from the audit
|
|
54
|
+
* record it wrote: a count of zero shows a pass that had nothing left to do
|
|
55
|
+
* (for example legacy links an earlier plan action already marked).
|
|
56
|
+
*/
|
|
57
|
+
export declare function describeMigrationRun(summary: MigrationRunSummary): string;
|
|
21
58
|
export {};
|
|
22
59
|
//# sourceMappingURL=migrate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAmEA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,uFAAuF;IACvF,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qFAAqF;IACrF,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,uBAAuB,EAAE,SAAS,GAAG,WAAW,GAAG,gBAAgB,GAAG,IAAI,CAAC;IAC3E,MAAM,EAAE,SAAS,CAAC;IAClB,4BAA4B,EAAE,MAAM,CAAC;IACrC,8EAA8E;IAC9E,oBAAoB,EAAE,MAAM,CAAC;IAC7B,8DAA8D;IAC9D,4BAA4B,EAAE,MAAM,CAAC;IACrC,sEAAsE;IACtE,oBAAoB,EAAE,MAAM,CAAC;IAC7B,4FAA4F;IAC5F,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,oBAAoB,CAAC;IAC5B,0EAA0E;IAC1E,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAyID,wBAAgB,mBAAmB,CACjC,gBAAgB,EAAE,OAAO,EACzB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,MAAM,CAUR;AAsOD,wBAAgB,+BAA+B,IAAI;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAKtE;AAGD,wBAAsB,cAAc,CAClC,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAgZ9D;AAED;;;;GAIG;AAEH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAGzE"}
|