@towles/tool 0.0.52 → 0.0.54

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 CHANGED
@@ -1,14 +1,6 @@
1
1
  # Towles Tool
2
2
 
3
- Personal CLI toolkit with autonomous task runner and quality-of-life commands for daily development.
4
-
5
- ## Features
6
-
7
- - **Ralph** - Autonomous task runner with session forking and context reuse
8
- - **Observability** - Token usage visualization with interactive treemaps
9
- - **Git workflows** - Branch creation, PR generation, and cleanup
10
- - **Journaling** - Daily notes, meeting notes, and general notes
11
- - **Claude Code plugins** - Personal plugin marketplace for Claude Code integration
3
+ Personal CLI toolkit with autonomous task runner and developer utilities.
12
4
 
13
5
  ## Installation
14
6
 
@@ -26,93 +18,71 @@ claude plugin update tt@towles-tool
26
18
  git clone https://github.com/ChrisTowles/towles-tool.git
27
19
  cd towles-tool
28
20
  pnpm install
29
- pnpm start # Run directly with tsx
21
+ pnpm start
30
22
  ```
31
23
 
32
24
  ## CLI Commands
33
25
 
34
26
  ### Ralph (autonomous runner)
35
27
 
36
- | Command | Description |
37
- | --------------------------- | --------------------------------------------- |
38
- | `tt ralph plan add <desc>` | Add task to plan |
39
- | `tt ralph plan list` | View tasks |
40
- | `tt ralph plan done <id>` | Mark task complete |
41
- | `tt ralph plan remove <id>` | Remove task |
42
- | `tt ralph run` | Run autonomous loop (auto-commits by default) |
43
- | `tt ralph show` | Show plan with mermaid graph |
28
+ | Command | Description |
29
+ | ----------------------------- | ---------------------- |
30
+ | `tt ralph plan add "path.md"` | Add plan from file |
31
+ | `tt ralph plan list` | View plans |
32
+ | `tt ralph plan done <id>` | Mark complete |
33
+ | `tt ralph plan remove <id>` | Remove plan |
34
+ | `tt ralph show` | Show plan with mermaid |
35
+ | `tt ralph run` | Run (auto-commits) |
36
+ | `tt ralph run --planId 5` | Run specific plan |
44
37
 
45
38
  ### Observability
46
39
 
47
- | Command | Description |
48
- | ------------------------- | ------------------------------------- |
49
- | `tt graph` | Generate HTML treemap of all sessions |
50
- | `tt graph --session <id>` | Single session treemap |
51
- | `tt graph --open` | Auto-open in browser |
52
-
53
- Treemap colors indicate input/output token ratio: green <2:1, yellow 2-5:1, red >5:1.
40
+ | Command | Description |
41
+ | ------------------------- | ------------------------ |
42
+ | `tt graph` | Token Usage (auto-opens) |
43
+ | `tt graph --session <id>` | Single session |
44
+ | `tt graph --days 14` | Filter to last N days |
54
45
 
55
46
  ### Git
56
47
 
57
- | Command | Alias | Description |
58
- | -------------------- | ------- | ------------------------------- |
59
- | `tt gh branch` | | Create branch from GitHub issue |
60
- | `tt gh pr` | `tt pr` | Create pull request |
61
- | `tt gh branch-clean` | | Delete merged branches |
48
+ | Command | Description |
49
+ | -------------------- | ------------------------------- |
50
+ | `tt gh branch` | Create branch from GitHub issue |
51
+ | `tt gh pr` | Create pull request |
52
+ | `tt gh branch-clean` | Delete merged branches |
62
53
 
63
54
  ### Journaling
64
55
 
65
- | Command | Alias | Description |
66
- | ------------------------ | ---------- | -------------------------------- |
67
- | `tt journal daily-notes` | `tt today` | Weekly files with daily sections |
68
- | `tt journal meeting` | `tt m` | Meeting notes |
69
- | `tt journal note` | `tt n` | General notes |
56
+ | Command | Alias | Description |
57
+ | ------------------------ | ---------- | ------------- |
58
+ | `tt journal daily-notes` | `tt today` | Weekly/daily |
59
+ | `tt journal meeting` | `tt m` | Meeting notes |
60
+ | `tt journal note` | `tt n` | General notes |
70
61
 
71
62
  ### Utilities
72
63
 
73
- | Command | Alias | Description |
74
- | ------------ | -------- | ------------------------------ |
75
- | `tt config` | `tt cfg` | Show configuration |
76
- | `tt doctor` | | Check dependencies |
77
- | `tt install` | | Configure Claude Code settings |
78
-
79
- ## Claude Code Plugin Skills
80
-
81
- Available via `/tt:<command>`:
82
-
83
- | Command | Description |
84
- | ------------- | --------------------------------------------- |
85
- | `/tt:commit` | AI-powered conventional commit messages |
86
- | `/tt:plan` | Interview user and create implementation plan |
87
- | `/tt:improve` | Explore codebase and suggest improvements |
88
- | `/tt:refine` | Fix grammar/spelling in files |
89
-
90
- ## Development
64
+ | Command | Description |
65
+ | ------------ | ------------------------------ |
66
+ | `tt config` | Show configuration |
67
+ | `tt doctor` | Check dependencies |
68
+ | `tt install` | Configure Claude Code settings |
91
69
 
92
- ```bash
93
- pnpm start # Run CLI with tsx
94
- pnpm test # Run tests
95
- pnpm lint # Run oxlint
96
- pnpm format # Format with oxfmt
97
- pnpm typecheck # Type check
98
- ```
99
-
100
- ### Releasing
101
-
102
- ```bash
103
- gh workflow run release.yml -f bump_type=patch # or minor/major
104
- gh run watch
105
- ```
70
+ ## Claude Code Skills
106
71
 
107
- ## Resources
72
+ | Skill | Description |
73
+ | ------------------------ | ----------------------------- |
74
+ | `/tt:plan` | Create implementation plan |
75
+ | `/tt:improve` | Suggest codebase improvements |
76
+ | `/tt:refactor-claude-md` | Fix grammar/spelling |
77
+ | `/tt:refine` | Fix grammar/spelling |
108
78
 
109
- ### Claude Code Plugin Development
79
+ ## Guidelines
110
80
 
111
- - [Claude Code Plugins Announcement](https://www.anthropic.com/news/claude-code-plugins)
112
- - [Official Claude Code Plugins](https://github.com/anthropics/claude-code/tree/main/plugins)
113
- - [Skills Guide](https://docs.claude.com/en/api/skills-guide)
114
- - [Best Practices](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/best-practices)
81
+ - [Architecture](docs/architecture.md) - CLI structure, plugin system, tech stack
82
+ - [Claude Code Planning and Running Usage](docs/ralph-tools-for-claude-code.md) - "Claude Code" autonomous runner
83
+ - [CICD via GitHub Actions](docs/github-actions.md) - Automated release workflow
84
+ - [Testing](docs/testings.md) - Info about Tests
115
85
 
116
86
  ## License
117
87
 
118
- [MIT](./LICENSE) License © [Chris Towles](https://github.com/ChrisTowles)
88
+ [MIT](./LICENSE) © [Chris Towles](https://github.com/ChrisTowles)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@towles/tool",
3
- "version": "0.0.52",
3
+ "version": "0.0.54",
4
4
  "description": "CLI tool with autonomous task runner (ralph), observability, and quality-of-life commands for daily development.",
5
5
  "keywords": [
6
6
  "autonomic",
@@ -79,15 +79,16 @@
79
79
  "vitest": "^3.1.3"
80
80
  },
81
81
  "simple-git-hooks": {
82
- "pre-commit": "pnpm lint-staged && pnpm typecheck"
82
+ "pre-commit": "pnpm lint-staged && pnpm format && pnpm typecheck"
83
83
  },
84
84
  "lint-staged": {
85
85
  "package.json": "oxfmt --write",
86
86
  "*.{ts,tsx,mts,cts,js,cjs,mjs}": [
87
- "oxfmt --write",
88
87
  "oxlint --fix"
89
88
  ],
90
- "*.{json,md,yaml,yml}": "oxfmt --write"
89
+ "*.*": [
90
+ "oxfmt --write"
91
+ ]
91
92
  },
92
93
  "oclif": {
93
94
  "bin": "tt",
@@ -1,6 +1,6 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
- import { Flags } from "@oclif/core";
3
+ import { Args, Flags } from "@oclif/core";
4
4
  import consola from "consola";
5
5
  import { colors } from "consola/utils";
6
6
  import { BaseCommand } from "../../base.js";
@@ -22,17 +22,19 @@ export default class PlanAdd extends BaseCommand {
22
22
  static override examples = [
23
23
  {
24
24
  description: "Add a plan from a markdown file",
25
- command: "<%= config.bin %> <%= command.id %> --file docs/plans/2025-01-18-feature.md",
25
+ command: "<%= config.bin %> <%= command.id %> docs/plans/2025-01-18-feature.md",
26
26
  },
27
27
  ];
28
28
 
29
- static override flags = {
30
- ...BaseCommand.baseFlags,
31
- file: Flags.string({
32
- char: "f",
29
+ static override args = {
30
+ file: Args.string({
33
31
  description: "Path to plan file (markdown)",
34
32
  required: true,
35
33
  }),
34
+ };
35
+
36
+ static override flags = {
37
+ ...BaseCommand.baseFlags,
36
38
  stateFile: Flags.string({
37
39
  char: "s",
38
40
  description: `State file path (default: ${DEFAULT_STATE_FILE})`,
@@ -40,11 +42,11 @@ export default class PlanAdd extends BaseCommand {
40
42
  };
41
43
 
42
44
  async run(): Promise<void> {
43
- const { flags } = await this.parse(PlanAdd);
45
+ const { args, flags } = await this.parse(PlanAdd);
44
46
  const ralphSettings = this.settings.settings.ralphSettings;
45
47
  const stateFile = resolveRalphPath(flags.stateFile, "stateFile", ralphSettings);
46
48
 
47
- const planFilePath = resolve(flags.file);
49
+ const planFilePath = resolve(args.file);
48
50
 
49
51
  if (!existsSync(planFilePath)) {
50
52
  this.error(`Plan file not found: ${planFilePath}`);
@@ -22,6 +22,7 @@ import {
22
22
  DEFAULT_STATE_FILE,
23
23
  DEFAULT_HISTORY_FILE,
24
24
  DEFAULT_COMPLETION_MARKER,
25
+ DEFAULT_TASK_DONE_MARKER,
25
26
  CLAUDE_DEFAULT_ARGS,
26
27
  } from "../../lib/ralph/index.js";
27
28
 
@@ -45,6 +46,7 @@ describe("ralph-loop", () => {
45
46
  expect(DEFAULT_STATE_FILE).toBe("./.claude/.ralph/ralph-state.local.json");
46
47
  expect(DEFAULT_HISTORY_FILE).toBe("./.claude/.ralph/ralph-history.local.log");
47
48
  expect(DEFAULT_COMPLETION_MARKER).toBe("RALPH_DONE");
49
+ expect(DEFAULT_TASK_DONE_MARKER).toBe("TASK_DONE");
48
50
  expect(CLAUDE_DEFAULT_ARGS).toEqual([
49
51
  "--print",
50
52
  "--verbose",
@@ -99,43 +101,81 @@ describe("ralph-loop", () => {
99
101
  status: "ready",
100
102
  addedAt: new Date().toISOString(),
101
103
  };
102
- const testPlanContent = "First plan content";
103
104
 
104
105
  it("should include completion marker", () => {
105
106
  const prompt = buildIterationPrompt({
106
107
  completionMarker: "RALPH_DONE",
108
+ taskDoneMarker: "TASK_DONE",
107
109
  plan: testPlan,
108
- planContent: testPlanContent,
109
110
  });
110
111
  expect(prompt).toContain("RALPH_DONE");
111
112
  });
112
113
 
113
- it("should include plan content", () => {
114
+ it("should include plan file path for reading", () => {
114
115
  const prompt = buildIterationPrompt({
115
116
  completionMarker: "RALPH_DONE",
117
+ taskDoneMarker: "TASK_DONE",
116
118
  plan: testPlan,
117
- planContent: testPlanContent,
118
119
  });
119
- expect(prompt).toContain("First plan content");
120
+ expect(prompt).toContain("/tmp/first-plan.md");
121
+ expect(prompt).toContain("Read Plan:");
120
122
  });
121
123
 
122
- it("should include mark done command with plan id", () => {
124
+ it("should include instruction to update plan file", () => {
123
125
  const prompt = buildIterationPrompt({
124
126
  completionMarker: "RALPH_DONE",
127
+ taskDoneMarker: "TASK_DONE",
125
128
  plan: testPlan,
126
- planContent: testPlanContent,
127
129
  });
128
- expect(prompt).toContain("tt ralph plan done 1");
130
+ expect(prompt).toContain("Update the plan");
131
+ expect(prompt).toContain("/tmp/first-plan.md");
129
132
  });
130
133
 
131
134
  it("should include custom completion marker", () => {
132
135
  const prompt = buildIterationPrompt({
133
136
  completionMarker: "CUSTOM_MARKER",
137
+ taskDoneMarker: "TASK_DONE",
134
138
  plan: testPlan,
135
- planContent: testPlanContent,
136
139
  });
137
140
  expect(prompt).toContain("CUSTOM_MARKER");
138
141
  });
142
+
143
+ it("should include TASK_DONE for tasks remaining", () => {
144
+ const prompt = buildIterationPrompt({
145
+ completionMarker: "RALPH_DONE",
146
+ taskDoneMarker: "TASK_DONE",
147
+ plan: testPlan,
148
+ });
149
+ expect(prompt).toContain("TASK_DONE");
150
+ expect(prompt).toContain("tasks remain");
151
+ });
152
+
153
+ it("should include RALPH_DONE for plan complete", () => {
154
+ const prompt = buildIterationPrompt({
155
+ completionMarker: "RALPH_DONE",
156
+ taskDoneMarker: "TASK_DONE",
157
+ plan: testPlan,
158
+ });
159
+ expect(prompt).toContain("RALPH_DONE");
160
+ expect(prompt).toContain("plan is complete");
161
+ });
162
+
163
+ it("should skip commit step when skipCommit is true", () => {
164
+ const promptWithCommit = buildIterationPrompt({
165
+ completionMarker: "RALPH_DONE",
166
+ taskDoneMarker: "TASK_DONE",
167
+ plan: testPlan,
168
+ skipCommit: false,
169
+ });
170
+ const promptWithoutCommit = buildIterationPrompt({
171
+ completionMarker: "RALPH_DONE",
172
+ taskDoneMarker: "TASK_DONE",
173
+ plan: testPlan,
174
+ skipCommit: true,
175
+ });
176
+ expect(promptWithCommit).toContain("git commit");
177
+ expect(promptWithoutCommit).not.toContain("git commit");
178
+ });
139
179
  });
140
180
 
141
181
  describe("extractOutputSummary", () => {
@@ -200,6 +240,18 @@ describe("ralph-loop", () => {
200
240
  it("should be case-sensitive", () => {
201
241
  expect(detectCompletionMarker("ralph_done", "RALPH_DONE")).toBe(false);
202
242
  });
243
+
244
+ it("should detect TASK_DONE marker", () => {
245
+ expect(detectCompletionMarker("finished <promise>TASK_DONE</promise>", "TASK_DONE")).toBe(
246
+ true,
247
+ );
248
+ });
249
+
250
+ it("should distinguish TASK_DONE from RALPH_DONE", () => {
251
+ const output = "<promise>TASK_DONE</promise>";
252
+ expect(detectCompletionMarker(output, "TASK_DONE")).toBe(true);
253
+ expect(detectCompletionMarker(output, "RALPH_DONE")).toBe(false);
254
+ });
203
255
  });
204
256
 
205
257
  describe("state transitions", () => {
@@ -275,6 +327,26 @@ describe("ralph-loop", () => {
275
327
  expect(parsed2.outputSummary).toBe("second");
276
328
  expect(parsed2.markerFound).toBe(true);
277
329
  });
330
+
331
+ it("should include taskMarkerFound field", () => {
332
+ const history: IterationHistory = {
333
+ iteration: 1,
334
+ startedAt: "2026-01-19T10:00:00Z",
335
+ completedAt: "2026-01-19T10:01:00Z",
336
+ durationMs: 60000,
337
+ durationHuman: "1m 0s",
338
+ outputSummary: "test output",
339
+ markerFound: false,
340
+ taskMarkerFound: true,
341
+ };
342
+
343
+ appendHistory(history, testHistoryFile);
344
+
345
+ const content = require("node:fs").readFileSync(testHistoryFile, "utf-8");
346
+ const parsed = JSON.parse(content.trim());
347
+ expect(parsed.taskMarkerFound).toBe(true);
348
+ expect(parsed.markerFound).toBe(false);
349
+ });
278
350
  });
279
351
 
280
352
  describe("addPlanToState", () => {
@@ -8,13 +8,13 @@ import {
8
8
  DEFAULT_LOG_FILE,
9
9
  DEFAULT_MAX_ITERATIONS,
10
10
  DEFAULT_COMPLETION_MARKER,
11
+ DEFAULT_TASK_DONE_MARKER,
11
12
  CLAUDE_DEFAULT_ARGS,
12
13
  loadState,
13
14
  saveState,
14
15
  appendHistory,
15
16
  resolveRalphPath,
16
17
  getRalphPaths,
17
- readPlanContent,
18
18
  } from "../../lib/ralph/state.js";
19
19
  import {
20
20
  buildIterationPrompt,
@@ -94,6 +94,10 @@ export default class Run extends BaseCommand {
94
94
  description: "Completion marker",
95
95
  default: DEFAULT_COMPLETION_MARKER,
96
96
  }),
97
+ taskDoneMarker: Flags.string({
98
+ description: "Task done marker",
99
+ default: DEFAULT_TASK_DONE_MARKER,
100
+ }),
97
101
  };
98
102
 
99
103
  async run(): Promise<void> {
@@ -147,6 +151,7 @@ export default class Run extends BaseCommand {
147
151
  consola.log(` State file: ${stateFile}`);
148
152
  consola.log(` Log file: ${logFile}`);
149
153
  consola.log(` Completion marker: ${flags.completionMarker}`);
154
+ consola.log(` Task done marker: ${flags.taskDoneMarker}`);
150
155
  consola.log(` Auto-commit: ${flags.autoCommit}`);
151
156
  consola.log(` Claude args: ${[...CLAUDE_DEFAULT_ARGS, ...extraClaudeArgs].join(" ")}`);
152
157
  consola.log(` Remaining plans: ${remainingPlans.length}`);
@@ -154,17 +159,11 @@ export default class Run extends BaseCommand {
154
159
  consola.log(colors.cyan("\nCurrent plan:"));
155
160
  consola.log(` #${currentPlan.id}: ${currentPlan.planFilePath}`);
