@agent-plan/pi-adapter 0.2.19-next.5 → 0.2.19-next.7
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/dist/index.d.ts.map +1 -1
- package/dist/index.js +92 -47
- package/package.json +3 -3
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,iCAAiC,CAAC;AAgvBtF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAw9F9D"}
|
package/dist/index.js
CHANGED
|
@@ -512,6 +512,9 @@ function statusIcon(status) {
|
|
|
512
512
|
};
|
|
513
513
|
return icons[status] ?? "❓";
|
|
514
514
|
}
|
|
515
|
+
function pad(n) {
|
|
516
|
+
return String(n).padStart(3, "0");
|
|
517
|
+
}
|
|
515
518
|
function applyTaskLifecycleDates(task, nextStatus, now) {
|
|
516
519
|
const previousStatus = task.status;
|
|
517
520
|
if (nextStatus === "in-progress" && !task.startedAt) {
|
|
@@ -1379,7 +1382,7 @@ export default function planPiExtension(pi) {
|
|
|
1379
1382
|
return doc;
|
|
1380
1383
|
});
|
|
1381
1384
|
await st.writeGenerated();
|
|
1382
|
-
ctx.ui.notify(`Feature created: ${feature.
|
|
1385
|
+
ctx.ui.notify(`Feature created: ${formatFeatureRef(feature.number)}${feature.shortId ? ` +MI+ ${feature.shortId}` : ""} +EM+ ${feature.name}`, "info");
|
|
1383
1386
|
return;
|
|
1384
1387
|
}
|
|
1385
1388
|
if (b === "show") {
|
|
@@ -1460,7 +1463,7 @@ export default function planPiExtension(pi) {
|
|
|
1460
1463
|
});
|
|
1461
1464
|
feature = updatedDoc.features.find((entry) => entry.id === featureId) ?? feature;
|
|
1462
1465
|
await st.writeGenerated();
|
|
1463
|
-
ctx.ui.notify(`Feature updated: ${feature.
|
|
1466
|
+
ctx.ui.notify(`Feature updated: ${formatFeatureRef(feature.number)}${feature.shortId ? ` +MI+ ${feature.shortId}` : ""} +EM+ ${feature.name} (${feature.status})`, "info");
|
|
1464
1467
|
return;
|
|
1465
1468
|
}
|
|
1466
1469
|
if (b === "delete") {
|
|
@@ -2605,7 +2608,7 @@ export default function planPiExtension(pi) {
|
|
|
2605
2608
|
pi.registerTool({
|
|
2606
2609
|
name: "plan_repair",
|
|
2607
2610
|
label: "Plan Repair",
|
|
2608
|
-
description: "Repair dangling feature→phase references and report plan integrity (duplicate phase ids, dangling phase refs). Safe to run anytime; run if the planner reports ENOENT/phase not found or after manual edits to .planner/.",
|
|
2611
|
+
description: "Repair dangling feature→phase references, rebuild phase containment from each task's own phaseId (heals the migrateToGlobalSequence task-shuffle bug), and report plan integrity (duplicate phase ids, dangling phase refs). Safe to run anytime; run if the planner reports ENOENT/phase not found, tasks appear in the wrong phase, or after manual edits to .planner/.",
|
|
2609
2612
|
parameters: Type.Object({}),
|
|
2610
2613
|
async execute(_id, _params, _signal, _onUpdate, ctx) {
|
|
2611
2614
|
const st = await requirePlan(ctx);
|
|
@@ -2613,9 +2616,11 @@ export default function planPiExtension(pi) {
|
|
|
2613
2616
|
return { content: [{ type: "text", text: "No .planner/ found." }], details: {} };
|
|
2614
2617
|
const report = await st.repair();
|
|
2615
2618
|
const m = report.migrated;
|
|
2619
|
+
const c = report.containment;
|
|
2616
2620
|
const lines = [
|
|
2617
2621
|
`Repair complete.`,
|
|
2618
2622
|
`Migration: renamed=${m.renamed}, repaired=${m.repaired} refs, inferred=${m.inferred}.`,
|
|
2623
|
+
`Containment: ${c.changed} phase file${c.changed === 1 ? "" : "s"} rewritten (tasks regrouped by their own phaseId), ${c.tasks} tasks scanned, ${c.orphan} orphan.`,
|
|
2619
2624
|
`Integrity: duplicatePhaseIds=${report.integrity.duplicatePhaseIds.length}, danglingPhaseIds=${report.integrity.danglingPhaseIds.length}.`,
|
|
2620
2625
|
];
|
|
2621
2626
|
if (report.integrity.danglingPhaseIds.length)
|
|
@@ -2823,38 +2828,44 @@ export default function planPiExtension(pi) {
|
|
|
2823
2828
|
pi.registerTool({
|
|
2824
2829
|
name: "feature_list",
|
|
2825
2830
|
label: "Feature List",
|
|
2826
|
-
description: "List
|
|
2827
|
-
parameters: Type.Object({}),
|
|
2828
|
-
async execute(_id,
|
|
2831
|
+
description: "List features (compact: F00x · shortId — name (status; N phases, M tasks)). Use this to discover refs cheaply — do NOT read .planner/ files or plan_get full=true to find entities.",
|
|
2832
|
+
parameters: Type.Object({ featureRef: Type.Optional(Type.String({ description: "Optional: filter to one feature (F00x/shortId/UUID/name)" })) }),
|
|
2833
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
2829
2834
|
const st = await requirePlan(ctx);
|
|
2830
2835
|
if (!st)
|
|
2831
2836
|
return { content: [{ type: "text", text: "No .planner/ found." }], details: {} };
|
|
2832
|
-
const
|
|
2837
|
+
const all = (await st.loadFeatures()).features;
|
|
2838
|
+
const ref = params.featureRef?.trim();
|
|
2839
|
+
const matchF = (f) => !ref || formatFeatureRef(f.number) === ref || f.shortId === ref || f.id === ref || f.name.toLowerCase() === ref.toLowerCase();
|
|
2840
|
+
const features = all.filter(matchF);
|
|
2833
2841
|
const phases = await st.loadAllPhases();
|
|
2834
2842
|
const summary = features.map((f) => {
|
|
2835
2843
|
const fp = phases.filter((p) => p.featureId === f.id);
|
|
2836
|
-
return `- ${
|
|
2844
|
+
return `- ${formatFeatureRef(f.number)}${f.shortId ? ` · ${f.shortId}` : ""} — ${f.name} (${f.status}; ${fp.length} phases, ${fp.reduce((t, p) => t + p.tasks.length, 0)} tasks)`;
|
|
2837
2845
|
}).join("\n");
|
|
2838
|
-
return { content: [{ type: "text", text: summary || "No features" }], details: {
|
|
2846
|
+
return { content: [{ type: "text", text: summary || "No features" }], details: {} };
|
|
2839
2847
|
},
|
|
2840
2848
|
});
|
|
2841
2849
|
pi.registerTool({
|
|
2842
2850
|
name: "feature_get",
|
|
2843
2851
|
label: "Feature Get",
|
|
2844
|
-
description: "
|
|
2852
|
+
description: "Show a feature. Compact identity by default (saves tokens); pass full=true to include the description.",
|
|
2845
2853
|
parameters: Type.Object({
|
|
2846
|
-
featureId: Type.String({ description: "Feature
|
|
2854
|
+
featureId: Type.String({ description: "Feature ref: F00x, shortId, UUID, or name" }),
|
|
2855
|
+
full: Type.Optional(Type.Boolean({ description: "If true, include the feature description. Default: compact identity only." })),
|
|
2847
2856
|
}),
|
|
2848
2857
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
2849
2858
|
const st = await requirePlan(ctx);
|
|
2850
2859
|
if (!st)
|
|
2851
2860
|
return { content: [{ type: "text", text: "No .planner/ found." }], details: {} };
|
|
2852
2861
|
const features = (await st.loadFeatures()).features;
|
|
2853
|
-
const
|
|
2862
|
+
const ref = params.featureId.trim();
|
|
2863
|
+
const feature = features.find((f) => formatFeatureRef(f.number) === ref || f.shortId === ref || f.id === ref || f.name.toLowerCase() === ref.toLowerCase());
|
|
2854
2864
|
if (!feature)
|
|
2855
|
-
return { content: [{ type: "text", text: `Feature not found: ${
|
|
2865
|
+
return { content: [{ type: "text", text: `Feature not found: ${ref}` }], details: {} };
|
|
2856
2866
|
const phases = (await st.loadAllPhases()).filter((p) => p.featureId === feature.id);
|
|
2857
|
-
|
|
2867
|
+
const summary = `${feature.name} — ${formatFeatureRef(feature.number)}${feature.shortId ? ` · ${feature.shortId}` : ""} (${feature.status}; ${phases.length} phases)`;
|
|
2868
|
+
return { content: [{ type: "text", text: params.full ? `${summary}\n\n${feature.description || ""}` : summary }], details: {} };
|
|
2858
2869
|
},
|
|
2859
2870
|
});
|
|
2860
2871
|
pi.registerTool({
|
|
@@ -3020,40 +3031,52 @@ export default function planPiExtension(pi) {
|
|
|
3020
3031
|
pi.registerTool({
|
|
3021
3032
|
name: "phase_list",
|
|
3022
3033
|
label: "Phase List",
|
|
3023
|
-
description: "List
|
|
3034
|
+
description: "List phases (compact: F00x/P00x · shortId — title (status; N tasks) [F00x]). Filters: featureRef, status. Cheap discovery — do NOT read .planner/ files or plan_get full=true.",
|
|
3024
3035
|
parameters: Type.Object({
|
|
3025
|
-
|
|
3036
|
+
featureRef: Type.Optional(Type.String({ description: "Optional: filter to one feature (F00x/shortId/UUID/name)" })),
|
|
3037
|
+
status: Type.Optional(Type.String({ description: "Optional: filter by status name" })),
|
|
3026
3038
|
}),
|
|
3027
3039
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
3028
3040
|
const st = await requirePlan(ctx);
|
|
3029
3041
|
if (!st)
|
|
3030
3042
|
return { content: [{ type: "text", text: "No .planner/ found." }], details: {} };
|
|
3043
|
+
const features = (await st.loadFeatures()).features;
|
|
3031
3044
|
let phases = await st.loadAllPhases();
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3045
|
+
const fref = params.featureRef?.trim();
|
|
3046
|
+
if (fref) {
|
|
3047
|
+
const f = features.find((x) => formatFeatureRef(x.number) === fref || x.shortId === fref || x.id === fref || x.name.toLowerCase() === fref.toLowerCase());
|
|
3048
|
+
if (!f)
|
|
3049
|
+
return { content: [{ type: "text", text: `Feature not found: ${fref}` }], details: {} };
|
|
3050
|
+
phases = phases.filter((p) => p.featureId === f.id);
|
|
3051
|
+
}
|
|
3052
|
+
if (params.status)
|
|
3053
|
+
phases = phases.filter((p) => p.status === params.status);
|
|
3054
|
+
const summary = phases.map((p) => {
|
|
3055
|
+
const fNum = featureNumberOfPhase(p, features);
|
|
3056
|
+
const fTag = fNum !== undefined ? ` [F${pad(fNum)}]` : "";
|
|
3057
|
+
return `- ${formatPhaseRef(p.number, fNum)}${p.shortId ? ` · ${p.shortId}` : ""} — ${p.title} (${p.status}; ${p.tasks.length} tasks)${fTag}`;
|
|
3058
|
+
}).join("\n");
|
|
3059
|
+
return { content: [{ type: "text", text: summary || "No phases" }], details: {} };
|
|
3036
3060
|
},
|
|
3037
3061
|
});
|
|
3038
3062
|
pi.registerTool({
|
|
3039
3063
|
name: "phase_get",
|
|
3040
3064
|
label: "Phase Get",
|
|
3041
|
-
description: "
|
|
3065
|
+
description: "Show a phase. Compact identity by default (saves tokens); pass full=true to include the description.",
|
|
3042
3066
|
parameters: Type.Object({
|
|
3043
|
-
phaseId: Type.String({ description: "Phase
|
|
3067
|
+
phaseId: Type.String({ description: "Phase ref: F00x/P00x, bare P00x (global), shortId, UUID, or title" }),
|
|
3068
|
+
full: Type.Optional(Type.Boolean({ description: "If true, include the phase description. Default: compact identity only." })),
|
|
3044
3069
|
}),
|
|
3045
3070
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
3046
3071
|
const st = await requirePlan(ctx);
|
|
3047
3072
|
if (!st)
|
|
3048
3073
|
return { content: [{ type: "text", text: "No .planner/ found." }], details: {} };
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
}
|
|
3053
|
-
catch {
|
|
3074
|
+
const features = (await st.loadFeatures()).features;
|
|
3075
|
+
const phase = findPhaseByRef(await st.loadAllPhases(), features, params.phaseId.trim());
|
|
3076
|
+
if (!phase)
|
|
3054
3077
|
return { content: [{ type: "text", text: `Phase not found: ${params.phaseId}` }], details: {} };
|
|
3055
|
-
}
|
|
3056
|
-
return { content: [{ type: "text", text:
|
|
3078
|
+
const summary = `${phase.title} — ${formatPhaseRef(phase.number, featureNumberOfPhase(phase, features))}${phase.shortId ? ` · ${phase.shortId}` : ""} (${phase.status}; ${phase.tasks.length} tasks)`;
|
|
3079
|
+
return { content: [{ type: "text", text: params.full ? `${summary}\n\n${phase.description || ""}` : summary }], details: {} };
|
|
3057
3080
|
},
|
|
3058
3081
|
});
|
|
3059
3082
|
pi.registerTool({
|
|
@@ -3242,42 +3265,64 @@ export default function planPiExtension(pi) {
|
|
|
3242
3265
|
pi.registerTool({
|
|
3243
3266
|
name: "task_list",
|
|
3244
3267
|
label: "Task List",
|
|
3245
|
-
description: "List tasks
|
|
3268
|
+
description: "List tasks (compact: F00x/P00x/T00x · shortId — title (status)). Filters: featureRef, phaseRef, status. Omit all to list every task. Cheap discovery — do NOT read .planner/ files or plan_get full=true.",
|
|
3246
3269
|
parameters: Type.Object({
|
|
3247
|
-
|
|
3270
|
+
featureRef: Type.Optional(Type.String({})),
|
|
3271
|
+
phaseRef: Type.Optional(Type.String({})),
|
|
3272
|
+
status: Type.Optional(Type.String({ description: "Optional: filter by status name" })),
|
|
3248
3273
|
}),
|
|
3249
3274
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
3250
3275
|
const st = await requirePlan(ctx);
|
|
3251
3276
|
if (!st)
|
|
3252
3277
|
return { content: [{ type: "text", text: "No .planner/ found." }], details: {} };
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3278
|
+
const features = (await st.loadFeatures()).features;
|
|
3279
|
+
let phases = await st.loadAllPhases();
|
|
3280
|
+
const fref = params.featureRef?.trim();
|
|
3281
|
+
if (fref) {
|
|
3282
|
+
const f = features.find((x) => formatFeatureRef(x.number) === fref || x.shortId === fref || x.id === fref || x.name.toLowerCase() === fref.toLowerCase());
|
|
3283
|
+
if (!f)
|
|
3284
|
+
return { content: [{ type: "text", text: `Feature not found: ${fref}` }], details: {} };
|
|
3285
|
+
phases = phases.filter((p) => p.featureId === f.id);
|
|
3286
|
+
}
|
|
3287
|
+
const pref = params.phaseRef?.trim();
|
|
3288
|
+
if (pref) {
|
|
3289
|
+
const p = findPhaseByRef(phases, features, pref);
|
|
3290
|
+
if (!p)
|
|
3291
|
+
return { content: [{ type: "text", text: `Phase not found: ${pref}` }], details: {} };
|
|
3292
|
+
phases = [p];
|
|
3293
|
+
}
|
|
3294
|
+
const out = [];
|
|
3295
|
+
for (const phase of phases) {
|
|
3296
|
+
for (const task of phase.tasks) {
|
|
3297
|
+
if (params.status && task.status !== params.status)
|
|
3298
|
+
continue;
|
|
3299
|
+
out.push(`- ${formatPhaseRef(phase.number, featureNumberOfPhase(phase, features))}/T${pad(task.number)}${task.shortId ? ` · ${task.shortId}` : ""} — ${task.title} (${task.status})`);
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
return { content: [{ type: "text", text: out.join("\n") || "No tasks" }], details: {} };
|
|
3262
3303
|
},
|
|
3263
3304
|
});
|
|
3264
3305
|
pi.registerTool({
|
|
3265
3306
|
name: "task_get",
|
|
3266
3307
|
label: "Task Get",
|
|
3267
|
-
description: "
|
|
3308
|
+
description: "Show a task. Compact identity by default (saves tokens); pass full=true to include the description + statusLog.",
|
|
3268
3309
|
parameters: Type.Object({
|
|
3269
3310
|
taskId: Type.String({ description: "Task ref: F00x/P00x/T00x, bare T00x (global), 5-char shortId, UUID, or title" }),
|
|
3311
|
+
full: Type.Optional(Type.Boolean({ description: "If true, include the task description + statusLog. Default: compact identity only." })),
|
|
3270
3312
|
}),
|
|
3271
3313
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
3272
3314
|
const st = await requirePlan(ctx);
|
|
3273
3315
|
if (!st)
|
|
3274
3316
|
return { content: [{ type: "text", text: "No .planner/ found." }], details: {} };
|
|
3275
|
-
const
|
|
3276
|
-
const
|
|
3277
|
-
|
|
3278
|
-
if (!task || !phase)
|
|
3317
|
+
const features = (await st.loadFeatures()).features;
|
|
3318
|
+
const found = findTaskByRef(await st.loadAllPhases(), features, params.taskId.trim());
|
|
3319
|
+
if (!found)
|
|
3279
3320
|
return { content: [{ type: "text", text: `Task not found: ${params.taskId}` }], details: {} };
|
|
3280
|
-
|
|
3321
|
+
const summary = `${found.task.title} — ${formatPhaseRef(found.phase.number, featureNumberOfPhase(found.phase, features))}/T${pad(found.task.number)}${found.task.shortId ? ` · ${found.task.shortId}` : ""} (${found.task.status})`;
|
|
3322
|
+
if (!params.full)
|
|
3323
|
+
return { content: [{ type: "text", text: summary }], details: {} };
|
|
3324
|
+
const log = (found.task.statusLog ?? []).map((e) => ` - ${e.date.slice(0, 10)} ${e.title}`).join("\n");
|
|
3325
|
+
return { content: [{ type: "text", text: `${summary}\n\n${found.task.description || ""}${log ? `\n\nStatus log:\n${log}` : ""}` }], details: {} };
|
|
3281
3326
|
},
|
|
3282
3327
|
});
|
|
3283
3328
|
pi.registerTool({
|
|
@@ -3776,7 +3821,7 @@ export default function planPiExtension(pi) {
|
|
|
3776
3821
|
"3. IMMEDIATE SYNC: Update task status AT THE EXACT MOMENT of transition. Start = task_start NOW. Done = task_complete NOW. Blocked = task_update with motivation NOW. Never batch status updates.",
|
|
3777
3822
|
"4. BLOCKED MOTIVATION: Transitions to blocked/canceled/rejected/deferred/waiting/planned(from non-planned) MUST include a detailed 'motivation' parameter. Write it as if the next person has zero context.",
|
|
3778
3823
|
"5. NO SHORTCUTS: If a tool blocks you, follow the protocol. Bypasses are for emergencies only, not for convenience.",
|
|
3779
|
-
"6. ENTITY REFERENCES
|
|
3824
|
+
"6. ENTITY REFERENCES & FAST LOOKUP: Feature/Phase/Task numbers are GLOBALLY unique (one P003, one T007 in the whole plan) — bare P00x/T00x is a valid unambiguous ref. PREFER the 5-char shortId (e.g. UUXD1) or composite F00x/P00x/T00x in chat; NEVER a raw UUID. To FIND an entity: call feature_list / phase_list / task_list (compact, ~2K tokens for the whole plan) — do NOT read .planner/*.json files or plan_get full=true (~32K tokens) to locate an entity. To ACT: pass the ref directly to the action tool (task_start/complete/update/phase_update accept F00x/P00x/T00x, bare P00x/T00x, shortId, UUID, title). To read a description: call task_get / phase_get / feature_get with full=true for that ONE entity. Tool responses already show shortId + compositeRef; reuse them.",
|
|
3780
3825
|
"═══════════════════════════════════════════════════════════════",
|
|
3781
3826
|
"",
|
|
3782
3827
|
// ── Project details ──
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-plan/pi-adapter",
|
|
3
|
-
"version": "0.2.19-next.
|
|
3
|
+
"version": "0.2.19-next.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Pi agent extension for Agent Plan — structured project planning in .planner/",
|
|
6
6
|
"keywords": [
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"typebox": "^1.1.24",
|
|
30
30
|
"zod": "^3.24.0",
|
|
31
|
-
"@agent-plan/core": "0.2.19-next.
|
|
32
|
-
"@agent-plan/server": "0.2.19-next.
|
|
31
|
+
"@agent-plan/core": "0.2.19-next.7",
|
|
32
|
+
"@agent-plan/server": "0.2.19-next.7"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@earendil-works/pi-coding-agent": "^0.74.0",
|