@tmddev/tmd 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/README.md +195 -3
- package/dist/cli.js +70 -10
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +60 -0
- package/dist/commands/pipeline.d.ts +26 -0
- package/dist/commands/pipeline.js +168 -0
- package/dist/commands/schemas.d.ts +4 -0
- package/dist/commands/schemas.js +57 -0
- package/dist/commands/skills.d.ts +1 -1
- package/dist/commands/skills.js +247 -162
- package/dist/commands/validate.d.ts +9 -0
- package/dist/commands/validate.js +179 -0
- package/dist/commands/view.d.ts +2 -0
- package/dist/commands/view.js +65 -0
- package/dist/tmd-skills +0 -0
- package/dist/types.d.ts +21 -1
- package/dist/utils/github.d.ts +18 -0
- package/dist/utils/github.js +165 -0
- package/dist/utils/paths.d.ts +1 -0
- package/dist/utils/paths.js +4 -0
- package/dist/utils/pipeline-config.d.ts +37 -0
- package/dist/utils/pipeline-config.js +117 -0
- package/dist/utils/pipeline.d.ts +81 -0
- package/dist/utils/pipeline.js +580 -0
- package/dist/utils/skills.d.ts +10 -2
- package/dist/utils/skills.js +152 -150
- package/dist/utils/skillssh.d.ts +1 -1
- package/dist/utils/skillssh.js +39 -27
- package/dist/utils/step-executor.d.ts +58 -0
- package/dist/utils/step-executor.js +374 -0
- package/dist/utils/templates.d.ts +1 -0
- package/dist/utils/templates.js +30 -0
- package/package.json +22 -21
- package/scripts/postinstall.js +11 -8
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline orchestration engine for automated PDCA execution
|
|
3
|
+
*/
|
|
4
|
+
import { type PipelineConfig } from './pipeline-config.js';
|
|
5
|
+
export type PipelinePhase = 'do' | 'check' | 'act';
|
|
6
|
+
export interface PhaseResult {
|
|
7
|
+
phase: PipelinePhase;
|
|
8
|
+
success: boolean;
|
|
9
|
+
startTime: Date;
|
|
10
|
+
endTime: Date;
|
|
11
|
+
error?: string;
|
|
12
|
+
details?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
export interface PipelineResult {
|
|
15
|
+
taskId: string;
|
|
16
|
+
success: boolean;
|
|
17
|
+
startTime: Date;
|
|
18
|
+
endTime: Date;
|
|
19
|
+
phases: PhaseResult[];
|
|
20
|
+
error?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface PipelineState {
|
|
23
|
+
taskId: string;
|
|
24
|
+
currentPhase?: PipelinePhase;
|
|
25
|
+
completedPhases: PipelinePhase[];
|
|
26
|
+
lastRun?: Date;
|
|
27
|
+
lastError?: string;
|
|
28
|
+
phaseResults: PhaseResult[];
|
|
29
|
+
}
|
|
30
|
+
export declare class PipelineRunner {
|
|
31
|
+
private taskId;
|
|
32
|
+
private config;
|
|
33
|
+
private dryRun;
|
|
34
|
+
private state;
|
|
35
|
+
constructor(taskId: string, config: PipelineConfig, dryRun?: boolean);
|
|
36
|
+
/**
|
|
37
|
+
* Run the full pipeline from the specified phase
|
|
38
|
+
*/
|
|
39
|
+
run(fromPhase?: PipelinePhase): Promise<PipelineResult>;
|
|
40
|
+
/**
|
|
41
|
+
* Validate that prerequisites for starting from a phase are met
|
|
42
|
+
*/
|
|
43
|
+
private validatePrerequisites;
|
|
44
|
+
/**
|
|
45
|
+
* Execute a single phase
|
|
46
|
+
*/
|
|
47
|
+
private executePhase;
|
|
48
|
+
/**
|
|
49
|
+
* Execute the Do phase
|
|
50
|
+
*/
|
|
51
|
+
private executeDoPhase;
|
|
52
|
+
/**
|
|
53
|
+
* Execute skill steps
|
|
54
|
+
*/
|
|
55
|
+
private executeSkillSteps;
|
|
56
|
+
/**
|
|
57
|
+
* Write execution results to execution.md and data.md
|
|
58
|
+
*/
|
|
59
|
+
private writeExecutionResults;
|
|
60
|
+
/**
|
|
61
|
+
* Execute the Check phase
|
|
62
|
+
*/
|
|
63
|
+
private executeCheckPhase;
|
|
64
|
+
/**
|
|
65
|
+
* Write check phase results
|
|
66
|
+
*/
|
|
67
|
+
private writeCheckResults;
|
|
68
|
+
/**
|
|
69
|
+
* Execute the Act phase
|
|
70
|
+
*/
|
|
71
|
+
private executeActPhase;
|
|
72
|
+
/**
|
|
73
|
+
* Get current pipeline state
|
|
74
|
+
*/
|
|
75
|
+
getState(): PipelineState;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get the current pipeline state for a task
|
|
79
|
+
*/
|
|
80
|
+
export declare function getPipelineState(taskId: string): PipelineState;
|
|
81
|
+
//# sourceMappingURL=pipeline.d.ts.map
|
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline orchestration engine for automated PDCA execution
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import yaml from 'js-yaml';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { getPlanDir, getDoDir, getCheckDir, getActDir, ensureDir } from './paths.js';
|
|
9
|
+
import { getTaskStatus, updateTaskStatus } from './task-status.js';
|
|
10
|
+
import { parseSuccessCriteria } from './pipeline-config.js';
|
|
11
|
+
import { executeStep } from './step-executor.js';
|
|
12
|
+
import { extractTasks, updateTaskStatus as updatePlanTaskStatus, detectSkillReference } from './tasks.js';
|
|
13
|
+
import { extractGoals, extractResults, compareGoalsAndResults } from './comparison.js';
|
|
14
|
+
import { identifyDeviations, analyzeRootCauses, recommendAdjustments } from './analysis.js';
|
|
15
|
+
import { parseEvaluationResults, parseDeviationAnalysis, identifySuccesses, identifyFailures, identifyUnresolvedIssues } from './act-processing.js';
|
|
16
|
+
import { extractSuccessfulPractices, generateStandardizationRecommendations, formatStandardizationDocument } from './standardization.js';
|
|
17
|
+
import { executionTemplate, evaluationTemplate, improvementTemplate, renderTemplate } from './templates.js';
|
|
18
|
+
import { getSkillDir } from './paths.js';
|
|
19
|
+
export class PipelineRunner {
|
|
20
|
+
taskId;
|
|
21
|
+
config;
|
|
22
|
+
dryRun;
|
|
23
|
+
state;
|
|
24
|
+
constructor(taskId, config, dryRun = false) {
|
|
25
|
+
this.taskId = taskId;
|
|
26
|
+
this.config = config;
|
|
27
|
+
this.dryRun = dryRun;
|
|
28
|
+
this.state = {
|
|
29
|
+
taskId,
|
|
30
|
+
completedPhases: [],
|
|
31
|
+
phaseResults: []
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Run the full pipeline from the specified phase
|
|
36
|
+
*/
|
|
37
|
+
async run(fromPhase) {
|
|
38
|
+
const startTime = new Date();
|
|
39
|
+
const phases = ['do', 'check', 'act'];
|
|
40
|
+
const startIndex = fromPhase ? phases.indexOf(fromPhase) : 0;
|
|
41
|
+
if (startIndex === -1) {
|
|
42
|
+
return {
|
|
43
|
+
taskId: this.taskId,
|
|
44
|
+
success: false,
|
|
45
|
+
startTime,
|
|
46
|
+
endTime: new Date(),
|
|
47
|
+
phases: [],
|
|
48
|
+
error: `Invalid phase: ${fromPhase ?? ''}`
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// Validate prerequisites
|
|
52
|
+
const prereqError = this.validatePrerequisites(fromPhase);
|
|
53
|
+
if (prereqError) {
|
|
54
|
+
return {
|
|
55
|
+
taskId: this.taskId,
|
|
56
|
+
success: false,
|
|
57
|
+
startTime,
|
|
58
|
+
endTime: new Date(),
|
|
59
|
+
phases: [],
|
|
60
|
+
error: prereqError
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const phaseResults = [];
|
|
64
|
+
let success = true;
|
|
65
|
+
let lastError;
|
|
66
|
+
const phasesToRun = phases.slice(startIndex);
|
|
67
|
+
for (const phase of phasesToRun) {
|
|
68
|
+
const phaseConfig = this.config.phases[phase];
|
|
69
|
+
if (!phaseConfig.auto) {
|
|
70
|
+
console.log(chalk.yellow(` Skipping ${phase} phase (auto: false)`));
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
console.log(chalk.blue(`\n Phase: ${phase.toUpperCase()}`));
|
|
74
|
+
this.state.currentPhase = phase;
|
|
75
|
+
const phaseResult = await this.executePhase(phase);
|
|
76
|
+
phaseResults.push(phaseResult);
|
|
77
|
+
if (!phaseResult.success) {
|
|
78
|
+
success = false;
|
|
79
|
+
lastError = phaseResult.error;
|
|
80
|
+
console.log(chalk.red(` Phase ${phase} failed: ${phaseResult.error ?? ''}`));
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
this.state.completedPhases.push(phase);
|
|
84
|
+
console.log(chalk.green(` Phase ${phase} completed`));
|
|
85
|
+
}
|
|
86
|
+
this.state.lastRun = new Date();
|
|
87
|
+
this.state.phaseResults = phaseResults;
|
|
88
|
+
if (!success && lastError != null) {
|
|
89
|
+
this.state.lastError = lastError;
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
taskId: this.taskId,
|
|
93
|
+
success,
|
|
94
|
+
startTime,
|
|
95
|
+
endTime: new Date(),
|
|
96
|
+
phases: phaseResults,
|
|
97
|
+
...(lastError != null ? { error: lastError } : {})
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Validate that prerequisites for starting from a phase are met
|
|
102
|
+
*/
|
|
103
|
+
validatePrerequisites(fromPhase) {
|
|
104
|
+
const planPath = join(getPlanDir(this.taskId), 'plan.md');
|
|
105
|
+
if (!existsSync(planPath)) {
|
|
106
|
+
return `Plan not found for task: ${this.taskId}`;
|
|
107
|
+
}
|
|
108
|
+
if (fromPhase === 'check') {
|
|
109
|
+
const executionPath = join(getDoDir(this.taskId), 'execution.md');
|
|
110
|
+
if (!existsSync(executionPath)) {
|
|
111
|
+
return `Execution not found for task: ${this.taskId}. Run Do phase first.`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (fromPhase === 'act') {
|
|
115
|
+
const evaluationPath = join(getCheckDir(this.taskId), 'evaluation.md');
|
|
116
|
+
if (!existsSync(evaluationPath)) {
|
|
117
|
+
return `Evaluation not found for task: ${this.taskId}. Run Check phase first.`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Execute a single phase
|
|
124
|
+
*/
|
|
125
|
+
async executePhase(phase) {
|
|
126
|
+
const startTime = new Date();
|
|
127
|
+
try {
|
|
128
|
+
switch (phase) {
|
|
129
|
+
case 'do':
|
|
130
|
+
return await this.executeDoPhase(startTime);
|
|
131
|
+
case 'check':
|
|
132
|
+
return await this.executeCheckPhase(startTime);
|
|
133
|
+
case 'act':
|
|
134
|
+
return await this.executeActPhase(startTime);
|
|
135
|
+
default:
|
|
136
|
+
return {
|
|
137
|
+
phase,
|
|
138
|
+
success: false,
|
|
139
|
+
startTime,
|
|
140
|
+
endTime: new Date(),
|
|
141
|
+
error: `Unknown phase: ${String(phase)}`
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
return {
|
|
147
|
+
phase,
|
|
148
|
+
success: false,
|
|
149
|
+
startTime,
|
|
150
|
+
endTime: new Date(),
|
|
151
|
+
error: error instanceof Error ? error.message : String(error)
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Execute the Do phase
|
|
157
|
+
*/
|
|
158
|
+
async executeDoPhase(startTime) {
|
|
159
|
+
const planPath = join(getPlanDir(this.taskId), 'plan.md');
|
|
160
|
+
const doDir = getDoDir(this.taskId);
|
|
161
|
+
const phaseConfig = this.config.phases.do;
|
|
162
|
+
if (!this.dryRun) {
|
|
163
|
+
ensureDir(doDir);
|
|
164
|
+
}
|
|
165
|
+
// Extract tasks
|
|
166
|
+
const tasks = extractTasks(planPath);
|
|
167
|
+
const incompleteTasks = tasks.filter(t => !t.completed);
|
|
168
|
+
if (incompleteTasks.length === 0) {
|
|
169
|
+
console.log(chalk.yellow(' All tasks already completed'));
|
|
170
|
+
return {
|
|
171
|
+
phase: 'do',
|
|
172
|
+
success: true,
|
|
173
|
+
startTime,
|
|
174
|
+
endTime: new Date(),
|
|
175
|
+
details: { tasksCompleted: tasks.length, totalTasks: tasks.length }
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
console.log(chalk.gray(` Tasks to execute: ${String(incompleteTasks.length)}/${String(tasks.length)}`));
|
|
179
|
+
// Initialize execution.md
|
|
180
|
+
const executionPath = join(doDir, 'execution.md');
|
|
181
|
+
const dataPath = join(doDir, 'data.md');
|
|
182
|
+
if (!this.dryRun) {
|
|
183
|
+
if (!existsSync(executionPath)) {
|
|
184
|
+
writeFileSync(executionPath, renderTemplate(executionTemplate, { taskId: this.taskId }));
|
|
185
|
+
}
|
|
186
|
+
if (!existsSync(dataPath)) {
|
|
187
|
+
writeFileSync(dataPath, '# Data Collection\n\n');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// Execute tasks
|
|
191
|
+
const stepResults = [];
|
|
192
|
+
const actionsTaken = [];
|
|
193
|
+
const dataCollected = [];
|
|
194
|
+
const observations = [];
|
|
195
|
+
const issues = [];
|
|
196
|
+
const context = {
|
|
197
|
+
taskId: this.taskId,
|
|
198
|
+
workingDir: process.cwd(),
|
|
199
|
+
dryRun: this.dryRun
|
|
200
|
+
};
|
|
201
|
+
for (const task of incompleteTasks) {
|
|
202
|
+
console.log(chalk.gray(` [${String(task.index)}] ${task.content.substring(0, 50)}${task.content.length > 50 ? '...' : ''}`));
|
|
203
|
+
// Check for skill reference
|
|
204
|
+
const skillRef = detectSkillReference(task.content);
|
|
205
|
+
if (skillRef) {
|
|
206
|
+
const skillResult = await this.executeSkillSteps(skillRef, context);
|
|
207
|
+
stepResults.push(...skillResult.results);
|
|
208
|
+
if (skillResult.output) {
|
|
209
|
+
dataCollected.push(`Skill ${skillRef}: ${skillResult.output}`);
|
|
210
|
+
}
|
|
211
|
+
if (!skillResult.success) {
|
|
212
|
+
issues.push(`Task ${String(task.index)} skill execution failed: ${skillResult.error ?? ''}`);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// Mark task as complete
|
|
217
|
+
if (!this.dryRun) {
|
|
218
|
+
updatePlanTaskStatus(planPath, task.index, true);
|
|
219
|
+
}
|
|
220
|
+
actionsTaken.push(`Completed task ${String(task.index)}: ${task.content}`);
|
|
221
|
+
}
|
|
222
|
+
// Check success criteria
|
|
223
|
+
const updatedTasks = this.dryRun ? tasks : extractTasks(planPath);
|
|
224
|
+
const completedCount = updatedTasks.filter(t => t.completed).length;
|
|
225
|
+
const criteriaResult = parseSuccessCriteria(phaseConfig.successCriteria, updatedTasks.length, completedCount);
|
|
226
|
+
if (!criteriaResult.passed) {
|
|
227
|
+
return {
|
|
228
|
+
phase: 'do',
|
|
229
|
+
success: false,
|
|
230
|
+
startTime,
|
|
231
|
+
endTime: new Date(),
|
|
232
|
+
...(criteriaResult.reason != null ? { error: criteriaResult.reason } : {}),
|
|
233
|
+
details: { tasksCompleted: completedCount, totalTasks: updatedTasks.length }
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
// Write execution results
|
|
237
|
+
if (!this.dryRun) {
|
|
238
|
+
this.writeExecutionResults(executionPath, dataPath, {
|
|
239
|
+
actionsTaken,
|
|
240
|
+
dataCollected,
|
|
241
|
+
observations,
|
|
242
|
+
issues,
|
|
243
|
+
stepResults
|
|
244
|
+
});
|
|
245
|
+
updateTaskStatus(this.taskId, 'doing');
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
phase: 'do',
|
|
249
|
+
success: true,
|
|
250
|
+
startTime,
|
|
251
|
+
endTime: new Date(),
|
|
252
|
+
details: {
|
|
253
|
+
tasksCompleted: completedCount,
|
|
254
|
+
totalTasks: updatedTasks.length,
|
|
255
|
+
stepsExecuted: stepResults.length
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Execute skill steps
|
|
261
|
+
*/
|
|
262
|
+
async executeSkillSteps(skillName, context) {
|
|
263
|
+
const skillDir = getSkillDir(skillName);
|
|
264
|
+
const stepsPath = join(skillDir, 'steps.yaml');
|
|
265
|
+
if (!existsSync(stepsPath)) {
|
|
266
|
+
return {
|
|
267
|
+
success: false,
|
|
268
|
+
results: [],
|
|
269
|
+
error: `Skill steps not found: ${skillName}`
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
try {
|
|
273
|
+
const stepsData = yaml.load(readFileSync(stepsPath, 'utf-8'));
|
|
274
|
+
const steps = (stepsData != null && typeof stepsData === 'object' && 'steps' in stepsData ? (stepsData.steps ?? []) : []);
|
|
275
|
+
const results = [];
|
|
276
|
+
const outputs = [];
|
|
277
|
+
for (const step of steps) {
|
|
278
|
+
const result = await executeStep(step, context);
|
|
279
|
+
results.push(result);
|
|
280
|
+
if (result.output) {
|
|
281
|
+
outputs.push(result.output);
|
|
282
|
+
}
|
|
283
|
+
if (!result.success) {
|
|
284
|
+
return {
|
|
285
|
+
success: false,
|
|
286
|
+
results,
|
|
287
|
+
output: outputs.join('\n'),
|
|
288
|
+
...(result.error != null ? { error: result.error } : {})
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
success: true,
|
|
294
|
+
results,
|
|
295
|
+
output: outputs.join('\n')
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
return {
|
|
300
|
+
success: false,
|
|
301
|
+
results: [],
|
|
302
|
+
error: error instanceof Error ? error.message : String(error)
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Write execution results to execution.md and data.md
|
|
308
|
+
*/
|
|
309
|
+
writeExecutionResults(executionPath, dataPath, results) {
|
|
310
|
+
let content = readFileSync(executionPath, 'utf-8');
|
|
311
|
+
const timestamp = new Date().toISOString();
|
|
312
|
+
// Add actions taken
|
|
313
|
+
if (results.actionsTaken.length > 0) {
|
|
314
|
+
const actionsSection = `\n## Actions Taken\n\n${results.actionsTaken.map(a => `- [${timestamp}] ${a}`).join('\n')}\n`;
|
|
315
|
+
content = content.replace(/(## Actions Taken\n<!-- Document actions performed -->\n)/, actionsSection);
|
|
316
|
+
}
|
|
317
|
+
// Add data collected
|
|
318
|
+
if (results.dataCollected.length > 0) {
|
|
319
|
+
const dataSection = `\n## Data Collected\n\n${results.dataCollected.map(d => `- ${d}`).join('\n')}\n`;
|
|
320
|
+
content = content.replace(/(## Data Collected\n<!-- Record data collected during execution -->\n)/, dataSection);
|
|
321
|
+
}
|
|
322
|
+
// Add observations
|
|
323
|
+
if (results.observations.length > 0) {
|
|
324
|
+
const obsSection = `\n## Observations\n\n${results.observations.map(o => `- ${o}`).join('\n')}\n`;
|
|
325
|
+
content = content.replace(/(## Observations\n<!-- Note any observations or insights -->\n)/, obsSection);
|
|
326
|
+
}
|
|
327
|
+
// Add issues
|
|
328
|
+
if (results.issues.length > 0) {
|
|
329
|
+
const issuesSection = `\n## Issues Encountered\n\n${results.issues.map(i => `- ${i}`).join('\n')}\n`;
|
|
330
|
+
content = content.replace(/(## Issues Encountered\n<!-- Document any problems or blockers -->\n)/, issuesSection);
|
|
331
|
+
}
|
|
332
|
+
writeFileSync(executionPath, content);
|
|
333
|
+
// Write step results to data.md
|
|
334
|
+
if (results.stepResults.length > 0) {
|
|
335
|
+
const stepData = results.stepResults.map((r, i) => ({
|
|
336
|
+
step: i + 1,
|
|
337
|
+
success: r.success,
|
|
338
|
+
duration: r.duration,
|
|
339
|
+
output: r.output?.substring(0, 200)
|
|
340
|
+
}));
|
|
341
|
+
const dataContent = readFileSync(dataPath, 'utf-8');
|
|
342
|
+
writeFileSync(dataPath, dataContent + '\n## Step Execution Results\n\n```yaml\n' + yaml.dump(stepData) + '```\n');
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Execute the Check phase
|
|
347
|
+
*/
|
|
348
|
+
async executeCheckPhase(startTime) {
|
|
349
|
+
await Promise.resolve();
|
|
350
|
+
const planPath = join(getPlanDir(this.taskId), 'plan.md');
|
|
351
|
+
const executionPath = join(getDoDir(this.taskId), 'execution.md');
|
|
352
|
+
const dataPath = join(getDoDir(this.taskId), 'data.md');
|
|
353
|
+
const checkDir = getCheckDir(this.taskId);
|
|
354
|
+
const phaseConfig = this.config.phases.check;
|
|
355
|
+
if (!this.dryRun) {
|
|
356
|
+
ensureDir(checkDir);
|
|
357
|
+
}
|
|
358
|
+
// Extract goals and results
|
|
359
|
+
const goals = extractGoals(planPath);
|
|
360
|
+
const results = extractResults(executionPath, dataPath);
|
|
361
|
+
console.log(chalk.gray(` Goals found: ${String(goals.length)}`));
|
|
362
|
+
console.log(chalk.gray(` Results found: ${String(results.length)}`));
|
|
363
|
+
// Perform comparison
|
|
364
|
+
const comparisons = compareGoalsAndResults(goals, results);
|
|
365
|
+
// Calculate statistics
|
|
366
|
+
const totalGoals = comparisons.length;
|
|
367
|
+
const metGoals = comparisons.filter(c => c.status === 'met').length;
|
|
368
|
+
const unmetGoals = comparisons.filter(c => c.status === 'unmet').length;
|
|
369
|
+
const partialGoals = comparisons.filter(c => c.status === 'partial').length;
|
|
370
|
+
console.log(chalk.gray(` Goals met: ${String(metGoals)}/${String(totalGoals)}`));
|
|
371
|
+
if (unmetGoals > 0) {
|
|
372
|
+
console.log(chalk.yellow(` Goals unmet: ${String(unmetGoals)}`));
|
|
373
|
+
}
|
|
374
|
+
// Identify deviations
|
|
375
|
+
const deviations = identifyDeviations(comparisons);
|
|
376
|
+
let rootCauses = [];
|
|
377
|
+
let recommendations = [];
|
|
378
|
+
if (phaseConfig.analyze && deviations.length > 0) {
|
|
379
|
+
rootCauses = analyzeRootCauses(deviations);
|
|
380
|
+
}
|
|
381
|
+
if (phaseConfig.recommend && deviations.length > 0) {
|
|
382
|
+
recommendations = recommendAdjustments(deviations, rootCauses);
|
|
383
|
+
}
|
|
384
|
+
// Check success criteria
|
|
385
|
+
const criteria = phaseConfig.successCriteria;
|
|
386
|
+
if (criteria) {
|
|
387
|
+
if (!criteria.allowUnmetGoals && unmetGoals > 0) {
|
|
388
|
+
return {
|
|
389
|
+
phase: 'check',
|
|
390
|
+
success: false,
|
|
391
|
+
startTime,
|
|
392
|
+
endTime: new Date(),
|
|
393
|
+
error: `${String(unmetGoals)} goal(s) not met`,
|
|
394
|
+
details: { totalGoals, metGoals, unmetGoals, partialGoals }
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// Write evaluation and deviation documents
|
|
399
|
+
if (!this.dryRun) {
|
|
400
|
+
this.writeCheckResults(checkDir, {
|
|
401
|
+
comparisons,
|
|
402
|
+
deviations,
|
|
403
|
+
rootCauses,
|
|
404
|
+
recommendations,
|
|
405
|
+
totalGoals,
|
|
406
|
+
metGoals,
|
|
407
|
+
unmetGoals
|
|
408
|
+
});
|
|
409
|
+
updateTaskStatus(this.taskId, 'checking');
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
phase: 'check',
|
|
413
|
+
success: true,
|
|
414
|
+
startTime,
|
|
415
|
+
endTime: new Date(),
|
|
416
|
+
details: { totalGoals, metGoals, unmetGoals, partialGoals, deviations: deviations.length }
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Write check phase results
|
|
421
|
+
*/
|
|
422
|
+
writeCheckResults(checkDir, data) {
|
|
423
|
+
// Generate evaluation.md
|
|
424
|
+
let evaluationContent = renderTemplate(evaluationTemplate, { taskId: this.taskId });
|
|
425
|
+
// Add comparison table
|
|
426
|
+
evaluationContent += '\n## Comparison with Plan\n\n';
|
|
427
|
+
evaluationContent += '| Goal | Planned | Actual | Status | Deviation |\n';
|
|
428
|
+
evaluationContent += '|------|---------|--------|--------|-----------|\n';
|
|
429
|
+
for (const comp of data.comparisons) {
|
|
430
|
+
const goal = comp.goal.description.substring(0, 50) + (comp.goal.description.length > 50 ? '...' : '');
|
|
431
|
+
const planned = comp.goal.targetValue !== undefined ? String(comp.goal.targetValue) : 'N/A';
|
|
432
|
+
const actual = comp.result?.value !== undefined ? String(comp.result.value) : 'No data';
|
|
433
|
+
const status = comp.status === 'met' ? 'Met' : comp.status === 'unmet' ? 'Unmet' : comp.status;
|
|
434
|
+
const deviation = comp.deviation !== undefined ? comp.deviation.toFixed(2) : 'N/A';
|
|
435
|
+
evaluationContent += `| ${goal} | ${planned} | ${actual} | ${status} | ${deviation} |\n`;
|
|
436
|
+
}
|
|
437
|
+
evaluationContent += '\n## Results Summary\n\n';
|
|
438
|
+
evaluationContent += `- Total goals: ${String(data.totalGoals)}\n`;
|
|
439
|
+
evaluationContent += `- Goals met: ${String(data.metGoals)}\n`;
|
|
440
|
+
evaluationContent += `- Goals unmet: ${String(data.unmetGoals)}\n`;
|
|
441
|
+
if (data.recommendations.length > 0) {
|
|
442
|
+
evaluationContent += '\n## Recommendations\n\n';
|
|
443
|
+
for (const rec of data.recommendations) {
|
|
444
|
+
evaluationContent += `- **${rec.action}** (${rec.priority}): ${rec.description}\n`;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
writeFileSync(join(checkDir, 'evaluation.md'), evaluationContent);
|
|
448
|
+
// Generate deviation.md
|
|
449
|
+
let deviationContent = '# Deviation Analysis\n\n';
|
|
450
|
+
if (data.deviations.length === 0) {
|
|
451
|
+
deviationContent += 'No deviations found. All goals were met.\n';
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
deviationContent += '## Deviations Identified\n\n';
|
|
455
|
+
for (const dev of data.deviations) {
|
|
456
|
+
deviationContent += `### ${dev.description}\n`;
|
|
457
|
+
deviationContent += `- Type: ${dev.type}\n`;
|
|
458
|
+
deviationContent += `- Severity: ${dev.severity}\n\n`;
|
|
459
|
+
}
|
|
460
|
+
if (data.rootCauses.length > 0) {
|
|
461
|
+
deviationContent += '## Root Cause Analysis\n\n';
|
|
462
|
+
for (const cause of data.rootCauses) {
|
|
463
|
+
deviationContent += `### ${cause.category}\n`;
|
|
464
|
+
deviationContent += `${cause.description}\n\n`;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
writeFileSync(join(checkDir, 'deviation.md'), deviationContent);
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Execute the Act phase
|
|
472
|
+
*/
|
|
473
|
+
async executeActPhase(startTime) {
|
|
474
|
+
await Promise.resolve();
|
|
475
|
+
const evaluationPath = join(getCheckDir(this.taskId), 'evaluation.md');
|
|
476
|
+
const deviationPath = join(getCheckDir(this.taskId), 'deviation.md');
|
|
477
|
+
const actDir = getActDir(this.taskId);
|
|
478
|
+
const phaseConfig = this.config.phases.act;
|
|
479
|
+
if (!this.dryRun) {
|
|
480
|
+
ensureDir(actDir);
|
|
481
|
+
}
|
|
482
|
+
// Parse check results
|
|
483
|
+
const evaluationResults = parseEvaluationResults(evaluationPath);
|
|
484
|
+
const deviationAnalysis = existsSync(deviationPath)
|
|
485
|
+
? parseDeviationAnalysis(deviationPath)
|
|
486
|
+
: { deviations: [], rootCauses: [] };
|
|
487
|
+
// Identify successes and failures
|
|
488
|
+
const successes = identifySuccesses(evaluationResults);
|
|
489
|
+
const failures = identifyFailures(deviationAnalysis, evaluationResults);
|
|
490
|
+
const unresolvedIssues = identifyUnresolvedIssues(evaluationResults, deviationAnalysis);
|
|
491
|
+
console.log(chalk.gray(` Successes: ${String(successes.length)}`));
|
|
492
|
+
console.log(chalk.gray(` Failures: ${String(failures.length)}`));
|
|
493
|
+
console.log(chalk.gray(` Unresolved: ${String(unresolvedIssues.length)}`));
|
|
494
|
+
if (!this.dryRun) {
|
|
495
|
+
// Generate improvement.md
|
|
496
|
+
let improvementContent = renderTemplate(improvementTemplate, { taskId: this.taskId });
|
|
497
|
+
if (failures.length > 0) {
|
|
498
|
+
improvementContent += '\n## Corrective Actions\n\n';
|
|
499
|
+
for (const failure of failures) {
|
|
500
|
+
improvementContent += `- **${failure.goal}**: ${failure.lesson ?? failure.deviation}\n`;
|
|
501
|
+
}
|
|
502
|
+
improvementContent += '\n## Lessons Learned\n\n';
|
|
503
|
+
for (const failure of failures) {
|
|
504
|
+
if (failure.lesson) {
|
|
505
|
+
improvementContent += `- ${failure.lesson}\n`;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
writeFileSync(join(actDir, 'improvement.md'), improvementContent);
|
|
510
|
+
// Generate standardization.md if enabled
|
|
511
|
+
if (phaseConfig.standardize && successes.length > 0) {
|
|
512
|
+
const practices = extractSuccessfulPractices(successes);
|
|
513
|
+
const recommendations = generateStandardizationRecommendations(practices);
|
|
514
|
+
const standardizationContent = formatStandardizationDocument(practices, recommendations);
|
|
515
|
+
writeFileSync(join(actDir, 'standardization.md'), standardizationContent);
|
|
516
|
+
}
|
|
517
|
+
// Generate next-cycle-items.md if enabled
|
|
518
|
+
if (phaseConfig.carryForward && unresolvedIssues.length > 0) {
|
|
519
|
+
let nextCycleContent = `# Next Cycle Planning Items\n\n`;
|
|
520
|
+
nextCycleContent += `Items from task ${this.taskId}:\n\n`;
|
|
521
|
+
for (const issue of unresolvedIssues) {
|
|
522
|
+
nextCycleContent += `- [ ] ${issue.description} (Priority: ${issue.priority})\n`;
|
|
523
|
+
}
|
|
524
|
+
writeFileSync(join(actDir, 'next-cycle-items.md'), nextCycleContent);
|
|
525
|
+
}
|
|
526
|
+
// Update status
|
|
527
|
+
if (phaseConfig.completeOnSuccess) {
|
|
528
|
+
updateTaskStatus(this.taskId, 'completed');
|
|
529
|
+
console.log(chalk.green(' Task marked as completed'));
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
updateTaskStatus(this.taskId, 'acting');
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return {
|
|
536
|
+
phase: 'act',
|
|
537
|
+
success: true,
|
|
538
|
+
startTime,
|
|
539
|
+
endTime: new Date(),
|
|
540
|
+
details: {
|
|
541
|
+
successes: successes.length,
|
|
542
|
+
failures: failures.length,
|
|
543
|
+
unresolvedIssues: unresolvedIssues.length
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Get current pipeline state
|
|
549
|
+
*/
|
|
550
|
+
getState() {
|
|
551
|
+
return { ...this.state };
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Get the current pipeline state for a task
|
|
556
|
+
*/
|
|
557
|
+
export function getPipelineState(taskId) {
|
|
558
|
+
const status = getTaskStatus(taskId);
|
|
559
|
+
const completedPhases = [];
|
|
560
|
+
if (existsSync(join(getDoDir(taskId), 'execution.md'))) {
|
|
561
|
+
completedPhases.push('do');
|
|
562
|
+
}
|
|
563
|
+
if (existsSync(join(getCheckDir(taskId), 'evaluation.md'))) {
|
|
564
|
+
completedPhases.push('check');
|
|
565
|
+
}
|
|
566
|
+
if (existsSync(join(getActDir(taskId), 'improvement.md'))) {
|
|
567
|
+
completedPhases.push('act');
|
|
568
|
+
}
|
|
569
|
+
const currentPhase = status === 'doing' ? 'do'
|
|
570
|
+
: status === 'checking' ? 'check'
|
|
571
|
+
: status === 'acting' ? 'act'
|
|
572
|
+
: undefined;
|
|
573
|
+
return {
|
|
574
|
+
taskId,
|
|
575
|
+
...(currentPhase != null ? { currentPhase } : {}),
|
|
576
|
+
completedPhases,
|
|
577
|
+
phaseResults: []
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
//# sourceMappingURL=pipeline.js.map
|
package/dist/utils/skills.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
1
|
+
export interface CreateSkillOptions {
|
|
2
|
+
taskId?: string;
|
|
3
|
+
tags?: string[];
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Create a skill from a plan (tmd plan --skill). Generates name from description.
|
|
7
|
+
* Options.tags is applied when provided; options.taskId is used as createdFrom when provided.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createSkill(description: string, taskId: string, options?: CreateSkillOptions): string | null;
|
|
10
|
+
export declare function executeSkill(skillName: string, taskId: string, parallel?: boolean, maxConcurrency?: number, params?: Record<string, string>): Promise<void>;
|
|
3
11
|
//# sourceMappingURL=skills.d.ts.map
|