@forwardimpact/model 0.1.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.
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Checklist Derivation
3
+ *
4
+ * Checklists are derived from skills with agent.stages.{stage}.ready criteria.
5
+ * Each skill defines its own readiness criteria for stage transitions.
6
+ *
7
+ * Checklist = Stage × Skill Matrix × Skill Ready Criteria
8
+ */
9
+
10
+ /**
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)
13
+ */
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
+ };
19
+
20
+ /**
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
30
+ */
31
+ export function deriveChecklist({
32
+ stageId,
33
+ skillMatrix,
34
+ skills,
35
+ capabilities,
36
+ }) {
37
+ const targetStage = STAGE_TO_HANDOFF[stageId];
38
+ if (!targetStage) {
39
+ return [];
40
+ }
41
+
42
+ // Build skill lookup
43
+ const skillById = new Map(skills.map((s) => [s.id, s]));
44
+
45
+ // Build capability lookup
46
+ const capabilityById = new Map(capabilities.map((c) => [c.id, c]));
47
+
48
+ const result = [];
49
+
50
+ for (const entry of skillMatrix) {
51
+ const skill = skillById.get(entry.skillId);
52
+ if (!skill || !skill.agent || !skill.agent.stages) {
53
+ continue;
54
+ }
55
+
56
+ const stageData = skill.agent.stages[targetStage];
57
+ if (!stageData || !stageData.ready || stageData.ready.length === 0) {
58
+ continue;
59
+ }
60
+
61
+ // Get capability for this skill
62
+ const capability = capabilityById.get(entry.capability);
63
+ if (!capability) {
64
+ continue;
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
+ emojiIcon: capability.emojiIcon,
76
+ },
77
+ items: stageData.ready,
78
+ });
79
+ }
80
+
81
+ return result;
82
+ }
83
+
84
+ /**
85
+ * Format a checklist for display (markdown format)
86
+ * Groups items by skill with capability emoji
87
+ *
88
+ * @param {Array<{skill: Object, capability: Object, items: string[]}>} checklist - Derived checklist
89
+ * @returns {string} Markdown-formatted checklist
90
+ */
91
+ export function formatChecklistMarkdown(checklist) {
92
+ if (!checklist || checklist.length === 0) {
93
+ return "";
94
+ }
95
+
96
+ const sections = checklist.map(({ skill, capability, items }) => {
97
+ const header = `**${capability.emojiIcon} ${skill.name}**`;
98
+ const itemList = items.map((item) => `- [ ] ${item}`).join("\n");
99
+ return `${header}\n\n${itemList}`;
100
+ });
101
+
102
+ return sections.join("\n\n");
103
+ }