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.
|
|
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 =
|
|
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" ? ".
|
|
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-
|
|
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 =
|
|
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" ? ".
|
|
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.
|
|
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 =
|
|
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" ? ".
|
|
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 =
|
|
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" ? ".
|
|
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-
|
|
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
|
@@ -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
|
|
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
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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
|
-
|
|
431
|
-
if (kind === "
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
1194
|
+
const msg = buildPhaseNudge(decision.kind, raw, sessionId, decision.contextName);
|
|
1200
1195
|
try {
|
|
1201
1196
|
pi.sendMessage(
|
|
1202
1197
|
{ content: msg, display: true },
|
package/templates/workflow.md
CHANGED
|
@@ -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
|
|
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
|
|