@kody-ade/kody-engine-lite 0.1.65 → 0.1.67

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.
Files changed (71) hide show
  1. package/dist/bin/cli.js +158 -9
  2. package/package.json +1 -1
  3. package/templates/kody.yml +2 -1
  4. package/dist/agent-runner.d.ts +0 -4
  5. package/dist/agent-runner.js +0 -122
  6. package/dist/ci/parse-inputs.d.ts +0 -6
  7. package/dist/ci/parse-inputs.js +0 -76
  8. package/dist/ci/parse-safety.d.ts +0 -6
  9. package/dist/ci/parse-safety.js +0 -22
  10. package/dist/cli/args.d.ts +0 -13
  11. package/dist/cli/args.js +0 -42
  12. package/dist/cli/litellm.d.ts +0 -2
  13. package/dist/cli/litellm.js +0 -85
  14. package/dist/cli/task-resolution.d.ts +0 -2
  15. package/dist/cli/task-resolution.js +0 -41
  16. package/dist/config.d.ts +0 -49
  17. package/dist/config.js +0 -72
  18. package/dist/context.d.ts +0 -4
  19. package/dist/context.js +0 -83
  20. package/dist/definitions.d.ts +0 -3
  21. package/dist/definitions.js +0 -59
  22. package/dist/entry.d.ts +0 -1
  23. package/dist/entry.js +0 -236
  24. package/dist/git-utils.d.ts +0 -13
  25. package/dist/git-utils.js +0 -174
  26. package/dist/github-api.d.ts +0 -14
  27. package/dist/github-api.js +0 -114
  28. package/dist/kody-utils.d.ts +0 -1
  29. package/dist/kody-utils.js +0 -9
  30. package/dist/learning/auto-learn.d.ts +0 -2
  31. package/dist/learning/auto-learn.js +0 -169
  32. package/dist/logger.d.ts +0 -14
  33. package/dist/logger.js +0 -51
  34. package/dist/memory.d.ts +0 -1
  35. package/dist/memory.js +0 -20
  36. package/dist/observer.d.ts +0 -9
  37. package/dist/observer.js +0 -80
  38. package/dist/pipeline/complexity.d.ts +0 -3
  39. package/dist/pipeline/complexity.js +0 -12
  40. package/dist/pipeline/executor-registry.d.ts +0 -3
  41. package/dist/pipeline/executor-registry.js +0 -20
  42. package/dist/pipeline/hooks.d.ts +0 -17
  43. package/dist/pipeline/hooks.js +0 -110
  44. package/dist/pipeline/questions.d.ts +0 -2
  45. package/dist/pipeline/questions.js +0 -44
  46. package/dist/pipeline/runner-selection.d.ts +0 -2
  47. package/dist/pipeline/runner-selection.js +0 -13
  48. package/dist/pipeline/state.d.ts +0 -4
  49. package/dist/pipeline/state.js +0 -37
  50. package/dist/pipeline.d.ts +0 -3
  51. package/dist/pipeline.js +0 -213
  52. package/dist/preflight.d.ts +0 -1
  53. package/dist/preflight.js +0 -69
  54. package/dist/retrospective.d.ts +0 -26
  55. package/dist/retrospective.js +0 -211
  56. package/dist/stages/agent.d.ts +0 -2
  57. package/dist/stages/agent.js +0 -94
  58. package/dist/stages/gate.d.ts +0 -2
  59. package/dist/stages/gate.js +0 -32
  60. package/dist/stages/review.d.ts +0 -2
  61. package/dist/stages/review.js +0 -32
  62. package/dist/stages/ship.d.ts +0 -3
  63. package/dist/stages/ship.js +0 -154
  64. package/dist/stages/verify.d.ts +0 -2
  65. package/dist/stages/verify.js +0 -94
  66. package/dist/types.d.ts +0 -61
  67. package/dist/types.js +0 -1
  68. package/dist/validators.d.ts +0 -8
  69. package/dist/validators.js +0 -42
  70. package/dist/verify-runner.d.ts +0 -11
  71. package/dist/verify-runner.js +0 -110
