continuous-improvement 3.1.0 → 3.9.1
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/.claude-plugin/marketplace.json +66 -0
- package/CHANGELOG.md +216 -0
- package/QUICKSTART.md +79 -12
- package/README.md +229 -349
- package/SKILL.md +87 -9
- package/action.yml +1 -1
- package/bin/analyze.sh +9 -1
- package/bin/backfill.mjs +172 -0
- package/bin/check-doc-runtime-claims.mjs +120 -0
- package/bin/check-docs-substrings.mjs +333 -0
- package/bin/check-everything-mirror.mjs +145 -0
- package/bin/check-routing-targets.mjs +151 -0
- package/bin/check-skill-law-tag.mjs +128 -0
- package/bin/check-skill-mirror.mjs +119 -0
- package/bin/check-skill-tiers.mjs +116 -0
- package/bin/check-third-party-shape.mjs +202 -0
- package/bin/generate-plugin-manifests.mjs +171 -0
- package/bin/harvest-friction.mjs +279 -0
- package/bin/hook-stats.mjs +258 -0
- package/bin/install.mjs +407 -499
- package/bin/lint-transcript.mjs +182 -210
- package/bin/mcp-server.mjs +834 -608
- package/bin/observe.mjs +148 -0
- package/bin/pre-commit-block-strays.sh +49 -0
- package/bin/refresh-third-party.mjs +416 -0
- package/bin/unified-cli.mjs +533 -0
- package/commands/continuous-improvement.md +43 -2
- package/commands/discipline.md +14 -0
- package/commands/harvest.md +76 -0
- package/commands/learn-eval.md +117 -0
- package/commands/planning-with-files.md +66 -0
- package/commands/proceed-with-the-recommendation.md +62 -0
- package/commands/ralph.md +103 -0
- package/commands/release-train.md +81 -0
- package/commands/seven-laws.md +16 -0
- package/commands/superpowers.md +180 -0
- package/commands/swarm.md +101 -0
- package/commands/workspace-surface-audit.md +77 -0
- package/hooks/gateguard.mjs +172 -0
- package/hooks/observe.sh +42 -4
- package/hooks/session.sh +3 -3
- package/hooks/three-section-close.mjs +181 -0
- package/instinct-packs/meta.json +16 -0
- package/lib/cli-anything.mjs +401 -0
- package/lib/compound-engineering.mjs +831 -0
- package/lib/gateguard-state.mjs +85 -0
- package/lib/observe-event.mjs +128 -0
- package/lib/plugin-metadata.mjs +435 -0
- package/lib/pm-marketplace.mjs +61 -0
- package/lib/pm-skills.mjs +1274 -0
- package/lib/resolve-home-dir.mjs +43 -0
- package/lib/skill-tiers.mjs +137 -0
- package/lib/unified-plugin.mjs +924 -0
- package/llms.txt +32 -7
- package/package.json +29 -19
- package/plugins/beginner.json +17 -6
- package/plugins/continuous-improvement/.claude-plugin/marketplace.json +20 -0
- package/plugins/continuous-improvement/.claude-plugin/plugin.json +26 -0
- package/plugins/continuous-improvement/LICENSE +21 -0
- package/plugins/continuous-improvement/README.md +57 -0
- package/plugins/continuous-improvement/agents/README.md +120 -0
- package/plugins/continuous-improvement/agents/code-reviewer.md +97 -0
- package/plugins/continuous-improvement/agents/security-auditor.md +101 -0
- package/plugins/continuous-improvement/agents/test-engineer.md +95 -0
- package/plugins/continuous-improvement/bin/backfill.mjs +172 -0
- package/plugins/continuous-improvement/bin/mcp-server.mjs +889 -0
- package/plugins/continuous-improvement/bin/observe.mjs +148 -0
- package/plugins/continuous-improvement/commands/continuous-improvement.md +115 -0
- package/plugins/continuous-improvement/commands/dashboard.md +56 -0
- package/plugins/continuous-improvement/commands/discipline.md +51 -0
- package/plugins/continuous-improvement/commands/harvest.md +76 -0
- package/plugins/continuous-improvement/commands/learn-eval.md +117 -0
- package/plugins/continuous-improvement/commands/planning-with-files.md +66 -0
- package/plugins/continuous-improvement/commands/proceed-with-the-recommendation.md +62 -0
- package/plugins/continuous-improvement/commands/ralph.md +103 -0
- package/plugins/continuous-improvement/commands/release-train.md +81 -0
- package/plugins/continuous-improvement/commands/seven-laws.md +16 -0
- package/plugins/continuous-improvement/commands/superpowers.md +180 -0
- package/plugins/continuous-improvement/commands/swarm.md +101 -0
- package/plugins/continuous-improvement/commands/workspace-surface-audit.md +77 -0
- package/plugins/continuous-improvement/hooks/gateguard.mjs +172 -0
- package/plugins/continuous-improvement/hooks/hooks.json +65 -0
- package/plugins/continuous-improvement/hooks/observe.sh +172 -0
- package/plugins/continuous-improvement/hooks/session.sh +106 -0
- package/plugins/continuous-improvement/hooks/three-section-close.mjs +181 -0
- package/plugins/continuous-improvement/instinct-packs/go.json +58 -0
- package/plugins/continuous-improvement/instinct-packs/meta.json +16 -0
- package/plugins/continuous-improvement/instinct-packs/python.json +58 -0
- package/plugins/continuous-improvement/instinct-packs/react.json +58 -0
- package/plugins/continuous-improvement/lib/observe-event.mjs +128 -0
- package/plugins/continuous-improvement/lib/plugin-metadata.mjs +435 -0
- package/plugins/continuous-improvement/lib/resolve-home-dir.mjs +43 -0
- package/plugins/continuous-improvement/skills/README.md +37 -0
- package/plugins/continuous-improvement/skills/continuous-improvement/SKILL.md +249 -0
- package/plugins/continuous-improvement/skills/deploy-receipt/SKILL.md +178 -0
- package/plugins/continuous-improvement/skills/gateguard/SKILL.md +163 -0
- package/plugins/continuous-improvement/skills/para-memory-files/SKILL.md +108 -0
- package/plugins/continuous-improvement/skills/proceed-with-the-recommendation/SKILL.md +447 -0
- package/plugins/continuous-improvement/skills/ralph/SKILL.md +221 -0
- package/plugins/continuous-improvement/skills/recovery-classification/SKILL.md +73 -0
- package/plugins/continuous-improvement/skills/safety-guard/SKILL.md +76 -0
- package/plugins/continuous-improvement/skills/state-reconciliation/SKILL.md +63 -0
- package/plugins/continuous-improvement/skills/strategic-compact/SKILL.md +104 -0
- package/plugins/continuous-improvement/skills/superpowers/SKILL.md +219 -0
- package/plugins/continuous-improvement/skills/tdd-workflow/SKILL.md +411 -0
- package/plugins/continuous-improvement/skills/token-budget-advisor/SKILL.md +136 -0
- package/plugins/continuous-improvement/skills/verification-loop/SKILL.md +224 -0
- package/plugins/continuous-improvement/skills/wild-risa-balance/SKILL.md +191 -0
- package/plugins/continuous-improvement/skills/workspace-surface-audit/SKILL.md +147 -0
- package/plugins/continuous-improvement/skills/worktree-safety/SKILL.md +66 -0
- package/plugins/continuous-improvement/templates/planning-with-files/findings.md +8 -0
- package/plugins/continuous-improvement/templates/planning-with-files/progress.md +7 -0
- package/plugins/continuous-improvement/templates/planning-with-files/task_plan.md +23 -0
- package/plugins/expert.json +26 -5
- package/skills/README.md +79 -0
- package/skills/deploy-receipt.md +178 -0
- package/skills/gateguard.md +163 -0
- package/skills/para-memory-files.md +108 -0
- package/skills/proceed-with-the-recommendation.md +447 -0
- package/skills/ralph.md +221 -0
- package/skills/recovery-classification.md +73 -0
- package/skills/safety-guard.md +76 -0
- package/skills/state-reconciliation.md +63 -0
- package/skills/strategic-compact.md +104 -0
- package/skills/superpowers.md +219 -0
- package/skills/tdd-workflow.md +411 -0
- package/skills/token-budget-advisor.md +136 -0
- package/skills/verification-loop.md +224 -0
- package/skills/wild-risa-balance.md +191 -0
- package/skills/workspace-surface-audit.md +147 -0
- package/skills/worktree-safety.md +66 -0
- package/templates/insights-claude-md.md +91 -0
- package/templates/planning-with-files/findings.md +8 -0
- package/templates/planning-with-files/progress.md +7 -0
- package/templates/planning-with-files/task_plan.md +23 -0
- package/templates/verify-ladder.example.json +60 -0
|
@@ -0,0 +1,831 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Compound Engineering: iterative brainstorm → plan → work → review loop.
|
|
4
|
+
*
|
|
5
|
+
* Documents learnings across sessions so the same mistakes aren't repeated.
|
|
6
|
+
*/
|
|
7
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
const DEFAULT_OPTIONS = {
|
|
10
|
+
workspace: "./compound-workspace",
|
|
11
|
+
learningsPath: "./learnings.json",
|
|
12
|
+
verbose: false,
|
|
13
|
+
};
|
|
14
|
+
function getErrorMessage(error) {
|
|
15
|
+
return error instanceof Error ? error.message : String(error);
|
|
16
|
+
}
|
|
17
|
+
function clamp(value, min, max) {
|
|
18
|
+
return Math.max(min, Math.min(max, value));
|
|
19
|
+
}
|
|
20
|
+
function formatMinutes(totalMinutes) {
|
|
21
|
+
const minutes = Math.max(0, Math.round(totalMinutes));
|
|
22
|
+
const hours = Math.floor(minutes / 60);
|
|
23
|
+
const remainingMinutes = minutes % 60;
|
|
24
|
+
if (hours === 0) {
|
|
25
|
+
return `${minutes} minutes`;
|
|
26
|
+
}
|
|
27
|
+
if (remainingMinutes === 0) {
|
|
28
|
+
return hours === 1 ? "1 hour" : `${hours} hours`;
|
|
29
|
+
}
|
|
30
|
+
return `${hours}h ${remainingMinutes}m`;
|
|
31
|
+
}
|
|
32
|
+
function parseEstimatedMinutes(value) {
|
|
33
|
+
if (!value) {
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
const normalized = value.toLowerCase();
|
|
37
|
+
const rangeMatch = normalized.match(/(\d+)\s*-\s*(\d+)/);
|
|
38
|
+
if (rangeMatch) {
|
|
39
|
+
const low = Number.parseInt(rangeMatch[1] ?? "0", 10);
|
|
40
|
+
const high = Number.parseInt(rangeMatch[2] ?? "0", 10);
|
|
41
|
+
const average = Math.round((low + high) / 2);
|
|
42
|
+
return normalized.includes("hour") ? average * 60 : average;
|
|
43
|
+
}
|
|
44
|
+
const hourMatch = normalized.match(/(\d+)\s*hour/);
|
|
45
|
+
if (hourMatch) {
|
|
46
|
+
return Number.parseInt(hourMatch[1] ?? "0", 10) * 60;
|
|
47
|
+
}
|
|
48
|
+
const minuteMatch = normalized.match(/(\d+)\s*minute/);
|
|
49
|
+
if (minuteMatch) {
|
|
50
|
+
return Number.parseInt(minuteMatch[1] ?? "0", 10);
|
|
51
|
+
}
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
export default class CompoundEngineering {
|
|
55
|
+
options;
|
|
56
|
+
currentSession = null;
|
|
57
|
+
learnings = new Map();
|
|
58
|
+
constructor(options = {}) {
|
|
59
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
60
|
+
}
|
|
61
|
+
async startSession(projectName, objective) {
|
|
62
|
+
const sessionId = this.generateSessionId();
|
|
63
|
+
const startTime = new Date().toISOString();
|
|
64
|
+
this.currentSession = {
|
|
65
|
+
id: sessionId,
|
|
66
|
+
projectName,
|
|
67
|
+
objective,
|
|
68
|
+
phase: "brainstorming",
|
|
69
|
+
startTime,
|
|
70
|
+
phases: {
|
|
71
|
+
brainstorming: { status: "active", startTime },
|
|
72
|
+
planning: { status: "pending" },
|
|
73
|
+
working: { status: "pending" },
|
|
74
|
+
reviewing: { status: "pending" },
|
|
75
|
+
},
|
|
76
|
+
artifacts: {},
|
|
77
|
+
learnings: [],
|
|
78
|
+
decisions: [],
|
|
79
|
+
};
|
|
80
|
+
await this.ensureWorkspace();
|
|
81
|
+
await this.saveSession();
|
|
82
|
+
await this.loadLearnings();
|
|
83
|
+
this.log(`🚀 Started compound engineering session: ${sessionId}`);
|
|
84
|
+
this.log(`📋 Objective: ${objective}`);
|
|
85
|
+
return this.currentSession;
|
|
86
|
+
}
|
|
87
|
+
async brainstorm(context = {}) {
|
|
88
|
+
const session = this.requireSession();
|
|
89
|
+
const phaseStart = new Date().toISOString();
|
|
90
|
+
session.phase = "brainstorming";
|
|
91
|
+
session.phases.brainstorming.startTime ||= phaseStart;
|
|
92
|
+
session.phases.brainstorming.status = "active";
|
|
93
|
+
const relevantLearnings = this.getRelevantLearnings("brainstorming");
|
|
94
|
+
const brainstormResults = this.generateBrainstormResults(session.objective, context, relevantLearnings);
|
|
95
|
+
session.artifacts.brainstorming = brainstormResults;
|
|
96
|
+
session.phases.brainstorming.endTime = new Date().toISOString();
|
|
97
|
+
session.phases.brainstorming.status = "completed";
|
|
98
|
+
await this.saveSession();
|
|
99
|
+
this.log(`💭 Brainstorming phase completed`);
|
|
100
|
+
return brainstormResults;
|
|
101
|
+
}
|
|
102
|
+
async plan(brainstormResults = {}, preferences = {}) {
|
|
103
|
+
const session = this.requireSession();
|
|
104
|
+
const phaseStart = new Date().toISOString();
|
|
105
|
+
session.phase = "planning";
|
|
106
|
+
session.phases.brainstorming.status = "completed";
|
|
107
|
+
session.phases.brainstorming.endTime ||= phaseStart;
|
|
108
|
+
session.phases.planning.startTime ||= phaseStart;
|
|
109
|
+
session.phases.planning.status = "active";
|
|
110
|
+
const normalized = {
|
|
111
|
+
ideas: brainstormResults.ideas ?? [],
|
|
112
|
+
constraints: brainstormResults.constraints ?? [],
|
|
113
|
+
assumptions: brainstormResults.assumptions ?? [],
|
|
114
|
+
risks: brainstormResults.risks ?? [],
|
|
115
|
+
opportunities: brainstormResults.opportunities ?? [],
|
|
116
|
+
timestamp: brainstormResults.timestamp ?? new Date().toISOString(),
|
|
117
|
+
};
|
|
118
|
+
const relevantLearnings = this.getRelevantLearnings("planning");
|
|
119
|
+
const plan = this.createPlan(session.objective, normalized, preferences, relevantLearnings);
|
|
120
|
+
session.artifacts.planning = plan;
|
|
121
|
+
session.decisions.push(...plan.decisions);
|
|
122
|
+
session.phases.planning.endTime = new Date().toISOString();
|
|
123
|
+
session.phases.planning.status = "completed";
|
|
124
|
+
await this.saveSession();
|
|
125
|
+
this.log(`📋 Planning phase completed`);
|
|
126
|
+
return plan;
|
|
127
|
+
}
|
|
128
|
+
async work(plan, progressCallback = null) {
|
|
129
|
+
const session = this.requireSession();
|
|
130
|
+
const phaseStart = new Date().toISOString();
|
|
131
|
+
session.phase = "working";
|
|
132
|
+
session.phases.planning.status = "completed";
|
|
133
|
+
session.phases.planning.endTime ||= phaseStart;
|
|
134
|
+
session.phases.working.startTime ||= phaseStart;
|
|
135
|
+
session.phases.working.status = "active";
|
|
136
|
+
const workLog = {
|
|
137
|
+
steps: [],
|
|
138
|
+
issues: [],
|
|
139
|
+
solutions: [],
|
|
140
|
+
timestamp: new Date().toISOString(),
|
|
141
|
+
};
|
|
142
|
+
for (const step of plan.steps) {
|
|
143
|
+
const stepResult = await this.executeStep(step, progressCallback);
|
|
144
|
+
workLog.steps.push(stepResult);
|
|
145
|
+
if (stepResult.issues.length > 0) {
|
|
146
|
+
workLog.issues.push(...stepResult.issues);
|
|
147
|
+
workLog.solutions.push(...stepResult.solutions);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
session.artifacts.working = workLog;
|
|
151
|
+
session.phases.working.endTime = new Date().toISOString();
|
|
152
|
+
session.phases.working.status = "completed";
|
|
153
|
+
await this.saveSession();
|
|
154
|
+
this.log(`🔧 Working phase completed`);
|
|
155
|
+
return workLog;
|
|
156
|
+
}
|
|
157
|
+
async review(workResults = {}, criteria = null) {
|
|
158
|
+
const session = this.requireSession();
|
|
159
|
+
const phaseStart = new Date().toISOString();
|
|
160
|
+
session.phase = "reviewing";
|
|
161
|
+
session.phases.working.status = "completed";
|
|
162
|
+
session.phases.working.endTime ||= phaseStart;
|
|
163
|
+
session.phases.reviewing.startTime ||= phaseStart;
|
|
164
|
+
session.phases.reviewing.status = "active";
|
|
165
|
+
const normalized = {
|
|
166
|
+
steps: workResults.steps ?? [],
|
|
167
|
+
issues: workResults.issues ?? [],
|
|
168
|
+
solutions: workResults.solutions ?? [],
|
|
169
|
+
timestamp: workResults.timestamp ?? new Date().toISOString(),
|
|
170
|
+
};
|
|
171
|
+
const review = this.performReview(session, normalized, criteria);
|
|
172
|
+
const sessionLearnings = this.extractLearnings(session, review);
|
|
173
|
+
session.learnings = sessionLearnings;
|
|
174
|
+
await this.saveLearnings(sessionLearnings);
|
|
175
|
+
session.artifacts.reviewing = review;
|
|
176
|
+
session.phases.reviewing.status = "completed";
|
|
177
|
+
session.endTime = new Date().toISOString();
|
|
178
|
+
await this.saveSession();
|
|
179
|
+
this.log(`🔍 Reviewing phase completed`);
|
|
180
|
+
this.log(`📚 Extracted ${sessionLearnings.length} learnings`);
|
|
181
|
+
return { review, learnings: sessionLearnings };
|
|
182
|
+
}
|
|
183
|
+
generateBrainstormPrompt(objective, context, relevantLearnings) {
|
|
184
|
+
let prompt = `Brainstorm ideas for: ${objective}\n\n`;
|
|
185
|
+
if (Object.keys(context).length > 0) {
|
|
186
|
+
prompt += `Context:\n${JSON.stringify(context, null, 2)}\n\n`;
|
|
187
|
+
}
|
|
188
|
+
if (relevantLearnings.length > 0) {
|
|
189
|
+
prompt += `Relevant past learnings:\n`;
|
|
190
|
+
for (const learning of relevantLearnings) {
|
|
191
|
+
prompt += `- ${learning.summary}\n`;
|
|
192
|
+
}
|
|
193
|
+
prompt += "\n";
|
|
194
|
+
}
|
|
195
|
+
prompt += `Please brainstorm:
|
|
196
|
+
1. Potential approaches and solutions
|
|
197
|
+
2. Constraints and limitations
|
|
198
|
+
3. Key assumptions
|
|
199
|
+
4. Potential risks
|
|
200
|
+
5. Opportunities and innovations
|
|
201
|
+
|
|
202
|
+
Structure your response as JSON with keys: ideas, constraints, assumptions, risks, opportunities`;
|
|
203
|
+
return prompt;
|
|
204
|
+
}
|
|
205
|
+
createPlan(objective, brainstormResults, preferences, relevantLearnings) {
|
|
206
|
+
const approach = this.selectApproach(brainstormResults.ideas);
|
|
207
|
+
const steps = this.createExecutionSteps(approach, brainstormResults.constraints);
|
|
208
|
+
const decisions = this.buildPlanDecisions(objective, brainstormResults.constraints, relevantLearnings, preferences);
|
|
209
|
+
const plan = {
|
|
210
|
+
objective,
|
|
211
|
+
approach,
|
|
212
|
+
steps,
|
|
213
|
+
timeline: this.estimateTimeline(steps),
|
|
214
|
+
resources: this.identifyResources(steps, brainstormResults.constraints, relevantLearnings),
|
|
215
|
+
risks: brainstormResults.risks,
|
|
216
|
+
mitigations: this.createMitigations(brainstormResults.risks),
|
|
217
|
+
decisions,
|
|
218
|
+
successCriteria: this.defineSuccessCriteria(objective, brainstormResults.constraints),
|
|
219
|
+
timestamp: new Date().toISOString(),
|
|
220
|
+
};
|
|
221
|
+
if (relevantLearnings.length > 0) {
|
|
222
|
+
plan.considerations = relevantLearnings.map((l) => l.summary);
|
|
223
|
+
}
|
|
224
|
+
return plan;
|
|
225
|
+
}
|
|
226
|
+
async executeStep(step, progressCallback) {
|
|
227
|
+
const stepResult = {
|
|
228
|
+
step: step.description,
|
|
229
|
+
status: "pending",
|
|
230
|
+
startTime: new Date().toISOString(),
|
|
231
|
+
issues: [],
|
|
232
|
+
solutions: [],
|
|
233
|
+
artifacts: [],
|
|
234
|
+
};
|
|
235
|
+
try {
|
|
236
|
+
this.log(`⚡ Executing: ${step.description}`);
|
|
237
|
+
progressCallback?.(`Starting: ${step.description}`);
|
|
238
|
+
stepResult.status = "in_progress";
|
|
239
|
+
const knownIssues = this.checkForKnownIssues(step);
|
|
240
|
+
if (knownIssues.length > 0) {
|
|
241
|
+
stepResult.issues.push(...knownIssues);
|
|
242
|
+
for (const issue of knownIssues) {
|
|
243
|
+
stepResult.solutions.push(...this.getKnownSolutions(issue));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
stepResult.status = "completed";
|
|
247
|
+
stepResult.endTime = new Date().toISOString();
|
|
248
|
+
progressCallback?.(`Completed: ${step.description}`);
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
stepResult.status = "failed";
|
|
252
|
+
stepResult.error = getErrorMessage(error);
|
|
253
|
+
stepResult.endTime = new Date().toISOString();
|
|
254
|
+
}
|
|
255
|
+
return stepResult;
|
|
256
|
+
}
|
|
257
|
+
performReview(session, workResults, criteria) {
|
|
258
|
+
void criteria;
|
|
259
|
+
const review = {
|
|
260
|
+
sessionId: session.id,
|
|
261
|
+
objective: session.objective,
|
|
262
|
+
overallScore: 0,
|
|
263
|
+
strengths: [],
|
|
264
|
+
weaknesses: [],
|
|
265
|
+
improvements: [],
|
|
266
|
+
unexpectedOutcomes: [],
|
|
267
|
+
processMetrics: this.calculateProcessMetrics(session),
|
|
268
|
+
outcomeMetrics: this.calculateOutcomeMetrics(workResults),
|
|
269
|
+
recommendations: [],
|
|
270
|
+
timestamp: new Date().toISOString(),
|
|
271
|
+
};
|
|
272
|
+
for (const [phase, artifact] of Object.entries(session.artifacts)) {
|
|
273
|
+
const phaseReview = this.reviewPhase(phase, artifact);
|
|
274
|
+
review.strengths.push(...phaseReview.strengths);
|
|
275
|
+
review.weaknesses.push(...phaseReview.weaknesses);
|
|
276
|
+
review.improvements.push(...phaseReview.improvements);
|
|
277
|
+
}
|
|
278
|
+
review.overallScore = this.calculateOverallScore(review);
|
|
279
|
+
review.recommendations = this.generateRecommendations(review);
|
|
280
|
+
return review;
|
|
281
|
+
}
|
|
282
|
+
extractLearnings(session, review) {
|
|
283
|
+
const learnings = [];
|
|
284
|
+
for (const learning of session.learnings) {
|
|
285
|
+
learnings.push({
|
|
286
|
+
id: this.generateLearningId(),
|
|
287
|
+
type: learning.type ?? "process",
|
|
288
|
+
category: learning.category ?? "general",
|
|
289
|
+
summary: learning.summary,
|
|
290
|
+
context: learning.context,
|
|
291
|
+
impact: learning.impact ?? "medium",
|
|
292
|
+
confidence: learning.confidence ?? 0.7,
|
|
293
|
+
sessionId: session.id,
|
|
294
|
+
timestamp: new Date().toISOString(),
|
|
295
|
+
tags: this.extractTags(learning),
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
if (review.weaknesses.length > 0) {
|
|
299
|
+
learnings.push({
|
|
300
|
+
id: this.generateLearningId(),
|
|
301
|
+
type: "improvement",
|
|
302
|
+
category: "quality",
|
|
303
|
+
summary: `Address identified weaknesses: ${review.weaknesses.slice(0, 3).join(", ")}`,
|
|
304
|
+
context: { weaknesses: review.weaknesses },
|
|
305
|
+
impact: "high",
|
|
306
|
+
confidence: 0.8,
|
|
307
|
+
sessionId: session.id,
|
|
308
|
+
timestamp: new Date().toISOString(),
|
|
309
|
+
tags: ["quality", "improvement"],
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
return learnings;
|
|
313
|
+
}
|
|
314
|
+
generateSessionId() {
|
|
315
|
+
return `session_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
316
|
+
}
|
|
317
|
+
generateLearningId() {
|
|
318
|
+
return `learning_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
319
|
+
}
|
|
320
|
+
async ensureWorkspace() {
|
|
321
|
+
try {
|
|
322
|
+
await mkdir(this.options.workspace, { recursive: true });
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
// Directory may already exist.
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async saveSession() {
|
|
329
|
+
if (!this.currentSession)
|
|
330
|
+
return;
|
|
331
|
+
const sessionPath = join(this.options.workspace, `${this.currentSession.id}.json`);
|
|
332
|
+
await writeFile(sessionPath, JSON.stringify(this.currentSession, null, 2));
|
|
333
|
+
}
|
|
334
|
+
async loadLearnings() {
|
|
335
|
+
try {
|
|
336
|
+
const content = await readFile(this.options.learningsPath, "utf8");
|
|
337
|
+
const parsed = JSON.parse(content);
|
|
338
|
+
this.learnings = new Map(parsed.map((l) => [l.id, l]));
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
this.learnings = new Map();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
async saveLearnings(newLearnings) {
|
|
345
|
+
for (const learning of newLearnings) {
|
|
346
|
+
this.learnings.set(learning.id, learning);
|
|
347
|
+
}
|
|
348
|
+
const learningsArray = Array.from(this.learnings.values());
|
|
349
|
+
await writeFile(this.options.learningsPath, JSON.stringify(learningsArray, null, 2));
|
|
350
|
+
}
|
|
351
|
+
getRelevantLearnings(phase) {
|
|
352
|
+
return Array.from(this.learnings.values())
|
|
353
|
+
.filter((l) => l.phase === phase || l.category === "general")
|
|
354
|
+
.filter((l) => l.confidence >= 0.5)
|
|
355
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
356
|
+
.slice(0, 5);
|
|
357
|
+
}
|
|
358
|
+
selectApproach(ideas) {
|
|
359
|
+
if (ideas.length === 0) {
|
|
360
|
+
return {
|
|
361
|
+
description: "Incremental implementation with explicit verification checkpoints",
|
|
362
|
+
pros: ["Keeps scope tight", "Reduces regressions"],
|
|
363
|
+
cons: [],
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
const scoredIdeas = ideas.map((idea, index) => {
|
|
367
|
+
if (typeof idea === "string") {
|
|
368
|
+
return { idea, score: 1 - index * 0.01 };
|
|
369
|
+
}
|
|
370
|
+
const pros = idea.pros?.length ?? 0;
|
|
371
|
+
const cons = idea.cons?.length ?? 0;
|
|
372
|
+
const hasDescription = idea.description ? 1 : 0;
|
|
373
|
+
return {
|
|
374
|
+
idea,
|
|
375
|
+
score: pros * 2 + hasDescription - cons - index * 0.01,
|
|
376
|
+
};
|
|
377
|
+
});
|
|
378
|
+
scoredIdeas.sort((left, right) => right.score - left.score);
|
|
379
|
+
return scoredIdeas[0]?.idea ?? ideas[0];
|
|
380
|
+
}
|
|
381
|
+
estimateTimeline(steps) {
|
|
382
|
+
const workingMinutes = steps.reduce((total, step) => total + parseEstimatedMinutes(step.estimatedTime), 0);
|
|
383
|
+
const planningMinutes = 20;
|
|
384
|
+
const brainstormingMinutes = 20;
|
|
385
|
+
const reviewingMinutes = 15;
|
|
386
|
+
const totalMinutes = brainstormingMinutes + planningMinutes + workingMinutes + reviewingMinutes;
|
|
387
|
+
return {
|
|
388
|
+
estimated: formatMinutes(totalMinutes),
|
|
389
|
+
phases: {
|
|
390
|
+
brainstorming: formatMinutes(brainstormingMinutes),
|
|
391
|
+
planning: formatMinutes(planningMinutes),
|
|
392
|
+
working: formatMinutes(workingMinutes),
|
|
393
|
+
reviewing: formatMinutes(reviewingMinutes),
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
identifyResources(steps, constraints, relevantLearnings) {
|
|
398
|
+
const tools = new Set();
|
|
399
|
+
const dependencies = new Set();
|
|
400
|
+
for (const step of steps) {
|
|
401
|
+
const description = step.description.toLowerCase();
|
|
402
|
+
if (description.includes("review") || description.includes("inspect")) {
|
|
403
|
+
tools.add("code search");
|
|
404
|
+
}
|
|
405
|
+
if (description.includes("implement") ||
|
|
406
|
+
description.includes("build") ||
|
|
407
|
+
description.includes("deliver")) {
|
|
408
|
+
tools.add("editor/runtime");
|
|
409
|
+
}
|
|
410
|
+
if (description.includes("verify") ||
|
|
411
|
+
description.includes("test") ||
|
|
412
|
+
description.includes("validate")) {
|
|
413
|
+
tools.add("test runner");
|
|
414
|
+
}
|
|
415
|
+
if (description.includes("document")) {
|
|
416
|
+
tools.add("documentation");
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
for (const constraint of constraints) {
|
|
420
|
+
const normalized = constraint.toLowerCase();
|
|
421
|
+
if (normalized.includes("api"))
|
|
422
|
+
dependencies.add("api contract");
|
|
423
|
+
if (normalized.includes("database") || normalized.includes("schema")) {
|
|
424
|
+
dependencies.add("data model");
|
|
425
|
+
}
|
|
426
|
+
if (normalized.includes("service"))
|
|
427
|
+
dependencies.add("service boundary");
|
|
428
|
+
if (normalized.includes("ui") || normalized.includes("frontend")) {
|
|
429
|
+
dependencies.add("ui surface");
|
|
430
|
+
}
|
|
431
|
+
if (normalized.includes("test"))
|
|
432
|
+
dependencies.add("existing test coverage");
|
|
433
|
+
}
|
|
434
|
+
return {
|
|
435
|
+
tools: Array.from(tools),
|
|
436
|
+
dependencies: Array.from(dependencies),
|
|
437
|
+
skills: relevantLearnings.slice(0, 3).map((learning) => learning.summary),
|
|
438
|
+
time: this.estimateTimeline(steps).estimated,
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
createMitigations(risks) {
|
|
442
|
+
return risks.map((risk) => ({
|
|
443
|
+
risk,
|
|
444
|
+
mitigation: `Monitor and address ${risk.toLowerCase()} early`,
|
|
445
|
+
}));
|
|
446
|
+
}
|
|
447
|
+
defineSuccessCriteria(objective, constraints) {
|
|
448
|
+
const criteria = [
|
|
449
|
+
`Deliver the objective: ${objective}`,
|
|
450
|
+
"Complete verification without critical regressions",
|
|
451
|
+
"Document decisions and learnings for the next iteration",
|
|
452
|
+
];
|
|
453
|
+
if (constraints.length > 0) {
|
|
454
|
+
criteria.push(`Satisfy key constraints: ${constraints.slice(0, 2).join("; ")}`);
|
|
455
|
+
}
|
|
456
|
+
return criteria;
|
|
457
|
+
}
|
|
458
|
+
createExecutionSteps(approach, constraints) {
|
|
459
|
+
const approachDescription = typeof approach === "string"
|
|
460
|
+
? approach
|
|
461
|
+
: approach.description ?? "the chosen implementation path";
|
|
462
|
+
const normalizedApproach = approachDescription.replace(/\.$/, "").toLowerCase();
|
|
463
|
+
const steps = [
|
|
464
|
+
{
|
|
465
|
+
description: "Inspect the existing implementation surface and confirm the smallest safe change",
|
|
466
|
+
estimatedTime: "20 minutes",
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
description: `Implement ${normalizedApproach}`,
|
|
470
|
+
estimatedTime: "45 minutes",
|
|
471
|
+
},
|
|
472
|
+
];
|
|
473
|
+
if (constraints.length > 0) {
|
|
474
|
+
steps.push({
|
|
475
|
+
description: `Validate constraints and edge cases: ${constraints.slice(0, 2).join("; ")}`,
|
|
476
|
+
estimatedTime: "20 minutes",
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
steps.push({
|
|
480
|
+
description: "Run targeted verification and regression checks",
|
|
481
|
+
estimatedTime: "20 minutes",
|
|
482
|
+
}, {
|
|
483
|
+
description: "Document decisions, learnings, and follow-up work",
|
|
484
|
+
estimatedTime: "15 minutes",
|
|
485
|
+
});
|
|
486
|
+
return steps;
|
|
487
|
+
}
|
|
488
|
+
checkForKnownIssues(step) {
|
|
489
|
+
return Array.from(this.learnings.values())
|
|
490
|
+
.filter((l) => l.type === "issue" &&
|
|
491
|
+
l.context &&
|
|
492
|
+
l.context.step === step.description)
|
|
493
|
+
.map((l) => l.summary);
|
|
494
|
+
}
|
|
495
|
+
getKnownSolutions(issue) {
|
|
496
|
+
return Array.from(this.learnings.values())
|
|
497
|
+
.filter((l) => l.type === "solution" &&
|
|
498
|
+
l.context &&
|
|
499
|
+
l.context.issue === issue)
|
|
500
|
+
.map((l) => l.summary);
|
|
501
|
+
}
|
|
502
|
+
reviewPhase(phase, artifact) {
|
|
503
|
+
const strengths = [];
|
|
504
|
+
const weaknesses = [];
|
|
505
|
+
const improvements = [];
|
|
506
|
+
if (phase === "brainstorming") {
|
|
507
|
+
const brainstorm = artifact;
|
|
508
|
+
if (brainstorm.ideas.length > 0) {
|
|
509
|
+
strengths.push(`Brainstorm produced ${brainstorm.ideas.length} concrete approach ideas`);
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
weaknesses.push("Brainstorming did not capture any concrete approaches");
|
|
513
|
+
improvements.push("Capture at least one concrete approach before planning");
|
|
514
|
+
}
|
|
515
|
+
if (brainstorm.risks.length > 0) {
|
|
516
|
+
strengths.push(`Brainstorm surfaced ${brainstorm.risks.length} delivery risks early`);
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
weaknesses.push("Brainstorming did not identify delivery risks");
|
|
520
|
+
}
|
|
521
|
+
if (brainstorm.constraints.length === 0) {
|
|
522
|
+
improvements.push("Record explicit constraints to keep the plan grounded");
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
else if (phase === "planning") {
|
|
526
|
+
const plan = artifact;
|
|
527
|
+
if (plan.steps.length > 0) {
|
|
528
|
+
strengths.push(`Plan defines ${plan.steps.length} executable steps`);
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
weaknesses.push("Planning produced no executable steps");
|
|
532
|
+
}
|
|
533
|
+
if (plan.successCriteria.length > 0) {
|
|
534
|
+
strengths.push("Plan includes explicit success criteria");
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
weaknesses.push("Plan is missing success criteria");
|
|
538
|
+
improvements.push("Define verification-oriented success criteria");
|
|
539
|
+
}
|
|
540
|
+
if (plan.risks.length === 0) {
|
|
541
|
+
improvements.push("Capture at least one explicit risk and mitigation");
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
else if (phase === "working") {
|
|
545
|
+
const work = artifact;
|
|
546
|
+
const completedSteps = work.steps.filter((step) => step.status === "completed").length;
|
|
547
|
+
const failedSteps = work.steps.filter((step) => step.status === "failed").length;
|
|
548
|
+
if (completedSteps > 0) {
|
|
549
|
+
strengths.push(`Execution completed ${completedSteps}/${work.steps.length} planned steps`);
|
|
550
|
+
}
|
|
551
|
+
if (work.solutions.length > 0) {
|
|
552
|
+
strengths.push(`Execution documented ${work.solutions.length} issue resolution(s)`);
|
|
553
|
+
}
|
|
554
|
+
if (failedSteps > 0) {
|
|
555
|
+
weaknesses.push(`${failedSteps} execution step(s) failed`);
|
|
556
|
+
}
|
|
557
|
+
if (work.issues.length > work.solutions.length) {
|
|
558
|
+
improvements.push("Close the gap between discovered issues and documented solutions");
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
else if (phase === "reviewing") {
|
|
562
|
+
const review = artifact;
|
|
563
|
+
if (review.recommendations.length > 0) {
|
|
564
|
+
strengths.push(`Review produced ${review.recommendations.length} follow-up recommendation(s)`);
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
weaknesses.push("Review phase produced no follow-up recommendations");
|
|
568
|
+
}
|
|
569
|
+
if (review.overallScore < 80) {
|
|
570
|
+
improvements.push("Increase verification depth before closing the session");
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (strengths.length === 0 && weaknesses.length === 0 && improvements.length === 0) {
|
|
574
|
+
strengths.push(`Phase ${phase} completed and recorded`);
|
|
575
|
+
}
|
|
576
|
+
return { strengths, weaknesses, improvements };
|
|
577
|
+
}
|
|
578
|
+
calculateProcessMetrics(session) {
|
|
579
|
+
const phases = session.phases;
|
|
580
|
+
const start = session.startTime ? new Date(session.startTime).getTime() : NaN;
|
|
581
|
+
const end = session.endTime ? new Date(session.endTime).getTime() : NaN;
|
|
582
|
+
const totalMs = Number.isFinite(start) && Number.isFinite(end) ? end - start : 0;
|
|
583
|
+
const phaseDurations = {};
|
|
584
|
+
for (const [phase, data] of Object.entries(phases)) {
|
|
585
|
+
if (data.startTime && data.endTime) {
|
|
586
|
+
const phaseMs = new Date(data.endTime).getTime() - new Date(data.startTime).getTime();
|
|
587
|
+
phaseDurations[phase] = `${Math.round(phaseMs / 60000)} minutes`;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return {
|
|
591
|
+
totalDuration: `${Math.round(totalMs / 60000)} minutes`,
|
|
592
|
+
phaseDurations,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
calculateOutcomeMetrics(workResults) {
|
|
596
|
+
return {
|
|
597
|
+
stepsCompleted: workResults.steps.filter((s) => s.status === "completed").length,
|
|
598
|
+
issuesEncountered: workResults.issues.length,
|
|
599
|
+
solutionsApplied: workResults.solutions.length,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
calculateOverallScore(review) {
|
|
603
|
+
let score = 70;
|
|
604
|
+
score += review.strengths.length * 5;
|
|
605
|
+
score -= review.weaknesses.length * 10;
|
|
606
|
+
score += review.improvements.length * 2;
|
|
607
|
+
return Math.max(0, Math.min(100, score));
|
|
608
|
+
}
|
|
609
|
+
generateRecommendations(review) {
|
|
610
|
+
const recommendations = [];
|
|
611
|
+
if (review.weaknesses.length > 0) {
|
|
612
|
+
recommendations.push("Focus on addressing identified weaknesses in future sessions");
|
|
613
|
+
}
|
|
614
|
+
const totalDuration = review.processMetrics.totalDuration;
|
|
615
|
+
if (typeof totalDuration === "string" && totalDuration.includes("minutes")) {
|
|
616
|
+
const minutes = parseInt(totalDuration, 10);
|
|
617
|
+
if (Number.isFinite(minutes) && minutes > 180) {
|
|
618
|
+
recommendations.push("Consider breaking down large objectives into smaller sessions");
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return recommendations;
|
|
622
|
+
}
|
|
623
|
+
extractTags(learning) {
|
|
624
|
+
const tags = [];
|
|
625
|
+
if (learning.type)
|
|
626
|
+
tags.push(learning.type);
|
|
627
|
+
if (learning.category)
|
|
628
|
+
tags.push(learning.category);
|
|
629
|
+
const tech = learning.context?.technology;
|
|
630
|
+
if (tech)
|
|
631
|
+
tags.push(tech);
|
|
632
|
+
return tags;
|
|
633
|
+
}
|
|
634
|
+
generateBrainstormResults(objective, context, relevantLearnings) {
|
|
635
|
+
const contextEntries = Object.entries(context).filter(([, value]) => value !== undefined &&
|
|
636
|
+
value !== null &&
|
|
637
|
+
!(typeof value === "string" && value.trim().length === 0));
|
|
638
|
+
const contextLabels = contextEntries.map(([key]) => this.humanizeKey(key));
|
|
639
|
+
const leadLearning = relevantLearnings[0];
|
|
640
|
+
const ideas = [
|
|
641
|
+
`Deliver the smallest verifiable slice of "${objective}" first`,
|
|
642
|
+
contextLabels.length > 0
|
|
643
|
+
? `Reuse existing context around ${contextLabels.slice(0, 2).join(" and ")} before introducing new work`
|
|
644
|
+
: "Inspect the current implementation before changing anything",
|
|
645
|
+
];
|
|
646
|
+
if (leadLearning) {
|
|
647
|
+
ideas.push({
|
|
648
|
+
description: "Apply the strongest relevant prior learning to reduce repeated mistakes",
|
|
649
|
+
pros: [leadLearning.summary],
|
|
650
|
+
cons: [],
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
const constraints = contextEntries.map(([key, value]) => `${this.humanizeKey(key)}: ${this.summarizeContextValue(value)}`);
|
|
654
|
+
const assumptions = [
|
|
655
|
+
"The task can be delivered incrementally with verification checkpoints",
|
|
656
|
+
contextEntries.length > 0
|
|
657
|
+
? "The supplied context reflects the current project constraints"
|
|
658
|
+
: "Additional project constraints will surface during implementation",
|
|
659
|
+
];
|
|
660
|
+
const risks = [
|
|
661
|
+
"Existing implementation may already cover part of the objective",
|
|
662
|
+
constraints.length > 0
|
|
663
|
+
? `Constraints may limit the solution space: ${constraints[0]}`
|
|
664
|
+
: "Unstated constraints may appear during execution",
|
|
665
|
+
];
|
|
666
|
+
const opportunities = [
|
|
667
|
+
"Reuse existing implementation instead of creating parallel paths",
|
|
668
|
+
relevantLearnings.length > 0
|
|
669
|
+
? "Promote repeatable learnings into default workflow habits"
|
|
670
|
+
: "Capture a reusable learning from this session",
|
|
671
|
+
];
|
|
672
|
+
return {
|
|
673
|
+
ideas,
|
|
674
|
+
constraints,
|
|
675
|
+
assumptions,
|
|
676
|
+
risks,
|
|
677
|
+
opportunities,
|
|
678
|
+
timestamp: new Date().toISOString(),
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
buildPlanDecisions(objective, constraints, relevantLearnings, preferences) {
|
|
682
|
+
const decisions = [
|
|
683
|
+
`Optimize for an incremental delivery path for: ${objective}`,
|
|
684
|
+
"Keep verification as a first-class step instead of a final afterthought",
|
|
685
|
+
];
|
|
686
|
+
if (constraints.length > 0) {
|
|
687
|
+
decisions.push(`Honor the recorded constraints before expanding scope`);
|
|
688
|
+
}
|
|
689
|
+
if (Object.keys(preferences).length > 0) {
|
|
690
|
+
decisions.push("Respect explicit execution preferences supplied for the session");
|
|
691
|
+
}
|
|
692
|
+
if (relevantLearnings.length > 0) {
|
|
693
|
+
decisions.push(`Reuse prior learning: ${relevantLearnings[0].summary}`);
|
|
694
|
+
}
|
|
695
|
+
return decisions;
|
|
696
|
+
}
|
|
697
|
+
humanizeKey(key) {
|
|
698
|
+
return key
|
|
699
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
700
|
+
.replace(/[_-]+/g, " ")
|
|
701
|
+
.toLowerCase();
|
|
702
|
+
}
|
|
703
|
+
summarizeContextValue(value) {
|
|
704
|
+
if (typeof value === "string") {
|
|
705
|
+
return value.length > 60 ? `${value.slice(0, 57)}...` : value;
|
|
706
|
+
}
|
|
707
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
708
|
+
return String(value);
|
|
709
|
+
}
|
|
710
|
+
if (Array.isArray(value)) {
|
|
711
|
+
return value.slice(0, 3).map((item) => this.summarizeContextValue(item)).join(", ");
|
|
712
|
+
}
|
|
713
|
+
if (value && typeof value === "object") {
|
|
714
|
+
return `object with keys: ${Object.keys(value).slice(0, 3).join(", ")}`;
|
|
715
|
+
}
|
|
716
|
+
return "unspecified";
|
|
717
|
+
}
|
|
718
|
+
log(message) {
|
|
719
|
+
if (this.options.verbose) {
|
|
720
|
+
console.log(`[CompoundEngineering] ${message}`);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
getSessionSummary(_sessionId) {
|
|
724
|
+
return this.currentSession;
|
|
725
|
+
}
|
|
726
|
+
getAllLearnings() {
|
|
727
|
+
return Array.from(this.learnings.values());
|
|
728
|
+
}
|
|
729
|
+
searchLearnings(query) {
|
|
730
|
+
const lowerQuery = query.toLowerCase();
|
|
731
|
+
return Array.from(this.learnings.values()).filter((l) => l.summary.toLowerCase().includes(lowerQuery) ||
|
|
732
|
+
(l.tags ?? []).some((tag) => tag.toLowerCase().includes(lowerQuery)) ||
|
|
733
|
+
l.category.toLowerCase().includes(lowerQuery));
|
|
734
|
+
}
|
|
735
|
+
requireSession() {
|
|
736
|
+
if (!this.currentSession) {
|
|
737
|
+
throw new Error("No active session. Call startSession() first.");
|
|
738
|
+
}
|
|
739
|
+
return this.currentSession;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
743
|
+
const [, , command, ...args] = process.argv;
|
|
744
|
+
if (!command || command === "--help" || command === "-h") {
|
|
745
|
+
console.log(`
|
|
746
|
+
Usage: compound-engineering <command> [options]
|
|
747
|
+
|
|
748
|
+
Commands:
|
|
749
|
+
start <project-name> <objective> Start a new session
|
|
750
|
+
list-sessions List all sessions
|
|
751
|
+
learnings Show all learnings
|
|
752
|
+
search <query> Search learnings
|
|
753
|
+
|
|
754
|
+
Options:
|
|
755
|
+
--workspace <dir> Workspace directory (default: ./compound-workspace)
|
|
756
|
+
--learnings <file> Learnings file path (default: ./learnings.json)
|
|
757
|
+
--verbose Enable verbose logging
|
|
758
|
+
`);
|
|
759
|
+
process.exit(0);
|
|
760
|
+
}
|
|
761
|
+
const options = {};
|
|
762
|
+
for (let i = 0; i < args.length; i++) {
|
|
763
|
+
const flag = args[i];
|
|
764
|
+
if (flag === "--workspace" && args[i + 1]) {
|
|
765
|
+
options.workspace = args[i + 1];
|
|
766
|
+
i++;
|
|
767
|
+
}
|
|
768
|
+
else if (flag === "--learnings" && args[i + 1]) {
|
|
769
|
+
options.learningsPath = args[i + 1];
|
|
770
|
+
i++;
|
|
771
|
+
}
|
|
772
|
+
else if (flag === "--verbose") {
|
|
773
|
+
options.verbose = true;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
const compoundEngineering = new CompoundEngineering(options);
|
|
777
|
+
switch (command) {
|
|
778
|
+
case "start":
|
|
779
|
+
if (args.length < 2) {
|
|
780
|
+
console.error("Error: Project name and objective required");
|
|
781
|
+
process.exit(1);
|
|
782
|
+
}
|
|
783
|
+
compoundEngineering
|
|
784
|
+
.startSession(args[0], args.slice(1).join(" "))
|
|
785
|
+
.then((session) => {
|
|
786
|
+
console.log(`✅ Session started: ${session.id}`);
|
|
787
|
+
})
|
|
788
|
+
.catch((error) => {
|
|
789
|
+
console.error(`❌ Error: ${getErrorMessage(error)}`);
|
|
790
|
+
process.exit(1);
|
|
791
|
+
});
|
|
792
|
+
break;
|
|
793
|
+
case "learnings":
|
|
794
|
+
compoundEngineering
|
|
795
|
+
.loadLearnings()
|
|
796
|
+
.then(() => {
|
|
797
|
+
const learnings = compoundEngineering.getAllLearnings();
|
|
798
|
+
console.log(`📚 Found ${learnings.length} learnings:`);
|
|
799
|
+
for (const learning of learnings) {
|
|
800
|
+
console.log(` [${learning.confidence.toFixed(2)}] ${learning.summary}`);
|
|
801
|
+
}
|
|
802
|
+
})
|
|
803
|
+
.catch((error) => {
|
|
804
|
+
console.error(`❌ Error: ${getErrorMessage(error)}`);
|
|
805
|
+
process.exit(1);
|
|
806
|
+
});
|
|
807
|
+
break;
|
|
808
|
+
case "search":
|
|
809
|
+
if (args.length === 0) {
|
|
810
|
+
console.error("Error: Search query required");
|
|
811
|
+
process.exit(1);
|
|
812
|
+
}
|
|
813
|
+
compoundEngineering
|
|
814
|
+
.loadLearnings()
|
|
815
|
+
.then(() => {
|
|
816
|
+
const results = compoundEngineering.searchLearnings(args.join(" "));
|
|
817
|
+
console.log(`🔍 Found ${results.length} matching learnings:`);
|
|
818
|
+
for (const learning of results) {
|
|
819
|
+
console.log(` [${learning.confidence.toFixed(2)}] ${learning.summary}`);
|
|
820
|
+
}
|
|
821
|
+
})
|
|
822
|
+
.catch((error) => {
|
|
823
|
+
console.error(`❌ Error: ${getErrorMessage(error)}`);
|
|
824
|
+
process.exit(1);
|
|
825
|
+
});
|
|
826
|
+
break;
|
|
827
|
+
default:
|
|
828
|
+
console.error(`Unknown command: ${command}`);
|
|
829
|
+
process.exit(1);
|
|
830
|
+
}
|
|
831
|
+
}
|