@agent-plan/pi-adapter 0.2.19-next.4 → 0.2.19-next.5

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.
@@ -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;AA6uBtF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA67F9D"}
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;AA6uBtF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAk7F9D"}
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@
9
9
  * - inject context into prompts
10
10
  */
11
11
  import { Type } from "typebox";
12
- import { ExportService, PlanStore, setWriteBusyHook, setWriteNotifyHook, migrateToUuids, withFeatureLock, needsMotivation, findPhaseByRef, buildRecap } from "@agent-plan/core";
12
+ import { ExportService, PlanStore, setWriteBusyHook, setWriteNotifyHook, migrateToUuids, migrateToGlobalSequence, withFeatureLock, needsMotivation, findPhaseByRef, findTaskByRef, buildRecap } from "@agent-plan/core";
13
13
  import { createChecklistItemId, createFeatureId, createPhaseId, createShortId, createTaskId, clampSlug, normalizeSlug, formatPhaseRef, formatFeatureRef, featureNumberOfPhase } from "@agent-plan/core/naming";
14
14
  import { join, dirname } from "node:path";
15
15
  import { homedir } from "node:os";
@@ -1146,19 +1146,6 @@ export default function planPiExtension(pi) {
1146
1146
  ?? phases.find((phase) => phase.title.toLowerCase().includes(normalized))
1147
1147
  ?? null;
1148
1148
  }
