guardian-framework 0.1.127 → 0.1.129
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.js
CHANGED
|
@@ -3202,7 +3202,7 @@ __export(exports_package, {
|
|
|
3202
3202
|
bin: () => bin,
|
|
3203
3203
|
author: () => author
|
|
3204
3204
|
});
|
|
3205
|
-
var name = "guardian-framework", version = "0.1.
|
|
3205
|
+
var name = "guardian-framework", version = "0.1.129", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
|
|
3206
3206
|
var init_package = __esm(() => {
|
|
3207
3207
|
exports = {
|
|
3208
3208
|
".": {
|
|
@@ -12178,6 +12178,60 @@ function answerExploration(sessionId, responseJson, options) {
|
|
|
12178
12178
|
warnings
|
|
12179
12179
|
};
|
|
12180
12180
|
}
|
|
12181
|
+
function moduleHasImplementation(projectDir, moduleName) {
|
|
12182
|
+
const sanitized = (moduleName || "").toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
12183
|
+
if (!sanitized)
|
|
12184
|
+
return false;
|
|
12185
|
+
const roots = ["src", "crates", "packages", "internal", "lib", "app", "modules"];
|
|
12186
|
+
const exts = ".*.(java|kt|ts|tsx|js|jsx|rs|py|go)$";
|
|
12187
|
+
for (const root of roots) {
|
|
12188
|
+
const base = path2.join(projectDir, root);
|
|
12189
|
+
if (!fs.existsSync(base))
|
|
12190
|
+
continue;
|
|
12191
|
+
try {
|
|
12192
|
+
const walk = (dir, depth) => {
|
|
12193
|
+
if (depth > 6)
|
|
12194
|
+
return false;
|
|
12195
|
+
let entries;
|
|
12196
|
+
try {
|
|
12197
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
12198
|
+
} catch {
|
|
12199
|
+
return false;
|
|
12200
|
+
}
|
|
12201
|
+
for (const e of entries) {
|
|
12202
|
+
if (e.name === "node_modules" || e.name === "target" || e.name === "dist" || e.name === ".git")
|
|
12203
|
+
continue;
|
|
12204
|
+
const full = path2.join(dir, e.name);
|
|
12205
|
+
if (e.isDirectory()) {
|
|
12206
|
+
if (e.name === sanitized && hasSourceBelow(full, depth))
|
|
12207
|
+
return true;
|
|
12208
|
+
if (walk(full, depth + 1))
|
|
12209
|
+
return true;
|
|
12210
|
+
}
|
|
12211
|
+
}
|
|
12212
|
+
return false;
|
|
12213
|
+
};
|
|
12214
|
+
const hasSourceBelow = (dir, depth) => {
|
|
12215
|
+
let entries;
|
|
12216
|
+
try {
|
|
12217
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
12218
|
+
} catch {
|
|
12219
|
+
return false;
|
|
12220
|
+
}
|
|
12221
|
+
for (const e of entries) {
|
|
12222
|
+
if (e.isFile() && new RegExp(exts).test(e.name) && !/test|spec/i.test(e.name))
|
|
12223
|
+
return true;
|
|
12224
|
+
if (e.isDirectory() && e.name !== "node_modules" && e.name !== "target" && depth < 6 && hasSourceBelow(path2.join(dir, e.name), depth + 1))
|
|
12225
|
+
return true;
|
|
12226
|
+
}
|
|
12227
|
+
return false;
|
|
12228
|
+
};
|
|
12229
|
+
if (walk(base, 0))
|
|
12230
|
+
return true;
|
|
12231
|
+
} catch {}
|
|
12232
|
+
}
|
|
12233
|
+
return false;
|
|
12234
|
+
}
|
|
12181
12235
|
function readExplorationSession(sessionId, projectDir) {
|
|
12182
12236
|
const root = projectDir ?? process.cwd();
|
|
12183
12237
|
const filePath = path2.join(root, ".pi", "domain", "exploration", `${sessionId}.md`);
|
|
@@ -12376,8 +12430,18 @@ function scaffoldFromExploration(sessionId, options) {
|
|
|
12376
12430
|
const depthHeaderNote = bc.subdomainType === undefined || bc.subdomainType === null ? " (unclassified \u2014 defaulted to tactical; re-run exploration with ADR-008 depth)" : "";
|
|
12377
12431
|
const contextEdges = (result.contextMap || []).filter((e) => e.from === bc.name || e.to === bc.name);
|
|
12378
12432
|
const invariants = bc.invariants || [];
|
|
12433
|
+
const force = options?.overwrite === true;
|
|
12379
12434
|
if (!dryRun && fs.existsSync(filePath)) {
|
|
12380
|
-
|
|
12435
|
+
const implemented = moduleHasImplementation(projectDir, moduleName);
|
|
12436
|
+
if (implemented && !force) {
|
|
12437
|
+
warnings.push(`Module is IMPLEMENTED, preserved (use overwrite:true to force): ${fileName}`);
|
|
12438
|
+
continue;
|
|
12439
|
+
}
|
|
12440
|
+
if (implemented && force) {
|
|
12441
|
+
warnings.push(`Overwriting implemented module doc (--force): ${fileName}`);
|
|
12442
|
+
} else {
|
|
12443
|
+
warnings.push(`Refreshing planned module doc (no implementation yet): ${fileName}`);
|
|
12444
|
+
}
|
|
12381
12445
|
}
|
|
12382
12446
|
const aggregateRoots = contextEntities.filter((e) => e.type === "aggregate-root");
|
|
12383
12447
|
const regularEntities = contextEntities.filter((e) => e.type === "entity");
|
|
@@ -13150,7 +13214,7 @@ async function runDomain(targetDir, args, options) {
|
|
|
13150
13214
|
await runExplore(targetDir, options);
|
|
13151
13215
|
break;
|
|
13152
13216
|
case "scaffold":
|
|
13153
|
-
await runScaffold(targetDir, args[1], options);
|
|
13217
|
+
await runScaffold(targetDir, args[1], { ...options, overwrite: args.includes("--force") });
|
|
13154
13218
|
break;
|
|
13155
13219
|
case "answer":
|
|
13156
13220
|
await runAnswer(targetDir, args[1], args[2], options);
|
|
@@ -13257,7 +13321,8 @@ async function runScaffold(targetDir, sessionId, options) {
|
|
|
13257
13321
|
try {
|
|
13258
13322
|
const result = scaffoldFromExploration(sessionId, {
|
|
13259
13323
|
projectDir: targetDir,
|
|
13260
|
-
dryRun: options.dryRun
|
|
13324
|
+
dryRun: options.dryRun,
|
|
13325
|
+
overwrite: options.overwrite
|
|
13261
13326
|
});
|
|
13262
13327
|
if (options.dryRun) {
|
|
13263
13328
|
outro(`Dry-run: would generate ${result.modulesGenerated} module(s)`);
|
package/dist/exports.js
CHANGED
|
@@ -1987,7 +1987,7 @@ __export(exports_package, {
|
|
|
1987
1987
|
bin: () => bin,
|
|
1988
1988
|
author: () => author
|
|
1989
1989
|
});
|
|
1990
|
-
var name = "guardian-framework", version = "0.1.
|
|
1990
|
+
var name = "guardian-framework", version = "0.1.129", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
|
|
1991
1991
|
var init_package = __esm(() => {
|
|
1992
1992
|
exports = {
|
|
1993
1993
|
".": {
|
|
@@ -12502,6 +12502,60 @@ function answerExploration(sessionId, responseJson, options) {
|
|
|
12502
12502
|
warnings
|
|
12503
12503
|
};
|
|
12504
12504
|
}
|
|
12505
|
+
function moduleHasImplementation(projectDir, moduleName) {
|
|
12506
|
+
const sanitized = (moduleName || "").toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
12507
|
+
if (!sanitized)
|
|
12508
|
+
return false;
|
|
12509
|
+
const roots = ["src", "crates", "packages", "internal", "lib", "app", "modules"];
|
|
12510
|
+
const exts = ".*.(java|kt|ts|tsx|js|jsx|rs|py|go)$";
|
|
12511
|
+
for (const root of roots) {
|
|
12512
|
+
const base = path11.join(projectDir, root);
|
|
12513
|
+
if (!fs12.existsSync(base))
|
|
12514
|
+
continue;
|
|
12515
|
+
try {
|
|
12516
|
+
const walk = (dir, depth) => {
|
|
12517
|
+
if (depth > 6)
|
|
12518
|
+
return false;
|
|
12519
|
+
let entries;
|
|
12520
|
+
try {
|
|
12521
|
+
entries = fs12.readdirSync(dir, { withFileTypes: true });
|
|
12522
|
+
} catch {
|
|
12523
|
+
return false;
|
|
12524
|
+
}
|
|
12525
|
+
for (const e of entries) {
|
|
12526
|
+
if (e.name === "node_modules" || e.name === "target" || e.name === "dist" || e.name === ".git")
|
|
12527
|
+
continue;
|
|
12528
|
+
const full = path11.join(dir, e.name);
|
|
12529
|
+
if (e.isDirectory()) {
|
|
12530
|
+
if (e.name === sanitized && hasSourceBelow(full, depth))
|
|
12531
|
+
return true;
|
|
12532
|
+
if (walk(full, depth + 1))
|
|
12533
|
+
return true;
|
|
12534
|
+
}
|
|
12535
|
+
}
|
|
12536
|
+
return false;
|
|
12537
|
+
};
|
|
12538
|
+
const hasSourceBelow = (dir, depth) => {
|
|
12539
|
+
let entries;
|
|
12540
|
+
try {
|
|
12541
|
+
entries = fs12.readdirSync(dir, { withFileTypes: true });
|
|
12542
|
+
} catch {
|
|
12543
|
+
return false;
|
|
12544
|
+
}
|
|
12545
|
+
for (const e of entries) {
|
|
12546
|
+
if (e.isFile() && new RegExp(exts).test(e.name) && !/test|spec/i.test(e.name))
|
|
12547
|
+
return true;
|
|
12548
|
+
if (e.isDirectory() && e.name !== "node_modules" && e.name !== "target" && depth < 6 && hasSourceBelow(path11.join(dir, e.name), depth + 1))
|
|
12549
|
+
return true;
|
|
12550
|
+
}
|
|
12551
|
+
return false;
|
|
12552
|
+
};
|
|
12553
|
+
if (walk(base, 0))
|
|
12554
|
+
return true;
|
|
12555
|
+
} catch {}
|
|
12556
|
+
}
|
|
12557
|
+
return false;
|
|
12558
|
+
}
|
|
12505
12559
|
function readExplorationSession(sessionId, projectDir) {
|
|
12506
12560
|
const root = projectDir ?? process.cwd();
|
|
12507
12561
|
const filePath = path11.join(root, ".pi", "domain", "exploration", `${sessionId}.md`);
|
|
@@ -12700,8 +12754,18 @@ function scaffoldFromExploration(sessionId, options) {
|
|
|
12700
12754
|
const depthHeaderNote = bc.subdomainType === undefined || bc.subdomainType === null ? " (unclassified — defaulted to tactical; re-run exploration with ADR-008 depth)" : "";
|
|
12701
12755
|
const contextEdges = (result.contextMap || []).filter((e) => e.from === bc.name || e.to === bc.name);
|
|
12702
12756
|
const invariants = bc.invariants || [];
|
|
12757
|
+
const force = options?.overwrite === true;
|
|
12703
12758
|
if (!dryRun && fs12.existsSync(filePath)) {
|
|
12704
|
-
|
|
12759
|
+
const implemented = moduleHasImplementation(projectDir, moduleName);
|
|
12760
|
+
if (implemented && !force) {
|
|
12761
|
+
warnings.push(`Module is IMPLEMENTED, preserved (use overwrite:true to force): ${fileName}`);
|
|
12762
|
+
continue;
|
|
12763
|
+
}
|
|
12764
|
+
if (implemented && force) {
|
|
12765
|
+
warnings.push(`Overwriting implemented module doc (--force): ${fileName}`);
|
|
12766
|
+
} else {
|
|
12767
|
+
warnings.push(`Refreshing planned module doc (no implementation yet): ${fileName}`);
|
|
12768
|
+
}
|
|
12705
12769
|
}
|
|
12706
12770
|
const aggregateRoots = contextEntities.filter((e) => e.type === "aggregate-root");
|
|
12707
12771
|
const regularEntities = contextEntities.filter((e) => e.type === "entity");
|
|
@@ -13474,7 +13538,7 @@ async function runDomain(targetDir, args, options) {
|
|
|
13474
13538
|
await runExplore(targetDir, options);
|
|
13475
13539
|
break;
|
|
13476
13540
|
case "scaffold":
|
|
13477
|
-
await runScaffold(targetDir, args[1], options);
|
|
13541
|
+
await runScaffold(targetDir, args[1], { ...options, overwrite: args.includes("--force") });
|
|
13478
13542
|
break;
|
|
13479
13543
|
case "answer":
|
|
13480
13544
|
await runAnswer(targetDir, args[1], args[2], options);
|
|
@@ -13581,7 +13645,8 @@ async function runScaffold(targetDir, sessionId, options) {
|
|
|
13581
13645
|
try {
|
|
13582
13646
|
const result = scaffoldFromExploration(sessionId, {
|
|
13583
13647
|
projectDir: targetDir,
|
|
13584
|
-
dryRun: options.dryRun
|
|
13648
|
+
dryRun: options.dryRun,
|
|
13649
|
+
overwrite: options.overwrite
|
|
13585
13650
|
});
|
|
13586
13651
|
if (options.dryRun) {
|
|
13587
13652
|
outro(`Dry-run: would generate ${result.modulesGenerated} module(s)`);
|
package/package.json
CHANGED
|
@@ -239,6 +239,45 @@ function mergeRawJson(existing: Record<string, unknown>, incoming: Record<string
|
|
|
239
239
|
return merged;
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
// True when a module has real implementation code (not just a planned doc).
|
|
243
|
+
function moduleHasCode(cwd: string, moduleName: string): boolean {
|
|
244
|
+
const sanitized = (moduleName || "").toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
245
|
+
if (!sanitized) return false;
|
|
246
|
+
const roots = ["src", "crates", "packages", "internal", "lib", "app", "modules"];
|
|
247
|
+
const codeFile = (n: string): boolean => /.*\.(java|kt|ts|tsx|js|jsx|rs|py|go)$/.test(n) && !/test|spec/i.test(n);
|
|
248
|
+
const walk = (dir: string, depth: number): boolean => {
|
|
249
|
+
if (depth > 6) return false;
|
|
250
|
+
let entries;
|
|
251
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return false; }
|
|
252
|
+
for (const e of entries) {
|
|
253
|
+
if (["node_modules", "target", "dist", ".git"].includes(e.name)) continue;
|
|
254
|
+
const full = path.join(dir, e.name);
|
|
255
|
+
if (e.isFile() && codeFile(e.name)) return true;
|
|
256
|
+
if (e.isDirectory() && (e.name === sanitized || walk(full, depth + 1))) {
|
|
257
|
+
// entered the module dir: any code file below counts
|
|
258
|
+
if (e.name === sanitized && hasCodeBelow(full, depth)) return true;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return false;
|
|
262
|
+
};
|
|
263
|
+
const hasCodeBelow = (dir: string, depth: number): boolean => {
|
|
264
|
+
let entries;
|
|
265
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return false; }
|
|
266
|
+
for (const e of entries) {
|
|
267
|
+
if (["node_modules", "target", "dist"].includes(e.name)) continue;
|
|
268
|
+
const full = path.join(dir, e.name);
|
|
269
|
+
if (e.isFile() && codeFile(e.name)) return true;
|
|
270
|
+
if (e.isDirectory() && depth < 6 && hasCodeBelow(full, depth + 1)) return true;
|
|
271
|
+
}
|
|
272
|
+
return false;
|
|
273
|
+
};
|
|
274
|
+
for (const r of roots) {
|
|
275
|
+
const base = path.join(cwd, r);
|
|
276
|
+
if (fs.existsSync(base) && walk(base, 0)) return true;
|
|
277
|
+
}
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
|
|
242
281
|
function buildExplorationPrompt(context: string): string {
|
|
243
282
|
return [
|
|
244
283
|
"You are a Domain-Driven Design expert. Analyze the following business",
|
|
@@ -978,10 +1017,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
978
1017
|
// /domain --architect-scaffold <session-id>
|
|
979
1018
|
// /domain --architect-scaffold <session-id>
|
|
980
1019
|
if (trimmed.startsWith("--architect-scaffold")) {
|
|
981
|
-
const
|
|
1020
|
+
const rest = trimmed.slice("--architect-scaffold".length).trim();
|
|
1021
|
+
const forceScaffold = rest.includes("--force");
|
|
1022
|
+
const sessionId = rest.split(/\s+/)[0] || "";
|
|
982
1023
|
if (!sessionId) {
|
|
983
1024
|
ctx.ui.notify(
|
|
984
|
-
"Usage: /domain --architect-scaffold <session-id>",
|
|
1025
|
+
"Usage: /domain --architect-scaffold <session-id> [--force]",
|
|
985
1026
|
"error",
|
|
986
1027
|
);
|
|
987
1028
|
return "(domain command handled)";
|
|
@@ -1056,7 +1097,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1056
1097
|
let scaffoldError = "";
|
|
1057
1098
|
|
|
1058
1099
|
try {
|
|
1059
|
-
const cmd = "guardian-framework domain scaffold " + sessionId + " 2>&1";
|
|
1100
|
+
const cmd = "guardian-framework domain scaffold " + sessionId + (forceScaffold ? " --force" : "") + " 2>&1";
|
|
1060
1101
|
const shellResult = await shellExec(ctx, cmd);
|
|
1061
1102
|
if (shellResult.exitCode === 0) {
|
|
1062
1103
|
const outputLines = shellResult.stdout.split("\n");
|
|
@@ -1079,8 +1120,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
1079
1120
|
ctx.ui.notify("Module scaffold unavailable - agent can create docs directly", "info");
|
|
1080
1121
|
}
|
|
1081
1122
|
|
|
1082
|
-
// Step 2:
|
|
1123
|
+
// Step 2: Refresh ADR-001 only when it does not exist yet (or --force).
|
|
1124
|
+
// Never clobber an existing ADR that documents implemented architecture.
|
|
1083
1125
|
const adrPath = path.join(decisionsDir, "ADR-001-architecture-pattern.md");
|
|
1126
|
+
if (fs.existsSync(adrPath) && !forceScaffold) {
|
|
1127
|
+
ctx.ui.notify("Existing ADR-001 preserved — module docs scaffolded (use --force to refresh ADR)", "info");
|
|
1128
|
+
return "(domain command handled)";
|
|
1129
|
+
}
|
|
1084
1130
|
const bcList = bcNames.length > 0
|
|
1085
1131
|
? bcNames.map(n => " - " + n).join("\n")
|
|
1086
1132
|
: " - (to be defined during architecture planning)";
|
|
@@ -1229,9 +1275,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
1229
1275
|
return resultLines.join("\n");
|
|
1230
1276
|
}
|
|
1231
1277
|
if (trimmed.startsWith("--enrich")) {
|
|
1232
|
-
const
|
|
1278
|
+
const restE = trimmed.slice("--enrich".length).trim();
|
|
1279
|
+
const forceEnrich = restE.includes("--force");
|
|
1280
|
+
const sessionId = restE.split(/\s+/)[0] || "";
|
|
1233
1281
|
if (!sessionId) {
|
|
1234
|
-
ctx.ui.notify("Usage: /domain --enrich <session-id>", "error");
|
|
1282
|
+
ctx.ui.notify("Usage: /domain --enrich <session-id> [--force]", "error");
|
|
1235
1283
|
return "(domain command handled)";
|
|
1236
1284
|
}
|
|
1237
1285
|
|
|
@@ -1244,9 +1292,47 @@ export default function (pi: ExtensionAPI) {
|
|
|
1244
1292
|
return "(domain command handled)";
|
|
1245
1293
|
}
|
|
1246
1294
|
|
|
1247
|
-
|
|
1295
|
+
// Enrichment is SESSION-SCOPED: only module docs that belong to THIS
|
|
1296
|
+
// session's bounded contexts may be touched, and existing module docs
|
|
1297
|
+
// are preserved unless --force. Prevents clobbering other streams.
|
|
1298
|
+
const allModules = fs.readdirSync(modulesDir).filter(f => f.endsWith(".md") && f !== "module-template.md");
|
|
1299
|
+
const kebab = (n: string) => n.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
1300
|
+
let sessionModules: string[] = [];
|
|
1301
|
+
const sRawPath = path.join(explorationDir, sessionId + ".raw.json");
|
|
1302
|
+
if (fs.existsSync(sRawPath)) {
|
|
1303
|
+
try {
|
|
1304
|
+
const rawS = JSON.parse(fs.readFileSync(sRawPath, "utf-8")) as Record<string, unknown>;
|
|
1305
|
+
const bcs = Array.isArray(rawS.boundedContexts) ? (rawS.boundedContexts as Record<string, unknown>[]) : [];
|
|
1306
|
+
sessionModules = bcs.map((bc) => kebab(String(bc.name ?? "")) + ".md").filter((n) => allModules.includes(n));
|
|
1307
|
+
} catch { /* ignore */ }
|
|
1308
|
+
}
|
|
1309
|
+
if (sessionModules.length === 0) {
|
|
1310
|
+
// fallback: session md bounded-contexts table
|
|
1311
|
+
const md = fs.existsSync(sessionPath) ? fs.readFileSync(sessionPath, "utf-8") : "";
|
|
1312
|
+
const m = md.match(/## Bounded Contexts[\s\S]*?\n((?:\|.*\n)+?)(?=\n##|$)/);
|
|
1313
|
+
if (m) {
|
|
1314
|
+
sessionModules = m[1].split("\n")
|
|
1315
|
+
.map((l) => l.split("|").map((c) => c.trim()).filter(Boolean)[0])
|
|
1316
|
+
.filter(Boolean)
|
|
1317
|
+
.map((n) => kebab(n) + ".md")
|
|
1318
|
+
.filter((n) => allModules.includes(n));
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
const preserved: string[] = [];
|
|
1322
|
+
const moduleFiles = sessionModules.filter((f) => {
|
|
1323
|
+
const exists = fs.existsSync(path.join(modulesDir, f));
|
|
1324
|
+
const implemented = moduleHasCode(ctx.cwd, f.replace(/\.md$/, ""));
|
|
1325
|
+
if (exists && implemented && !forceEnrich) {
|
|
1326
|
+
preserved.push(f);
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
1329
|
+
return true;
|
|
1330
|
+
});
|
|
1331
|
+
if (preserved.length > 0) {
|
|
1332
|
+
ctx.ui.notify("Preserving existing module docs (use --force to enrich): " + preserved.join(", "), "info");
|
|
1333
|
+
}
|
|
1248
1334
|
if (moduleFiles.length === 0) {
|
|
1249
|
-
ctx.ui.notify("No module docs
|
|
1335
|
+
ctx.ui.notify("No new module docs for this session. Existing docs preserved — no enrich needed (or pass --force).", "error");
|
|
1250
1336
|
return "(domain command handled)";
|
|
1251
1337
|
}
|
|
1252
1338
|
|