guardian-framework 0.1.135 → 0.1.137

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
@@ -3206,7 +3206,7 @@ __export(exports_package, {
3206
3206
  bin: () => bin,
3207
3207
  author: () => author
3208
3208
  });
3209
- var name = "guardian-framework", version = "0.1.135", 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;
3209
+ var name = "guardian-framework", version = "0.1.137", 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;
3210
3210
  var init_package = __esm(() => {
3211
3211
  exports = {
3212
3212
  ".": {
@@ -10873,7 +10873,7 @@ Run with --force to overwrite.`);
10873
10873
  }
10874
10874
  const generatedFiles = {};
10875
10875
  for (const tool of toolsToGenerate) {
10876
- const exportDir = tool === "pi" ? path7.join(targetDir, ".agents") : path7.join(targetDir, `.${tool}`);
10876
+ const exportDir = path7.join(targetDir, `.${tool}`);
10877
10877
  if (options.dryRun) {
10878
10878
  if (s)
10879
10879
  s.stop(`Dry run: Would generate .${tool}/ from .pi/`);
@@ -10907,7 +10907,7 @@ Run with --force to overwrite.`);
10907
10907
  s.stop("Exports generated successfully!");
10908
10908
  outro(`
10909
10909
  Generated exports:
10910
- ${toolsToGenerate.map((t2) => t2 === "pi" ? ".agents/skills/ (pi skills)" : `.${t2}/`).join(`
10910
+ ${toolsToGenerate.map((t2) => t2 === "pi" ? ".pi/skills/ (pi skills)" : `.${t2}/`).join(`
10911
10911
  `)}
10912
10912
 
10913
10913
  From .pi/ source.
@@ -11301,8 +11301,6 @@ function isFrontMatterFile(userContent, newContent) {
11301
11301
  function regenerateAllExports(targetDir, manifest) {
11302
11302
  const piDir = path9.join(targetDir, ".pi");
11303
11303
  for (const tool of manifest.tools) {
11304
- if (tool === "pi")
11305
- continue;
11306
11304
  const exportDir = path9.join(targetDir, `.${tool}`);
11307
11305
  generateExport(exportDir, tool, piDir, manifest, {});
11308
11306
  }
@@ -12412,7 +12410,7 @@ function scaffoldFromExploration(sessionId, options) {
12412
12410
  entityContext[entity.name] = entity.context;
12413
12411
  }
12414
12412
  function contextToModule(name) {
12415
- return name.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
12413
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[^A-Za-z0-9]+/g, "-").replace(/(^-|-$)/g, "").toLowerCase();
12416
12414
  }
12417
12415
  function inferContextDependencies(contextName) {
12418
12416
  const deps = [];
@@ -13891,7 +13889,6 @@ async function askExistingFrameworkAction() {
13891
13889
  init_templates();
13892
13890
  init_generate();
13893
13891
  var PI_DIR = ".pi";
13894
- var AGENTS_DIR = ".agents";
13895
13892
  async function runInit(targetDir = process.cwd()) {
13896
13893
  if (!templatesExist()) {
13897
13894
  showError("Templates not found. Ensure templates/pi/ exists.");
@@ -13947,7 +13944,7 @@ async function scaffoldFramework(targetDir, options) {
13947
13944
  const scaffoldErrors = scaffoldPiDirectory(piDir, context, scaffoldedFiles);
13948
13945
  const generatedFiles = {};
13949
13946
  for (const tool of options.tools) {
13950
- const exportDir = tool === "pi" ? path10.join(targetDir, AGENTS_DIR) : path10.join(targetDir, `.${tool}`);
13947
+ const exportDir = path10.join(targetDir, `.${tool}`);
13951
13948
  generateExport(exportDir, tool, piDir, manifest, generatedFiles);
13952
13949
  }
13953
13950
  for (const [filePath, record] of Object.entries(generatedFiles)) {
@@ -13976,7 +13973,7 @@ async function scaffoldFramework(targetDir, options) {
13976
13973
  }
13977
13974
  updateManifestAfterScaffold(targetDir, manifest, scaffoldedFiles);
13978
13975
  s.stop("Framework scaffolded successfully!");
13979
- const exportPaths = options.tools.map((t2) => t2 === "pi" ? ".agents/skills/ (pi skills)" : `.${t2}/`);
13976
+ const exportPaths = options.tools.map((t2) => t2 === "pi" ? ".pi/skills/ (pi skills)" : `.${t2}/`);
13980
13977
  showSuccess(`
13981
13978
  Scaffolded Guardian framework:
13982
13979
  .pi/ (source of truth)
package/dist/exports.js CHANGED
@@ -1991,7 +1991,7 @@ __export(exports_package, {
1991
1991
  bin: () => bin,
1992
1992
  author: () => author
1993
1993
  });
1994
- var name = "guardian-framework", version = "0.1.135", 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;
1994
+ var name = "guardian-framework", version = "0.1.137", 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;
1995
1995
  var init_package = __esm(() => {
1996
1996
  exports = {
1997
1997
  ".": {
@@ -9997,7 +9997,7 @@ Run with --force to overwrite.`);
9997
9997
  }
9998
9998
  const generatedFiles = {};
9999
9999
  for (const tool of toolsToGenerate) {
10000
- const exportDir = tool === "pi" ? path4.join(targetDir, ".agents") : path4.join(targetDir, `.${tool}`);
10000
+ const exportDir = path4.join(targetDir, `.${tool}`);
10001
10001
  if (options.dryRun) {
10002
10002
  if (s)
10003
10003
  s.stop(`Dry run: Would generate .${tool}/ from .pi/`);
@@ -10031,7 +10031,7 @@ Run with --force to overwrite.`);
10031
10031
  s.stop("Exports generated successfully!");
10032
10032
  outro(`
10033
10033
  Generated exports:
10034
- ${toolsToGenerate.map((t2) => t2 === "pi" ? ".agents/skills/ (pi skills)" : `.${t2}/`).join(`
10034
+ ${toolsToGenerate.map((t2) => t2 === "pi" ? ".pi/skills/ (pi skills)" : `.${t2}/`).join(`
10035
10035
  `)}
10036
10036
 
10037
10037
  From .pi/ source.
@@ -10425,8 +10425,6 @@ function isFrontMatterFile(userContent, newContent) {
10425
10425
  function regenerateAllExports(targetDir, manifest) {
10426
10426
  const piDir = path5.join(targetDir, ".pi");
10427
10427
  for (const tool of manifest.tools) {
10428
- if (tool === "pi")
10429
- continue;
10430
10428
  const exportDir = path5.join(targetDir, `.${tool}`);
10431
10429
  generateExport(exportDir, tool, piDir, manifest, {});
10432
10430
  }
@@ -10963,7 +10961,6 @@ async function askExistingFrameworkAction() {
10963
10961
  init_templates();
10964
10962
  init_generate();
10965
10963
  var PI_DIR = ".pi";
10966
- var AGENTS_DIR = ".agents";
10967
10964
  async function runInit(targetDir = process.cwd()) {
10968
10965
  if (!templatesExist()) {
10969
10966
  showError("Templates not found. Ensure templates/pi/ exists.");
@@ -11019,7 +11016,7 @@ async function scaffoldFramework(targetDir, options) {
11019
11016
  const scaffoldErrors = scaffoldPiDirectory(piDir, context, scaffoldedFiles);
11020
11017
  const generatedFiles = {};
11021
11018
  for (const tool of options.tools) {
11022
- const exportDir = tool === "pi" ? path6.join(targetDir, AGENTS_DIR) : path6.join(targetDir, `.${tool}`);
11019
+ const exportDir = path6.join(targetDir, `.${tool}`);
11023
11020
  generateExport(exportDir, tool, piDir, manifest, generatedFiles);
11024
11021
  }
11025
11022
  for (const [filePath, record] of Object.entries(generatedFiles)) {
@@ -11048,7 +11045,7 @@ async function scaffoldFramework(targetDir, options) {
11048
11045
  }
11049
11046
  updateManifestAfterScaffold(targetDir, manifest, scaffoldedFiles);
11050
11047
  s.stop("Framework scaffolded successfully!");
11051
- const exportPaths = options.tools.map((t2) => t2 === "pi" ? ".agents/skills/ (pi skills)" : `.${t2}/`);
11048
+ const exportPaths = options.tools.map((t2) => t2 === "pi" ? ".pi/skills/ (pi skills)" : `.${t2}/`);
11052
11049
  showSuccess(`
11053
11050
  Scaffolded Guardian framework:
11054
11051
  .pi/ (source of truth)
@@ -12736,7 +12733,7 @@ function scaffoldFromExploration(sessionId, options) {
12736
12733
  entityContext[entity.name] = entity.context;
12737
12734
  }
12738
12735
  function contextToModule(name2) {
12739
- return name2.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
12736
+ return name2.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[^A-Za-z0-9]+/g, "-").replace(/(^-|-$)/g, "").toLowerCase();
12740
12737
  }
12741
12738
  function inferContextDependencies(contextName) {
12742
12739
  const deps = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardian-framework",
3
- "version": "0.1.135",
3
+ "version": "0.1.137",
4
4
  "description": "Token-optimized agentic framework scaffolder with pi-first architecture",
5
5
  "type": "module",
6
6
  "main": "dist/exports.js",
@@ -304,135 +304,130 @@ function maxSeqId(items: unknown[] | undefined, prefix: string): number {
304
304
  return max;
305
305
  }
306
306
 
307
- const DRIVER_RULES = [
308
- "- Reply with ONLY the small JSON for the requested step — no prose, no fences, no reasoning.",
309
- "- Emit AT MOST 5 items per message (fewer if any item is long). Keep the JSON well under",
310
- " ~2,500 tokens so the domain_save_result call that follows always fits in the same message.",
311
- "- Call domain_save_result with { sessionId, responseJson: <that exact JSON> } and STOP —",
312
- " nothing after the tool call. The driver sends your next step after it saves.",
313
- ].join("\n");
314
-
315
- function buildStrategicMessage(sessionId: string, businessContext: string): string {
316
- return [
317
- "Analyze this business domain with Domain-Driven Design. Work in SMALL incremental steps —",
318
- "one phase per message; the driver will send each next phase after you save. This first",
319
- "message is the STRATEGIC PASS only.",
320
- "",
321
- "Session: " + sessionId,
322
- "Business Context: " + businessContext,
323
- "",
324
- "Reply with ONLY this JSON:",
325
- '```json',
326
- '{',
327
- ' "businessContext": "one-line summary of the domain",',
328
- ' "actors": [{ "name": "...", "description": "...", "interactions": "..." }],',
329
- ' "assumptions": [{ "assumption": "...", "impactIfWrong": "...", "mitigation": "..." }],',
330
- ' "boundedContexts": [',
331
- ' { "name": "...", "description": "...", "entities": ["..."],',
332
- ' "subdomainType": "core|supporting|generic", "subdomainRationale": "one line" }',
333
- ' ],',
334
- ' "contextMap": [{ "from": "A", "to": "B", "relationship": "partnership|shared-kernel|conformist|anticorruption-layer|open-host", "note": "..." }],',
335
- ' "openQuestions": "..."',
336
- '}',
337
- '```',
338
- "",
339
- DRIVER_RULES,
340
- ].join("\n");
341
- }
342
-
343
- function buildRequirementsMessage(raw: Record<string, unknown>, sessionId: string): string {
344
- const fr = Array.isArray(raw.functionalRequirements) ? (raw.functionalRequirements as Record<string, unknown>[]) : [];
345
- const nfr = Array.isArray(raw.nonFunctionalRequirements) ? (raw.nonFunctionalRequirements as Record<string, unknown>[]) : [];
346
- const nextFr = maxSeqId(fr, "FR") + 1;
347
- const nextNfr = maxSeqId(nfr, "NFR") + 1;
348
- return [
349
- "Session " + sessionId + " — REQUIREMENTS PASS (batch). Saved so far: " +
350
- fr.length + " functional, " + nfr.length + " non-functional.",
351
- "Produce the NEXT contiguous batch: up to 5 functional requirements starting at FR-" +
352
- String(nextFr).padStart(3, "0") + (fr.length === 0 ? "" : " (continue from the last saved id)") +
353
- " and/or up to 5 non-functional requirements starting at NFR-" + String(nextNfr).padStart(3, "0") + ".",
354
- "Only include the type you still have left to deliver.",
355
- "",
356
- "Reply with ONLY this JSON:",
357
- '```json',
358
- '{',
359
- ' "functionalRequirements": [',
360
- ' { "id": "FR-001", "requirement": "The system shall...", "priority": "critical|high|medium|low", "boundedContext": "ContextName" }',
361
- ' ],',
362
- ' "nonFunctionalRequirements": [',
363
- ' { "id": "NFR-001", "requirement": "The system shall...", "category": "performance|security|scalability|availability|maintainability", "target": "measurable target" }',
364
- ' ]',
365
- '}',
366
- '```',
367
- "On your FINAL requirements batch add \"requirementsComplete\": true to the JSON",
368
- "(omit both arrays if a type is done and only send the flag).",
369
- "",
370
- DRIVER_RULES,
371
- ].join("\n");
372
- }
373
-
374
- function buildUsecasesMessage(sessionId: string): string {
375
- return [
376
- "Session " + sessionId + " — USE-CASES PASS (batch). Produce up to 4 application services for the",
377
- "core contexts (name the owning context exactly as in the strategic pass).",
378
- "",
379
- "Reply with ONLY this JSON:",
380
- '```json',
381
- '{',
382
- ' "applicationServices": [',
383
- ' { "name": "ApproveRequest", "context": "ApprovalWorkflow", "useCase": "what this application service orchestrates",',
384
- ' "commands": ["verify intent", "consume"], "aggregates": ["ApprovalTicket"], "mapsToFr": "FR-00x" }',
385
- ' ]',
386
- '}',
387
- '```',
388
- "On your FINAL use-cases batch add \"usecasesComplete\": true to the JSON.",
389
- "",
390
- DRIVER_RULES,
391
- ].join("\n");
392
- }
393
307
 
394
- function buildContextMessage(raw: Record<string, unknown>, sessionId: string, contextName: string): string {
395
- const bc = (Array.isArray(raw.boundedContexts) ? (raw.boundedContexts as Record<string, unknown>[]) : [])
396
- .find((x) => String(x.name ?? "") === contextName);
397
- const need = (Array.isArray(bc?.entities) ? (bc!.entities as unknown[]) : []).map(String).filter(Boolean);
398
- const done = Array.isArray(raw.entities) ? (raw.entities as Record<string, unknown>[])
399
- .filter((e) => String(e.context ?? "") === contextName).map((e) => String(e.name ?? "")) : [];
400
- const remaining = need.filter((n) => !done.includes(n));
308
+ function buildExploreProtocolMessage(sessionId: string, businessContext: string): string {
401
309
  return [
402
- "Session " + sessionId + " — PER-CONTEXT PASS for \"" + contextName + "\"" +
403
- (remaining.length > 0 ? " (still needed from strategic list: " + remaining.join(", ") + ")" : "") + ".",
404
- "Enrich this ONE context: entities with entity-vs-VO justification, domain events with triage,",
405
- "invariants, and this context's ubiquitous language. Up to 6 entities per message — if the",
406
- "context has more, the driver will ask you to continue this same context.",
407
- "",
408
- "Reply with ONLY this JSON:",
409
- '```json',
410
- '{',
411
- ' "boundedContexts": [{ "name": "' + contextName + '", "description": "...", "entities": ["Entity1"],',
412
- ' "invariants": [{ "rule": "...", "illegalStatePrevented": "..." }] }],',
413
- ' "entities": [',
414
- ' { "name": "Entity1", "context": "' + contextName + '", "type": "entity|value-object|aggregate-root",',
415
- ' "description": "...",',
416
- ' "justification": { "identityPersists": true, "immutable": false, "equalByState": false } }',
417
- ' ],',
418
- ' "domainEvents": [',
419
- ' { "name": "Event", "context": "' + contextName + '", "description": "...", "triggeredBy": "...",',
420
- ' "triage": { "domainExpertCares": true, "crossesAggregateBoundary": false, "needsAudit": false } }',
421
- ' ],',
422
- ' "ubiquitousLanguage": [{ "term": "...", "definition": "...", "boundedContext": "' + contextName + '", "aliases": [], "examples": "" }]',
423
- '}',
424
- '```',
425
- "",
426
- DRIVER_RULES,
427
- ].join("\n");
310
+ "Analyze this business domain with Domain-Driven Design. You MUST work in SMALL",
311
+ "incremental steps — never produce the whole model in one message, or you will be",
312
+ "truncated mid-JSON and lose work.",
313
+ "",
314
+ "Session: " + sessionId,
315
+ "Business Context: " + businessContext,
316
+ "",
317
+ "STEP PROTOCOL (strict — one step per message, each message <= ~1,500 tokens of JSON):",
318
+ "",
319
+ "1. STRATEGIC PASS (this message): reply with ONLY this JSON, nothing else —",
320
+ '```json',
321
+ '{',
322
+ ' "businessContext": "one-line summary of the domain",',
323
+ ' "actors": [{ "name": "...", "description": "...", "interactions": "..." }],',
324
+ ' "assumptions": [{ "assumption": "...", "impactIfWrong": "...", "mitigation": "..." }],',
325
+ ' "boundedContexts": [',
326
+ ' { "name": "...", "description": "...", "entities": ["..."],',
327
+ ' "subdomainType": "core|supporting|generic", "subdomainRationale": "one line" }',
328
+ ' ],',
329
+ ' "contextMap": [{ "from": "A", "to": "B", "relationship": "partnership|shared-kernel|conformist|anticorruption-layer|open-host", "note": "..." }],',
330
+ ' "openQuestions": "..."',
331
+ '}',
332
+ '```',
333
+ "then call domain_save_result with { sessionId, responseJson: <that JSON> }. It merges;",
334
+ "the file is saved for you. Do NOT also edit exploration.md by hand.",
335
+ "",
336
+ "1b. REQUIREMENTS PASS (next message): reply with ONLY this JSON, then call domain_save_result",
337
+ "again (merge is by id — existing entries are untouched):",
338
+ '```json',
339
+ '{',
340
+ ' "functionalRequirements": [',
341
+ ' { "id": "FR-001", "requirement": "The system shall...", "priority": "critical|high|medium|low", "boundedContext": "ContextName" }',
342
+ ' ],',
343
+ ' "nonFunctionalRequirements": [',
344
+ ' { "id": "NFR-001", "requirement": "The system shall...", "category": "performance|security|scalability|availability|maintainability", "target": "measurable target" }',
345
+ ' ]',
346
+ '}',
347
+ '```',
348
+ "",
349
+ "1c. USE-CASES PASS (next message): reply with ONLY this JSON for ALL core contexts, then call",
350
+ "domain_save_result (merge is by name+context):",
351
+ '```json',
352
+ '{',
353
+ ' "applicationServices": [',
354
+ ' { "name": "ApproveRequest", "context": "ApprovalWorkflow", "useCase": "what this application service orchestrates",',
355
+ ' "commands": ["verify intent", "consume"], "aggregates": ["ApprovalTicket"], "mapsToFr": "FR-00x" }',
356
+ ' ]',
357
+ '}',
358
+ '```',
359
+ "",
360
+ "2. PER-CONTEXT PASS (repeat for EACH core/tactical bounded context, one context per message):",
361
+ "reply with ONLY this JSON for one context, then call domain_save_result again:",
362
+ '```json',
363
+ '{',
364
+ ' "boundedContexts": [{ "name": "TheContext", "description": "...", "entities": ["Entity1"],',
365
+ ' "invariants": [{ "rule": "...", "illegalStatePrevented": "..." }] }],',
366
+ ' "entities": [',
367
+ ' { "name": "Entity1", "context": "TheContext", "type": "entity|value-object|aggregate-root",',
368
+ ' "description": "...",',
369
+ ' "justification": { "identityPersists": true, "immutable": false, "equalByState": false } }',
370
+ ' ],',
371
+ ' "domainEvents": [',
372
+ ' { "name": "Event", "context": "TheContext", "description": "...", "triggeredBy": "...",',
373
+ ' "triage": { "domainExpertCares": true, "crossesAggregateBoundary": false, "needsAudit": false } }',
374
+ ' ],',
375
+ ' "ubiquitousLanguage": [{ "term": "...", "definition": "...", "boundedContext": "TheContext", "aliases": [], "examples": "" }]',
376
+ '}',
377
+ '```',
378
+ "Supporting/generic contexts need no detail pass — the strategic entry is enough.",
379
+ "",
380
+ "RULES:",
381
+ "- If a message of yours is cut off mid-JSON, continue from exactly where you stopped in the",
382
+ " NEXT message (never restart the whole model).",
383
+ "- Never emit the full model in one response and never exceed ~1,500 tokens of JSON per message.",
384
+ "- HARD RULE: never narrate or reason in chat. No prose analysis, no relationship",
385
+ " deliberation, no summaries — those burn your output budget and truncate the JSON.",
386
+ " Emit ONLY the small JSON step for the current phase and stop.",
387
+ "- After the last context, run /domain --validate " + sessionId + " and report the checks.",
388
+ ].join("\n");
428
389
  }
429
-
430
- function buildPhaseMessage(kind: PhaseKind, raw: Record<string, unknown>, sessionId: string, contextName?: string): string {
431
- if (kind === "strategic") return buildStrategicMessage(sessionId, String(raw.businessContext ?? ""));
432
- if (kind === "requirements") return buildRequirementsMessage(raw, sessionId);
433
- if (kind === "usecases") return buildUsecasesMessage(sessionId);
434
- if (kind === "context") return buildContextMessage(raw, sessionId, contextName ?? "");
435
- return "";
390
+ function buildPhaseNudge(kind: PhaseKind, raw: Record<string, unknown>, sessionId: string, contextName?: string): string {
391
+ if (kind === "strategic") return buildExploreProtocolMessage(sessionId, String(raw.businessContext ?? ""));
392
+ if (kind === "requirements") {
393
+ const fr = Array.isArray(raw.functionalRequirements) ? (raw.functionalRequirements as Record<string, unknown>[]) : [];
394
+ const nfr = Array.isArray(raw.nonFunctionalRequirements) ? (raw.nonFunctionalRequirements as Record<string, unknown>[]) : [];
395
+ const nextFr = maxSeqId(fr, "FR") + 1;
396
+ const nextNfr = maxSeqId(nfr, "NFR") + 1;
397
+ return [
398
+ "Continue — REQUIREMENTS PASS (" + fr.length + " FR, " + nfr.length + " NFR saved).",
399
+ "Emit the next batch using the requirements schema from the original protocol: up to 5",
400
+ "functional requirements starting at FR-" + String(nextFr).padStart(3, "0") +
401
+ " and/or up to 5 non-functional starting at NFR-" + String(nextNfr).padStart(3, "0") + ".",
402
+ 'Call domain_save_result after each batch; add "requirementsComplete": true to your FINAL batch.',
403
+ "Reply with ONLY the JSON, then the tool call.",
404
+ ].join("\n");
405
+ }
406
+ if (kind === "usecases") {
407
+ return [
408
+ "Continue — USE-CASES PASS.",
409
+ "Deliver the application services for the core contexts using the use-cases schema from the",
410
+ 'original protocol. Call domain_save_result when done; add "usecasesComplete": true to your',
411
+ "FINAL batch. Reply with ONLY the JSON, then the tool call.",
412
+ ].join("\n");
413
+ }
414
+ if (kind === "context") {
415
+ const bcs = Array.isArray(raw.boundedContexts) ? (raw.boundedContexts as Record<string, unknown>[]) : [];
416
+ const bc = bcs.find((x) => String(x.name ?? "") === contextName);
417
+ const need = (Array.isArray(bc?.entities) ? (bc!.entities as unknown[]) : []).map(String).filter(Boolean);
418
+ const have = new Set((Array.isArray(raw.entities) ? (raw.entities as Record<string, unknown>[]) : [])
419
+ .filter((e) => String(e.context ?? "") === contextName).map((e) => String(e.name ?? "")));
420
+ const remaining = need.filter((n) => !have.has(n));
421
+ return [
422
+ 'Continue — PER-CONTEXT PASS for "' + (contextName ?? "") + '"' +
423
+ (remaining.length > 0 ? " (still needed: " + remaining.join(", ") + ")" : "") + ".",
424
+ "Enrich this ONE context (entities with entity-vs-VO justification, domain events with triage,",
425
+ "invariants, ubiquitous language) using the schema from the original protocol. Up to 6 entities",
426
+ "per message; if more remain, call domain_save_result and you will be nudged to continue.",
427
+ "Reply with ONLY the JSON, then the tool call.",
428
+ ].join("\n");
429
+ }
430
+ return "Continue with the next exploration phase using the schemas from the original protocol.";
436
431
  }
437
432
 
438
433
  function moduleHasCode(cwd: string, moduleName: string): boolean {
@@ -837,7 +832,7 @@ export default function (pi: ExtensionAPI) {
837
832
  return { content: [{ type: "text" as const, text: savedNote + " Exploration content complete — run /domain --validate " + sessionId }] };
838
833
  }
839
834
  const label = decision.kind === "context" ? "context " + (decision.contextName ?? "") : decision.kind;
840
- const nextMsg = buildPhaseMessage(decision.kind, parsedJson, sessionId, decision.contextName);
835
+ const nextMsg = buildPhaseNudge(decision.kind, parsedJson, sessionId, decision.contextName);
841
836
  try {
842
837
  pi.sendMessage(
843
838
  { content: nextMsg, display: true },
@@ -1155,7 +1150,7 @@ export default function (pi: ExtensionAPI) {
1155
1150
  // Inject the DDD analysis prompt as a follow-up message to the agent.
1156
1151
  // Using sendMessage with triggerTurn=true causes the agent to process
1157
1152
  // this as a new conversation turn, not as a command response.
1158
- const analysisPrompt = buildStrategicMessage(sessionId, sanitized);
1153
+ const analysisPrompt = buildExploreProtocolMessage(sessionId, sanitized);
1159
1154
 
1160
1155
 
1161
1156
  try {
@@ -1196,7 +1191,7 @@ export default function (pi: ExtensionAPI) {
1196
1191
  }
1197
1192
  const label = decision.kind === "context" ? "context " + decision.contextName : decision.kind;
1198
1193
  ctx.ui.notify("Resumed " + sessionId + " at phase: " + label, "success");
1199
- const msg = buildPhaseMessage(decision.kind, raw, sessionId, decision.contextName);
1194
+ const msg = buildPhaseNudge(decision.kind, raw, sessionId, decision.contextName);
1200
1195
  try {
1201
1196
  pi.sendMessage(
1202
1197
  { content: msg, display: true },
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2026-09-07T07:58:06Z",
2
+ "timestamp": "2026-09-25T16:13:52Z",
3
3
  "mode": "all",
4
4
  "stages_run": [],
5
5
  "summary": {
@@ -9,7 +9,7 @@ Architecture-first: every change traces back to canonical architecture docs.
9
9
 
10
10
  1. Ensure `git init` has been run and `.gitignore` is in place (Guardian scaffolds a comprehensive `.gitignore`)
11
11
  2. Fill in project context in `.pi/agent/AGENTS.md` (replace `[bracketed]` placeholders)
12
- 3. Run `npx guardian-framework generate` to create `.agents/`, `.claude/`, etc.
12
+ 3. Run `npx guardian-framework generate` to regenerate tool exports (pi skills under `.pi/skills/`, plus `.claude/` etc. for other tools)
13
13
 
14
14
  > **Multi-techstack repos:** If your repository has both backend and frontend (e.g., Java + TypeScript), run `guardian-framework init` in each directory with the same repository URL. Each gets its own `.pi/`, manifest, build config, and CI pipeline. Scope CI path triggers to avoid collisions (`backend/**`, `frontend/**`).
15
15