1149
- function findTaskByRef(phases, ref) {
1150
- const normalized = ref.trim().toLowerCase();
1151
- if (!normalized)
1152
- return null;
1153
- for (const phase of phases) {
1154
- const task = phase.tasks.find((entryTask) => entryTask.id.toLowerCase() === normalized
1155
- || entryTask.title.toLowerCase() === normalized
1156
- || entryTask.title.toLowerCase().includes(normalized));
1157
- if (task)
1158
- return { phase, task };
1159
- }
1160
- return null;
1161
- }
1162
1149
  function profile_packageManager(pkg, lockfile) {
1163
1150
  if (pkg?.packageManager)
1164
1151
  return pkg.packageManager;
@@ -1363,7 +1350,7 @@ export default function planPiExtension(pi) {
1363
1350
  return;
1364
1351
  }
1365
1352
  const now = nowISO();
1366
- const featureNumber = (await st.loadFeatures()).features.length + 1;
1353
+ const featureNumber = await st.allocFeatureNumber();
1367
1354
  const shortId = createShortId(await st.assignedShortIds());
1368
1355
  const priority = await st.nextPriority("feature");
1369
1356
  const feature = {
@@ -1550,7 +1537,7 @@ export default function planPiExtension(pi) {
1550
1537
  await withFeatureLock(feature.id, async () => {
1551
1538
  const phases = await st.loadAllPhases();
1552
1539
  const featurePhases = phases.filter((p) => p.featureId === feature.id);
1553
- const number = featurePhases.reduce((max, p) => Math.max(max, p.number), 0) + 1;
1540
+ const number = await st.allocPhaseNumber();
1554
1541
  const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
1555
1542
  const id = createPhaseId();
1556
1543
  const shortId = createShortId(await st.assignedShortIds());
@@ -1577,7 +1564,7 @@ export default function planPiExtension(pi) {
1577
1564
  });
1578
1565
  if (!phase)
1579
1566
  return;
1580
- ctx.ui.notify(`Phase created: ${phase.id} — ${title} (feature ${feature.name})`, "info");
1567
+ ctx.ui.notify(`Phase created: ${formatPhaseRef(phase.number, feature.number)} — ${title}${phase.shortId ? ` · ${phase.shortId}` : ""} (feature ${feature.name})`, "info");
1581
1568
  const startDiscuss = await ctx.ui.input("Start phase discuss now? Type yes to continue");
1582
1569
  if (isYes(startDiscuss)) {
1583
1570
  await handlePlanner(`phase discuss ${phase.id}`, ctx);
@@ -1602,7 +1589,7 @@ export default function planPiExtension(pi) {
1602
1589
  phase.status = "discovery";
1603
1590
  phase.updatedAt = nowISO();
1604
1591
  await st.savePhase(phase);
1605
- ctx.ui.notify(`Discovery for ${phase.id}…`, "info");
1592
+ ctx.ui.notify(`Discovery for ${formatPhaseRef(phase.number, featureNumberOfPhase(phase, (await st.loadFeatures()).features))}…`, "info");
1606
1593
  // ── Recap: give full context before planning tasks ──
1607
1594
  const recapPlan = await st.loadAll();
1608
1595
  const recapProject = recapPlan.project;
@@ -1667,7 +1654,7 @@ export default function planPiExtension(pi) {
1667
1654
  phase.updatedAt = nowISO();
1668
1655
  await st.savePhase(phase);
1669
1656
  await st.writeGenerated();
1670
- ctx.ui.notify(`Phase ${phase.id} is now **planned**.`, "info");
1657
+ ctx.ui.notify(`Phase ${formatPhaseRef(phase.number, featureNumberOfPhase(phase, (await st.loadFeatures()).features))} is now **planned**.${phase.shortId ? ` · ${phase.shortId}` : ""}`, "info");
1671
1658
  return;
1672
1659
  }
1673
1660
  if (b === "show") {
@@ -1715,7 +1702,7 @@ export default function planPiExtension(pi) {
1715
1702
  }
1716
1703
  await st.deletePhase(phase.id);
1717
1704
  await st.writeGenerated();
1718
- ctx.ui.notify(`Phase deleted: ${phase.id}`, "info");
1705
+ ctx.ui.notify(`Phase deleted: ${formatPhaseRef(phase.number, featureNumberOfPhase(phase, (await st.loadFeatures()).features))}${phase.shortId ? ` · ${phase.shortId}` : ""}`, "info");
1719
1706
  return;
1720
1707
  }
1721
1708
  if (b === "update") {
@@ -1767,7 +1754,7 @@ export default function planPiExtension(pi) {
1767
1754
  phase.updatedAt = nowISO();
1768
1755
  await st.savePhase(phase);
1769
1756
  await st.writeGenerated();
1770
- ctx.ui.notify(`Phase updated: ${phase.id} — ${phase.title} (${phase.status})`, "info");
1757
+ ctx.ui.notify(`Phase updated: ${formatPhaseRef(phase.number, featureNumberOfPhase(phase, (await st.loadFeatures()).features))} — ${phase.title} (${phase.status})${phase.shortId ? ` · ${phase.shortId}` : ""}`, "info");
1771
1758
  return;
1772
1759
  }
1773
1760
  ctx.ui.notify(`Unknown phase action "${b}". Try: add, show, discuss, delete, update`, "warning");
@@ -1793,7 +1780,7 @@ export default function planPiExtension(pi) {
1793
1780
  return;
1794
1781
  }
1795
1782
  const shortName = clampSlug(title, 30, `task-${Date.now().toString(36)}`);
1796
- const taskNum = phase.tasks.length + 1;
1783
+ const taskNum = await st.allocTaskNumber();
1797
1784
  const taskId = createTaskId();
1798
1785
  const shortId = createShortId(await st.assignedShortIds());
1799
1786
  const priority = await st.nextPriority("task", phase.id);
@@ -1825,7 +1812,7 @@ export default function planPiExtension(pi) {
1825
1812
  }
1826
1813
  if (b === "discuss") {
1827
1814
  const phases = await st.loadAllPhases();
1828
- let resolved = subArgs.trim() ? findTaskByRef(phases, subArgs.trim()) : null;
1815
+ let resolved = subArgs.trim() ? findTaskByRef(phases, (await st.loadFeatures()).features, subArgs.trim()) : null;
1829
1816
  if (!resolved) {
1830
1817
  const phase = await pickPhase();
1831
1818
  if (!phase) {
@@ -1859,14 +1846,14 @@ export default function planPiExtension(pi) {
1859
1846
  phase.updatedAt = nowISO();
1860
1847
  await st.savePhase(phase);
1861
1848
  await st.writeGenerated();
1862
- ctx.ui.notify(`Task ${task.id} discussed and updated.`, "info");
1849
+ ctx.ui.notify(`Task ${formatPhaseRef(phase.number, featureNumberOfPhase(phase, (await st.loadFeatures()).features))}/T${String(task.number).padStart(3, "0")} discussed and updated.`, "info");
1863
1850
  return;
1864
1851
  }
1865
1852
  if (b === "show") {
1866
1853
  let phase = null;
1867
1854
  let task = null;
1868
1855
  if (subArgs.trim()) {
1869
- const resolved = findTaskByRef(await st.loadAllPhases(), subArgs.trim());
1856
+ const resolved = findTaskByRef(await st.loadAllPhases(), (await st.loadFeatures()).features, subArgs.trim());
1870
1857
  phase = resolved?.phase ?? null;
1871
1858
  task = resolved?.task ?? null;
1872
1859
  }
@@ -1928,7 +1915,7 @@ export default function planPiExtension(pi) {
1928
1915
  phase.updatedAt = nowISO();
1929
1916
  await st.savePhase(phase);
1930
1917
  await st.writeGenerated();
1931
- ctx.ui.notify(`Task deleted: ${task.id}`, "info");
1918
+ ctx.ui.notify(`Task deleted: ${formatPhaseRef(phase.number, featureNumberOfPhase(phase, (await st.loadFeatures()).features))}/T${String(task.number).padStart(3, "0")}${task.shortId ? ` · ${task.shortId}` : ""}`, "info");
1932
1919
  return;
1933
1920
  }
1934
1921
  if (b === "update") {
@@ -1977,14 +1964,14 @@ export default function planPiExtension(pi) {
1977
1964
  phase.updatedAt = now;
1978
1965
  await st.savePhase(phase);
1979
1966
  await st.writeGenerated();
1980
- ctx.ui.notify(`Task updated: ${task.id} — ${task.title} (${task.status})`, "info");
1967
+ ctx.ui.notify(`Task updated: ${formatPhaseRef(phase.number, featureNumberOfPhase(phase, (await st.loadFeatures()).features))}/T${String(task.number).padStart(3, "0")} — ${task.title} (${task.status})${task.shortId ? ` · ${task.shortId}` : ""}`, "info");
1981
1968
  return;
1982
1969
  }
1983
1970
  if (b === "start") {
1984
1971
  let phase = null;
1985
1972
  let task = null;
1986
1973
  if (subArgs.trim()) {
1987
- const resolved = findTaskByRef(await st.loadAllPhases(), subArgs.trim());
1974
+ const resolved = findTaskByRef(await st.loadAllPhases(), (await st.loadFeatures()).features, subArgs.trim());
1988
1975
  phase = resolved?.phase ?? null;
1989
1976
  task = resolved?.task ?? null;
1990
1977
  }
@@ -2036,14 +2023,14 @@ export default function planPiExtension(pi) {
2036
2023
  phase.updatedAt = now;
2037
2024
  await st.savePhase(phase);
2038
2025
  await st.writeGenerated();
2039
- ctx.ui.notify(`✅ Task started: ${task.id} — ${task.title} (in-progress)`, "info");
2026
+ ctx.ui.notify(`✅ Task started: ${formatPhaseRef(phase.number, featureNumberOfPhase(phase, (await st.loadFeatures()).features))}/T${String(task.number).padStart(3, "0")} — ${task.title} (in-progress)${task.shortId ? ` · ${task.shortId}` : ""}`, "info");
2040
2027
  return;
2041
2028
  }
2042
2029
  if (b === "complete") {
2043
2030
  let phase = null;
2044
2031
  let task = null;
2045
2032
  if (subArgs.trim()) {
2046
- const resolved = findTaskByRef(await st.loadAllPhases(), subArgs.trim());
2033
+ const resolved = findTaskByRef(await st.loadAllPhases(), (await st.loadFeatures()).features, subArgs.trim());
2047
2034
  phase = resolved?.phase ?? null;
2048
2035
  task = resolved?.task ?? null;
2049
2036
  }
@@ -2091,7 +2078,7 @@ export default function planPiExtension(pi) {
2091
2078
  phase.updatedAt = now;
2092
2079
  await st.savePhase(phase);
2093
2080
  await st.writeGenerated();
2094
- ctx.ui.notify(`✅ Task completed: ${task.id} — ${task.title} (done)`, "info");
2081
+ ctx.ui.notify(`✅ Task completed: ${formatPhaseRef(phase.number, featureNumberOfPhase(phase, (await st.loadFeatures()).features))}/T${String(task.number).padStart(3, "0")} — ${task.title} (done)${task.shortId ? ` · ${task.shortId}` : ""}`, "info");
2095
2082
  return;
2096
2083
  }
2097
2084
  ctx.ui.notify(`Unknown task action "${b}". Try: add, show, discuss, delete, update, start, complete`, "warning");
@@ -2890,7 +2877,7 @@ export default function planPiExtension(pi) {
2890
2877
  const priority = await st.nextPriority("feature");
2891
2878
  const feature = {
2892
2879
  id: createFeatureId(),
2893
- number: currentFeatures.length + 1,
2880
+ number: await st.allocFeatureNumber(),
2894
2881
  shortId,
2895
2882
  priority,
2896
2883
  name: params.name,
@@ -2924,7 +2911,7 @@ export default function planPiExtension(pi) {
2924
2911
  label: "Feature Update",
2925
2912
  description: "Update one or more fields of a feature. Only provided fields are changed. IMPORTANT: feature status is derived from child phases/tasks, so do not update feature.status directly unless truly necessary.",
2926
2913
  parameters: Type.Object({
2927
- featureId: Type.String({ description: "Feature ID" }),
2914
+ featureId: Type.String({ description: "Feature ref: F00x, shortId, UUID, or name" }),
2928
2915
  name: Type.Optional(Type.String({ description: "New name" })),
2929
2916
  description: Type.Optional(Type.String({ description: "New description" })),
2930
2917
  status: Type.Optional(Type.String({ description: "New status: planned|in-progress|done|blocked|canceled. Avoid setting this directly unless you truly need an override: feature status is derived from child phases/tasks." })),
@@ -2999,7 +2986,7 @@ export default function planPiExtension(pi) {
2999
2986
  label: "Feature Delete",
3000
2987
  description: "Delete a feature. Its phases are unlinked (featureId cleared) but NOT deleted unless cascade=true.",
3001
2988
  parameters: Type.Object({
3002
- featureId: Type.String({ description: "Feature ID to delete" }),
2989
+ featureId: Type.String({ description: "Feature ref: F00x, shortId, UUID, or name to delete" }),
3003
2990
  cascade: Type.Optional(Type.Boolean({ description: "If true, also delete all phases belonging to this feature. Default: false" })),
3004
2991
  }),
3005
2992
  async execute(_id, params, _signal, _onUpdate, ctx) {
@@ -3090,7 +3077,7 @@ export default function planPiExtension(pi) {
3090
3077
  await withFeatureLock(params.featureId, async () => {
3091
3078
  const phases = await st.loadAllPhases();
3092
3079
  const featurePhases = phases.filter((p) => p.featureId === params.featureId);
3093
- const number = featurePhases.reduce((max, p) => Math.max(max, p.number), 0) + 1;
3080
+ const number = await st.allocPhaseNumber();
3094
3081
  const id = createPhaseId();
3095
3082
  const shortId = createShortId(await st.assignedShortIds());
3096
3083
  const priority = await st.nextPriority("phase", params.featureId);
@@ -3133,7 +3120,7 @@ export default function planPiExtension(pi) {
3133
3120
  label: "Phase Update",
3134
3121
  description: "Update one or more fields of a phase. Only provided fields are changed. Supports re-linking to a feature via featureId. IMPORTANT: phase status is derived from task statuses, so do not update phase.status directly unless truly necessary.",
3135
3122
  parameters: Type.Object({
3136
- phaseId: Type.String({ description: "Phase ID" }),
3123
+ phaseId: Type.String({ description: "Phase ref: F00x/P00x, bare P00x (global), shortId, UUID, or title" }),
3137
3124
  title: Type.Optional(Type.String({ description: "New title" })),
3138
3125
  status: Type.Optional(Type.String({ description: "New status: draft|discovery|planned|in-progress|done|blocked|canceled. Avoid setting this directly unless you truly need an override: phase status is derived from task statuses." })),
3139
3126
  summary: Type.Optional(Type.String({ description: "New summary" })),
@@ -3223,7 +3210,7 @@ export default function planPiExtension(pi) {
3223
3210
  label: "Phase Delete",
3224
3211
  description: "Delete a phase. Its tasks are deleted with it (cascade). Unlinks the phase from its feature.",
3225
3212
  parameters: Type.Object({
3226
- phaseId: Type.String({ description: "Phase ID to delete" }),
3213
+ phaseId: Type.String({ description: "Phase ref: F00x/P00x, bare P00x (global), shortId, UUID, or title to delete" }),
3227
3214
  }),
3228
3215
  async execute(_id, params, _signal, _onUpdate, ctx) {
3229
3216
  const st = await requirePlan(ctx);
@@ -3257,7 +3244,7 @@ export default function planPiExtension(pi) {
3257
3244
  label: "Task List",
3258
3245
  description: "List tasks of a phase with id, status, and title.",
3259
3246
  parameters: Type.Object({
3260
- phaseId: Type.String({ description: "Phase ID" }),
3247
+ phaseId: Type.String({ description: "Phase ref: F00x/P00x, bare P00x (global), shortId, UUID, or title" }),
3261
3248
  }),
3262
3249
  async execute(_id, params, _signal, _onUpdate, ctx) {
3263
3250
  const st = await requirePlan(ctx);
@@ -3279,7 +3266,7 @@ export default function planPiExtension(pi) {
3279
3266
  label: "Task Get",
3280
3267
  description: "Read full details of a single task.",
3281
3268
  parameters: Type.Object({
3282
- taskId: Type.String({ description: "Task ID" }),
3269
+ taskId: Type.String({ description: "Task ref: F00x/P00x/T00x, bare T00x (global), 5-char shortId, UUID, or title" }),
3283
3270
  }),
3284
3271
  async execute(_id, params, _signal, _onUpdate, ctx) {
3285
3272
  const st = await requirePlan(ctx);
@@ -3323,7 +3310,7 @@ export default function planPiExtension(pi) {
3323
3310
  const initialStatus = params.status ?? "planned";
3324
3311
  const existingPhase = await st.loadPhase(params.phaseId);
3325
3312
  const task = {
3326
- id: taskId, phaseId: params.phaseId, number: existingPhase.tasks.length + 1, shortId, priority, shortName,
3313
+ id: taskId, phaseId: params.phaseId, number: await st.allocTaskNumber(), shortId, priority, shortName,
3327
3314
  title: params.title,
3328
3315
  status: initialStatus,
3329
3316
  description: params.description ?? "",
@@ -3354,7 +3341,7 @@ export default function planPiExtension(pi) {
3354
3341
  label: "Task Update",
3355
3342
  description: "Update one or more fields of a task. Only provided fields are changed. Do NOT use this tool to start or complete work; use task_start and task_complete for lifecycle transitions so startedAt/completedAt stay correct.",
3356
3343
  parameters: Type.Object({
3357
- taskId: Type.String({ description: "Task ID" }),
3344
+ taskId: Type.String({ description: "Task ref: F00x/P00x/T00x, bare T00x (global), 5-char shortId, UUID, or title" }),
3358
3345
  title: Type.Optional(Type.String({ description: "New title" })),
3359
3346
  status: Type.Optional(Type.String({ description: "New status: planned|in-progress|done|blocked|canceled|rejected|deferred|waiting" })),
3360
3347
  description: Type.Optional(Type.String({ description: "New description" })),
@@ -3442,7 +3429,7 @@ export default function planPiExtension(pi) {
3442
3429
  label: "Task Delete",
3443
3430
  description: "Delete a task from its phase.",
3444
3431
  parameters: Type.Object({
3445
- taskId: Type.String({ description: "Task ID to delete" }),
3432
+ taskId: Type.String({ description: "Task ref: F00x/P00x/T00x, bare T00x (global), 5-char shortId, UUID, or title to delete" }),
3446
3433
  }),
3447
3434
  async execute(_id, params, _signal, _onUpdate, ctx) {
3448
3435
  const st = await requirePlan(ctx);
@@ -3468,7 +3455,7 @@ export default function planPiExtension(pi) {
3468
3455
  label: "Task Start",
3469
3456
  description: "Set a task to in-progress. Use this BEFORE starting implementation work on any task. Sets startedAt timestamp automatically.",
3470
3457
  parameters: Type.Object({
3471
- taskId: Type.String({ description: "Task ID to start" }),
3458
+ taskId: Type.String({ description: "Task ref: F00x/P00x/T00x, bare T00x (global), 5-char shortId, UUID, or title to start" }),
3472
3459
  }),
3473
3460
  async execute(_id, params, _signal, _onUpdate, ctx) {
3474
3461
  const st = await requirePlan(ctx);
@@ -3506,7 +3493,7 @@ export default function planPiExtension(pi) {
3506
3493
  label: "Task Complete",
3507
3494
  description: "Mark a task as done. Sets completedAt and startedAt (if missing) automatically. Checks for unchecked checklist items and warns unless force=true.",
3508
3495
  parameters: Type.Object({
3509
- taskId: Type.String({ description: "Task ID to complete" }),
3496
+ taskId: Type.String({ description: "Task ref: F00x/P00x/T00x, bare T00x (global), 5-char shortId, UUID, or title to complete" }),
3510
3497
  force: Type.Optional(Type.Boolean({ description: "Skip checklist completion check. Default: false" })),
3511
3498
  description_update: Type.Optional(Type.String({ description: "Post-hoc summary: commit hash(s), files touched, decisions made, updated code references with new line numbers. Keeps the planner alive and traceable." })),
3512
3499
  }),
@@ -3646,6 +3633,7 @@ export default function planPiExtension(pi) {
3646
3633
  // add seconds of latency on every turn if run unconditionally.
3647
3634
  if (!plannerHeavyInitDone) {
3648
3635
  await migrateToUuids(st);
3636
+ await migrateToGlobalSequence(st).catch(() => null);
3649
3637
  await st.ensureShortIdsAndPriority().catch(() => null);
3650
3638
  await ensureProjectLanguagePreferences(st).catch(() => null);
3651
3639
  await maybeHealStatuses(st, ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-plan/pi-adapter",
3
- "version": "0.2.19-next.4",
3
+ "version": "0.2.19-next.5",
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.4",
32
- "@agent-plan/server": "0.2.19-next.4"
31
+ "@agent-plan/core": "0.2.19-next.5",
32
+ "@agent-plan/server": "0.2.19-next.5"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@earendil-works/pi-coding-agent": "^0.74.0",