@forwardimpact/pathway 0.1.0 → 0.2.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/app/commands/agent.js +109 -21
- package/app/commands/command-factory.js +3 -3
- package/app/commands/interview.js +14 -7
- package/app/commands/job.js +43 -29
- package/app/commands/progress.js +14 -7
- package/app/commands/serve.js +5 -0
- package/app/commands/stage.js +0 -10
- package/app/commands/track.js +5 -8
- package/app/components/builder.js +111 -27
- package/app/css/components/surfaces.css +16 -0
- package/app/formatters/agent/profile.js +113 -87
- package/app/formatters/agent/skill.js +64 -31
- package/app/formatters/behaviour/dom.js +3 -0
- package/app/formatters/behaviour/microdata.js +106 -0
- package/app/formatters/discipline/dom.js +28 -1
- package/app/formatters/discipline/microdata.js +117 -0
- package/app/formatters/discipline/shared.js +49 -8
- package/app/formatters/driver/dom.js +3 -0
- package/app/formatters/driver/microdata.js +91 -0
- package/app/formatters/grade/dom.js +3 -0
- package/app/formatters/grade/microdata.js +151 -0
- package/app/formatters/index.js +32 -1
- package/app/formatters/interview/shared.js +13 -8
- package/app/formatters/job/description.js +5 -3
- package/app/formatters/json-ld.js +242 -0
- package/app/formatters/microdata-shared.js +184 -0
- package/app/formatters/progress/shared.js +14 -11
- package/app/formatters/skill/dom.js +3 -0
- package/app/formatters/skill/microdata.js +151 -0
- package/app/formatters/stage/dom.js +3 -18
- package/app/formatters/stage/microdata.js +110 -0
- package/app/formatters/stage/shared.js +0 -27
- package/app/formatters/track/dom.js +5 -30
- package/app/formatters/track/markdown.js +2 -25
- package/app/formatters/track/microdata.js +111 -0
- package/app/formatters/track/shared.js +6 -58
- package/app/handout-main.js +26 -12
- package/app/index.html +11 -0
- package/app/lib/card-mappers.js +17 -12
- package/app/lib/job-cache.js +12 -9
- package/app/lib/template-loader.js +66 -0
- package/app/lib/yaml-loader.js +25 -8
- package/app/main.js +8 -4
- package/app/model/agent.js +158 -130
- package/app/model/checklist.js +57 -91
- package/app/model/derivation.js +135 -68
- package/app/model/index-generator.js +1 -7
- package/app/model/job.js +19 -13
- package/app/model/levels.js +20 -12
- package/app/model/loader.js +41 -17
- package/app/model/matching.js +33 -3
- package/app/model/profile.js +38 -45
- package/app/model/schema-validation.js +438 -0
- package/app/model/validation.js +747 -68
- package/app/pages/agent-builder.js +119 -25
- package/app/pages/assessment-results.js +10 -4
- package/app/pages/discipline.js +36 -6
- package/app/pages/driver.js +9 -47
- package/app/pages/interview-builder.js +3 -1
- package/app/pages/interview.js +15 -4
- package/app/pages/job-builder.js +4 -1
- package/app/pages/job.js +15 -4
- package/app/pages/landing.js +10 -10
- package/app/pages/progress-builder.js +3 -1
- package/app/pages/progress.js +72 -21
- package/app/pages/stage.js +3 -126
- package/app/slide-main.js +45 -17
- package/app/slides/index.js +3 -1
- package/app/slides/overview.js +40 -4
- package/app/slides/progress.js +4 -2
- package/bin/pathway.js +18 -64
- package/examples/agents/.claude/skills/architecture-design/SKILL.md +58 -16
- package/examples/agents/.claude/skills/cloud-platforms/SKILL.md +59 -18
- package/examples/agents/.claude/skills/code-quality-review/SKILL.md +58 -17
- package/examples/agents/.claude/skills/devops-cicd/SKILL.md +64 -18
- package/examples/agents/.claude/skills/full-stack-development/SKILL.md +59 -15
- package/examples/agents/.claude/skills/sre-practices/SKILL.md +64 -18
- package/examples/agents/.claude/skills/technical-debt-management/SKILL.md +58 -17
- package/examples/agents/.github/agents/se-platform-code.agent.md +39 -88
- package/examples/agents/.github/agents/se-platform-plan.agent.md +41 -88
- package/examples/agents/.github/agents/se-platform-review.agent.md +38 -15
- package/examples/agents/.vscode/settings.json +1 -1
- package/examples/behaviours/outcome_ownership.yaml +1 -2
- package/examples/behaviours/polymathic_knowledge.yaml +1 -2
- package/examples/behaviours/precise_communication.yaml +1 -2
- package/examples/behaviours/relentless_curiosity.yaml +1 -2
- package/examples/behaviours/systems_thinking.yaml +1 -2
- package/examples/capabilities/business.yaml +80 -142
- package/examples/capabilities/delivery.yaml +155 -219
- package/examples/capabilities/people.yaml +2 -34
- package/examples/capabilities/reliability.yaml +161 -80
- package/examples/capabilities/scale.yaml +234 -252
- package/examples/copilot-setup-steps.yaml +25 -0
- package/examples/devcontainer.yaml +21 -0
- package/examples/disciplines/_index.yaml +1 -0
- package/examples/disciplines/data_engineering.yaml +14 -12
- package/examples/disciplines/engineering_management.yaml +63 -0
- package/examples/disciplines/software_engineering.yaml +14 -12
- package/examples/drivers.yaml +1 -4
- package/examples/framework.yaml +1 -2
- package/examples/grades.yaml +1 -3
- package/examples/questions/behaviours/outcome_ownership.yaml +1 -2
- package/examples/questions/behaviours/polymathic_knowledge.yaml +1 -2
- package/examples/questions/behaviours/precise_communication.yaml +1 -2
- package/examples/questions/behaviours/relentless_curiosity.yaml +1 -2
- package/examples/questions/behaviours/systems_thinking.yaml +1 -2
- package/examples/questions/skills/architecture_design.yaml +1 -2
- package/examples/questions/skills/cloud_platforms.yaml +1 -2
- package/examples/questions/skills/code_quality.yaml +1 -2
- package/examples/questions/skills/data_modeling.yaml +1 -2
- package/examples/questions/skills/devops.yaml +1 -2
- package/examples/questions/skills/full_stack_development.yaml +1 -2
- package/examples/questions/skills/sre_practices.yaml +1 -2
- package/examples/questions/skills/stakeholder_management.yaml +1 -2
- package/examples/questions/skills/team_collaboration.yaml +1 -2
- package/examples/questions/skills/technical_writing.yaml +1 -2
- package/examples/self-assessments.yaml +1 -3
- package/examples/stages.yaml +101 -46
- package/examples/tracks/_index.yaml +0 -1
- package/examples/tracks/platform.yaml +8 -13
- package/examples/tracks/sre.yaml +8 -18
- package/examples/vscode-settings.yaml +2 -7
- package/package.json +9 -3
- package/templates/agent.template.md +65 -0
- package/templates/skill.template.md +28 -0
- package/examples/agents/.claude/skills/data-modeling/SKILL.md +0 -99
- package/examples/agents/.claude/skills/developer-experience/SKILL.md +0 -99
- package/examples/agents/.claude/skills/knowledge-management/SKILL.md +0 -100
- package/examples/agents/.claude/skills/pattern-generalization/SKILL.md +0 -102
- package/examples/agents/.claude/skills/technical-writing/SKILL.md +0 -129
- package/examples/tracks/manager.yaml +0 -53
package/app/model/agent.js
CHANGED
|
@@ -104,8 +104,9 @@ export function toKebabCase(id) {
|
|
|
104
104
|
/**
|
|
105
105
|
* Derive agent skills using the unified profile system
|
|
106
106
|
* Returns skills sorted by level (highest first) for the given discipline × track
|
|
107
|
-
* Excludes human-only skills
|
|
108
|
-
*
|
|
107
|
+
* Excludes human-only skills and keeps only skills at the highest derived level.
|
|
108
|
+
* This approach respects track modifiers—a broad skill boosted to the same level
|
|
109
|
+
* as primary skills will be included.
|
|
109
110
|
* @param {Object} params - Parameters
|
|
110
111
|
* @param {Object} params.discipline - Human discipline definition
|
|
111
112
|
* @param {Object} params.track - Human track definition
|
|
@@ -123,7 +124,7 @@ export function deriveAgentSkills({ discipline, track, grade, skills }) {
|
|
|
123
124
|
});
|
|
124
125
|
|
|
125
126
|
// Apply agent-specific filtering and sorting
|
|
126
|
-
const filtered = filterSkillsForAgent(skillMatrix
|
|
127
|
+
const filtered = filterSkillsForAgent(skillMatrix);
|
|
127
128
|
return sortByLevelDescending(filtered);
|
|
128
129
|
}
|
|
129
130
|
|
|
@@ -224,28 +225,110 @@ function buildWorkingStyleFromBehaviours(
|
|
|
224
225
|
return sections.join("\n");
|
|
225
226
|
}
|
|
226
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Stage ID to display name and next stage mapping
|
|
230
|
+
*/
|
|
231
|
+
const STAGE_INFO = {
|
|
232
|
+
plan: { name: "Plan", nextStage: "Code" },
|
|
233
|
+
code: { name: "Code", nextStage: "Review" },
|
|
234
|
+
review: { name: "Review", nextStage: "Complete" },
|
|
235
|
+
};
|
|
236
|
+
|
|
227
237
|
/**
|
|
228
238
|
* Generate SKILL.md content from skill data
|
|
229
|
-
* @param {Object} skillData - Skill with agent section
|
|
230
|
-
* @returns {Object} Skill with frontmatter,
|
|
239
|
+
* @param {Object} skillData - Skill with agent section containing stages
|
|
240
|
+
* @returns {Object} Skill with frontmatter, title, stages array, reference, dirname
|
|
231
241
|
*/
|
|
232
242
|
export function generateSkillMd(skillData) {
|
|
233
|
-
const { agent } = skillData;
|
|
243
|
+
const { agent, name } = skillData;
|
|
234
244
|
|
|
235
245
|
if (!agent) {
|
|
236
246
|
throw new Error(`Skill ${skillData.id} has no agent section`);
|
|
237
247
|
}
|
|
238
248
|
|
|
249
|
+
if (!agent.stages) {
|
|
250
|
+
throw new Error(`Skill ${skillData.id} agent section missing stages`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Transform stages object to array for template rendering
|
|
254
|
+
const stagesArray = Object.entries(agent.stages).map(
|
|
255
|
+
([stageId, stageData]) => {
|
|
256
|
+
const info = STAGE_INFO[stageId] || {
|
|
257
|
+
name: stageId,
|
|
258
|
+
nextStage: "Next",
|
|
259
|
+
};
|
|
260
|
+
return {
|
|
261
|
+
stageId,
|
|
262
|
+
stageName: info.name,
|
|
263
|
+
nextStageName: info.nextStage,
|
|
264
|
+
focus: stageData.focus,
|
|
265
|
+
activities: stageData.activities || [],
|
|
266
|
+
ready: stageData.ready || [],
|
|
267
|
+
};
|
|
268
|
+
},
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// Sort stages in order: plan, code, review
|
|
272
|
+
const stageOrder = ["plan", "code", "review"];
|
|
273
|
+
stagesArray.sort(
|
|
274
|
+
(a, b) => stageOrder.indexOf(a.stageId) - stageOrder.indexOf(b.stageId),
|
|
275
|
+
);
|
|
276
|
+
|
|
239
277
|
return {
|
|
240
278
|
frontmatter: {
|
|
241
279
|
name: agent.name,
|
|
242
280
|
description: agent.description.trim(),
|
|
243
281
|
},
|
|
244
|
-
|
|
282
|
+
title: name,
|
|
283
|
+
stages: stagesArray,
|
|
284
|
+
reference: agent.reference ? agent.reference.trim() : "",
|
|
245
285
|
dirname: agent.name,
|
|
246
286
|
};
|
|
247
287
|
}
|
|
248
288
|
|
|
289
|
+
/**
|
|
290
|
+
* Estimate total character length of bodyData fields
|
|
291
|
+
* @param {Object} bodyData - Structured profile body data
|
|
292
|
+
* @returns {number} Estimated character count
|
|
293
|
+
*/
|
|
294
|
+
function estimateBodyDataLength(bodyData) {
|
|
295
|
+
let length = 0;
|
|
296
|
+
|
|
297
|
+
// String fields
|
|
298
|
+
const stringFields = [
|
|
299
|
+
"title",
|
|
300
|
+
"stageDescription",
|
|
301
|
+
"identity",
|
|
302
|
+
"priority",
|
|
303
|
+
"delegation",
|
|
304
|
+
"operationalContext",
|
|
305
|
+
"workingStyle",
|
|
306
|
+
"beforeHandoff",
|
|
307
|
+
];
|
|
308
|
+
for (const field of stringFields) {
|
|
309
|
+
if (bodyData[field]) {
|
|
310
|
+
length += bodyData[field].length;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Array fields
|
|
315
|
+
if (bodyData.capabilities) {
|
|
316
|
+
length += bodyData.capabilities.join(", ").length;
|
|
317
|
+
}
|
|
318
|
+
if (bodyData.beforeMakingChanges) {
|
|
319
|
+
for (const item of bodyData.beforeMakingChanges) {
|
|
320
|
+
length += item.text.length + 5; // +5 for "1. " prefix
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (bodyData.constraints) {
|
|
324
|
+
for (const c of bodyData.constraints) {
|
|
325
|
+
length += c.length + 2; // +2 for "- " prefix
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return length;
|
|
330
|
+
}
|
|
331
|
+
|
|
249
332
|
/**
|
|
250
333
|
* Validate agent profile against spec constraints
|
|
251
334
|
* @param {Object} profile - Generated profile
|
|
@@ -266,9 +349,10 @@ export function validateAgentProfile(profile) {
|
|
|
266
349
|
}
|
|
267
350
|
}
|
|
268
351
|
|
|
269
|
-
// Body length limit (30,000 chars)
|
|
270
|
-
|
|
271
|
-
|
|
352
|
+
// Body length limit (30,000 chars) - estimate from bodyData fields
|
|
353
|
+
const bodyLength = estimateBodyDataLength(profile.bodyData);
|
|
354
|
+
if (bodyLength > 30000) {
|
|
355
|
+
errors.push(`Body exceeds 30,000 character limit (${bodyLength})`);
|
|
272
356
|
}
|
|
273
357
|
|
|
274
358
|
// Tools format
|
|
@@ -322,17 +406,6 @@ export function validateAgentSkill(skill) {
|
|
|
322
406
|
// Stage-Based Agent Generation
|
|
323
407
|
// =============================================================================
|
|
324
408
|
|
|
325
|
-
/**
|
|
326
|
-
* Derive tools for a stage-based agent
|
|
327
|
-
* Stages define the authoritative tool set for each lifecycle phase
|
|
328
|
-
* @param {Object} params - Parameters
|
|
329
|
-
* @param {Object} params.stage - Stage definition from stages.yaml
|
|
330
|
-
* @returns {string[]} Array of tool names
|
|
331
|
-
*/
|
|
332
|
-
export function deriveAgentTools({ stage }) {
|
|
333
|
-
return stage.availableTools || [];
|
|
334
|
-
}
|
|
335
|
-
|
|
336
409
|
/**
|
|
337
410
|
* Derive handoff buttons for a stage-based agent
|
|
338
411
|
* Generates handoff button definitions from stage.handoffs with rich prompts
|
|
@@ -349,8 +422,9 @@ export function deriveHandoffs({ stage, discipline, track, stages }) {
|
|
|
349
422
|
return [];
|
|
350
423
|
}
|
|
351
424
|
|
|
352
|
-
// Build base name for target agents
|
|
353
|
-
const
|
|
425
|
+
// Build base name for target agents (matches filename without .agent.md)
|
|
426
|
+
const abbrev = getDisciplineAbbreviation(discipline.id);
|
|
427
|
+
const baseName = `${abbrev}-${toKebabCase(track.id)}`;
|
|
354
428
|
|
|
355
429
|
return stage.handoffs.map((handoff) => {
|
|
356
430
|
// Find the target stage to get its entry criteria
|
|
@@ -390,19 +464,16 @@ export function deriveHandoffs({ stage, discipline, track, stages }) {
|
|
|
390
464
|
/**
|
|
391
465
|
* Get the handoff type for a stage (used for checklist derivation)
|
|
392
466
|
* @param {string} stageId - Stage ID (plan, code, review)
|
|
393
|
-
* @returns {string|null}
|
|
467
|
+
* @returns {string|null} Stage ID for checklist or null
|
|
394
468
|
*/
|
|
395
|
-
function
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
code: "code_to_review",
|
|
399
|
-
review: null, // Review stage doesn't need a checklist
|
|
400
|
-
};
|
|
401
|
-
return handoffMap[stageId] || null;
|
|
469
|
+
function getChecklistStage(stageId) {
|
|
470
|
+
// Plan and code stages have checklists, review doesn't
|
|
471
|
+
return stageId === "review" ? null : stageId;
|
|
402
472
|
}
|
|
403
473
|
|
|
404
474
|
/**
|
|
405
|
-
* Build the profile body for a stage-based agent
|
|
475
|
+
* Build the profile body data for a stage-based agent
|
|
476
|
+
* Returns structured data for template rendering
|
|
406
477
|
* @param {Object} params - Parameters
|
|
407
478
|
* @param {Object} params.stage - Stage definition
|
|
408
479
|
* @param {Object} params.humanDiscipline - Human discipline definition
|
|
@@ -413,9 +484,9 @@ function getHandoffForStage(stageId) {
|
|
|
413
484
|
* @param {Array} params.derivedBehaviours - Behaviours sorted by maturity
|
|
414
485
|
* @param {Array} params.agentBehaviours - Agent behaviour definitions
|
|
415
486
|
* @param {string} params.checklistMarkdown - Pre-formatted checklist markdown
|
|
416
|
-
* @returns {
|
|
487
|
+
* @returns {Object} Structured profile body data
|
|
417
488
|
*/
|
|
418
|
-
function
|
|
489
|
+
function buildStageProfileBodyData({
|
|
419
490
|
stage,
|
|
420
491
|
humanDiscipline,
|
|
421
492
|
humanTrack,
|
|
@@ -428,103 +499,64 @@ function buildStageProfileBody({
|
|
|
428
499
|
}) {
|
|
429
500
|
const name = `${humanDiscipline.specialization || humanDiscipline.name} - ${humanTrack.name}`;
|
|
430
501
|
const stageName = stage.name.charAt(0).toUpperCase() + stage.name.slice(1);
|
|
431
|
-
const sections = [];
|
|
432
|
-
|
|
433
|
-
// Title with stage indicator
|
|
434
|
-
sections.push(`# ${name} - ${stageName} Agent`);
|
|
435
|
-
sections.push("");
|
|
436
502
|
|
|
437
|
-
//
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
//
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
503
|
+
// Build identity - prefer track, fall back to discipline
|
|
504
|
+
const rawIdentity = agentTrack.identity || agentDiscipline.identity;
|
|
505
|
+
const identity = substituteTemplateVars(rawIdentity, humanDiscipline);
|
|
506
|
+
|
|
507
|
+
// Build priority - prefer track, fall back to discipline (optional)
|
|
508
|
+
const rawPriority = agentTrack.priority || agentDiscipline.priority;
|
|
509
|
+
const priority = rawPriority
|
|
510
|
+
? substituteTemplateVars(rawPriority, humanDiscipline)
|
|
511
|
+
: null;
|
|
512
|
+
|
|
513
|
+
// Build beforeMakingChanges list - prefer track, fall back to discipline
|
|
514
|
+
const rawSteps =
|
|
515
|
+
agentTrack.beforeMakingChanges || agentDiscipline.beforeMakingChanges || [];
|
|
516
|
+
const beforeMakingChanges = rawSteps.map((text, i) => ({
|
|
517
|
+
index: i + 1,
|
|
518
|
+
text: substituteTemplateVars(text, humanDiscipline),
|
|
519
|
+
}));
|
|
520
|
+
|
|
521
|
+
// Delegation (from discipline only, optional)
|
|
522
|
+
const rawDelegation = agentDiscipline.delegation;
|
|
523
|
+
const delegation = rawDelegation
|
|
524
|
+
? substituteTemplateVars(rawDelegation, humanDiscipline)
|
|
525
|
+
: null;
|
|
454
526
|
|
|
455
527
|
// Primary capabilities from derived skills
|
|
456
|
-
const
|
|
457
|
-
if (topSkills.length > 0) {
|
|
458
|
-
sections.push("Your primary capabilities:");
|
|
459
|
-
for (const skill of topSkills) {
|
|
460
|
-
sections.push(`- ${skill.skillName}`);
|
|
461
|
-
}
|
|
462
|
-
sections.push("");
|
|
463
|
-
}
|
|
528
|
+
const capabilities = derivedSkills.slice(0, 6).map((s) => s.skillName);
|
|
464
529
|
|
|
465
530
|
// Operational Context - use track's roleContext (shared with human job descriptions)
|
|
466
|
-
|
|
467
|
-
sections.push("");
|
|
468
|
-
sections.push(humanTrack.roleContext.trim());
|
|
469
|
-
sections.push("");
|
|
531
|
+
const operationalContext = humanTrack.roleContext.trim();
|
|
470
532
|
|
|
471
|
-
// Working Style from derived behaviours
|
|
533
|
+
// Working Style from derived behaviours (still markdown for now)
|
|
472
534
|
const workingStyle = buildWorkingStyleFromBehaviours(
|
|
473
535
|
derivedBehaviours,
|
|
474
536
|
agentBehaviours,
|
|
475
537
|
3,
|
|
476
538
|
);
|
|
477
|
-
sections.push(workingStyle);
|
|
478
|
-
|
|
479
|
-
// Before Handoff (if provided)
|
|
480
|
-
if (checklistMarkdown) {
|
|
481
|
-
sections.push("## Before Handoff");
|
|
482
|
-
sections.push("");
|
|
483
|
-
sections.push(
|
|
484
|
-
"Before offering a handoff, verify and summarize completion of these items:",
|
|
485
|
-
);
|
|
486
|
-
sections.push("");
|
|
487
|
-
sections.push(checklistMarkdown);
|
|
488
|
-
sections.push("");
|
|
489
|
-
sections.push(
|
|
490
|
-
"When verified, summarize what was accomplished then offer the handoff.",
|
|
491
|
-
);
|
|
492
|
-
sections.push("If items are incomplete, explain what remains.");
|
|
493
|
-
sections.push("");
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
// Return Format section
|
|
497
|
-
sections.push("## Return Format");
|
|
498
|
-
sections.push("");
|
|
499
|
-
sections.push(
|
|
500
|
-
"When completing work (for handoff or as a subagent), provide:",
|
|
501
|
-
);
|
|
502
|
-
sections.push("");
|
|
503
|
-
sections.push("1. **Work completed**: What was accomplished");
|
|
504
|
-
sections.push(
|
|
505
|
-
"2. **Checklist status**: Items verified from Before Handoff section",
|
|
506
|
-
);
|
|
507
|
-
sections.push(
|
|
508
|
-
"3. **Recommendation**: Ready for next stage, or needs more work",
|
|
509
|
-
);
|
|
510
|
-
sections.push("");
|
|
511
539
|
|
|
512
540
|
// Constraints (stage + discipline + track)
|
|
513
|
-
const
|
|
541
|
+
const constraints = [
|
|
514
542
|
...(stage.constraints || []),
|
|
515
543
|
...(agentDiscipline.constraints || []),
|
|
516
544
|
...(agentTrack.constraints || []),
|
|
517
545
|
];
|
|
518
|
-
if (allConstraints.length > 0) {
|
|
519
|
-
sections.push("## Constraints");
|
|
520
|
-
sections.push("");
|
|
521
|
-
for (const constraint of allConstraints) {
|
|
522
|
-
sections.push(`- ${constraint}`);
|
|
523
|
-
}
|
|
524
|
-
sections.push("");
|
|
525
|
-
}
|
|
526
546
|
|
|
527
|
-
return
|
|
547
|
+
return {
|
|
548
|
+
title: `${name} - ${stageName} Agent`,
|
|
549
|
+
stageDescription: stage.description,
|
|
550
|
+
identity: identity.trim(),
|
|
551
|
+
priority: priority ? priority.trim() : null,
|
|
552
|
+
capabilities,
|
|
553
|
+
beforeMakingChanges,
|
|
554
|
+
delegation: delegation ? delegation.trim() : null,
|
|
555
|
+
operationalContext,
|
|
556
|
+
workingStyle,
|
|
557
|
+
beforeHandoff: checklistMarkdown || null,
|
|
558
|
+
constraints,
|
|
559
|
+
};
|
|
528
560
|
}
|
|
529
561
|
|
|
530
562
|
/**
|
|
@@ -540,7 +572,7 @@ function buildStageProfileBody({
|
|
|
540
572
|
* @param {Array} params.agentBehaviours - Agent behaviour definitions
|
|
541
573
|
* @param {Object} params.agentDiscipline - Agent discipline definition
|
|
542
574
|
* @param {Object} params.agentTrack - Agent track definition
|
|
543
|
-
* @param {Array} params.capabilities - Capabilities
|
|
575
|
+
* @param {Array} params.capabilities - Capabilities for checklist grouping
|
|
544
576
|
* @param {Array} params.stages - All stages (for handoff entry criteria)
|
|
545
577
|
* @returns {Object} Agent definition with skills, behaviours, tools, handoffs, constraints, checklist
|
|
546
578
|
*/
|
|
@@ -572,9 +604,6 @@ export function deriveStageAgent({
|
|
|
572
604
|
behaviours,
|
|
573
605
|
});
|
|
574
606
|
|
|
575
|
-
// Derive tools from stage
|
|
576
|
-
const tools = deriveAgentTools({ stage });
|
|
577
|
-
|
|
578
607
|
// Derive handoffs from stage
|
|
579
608
|
const handoffs = deriveHandoffs({
|
|
580
609
|
stage,
|
|
@@ -584,12 +613,13 @@ export function deriveStageAgent({
|
|
|
584
613
|
});
|
|
585
614
|
|
|
586
615
|
// Derive checklist if applicable
|
|
587
|
-
const
|
|
616
|
+
const checklistStage = getChecklistStage(stage.id);
|
|
588
617
|
let checklist = [];
|
|
589
|
-
if (
|
|
618
|
+
if (checklistStage && capabilities) {
|
|
590
619
|
checklist = deriveChecklist({
|
|
591
|
-
|
|
620
|
+
stageId: checklistStage,
|
|
592
621
|
skillMatrix: derivedSkills,
|
|
622
|
+
skills,
|
|
593
623
|
capabilities,
|
|
594
624
|
});
|
|
595
625
|
}
|
|
@@ -600,7 +630,6 @@ export function deriveStageAgent({
|
|
|
600
630
|
track,
|
|
601
631
|
derivedSkills,
|
|
602
632
|
derivedBehaviours,
|
|
603
|
-
tools,
|
|
604
633
|
handoffs,
|
|
605
634
|
constraints: [
|
|
606
635
|
...(stage.constraints || []),
|
|
@@ -616,7 +645,7 @@ export function deriveStageAgent({
|
|
|
616
645
|
|
|
617
646
|
/**
|
|
618
647
|
* Generate a stage-specific agent profile (.agent.md)
|
|
619
|
-
* Produces the complete profile with frontmatter,
|
|
648
|
+
* Produces the complete profile with frontmatter, bodyData, and filename
|
|
620
649
|
* @param {Object} params - Parameters
|
|
621
650
|
* @param {Object} params.discipline - Human discipline definition
|
|
622
651
|
* @param {Object} params.track - Human track definition
|
|
@@ -629,7 +658,7 @@ export function deriveStageAgent({
|
|
|
629
658
|
* @param {Object} params.agentTrack - Agent track definition
|
|
630
659
|
* @param {Array} params.capabilities - Capabilities with checklists
|
|
631
660
|
* @param {Array} params.stages - All stages (for handoff entry criteria)
|
|
632
|
-
* @returns {Object} Profile with frontmatter,
|
|
661
|
+
* @returns {Object} Profile with frontmatter, bodyData, and filename
|
|
633
662
|
*/
|
|
634
663
|
export function generateStageAgentProfile({
|
|
635
664
|
discipline,
|
|
@@ -659,10 +688,10 @@ export function generateStageAgentProfile({
|
|
|
659
688
|
stages,
|
|
660
689
|
});
|
|
661
690
|
|
|
662
|
-
// Build names
|
|
663
|
-
const fullName = `${toKebabCase(discipline.id)}-${toKebabCase(track.id)}-${stage.id}`;
|
|
691
|
+
// Build names (abbreviated form used consistently for filename, name, and handoffs)
|
|
664
692
|
const abbrev = getDisciplineAbbreviation(discipline.id);
|
|
665
|
-
const
|
|
693
|
+
const fullName = `${abbrev}-${toKebabCase(track.id)}-${stage.id}`;
|
|
694
|
+
const filename = `${fullName}.agent.md`;
|
|
666
695
|
|
|
667
696
|
// Build description
|
|
668
697
|
const disciplineDesc = discipline.description.trim().split("\n")[0];
|
|
@@ -672,8 +701,8 @@ export function generateStageAgentProfile({
|
|
|
672
701
|
// Format checklist as markdown
|
|
673
702
|
const checklistMarkdown = formatChecklistMarkdown(agent.checklist);
|
|
674
703
|
|
|
675
|
-
// Build profile body
|
|
676
|
-
const
|
|
704
|
+
// Build structured profile body data
|
|
705
|
+
const bodyData = buildStageProfileBodyData({
|
|
677
706
|
stage,
|
|
678
707
|
humanDiscipline: discipline,
|
|
679
708
|
humanTrack: track,
|
|
@@ -689,14 +718,13 @@ export function generateStageAgentProfile({
|
|
|
689
718
|
const frontmatter = {
|
|
690
719
|
name: fullName,
|
|
691
720
|
description,
|
|
692
|
-
tools: agent.tools,
|
|
693
721
|
infer: true,
|
|
694
722
|
...(agent.handoffs.length > 0 && { handoffs: agent.handoffs }),
|
|
695
723
|
};
|
|
696
724
|
|
|
697
725
|
return {
|
|
698
726
|
frontmatter,
|
|
699
|
-
|
|
727
|
+
bodyData,
|
|
700
728
|
filename,
|
|
701
729
|
};
|
|
702
730
|
}
|
package/app/model/checklist.js
CHANGED
|
@@ -1,116 +1,81 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Checklist Derivation
|
|
3
3
|
*
|
|
4
|
-
* Checklists are derived from
|
|
5
|
-
*
|
|
4
|
+
* Checklists are derived from skills with agent.stages.{stage}.ready criteria.
|
|
5
|
+
* Each skill defines its own readiness criteria for stage transitions.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* and the capability checklists provide items for each level.
|
|
7
|
+
* Checklist = Stage × Skill Matrix × Skill Ready Criteria
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
|
-
import { SKILL_LEVEL_ORDER, getSkillLevelIndex } from "./levels.js";
|
|
12
|
-
|
|
13
10
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* @param {string} capabilityId - Capability ID to check
|
|
17
|
-
* @returns {string|null} Maximum skill level or null if no skills in this capability
|
|
11
|
+
* Map from stage ID to the stage whose ready criteria should be shown
|
|
12
|
+
* (i.e., what must be ready before leaving this stage)
|
|
18
13
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
if (skillsInCapability.length === 0) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Find the highest level among skills in this capability
|
|
29
|
-
let maxIndex = -1;
|
|
30
|
-
let maxLevel = null;
|
|
31
|
-
|
|
32
|
-
for (const entry of skillsInCapability) {
|
|
33
|
-
const index = getSkillLevelIndex(entry.level);
|
|
34
|
-
if (index > maxIndex) {
|
|
35
|
-
maxIndex = index;
|
|
36
|
-
maxLevel = entry.level;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return maxLevel;
|
|
41
|
-
}
|
|
14
|
+
const STAGE_TO_HANDOFF = {
|
|
15
|
+
plan: "plan", // Show plan.ready before leaving plan
|
|
16
|
+
code: "code", // Show code.ready before leaving code
|
|
17
|
+
review: "review", // Show review.ready (completion criteria)
|
|
18
|
+
};
|
|
42
19
|
|
|
43
20
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* @
|
|
21
|
+
* Derive checklist items for a specific stage
|
|
22
|
+
* Returns skills grouped by capability with their ready criteria
|
|
23
|
+
*
|
|
24
|
+
* @param {Object} params
|
|
25
|
+
* @param {string} params.stageId - Current stage (plan, code, review)
|
|
26
|
+
* @param {Array} params.skillMatrix - Derived skill matrix with skill details
|
|
27
|
+
* @param {Array} params.skills - All skills (to look up agent.stages)
|
|
28
|
+
* @param {Array} params.capabilities - All capabilities (for emoji lookup)
|
|
29
|
+
* @returns {Array<{skill: Object, capability: Object, items: string[]}>} Checklist items grouped by skill
|
|
48
30
|
*/
|
|
49
|
-
function
|
|
50
|
-
|
|
31
|
+
export function deriveChecklist({
|
|
32
|
+
stageId,
|
|
33
|
+
skillMatrix,
|
|
34
|
+
skills,
|
|
35
|
+
capabilities,
|
|
36
|
+
}) {
|
|
37
|
+
const targetStage = STAGE_TO_HANDOFF[stageId];
|
|
38
|
+
if (!targetStage) {
|
|
51
39
|
return [];
|
|
52
40
|
}
|
|
53
41
|
|
|
54
|
-
|
|
55
|
-
const
|
|
42
|
+
// Build skill lookup
|
|
43
|
+
const skillById = new Map(skills.map((s) => [s.id, s]));
|
|
56
44
|
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
const levelIndex = getSkillLevelIndex(level);
|
|
60
|
-
if (levelIndex <= maxIndex && checklists[level]) {
|
|
61
|
-
items.push(...checklists[level]);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return items;
|
|
66
|
-
}
|
|
45
|
+
// Build capability lookup
|
|
46
|
+
const capabilityById = new Map(capabilities.map((c) => [c.id, c]));
|
|
67
47
|
|
|
68
|
-
/**
|
|
69
|
-
* Derive checklist items for a specific handoff
|
|
70
|
-
*
|
|
71
|
-
* @param {Object} params
|
|
72
|
-
* @param {string} params.handoff - Handoff type (plan_to_code, code_to_review)
|
|
73
|
-
* @param {Array} params.skillMatrix - Derived skill matrix
|
|
74
|
-
* @param {Array} params.capabilities - All capabilities with checklists
|
|
75
|
-
* @returns {Array<{capability: Object, level: string, items: string[]}>} Checklist items grouped by capability
|
|
76
|
-
*/
|
|
77
|
-
export function deriveChecklist({ handoff, skillMatrix, capabilities }) {
|
|
78
48
|
const result = [];
|
|
79
49
|
|
|
80
|
-
for (const
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
!capability.transitionChecklists ||
|
|
84
|
-
!capability.transitionChecklists[handoff]
|
|
85
|
-
) {
|
|
50
|
+
for (const entry of skillMatrix) {
|
|
51
|
+
const skill = skillById.get(entry.skillId);
|
|
52
|
+
if (!skill || !skill.agent || !skill.agent.stages) {
|
|
86
53
|
continue;
|
|
87
54
|
}
|
|
88
55
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
// Skip awareness level - not ready for checklists
|
|
93
|
-
if (!maxLevel || maxLevel === "awareness") {
|
|
56
|
+
const stageData = skill.agent.stages[targetStage];
|
|
57
|
+
if (!stageData || !stageData.ready || stageData.ready.length === 0) {
|
|
94
58
|
continue;
|
|
95
59
|
}
|
|
96
60
|
|
|
97
|
-
// Get
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
if (items.length > 0) {
|
|
104
|
-
result.push({
|
|
105
|
-
capability: {
|
|
106
|
-
id: capability.id,
|
|
107
|
-
name: capability.name,
|
|
108
|
-
emoji: capability.emoji,
|
|
109
|
-
},
|
|
110
|
-
level: maxLevel,
|
|
111
|
-
items,
|
|
112
|
-
});
|
|
61
|
+
// Get capability for this skill
|
|
62
|
+
const capability = capabilityById.get(entry.capability);
|
|
63
|
+
if (!capability) {
|
|
64
|
+
continue;
|
|
113
65
|
}
|
|
66
|
+
|
|
67
|
+
result.push({
|
|
68
|
+
skill: {
|
|
69
|
+
id: skill.id,
|
|
70
|
+
name: skill.name,
|
|
71
|
+
},
|
|
72
|
+
capability: {
|
|
73
|
+
id: capability.id,
|
|
74
|
+
name: capability.name,
|
|
75
|
+
emoji: capability.emoji,
|
|
76
|
+
},
|
|
77
|
+
items: stageData.ready,
|
|
78
|
+
});
|
|
114
79
|
}
|
|
115
80
|
|
|
116
81
|
return result;
|
|
@@ -118,8 +83,9 @@ export function deriveChecklist({ handoff, skillMatrix, capabilities }) {
|
|
|
118
83
|
|
|
119
84
|
/**
|
|
120
85
|
* Format a checklist for display (markdown format)
|
|
86
|
+
* Groups items by skill with capability emoji
|
|
121
87
|
*
|
|
122
|
-
* @param {Array<{
|
|
88
|
+
* @param {Array<{skill: Object, capability: Object, items: string[]}>} checklist - Derived checklist
|
|
123
89
|
* @returns {string} Markdown-formatted checklist
|
|
124
90
|
*/
|
|
125
91
|
export function formatChecklistMarkdown(checklist) {
|
|
@@ -127,8 +93,8 @@ export function formatChecklistMarkdown(checklist) {
|
|
|
127
93
|
return "";
|
|
128
94
|
}
|
|
129
95
|
|
|
130
|
-
const sections = checklist.map(({ capability, items }) => {
|
|
131
|
-
const header = `**${capability.emoji} ${
|
|
96
|
+
const sections = checklist.map(({ skill, capability, items }) => {
|
|
97
|
+
const header = `**${capability.emoji} ${skill.name}**`;
|
|
132
98
|
const itemList = items.map((item) => `- [ ] ${item}`).join("\n");
|
|
133
99
|
return `${header}\n\n${itemList}`;
|
|
134
100
|
});
|