taskplane 0.23.13 → 0.23.14
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.
|
@@ -2938,6 +2938,83 @@ export function buildAgentIdFromLane(
|
|
|
2938
2938
|
*
|
|
2939
2939
|
* @since TP-102
|
|
2940
2940
|
*/
|
|
2941
|
+
/**
|
|
2942
|
+
* Parse an agent .md file: extract frontmatter and body.
|
|
2943
|
+
* Returns null if file doesn't exist or is malformed.
|
|
2944
|
+
* @since TP-117
|
|
2945
|
+
*/
|
|
2946
|
+
function parseAgentFile(filePath: string): { fm: Record<string, string>; body: string } | null {
|
|
2947
|
+
try {
|
|
2948
|
+
if (!existsSync(filePath)) return null;
|
|
2949
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
2950
|
+
const fmEnd = raw.indexOf("---", 4);
|
|
2951
|
+
if (fmEnd < 0) return { fm: {}, body: raw.trim() };
|
|
2952
|
+
const fmBlock = raw.slice(4, fmEnd).trim();
|
|
2953
|
+
const fm: Record<string, string> = {};
|
|
2954
|
+
for (const line of fmBlock.split("\n")) {
|
|
2955
|
+
const m = line.match(/^([\w-]+)\s*:\s*(.+)/);
|
|
2956
|
+
if (m) fm[m[1]] = m[2].trim();
|
|
2957
|
+
}
|
|
2958
|
+
return { fm, body: raw.slice(fmEnd + 3).trim() };
|
|
2959
|
+
} catch { return null; }
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
/**
|
|
2963
|
+
* Load the base agent prompt from the taskplane package's templates/ directory.
|
|
2964
|
+
* Resolves the package root via well-known npm global paths.
|
|
2965
|
+
* @since TP-117
|
|
2966
|
+
*/
|
|
2967
|
+
function loadBaseAgentPrompt(agentName: string): string {
|
|
2968
|
+
const relPath = join("node_modules", "taskplane", "templates", "agents", `${agentName}.md`);
|
|
2969
|
+
const candidates: string[] = [];
|
|
2970
|
+
|
|
2971
|
+
// Global npm paths
|
|
2972
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
2973
|
+
if (process.env.APPDATA) candidates.push(join(process.env.APPDATA, "npm", relPath));
|
|
2974
|
+
if (home) {
|
|
2975
|
+
candidates.push(join(home, "AppData", "Roaming", "npm", relPath));
|
|
2976
|
+
candidates.push(join(home, ".npm-global", "lib", relPath));
|
|
2977
|
+
}
|
|
2978
|
+
candidates.push(join("/usr", "local", "lib", relPath));
|
|
2979
|
+
candidates.push(join("/opt", "homebrew", "lib", relPath));
|
|
2980
|
+
|
|
2981
|
+
// Dynamic: npm root -g
|
|
2982
|
+
try {
|
|
2983
|
+
const result = spawnSync("npm", ["root", "-g"], { encoding: "utf-8", timeout: 5000, shell: true });
|
|
2984
|
+
if (result.stdout?.trim()) {
|
|
2985
|
+
candidates.push(join(result.stdout.trim(), "taskplane", "templates", "agents", `${agentName}.md`));
|
|
2986
|
+
}
|
|
2987
|
+
} catch { /* ignore */ }
|
|
2988
|
+
|
|
2989
|
+
for (const p of candidates) {
|
|
2990
|
+
const def = parseAgentFile(p);
|
|
2991
|
+
if (def?.body) return def.body;
|
|
2992
|
+
}
|
|
2993
|
+
return "";
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
/**
|
|
2997
|
+
* Load local project agent prompt from .pi/agents/ or agents/ directory.
|
|
2998
|
+
* Supports standalone mode (local replaces base entirely).
|
|
2999
|
+
* @since TP-117
|
|
3000
|
+
*/
|
|
3001
|
+
function loadLocalAgentPrompt(stateRoot: string, agentName: string): string {
|
|
3002
|
+
const paths = [
|
|
3003
|
+
join(stateRoot, ".pi", "agents", `${agentName}.md`),
|
|
3004
|
+
join(stateRoot, "agents", `${agentName}.md`),
|
|
3005
|
+
];
|
|
3006
|
+
for (const p of paths) {
|
|
3007
|
+
const def = parseAgentFile(p);
|
|
3008
|
+
if (def) {
|
|
3009
|
+
// standalone: true → use local as-is (body only, replaces base)
|
|
3010
|
+
if (def.fm.standalone === "true") return def.body;
|
|
3011
|
+
// Otherwise return body as project-specific guidance to append
|
|
3012
|
+
if (def.body) return def.body;
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
return "";
|
|
3016
|
+
}
|
|
3017
|
+
|
|
2941
3018
|
export function resolveRuntimeStateRoot(
|
|
2942
3019
|
repoRoot: string,
|
|
2943
3020
|
workspaceRoot?: string,
|
|
@@ -2987,16 +3064,20 @@ export async function executeLaneV2(
|
|
|
2987
3064
|
const opId = resolveOperatorId(config);
|
|
2988
3065
|
const agentIdPrefix = `${tmuxPrefix}-${opId}`;
|
|
2989
3066
|
|
|
2990
|
-
// Load worker agent definition
|
|
3067
|
+
// Load worker agent definition: compose base template + local project guidance.
|
|
3068
|
+
// The base template (templates/agents/task-worker.md) contains critical behavioral
|
|
3069
|
+
// rules: checkpoint discipline, STATUS.md resume algorithm, review_step instructions.
|
|
3070
|
+
// The local file (.pi/agents/task-worker.md) adds project-specific guidance.
|
|
2991
3071
|
let workerSystemPrompt = "You are a task execution agent. Read STATUS.md first, find unchecked items, work on them, checkpoint after each.";
|
|
2992
3072
|
try {
|
|
2993
|
-
const
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3073
|
+
const basePrompt = loadBaseAgentPrompt("task-worker");
|
|
3074
|
+
const localPrompt = loadLocalAgentPrompt(stateRoot, "task-worker");
|
|
3075
|
+
if (basePrompt && localPrompt) {
|
|
3076
|
+
workerSystemPrompt = basePrompt + "\n\n---\n\n## Project-Specific Guidance\n\n" + localPrompt;
|
|
3077
|
+
} else if (basePrompt) {
|
|
3078
|
+
workerSystemPrompt = basePrompt;
|
|
3079
|
+
} else if (localPrompt) {
|
|
3080
|
+
workerSystemPrompt = localPrompt;
|
|
3000
3081
|
}
|
|
3001
3082
|
} catch { /* use default */ }
|
|
3002
3083
|
|