156
161
 
157
- // Read plan content
158
- const planContent = readPlanContent(currentPlan, state, stateFile);
159
- if (!planContent) {
160
- this.error(`Cannot read plan file: ${currentPlan.planFilePath}`);
161
- }
162
-
163
162
  // Show prompt preview
164
163
  const prompt = buildIterationPrompt({
165
164
  completionMarker: flags.completionMarker,
165
+ taskDoneMarker: flags.taskDoneMarker,
166
166
  plan: currentPlan,
167
- planContent,
168
167
  skipCommit: !flags.autoCommit,
169
168
  });
170
169
  consola.log(colors.dim("─".repeat(60)));
@@ -248,18 +247,10 @@ export default class Run extends BaseCommand {
248
247
  break;
249
248
  }
250
249
 
251
- // Read plan content
252
- const planContent = readPlanContent(plan, state, stateFile);
253
- if (!planContent) {
254
- consola.log(colors.yellow(`⚠ Skipping plan #${plan.id}: cannot read file`));
255
- logStream.write(`⚠ Skipping plan #${plan.id}: cannot read file\n`);
256
- continue;
257
- }
258
-
259
250
  const prompt = buildIterationPrompt({
260
251
  completionMarker: flags.completionMarker,
252
+ taskDoneMarker: flags.taskDoneMarker,
261
253
  plan: plan,
262
- planContent,
263
254
  skipCommit: !flags.autoCommit,
264
255
  });
