taskplane 0.3.0 → 0.4.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 +14 -5
- package/bin/gitignore-patterns.mjs +78 -0
- package/bin/taskplane.mjs +1356 -34
- package/dashboard/server.cjs +13 -1
- package/extensions/task-runner.ts +212 -57
- package/extensions/taskplane/config-loader.ts +860 -0
- package/extensions/taskplane/config-schema.ts +468 -0
- package/extensions/taskplane/config.ts +37 -93
- package/extensions/taskplane/engine.ts +2 -0
- package/extensions/taskplane/extension.ts +19 -0
- package/extensions/taskplane/merge.ts +12 -4
- package/extensions/taskplane/resume.ts +23 -14
- package/extensions/taskplane/settings-tui.ts +1387 -0
- package/extensions/taskplane/types.ts +81 -1
- package/extensions/taskplane/workspace.ts +204 -10
- package/package.json +1 -1
- package/skills/create-taskplane-task/SKILL.md +7 -5
- package/skills/create-taskplane-task/references/prompt-template.md +4 -3
- package/templates/agents/local/task-merger.md +27 -0
- package/templates/agents/local/task-reviewer.md +29 -0
- package/templates/agents/local/task-worker.md +30 -0
- package/templates/agents/task-worker.md +45 -31
- package/templates/config/task-orchestrator.yaml +3 -0
|
@@ -36,6 +36,8 @@ export interface OrchestratorConfig {
|
|
|
36
36
|
tools: string;
|
|
37
37
|
verify: string[];
|
|
38
38
|
order: "fewest-files-first" | "sequential";
|
|
39
|
+
/** Merge agent timeout in minutes. Default: 10. Increase for large batches. */
|
|
40
|
+
timeout_minutes: number;
|
|
39
41
|
};
|
|
40
42
|
failure: {
|
|
41
43
|
on_task_failure: "skip-dependents" | "stop-wave" | "stop-all";
|
|
@@ -168,6 +170,7 @@ export const DEFAULT_ORCHESTRATOR_CONFIG: OrchestratorConfig = {
|
|
|
168
170
|
tools: "read,write,edit,bash,grep,find,ls",
|
|
169
171
|
verify: [],
|
|
170
172
|
order: "fewest-files-first",
|
|
173
|
+
timeout_minutes: 10,
|
|
171
174
|
},
|
|
172
175
|
failure: {
|
|
173
176
|
on_task_failure: "skip-dependents",
|
|
@@ -1049,7 +1052,8 @@ export class MergeError extends Error {
|
|
|
1049
1052
|
* Merge agents typically complete in 10-60 seconds. A 5-minute timeout
|
|
1050
1053
|
* is generous and covers verification (go build) on large codebases.
|
|
1051
1054
|
*/
|
|
1052
|
-
|
|
1055
|
+
/** Default merge agent timeout. Use config.merge.timeout_minutes to override. */
|
|
1056
|
+
export const MERGE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
1053
1057
|
|
|
1054
1058
|
/**
|
|
1055
1059
|
* Polling interval for merge result file (ms).
|
|
@@ -1796,6 +1800,14 @@ export interface ExecutionContext {
|
|
|
1796
1800
|
taskRunnerConfig: TaskRunnerConfig;
|
|
1797
1801
|
/** Loaded orchestrator configuration */
|
|
1798
1802
|
orchestratorConfig: OrchestratorConfig;
|
|
1803
|
+
/**
|
|
1804
|
+
* Resolved pointer for config/agent paths (null in repo mode).
|
|
1805
|
+
*
|
|
1806
|
+
* When present, `pointer.configRoot` and `pointer.agentRoot` point to
|
|
1807
|
+
* the config repo's config directory. State/sidecar paths are NOT
|
|
1808
|
+
* affected — they always live at `<workspaceRoot>/.pi/`.
|
|
1809
|
+
*/
|
|
1810
|
+
pointer: PointerResolution | null;
|
|
1799
1811
|
}
|
|
1800
1812
|
|
|
1801
1813
|
|
|
@@ -1861,6 +1873,73 @@ export class WorkspaceConfigError extends Error {
|
|
|
1861
1873
|
}
|
|
1862
1874
|
|
|
1863
1875
|
|
|
1876
|
+
// ── Pointer Resolution Types ─────────────────────────────────────────
|
|
1877
|
+
|
|
1878
|
+
/**
|
|
1879
|
+
* Canonical filename for the workspace pointer file.
|
|
1880
|
+
* Located at `<workspace-root>/.pi/taskplane-pointer.json`.
|
|
1881
|
+
*
|
|
1882
|
+
* Created by `taskplane init` in workspace mode. Points to the config
|
|
1883
|
+
* repo and config path within it. Not committed to git — each user
|
|
1884
|
+
* creates it during onboarding.
|
|
1885
|
+
*/
|
|
1886
|
+
export const POINTER_FILENAME = "taskplane-pointer.json";
|
|
1887
|
+
|
|
1888
|
+
/**
|
|
1889
|
+
* Resolve the absolute path to the pointer file.
|
|
1890
|
+
* @param workspaceRoot - Absolute path to the workspace root
|
|
1891
|
+
*/
|
|
1892
|
+
export function pointerFilePath(workspaceRoot: string): string {
|
|
1893
|
+
return join(workspaceRoot, ".pi", POINTER_FILENAME);
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
/**
|
|
1897
|
+
* Result of resolving the workspace pointer file.
|
|
1898
|
+
*
|
|
1899
|
+
* This is the primary contract for downstream consumers (task-runner,
|
|
1900
|
+
* orchestrator, merge agent, dashboard). All pointer failures are
|
|
1901
|
+
* non-fatal: when the pointer cannot be resolved, `used` is false and
|
|
1902
|
+
* `configRoot`/`agentRoot` fall back to workspace-root paths.
|
|
1903
|
+
*
|
|
1904
|
+
* State/sidecar paths are NOT affected by the pointer — they always
|
|
1905
|
+
* live at `<workspace-root>/.pi/` regardless of pointer resolution.
|
|
1906
|
+
*
|
|
1907
|
+
* In repo mode, `resolvePointer()` returns null (pointer is ignored
|
|
1908
|
+
* entirely, even if a file happens to exist).
|
|
1909
|
+
*/
|
|
1910
|
+
export interface PointerResolution {
|
|
1911
|
+
/**
|
|
1912
|
+
* Whether the pointer was successfully resolved.
|
|
1913
|
+
* - true: pointer file was found, parsed, and config_repo resolved
|
|
1914
|
+
* to a known repo in WorkspaceConfig.repos.
|
|
1915
|
+
* - false: pointer was missing, malformed, or referenced an unknown
|
|
1916
|
+
* repo. Fallback paths are used instead.
|
|
1917
|
+
*/
|
|
1918
|
+
used: boolean;
|
|
1919
|
+
|
|
1920
|
+
/**
|
|
1921
|
+
* Resolved config root directory.
|
|
1922
|
+
* - When used=true: `<config-repo-path>/<config_path>/`
|
|
1923
|
+
* - When used=false: `<workspace-root>/.pi/` (existing fallback)
|
|
1924
|
+
*/
|
|
1925
|
+
configRoot: string;
|
|
1926
|
+
|
|
1927
|
+
/**
|
|
1928
|
+
* Resolved agent overrides directory.
|
|
1929
|
+
* - When used=true: `<config-repo-path>/<config_path>/agents/`
|
|
1930
|
+
* - When used=false: `<workspace-root>/.pi/agents/` (existing fallback)
|
|
1931
|
+
*/
|
|
1932
|
+
agentRoot: string;
|
|
1933
|
+
|
|
1934
|
+
/**
|
|
1935
|
+
* Warning message when pointer resolution fell back.
|
|
1936
|
+
* - undefined when used=true (no warning)
|
|
1937
|
+
* - Human-readable reason string when used=false
|
|
1938
|
+
*/
|
|
1939
|
+
warning?: string;
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
|
|
1864
1943
|
// ── Workspace Defaults ───────────────────────────────────────────────
|
|
1865
1944
|
|
|
1866
1945
|
/**
|
|
@@ -1900,6 +1979,7 @@ export function createRepoModeContext(
|
|
|
1900
1979
|
workspaceConfig: null,
|
|
1901
1980
|
taskRunnerConfig,
|
|
1902
1981
|
orchestratorConfig,
|
|
1982
|
+
pointer: null,
|
|
1903
1983
|
};
|
|
1904
1984
|
}
|
|
1905
1985
|
|
|
@@ -37,16 +37,18 @@
|
|
|
37
37
|
* @module orch/workspace
|
|
38
38
|
*/
|
|
39
39
|
import { readFileSync, existsSync, realpathSync } from "fs";
|
|
40
|
-
import { resolve } from "path";
|
|
40
|
+
import { resolve, relative, isAbsolute } from "path";
|
|
41
41
|
import { parse as yamlParse } from "yaml";
|
|
42
42
|
|
|
43
43
|
import { runGit } from "./git.ts";
|
|
44
44
|
import {
|
|
45
45
|
WorkspaceConfigError,
|
|
46
46
|
workspaceConfigPath,
|
|
47
|
+
pointerFilePath,
|
|
47
48
|
type WorkspaceConfig,
|
|
48
49
|
type WorkspaceRepoConfig,
|
|
49
50
|
type WorkspaceRoutingConfig,
|
|
51
|
+
type PointerResolution,
|
|
50
52
|
} from "./types.ts";
|
|
51
53
|
|
|
52
54
|
|
|
@@ -94,6 +96,185 @@ function resolveAbsolutePath(p: string, base: string): string {
|
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
|
|
99
|
+
// ── Pointer Resolution ───────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Resolve the workspace pointer file to find config and agent roots.
|
|
103
|
+
*
|
|
104
|
+
* The pointer file (`<workspace-root>/.pi/taskplane-pointer.json`) tells
|
|
105
|
+
* Taskplane where to find project config and agent overrides in workspace
|
|
106
|
+
* (polyrepo) mode. It's created by `taskplane init` and is local-only
|
|
107
|
+
* (not committed to git).
|
|
108
|
+
*
|
|
109
|
+
* **Repo mode:** Returns null. The pointer is workspace-only — in repo
|
|
110
|
+
* mode it is never read, even if a file happens to exist on disk.
|
|
111
|
+
*
|
|
112
|
+
* **Workspace mode:** Reads and validates the pointer, then resolves
|
|
113
|
+
* config and agent roots. All failures are non-fatal:
|
|
114
|
+
* - Missing pointer file → warn + fallback
|
|
115
|
+
* - Malformed JSON → warn + fallback
|
|
116
|
+
* - Missing required fields → warn + fallback
|
|
117
|
+
* - Unknown config_repo (not in WorkspaceConfig.repos) → warn + fallback
|
|
118
|
+
* - Path traversal in config_path → warn + fallback
|
|
119
|
+
*
|
|
120
|
+
* Fallback paths: `<workspace-root>/.pi/` for config,
|
|
121
|
+
* `<workspace-root>/.pi/agents/` for agents.
|
|
122
|
+
*
|
|
123
|
+
* State/sidecar paths are NOT affected by the pointer and are not
|
|
124
|
+
* included in the return value — they always live at
|
|
125
|
+
* `<workspace-root>/.pi/` regardless.
|
|
126
|
+
*
|
|
127
|
+
* @param workspaceRoot - Absolute path to the workspace root directory
|
|
128
|
+
* @param workspaceConfig - Loaded workspace config (null = repo mode → returns null)
|
|
129
|
+
* @returns PointerResolution with resolved paths, or null in repo mode
|
|
130
|
+
*/
|
|
131
|
+
export function resolvePointer(
|
|
132
|
+
workspaceRoot: string,
|
|
133
|
+
workspaceConfig: WorkspaceConfig | null,
|
|
134
|
+
): PointerResolution | null {
|
|
135
|
+
// ── Repo mode: pointer is ignored entirely ───────────────────
|
|
136
|
+
if (workspaceConfig === null) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const fallbackConfigRoot = resolve(workspaceRoot, ".pi");
|
|
141
|
+
const fallbackAgentRoot = resolve(workspaceRoot, ".pi", "agents");
|
|
142
|
+
|
|
143
|
+
const filePath = pointerFilePath(workspaceRoot);
|
|
144
|
+
|
|
145
|
+
// ── 1. File existence ────────────────────────────────────────
|
|
146
|
+
if (!existsSync(filePath)) {
|
|
147
|
+
return {
|
|
148
|
+
used: false,
|
|
149
|
+
configRoot: fallbackConfigRoot,
|
|
150
|
+
agentRoot: fallbackAgentRoot,
|
|
151
|
+
warning: `Pointer file not found: ${filePath}. Run 'taskplane init' to create it.`,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ── 2. Read file ─────────────────────────────────────────────
|
|
156
|
+
let rawContent: string;
|
|
157
|
+
try {
|
|
158
|
+
rawContent = readFileSync(filePath, "utf-8");
|
|
159
|
+
} catch (err: unknown) {
|
|
160
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
161
|
+
return {
|
|
162
|
+
used: false,
|
|
163
|
+
configRoot: fallbackConfigRoot,
|
|
164
|
+
agentRoot: fallbackAgentRoot,
|
|
165
|
+
warning: `Cannot read pointer file ${filePath}: ${msg}`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ── 3. Parse JSON ────────────────────────────────────────────
|
|
170
|
+
let parsed: unknown;
|
|
171
|
+
try {
|
|
172
|
+
parsed = JSON.parse(rawContent);
|
|
173
|
+
} catch {
|
|
174
|
+
return {
|
|
175
|
+
used: false,
|
|
176
|
+
configRoot: fallbackConfigRoot,
|
|
177
|
+
agentRoot: fallbackAgentRoot,
|
|
178
|
+
warning: `Pointer file ${filePath} contains invalid JSON.`,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ── 4. Validate shape ────────────────────────────────────────
|
|
183
|
+
if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
184
|
+
return {
|
|
185
|
+
used: false,
|
|
186
|
+
configRoot: fallbackConfigRoot,
|
|
187
|
+
agentRoot: fallbackAgentRoot,
|
|
188
|
+
warning: `Pointer file ${filePath} must be a JSON object.`,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const doc = parsed as Record<string, unknown>;
|
|
193
|
+
const configRepo = doc.config_repo;
|
|
194
|
+
const configPath = doc.config_path;
|
|
195
|
+
|
|
196
|
+
if (!configRepo || typeof configRepo !== "string" || configRepo.trim() === "") {
|
|
197
|
+
return {
|
|
198
|
+
used: false,
|
|
199
|
+
configRoot: fallbackConfigRoot,
|
|
200
|
+
agentRoot: fallbackAgentRoot,
|
|
201
|
+
warning: `Pointer file ${filePath} is missing required field 'config_repo'.`,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!configPath || typeof configPath !== "string" || configPath.trim() === "") {
|
|
206
|
+
return {
|
|
207
|
+
used: false,
|
|
208
|
+
configRoot: fallbackConfigRoot,
|
|
209
|
+
agentRoot: fallbackAgentRoot,
|
|
210
|
+
warning: `Pointer file ${filePath} is missing required field 'config_path'.`,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ── 5. Guard path traversal ──────────────────────────────────
|
|
215
|
+
const normalizedConfigPath = configPath.trim().replace(/\\/g, "/");
|
|
216
|
+
|
|
217
|
+
// Reject absolute paths (POSIX `/...` and Windows `C:/...`, `\\...`)
|
|
218
|
+
if (isAbsolute(normalizedConfigPath) || isAbsolute(configPath.trim())) {
|
|
219
|
+
return {
|
|
220
|
+
used: false,
|
|
221
|
+
configRoot: fallbackConfigRoot,
|
|
222
|
+
agentRoot: fallbackAgentRoot,
|
|
223
|
+
warning: `Pointer file ${filePath} has invalid config_path '${configPath}' (absolute paths not allowed).`,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Reject traversal sequences
|
|
228
|
+
if (
|
|
229
|
+
normalizedConfigPath.startsWith("..") ||
|
|
230
|
+
normalizedConfigPath.includes("/../") ||
|
|
231
|
+
normalizedConfigPath.endsWith("/..")
|
|
232
|
+
) {
|
|
233
|
+
return {
|
|
234
|
+
used: false,
|
|
235
|
+
configRoot: fallbackConfigRoot,
|
|
236
|
+
agentRoot: fallbackAgentRoot,
|
|
237
|
+
warning: `Pointer file ${filePath} has invalid config_path '${configPath}' (path traversal not allowed).`,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ── 6. Resolve config_repo against workspace repos map ──────
|
|
242
|
+
const repoId = configRepo.trim();
|
|
243
|
+
const repoConfig = workspaceConfig.repos.get(repoId);
|
|
244
|
+
if (!repoConfig) {
|
|
245
|
+
const available = Array.from(workspaceConfig.repos.keys()).join(", ");
|
|
246
|
+
return {
|
|
247
|
+
used: false,
|
|
248
|
+
configRoot: fallbackConfigRoot,
|
|
249
|
+
agentRoot: fallbackAgentRoot,
|
|
250
|
+
warning: `Pointer file ${filePath}: config_repo '${repoId}' not found in workspace repos. Available repos: ${available}`,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ── 7. Build resolved paths + containment check ──────────────
|
|
255
|
+
const resolvedConfigRoot = resolve(repoConfig.path, normalizedConfigPath);
|
|
256
|
+
|
|
257
|
+
// Verify the resolved path is within the repo root (defense-in-depth)
|
|
258
|
+
const rel = relative(repoConfig.path, resolvedConfigRoot);
|
|
259
|
+
if (rel.startsWith("..") || isAbsolute(rel)) {
|
|
260
|
+
return {
|
|
261
|
+
used: false,
|
|
262
|
+
configRoot: fallbackConfigRoot,
|
|
263
|
+
agentRoot: fallbackAgentRoot,
|
|
264
|
+
warning: `Pointer file ${filePath} has invalid config_path '${configPath}' (resolved path escapes config repo root).`,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const resolvedAgentRoot = resolve(resolvedConfigRoot, "agents");
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
used: true,
|
|
272
|
+
configRoot: resolvedConfigRoot,
|
|
273
|
+
agentRoot: resolvedAgentRoot,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
|
|
97
278
|
// ── Workspace Config Loading ─────────────────────────────────────────
|
|
98
279
|
|
|
99
280
|
/**
|
|
@@ -366,34 +547,47 @@ export function loadWorkspaceConfig(workspaceRoot: string): WorkspaceConfig | nu
|
|
|
366
547
|
*/
|
|
367
548
|
export function buildExecutionContext(
|
|
368
549
|
cwd: string,
|
|
369
|
-
loadOrchConfig: (root: string) => import("./types.ts").OrchestratorConfig,
|
|
370
|
-
loadTaskConfig: (root: string) => import("./types.ts").TaskRunnerConfig,
|
|
550
|
+
loadOrchConfig: (root: string, pointerConfigRoot?: string) => import("./types.ts").OrchestratorConfig,
|
|
551
|
+
loadTaskConfig: (root: string, pointerConfigRoot?: string) => import("./types.ts").TaskRunnerConfig,
|
|
371
552
|
): import("./types.ts").ExecutionContext {
|
|
372
|
-
const orchestratorConfig = loadOrchConfig(cwd);
|
|
373
|
-
const taskRunnerConfig = loadTaskConfig(cwd);
|
|
374
|
-
|
|
375
553
|
const workspaceConfig = loadWorkspaceConfig(cwd);
|
|
376
554
|
|
|
377
555
|
if (workspaceConfig === null) {
|
|
378
|
-
// Repo mode:
|
|
556
|
+
// Repo mode: pointer is ignored entirely. Config loads from cwd.
|
|
557
|
+
const orchestratorConfig = loadOrchConfig(cwd);
|
|
558
|
+
const taskRunnerConfig = loadTaskConfig(cwd);
|
|
559
|
+
|
|
379
560
|
return {
|
|
380
561
|
workspaceRoot: cwd,
|
|
381
562
|
repoRoot: cwd,
|
|
382
563
|
mode: "repo",
|
|
383
564
|
workspaceConfig: null,
|
|
384
|
-
taskRunnerConfig,
|
|
385
565
|
orchestratorConfig,
|
|
566
|
+
taskRunnerConfig,
|
|
567
|
+
pointer: null,
|
|
386
568
|
};
|
|
387
569
|
}
|
|
388
570
|
|
|
389
|
-
// Workspace mode:
|
|
571
|
+
// Workspace mode: resolve pointer once, pass configRoot to config loaders.
|
|
572
|
+
const pointer = resolvePointer(cwd, workspaceConfig);
|
|
573
|
+
|
|
574
|
+
// Log pointer warning once at startup (non-fatal).
|
|
575
|
+
if (pointer && pointer.warning) {
|
|
576
|
+
console.error(`[taskplane] pointer warning: ${pointer.warning}`);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
const pointerConfigRoot = pointer?.configRoot;
|
|
580
|
+
const orchestratorConfig = loadOrchConfig(cwd, pointerConfigRoot);
|
|
581
|
+
const taskRunnerConfig = loadTaskConfig(cwd, pointerConfigRoot);
|
|
582
|
+
|
|
390
583
|
const defaultRepo = workspaceConfig.repos.get(workspaceConfig.routing.defaultRepo)!;
|
|
391
584
|
return {
|
|
392
585
|
workspaceRoot: cwd,
|
|
393
586
|
repoRoot: defaultRepo.path,
|
|
394
587
|
mode: "workspace",
|
|
395
588
|
workspaceConfig,
|
|
396
|
-
taskRunnerConfig,
|
|
397
589
|
orchestratorConfig,
|
|
590
|
+
taskRunnerConfig,
|
|
591
|
+
pointer,
|
|
398
592
|
};
|
|
399
593
|
}
|
package/package.json
CHANGED
|
@@ -326,11 +326,13 @@ Verify every task against this before reporting the launch command:
|
|
|
326
326
|
|
|
327
327
|
## Git Commit Convention
|
|
328
328
|
|
|
329
|
-
The prompt template includes a `## Git Commit Convention` section
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
the prefix
|
|
329
|
+
The prompt template includes a `## Git Commit Convention` section. Workers
|
|
330
|
+
commit at **step boundaries** (not after every checkbox) to keep git history
|
|
331
|
+
meaningful. Hydration commits are the exception — STATUS.md expansions are
|
|
332
|
+
committed immediately to preserve the plan for crash recovery. Always include
|
|
333
|
+
the task ID prefix — without it, there's no way to trace commits back to the
|
|
334
|
+
task that produced them (`git log --grep="PM-004"` only works if the prefix
|
|
335
|
+
is there).
|
|
334
336
|
|
|
335
337
|
---
|
|
336
338
|
|
|
@@ -118,12 +118,13 @@ Copy this template when creating a new task. Replace all `[bracketed]` fields.
|
|
|
118
118
|
|
|
119
119
|
## Git Commit Convention
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
Commits happen at **step boundaries** (not after every checkbox). All commits
|
|
122
|
+
for this task MUST include the task ID for traceability:
|
|
122
123
|
|
|
123
|
-
- **
|
|
124
|
+
- **Step completion:** `feat([PREFIX-###]): complete Step N — description`
|
|
124
125
|
- **Bug fixes:** `fix([PREFIX-###]): description`
|
|
125
126
|
- **Tests:** `test([PREFIX-###]): description`
|
|
126
|
-
- **
|
|
127
|
+
- **Hydration:** `hydrate: [PREFIX-###] expand Step N checkboxes`
|
|
127
128
|
|
|
128
129
|
## Do NOT
|
|
129
130
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: task-merger
|
|
3
|
+
# tools: read,write,edit,bash,grep,find,ls
|
|
4
|
+
# model:
|
|
5
|
+
# standalone: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<!-- ═══════════════════════════════════════════════════════════════════
|
|
9
|
+
Project-Specific Merger Guidance
|
|
10
|
+
|
|
11
|
+
This file is COMPOSED with the base task-merger prompt shipped in the
|
|
12
|
+
taskplane package. Your content here is appended after the base prompt.
|
|
13
|
+
|
|
14
|
+
The base prompt (maintained by taskplane) handles:
|
|
15
|
+
- Branch merge workflow (fast-forward, 3-way, conflict resolution)
|
|
16
|
+
- Post-merge verification command execution
|
|
17
|
+
- Result file JSON format and writing conventions
|
|
18
|
+
|
|
19
|
+
Add project-specific merge rules below. Common examples:
|
|
20
|
+
- Post-merge verification commands (build, lint, test)
|
|
21
|
+
- Conflict resolution preferences
|
|
22
|
+
- Protected files that should never be auto-merged
|
|
23
|
+
|
|
24
|
+
To override frontmatter values (tools, model), uncomment and edit above.
|
|
25
|
+
To use this file as a FULLY STANDALONE prompt (ignoring the base),
|
|
26
|
+
uncomment `standalone: true` above and write the complete prompt below.
|
|
27
|
+
═══════════════════════════════════════════════════════════════════ -->
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: task-reviewer
|
|
3
|
+
# tools: read,write,bash,grep,find,ls
|
|
4
|
+
# model: openai/gpt-5.3-codex
|
|
5
|
+
# standalone: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<!-- ═══════════════════════════════════════════════════════════════════
|
|
9
|
+
Project-Specific Reviewer Guidance
|
|
10
|
+
|
|
11
|
+
This file is COMPOSED with the base task-reviewer prompt shipped in the
|
|
12
|
+
taskplane package. Your content here is appended after the base prompt.
|
|
13
|
+
|
|
14
|
+
The base prompt (maintained by taskplane) handles:
|
|
15
|
+
- Plan review and code review workflows
|
|
16
|
+
- Verdict format (APPROVE / REVISE)
|
|
17
|
+
- Review file output conventions
|
|
18
|
+
- Plan granularity guidance
|
|
19
|
+
|
|
20
|
+
Add project-specific review criteria below. Common examples:
|
|
21
|
+
- Required test coverage thresholds
|
|
22
|
+
- Security review checklist items
|
|
23
|
+
- Architecture constraints to enforce
|
|
24
|
+
- Performance requirements
|
|
25
|
+
|
|
26
|
+
To override frontmatter values (tools, model), uncomment and edit above.
|
|
27
|
+
To use this file as a FULLY STANDALONE prompt (ignoring the base),
|
|
28
|
+
uncomment `standalone: true` above and write the complete prompt below.
|
|
29
|
+
═══════════════════════════════════════════════════════════════════ -->
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: task-worker
|
|
3
|
+
# tools: read,write,edit,bash,grep,find,ls
|
|
4
|
+
# model: anthropic/claude-sonnet-4-20250514
|
|
5
|
+
# standalone: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<!-- ═══════════════════════════════════════════════════════════════════
|
|
9
|
+
Project-Specific Worker Guidance
|
|
10
|
+
|
|
11
|
+
This file is COMPOSED with the base task-worker prompt shipped in the
|
|
12
|
+
taskplane package. Your content here is appended after the base prompt.
|
|
13
|
+
|
|
14
|
+
The base prompt (maintained by taskplane) handles:
|
|
15
|
+
- STATUS.md-first workflow and checkpoint discipline
|
|
16
|
+
- Fresh-context loop behavior and iteration rules
|
|
17
|
+
- Git commit conventions and .DONE file creation
|
|
18
|
+
- Review response handling
|
|
19
|
+
|
|
20
|
+
Add project-specific rules below. Common examples:
|
|
21
|
+
- Preferred package manager (pnpm, yarn, bun)
|
|
22
|
+
- Test commands (make test, npm run test:unit)
|
|
23
|
+
- Coding standards (linting, formatting)
|
|
24
|
+
- Framework-specific patterns
|
|
25
|
+
- Environment or deployment constraints
|
|
26
|
+
|
|
27
|
+
To override frontmatter values (tools, model), uncomment and edit above.
|
|
28
|
+
To use this file as a FULLY STANDALONE prompt (ignoring the base),
|
|
29
|
+
uncomment `standalone: true` above and write the complete prompt below.
|
|
30
|
+
═══════════════════════════════════════════════════════════════════ -->
|
|
@@ -18,45 +18,59 @@ ONLY memory.
|
|
|
18
18
|
|
|
19
19
|
## Checkpoint Discipline (CRITICAL)
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
There are two distinct actions: **checking off items** and **git commits**.
|
|
22
|
+
They happen at different cadences.
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
- oldText: `- [ ] The item text`
|
|
25
|
-
- newText: `- [x] The item text`
|
|
24
|
+
### Checking off items (after EACH checkbox)
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
git add -A && git commit -m "checkpoint: <what you did>"
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
3. **Check for wrap-up signal:**
|
|
33
|
-
```bash
|
|
34
|
-
if test -f "<TASK_FOLDER>/.task-wrap-up" || test -f "<TASK_FOLDER>/.wiggum-wrap-up"; then
|
|
35
|
-
echo "WRAP_UP_SIGNAL"
|
|
36
|
-
fi
|
|
37
|
-
```
|
|
38
|
-
Primary signal file is `.task-wrap-up`; `.wiggum-wrap-up` is legacy and still supported.
|
|
39
|
-
If either signal exists, STOP immediately after this checkpoint.
|
|
40
|
-
|
|
41
|
-
### Example checkpoint sequence:
|
|
42
|
-
|
|
43
|
-
After verifying that source files exist, immediately do:
|
|
26
|
+
After completing each checkbox item, **immediately update STATUS.md**:
|
|
44
27
|
|
|
45
28
|
```
|
|
46
29
|
edit STATUS.md
|
|
47
|
-
oldText: "- [ ]
|
|
48
|
-
newText: "- [x]
|
|
30
|
+
oldText: "- [ ] The item text"
|
|
31
|
+
newText: "- [x] The item text"
|
|
49
32
|
```
|
|
50
33
|
|
|
51
|
-
Then
|
|
34
|
+
Then **check for wrap-up signal:**
|
|
52
35
|
```bash
|
|
53
|
-
|
|
36
|
+
if test -f "<TASK_FOLDER>/.task-wrap-up" || test -f "<TASK_FOLDER>/.wiggum-wrap-up"; then
|
|
37
|
+
echo "WRAP_UP_SIGNAL"
|
|
38
|
+
fi
|
|
54
39
|
```
|
|
40
|
+
Primary signal file is `.task-wrap-up`; `.wiggum-wrap-up` is legacy and still supported.
|
|
41
|
+
If either signal exists, STOP immediately after this checkpoint.
|
|
55
42
|
|
|
56
|
-
**NEVER batch updates.** Check off ONE item, commit, then do the next.
|
|
57
43
|
If you do work but don't edit STATUS.md, that work is INVISIBLE to the
|
|
58
44
|
orchestrator and you will be re-spawned to do it again.
|
|
59
45
|
|
|
46
|
+
### Git commits (after completing a STEP)
|
|
47
|
+
|
|
48
|
+
Git commits happen at **step boundaries**, not after every checkbox. When all
|
|
49
|
+
checkboxes in a step are checked off:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
git add -A && git commit -m "feat(TASK-ID): complete Step N — description"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This keeps the git history meaningful — one coherent commit per step instead of
|
|
56
|
+
dozens of micro-commits that nobody reads.
|
|
57
|
+
|
|
58
|
+
**Exceptions** — commit immediately (before step completion) in these cases:
|
|
59
|
+
- **Hydration:** After expanding STATUS.md with new checkboxes, commit before
|
|
60
|
+
implementing: `git add -A && git commit -m "hydrate: expand Step N checkboxes"`
|
|
61
|
+
- **REVISE response:** After adding reviewer revision items to STATUS.md:
|
|
62
|
+
`git add -A && git commit -m "hydrate: add R00N revision items to Step N"`
|
|
63
|
+
- **Wrap-up signal:** If stopping mid-step due to a wrap-up signal, commit
|
|
64
|
+
whatever is done so far.
|
|
65
|
+
|
|
66
|
+
### Why this approach
|
|
67
|
+
|
|
68
|
+
STATUS.md is the worker's memory, not git. Checking off items in STATUS.md
|
|
69
|
+
ensures the next worker iteration knows where to resume. Git commits preserve
|
|
70
|
+
file changes at meaningful milestones. Per-checkbox commits waste tool calls
|
|
71
|
+
on git housekeeping without adding recovery value — the files are already on
|
|
72
|
+
disk in the worktree.
|
|
73
|
+
|
|
60
74
|
## STATUS.md Hydration (MANDATORY)
|
|
61
75
|
|
|
62
76
|
STATUS.md is your ONLY memory. It needs enough structure so progress survives
|
|
@@ -85,7 +99,7 @@ Before implementing anything, assess whether the step needs expansion:
|
|
|
85
99
|
3. **If expansion is needed**, add checkboxes for **distinct outcomes** you've
|
|
86
100
|
identified — not for every individual code change. Think: "what are the 2-5
|
|
87
101
|
things that need to be true when this step is done?"
|
|
88
|
-
4. **Commit the hydrated STATUS.md immediately**
|
|
102
|
+
4. **Commit the hydrated STATUS.md immediately** (see Checkpoint Discipline exceptions):
|
|
89
103
|
```bash
|
|
90
104
|
git add -A && git commit -m "hydrate: expand Step N checkboxes"
|
|
91
105
|
```
|
|
@@ -104,7 +118,7 @@ When a reviewer returns REVISE with specific feedback items:
|
|
|
104
118
|
1. **Read the review file** in `.reviews/`
|
|
105
119
|
2. **Add revision items as new checkboxes** in the current step — group related
|
|
106
120
|
fixes into single checkboxes rather than creating one per reviewer sentence
|
|
107
|
-
3. **Commit the hydrated STATUS.md
|
|
121
|
+
3. **Commit the hydrated STATUS.md** (see Checkpoint Discipline exceptions):
|
|
108
122
|
```bash
|
|
109
123
|
git add -A && git commit -m "hydrate: add R00N revision items to Step N"
|
|
110
124
|
```
|
|
@@ -112,9 +126,9 @@ When a reviewer returns REVISE with specific feedback items:
|
|
|
112
126
|
|
|
113
127
|
### Rules
|
|
114
128
|
|
|
115
|
-
- **Hydration
|
|
116
|
-
implementing. If the iteration ends between hydration and implementation,
|
|
117
|
-
plan is preserved for the next worker.
|
|
129
|
+
- **Hydration gets an immediate commit.** Always commit STATUS.md after hydrating,
|
|
130
|
+
before implementing. If the iteration ends between hydration and implementation,
|
|
131
|
+
the plan is preserved for the next worker.
|
|
118
132
|
- **One checkbox per meaningful outcome.** "Implement the CRUD methods" is one
|
|
119
133
|
checkbox if they're straightforward. "Implement create + implement delete" is
|
|
120
134
|
two checkboxes if they involve genuinely different logic. Use judgment — the
|