taskplane 0.3.1 → 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 +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
package/dashboard/server.cjs
CHANGED
|
@@ -23,6 +23,12 @@ const MAX_PORT_ATTEMPTS = 20;
|
|
|
23
23
|
const POLL_INTERVAL = 2000; // ms between state checks
|
|
24
24
|
|
|
25
25
|
// REPO_ROOT is resolved after parseArgs() — see initialization below.
|
|
26
|
+
// In workspace mode, REPO_ROOT is the workspace root (passed via --root).
|
|
27
|
+
// All dashboard state paths (batch-state, lane-state, conversation logs,
|
|
28
|
+
// batch-history) live at <REPO_ROOT>/.pi/ — this is runtime/sidecar state
|
|
29
|
+
// which does NOT follow the taskplane-pointer.json resolution chain.
|
|
30
|
+
// The pointer directs config/agent lookups to a config repo, but the
|
|
31
|
+
// dashboard only reads state files, so no pointer resolution is needed here.
|
|
26
32
|
let REPO_ROOT;
|
|
27
33
|
let BATCH_STATE_PATH;
|
|
28
34
|
let BATCH_HISTORY_PATH;
|
|
@@ -630,7 +636,13 @@ async function findPort(server, start, explicit) {
|
|
|
630
636
|
async function main() {
|
|
631
637
|
const opts = parseArgs();
|
|
632
638
|
|
|
633
|
-
// Resolve project root: --root flag > cwd
|
|
639
|
+
// Resolve project root: --root flag > cwd.
|
|
640
|
+
// In workspace mode this is the workspace root. All state/sidecar files
|
|
641
|
+
// (batch-state, lane-state, conversation logs, batch-history) live at
|
|
642
|
+
// <REPO_ROOT>/.pi/ and are NOT affected by taskplane-pointer.json.
|
|
643
|
+
// The pointer only redirects config/agent resolution in task-runner and
|
|
644
|
+
// orchestrator — the dashboard reads only runtime state, so no pointer
|
|
645
|
+
// resolution is performed here.
|
|
634
646
|
REPO_ROOT = path.resolve(opts.root || process.cwd());
|
|
635
647
|
BATCH_STATE_PATH = path.join(REPO_ROOT, ".pi", "batch-state.json");
|
|
636
648
|
BATCH_HISTORY_PATH = path.join(REPO_ROOT, ".pi", "batch-history.json");
|
|
@@ -26,7 +26,9 @@ import {
|
|
|
26
26
|
} from "fs";
|
|
27
27
|
import { tmpdir } from "os";
|
|
28
28
|
import { join, dirname, basename, resolve } from "path";
|
|
29
|
-
import {
|
|
29
|
+
import { loadProjectConfig, toTaskConfig } from "./taskplane/config-loader.ts";
|
|
30
|
+
import { loadWorkspaceConfig, resolvePointer } from "./taskplane/workspace.ts";
|
|
31
|
+
import type { PointerResolution } from "./taskplane/types.ts";
|
|
30
32
|
|
|
31
33
|
|
|
32
34
|
// ── Types ────────────────────────────────────────────────────────────
|
|
@@ -137,54 +139,75 @@ const DEFAULT_CONFIG: TaskConfig = {
|
|
|
137
139
|
},
|
|
138
140
|
};
|
|
139
141
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
142
|
+
// ── Pointer Resolution (Workspace Mode) ──────────────────────────────
|
|
143
|
+
|
|
144
|
+
/** Track whether a pointer warning has been logged this session (log once). */
|
|
145
|
+
let _pointerWarningLogged = false;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Resolve the workspace pointer for config and agent path redirection.
|
|
149
|
+
*
|
|
150
|
+
* In workspace mode (TASKPLANE_WORKSPACE_ROOT set), reads the pointer
|
|
151
|
+
* file and resolves config/agent roots to the config repo. In repo mode,
|
|
152
|
+
* returns null (no pointer resolution needed).
|
|
153
|
+
*
|
|
154
|
+
* All pointer failures are non-fatal: missing, malformed, or invalid
|
|
155
|
+
* pointer files produce a warning and fall back to existing paths.
|
|
156
|
+
* Warning is logged to stderr once per session for operator visibility.
|
|
157
|
+
*
|
|
158
|
+
* @returns PointerResolution with resolved paths, or null in repo mode
|
|
159
|
+
*/
|
|
160
|
+
function resolveTaskRunnerPointer(): PointerResolution | null {
|
|
161
|
+
const wsRoot = process.env.TASKPLANE_WORKSPACE_ROOT;
|
|
162
|
+
if (!wsRoot) return null; // repo mode — no pointer needed
|
|
163
|
+
|
|
148
164
|
try {
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
// Parse standards_overrides: Record<areaName, { docs?, rules? }>
|
|
152
|
-
const rawOverrides = loaded?.standards_overrides || {};
|
|
153
|
-
const parsedOverrides: Record<string, { docs?: string[]; rules?: string[] }> = {};
|
|
154
|
-
for (const [key, val] of Object.entries(rawOverrides)) {
|
|
155
|
-
if (val && typeof val === "object") {
|
|
156
|
-
const v = val as any;
|
|
157
|
-
parsedOverrides[key] = {
|
|
158
|
-
docs: Array.isArray(v.docs) ? v.docs : undefined,
|
|
159
|
-
rules: Array.isArray(v.rules) ? v.rules : undefined,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
}
|
|
165
|
+
const wsConfig = loadWorkspaceConfig(wsRoot);
|
|
166
|
+
const result = resolvePointer(wsRoot, wsConfig);
|
|
163
167
|
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (val && typeof val === "object" && (val as any).path) {
|
|
169
|
-
parsedAreas[key] = { path: (val as any).path };
|
|
170
|
-
}
|
|
168
|
+
// Surface pointer warnings once per session for operator visibility
|
|
169
|
+
if (result?.warning && !_pointerWarningLogged) {
|
|
170
|
+
_pointerWarningLogged = true;
|
|
171
|
+
console.error(`[task-runner] pointer: ${result.warning}`);
|
|
171
172
|
}
|
|
172
173
|
|
|
173
|
-
return
|
|
174
|
-
project: { ...DEFAULT_CONFIG.project, ...loaded?.project },
|
|
175
|
-
paths: { ...DEFAULT_CONFIG.paths, ...loaded?.paths },
|
|
176
|
-
testing: { commands: { ...DEFAULT_CONFIG.testing.commands, ...loaded?.testing?.commands } },
|
|
177
|
-
standards: {
|
|
178
|
-
docs: loaded?.standards?.docs || DEFAULT_CONFIG.standards.docs,
|
|
179
|
-
rules: loaded?.standards?.rules || DEFAULT_CONFIG.standards.rules,
|
|
180
|
-
},
|
|
181
|
-
standards_overrides: parsedOverrides,
|
|
182
|
-
task_areas: parsedAreas,
|
|
183
|
-
worker: { ...DEFAULT_CONFIG.worker, ...loaded?.worker },
|
|
184
|
-
reviewer: { ...DEFAULT_CONFIG.reviewer, ...loaded?.reviewer },
|
|
185
|
-
context: { ...DEFAULT_CONFIG.context, ...loaded?.context },
|
|
186
|
-
};
|
|
174
|
+
return result;
|
|
187
175
|
} catch {
|
|
176
|
+
// Workspace config load failure — fall back gracefully
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Reset pointer warning state (for testing only). */
|
|
182
|
+
export function _resetPointerWarning(): void {
|
|
183
|
+
_pointerWarningLogged = false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Expose loadAgentDef for testing (not part of public API). */
|
|
187
|
+
export const _loadAgentDef = (cwd: string, name: string) => loadAgentDef(cwd, name);
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Load task-runner config via the unified config loader.
|
|
191
|
+
*
|
|
192
|
+
* Reads `.pi/taskplane-config.json` first; falls back to YAML files;
|
|
193
|
+
* then defaults. Returns the legacy snake_case TaskConfig shape so all
|
|
194
|
+
* downstream consumers remain unchanged.
|
|
195
|
+
*
|
|
196
|
+
* Config root resolution order (workspace mode with pointer):
|
|
197
|
+
* 1. cwd has config files → use cwd (local override)
|
|
198
|
+
* 2. Pointer-resolved config root has config files → use it
|
|
199
|
+
* 3. TASKPLANE_WORKSPACE_ROOT has config files → use it (legacy fallback)
|
|
200
|
+
* 4. Fall back to cwd (loaders will return defaults)
|
|
201
|
+
*
|
|
202
|
+
* Repo mode: pointer is ignored, existing behavior unchanged.
|
|
203
|
+
*/
|
|
204
|
+
export function loadConfig(cwd: string): TaskConfig {
|
|
205
|
+
try {
|
|
206
|
+
const pointer = resolveTaskRunnerPointer();
|
|
207
|
+
const unified = loadProjectConfig(cwd, pointer?.configRoot);
|
|
208
|
+
return toTaskConfig(unified);
|
|
209
|
+
} catch {
|
|
210
|
+
// If config loading fails (e.g., malformed JSON), fall back to defaults
|
|
188
211
|
return { ...DEFAULT_CONFIG };
|
|
189
212
|
}
|
|
190
213
|
}
|
|
@@ -428,18 +451,30 @@ function resolveBaseAgentPath(name: string): string {
|
|
|
428
451
|
*
|
|
429
452
|
* Inheritance model (default: compose base + local):
|
|
430
453
|
* 1. Load base agent from the shipped package (templates/agents/{name}.md)
|
|
431
|
-
* 2. Load local agent from .pi/agents/{name}.md (if it exists)
|
|
454
|
+
* 2. Load local agent from .pi/agents/{name}.md (if it exists) — or from
|
|
455
|
+
* the pointer-resolved agent root in workspace mode
|
|
432
456
|
* 3. If local file has `standalone: true` in frontmatter, use it as-is (no base)
|
|
433
457
|
* 4. Otherwise, compose: base prompt + separator + local content
|
|
434
458
|
* 5. Local frontmatter values (tools, model) override base values
|
|
435
459
|
*
|
|
436
|
-
*
|
|
460
|
+
* Local override resolution order:
|
|
461
|
+
* 1. `<cwd>/.pi/agents/{name}.md` — worktree/repo local override (always first)
|
|
462
|
+
* 2. `<cwd>/agents/{name}.md` — worktree/repo local override (legacy location)
|
|
463
|
+
* 3. `<pointerAgentRoot>/{name}.md` — pointer-resolved config repo agents (workspace mode)
|
|
464
|
+
* First found wins. If none found, base file is used directly.
|
|
465
|
+
*
|
|
437
466
|
* If no base file exists (e.g., custom agent), local file is used as-is.
|
|
438
467
|
*/
|
|
439
468
|
function loadAgentDef(cwd: string, name: string): { systemPrompt: string; tools: string; model: string } | null {
|
|
440
469
|
const basePath = resolveBaseAgentPath(name);
|
|
441
470
|
const localPaths = [join(cwd, ".pi", "agents", `${name}.md`), join(cwd, "agents", `${name}.md`)];
|
|
442
471
|
|
|
472
|
+
// In workspace mode, add pointer-resolved agent root as fallback
|
|
473
|
+
const pointer = resolveTaskRunnerPointer();
|
|
474
|
+
if (pointer?.agentRoot) {
|
|
475
|
+
localPaths.push(join(pointer.agentRoot, `${name}.md`));
|
|
476
|
+
}
|
|
477
|
+
|
|
443
478
|
// Load base from package
|
|
444
479
|
const baseDef = parseAgentFile(basePath);
|
|
445
480
|
|