kibi-cli 0.7.0 → 0.10.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/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +25 -1
- package/dist/commands/migrate.d.ts +9 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +183 -0
- package/dist/commands/sync/staging.d.ts +3 -0
- package/dist/commands/sync/staging.d.ts.map +1 -1
- package/dist/commands/sync/staging.js +58 -1
- package/dist/commands/sync.d.ts +18 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +19 -4
- package/dist/public/check-types.d.ts +3 -1
- package/dist/public/check-types.d.ts.map +1 -1
- package/dist/public/check-types.js +1 -0
- package/dist/public/ignore-policy.d.ts +10 -0
- package/dist/public/ignore-policy.d.ts.map +1 -0
- package/dist/public/ignore-policy.js +219 -0
- package/dist/public/operational-artifacts.d.ts +2 -0
- package/dist/public/operational-artifacts.d.ts.map +1 -0
- package/dist/public/operational-artifacts.js +4 -0
- package/dist/public/schema-version.d.ts +3 -0
- package/dist/public/schema-version.d.ts.map +1 -0
- package/dist/public/schema-version.js +1 -0
- package/dist/search-ranking.d.ts.map +1 -1
- package/dist/search-ranking.js +132 -25
- package/dist/utils/config.d.ts +1 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +35 -22
- package/dist/utils/rule-registry.d.ts.map +1 -1
- package/dist/utils/rule-registry.js +6 -0
- package/dist/utils/schema-version.d.ts +14 -0
- package/dist/utils/schema-version.d.ts.map +1 -0
- package/dist/utils/schema-version.js +59 -0
- package/dist/utils/strict-modeling.d.ts +64 -0
- package/dist/utils/strict-modeling.d.ts.map +1 -0
- package/dist/utils/strict-modeling.js +371 -0
- package/package.json +17 -3
- package/schema/config.json +8 -1
- package/src/public/check-types.ts +15 -1
- package/src/public/ignore-policy.ts +229 -0
- package/src/public/operational-artifacts.ts +5 -0
- package/src/public/schema-version.ts +6 -0
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAuCA,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB"}
|
package/dist/cli.js
CHANGED
|
@@ -25,6 +25,7 @@ import { gapsCommand } from "./commands/gaps.js";
|
|
|
25
25
|
import { gcCommand } from "./commands/gc.js";
|
|
26
26
|
import { graphCommand } from "./commands/graph.js";
|
|
27
27
|
import { initCommand } from "./commands/init.js";
|
|
28
|
+
import { migrateCommand } from "./commands/migrate.js";
|
|
28
29
|
import { queryCommand } from "./commands/query.js";
|
|
29
30
|
import { searchCommand } from "./commands/search.js";
|
|
30
31
|
import { statusCommand } from "./commands/status.js";
|
|
@@ -53,6 +54,14 @@ program
|
|
|
53
54
|
.action(withExitCode(async (options) => {
|
|
54
55
|
return initCommand(options);
|
|
55
56
|
}));
|
|
57
|
+
program
|
|
58
|
+
.command("migrate")
|
|
59
|
+
.description("Migrate .kb/config.json to the latest schema version")
|
|
60
|
+
.option("--dry-run", "Preview migration changes without writing files")
|
|
61
|
+
.option("--yes", "Apply migration changes without prompting")
|
|
62
|
+
.action(withExitCode(async (options) => {
|
|
63
|
+
return migrateCommand(options);
|
|
64
|
+
}));
|
|
56
65
|
program
|
|
57
66
|
.command("sync")
|
|
58
67
|
.description("Sync entities from documents")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAuDA,OAAO,EAGL,KAAK,SAAS,EAEf,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EAAE,SAAS,EAAE,CAAC;AAI1B,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA+GD,wBAAsB,YAAY,CAChC,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAuDA,OAAO,EAGL,KAAK,SAAS,EAEf,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EAAE,SAAS,EAAE,CAAC;AAI1B,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA+GD,wBAAsB,YAAY,CAChC,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA0R/B"}
|
package/dist/commands/check.js
CHANGED
|
@@ -278,6 +278,7 @@ export async function checkCommand(options) {
|
|
|
278
278
|
"domain-contradictions",
|
|
279
279
|
"strict-fact-shape",
|
|
280
280
|
"strict-req-fact-pairing",
|
|
281
|
+
"strict-readiness",
|
|
281
282
|
];
|
|
282
283
|
const canUseAggregated = Array.from(effectiveRules).every((r) => supportedRules.includes(r));
|
|
283
284
|
if (canUseAggregated) {
|
|
@@ -302,6 +303,7 @@ export async function checkCommand(options) {
|
|
|
302
303
|
await runCheck("domain-contradictions", checkDomainContradictions);
|
|
303
304
|
await runCheck("strict-fact-shape", checkStrictFactShape);
|
|
304
305
|
await runCheck("strict-req-fact-pairing", checkStrictReqFactPairing);
|
|
306
|
+
await runCheck("strict-readiness", checkStrictReadiness);
|
|
305
307
|
}
|
|
306
308
|
if (violations.length === 0) {
|
|
307
309
|
console.log("✓ No violations found. KB is valid.");
|
|
@@ -312,6 +314,12 @@ export async function checkCommand(options) {
|
|
|
312
314
|
for (const v of violations) {
|
|
313
315
|
const filename = v.source ? path.basename(v.source, ".md") : v.entityId;
|
|
314
316
|
console.log(`[${v.rule}] ${filename}`);
|
|
317
|
+
if (filename !== v.entityId) {
|
|
318
|
+
console.log(` Entity: ${v.entityId}`);
|
|
319
|
+
}
|
|
320
|
+
if (v.source) {
|
|
321
|
+
console.log(` Source: ${v.source}`);
|
|
322
|
+
}
|
|
315
323
|
console.log(` ${v.description}`);
|
|
316
324
|
if (options.fix && v.suggestion) {
|
|
317
325
|
console.log(` Suggestion: ${v.suggestion}`);
|
|
@@ -620,7 +628,7 @@ async function checkDomainContradictions(prolog) {
|
|
|
620
628
|
violations.push({
|
|
621
629
|
rule: "domain-contradictions",
|
|
622
630
|
entityId: `${reqA}/${reqB}`,
|
|
623
|
-
description: reason
|
|
631
|
+
description: `${reason} [strict-readiness: contradiction-ready]`,
|
|
624
632
|
suggestion: "Supersede one requirement or align both to the same required property",
|
|
625
633
|
});
|
|
626
634
|
}
|
|
@@ -658,6 +666,22 @@ async function checkStrictReqFactPairing(prolog) {
|
|
|
658
666
|
}
|
|
659
667
|
return violations;
|
|
660
668
|
}
|
|
669
|
+
async function checkStrictReadiness(prolog) {
|
|
670
|
+
const violations = [];
|
|
671
|
+
const result = await prolog.query(`findall(violation(Rule, EntityId, Desc, Sugg, Src),
|
|
672
|
+
checks:strict_readiness_violation(violation(Rule, EntityId, Desc, Sugg, Src)),
|
|
673
|
+
Violations)`);
|
|
674
|
+
if (!result.success || !result.bindings.Violations) {
|
|
675
|
+
return violations;
|
|
676
|
+
}
|
|
677
|
+
const violationsStr = result.bindings.Violations;
|
|
678
|
+
if (violationsStr && violationsStr !== "[]") {
|
|
679
|
+
for (const v of parseViolationRows(violationsStr)) {
|
|
680
|
+
violations.push(v);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return violations;
|
|
684
|
+
}
|
|
661
685
|
async function checkSymbolCoverage(prolog) {
|
|
662
686
|
const violations = [];
|
|
663
687
|
const uncoveredResult = await prolog.query("setof(Symbol, symbol_no_req_coverage(Symbol, _), Symbols)");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAkCA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AA6ID,wBAAsB,cAAc,CAClC,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAwG/B"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Kibi — repo-local, per-branch, queryable long-term memory for software projects
|
|
3
|
+
* Copyright (C) 2026 Piotr Franczyk
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
import { resolveActiveBranch } from "../utils/branch-resolver.js";
|
|
21
|
+
import { LATEST_KB_SCHEMA_VERSION, getSchemaVersionStatus, normalizeSchemaVersion, } from "../utils/schema-version.js";
|
|
22
|
+
const MIGRATION_AUDIT_VERSION = 1;
|
|
23
|
+
function printWarning(message) {
|
|
24
|
+
console.log(`Warning: ${message}`);
|
|
25
|
+
}
|
|
26
|
+
function toRelativePath(cwd, filePath) {
|
|
27
|
+
const relativePath = path.relative(cwd, filePath);
|
|
28
|
+
return relativePath.length > 0 ? relativePath : path.basename(filePath);
|
|
29
|
+
}
|
|
30
|
+
function resolveMigrationBranch(cwd) {
|
|
31
|
+
const result = resolveActiveBranch(cwd);
|
|
32
|
+
if ("error" in result) {
|
|
33
|
+
const isNonGitContext = result.code === "NOT_A_GIT_REPO" || result.code === "GIT_NOT_AVAILABLE";
|
|
34
|
+
if (isNonGitContext) {
|
|
35
|
+
return {
|
|
36
|
+
branch: "main",
|
|
37
|
+
warnings: [
|
|
38
|
+
"Not in a git repository; using 'main' for migration audit metadata.",
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
error: `Failed to resolve active branch: ${result.error}`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
branch: result.branch,
|
|
48
|
+
warnings: [],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function loadRawConfigDocument(cwd) {
|
|
52
|
+
const kbDir = path.join(cwd, ".kb");
|
|
53
|
+
const configPath = path.join(kbDir, "config.json");
|
|
54
|
+
if (!existsSync(kbDir)) {
|
|
55
|
+
return {
|
|
56
|
+
error: "Missing .kb/ directory. Run 'kibi init' before 'kibi migrate'.",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (!existsSync(configPath)) {
|
|
60
|
+
return {
|
|
61
|
+
error: "Missing .kb/config.json. Run 'kibi init' to create a baseline config before migrating.",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const parsed = JSON.parse(readFileSync(configPath, "utf8"));
|
|
66
|
+
if (parsed === null || Array.isArray(parsed) || typeof parsed !== "object") {
|
|
67
|
+
return {
|
|
68
|
+
error: ".kb/config.json must contain a JSON object. Fix the file and retry 'kibi migrate'.",
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
config: parsed,
|
|
73
|
+
configPath,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
78
|
+
return {
|
|
79
|
+
error: `Invalid .kb/config.json: ${message}. Fix the JSON or re-run 'kibi init'.`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function writeJsonAtomically(filePath, value) {
|
|
84
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
85
|
+
const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
86
|
+
writeFileSync(tempPath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
87
|
+
renameSync(tempPath, filePath);
|
|
88
|
+
}
|
|
89
|
+
function formatSchemaVersion(rawSchemaVersion, normalized) {
|
|
90
|
+
if (normalized === null) {
|
|
91
|
+
if (rawSchemaVersion === undefined) {
|
|
92
|
+
return "missing";
|
|
93
|
+
}
|
|
94
|
+
return `invalid (${JSON.stringify(rawSchemaVersion)})`;
|
|
95
|
+
}
|
|
96
|
+
return String(normalized);
|
|
97
|
+
}
|
|
98
|
+
function buildMigrationAuditRecord(args) {
|
|
99
|
+
return {
|
|
100
|
+
auditVersion: MIGRATION_AUDIT_VERSION,
|
|
101
|
+
branch: args.branch,
|
|
102
|
+
configPath: args.configPath,
|
|
103
|
+
fromVersion: args.fromVersion,
|
|
104
|
+
migratedAt: args.migratedAt,
|
|
105
|
+
status: "applied",
|
|
106
|
+
toVersion: LATEST_KB_SCHEMA_VERSION,
|
|
107
|
+
warning: args.warning,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
// implements REQ-003
|
|
111
|
+
export async function migrateCommand(options = {}) {
|
|
112
|
+
const cwd = process.cwd();
|
|
113
|
+
const branchResult = resolveMigrationBranch(cwd);
|
|
114
|
+
if ("error" in branchResult) {
|
|
115
|
+
console.error(branchResult.error);
|
|
116
|
+
return { exitCode: 1 };
|
|
117
|
+
}
|
|
118
|
+
const configResult = loadRawConfigDocument(cwd);
|
|
119
|
+
if ("error" in configResult) {
|
|
120
|
+
console.error(configResult.error);
|
|
121
|
+
return { exitCode: 1 };
|
|
122
|
+
}
|
|
123
|
+
const { branch, warnings: branchWarnings } = branchResult;
|
|
124
|
+
const { config, configPath } = configResult;
|
|
125
|
+
const configStatus = getSchemaVersionStatus(config);
|
|
126
|
+
const normalizedVersion = normalizeSchemaVersion(config.schemaVersion);
|
|
127
|
+
const rawSchemaVersion = config.schemaVersion;
|
|
128
|
+
const needsCanonicalSchemaWrite = normalizedVersion === LATEST_KB_SCHEMA_VERSION &&
|
|
129
|
+
rawSchemaVersion !== undefined &&
|
|
130
|
+
rawSchemaVersion !== LATEST_KB_SCHEMA_VERSION;
|
|
131
|
+
const migrationWarning = needsCanonicalSchemaWrite
|
|
132
|
+
? "KB config schemaVersion should be normalized to the latest numeric version."
|
|
133
|
+
: configStatus.warning;
|
|
134
|
+
const warnings = [...branchWarnings, ...(migrationWarning ? [migrationWarning] : [])];
|
|
135
|
+
const auditPath = path.join(cwd, ".kb", "migrations", `${branch}.json`);
|
|
136
|
+
const configPathRelative = toRelativePath(cwd, configPath);
|
|
137
|
+
const auditPathRelative = toRelativePath(cwd, auditPath);
|
|
138
|
+
for (const warning of warnings) {
|
|
139
|
+
printWarning(warning);
|
|
140
|
+
}
|
|
141
|
+
if (configStatus.currentVersion !== null &&
|
|
142
|
+
configStatus.currentVersion > configStatus.latestVersion) {
|
|
143
|
+
console.error(`Unsupported schemaVersion ${configStatus.currentVersion}. Upgrade kibi-cli before migrating this KB.`);
|
|
144
|
+
return { exitCode: 1 };
|
|
145
|
+
}
|
|
146
|
+
if (!configStatus.needsMigration && !needsCanonicalSchemaWrite) {
|
|
147
|
+
console.log(`No migration needed: ${configPathRelative} is already at schemaVersion ${LATEST_KB_SCHEMA_VERSION}.`);
|
|
148
|
+
if (existsSync(auditPath)) {
|
|
149
|
+
console.log(`Existing migration audit metadata: ${auditPathRelative}`);
|
|
150
|
+
}
|
|
151
|
+
return { exitCode: 0 };
|
|
152
|
+
}
|
|
153
|
+
const fromVersionLabel = formatSchemaVersion(rawSchemaVersion, normalizedVersion);
|
|
154
|
+
if (options.dryRun) {
|
|
155
|
+
console.log(`dry run: would migrate ${configPathRelative} schemaVersion from ${fromVersionLabel} to ${LATEST_KB_SCHEMA_VERSION}.`);
|
|
156
|
+
console.log(`dry run: would write migration audit metadata to ${auditPathRelative}.`);
|
|
157
|
+
console.log("Re-run with --yes to apply these changes.");
|
|
158
|
+
return { exitCode: 0 };
|
|
159
|
+
}
|
|
160
|
+
if (!options.yes) {
|
|
161
|
+
printWarning(`Migration required for ${configPathRelative}.`);
|
|
162
|
+
console.log("No changes applied.");
|
|
163
|
+
console.log("Use --dry-run to preview or --yes to apply the migration.");
|
|
164
|
+
return { exitCode: 0 };
|
|
165
|
+
}
|
|
166
|
+
const nextConfig = {
|
|
167
|
+
...config,
|
|
168
|
+
schemaVersion: LATEST_KB_SCHEMA_VERSION,
|
|
169
|
+
};
|
|
170
|
+
const migratedAt = new Date().toISOString();
|
|
171
|
+
writeJsonAtomically(configPath, nextConfig);
|
|
172
|
+
writeJsonAtomically(auditPath, buildMigrationAuditRecord({
|
|
173
|
+
branch,
|
|
174
|
+
configPath: configPathRelative,
|
|
175
|
+
fromVersion: configStatus.currentVersion,
|
|
176
|
+
migratedAt,
|
|
177
|
+
warning: migrationWarning,
|
|
178
|
+
}));
|
|
179
|
+
console.log(`Migrated ${configPathRelative} schemaVersion from ${fromVersionLabel} to ${LATEST_KB_SCHEMA_VERSION}.`);
|
|
180
|
+
console.log(`Wrote migration audit metadata to ${auditPathRelative}.`);
|
|
181
|
+
console.log("Migration complete. Future 'kibi migrate' runs will be a no-op.");
|
|
182
|
+
return { exitCode: 0 };
|
|
183
|
+
}
|
|
@@ -8,11 +8,14 @@ interface StagingDeps {
|
|
|
8
8
|
cwd: () => string;
|
|
9
9
|
existsSync: typeof existsSync;
|
|
10
10
|
fg: typeof fg;
|
|
11
|
+
isProcessAlive: (pid: number) => boolean;
|
|
11
12
|
mkdirSync: typeof mkdirSync;
|
|
12
13
|
moduleDir: string;
|
|
13
14
|
renameSync: typeof renameSync;
|
|
14
15
|
rmSync: typeof rmSync;
|
|
15
16
|
}
|
|
17
|
+
export declare function createUniqueStagingPath(currentBranch: string, rootDir: string, pid?: number, now?: number): string;
|
|
18
|
+
export declare function cleanupAbandonedStagingDirectories(stagingPath: string, deps?: Partial<StagingDeps>): Promise<void>;
|
|
16
19
|
export declare function prepareStagingEnvironment(stagingPath: string, livePath: string, rebuild: boolean, deps?: Partial<StagingDeps>): Promise<void>;
|
|
17
20
|
export declare function atomicPublish(stagingPath: string, livePath: string, deps?: Partial<StagingDeps>): void;
|
|
18
21
|
export declare function cleanupStaging(stagingPath: string, deps?: Partial<StagingDeps>): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staging.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/staging.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,MAAM,WAAW,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAEnE,UAAU,WAAW;IACnB,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,EAAE,EAAE,OAAO,EAAE,CAAC;IACd,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,MAAM,EAAE,OAAO,MAAM,CAAC;CACvB;
|
|
1
|
+
{"version":3,"file":"staging.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/staging.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,MAAM,WAAW,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAEnE,UAAU,WAAW;IACnB,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,EAAE,EAAE,OAAO,EAAE,CAAC;IACd,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACzC,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,MAAM,EAAE,OAAO,MAAM,CAAC;CACvB;AA8BD,wBAAgB,uBAAuB,CACrC,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,GAAG,SAAc,EACjB,GAAG,SAAa,GACf,MAAM,CAOR;AAGD,wBAAsB,kCAAkC,CACtD,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC1B,OAAO,CAAC,IAAI,CAAC,CAkDf;AAED,wBAAsB,yBAAyB,CAE7C,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC1B,OAAO,CAAC,IAAI,CAAC,CAaf;AA0CD,wBAAgB,aAAa,CAE3B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC1B,IAAI,CAeN;AAED,wBAAgB,cAAc,CAE5B,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC1B,IAAI,CAKN"}
|
|
@@ -27,6 +27,17 @@ function resolveDeps(overrides) {
|
|
|
27
27
|
cwd: () => process.cwd(),
|
|
28
28
|
existsSync,
|
|
29
29
|
fg,
|
|
30
|
+
isProcessAlive: (pid) => {
|
|
31
|
+
try {
|
|
32
|
+
process.kill(pid, 0);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return !(error instanceof Error &&
|
|
37
|
+
"code" in error &&
|
|
38
|
+
error.code === "ESRCH");
|
|
39
|
+
}
|
|
40
|
+
},
|
|
30
41
|
mkdirSync,
|
|
31
42
|
moduleDir: import.meta.dirname,
|
|
32
43
|
renameSync,
|
|
@@ -34,11 +45,54 @@ function resolveDeps(overrides) {
|
|
|
34
45
|
...overrides,
|
|
35
46
|
};
|
|
36
47
|
}
|
|
48
|
+
// implements REQ-003
|
|
49
|
+
export function createUniqueStagingPath(currentBranch, rootDir, pid = process.pid, now = Date.now()) {
|
|
50
|
+
return path.join(rootDir, ".kb", "branches", `${currentBranch}.staging.${pid}.${now}`);
|
|
51
|
+
}
|
|
52
|
+
// implements REQ-003
|
|
53
|
+
export async function cleanupAbandonedStagingDirectories(stagingPath, deps) {
|
|
54
|
+
const resolved = resolveDeps(deps);
|
|
55
|
+
const stagingDir = path.dirname(stagingPath);
|
|
56
|
+
const stagingBase = path.basename(stagingPath);
|
|
57
|
+
const match = /^(?<branch>.+)\.staging\.(?<pid>\d+)\.(?<timestamp>\d+)$/.exec(stagingBase);
|
|
58
|
+
if (!match?.groups) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const branch = match.groups.branch;
|
|
62
|
+
if (!branch) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const candidates = await resolved.fg(`${branch}.staging.*`, {
|
|
66
|
+
cwd: stagingDir,
|
|
67
|
+
absolute: true,
|
|
68
|
+
onlyDirectories: true,
|
|
69
|
+
suppressErrors: true,
|
|
70
|
+
});
|
|
71
|
+
for (const candidate of candidates) {
|
|
72
|
+
if (candidate === stagingPath) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const candidateBase = path.basename(candidate);
|
|
76
|
+
const candidateMatch = new RegExp(`^${escapeRegex(branch)}\\.staging\\.(\\d+)\\.(\\d+)$`).exec(candidateBase);
|
|
77
|
+
if (!candidateMatch) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const candidatePidText = candidateMatch[1];
|
|
81
|
+
if (!candidatePidText) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const candidatePid = Number.parseInt(candidatePidText, 10);
|
|
85
|
+
if (!Number.isFinite(candidatePid) || resolved.isProcessAlive(candidatePid)) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
cleanupStaging(candidate, resolved);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
37
91
|
export async function prepareStagingEnvironment(
|
|
38
92
|
// implements REQ-003
|
|
39
93
|
stagingPath, livePath, rebuild, deps) {
|
|
40
94
|
const resolved = resolveDeps(deps);
|
|
41
|
-
|
|
95
|
+
await cleanupAbandonedStagingDirectories(stagingPath, resolved);
|
|
42
96
|
cleanupStaging(stagingPath, resolved);
|
|
43
97
|
resolved.mkdirSync(stagingPath, { recursive: true });
|
|
44
98
|
if (!rebuild && resolved.existsSync(livePath)) {
|
|
@@ -107,3 +161,6 @@ stagingPath, deps) {
|
|
|
107
161
|
resolved.rmSync(stagingPath, { recursive: true, force: true });
|
|
108
162
|
}
|
|
109
163
|
}
|
|
164
|
+
function escapeRegex(value) {
|
|
165
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
166
|
+
}
|
package/dist/commands/sync.d.ts
CHANGED
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
import type { SyncSummary } from "../diagnostics.js";
|
|
2
|
+
import { PrologProcess } from "../prolog.js";
|
|
2
3
|
export declare class SyncError extends Error {
|
|
3
4
|
constructor(message: string);
|
|
4
5
|
}
|
|
5
6
|
export interface SyncResult extends SyncSummary {
|
|
6
7
|
exitCode?: number;
|
|
7
8
|
}
|
|
9
|
+
interface SyncCommandRuntimeContext {
|
|
10
|
+
currentBranch: string;
|
|
11
|
+
livePath: string;
|
|
12
|
+
rebuild: boolean;
|
|
13
|
+
stagingPath: string;
|
|
14
|
+
validateOnly: boolean;
|
|
15
|
+
}
|
|
16
|
+
interface SyncCommandRuntime {
|
|
17
|
+
afterAttach?: (context: SyncCommandRuntimeContext) => Promise<void> | void;
|
|
18
|
+
beforeSave?: (context: SyncCommandRuntimeContext & {
|
|
19
|
+
kbModified: boolean;
|
|
20
|
+
}) => Promise<void> | void;
|
|
21
|
+
createProlog?: (options: {
|
|
22
|
+
timeout: number;
|
|
23
|
+
}) => PrologProcess;
|
|
24
|
+
}
|
|
8
25
|
export declare function syncCommand(options?: {
|
|
9
26
|
validateOnly?: boolean;
|
|
10
27
|
rebuild?: boolean;
|
|
11
|
-
}): Promise<SyncResult>;
|
|
28
|
+
}, runtime?: SyncCommandRuntime): Promise<SyncResult>;
|
|
12
29
|
export { normalizeMarkdownPath } from "./sync/discovery.js";
|
|
13
30
|
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAc,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAc,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAejE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AA4B7C,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI5B;AAGD,MAAM,WAAW,UAAW,SAAQ,WAAW;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,yBAAyB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,kBAAkB;IAC1B,WAAW,CAAC,EAAE,CACZ,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,CACX,OAAO,EAAE,yBAAyB,GAAG;QAAE,UAAU,EAAE,OAAO,CAAA;KAAE,KACzD,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,aAAa,CAAC;CAChE;AAGD,wBAAsB,WAAW,CAC/B,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,EAC3D,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,UAAU,CAAC,CAmbrB;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/commands/sync.js
CHANGED
|
@@ -29,7 +29,7 @@ import { discoverSourceFiles, } from "./sync/discovery.js";
|
|
|
29
29
|
import { processExtractions } from "./sync/extraction.js";
|
|
30
30
|
import { refreshManifestCoordinates } from "./sync/manifest.js";
|
|
31
31
|
import { persistEntities, persistRelationships } from "./sync/persistence.js";
|
|
32
|
-
import { atomicPublish, cleanupStaging, prepareStagingEnvironment, } from "./sync/staging.js";
|
|
32
|
+
import { atomicPublish, cleanupStaging, createUniqueStagingPath, prepareStagingEnvironment, } from "./sync/staging.js";
|
|
33
33
|
export class SyncError extends Error {
|
|
34
34
|
constructor(message) {
|
|
35
35
|
super(message);
|
|
@@ -37,7 +37,7 @@ export class SyncError extends Error {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
// implements REQ-003, REQ-007
|
|
40
|
-
export async function syncCommand(options = {}) {
|
|
40
|
+
export async function syncCommand(options = {}, runtime = {}) {
|
|
41
41
|
const validateOnly = options.validateOnly ?? false;
|
|
42
42
|
const rebuild = options.rebuild ?? false;
|
|
43
43
|
const startTime = Date.now();
|
|
@@ -45,6 +45,7 @@ export async function syncCommand(options = {}) {
|
|
|
45
45
|
const entityCounts = {};
|
|
46
46
|
let published = false;
|
|
47
47
|
let currentBranch;
|
|
48
|
+
let stagingPath;
|
|
48
49
|
const getCurrentCommit = () => {
|
|
49
50
|
try {
|
|
50
51
|
return execSync("git rev-parse HEAD", {
|
|
@@ -199,16 +200,25 @@ export async function syncCommand(options = {}) {
|
|
|
199
200
|
if (!kbExists && !rebuild) {
|
|
200
201
|
diagnostics.push(createKbMissingDiagnostic(currentBranch, livePath));
|
|
201
202
|
}
|
|
202
|
-
|
|
203
|
+
stagingPath = createUniqueStagingPath(currentBranch, process.cwd());
|
|
204
|
+
const runtimeContext = {
|
|
205
|
+
currentBranch,
|
|
206
|
+
livePath,
|
|
207
|
+
rebuild,
|
|
208
|
+
stagingPath,
|
|
209
|
+
validateOnly,
|
|
210
|
+
};
|
|
203
211
|
await prepareStagingEnvironment(stagingPath, livePath, rebuild);
|
|
204
212
|
try {
|
|
205
|
-
const prolog =
|
|
213
|
+
const prolog = runtime.createProlog?.({ timeout: 120000 }) ??
|
|
214
|
+
new PrologProcess({ timeout: 120000 });
|
|
206
215
|
await prolog.start();
|
|
207
216
|
const attachResult = await prolog.query(`kb_attach('${stagingPath}')`);
|
|
208
217
|
if (!attachResult.success) {
|
|
209
218
|
await prolog.terminate();
|
|
210
219
|
throw new SyncError(`Failed to attach to staging KB: ${attachResult.error || "Unknown error"}`);
|
|
211
220
|
}
|
|
221
|
+
await runtime.afterAttach?.(runtimeContext);
|
|
212
222
|
const entityIds = new Set();
|
|
213
223
|
for (const { entity } of results) {
|
|
214
224
|
entityCounts[entity.type] = (entityCounts[entity.type] || 0) + 1;
|
|
@@ -263,6 +273,7 @@ export async function syncCommand(options = {}) {
|
|
|
263
273
|
if (kbModified) {
|
|
264
274
|
prolog.invalidateCache();
|
|
265
275
|
}
|
|
276
|
+
await runtime.beforeSave?.({ ...runtimeContext, kbModified });
|
|
266
277
|
const saveResult = await prolog.query("kb_save");
|
|
267
278
|
if (!saveResult.success) {
|
|
268
279
|
throw new SyncError(`Failed to save staging KB: ${saveResult.error || "Unknown error"}`);
|
|
@@ -270,6 +281,7 @@ export async function syncCommand(options = {}) {
|
|
|
270
281
|
await prolog.query("kb_detach");
|
|
271
282
|
await prolog.terminate();
|
|
272
283
|
atomicPublish(stagingPath, livePath);
|
|
284
|
+
cleanupStaging(stagingPath);
|
|
273
285
|
const evictedHashes = {};
|
|
274
286
|
const evictedSeenAt = {};
|
|
275
287
|
for (const [key, hash] of Object.entries(nextHashes)) {
|
|
@@ -312,6 +324,9 @@ export async function syncCommand(options = {}) {
|
|
|
312
324
|
}
|
|
313
325
|
}
|
|
314
326
|
catch (error) {
|
|
327
|
+
if (stagingPath) {
|
|
328
|
+
cleanupStaging(stagingPath);
|
|
329
|
+
}
|
|
315
330
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
316
331
|
console.error(`Error: ${errorMessage}`);
|
|
317
332
|
const commit = getCurrentCommit();
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Public re-export barrel for shared check types.
|
|
2
|
+
* Public re-export barrel for shared check types and MCP-consumed modeling helpers.
|
|
3
3
|
* Import from "kibi-cli/public/check-types" in MCP or external consumers.
|
|
4
4
|
*/
|
|
5
5
|
export type { ChecksConfig, RuleDefinition, SymbolTraceabilityOptions, Violation, } from "../utils/rule-registry.js";
|
|
6
|
+
export type { SemanticClaim, StableRequirementIds, StrictModelInput, StrictWriteSet, } from "../utils/strict-modeling.js";
|
|
6
7
|
export { DEFAULT_CHECKS_CONFIG, RULE_NAMES, RULES, getEffectiveRules, mergeChecksConfig, validateRuleName, } from "../utils/rule-registry.js";
|
|
8
|
+
export { buildStableRequirementIds, buildStrictWriteSet, modelRequirementClaims, normalizePropertyKey, normalizeSubjectKey, } from "../utils/strict-modeling.js";
|
|
7
9
|
//# sourceMappingURL=check-types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-types.d.ts","sourceRoot":"","sources":["../../src/public/check-types.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,YAAY,EACV,YAAY,EACZ,cAAc,EACd,yBAAyB,EACzB,SAAS,GACV,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"check-types.d.ts","sourceRoot":"","sources":["../../src/public/check-types.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,YAAY,EACV,YAAY,EACZ,cAAc,EACd,yBAAyB,EACzB,SAAS,GACV,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,GACf,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,qBAAqB,EACrB,UAAU,EACV,KAAK,EACL,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,6BAA6B,CAAC"}
|
|
@@ -16,3 +16,4 @@
|
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
export { DEFAULT_CHECKS_CONFIG, RULE_NAMES, RULES, getEffectiveRules, mergeChecksConfig, validateRuleName, } from "../utils/rule-registry.js";
|
|
19
|
+
export { buildStableRequirementIds, buildStrictWriteSet, modelRequirementClaims, normalizePropertyKey, normalizeSubjectKey, } from "../utils/strict-modeling.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface IgnorePolicy {
|
|
2
|
+
isIgnored(inputPath: string): boolean;
|
|
3
|
+
getFastGlobIgnoreGlobs(): string[];
|
|
4
|
+
explain(inputPath: string): {
|
|
5
|
+
ignored: boolean;
|
|
6
|
+
reason?: string;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export declare function createRepoIgnorePolicy(workspaceRoot: string): IgnorePolicy;
|
|
10
|
+
//# sourceMappingURL=ignore-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignore-policy.d.ts","sourceRoot":"","sources":["../../src/public/ignore-policy.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IACtC,sBAAsB,IAAI,MAAM,EAAE,CAAC;IACnC,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACnE;AAoBD,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,YAAY,CA8L1E"}
|