@towles/tool 0.0.53 → 0.0.55
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 +82 -72
- package/package.json +8 -7
- package/src/commands/auto-claude.ts +219 -0
- package/src/commands/doctor.ts +1 -34
- package/src/config/settings.ts +0 -10
- package/src/lib/auto-claude/config.test.ts +53 -0
- package/src/lib/auto-claude/config.ts +68 -0
- package/src/lib/auto-claude/index.ts +14 -0
- package/src/lib/auto-claude/pipeline.test.ts +14 -0
- package/src/lib/auto-claude/pipeline.ts +64 -0
- package/src/lib/auto-claude/prompt-templates/01-prompt-research.md +28 -0
- package/src/lib/auto-claude/prompt-templates/02-prompt-plan.md +28 -0
- package/src/lib/auto-claude/prompt-templates/03-prompt-plan-annotations.md +21 -0
- package/src/lib/auto-claude/prompt-templates/04-prompt-plan-implementation.md +33 -0
- package/src/lib/auto-claude/prompt-templates/05-prompt-implement.md +31 -0
- package/src/lib/auto-claude/prompt-templates/06-prompt-review.md +30 -0
- package/src/lib/auto-claude/prompt-templates/07-prompt-refresh.md +39 -0
- package/src/lib/auto-claude/prompt-templates/index.test.ts +145 -0
- package/src/lib/auto-claude/prompt-templates/index.ts +44 -0
- package/src/lib/auto-claude/steps/create-pr.ts +93 -0
- package/src/lib/auto-claude/steps/fetch-issues.ts +64 -0
- package/src/lib/auto-claude/steps/implement.ts +63 -0
- package/src/lib/auto-claude/steps/plan-annotations.ts +54 -0
- package/src/lib/auto-claude/steps/plan-implementation.ts +14 -0
- package/src/lib/auto-claude/steps/plan.ts +14 -0
- package/src/lib/auto-claude/steps/refresh.ts +114 -0
- package/src/lib/auto-claude/steps/remove-label.ts +22 -0
- package/src/lib/auto-claude/steps/research.ts +21 -0
- package/src/lib/auto-claude/steps/review.ts +14 -0
- package/src/lib/auto-claude/utils.test.ts +136 -0
- package/src/lib/auto-claude/utils.ts +334 -0
- package/src/commands/ralph/plan/add.ts +0 -69
- package/src/commands/ralph/plan/done.ts +0 -82
- package/src/commands/ralph/plan/list.test.ts +0 -48
- package/src/commands/ralph/plan/list.ts +0 -100
- package/src/commands/ralph/plan/remove.ts +0 -71
- package/src/commands/ralph/run.test.ts +0 -607
- package/src/commands/ralph/run.ts +0 -362
- package/src/commands/ralph/show.ts +0 -88
- package/src/lib/ralph/execution.ts +0 -292
- package/src/lib/ralph/formatter.ts +0 -240
- package/src/lib/ralph/index.ts +0 -4
- package/src/lib/ralph/state.ts +0 -201
|
@@ -1,362 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import { Flags } from "@oclif/core";
|
|
3
|
-
import consola from "consola";
|
|
4
|
-
import { colors } from "consola/utils";
|
|
5
|
-
import { BaseCommand } from "../base.js";
|
|
6
|
-
import {
|
|
7
|
-
DEFAULT_STATE_FILE,
|
|
8
|
-
DEFAULT_LOG_FILE,
|
|
9
|
-
DEFAULT_MAX_ITERATIONS,
|
|
10
|
-
DEFAULT_COMPLETION_MARKER,
|
|
11
|
-
DEFAULT_TASK_DONE_MARKER,
|
|
12
|
-
CLAUDE_DEFAULT_ARGS,
|
|
13
|
-
loadState,
|
|
14
|
-
saveState,
|
|
15
|
-
appendHistory,
|
|
16
|
-
resolveRalphPath,
|
|
17
|
-
getRalphPaths,
|
|
18
|
-
} from "../../lib/ralph/state.js";
|
|
19
|
-
import {
|
|
20
|
-
buildIterationPrompt,
|
|
21
|
-
formatDuration,
|
|
22
|
-
extractOutputSummary,
|
|
23
|
-
detectCompletionMarker,
|
|
24
|
-
} from "../../lib/ralph/formatter.js";
|
|
25
|
-
import { checkClaudeCli, runIteration } from "../../lib/ralph/execution.js";
|
|
26
|
-
import type { RalphPlan } from "../../lib/ralph/state.js";
|
|
27
|
-
|
|
28
|
-
/** Get the plan to work on: focused plan or first incomplete */
|
|
29
|
-
function getCurrentPlan(plans: RalphPlan[], focusedPlanId: number | null): RalphPlan | undefined {
|
|
30
|
-
if (focusedPlanId !== null) {
|
|
31
|
-
return plans.find((p) => p.id === focusedPlanId);
|
|
32
|
-
}
|
|
33
|
-
return plans.find((p) => p.status !== "done");
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Run the autonomous ralph loop
|
|
38
|
-
*/
|
|
39
|
-
export default class Run extends BaseCommand {
|
|
40
|
-
static override description = "Start the autonomous ralph loop";
|
|
41
|
-
|
|
42
|
-
static override examples = [
|
|
43
|
-
{ description: "Start the autonomous loop", command: "<%= config.bin %> <%= command.id %>" },
|
|
44
|
-
{
|
|
45
|
-
description: "Limit to 20 iterations",
|
|
46
|
-
command: "<%= config.bin %> <%= command.id %> --maxIterations 20",
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
description: "Focus on specific plan",
|
|
50
|
-
command: "<%= config.bin %> <%= command.id %> --planId 5",
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
description: "Run without auto-committing",
|
|
54
|
-
command: "<%= config.bin %> <%= command.id %> --no-autoCommit",
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
description: "Preview config without executing",
|
|
58
|
-
command: "<%= config.bin %> <%= command.id %> --dryRun",
|
|
59
|
-
},
|
|
60
|
-
];
|
|
61
|
-
|
|
62
|
-
static override flags = {
|
|
63
|
-
...BaseCommand.baseFlags,
|
|
64
|
-
stateFile: Flags.string({
|
|
65
|
-
char: "s",
|
|
66
|
-
description: `State file path (default: ${DEFAULT_STATE_FILE})`,
|
|
67
|
-
}),
|
|
68
|
-
planId: Flags.integer({
|
|
69
|
-
char: "p",
|
|
70
|
-
description: "Focus on specific plan ID",
|
|
71
|
-
}),
|
|
72
|
-
maxIterations: Flags.integer({
|
|
73
|
-
char: "m",
|
|
74
|
-
description: "Max iterations",
|
|
75
|
-
default: DEFAULT_MAX_ITERATIONS,
|
|
76
|
-
}),
|
|
77
|
-
autoCommit: Flags.boolean({
|
|
78
|
-
description: "Auto-commit after each completed plan",
|
|
79
|
-
default: true,
|
|
80
|
-
allowNo: true,
|
|
81
|
-
}),
|
|
82
|
-
dryRun: Flags.boolean({
|
|
83
|
-
char: "n",
|
|
84
|
-
description: "Show config without executing",
|
|
85
|
-
default: false,
|
|
86
|
-
}),
|
|
87
|
-
claudeArgs: Flags.string({
|
|
88
|
-
description: "Extra args to pass to claude CLI (space-separated)",
|
|
89
|
-
}),
|
|
90
|
-
logFile: Flags.string({
|
|
91
|
-
description: `Log file path (default: ${DEFAULT_LOG_FILE})`,
|
|
92
|
-
}),
|
|
93
|
-
completionMarker: Flags.string({
|
|
94
|
-
description: "Completion marker",
|
|
95
|
-
default: DEFAULT_COMPLETION_MARKER,
|
|
96
|
-
}),
|
|
97
|
-
taskDoneMarker: Flags.string({
|
|
98
|
-
description: "Task done marker",
|
|
99
|
-
default: DEFAULT_TASK_DONE_MARKER,
|
|
100
|
-
}),
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
async run(): Promise<void> {
|
|
104
|
-
const { flags } = await this.parse(Run);
|
|
105
|
-
const ralphSettings = this.settings.settings.ralphSettings;
|
|
106
|
-
const stateFile = resolveRalphPath(flags.stateFile, "stateFile", ralphSettings);
|
|
107
|
-
const logFile = resolveRalphPath(flags.logFile, "logFile", ralphSettings);
|
|
108
|
-
const ralphPaths = getRalphPaths(ralphSettings);
|
|
109
|
-
|
|
110
|
-
const maxIterations = flags.maxIterations;
|
|
111
|
-
const extraClaudeArgs = flags.claudeArgs?.split(" ").filter(Boolean) || [];
|
|
112
|
-
const focusedPlanId = flags.planId ?? null;
|
|
113
|
-
|
|
114
|
-
// Load existing state
|
|
115
|
-
let state = loadState(stateFile);
|
|
116
|
-
|
|
117
|
-
if (!state) {
|
|
118
|
-
this.error(`No state file found at: ${stateFile}\nUse: tt ralph plan add --file path.md`);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const remainingPlans = state.plans.filter((t) => t.status !== "done");
|
|
122
|
-
if (remainingPlans.length === 0) {
|
|
123
|
-
consola.log(colors.green("✅ All plans are done!"));
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Validate focused plan if specified
|
|
128
|
-
if (focusedPlanId !== null) {
|
|
129
|
-
const focusedPlan = state.plans.find((t) => t.id === focusedPlanId);
|
|
130
|
-
if (!focusedPlan) {
|
|
131
|
-
this.error(`Plan #${focusedPlanId} not found. Use: tt ralph plan list`);
|
|
132
|
-
}
|
|
133
|
-
if (focusedPlan.status === "done") {
|
|
134
|
-
consola.log(colors.yellow(`Plan #${focusedPlanId} is already done.`));
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Get current plan to work on
|
|
140
|
-
const currentPlan = getCurrentPlan(state.plans, focusedPlanId);
|
|
141
|
-
if (!currentPlan) {
|
|
142
|
-
consola.log(colors.green("✅ All plans are done!"));
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Dry run mode
|
|
147
|
-
if (flags.dryRun) {
|
|
148
|
-
consola.log(colors.bold("\n=== DRY RUN ===\n"));
|
|
149
|
-
consola.log(colors.cyan("Config:"));
|
|
150
|
-
consola.log(` Max iterations: ${maxIterations}`);
|
|
151
|
-
consola.log(` State file: ${stateFile}`);
|
|
152
|
-
consola.log(` Log file: ${logFile}`);
|
|
153
|
-
consola.log(` Completion marker: ${flags.completionMarker}`);
|
|
154
|
-
consola.log(` Task done marker: ${flags.taskDoneMarker}`);
|
|
155
|
-
consola.log(` Auto-commit: ${flags.autoCommit}`);
|
|
156
|
-
consola.log(` Claude args: ${[...CLAUDE_DEFAULT_ARGS, ...extraClaudeArgs].join(" ")}`);
|
|
157
|
-
consola.log(` Remaining plans: ${remainingPlans.length}`);
|
|
158
|
-
|
|
159
|
-
consola.log(colors.cyan("\nCurrent plan:"));
|
|
160
|
-
consola.log(` #${currentPlan.id}: ${currentPlan.planFilePath}`);
|
|
161
|
-
|
|
162
|
-
// Show prompt preview
|
|
163
|
-
const prompt = buildIterationPrompt({
|
|
164
|
-
completionMarker: flags.completionMarker,
|
|
165
|
-
taskDoneMarker: flags.taskDoneMarker,
|
|
166
|
-
plan: currentPlan,
|
|
167
|
-
skipCommit: !flags.autoCommit,
|
|
168
|
-
});
|
|
169
|
-
consola.log(colors.dim("─".repeat(60)));
|
|
170
|
-
consola.log(colors.bold("Prompt Preview"));
|
|
171
|
-
consola.log(colors.dim("─".repeat(60)));
|
|
172
|
-
consola.log(prompt);
|
|
173
|
-
consola.log(colors.dim("─".repeat(60)));
|
|
174
|
-
|
|
175
|
-
consola.log(colors.bold("\n=== END DRY RUN ===\n"));
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Check claude CLI is available
|
|
180
|
-
if (!(await checkClaudeCli())) {
|
|
181
|
-
this.error(
|
|
182
|
-
"claude CLI not found in PATH\nInstall Claude Code: https://docs.anthropic.com/en/docs/claude-code",
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Update state for this run
|
|
187
|
-
state.status = "running";
|
|
188
|
-
|
|
189
|
-
// Create log stream (append mode)
|
|
190
|
-
const logStream = fs.createWriteStream(logFile, { flags: "a" });
|
|
191
|
-
|
|
192
|
-
const ready = state.plans.filter((t) => t.status === "ready").length;
|
|
193
|
-
const done = state.plans.filter((t) => t.status === "done").length;
|
|
194
|
-
|
|
195
|
-
logStream.write(`\n${"=".repeat(60)}\n`);
|
|
196
|
-
logStream.write(`Ralph Loop Started: ${new Date().toISOString()}\n`);
|
|
197
|
-
logStream.write(`${"=".repeat(60)}\n\n`);
|
|
198
|
-
|
|
199
|
-
consola.log(colors.bold(colors.blue("\nRalph Loop Starting\n")));
|
|
200
|
-
consola.log(colors.dim(`Focus: ${focusedPlanId ? `Plan #${focusedPlanId}` : "Ralph picks"}`));
|
|
201
|
-
consola.log(colors.dim(`Max iterations: ${maxIterations}`));
|
|
202
|
-
consola.log(colors.dim(`Log file: ${logFile}`));
|
|
203
|
-
consola.log(colors.dim(`Auto-commit: ${flags.autoCommit}`));
|
|
204
|
-
consola.log(colors.dim(`Tasks: ${state.plans.length} (${done} done, ${ready} ready)`));
|
|
205
|
-
consola.log("");
|
|
206
|
-
|
|
207
|
-
logStream.write(`Focus: ${focusedPlanId ? `Plan #${focusedPlanId}` : "Ralph picks"}\n`);
|
|
208
|
-
logStream.write(`Max iterations: ${maxIterations}\n`);
|
|
209
|
-
logStream.write(`Tasks: ${state.plans.length} (${done} done, ${ready} ready)\n\n`);
|
|
210
|
-
|
|
211
|
-
// Handle SIGINT gracefully
|
|
212
|
-
let interrupted = false;
|
|
213
|
-
process.on("SIGINT", () => {
|
|
214
|
-
if (interrupted) {
|
|
215
|
-
logStream.end();
|
|
216
|
-
process.exit(130);
|
|
217
|
-
}
|
|
218
|
-
interrupted = true;
|
|
219
|
-
const msg = "\n\nInterrupted. Press Ctrl+C again to force exit.\n";
|
|
220
|
-
consola.log(colors.yellow(msg));
|
|
221
|
-
logStream.write(msg);
|
|
222
|
-
state.status = "error";
|
|
223
|
-
saveState(state, stateFile);
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
// Main loop
|
|
227
|
-
let completed = false;
|
|
228
|
-
let iteration = 0;
|
|
229
|
-
|
|
230
|
-
while (iteration < maxIterations && !interrupted && !completed) {
|
|
231
|
-
iteration++;
|
|
232
|
-
|
|
233
|
-
const iterHeader = `Iteration ${iteration}/${maxIterations}`;
|
|
234
|
-
logStream.write(`\n━━━ ${iterHeader} ━━━\n`);
|
|
235
|
-
|
|
236
|
-
const iterationStart = new Date().toISOString();
|
|
237
|
-
// Get current plan for this iteration
|
|
238
|
-
const plan = getCurrentPlan(state.plans, focusedPlanId);
|
|
239
|
-
if (!plan) {
|
|
240
|
-
completed = true;
|
|
241
|
-
state.status = "completed";
|
|
242
|
-
saveState(state, stateFile);
|
|
243
|
-
consola.log(
|
|
244
|
-
colors.bold(colors.green(`\n✅ All plans completed after ${iteration} iteration(s)`)),
|
|
245
|
-
);
|
|
246
|
-
logStream.write(`\n✅ All plans completed after ${iteration} iteration(s)\n`);
|
|
247
|
-
break;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const prompt = buildIterationPrompt({
|
|
251
|
-
completionMarker: flags.completionMarker,
|
|
252
|
-
taskDoneMarker: flags.taskDoneMarker,
|
|
253
|
-
plan: plan,
|
|
254
|
-
skipCommit: !flags.autoCommit,
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
// Log the prompt
|
|
258
|
-
logStream.write(`\n--- Prompt ---\n${prompt}\n--- End Prompt ---\n\n`);
|
|
259
|
-
|
|
260
|
-
// Build claude args
|
|
261
|
-
const iterClaudeArgs = [...extraClaudeArgs];
|
|
262
|
-
|
|
263
|
-
// Print iteration header
|
|
264
|
-
consola.log("");
|
|
265
|
-
consola.log(colors.bold(colors.blue(`━━━ ${iterHeader} ━━━`)));
|
|
266
|
-
consola.log(colors.dim("─".repeat(60)));
|
|
267
|
-
consola.log(colors.bold("Prompt"));
|
|
268
|
-
consola.log(colors.dim("─".repeat(60)));
|
|
269
|
-
consola.log(prompt);
|
|
270
|
-
consola.log(colors.dim("─".repeat(60)));
|
|
271
|
-
|
|
272
|
-
// Run iteration - output goes directly to stdout
|
|
273
|
-
const iterResult = await runIteration(prompt, iterClaudeArgs, logStream);
|
|
274
|
-
|
|
275
|
-
// Reload state from disk to pick up changes made by child claude process
|
|
276
|
-
const freshState = loadState(stateFile);
|
|
277
|
-
if (freshState) {
|
|
278
|
-
Object.assign(state, freshState);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const iterationEnd = new Date().toISOString();
|
|
282
|
-
const taskMarkerFound = detectCompletionMarker(iterResult.output, flags.taskDoneMarker);
|
|
283
|
-
const planMarkerFound = detectCompletionMarker(iterResult.output, flags.completionMarker);
|
|
284
|
-
|
|
285
|
-
// Calculate duration
|
|
286
|
-
const startTime = new Date(iterationStart).getTime();
|
|
287
|
-
const endTime = new Date(iterationEnd).getTime();
|
|
288
|
-
const durationMs = endTime - startTime;
|
|
289
|
-
const durationHuman = formatDuration(durationMs);
|
|
290
|
-
|
|
291
|
-
// Record history
|
|
292
|
-
appendHistory(
|
|
293
|
-
{
|
|
294
|
-
iteration,
|
|
295
|
-
startedAt: iterationStart,
|
|
296
|
-
completedAt: iterationEnd,
|
|
297
|
-
durationMs,
|
|
298
|
-
durationHuman,
|
|
299
|
-
outputSummary: extractOutputSummary(iterResult.output),
|
|
300
|
-
markerFound: planMarkerFound,
|
|
301
|
-
taskMarkerFound,
|
|
302
|
-
contextUsedPercent: iterResult.contextUsedPercent,
|
|
303
|
-
},
|
|
304
|
-
ralphPaths.historyFile,
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
// Save state
|
|
308
|
-
saveState(state, stateFile);
|
|
309
|
-
|
|
310
|
-
// Log marker status
|
|
311
|
-
if (taskMarkerFound) {
|
|
312
|
-
consola.log(colors.cyan(`Task marker found - current plan done, checking for more plans`));
|
|
313
|
-
logStream.write(`Task marker found - continuing to next plan\n`);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Log summary
|
|
317
|
-
const contextInfo =
|
|
318
|
-
iterResult.contextUsedPercent !== undefined
|
|
319
|
-
? ` | Context: ${iterResult.contextUsedPercent}%`
|
|
320
|
-
: "";
|
|
321
|
-
logStream.write(
|
|
322
|
-
`\n━━━ Iteration ${iteration} Summary ━━━\nDuration: ${durationHuman}${contextInfo}\nTask marker: ${taskMarkerFound ? "yes" : "no"}\nPlan marker: ${planMarkerFound ? "yes" : "no"}\n`,
|
|
323
|
-
);
|
|
324
|
-
consola.log(
|
|
325
|
-
colors.dim(
|
|
326
|
-
`Duration: ${durationHuman}${contextInfo} | Task: ${taskMarkerFound ? colors.green("yes") : colors.yellow("no")} | Plan: ${planMarkerFound ? colors.green("yes") : colors.yellow("no")}`,
|
|
327
|
-
),
|
|
328
|
-
);
|
|
329
|
-
|
|
330
|
-
// Check completion (only when ALL plans done marker found)
|
|
331
|
-
if (planMarkerFound) {
|
|
332
|
-
completed = true;
|
|
333
|
-
state.status = "completed";
|
|
334
|
-
saveState(state, stateFile);
|
|
335
|
-
consola.log(
|
|
336
|
-
colors.bold(colors.green(`\n✅ All plans completed after ${iteration} iteration(s)`)),
|
|
337
|
-
);
|
|
338
|
-
logStream.write(`\n✅ All plans completed after ${iteration} iteration(s)\n`);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
logStream.end();
|
|
343
|
-
|
|
344
|
-
// Final status
|
|
345
|
-
if (completed) {
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if (!interrupted && iteration >= maxIterations) {
|
|
350
|
-
state.status = "max_iterations_reached";
|
|
351
|
-
saveState(state, stateFile);
|
|
352
|
-
consola.log(
|
|
353
|
-
colors.bold(
|
|
354
|
-
colors.yellow(`\n⚠️ Max iterations (${maxIterations}) reached without completion`),
|
|
355
|
-
),
|
|
356
|
-
);
|
|
357
|
-
consola.log(colors.dim(`State saved to: ${stateFile}`));
|
|
358
|
-
logStream.write(`\n⚠️ Max iterations (${maxIterations}) reached without completion\n`);
|
|
359
|
-
this.exit(1);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { Flags } from "@oclif/core";
|
|
2
|
-
import consola from "consola";
|
|
3
|
-
import { colors } from "consola/utils";
|
|
4
|
-
import { BaseCommand } from "../base.js";
|
|
5
|
-
import { DEFAULT_STATE_FILE, loadState, resolveRalphPath } from "../../lib/ralph/state.js";
|
|
6
|
-
import {
|
|
7
|
-
formatPlanAsMarkdown,
|
|
8
|
-
formatPlanAsJson,
|
|
9
|
-
copyToClipboard,
|
|
10
|
-
} from "../../lib/ralph/formatter.js";
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Show plan summary with status, tasks, and mermaid graph
|
|
14
|
-
*/
|
|
15
|
-
export default class Show extends BaseCommand {
|
|
16
|
-
static override description = "Show plan summary with status, tasks, and mermaid graph";
|
|
17
|
-
|
|
18
|
-
static override examples = [
|
|
19
|
-
{
|
|
20
|
-
description: "Show plan summary with mermaid graph",
|
|
21
|
-
command: "<%= config.bin %> <%= command.id %>",
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
description: "Output plan as JSON",
|
|
25
|
-
command: "<%= config.bin %> <%= command.id %> --format json",
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
description: "Copy plan output to clipboard",
|
|
29
|
-
command: "<%= config.bin %> <%= command.id %> --copy",
|
|
30
|
-
},
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
static override flags = {
|
|
34
|
-
...BaseCommand.baseFlags,
|
|
35
|
-
stateFile: Flags.string({
|
|
36
|
-
char: "s",
|
|
37
|
-
description: `State file path (default: ${DEFAULT_STATE_FILE})`,
|
|
38
|
-
}),
|
|
39
|
-
format: Flags.string({
|
|
40
|
-
char: "f",
|
|
41
|
-
description: "Output format",
|
|
42
|
-
default: "default",
|
|
43
|
-
options: ["default", "markdown", "json"],
|
|
44
|
-
}),
|
|
45
|
-
copy: Flags.boolean({
|
|
46
|
-
char: "c",
|
|
47
|
-
description: "Copy output to clipboard",
|
|
48
|
-
default: false,
|
|
49
|
-
}),
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
async run(): Promise<void> {
|
|
53
|
-
const { flags } = await this.parse(Show);
|
|
54
|
-
const ralphSettings = this.settings.settings.ralphSettings;
|
|
55
|
-
const stateFile = resolveRalphPath(flags.stateFile, "stateFile", ralphSettings);
|
|
56
|
-
|
|
57
|
-
const state = loadState(stateFile);
|
|
58
|
-
|
|
59
|
-
if (!state) {
|
|
60
|
-
consola.log(colors.yellow(`No state file found at: ${stateFile}`));
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (state.plans.length === 0) {
|
|
65
|
-
consola.log(colors.yellow("No tasks in state file."));
|
|
66
|
-
consola.log(colors.dim('Use: tt ralph plan add "description"'));
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
let output: string;
|
|
71
|
-
|
|
72
|
-
if (flags.format === "json") {
|
|
73
|
-
output = formatPlanAsJson(state.plans, state);
|
|
74
|
-
} else {
|
|
75
|
-
output = formatPlanAsMarkdown(state.plans, state);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
consola.log(output);
|
|
79
|
-
|
|
80
|
-
if (flags.copy) {
|
|
81
|
-
if (copyToClipboard(output)) {
|
|
82
|
-
consola.log(colors.green("✓ Copied to clipboard"));
|
|
83
|
-
} else {
|
|
84
|
-
consola.log(colors.yellow("⚠ Could not copy to clipboard (xclip/xsel not installed?)"));
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|