@@ -1,12 +0,0 @@
1
- const COMPLEXITY_SKIP = {
2
- low: ["plan", "review", "review-fix"],
3
- medium: ["review-fix"],
4
- high: [],
5
- };
6
- export function filterByComplexity(stages, complexity) {
7
- const skip = COMPLEXITY_SKIP[complexity] ?? [];
8
- return stages.filter((s) => !skip.includes(s.name));
9
- }
10
- export function isValidComplexity(value) {
11
- return value in COMPLEXITY_SKIP;
12
- }
@@ -1,3 +0,0 @@
1
- import type { StageName, StageDefinition, StageResult, PipelineContext } from "../types.js";
2
- export type StageExecutor = (ctx: PipelineContext, def: StageDefinition) => StageResult | Promise<StageResult>;
3
- export declare function getExecutor(name: StageName): StageExecutor;
@@ -1,20 +0,0 @@
1
- import { executeAgentStage } from "../stages/agent.js";
2
- import { executeVerifyWithAutofix } from "../stages/verify.js";
3
- import { executeReviewWithFix } from "../stages/review.js";
4
- import { executeShipStage } from "../stages/ship.js";
5
- const EXECUTOR_REGISTRY = {
6
- taskify: executeAgentStage,
7
- plan: executeAgentStage,
8
- build: executeAgentStage,
9
- verify: executeVerifyWithAutofix,
10
- review: executeReviewWithFix,
11
- "review-fix": executeAgentStage,
12
- ship: executeShipStage,
13
- };
14
- export function getExecutor(name) {
15
- const executor = EXECUTOR_REGISTRY[name];
16
- if (!executor) {
17
- throw new Error(`No executor registered for stage: ${name}`);
18
- }
19
- return executor;
20
- }
@@ -1,17 +0,0 @@
1
- import type { StageDefinition, PipelineStatus, PipelineContext } from "../types.js";
2
- export declare function applyPreStageLabel(ctx: PipelineContext, def: StageDefinition): void;
3
- /**
4
- * Check for clarifying questions after taskify/plan.
5
- * Returns the paused PipelineStatus if pipeline should stop, null to continue.
6
- */
7
- export declare function checkQuestionsAfterStage(ctx: PipelineContext, def: StageDefinition, state: PipelineStatus): PipelineStatus | null;
8
- /**
9
- * Auto-detect complexity from task.json after taskify.
10
- * Returns new complexity + activeStages if detected, null otherwise.
11
- */
12
- export declare function autoDetectComplexity(ctx: PipelineContext, def: StageDefinition): {
13
- complexity: "low" | "medium" | "high";
14
- activeStages: StageDefinition[];
15
- } | null;
16
- export declare function commitAfterStage(ctx: PipelineContext, def: StageDefinition): void;
17
- export declare function postSkippedStagesComment(ctx: PipelineContext, complexity: string, activeStages: StageDefinition[]): void;
@@ -1,110 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import { STAGES } from "../definitions.js";
4
- import { setLifecycleLabel, setLabel, postComment } from "../github-api.js";
5
- import { commitAll } from "../git-utils.js";
6
- import { checkForQuestions } from "./questions.js";
7
- import { filterByComplexity, isValidComplexity } from "./complexity.js";
8
- import { writeState } from "./state.js";
9
- import { logger } from "../logger.js";
10
- // ─── Pre-stage ──────────────────────────────────────────────────────────────
11
- export function applyPreStageLabel(ctx, def) {
12
- if (!ctx.input.issueNumber || ctx.input.local)
13
- return;
14
- if (def.name === "build")
15
- setLifecycleLabel(ctx.input.issueNumber, "building");
16
- if (def.name === "review")
17
- setLifecycleLabel(ctx.input.issueNumber, "review");
18
- }
19
- // ─── Post-stage (success) ───────────────────────────────────────────────────
20
- /**
21
- * Check for clarifying questions after taskify/plan.
22
- * Returns the paused PipelineStatus if pipeline should stop, null to continue.
23
- */
24
- export function checkQuestionsAfterStage(ctx, def, state) {
25
- if (def.name !== "taskify" && def.name !== "plan")
26
- return null;
27
- if (ctx.input.dryRun)
28
- return null;
29
- const paused = checkForQuestions(ctx, def.name);
30
- if (!paused)
31
- return null;
32
- state.state = "failed";
33
- state.stages[def.name] = {
34
- ...state.stages[def.name],
35
- state: "completed",
36
- error: "paused: waiting for answers",
37
- };
38
- writeState(state, ctx.taskDir);
39
- logger.info(` Pipeline paused — questions posted on issue`);
40
- return state;
41
- }
42
- /**
43
- * Auto-detect complexity from task.json after taskify.
44
- * Returns new complexity + activeStages if detected, null otherwise.
45
- */
46
- export function autoDetectComplexity(ctx, def) {
47
- if (def.name !== "taskify")
48
- return null;
49
- if (ctx.input.complexity)
50
- return null;
51
- try {
52
- const taskJsonPath = path.join(ctx.taskDir, "task.json");
53
- if (!fs.existsSync(taskJsonPath))
54
- return null;
55
- const raw = fs.readFileSync(taskJsonPath, "utf-8");
56
- const cleaned = raw.replace(/^```json\s*\n?/m, "").replace(/\n?```\s*$/m, "");
57
- const taskJson = JSON.parse(cleaned);
58
- if (!taskJson.risk_level || !isValidComplexity(taskJson.risk_level))
59
- return null;
60
- const complexity = taskJson.risk_level;
61
- const activeStages = filterByComplexity(STAGES, complexity);
62
- logger.info(` Complexity auto-detected: ${complexity} (${activeStages.map(s => s.name).join(" → ")})`);
63
- if (ctx.input.issueNumber && !ctx.input.local) {
64
- try {
65
- setLifecycleLabel(ctx.input.issueNumber, complexity);
66
- }
67
- catch { /* ignore */ }
68
- if (taskJson.task_type) {
69
- try {
70
- setLabel(ctx.input.issueNumber, `kody:${taskJson.task_type}`);
71
- }
72
- catch { /* ignore */ }
73
- }
74
- }
75
- return { complexity, activeStages };
76
- }
77
- catch {
78
- return null;
79
- }
80
- }
81
- export function commitAfterStage(ctx, def) {
82
- if (ctx.input.dryRun || !ctx.input.issueNumber)
83
- return;
84
- if (def.name === "build") {
85
- try {
86
- commitAll(`feat(${ctx.taskId}): implement task`, ctx.projectDir);
87
- }
88
- catch { /* ignore */ }
89
- }
90
- if (def.name === "review-fix") {
91
- try {
92
- commitAll(`fix(${ctx.taskId}): address review`, ctx.projectDir);
93
- }
94
- catch { /* ignore */ }
95
- }
96
- }
97
- // ─── Skip logic ─────────────────────────────────────────────────────────────
98
- export function postSkippedStagesComment(ctx, complexity, activeStages) {
99
- if (!ctx.input.issueNumber || ctx.input.local || ctx.input.dryRun)
100
- return;
101
- const skipped = STAGES
102
- .filter(s => !activeStages.find(a => a.name === s.name))
103
- .map(s => s.name);
104
- if (skipped.length === 0)
105
- return;
106
- try {
107
- postComment(ctx.input.issueNumber, `⚡ **Complexity: ${complexity}** — skipping ${skipped.join(", ")} (not needed for ${complexity}-risk tasks)`);
108
- }
109
- catch { /* ignore */ }
110
- }
@@ -1,2 +0,0 @@
1
- import type { PipelineContext } from "../types.js";
2
- export declare function checkForQuestions(ctx: PipelineContext, stageName: string): boolean;
@@ -1,44 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import { postComment, setLifecycleLabel } from "../github-api.js";
4
- export function checkForQuestions(ctx, stageName) {
5
- if (ctx.input.local || !ctx.input.issueNumber)
6
- return false;
7
- try {
8
- if (stageName === "taskify") {
9
- const taskJsonPath = path.join(ctx.taskDir, "task.json");
10
- if (!fs.existsSync(taskJsonPath))
11
- return false;
12
- const raw = fs.readFileSync(taskJsonPath, "utf-8");
13
- const cleaned = raw.replace(/^```json\s*\n?/m, "").replace(/\n?```\s*$/m, "");
14
- const taskJson = JSON.parse(cleaned);
15
- if (taskJson.questions && Array.isArray(taskJson.questions) && taskJson.questions.length > 0) {
16
- const body = `🤔 **Kody has questions before proceeding:**\n\n${taskJson.questions.map((q, i) => `${i + 1}. ${q}`).join("\n")}\n\nReply with \`@kody approve\` and your answers in the comment body.`;
17
- postComment(ctx.input.issueNumber, body);
18
- setLifecycleLabel(ctx.input.issueNumber, "waiting");
19
- return true;
20
- }
21
- }
22
- if (stageName === "plan") {
23
- const planPath = path.join(ctx.taskDir, "plan.md");
24
- if (!fs.existsSync(planPath))
25
- return false;
26
- const plan = fs.readFileSync(planPath, "utf-8");
27
- const questionsMatch = plan.match(/## Questions\s*\n([\s\S]*?)(?=\n## |\n*$)/);
28
- if (questionsMatch) {
29
- const questionsText = questionsMatch[1].trim();
30
- const questions = questionsText.split("\n").filter((l) => l.startsWith("- ")).map((l) => l.slice(2));
31
- if (questions.length > 0) {
32
- const body = `🏗️ **Kody has architecture questions:**\n\n${questions.map((q, i) => `${i + 1}. ${q}`).join("\n")}\n\nReply with \`@kody approve\` and your answers in the comment body.`;
33
- postComment(ctx.input.issueNumber, body);
34
- setLifecycleLabel(ctx.input.issueNumber, "waiting");
35
- return true;
36
- }
37
- }
38
- }
39
- }
40
- catch {
41
- // Don't fail pipeline on question parsing errors
42
- }
43
- return false;
44
- }
@@ -1,2 +0,0 @@
1
- import type { PipelineContext, AgentRunner } from "../types.js";
2
- export declare function getRunnerForStage(ctx: PipelineContext, stageName: string): AgentRunner;
@@ -1,13 +0,0 @@
1
- import { getProjectConfig } from "../config.js";
2
- export function getRunnerForStage(ctx, stageName) {
3
- const config = getProjectConfig();
4
- const runnerName = config.agent.stageRunners?.[stageName] ??
5
- config.agent.defaultRunner ??
6
- Object.keys(ctx.runners)[0] ??
7
- "claude";
8
- const runner = ctx.runners[runnerName];
9
- if (!runner) {
10
- throw new Error(`Runner "${runnerName}" not found for stage ${stageName}. Available: ${Object.keys(ctx.runners).join(", ")}`);
11
- }
12
- return runner;
13
- }
@@ -1,4 +0,0 @@
1
- import type { PipelineStatus } from "../types.js";
2
- export declare function loadState(taskId: string, taskDir: string): PipelineStatus | null;
3
- export declare function writeState(state: PipelineStatus, taskDir: string): void;
4
- export declare function initState(taskId: string): PipelineStatus;
@@ -1,37 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import { STAGES } from "../definitions.js";
4
- export function loadState(taskId, taskDir) {
5
- const p = path.join(taskDir, "status.json");
6
- if (!fs.existsSync(p))
7
- return null;
8
- try {
9
- const raw = JSON.parse(fs.readFileSync(p, "utf-8"));
10
- if (raw.taskId === taskId)
11
- return raw;
12
- return null;
13
- }
14
- catch {
15
- return null;
16
- }
17
- }
18
- export function writeState(state, taskDir) {
19
- const updated = {
20
- ...state,
21
- updatedAt: new Date().toISOString(),
22
- };
23
- const target = path.join(taskDir, "status.json");
24
- const tmp = target + ".tmp";
25
- fs.writeFileSync(tmp, JSON.stringify(updated, null, 2));
26
- fs.renameSync(tmp, target);
27
- // Sync the caller's reference
28
- state.updatedAt = updated.updatedAt;
29
- }
30
- export function initState(taskId) {
31
- const stages = {};
32
- for (const stage of STAGES) {
33
- stages[stage.name] = { state: "pending", retries: 0 };
34
- }
35
- const now = new Date().toISOString();
36
- return { taskId, state: "running", stages, createdAt: now, updatedAt: now };
37
- }
@@ -1,3 +0,0 @@
1
- import type { PipelineStatus, PipelineContext } from "./types.js";
2
- export declare function runPipeline(ctx: PipelineContext): Promise<PipelineStatus>;
3
- export declare function printStatus(taskId: string, taskDir: string): void;
package/dist/pipeline.js DELETED
@@ -1,213 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import { STAGES } from "./definitions.js";
4
- import { ensureFeatureBranch, syncWithDefault } from "./git-utils.js";
5
- import { setLifecycleLabel } from "./github-api.js";
6
- import { logger, ciGroup, ciGroupEnd } from "./logger.js";
7
- import { loadState, writeState, initState } from "./pipeline/state.js";
8
- import { filterByComplexity } from "./pipeline/complexity.js";
9
- import { getExecutor } from "./pipeline/executor-registry.js";
10
- import { applyPreStageLabel, checkQuestionsAfterStage, autoDetectComplexity, commitAfterStage, postSkippedStagesComment, } from "./pipeline/hooks.js";
11
- import { autoLearn } from "./learning/auto-learn.js";
12
- import { runRetrospective } from "./retrospective.js";
13
- // ─── Helpers ────────────────────────────────────────────────────────────────
14
- function ensureFeatureBranchIfNeeded(ctx) {
15
- if (!ctx.input.issueNumber || ctx.input.dryRun)
16
- return;
17
- try {
18
- const taskMdPath = path.join(ctx.taskDir, "task.md");
19
- const title = fs.existsSync(taskMdPath)
20
- ? fs.readFileSync(taskMdPath, "utf-8").split("\n")[0].slice(0, 50)
21
- : ctx.taskId;
22
- ensureFeatureBranch(ctx.input.issueNumber, title, ctx.projectDir);
23
- syncWithDefault(ctx.projectDir);
24
- }
25
- catch (err) {
26
- logger.warn(` Failed to create/sync feature branch: ${err}`);
27
- }
28
- }
29
- // ─── Lock ───────────────────────────────────────────────────────────────────
30
- function acquireLock(taskDir) {
31
- const lockPath = path.join(taskDir, ".lock");
32
- if (fs.existsSync(lockPath)) {
33
- try {
34
- const pid = parseInt(fs.readFileSync(lockPath, "utf-8").trim(), 10);
35
- try {
36
- process.kill(pid, 0);
37
- throw new Error(`Pipeline already running (PID ${pid})`);
38
- }
39
- catch (e) {
40
- if (e.code !== "ESRCH")
41
- throw e;
42
- // PID not alive — stale lock, safe to overwrite
43
- }
44
- }
45
- catch (e) {
46
- if (e instanceof Error && e.message.startsWith("Pipeline already"))
47
- throw e;
48
- // Corrupt lock file — overwrite
49
- }
50
- }
51
- fs.writeFileSync(lockPath, String(process.pid));
52
- }
53
- function releaseLock(taskDir) {
54
- try {
55
- fs.unlinkSync(path.join(taskDir, ".lock"));
56
- }
57
- catch { /* ignore */ }
58
- }
59
- // ─── Pipeline Loop ──────────────────────────────────────────────────────────
60
- export async function runPipeline(ctx) {
61
- acquireLock(ctx.taskDir);
62
- try {
63
- return await runPipelineInner(ctx);
64
- }
65
- finally {
66
- releaseLock(ctx.taskDir);
67
- }
68
- }
69
- async function runPipelineInner(ctx) {
70
- const pipelineStartTime = Date.now();
71
- let state = loadState(ctx.taskId, ctx.taskDir);
72
- if (!state) {
73
- state = initState(ctx.taskId);
74
- writeState(state, ctx.taskDir);
75
- }
76
- // Reset for rerun
77
- if (state.state !== "running") {
78
- state.state = "running";
79
- for (const stage of STAGES) {
80
- const s = state.stages[stage.name];
81
- if (s.state === "running" || s.state === "failed" || s.state === "timeout") {
82
- state.stages[stage.name] = { ...s, state: "pending" };
83
- }
84
- }
85
- writeState(state, ctx.taskDir);
86
- }
87
- const fromStage = ctx.input.fromStage;
88
- let startExecution = !fromStage;
89
- logger.info(`Pipeline started: ${ctx.taskId}`);
90
- logger.info(`Stages: ${STAGES.map((s) => s.name).join(" → ")}`);
91
- if (fromStage)
92
- logger.info(`Resuming from: ${fromStage}`);
93
- if (ctx.input.issueNumber && !ctx.input.local) {
94
- const initialPhase = ctx.input.mode === "rerun" ? "building" : "planning";
95
- setLifecycleLabel(ctx.input.issueNumber, initialPhase);
96
- }
97
- ensureFeatureBranchIfNeeded(ctx);
98
- let complexity = ctx.input.complexity ?? "high";
99
- let activeStages = filterByComplexity(STAGES, complexity);
100
- let skippedStagesCommentPosted = false;
101
- for (const def of STAGES) {
102
- // fromStage skip
103
- if (!startExecution) {
104
- if (def.name === fromStage) {
105
- startExecution = true;
106
- }
107
- else {
108
- continue;
109
- }
110
- }
111
- if (state.stages[def.name].state === "completed") {
112
- logger.info(`[${def.name}] already completed, skipping`);
113
- continue;
114
- }
115
- // Complexity skip
116
- if (!activeStages.find((s) => s.name === def.name)) {
117
- logger.info(`[${def.name}] skipped (complexity: ${complexity})`);
118
- state.stages[def.name] = { state: "completed", retries: 0, outputFile: undefined };
119
- writeState(state, ctx.taskDir);
120
- if (!skippedStagesCommentPosted) {
121
- postSkippedStagesComment(ctx, complexity, activeStages);
122
- skippedStagesCommentPosted = true;
123
- }
124
- continue;
125
- }
126
- ciGroup(`Stage: ${def.name}`);
127
- state.stages[def.name] = { state: "running", startedAt: new Date().toISOString(), retries: 0 };
128
- writeState(state, ctx.taskDir);
129
- logger.info(`[${def.name}] starting...`);
130
- applyPreStageLabel(ctx, def);
131
- // Execute stage via registry
132
- let result;
133
- try {
134
- result = await getExecutor(def.name)(ctx, def);
135
- }
136
- catch (error) {
137
- result = {
138
- outcome: "failed",
139
- retries: 0,
140
- error: error instanceof Error ? error.message : String(error),
141
- };
142
- }
143
- ciGroupEnd();
144
- if (result.outcome === "completed") {
145
- state.stages[def.name] = {
146
- state: "completed",
147
- completedAt: new Date().toISOString(),
148
- retries: result.retries,
149
- outputFile: result.outputFile,
150
- };
151
- logger.info(`[${def.name}] ✓ completed`);
152
- const paused = checkQuestionsAfterStage(ctx, def, state);
153
- if (paused)
154
- return paused;
155
- const detected = autoDetectComplexity(ctx, def);
156
- if (detected) {
157
- complexity = detected.complexity;
158
- activeStages = detected.activeStages;
159
- }
160
- commitAfterStage(ctx, def);
161
- }
162
- else {
163
- // Failed or timed out
164
- const isTimeout = result.outcome === "timed_out";
165
- state.stages[def.name] = {
166
- state: isTimeout ? "timeout" : "failed",
167
- retries: result.retries,
168
- error: isTimeout ? "Stage timed out" : (result.error ?? "Stage failed"),
169
- };
170
- state.state = "failed";
171
- writeState(state, ctx.taskDir);
172
- logger.error(`[${def.name}] ${isTimeout ? "⏱ timed out" : `✗ failed: ${result.error}`}`);
173
- if (ctx.input.issueNumber && !ctx.input.local) {
174
- setLifecycleLabel(ctx.input.issueNumber, "failed");
175
- }
176
- break;
177
- }
178
- writeState(state, ctx.taskDir);
179
- }
180
- const allCompleted = STAGES.every((s) => state.stages[s.name].state === "completed");
181
- if (allCompleted) {
182
- state.state = "completed";
183
- writeState(state, ctx.taskDir);
184
- logger.info(`Pipeline completed: ${ctx.taskId}`);
185
- if (ctx.input.issueNumber && !ctx.input.local) {
186
- setLifecycleLabel(ctx.input.issueNumber, "done");
187
- }
188
- autoLearn(ctx);
189
- }
190
- await runRetrospective(ctx, state, pipelineStartTime).catch(() => { });
191
- return state;
192
- }
193
- // ─── Status Display ─────────────────────────────────────────────────────────
194
- export function printStatus(taskId, taskDir) {
195
- const state = loadState(taskId, taskDir);
196
- if (!state) {
197
- console.log(`No status found for task ${taskId}`);
198
- return;
199
- }
200
- console.log(`\nTask: ${state.taskId}`);
201
- console.log(`State: ${state.state}`);
202
- console.log(`Created: ${state.createdAt}`);
203
- console.log(`Updated: ${state.updatedAt}\n`);
204
- for (const stage of STAGES) {
205
- const s = state.stages[stage.name];
206
- const icon = s.state === "completed" ? "✓" :
207
- s.state === "failed" ? "✗" :
208
- s.state === "running" ? "▶" :
209
- s.state === "timeout" ? "⏱" : "○";
210
- const extra = s.error ? ` — ${s.error}` : "";
211
- console.log(` ${icon} ${stage.name}: ${s.state}${extra}`);
212
- }
213
- }
@@ -1 +0,0 @@
1
- export declare function runPreflight(): void;
package/dist/preflight.js DELETED
@@ -1,69 +0,0 @@
1
- import { execFileSync } from "child_process";
2
- import * as fs from "fs";
3
- import { logger } from "./logger.js";
4
- function check(name, fn) {
5
- try {
6
- const detail = fn() ?? undefined;
7
- return { name, ok: true, detail };
8
- }
9
- catch {
10
- return { name, ok: false };
11
- }
12
- }
13
- export function runPreflight() {
14
- const checks = [
15
- check("claude CLI", () => {
16
- const v = execFileSync("claude", ["--version"], {
17
- encoding: "utf-8",
18
- timeout: 10_000,
19
- stdio: ["pipe", "pipe", "pipe"],
20
- }).trim();
21
- return v;
22
- }),
23
- check("git repo", () => {
24
- execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
25
- encoding: "utf-8",
26
- timeout: 5_000,
27
- stdio: ["pipe", "pipe", "pipe"],
28
- });
29
- }),
30
- check("pnpm", () => {
31
- const v = execFileSync("pnpm", ["--version"], {
32
- encoding: "utf-8",
33
- timeout: 5_000,
34
- stdio: ["pipe", "pipe", "pipe"],
35
- }).trim();
36
- return v;
37
- }),
38
- check("node >= 18", () => {
39
- const v = execFileSync("node", ["--version"], {
40
- encoding: "utf-8",
41
- timeout: 5_000,
42
- stdio: ["pipe", "pipe", "pipe"],
43
- }).trim();
44
- const major = parseInt(v.replace("v", "").split(".")[0], 10);
45
- if (major < 18)
46
- throw new Error(`Node ${v} < 18`);
47
- return v;
48
- }),
49
- check("gh CLI", () => {
50
- const v = execFileSync("gh", ["--version"], {
51
- encoding: "utf-8",
52
- timeout: 5_000,
53
- stdio: ["pipe", "pipe", "pipe"],
54
- }).trim().split("\n")[0];
55
- return v;
56
- }),
57
- check("package.json", () => {
58
- if (!fs.existsSync("package.json"))
59
- throw new Error("not found");
60
- }),
61
- ];
62
- const failed = checks.filter((c) => !c.ok);
63
- for (const c of checks) {
64
- logger.info(` ${c.ok ? "✓" : "✗"} ${c.name}${c.detail ? ` (${c.detail})` : ""}`);
65
- }
66
- if (failed.length > 0) {
67
- throw new Error(`Preflight failed: ${failed.map((c) => c.name).join(", ")}`);
68
- }
69
- }
@@ -1,26 +0,0 @@
1
- import type { StageName, PipelineStatus, PipelineContext } from "./types.js";
2
- export interface RetrospectiveEntry {
3
- timestamp: string;
4
- taskId: string;
5
- outcome: "completed" | "failed";
6
- durationMs: number;
7
- stageResults: Record<string, {
8
- state: string;
9
- retries: number;
10
- durationMs?: number;
11
- error?: string;
12
- }>;
13
- failedStage?: StageName;
14
- observation: string;
15
- patternMatch: string | null;
16
- suggestion: string;
17
- pipelineFlaw: {
18
- component: string;
19
- issue: string;
20
- evidence: string;
21
- } | null;
22
- }
23
- export declare function collectRunContext(ctx: PipelineContext, state: PipelineStatus, pipelineStartTime: number): string;
24
- export declare function readPreviousRetrospectives(projectDir: string, limit?: number): RetrospectiveEntry[];
25
- export declare function appendRetrospectiveEntry(projectDir: string, entry: RetrospectiveEntry): void;
26
- export declare function runRetrospective(ctx: PipelineContext, state: PipelineStatus, pipelineStartTime: number): Promise<void>;