265
256
 
@@ -288,7 +279,8 @@ export default class Run extends BaseCommand {
288
279
  }
289
280
 
290
281
  const iterationEnd = new Date().toISOString();
291
- const markerFound = detectCompletionMarker(iterResult.output, flags.completionMarker);
282
+ const taskMarkerFound = detectCompletionMarker(iterResult.output, flags.taskDoneMarker);
283
+ const planMarkerFound = detectCompletionMarker(iterResult.output, flags.completionMarker);
292
284
 
293
285
  // Calculate duration
294
286
  const startTime = new Date(iterationStart).getTime();
@@ -305,7 +297,8 @@ export default class Run extends BaseCommand {
305
297
  durationMs,
306
298
  durationHuman,
307
299
  outputSummary: extractOutputSummary(iterResult.output),
308
- markerFound,
300
+ markerFound: planMarkerFound,
301
+ taskMarkerFound,
309
302
  contextUsedPercent: iterResult.contextUsedPercent,
310
303
  },
311
304
  ralphPaths.historyFile,
@@ -314,29 +307,35 @@ export default class Run extends BaseCommand {
314
307
  // Save state
315
308
  saveState(state, stateFile);
316
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
+
317
316
  // Log summary
318
317
  const contextInfo =
319
318
  iterResult.contextUsedPercent !== undefined
320
319
  ? ` | Context: ${iterResult.contextUsedPercent}%`
321
320
  : "";
322
321
  logStream.write(
323
- `\n━━━ Iteration ${iteration} Summary ━━━\nDuration: ${durationHuman}${contextInfo}\nMarker found: ${markerFound ? "yes" : "no"}\n`,
322
+ `\n━━━ Iteration ${iteration} Summary ━━━\nDuration: ${durationHuman}${contextInfo}\nTask marker: ${taskMarkerFound ? "yes" : "no"}\nPlan marker: ${planMarkerFound ? "yes" : "no"}\n`,
324
323
  );
325
324
  consola.log(
326
325
  colors.dim(
327
- `Duration: ${durationHuman}${contextInfo} | Marker: ${markerFound ? colors.green("yes") : colors.yellow("no")}`,
326
+ `Duration: ${durationHuman}${contextInfo} | Task: ${taskMarkerFound ? colors.green("yes") : colors.yellow("no")} | Plan: ${planMarkerFound ? colors.green("yes") : colors.yellow("no")}`,
328
327
  ),
329
328
  );
330
329
 
331
- // Check completion
332
- if (markerFound) {
330
+ // Check completion (only when ALL plans done marker found)
331
+ if (planMarkerFound) {
333
332
  completed = true;
334
333
  state.status = "completed";
335
334
  saveState(state, stateFile);
336
335
  consola.log(
337
- colors.bold(colors.green(`\n✅ Plan completed after ${iteration} iteration(s)`)),
336
+ colors.bold(colors.green(`\n✅ All plans completed after ${iteration} iteration(s)`)),
338
337
  );
339
- logStream.write(`\n✅ Plan completed after ${iteration} iteration(s)\n`);
338
+ logStream.write(`\n✅ All plans completed after ${iteration} iteration(s)\n`);
340
339
  }
341
340
  }
342
341
 
@@ -204,32 +204,28 @@ export function extractOutputSummary(output: string, maxLength: number = 2000):
204
204
 
205
205
  export interface BuildPromptOptions {
206
206
  completionMarker: string;
207
+ taskDoneMarker: string;
207
208
  plan: RalphPlan;
208
- planContent: string;
209
209
  skipCommit?: boolean;
210
210
  }
211
211
 
212
212
  export function buildIterationPrompt({
213
213
  completionMarker,
214
+ taskDoneMarker,
214
215
  plan,
215
- planContent,
216
216
  skipCommit = false,
217
217
  }: BuildPromptOptions): string {
218
218
  let step = 1;
219
219
 
220
220
  const prompt = `
221
- <plan>
222
- ${planContent}
223
- </plan>
224
-
225
- <instructions>
226
- ${step++}. Work on the plan above.
227
- ${step++}. Run type checks and tests.
228
- ${step++}. Mark done: \`tt ralph plan done ${plan.id}\`
221
+ <instructions>
222
+ ${step++}. Read Plan: \`${plan.planFilePath}\`
223
+ ${step++}. Choose next best task to work on.
224
+ ${step++}. Complete that task.
225
+ ${step++}. Update the plan \`${plan.planFilePath}\` to mark that task Done with any notes.
229
226
  ${skipCommit ? "" : `${step++}. Make a git commit.`}
227
+ ${step++}. If any tasks remain return <promise>${taskDoneMarker}</promise> else if plan is complete return <promise>${completionMarker}</promise>.
230
228
 
231
- **Before ending:** Run \`tt ralph plan list\` to check remaining plans.
232
- **ONLY if ALL PLANS are done** then Output: <promise>${completionMarker}</promise>
233
229
  </instructions>
234
230
  `;
235
231
  return prompt.trim();
@@ -12,6 +12,7 @@ import { RalphSettingsSchema } from "../../config/settings.js";
12
12
  export const DEFAULT_MAX_ITERATIONS = 10;
13
13
  export const DEFAULT_STATE_DIR = "./.claude/.ralph";
14
14
  export const DEFAULT_COMPLETION_MARKER = "RALPH_DONE";
15
+ export const DEFAULT_TASK_DONE_MARKER = "TASK_DONE";
15
16
 
16
17
  // File names within stateDir
17
18
  const STATE_FILE_NAME = "ralph-state.local.json";
@@ -93,6 +94,7 @@ export interface IterationHistory {
93
94
  durationHuman: string;
94
95
  outputSummary: string;
95
96
  markerFound: boolean;
97
+ taskMarkerFound?: boolean;
96
98
  contextUsedPercent?: number;
97
99
  }
98
100