taskplane 0.3.1 → 0.4.1
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 +1353 -32
- package/dashboard/server.cjs +13 -1
- package/extensions/task-runner.ts +81 -46
- 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/task-worker.md +45 -31
- package/templates/config/task-orchestrator.yaml +3 -0
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified project configuration schema for taskplane-config.json
|
|
3
|
+
*
|
|
4
|
+
* Merges all settings from task-runner.yaml and task-orchestrator.yaml
|
|
5
|
+
* into a single JSON-first configuration file with clear sections.
|
|
6
|
+
*
|
|
7
|
+
* Key naming policy:
|
|
8
|
+
* - JSON uses camelCase (e.g., `maxLanes`, `workerContextWindow`)
|
|
9
|
+
* - YAML fallback loader maps snake_case keys to camelCase equivalents
|
|
10
|
+
* - The runtime config object always uses the interfaces defined here
|
|
11
|
+
*
|
|
12
|
+
* Section map (old YAML → new JSON):
|
|
13
|
+
* task-runner.yaml:
|
|
14
|
+
* project → taskRunner.project
|
|
15
|
+
* paths → taskRunner.paths
|
|
16
|
+
* testing → taskRunner.testing
|
|
17
|
+
* standards → taskRunner.standards
|
|
18
|
+
* standards_overrides → taskRunner.standardsOverrides
|
|
19
|
+
* worker → taskRunner.worker
|
|
20
|
+
* reviewer → taskRunner.reviewer
|
|
21
|
+
* context → taskRunner.context
|
|
22
|
+
* task_areas → taskRunner.taskAreas
|
|
23
|
+
* reference_docs → taskRunner.referenceDocs
|
|
24
|
+
* never_load → taskRunner.neverLoad
|
|
25
|
+
* self_doc_targets → taskRunner.selfDocTargets
|
|
26
|
+
* protected_docs → taskRunner.protectedDocs
|
|
27
|
+
*
|
|
28
|
+
* task-orchestrator.yaml:
|
|
29
|
+
* orchestrator → orchestrator.orchestrator
|
|
30
|
+
* dependencies → orchestrator.dependencies
|
|
31
|
+
* assignment → orchestrator.assignment
|
|
32
|
+
* pre_warm → orchestrator.preWarm
|
|
33
|
+
* merge → orchestrator.merge
|
|
34
|
+
* failure → orchestrator.failure
|
|
35
|
+
* monitoring → orchestrator.monitoring
|
|
36
|
+
*
|
|
37
|
+
* @module config/schema
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
// ── Config Version ───────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Current config schema version.
|
|
44
|
+
*
|
|
45
|
+
* Semantics:
|
|
46
|
+
* - Required field in taskplane-config.json (must be present and valid)
|
|
47
|
+
* - Initial version: 1
|
|
48
|
+
* - Loader behavior for unknown future versions: reject with a clear
|
|
49
|
+
* error message telling the user to upgrade Taskplane
|
|
50
|
+
* - YAML fallback files have no version field; the loader treats them
|
|
51
|
+
* as implicitly version 1
|
|
52
|
+
*/
|
|
53
|
+
export const CONFIG_VERSION = 1;
|
|
54
|
+
|
|
55
|
+
// ── Canonical Config Path ────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Canonical filename for the unified JSON config.
|
|
59
|
+
* Resolved relative to project root: `.pi/taskplane-config.json`
|
|
60
|
+
*/
|
|
61
|
+
export const PROJECT_CONFIG_FILENAME = "taskplane-config.json";
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
// ── Task Runner Section Interfaces ───────────────────────────────────
|
|
65
|
+
|
|
66
|
+
/** Project metadata */
|
|
67
|
+
export interface ProjectMetadataConfig {
|
|
68
|
+
/** Project display name used in prompts/status UI context */
|
|
69
|
+
name: string;
|
|
70
|
+
/** Short project description for agent context */
|
|
71
|
+
description: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Path metadata for the project */
|
|
75
|
+
export interface PathsConfig {
|
|
76
|
+
/** Logical tasks root path metadata */
|
|
77
|
+
tasks: string;
|
|
78
|
+
/** Path to architecture document used in context references */
|
|
79
|
+
architecture?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Verification commands available to agents/reviewers */
|
|
83
|
+
export interface TestingConfig {
|
|
84
|
+
/** Named commands (e.g., { test: "npm test", build: "npm run build" }) */
|
|
85
|
+
commands: Record<string, string>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Coding standards for agent context */
|
|
89
|
+
export interface StandardsConfig {
|
|
90
|
+
/** Docs to treat as coding/review standards references */
|
|
91
|
+
docs: string[];
|
|
92
|
+
/** Plain-language rules injected into agent context */
|
|
93
|
+
rules: string[];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Per-area standards override */
|
|
97
|
+
export interface StandardsOverride {
|
|
98
|
+
/** Override docs for this area */
|
|
99
|
+
docs?: string[];
|
|
100
|
+
/** Override rules for this area */
|
|
101
|
+
rules?: string[];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Worker agent configuration */
|
|
105
|
+
export interface WorkerConfig {
|
|
106
|
+
/** Worker model. Empty string = inherit from active pi session model */
|
|
107
|
+
model: string;
|
|
108
|
+
/** Tool allowlist passed to worker agent invocations */
|
|
109
|
+
tools: string;
|
|
110
|
+
/** Thinking mode setting passed to worker agent */
|
|
111
|
+
thinking: string;
|
|
112
|
+
/** Optional spawn mode override for task-runner */
|
|
113
|
+
spawnMode?: "subprocess" | "tmux";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Reviewer agent configuration */
|
|
117
|
+
export interface ReviewerConfig {
|
|
118
|
+
/** Reviewer model (empty = inherit session model) */
|
|
119
|
+
model: string;
|
|
120
|
+
/** Tool allowlist for reviewer agent */
|
|
121
|
+
tools: string;
|
|
122
|
+
/** Thinking mode for reviewer */
|
|
123
|
+
thinking: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Context/resource limits for task execution */
|
|
127
|
+
export interface ContextConfig {
|
|
128
|
+
/** Context window size used for worker context pressure tracking */
|
|
129
|
+
workerContextWindow: number;
|
|
130
|
+
/** Warn threshold for context utilization (percent) */
|
|
131
|
+
warnPercent: number;
|
|
132
|
+
/** Hard-stop threshold for context utilization (percent) */
|
|
133
|
+
killPercent: number;
|
|
134
|
+
/** Max worker iterations per step before failure */
|
|
135
|
+
maxWorkerIterations: number;
|
|
136
|
+
/** Max revise loops per review stage */
|
|
137
|
+
maxReviewCycles: number;
|
|
138
|
+
/** Max no-progress iterations before marking failure */
|
|
139
|
+
noProgressLimit: number;
|
|
140
|
+
/** Optional per-worker wall-clock cap (minutes, used in tmux/orchestrated flows) */
|
|
141
|
+
maxWorkerMinutes?: number;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Task area definition */
|
|
145
|
+
export interface TaskAreaConfig {
|
|
146
|
+
/** Directory containing task folders */
|
|
147
|
+
path: string;
|
|
148
|
+
/** Task ID prefix convention for that area */
|
|
149
|
+
prefix: string;
|
|
150
|
+
/** Area context file path (CONTEXT.md) */
|
|
151
|
+
context: string;
|
|
152
|
+
/** Optional repo ID for routing tasks in this area (workspace mode only) */
|
|
153
|
+
repoId?: string;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Self-documentation target definition */
|
|
157
|
+
export interface SelfDocTarget {
|
|
158
|
+
/** File path where agents should log discoveries */
|
|
159
|
+
[key: string]: string;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
// ── Task Runner Combined Section ─────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* All task-runner settings, previously from `.pi/task-runner.yaml`.
|
|
167
|
+
*
|
|
168
|
+
* Contains sections consumed by both the task-runner extension directly
|
|
169
|
+
* and by broader ecosystem tooling (skills, workflows, orchestrator).
|
|
170
|
+
*/
|
|
171
|
+
export interface TaskRunnerSection {
|
|
172
|
+
/** Project metadata */
|
|
173
|
+
project: ProjectMetadataConfig;
|
|
174
|
+
/** Path metadata */
|
|
175
|
+
paths: PathsConfig;
|
|
176
|
+
/** Verification commands */
|
|
177
|
+
testing: TestingConfig;
|
|
178
|
+
/** Coding standards */
|
|
179
|
+
standards: StandardsConfig;
|
|
180
|
+
/** Per-area standards overrides, keyed by area name */
|
|
181
|
+
standardsOverrides: Record<string, StandardsOverride>;
|
|
182
|
+
/** Worker agent configuration */
|
|
183
|
+
worker: WorkerConfig;
|
|
184
|
+
/** Reviewer agent configuration */
|
|
185
|
+
reviewer: ReviewerConfig;
|
|
186
|
+
/** Context/resource limits */
|
|
187
|
+
context: ContextConfig;
|
|
188
|
+
/** Task area definitions, keyed by area name */
|
|
189
|
+
taskAreas: Record<string, TaskAreaConfig>;
|
|
190
|
+
/** Named reference docs catalog */
|
|
191
|
+
referenceDocs: Record<string, string>;
|
|
192
|
+
/** Files/docs that should not be loaded into task execution context */
|
|
193
|
+
neverLoad: string[];
|
|
194
|
+
/** Target anchors where agents should log discoveries */
|
|
195
|
+
selfDocTargets: Record<string, string>;
|
|
196
|
+
/** Paths requiring explicit user approval before modification */
|
|
197
|
+
protectedDocs: string[];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
// ── Orchestrator Section Interfaces ──────────────────────────────────
|
|
202
|
+
|
|
203
|
+
/** Core orchestrator settings */
|
|
204
|
+
export interface OrchestratorCoreConfig {
|
|
205
|
+
/** Maximum parallel execution lanes/worktrees */
|
|
206
|
+
maxLanes: number;
|
|
207
|
+
/** Where lane worktree directories are created */
|
|
208
|
+
worktreeLocation: "sibling" | "subdirectory";
|
|
209
|
+
/** Prefix used for worktree directory names and lane branch naming */
|
|
210
|
+
worktreePrefix: string;
|
|
211
|
+
/** Batch ID format used in logs/branch naming */
|
|
212
|
+
batchIdFormat: "timestamp" | "sequential";
|
|
213
|
+
/** How lane sessions are spawned */
|
|
214
|
+
spawnMode: "tmux" | "subprocess";
|
|
215
|
+
/** Prefix for orchestrator tmux sessions (tmux mode) */
|
|
216
|
+
tmuxPrefix: string;
|
|
217
|
+
/** Operator identifier. Auto-detected from OS username if empty */
|
|
218
|
+
operatorId: string;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Dependency resolution settings */
|
|
222
|
+
export interface DependenciesConfig {
|
|
223
|
+
/** Dependency extraction source */
|
|
224
|
+
source: "prompt" | "agent";
|
|
225
|
+
/** Cache dependency analysis results between runs */
|
|
226
|
+
cache: boolean;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Lane assignment settings */
|
|
230
|
+
export interface AssignmentConfig {
|
|
231
|
+
/** Lane assignment strategy */
|
|
232
|
+
strategy: "affinity-first" | "round-robin" | "load-balanced";
|
|
233
|
+
/** Relative weights used by size-aware assignment logic */
|
|
234
|
+
sizeWeights: Record<string, number>;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Pre-warm settings */
|
|
238
|
+
export interface PreWarmConfig {
|
|
239
|
+
/** Enable automatic pre-warm command detection */
|
|
240
|
+
autoDetect: boolean;
|
|
241
|
+
/** Named pre-warm commands */
|
|
242
|
+
commands: Record<string, string>;
|
|
243
|
+
/** Commands always run before wave execution */
|
|
244
|
+
always: string[];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Merge settings */
|
|
248
|
+
export interface MergeConfig {
|
|
249
|
+
/** Merge-agent model (empty = inherit active session model) */
|
|
250
|
+
model: string;
|
|
251
|
+
/** Merge-agent tool allowlist */
|
|
252
|
+
tools: string;
|
|
253
|
+
/** Verification commands run after merge operations */
|
|
254
|
+
verify: string[];
|
|
255
|
+
/** Lane merge ordering policy */
|
|
256
|
+
order: "fewest-files-first" | "sequential";
|
|
257
|
+
/** Merge-agent timeout in minutes */
|
|
258
|
+
timeoutMinutes?: number;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Failure policy settings */
|
|
262
|
+
export interface FailureConfig {
|
|
263
|
+
/** Batch behavior when a task fails */
|
|
264
|
+
onTaskFailure: "skip-dependents" | "stop-wave" | "stop-all";
|
|
265
|
+
/** Behavior when a merge step fails */
|
|
266
|
+
onMergeFailure: "pause" | "abort";
|
|
267
|
+
/** Stall detection threshold (minutes) */
|
|
268
|
+
stallTimeout: number;
|
|
269
|
+
/** Max worker runtime budget per task in orchestrated mode (minutes) */
|
|
270
|
+
maxWorkerMinutes: number;
|
|
271
|
+
/** Graceful abort wait time (seconds) before forced termination */
|
|
272
|
+
abortGracePeriod: number;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Monitoring settings */
|
|
276
|
+
export interface MonitoringConfig {
|
|
277
|
+
/** Poll interval (seconds) for lane/task monitoring loop */
|
|
278
|
+
pollInterval: number;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
// ── Orchestrator Combined Section ────────────────────────────────────
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* All orchestrator settings, previously from `.pi/task-orchestrator.yaml`.
|
|
286
|
+
*/
|
|
287
|
+
export interface OrchestratorSection {
|
|
288
|
+
/** Core orchestrator settings */
|
|
289
|
+
orchestrator: OrchestratorCoreConfig;
|
|
290
|
+
/** Dependency resolution */
|
|
291
|
+
dependencies: DependenciesConfig;
|
|
292
|
+
/** Lane assignment */
|
|
293
|
+
assignment: AssignmentConfig;
|
|
294
|
+
/** Pre-warm */
|
|
295
|
+
preWarm: PreWarmConfig;
|
|
296
|
+
/** Merge */
|
|
297
|
+
merge: MergeConfig;
|
|
298
|
+
/** Failure policy */
|
|
299
|
+
failure: FailureConfig;
|
|
300
|
+
/** Monitoring */
|
|
301
|
+
monitoring: MonitoringConfig;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
// ── Unified Config ───────────────────────────────────────────────────
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Unified project configuration — the single source of truth.
|
|
309
|
+
*
|
|
310
|
+
* This is the runtime config object produced by `loadProjectConfig()`.
|
|
311
|
+
* It merges all settings from both YAML files (or the single JSON file)
|
|
312
|
+
* into one typed structure.
|
|
313
|
+
*
|
|
314
|
+
* File: `.pi/taskplane-config.json`
|
|
315
|
+
*
|
|
316
|
+
* Example JSON structure:
|
|
317
|
+
* ```json
|
|
318
|
+
* {
|
|
319
|
+
* "configVersion": 1,
|
|
320
|
+
* "taskRunner": { ... },
|
|
321
|
+
* "orchestrator": { ... }
|
|
322
|
+
* }
|
|
323
|
+
* ```
|
|
324
|
+
*/
|
|
325
|
+
export interface TaskplaneConfig {
|
|
326
|
+
/** Schema version — must equal CONFIG_VERSION */
|
|
327
|
+
configVersion: number;
|
|
328
|
+
/** Task runner settings */
|
|
329
|
+
taskRunner: TaskRunnerSection;
|
|
330
|
+
/** Orchestrator settings */
|
|
331
|
+
orchestrator: OrchestratorSection;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
// ── User Preferences (Layer 2) ───────────────────────────────────────
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* User preferences — personal settings stored per-user.
|
|
339
|
+
*
|
|
340
|
+
* File: `~/.pi/agent/taskplane/preferences.json`
|
|
341
|
+
* (or `$PI_CODING_AGENT_DIR/taskplane/preferences.json` if set)
|
|
342
|
+
*
|
|
343
|
+
* These are "Layer 2" fields — they override project config (Layer 1)
|
|
344
|
+
* for user-scoped settings only. The merge is allowlist-based: only
|
|
345
|
+
* the fields defined here can be overridden by user preferences.
|
|
346
|
+
* Unknown keys in the preferences file are silently ignored.
|
|
347
|
+
*
|
|
348
|
+
* Preferences JSON uses camelCase keys matching the runtime config shape.
|
|
349
|
+
*
|
|
350
|
+
* Layer 2 allowlist — preference field → config path:
|
|
351
|
+
*
|
|
352
|
+
* | Preference field | Config path | Type |
|
|
353
|
+
* |--------------------|--------------------------------------|---------|
|
|
354
|
+
* | operatorId | orchestrator.orchestrator.operatorId | string |
|
|
355
|
+
* | tmuxPrefix | orchestrator.orchestrator.tmuxPrefix | string |
|
|
356
|
+
* | spawnMode | orchestrator.orchestrator.spawnMode | string |
|
|
357
|
+
* | workerModel | taskRunner.worker.model | string |
|
|
358
|
+
* | reviewerModel | taskRunner.reviewer.model | string |
|
|
359
|
+
* | mergeModel | orchestrator.merge.model | string |
|
|
360
|
+
* | dashboardPort | (preferences-only; not yet in schema)| number |
|
|
361
|
+
*/
|
|
362
|
+
export interface UserPreferences {
|
|
363
|
+
/** Operator identifier (overrides orchestrator.orchestrator.operatorId) */
|
|
364
|
+
operatorId?: string;
|
|
365
|
+
/** TMUX session prefix (overrides orchestrator.orchestrator.tmuxPrefix) */
|
|
366
|
+
tmuxPrefix?: string;
|
|
367
|
+
/** Spawn mode override (overrides orchestrator.orchestrator.spawnMode) */
|
|
368
|
+
spawnMode?: "tmux" | "subprocess";
|
|
369
|
+
/** Worker model override (overrides taskRunner.worker.model) */
|
|
370
|
+
workerModel?: string;
|
|
371
|
+
/** Reviewer model override (overrides taskRunner.reviewer.model) */
|
|
372
|
+
reviewerModel?: string;
|
|
373
|
+
/** Merge model override (overrides orchestrator.merge.model) */
|
|
374
|
+
mergeModel?: string;
|
|
375
|
+
/** Dashboard port (preferences-only; not yet wired into config schema) */
|
|
376
|
+
dashboardPort?: number;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** Default (empty) user preferences — all fields undefined means "no override". */
|
|
380
|
+
export const DEFAULT_USER_PREFERENCES: UserPreferences = {};
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Canonical filename for user preferences.
|
|
384
|
+
* Resolved relative to agent directory: `<agentDir>/taskplane/preferences.json`
|
|
385
|
+
*/
|
|
386
|
+
export const USER_PREFERENCES_FILENAME = "preferences.json";
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Subdirectory under the agent dir for taskplane preferences.
|
|
390
|
+
*/
|
|
391
|
+
export const USER_PREFERENCES_SUBDIR = "taskplane";
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
// ── Defaults ─────────────────────────────────────────────────────────
|
|
395
|
+
|
|
396
|
+
/** Default task runner section values */
|
|
397
|
+
export const DEFAULT_TASK_RUNNER_SECTION: TaskRunnerSection = {
|
|
398
|
+
project: { name: "Project", description: "" },
|
|
399
|
+
paths: { tasks: "docs/task-management" },
|
|
400
|
+
testing: { commands: {} },
|
|
401
|
+
standards: { docs: [], rules: [] },
|
|
402
|
+
standardsOverrides: {},
|
|
403
|
+
worker: { model: "", tools: "read,write,edit,bash,grep,find,ls", thinking: "off" },
|
|
404
|
+
reviewer: { model: "openai/gpt-5.3-codex", tools: "read,bash,grep,find,ls", thinking: "on" },
|
|
405
|
+
context: {
|
|
406
|
+
workerContextWindow: 200000,
|
|
407
|
+
warnPercent: 70,
|
|
408
|
+
killPercent: 85,
|
|
409
|
+
maxWorkerIterations: 20,
|
|
410
|
+
maxReviewCycles: 2,
|
|
411
|
+
noProgressLimit: 3,
|
|
412
|
+
},
|
|
413
|
+
taskAreas: {},
|
|
414
|
+
referenceDocs: {},
|
|
415
|
+
neverLoad: [],
|
|
416
|
+
selfDocTargets: {},
|
|
417
|
+
protectedDocs: [],
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
/** Default orchestrator section values */
|
|
421
|
+
export const DEFAULT_ORCHESTRATOR_SECTION: OrchestratorSection = {
|
|
422
|
+
orchestrator: {
|
|
423
|
+
maxLanes: 3,
|
|
424
|
+
worktreeLocation: "subdirectory",
|
|
425
|
+
worktreePrefix: "taskplane-wt",
|
|
426
|
+
batchIdFormat: "timestamp",
|
|
427
|
+
spawnMode: "subprocess",
|
|
428
|
+
tmuxPrefix: "orch",
|
|
429
|
+
operatorId: "",
|
|
430
|
+
},
|
|
431
|
+
dependencies: {
|
|
432
|
+
source: "prompt",
|
|
433
|
+
cache: true,
|
|
434
|
+
},
|
|
435
|
+
assignment: {
|
|
436
|
+
strategy: "affinity-first",
|
|
437
|
+
sizeWeights: { S: 1, M: 2, L: 4 },
|
|
438
|
+
},
|
|
439
|
+
preWarm: {
|
|
440
|
+
autoDetect: false,
|
|
441
|
+
commands: {},
|
|
442
|
+
always: [],
|
|
443
|
+
},
|
|
444
|
+
merge: {
|
|
445
|
+
model: "",
|
|
446
|
+
tools: "read,write,edit,bash,grep,find,ls",
|
|
447
|
+
verify: [],
|
|
448
|
+
order: "fewest-files-first",
|
|
449
|
+
timeoutMinutes: 10,
|
|
450
|
+
},
|
|
451
|
+
failure: {
|
|
452
|
+
onTaskFailure: "skip-dependents",
|
|
453
|
+
onMergeFailure: "pause",
|
|
454
|
+
stallTimeout: 30,
|
|
455
|
+
maxWorkerMinutes: 30,
|
|
456
|
+
abortGracePeriod: 60,
|
|
457
|
+
},
|
|
458
|
+
monitoring: {
|
|
459
|
+
pollInterval: 5,
|
|
460
|
+
},
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
/** Default unified config */
|
|
464
|
+
export const DEFAULT_PROJECT_CONFIG: TaskplaneConfig = {
|
|
465
|
+
configVersion: CONFIG_VERSION,
|
|
466
|
+
taskRunner: DEFAULT_TASK_RUNNER_SECTION,
|
|
467
|
+
orchestrator: DEFAULT_ORCHESTRATOR_SECTION,
|
|
468
|
+
};
|
|
@@ -1,107 +1,51 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Config loading
|
|
2
|
+
* Config loading — thin wrappers over the unified loader.
|
|
3
|
+
*
|
|
4
|
+
* These functions preserve the existing snake_case return shapes
|
|
5
|
+
* (`OrchestratorConfig`, `TaskRunnerConfig` from types.ts) so all
|
|
6
|
+
* downstream consumers remain unchanged during the JSON migration.
|
|
7
|
+
*
|
|
8
|
+
* The unified loader (`loadProjectConfig`) handles JSON-first loading
|
|
9
|
+
* with YAML fallback and defaults merging.
|
|
10
|
+
*
|
|
3
11
|
* @module orch/config
|
|
4
12
|
*/
|
|
5
|
-
import { readFileSync, existsSync } from "fs";
|
|
6
|
-
import { join } from "path";
|
|
7
|
-
import { parse as yamlParse } from "yaml";
|
|
8
13
|
|
|
9
|
-
import {
|
|
10
|
-
import type { OrchestratorConfig,
|
|
14
|
+
import { loadProjectConfig, toOrchestratorConfig, toTaskRunnerConfig } from "./config-loader.ts";
|
|
15
|
+
import type { OrchestratorConfig, TaskRunnerConfig } from "./types.ts";
|
|
11
16
|
|
|
12
17
|
// ── Config Loading ───────────────────────────────────────────────────
|
|
13
18
|
|
|
14
19
|
/**
|
|
15
|
-
* Load orchestrator config
|
|
16
|
-
*
|
|
20
|
+
* Load orchestrator config.
|
|
21
|
+
*
|
|
22
|
+
* Reads `.pi/taskplane-config.json` first; falls back to
|
|
23
|
+
* `.pi/task-orchestrator.yaml` + `.pi/task-runner.yaml`; then defaults.
|
|
24
|
+
*
|
|
25
|
+
* In workspace mode, `pointerConfigRoot` (from the resolved pointer file)
|
|
26
|
+
* is inserted into the config resolution chain between cwd-local and
|
|
27
|
+
* TASKPLANE_WORKSPACE_ROOT. See `resolveConfigRoot()` in config-loader.ts.
|
|
28
|
+
*
|
|
29
|
+
* Returns the legacy `OrchestratorConfig` (snake_case) shape.
|
|
17
30
|
*/
|
|
18
|
-
export function loadOrchestratorConfig(cwd: string): OrchestratorConfig {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
return { ...DEFAULT_ORCHESTRATOR_CONFIG };
|
|
22
|
-
}
|
|
23
|
-
try {
|
|
24
|
-
const raw = readFileSync(configPath, "utf-8");
|
|
25
|
-
const loaded = yamlParse(raw) as any;
|
|
26
|
-
return {
|
|
27
|
-
orchestrator: {
|
|
28
|
-
...DEFAULT_ORCHESTRATOR_CONFIG.orchestrator,
|
|
29
|
-
...loaded?.orchestrator,
|
|
30
|
-
},
|
|
31
|
-
dependencies: {
|
|
32
|
-
...DEFAULT_ORCHESTRATOR_CONFIG.dependencies,
|
|
33
|
-
...loaded?.dependencies,
|
|
34
|
-
},
|
|
35
|
-
assignment: {
|
|
36
|
-
...DEFAULT_ORCHESTRATOR_CONFIG.assignment,
|
|
37
|
-
...loaded?.assignment,
|
|
38
|
-
size_weights: {
|
|
39
|
-
...DEFAULT_ORCHESTRATOR_CONFIG.assignment.size_weights,
|
|
40
|
-
...loaded?.assignment?.size_weights,
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
pre_warm: {
|
|
44
|
-
...DEFAULT_ORCHESTRATOR_CONFIG.pre_warm,
|
|
45
|
-
...loaded?.pre_warm,
|
|
46
|
-
commands: {
|
|
47
|
-
...DEFAULT_ORCHESTRATOR_CONFIG.pre_warm.commands,
|
|
48
|
-
...loaded?.pre_warm?.commands,
|
|
49
|
-
},
|
|
50
|
-
always: loaded?.pre_warm?.always ?? DEFAULT_ORCHESTRATOR_CONFIG.pre_warm.always,
|
|
51
|
-
},
|
|
52
|
-
merge: {
|
|
53
|
-
...DEFAULT_ORCHESTRATOR_CONFIG.merge,
|
|
54
|
-
...loaded?.merge,
|
|
55
|
-
verify: loaded?.merge?.verify ?? DEFAULT_ORCHESTRATOR_CONFIG.merge.verify,
|
|
56
|
-
},
|
|
57
|
-
failure: {
|
|
58
|
-
...DEFAULT_ORCHESTRATOR_CONFIG.failure,
|
|
59
|
-
...loaded?.failure,
|
|
60
|
-
},
|
|
61
|
-
monitoring: {
|
|
62
|
-
...DEFAULT_ORCHESTRATOR_CONFIG.monitoring,
|
|
63
|
-
...loaded?.monitoring,
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
} catch {
|
|
67
|
-
return { ...DEFAULT_ORCHESTRATOR_CONFIG };
|
|
68
|
-
}
|
|
31
|
+
export function loadOrchestratorConfig(cwd: string, pointerConfigRoot?: string): OrchestratorConfig {
|
|
32
|
+
const unified = loadProjectConfig(cwd, pointerConfigRoot);
|
|
33
|
+
return toOrchestratorConfig(unified);
|
|
69
34
|
}
|
|
70
35
|
|
|
71
36
|
/**
|
|
72
|
-
* Load task-runner config
|
|
73
|
-
*
|
|
37
|
+
* Load task-runner config (orchestrator subset: task_areas + reference_docs).
|
|
38
|
+
*
|
|
39
|
+
* Reads `.pi/taskplane-config.json` first; falls back to
|
|
40
|
+
* `.pi/task-runner.yaml`; then defaults.
|
|
41
|
+
*
|
|
42
|
+
* In workspace mode, `pointerConfigRoot` (from the resolved pointer file)
|
|
43
|
+
* is inserted into the config resolution chain between cwd-local and
|
|
44
|
+
* TASKPLANE_WORKSPACE_ROOT. See `resolveConfigRoot()` in config-loader.ts.
|
|
45
|
+
*
|
|
46
|
+
* Returns the legacy `TaskRunnerConfig` (snake_case) shape.
|
|
74
47
|
*/
|
|
75
|
-
export function loadTaskRunnerConfig(cwd: string): TaskRunnerConfig {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
return { ...DEFAULT_TASK_RUNNER_CONFIG };
|
|
79
|
-
}
|
|
80
|
-
try {
|
|
81
|
-
const raw = readFileSync(configPath, "utf-8");
|
|
82
|
-
const loaded = yamlParse(raw) as any;
|
|
83
|
-
const taskAreas: Record<string, TaskArea> = {};
|
|
84
|
-
if (loaded?.task_areas) {
|
|
85
|
-
for (const [name, area] of Object.entries(loaded.task_areas)) {
|
|
86
|
-
const a = area as any;
|
|
87
|
-
const ta: TaskArea = {
|
|
88
|
-
path: a?.path || "",
|
|
89
|
-
prefix: a?.prefix || "",
|
|
90
|
-
context: a?.context || "",
|
|
91
|
-
};
|
|
92
|
-
// Parse repo_id (snake_case YAML key) into repoId for routing
|
|
93
|
-
if (a?.repo_id && typeof a.repo_id === "string" && a.repo_id.trim()) {
|
|
94
|
-
ta.repoId = a.repo_id.trim();
|
|
95
|
-
}
|
|
96
|
-
taskAreas[name] = ta;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return {
|
|
100
|
-
task_areas: taskAreas,
|
|
101
|
-
reference_docs: loaded?.reference_docs || {},
|
|
102
|
-
};
|
|
103
|
-
} catch {
|
|
104
|
-
return { ...DEFAULT_TASK_RUNNER_CONFIG };
|
|
105
|
-
}
|
|
48
|
+
export function loadTaskRunnerConfig(cwd: string, pointerConfigRoot?: string): TaskRunnerConfig {
|
|
49
|
+
const unified = loadProjectConfig(cwd, pointerConfigRoot);
|
|
50
|
+
return toTaskRunnerConfig(unified);
|
|
106
51
|
}
|
|
107
|
-
|
|
@@ -46,6 +46,7 @@ export async function executeOrchBatch(
|
|
|
46
46
|
onMonitorUpdate?: MonitorUpdateCallback,
|
|
47
47
|
workspaceConfig?: WorkspaceConfig | null,
|
|
48
48
|
workspaceRoot?: string,
|
|
49
|
+
agentRoot?: string,
|
|
49
50
|
): Promise<void> {
|
|
50
51
|
const repoRoot = cwd;
|
|
51
52
|
// State files (.pi/batch-state.json, lane-state, etc.) belong in the workspace root,
|
|
@@ -363,6 +364,7 @@ export async function executeOrchBatch(
|
|
|
363
364
|
batchState.baseBranch,
|
|
364
365
|
workspaceConfig,
|
|
365
366
|
stateRoot,
|
|
367
|
+
agentRoot,
|
|
366
368
|
);
|
|
367
369
|
allMergeResults.push(mergeResult);
|
|
368
370
|
batchState.mergeResults.push(mergeResult);
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
runPreflight,
|
|
33
33
|
} from "./index.ts";
|
|
34
34
|
import { buildExecutionContext } from "./workspace.ts";
|
|
35
|
+
import { openSettingsTui } from "./settings-tui.ts";
|
|
35
36
|
import type {
|
|
36
37
|
AbortMode,
|
|
37
38
|
ExecutionContext,
|
|
@@ -208,6 +209,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
208
209
|
},
|
|
209
210
|
execCtx!.workspaceConfig,
|
|
210
211
|
execCtx!.workspaceRoot,
|
|
212
|
+
execCtx!.pointer?.agentRoot,
|
|
211
213
|
);
|
|
212
214
|
|
|
213
215
|
// Final widget update after batch completes
|
|
@@ -400,6 +402,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
400
402
|
updateOrchWidget();
|
|
401
403
|
},
|
|
402
404
|
execCtx!.workspaceConfig,
|
|
405
|
+
execCtx!.workspaceRoot,
|
|
406
|
+
execCtx!.pointer?.agentRoot,
|
|
403
407
|
);
|
|
404
408
|
|
|
405
409
|
// Final widget update
|
|
@@ -643,6 +647,21 @@ export default function (pi: ExtensionAPI) {
|
|
|
643
647
|
},
|
|
644
648
|
});
|
|
645
649
|
|
|
650
|
+
// ── Settings TUI ─────────────────────────────────────────────────
|
|
651
|
+
|
|
652
|
+
pi.registerCommand("taskplane-settings", {
|
|
653
|
+
description: "View and edit taskplane configuration",
|
|
654
|
+
handler: async (_args, ctx) => {
|
|
655
|
+
if (!requireExecCtx(ctx)) return;
|
|
656
|
+
|
|
657
|
+
try {
|
|
658
|
+
await openSettingsTui(ctx, execCtx!.workspaceRoot, execCtx!.pointer?.configRoot);
|
|
659
|
+
} catch (err: any) {
|
|
660
|
+
ctx.ui.notify(`❌ Failed to load settings: ${err.message}`, "error");
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
});
|
|
664
|
+
|
|
646
665
|
// ── Session Lifecycle ────────────────────────────────────────────
|
|
647
666
|
|
|
648
667
|
pi.on("session_start", async (_event, ctx) => {
|