@agent-plan/core 0.2.19-next.2 → 0.2.19-next.3
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/plan-store.d.ts +26 -12
- package/dist/plan-store.d.ts.map +1 -1
- package/dist/plan-store.js +78 -48
- package/dist/schema.d.ts +99 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +17 -0
- package/package.json +1 -1
package/dist/plan-store.d.ts
CHANGED
|
@@ -63,7 +63,6 @@ export declare class PlanStore {
|
|
|
63
63
|
private codebasePath;
|
|
64
64
|
private resumePath;
|
|
65
65
|
private activityPath;
|
|
66
|
-
private handoffPath;
|
|
67
66
|
init(projectName: string): Promise<void>;
|
|
68
67
|
exists(): Promise<boolean>;
|
|
69
68
|
loadManifest(): Promise<Manifest>;
|
|
@@ -90,14 +89,6 @@ export declare class PlanStore {
|
|
|
90
89
|
/** True when a guard bypass is currently active (not expired). */
|
|
91
90
|
isGuardBypassed(): Promise<boolean>;
|
|
92
91
|
loadActivityLog(): Promise<ActivityLog>;
|
|
93
|
-
handoffExists(): Promise<boolean>;
|
|
94
|
-
loadHandoff(): Promise<{
|
|
95
|
-
content: string;
|
|
96
|
-
createdAt: string;
|
|
97
|
-
updatedAt: string;
|
|
98
|
-
} | null>;
|
|
99
|
-
saveHandoff(content: string): Promise<void>;
|
|
100
|
-
deleteHandoff(): Promise<void>;
|
|
101
92
|
appendActivity(type: string, ref: string, summary: string): Promise<ActivityEntry>;
|
|
102
93
|
/** Derive an up-to-date resume focus from the current workspace state. */
|
|
103
94
|
refreshResume(notes?: string, lastSessionSummary?: string): Promise<ResumeFocus>;
|
|
@@ -188,9 +179,32 @@ export declare class PlanStore {
|
|
|
188
179
|
/** Set the handoff text for a phase + stamp handoffUpdatedAt. Atomic per-file
|
|
189
180
|
* update via updatePhase (so the web UI refreshes via maybeAutoSync). */
|
|
190
181
|
setPhaseHandoff(phaseId: string, text: string): Promise<void>;
|
|
191
|
-
/**
|
|
192
|
-
*
|
|
193
|
-
|
|
182
|
+
/** Directory where cleared handoff content is archived as .md files
|
|
183
|
+
* (gitignored). Keeps the phase JSON lean while making past handoffs
|
|
184
|
+
* recoverable + human-readable. */
|
|
185
|
+
private handoffArchiveDir;
|
|
186
|
+
/** Mark the phase handoff as read/acknowledged on recap (sets handoffReadAt).
|
|
187
|
+
* Does NOT clear the handoff — content is kept until a task starts or the
|
|
188
|
+
* phase completes, so a restart between read and resume does not lose it. */
|
|
189
|
+
markHandoffRead(phaseId: string): Promise<void>;
|
|
190
|
+
/** One-time import of a legacy .planner/HANDOFF.md file (file-based handoff
|
|
191
|
+
* from before F004) into the entity-scoped phase.handoff. Idempotent: if the
|
|
192
|
+
* file is absent or empty, no-op. If it exists + non-empty + the target phase
|
|
193
|
+
* has no handoff, writes the content onto the current in-progress phase (or
|
|
194
|
+
* the first phase if none in-progress) with an "imported" handoffHistory entry,
|
|
195
|
+
* then renames the file to HANDOFF.md.bak so it won't re-import. If the target
|
|
196
|
+
* already has a handoff, the entity-scoped one wins and the file is just .bak'd. */
|
|
197
|
+
importLegacyHandoffFile(): Promise<{
|
|
198
|
+
imported: boolean;
|
|
199
|
+
phaseRef?: string;
|
|
200
|
+
}>;
|
|
201
|
+
/** Clear the handoff for a phase, archiving its content first. The handoff
|
|
202
|
+
* markdown is written to .planner/handoff-archive/<phaseId>-<ISO>.md and a
|
|
203
|
+
* metadata entry { file, clearedAt, reason } is prepended to handoffHistory
|
|
204
|
+
* (capped at 5; oldest file is deleted when trimmed). handoffUpdatedAt is
|
|
205
|
+
* left unchanged as an audit trail. If the handoff is empty, this is a no-op.
|
|
206
|
+
* reason: "task-started" | "phase-done" | "manual" | "superseded" | "imported". */
|
|
207
|
+
clearPhaseHandoff(phaseId: string, reason?: string): Promise<void>;
|
|
194
208
|
/** List all phases that have a non-empty handoff, newest first, with a
|
|
195
209
|
* human-readable composite ref (P00x or P00x(F00x)) and a first-line excerpt. */
|
|
196
210
|
listHandoffs(): Promise<PhaseHandoffSummary[]>;
|
package/dist/plan-store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan-store.d.ts","sourceRoot":"","sources":["../src/plan-store.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,OAAO,EACZ,KAAK,gBAAgB,EAGrB,KAAK,QAAQ,EAEb,KAAK,KAAK,EAEV,KAAK,aAAa,EAElB,KAAK,OAAO,EAEZ,KAAK,oBAAoB,EAIzB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAC;AAOrB,qBAAa,cAAe,SAAQ,KAAK;aAGrB,KAAK,CAAC,EAAE,OAAO;gBAD/B,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,EAAE,OAAO,YAAA;CAKlC;AAeD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,CAElF;AAKD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,CAEvE;AAaD,wBAAgB,eAAe,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAStF;AAmDD,mFAAmF;AACnF,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,yFAAyF;IACzF,SAAS,EAAE,MAAM,CAAC;CACnB;AASD,wBAAsB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEpE;AA4BD,qBAAa,SAAS;IACpB,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAS;IAO1B,OAAO,CAAC,eAAe,CAAS;gBAEpB,IAAI,EAAE,MAAM;IAIxB;;4EAEwE;IACxE,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAEpC;;;2BAGuB;YACT,UAAU;IAUxB,2EAA2E;IACrE,oBAAoB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAI/D;;oFAEgF;IAC1E,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;YAIrC,aAAa;IAK3B,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,yBAAyB;IAUjC,OAAO,CAAC,sBAAsB;IAc9B,OAAO,CAAC,0BAA0B;IAuE5B,uBAAuB,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"plan-store.d.ts","sourceRoot":"","sources":["../src/plan-store.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,OAAO,EACZ,KAAK,gBAAgB,EAGrB,KAAK,QAAQ,EAEb,KAAK,KAAK,EAEV,KAAK,aAAa,EAElB,KAAK,OAAO,EAEZ,KAAK,oBAAoB,EAIzB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAC;AAOrB,qBAAa,cAAe,SAAQ,KAAK;aAGrB,KAAK,CAAC,EAAE,OAAO;gBAD/B,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,EAAE,OAAO,YAAA;CAKlC;AAeD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,CAElF;AAKD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,CAEvE;AAaD,wBAAgB,eAAe,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAStF;AAmDD,mFAAmF;AACnF,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,yFAAyF;IACzF,SAAS,EAAE,MAAM,CAAC;CACnB;AASD,wBAAsB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEpE;AA4BD,qBAAa,SAAS;IACpB,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAS;IAO1B,OAAO,CAAC,eAAe,CAAS;gBAEpB,IAAI,EAAE,MAAM;IAIxB;;4EAEwE;IACxE,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAEpC;;;2BAGuB;YACT,UAAU;IAUxB,2EAA2E;IACrE,oBAAoB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAI/D;;oFAEgF;IAC1E,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;YAIrC,aAAa;IAK3B,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,yBAAyB;IAUjC,OAAO,CAAC,sBAAsB;IAc9B,OAAO,CAAC,0BAA0B;IAuE5B,uBAAuB,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAoB9D,OAAO,CAAC,YAAY;IAGpB,OAAO,CAAC,WAAW;IAGnB,OAAO,CAAC,gBAAgB;IAGxB,OAAO,CAAC,YAAY;IAGpB,OAAO,CAAC,WAAW;IAGnB,OAAO,CAAC,WAAW;IAGnB,OAAO,CAAC,gBAAgB;IAMxB;;gFAE4E;YAC9D,aAAa;IAa3B,OAAO,CAAC,SAAS;IAGjB,OAAO,CAAC,SAAS;IAGjB,OAAO,CAAC,YAAY;IAGpB,OAAO,CAAC,YAAY;IAGpB,OAAO,CAAC,UAAU;IAGlB,OAAO,CAAC,YAAY;IAKd,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8FxC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAW1B,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC;IAIjC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAM/B,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAMhD;;0BAEsB;YACR,eAAe;IAqCvB,YAAY,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAOzC,mBAAmB,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAQtD,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAM5D,UAAU,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAQzC,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpD;;;;;OAKG;IACG,oBAAoB,CAAC,eAAe,SAAK,GAAG,OAAO,CAAC,MAAM,CAAC;IAkBjE,qCAAqC;IAC/B,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQvC,kEAAkE;IAC5D,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAQnC,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC;IAQvC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAaxF,0EAA0E;IACpE,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAoBhF,gBAAgB,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAQjD,aAAa,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IA0BjC,OAAO,IAAI,OAAO,CAAC,aAAa,CAAC;IAYvC;4DACwD;IAClD,eAAe,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAmEzF;;;;;OAKG;IACG,oBAAoB,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IA0B1D;mFAC+E;IACzE,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAY9C;;;qEAGiE;IAC3D,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmB1F;;sFAEkF;IAC5E,yBAAyB,IAAI,OAAO,CAAC;QACzC,gBAAgB,EAAE,MAAM,CAAC;QACzB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,iBAAiB,EAAE,MAAM,EAAE,CAAC;KAC7B,CAAC;IAwFF,gFAAgF;IAC1E,MAAM,IAAI,OAAO,CAAC;QACtB,QAAQ,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAC;QAClE,QAAQ,EAAE;YAAE,gBAAgB,EAAE,MAAM,CAAC;YAAC,kBAAkB,EAAE,MAAM,CAAC;YAAC,iBAAiB,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;QAChG,SAAS,EAAE;YAAE,iBAAiB,EAAE,MAAM,EAAE,CAAC;YAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC;YAAC,iBAAiB,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;KACrG,CAAC;IAUF,0FAA0F;IACpF,iBAAiB,IAAI,OAAO,CAAC;QAAE,iBAAiB,EAAE,MAAM,EAAE,CAAC;QAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC;QAAC,iBAAiB,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IA4B5H,OAAO,CAAC,iBAAiB;IAoBzB,OAAO,CAAC,mBAAmB;IAoBrB,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAOvC;;;wFAGoF;IAC9E,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAe7D,aAAa,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAMjE,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,gBAAgB,KAAK,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAY7F,kBAAkB,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,oBAAoB,KAAK,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAM7G,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAO5C,YAAY,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAS7D,kGAAkG;YACpF,eAAe;IAsB7B,oFAAoF;IAC9E,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAW5C,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzD,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9C;;6DAEyD;IACnD,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAgBpF,8EAA8E;IACxE,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIvD;8EAC0E;IACpE,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnE;;wCAEoC;IACpC,OAAO,CAAC,iBAAiB;IAIzB;;kFAE8E;IACxE,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrD;;;;;;yFAMqF;IAC/E,uBAAuB,IAAI,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAyBlF;;;;;wFAKoF;IAC9E,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB1E;sFACkF;IAC5E,YAAY,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAoB9C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWjD,sFAAsF;IAChF,aAAa,IAAI,OAAO,CAAC,aAAa,CAAC;IAW7C,iEAAiE;IAC3D,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IA0BzC,qDAAqD;YACvC,aAAa;CAS5B"}
|
package/dist/plan-store.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { access, copyFile, mkdir, readdir, readFile, rename,
|
|
1
|
+
import { access, copyFile, mkdir, readdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { CodebaseProfileSchema, FeatureSchema, FeaturesDocumentSchema, ManifestSchema, PhaseSchema, ProjectSchema, RequirementsDocumentSchema, ResumeFocusSchema, ActivityLogSchema, } from "./schema.js";
|
|
4
4
|
import { createFeatureId, createPhaseId, createRequirementId, createShortId, createTaskId, formatPhaseRef, isLegacyPhaseId } from "./naming.js";
|
|
@@ -338,7 +338,7 @@ export class PlanStore {
|
|
|
338
338
|
};
|
|
339
339
|
}
|
|
340
340
|
async ensureStructureOrdering() {
|
|
341
|
-
|
|
341
|
+
const result = await this.runAsBatch(async () => {
|
|
342
342
|
const featuresDoc = await this.loadFeatures();
|
|
343
343
|
const phases = await this.loadAllPhases();
|
|
344
344
|
const normalized = this.normalizeStructureSnapshot(featuresDoc, phases);
|
|
@@ -350,6 +350,10 @@ export class PlanStore {
|
|
|
350
350
|
}
|
|
351
351
|
return { changed: true };
|
|
352
352
|
});
|
|
353
|
+
// One-time import of a legacy file-based HANDOFF.md (pre-F004) into the
|
|
354
|
+
// entity-scoped phase.handoff. Idempotent — renames the file to .bak.
|
|
355
|
+
await this.importLegacyHandoffFile().catch(() => { });
|
|
356
|
+
return result;
|
|
353
357
|
}
|
|
354
358
|
// ── Path helpers ─────────────────────────────────────────────────────
|
|
355
359
|
manifestPath() {
|
|
@@ -411,9 +415,6 @@ export class PlanStore {
|
|
|
411
415
|
activityPath() {
|
|
412
416
|
return join(this.root, "activity.json");
|
|
413
417
|
}
|
|
414
|
-
handoffPath() {
|
|
415
|
-
return join(this.root, "HANDOFF.md");
|
|
416
|
-
}
|
|
417
418
|
// ── Init ─────────────────────────────────────────────────────────────
|
|
418
419
|
async init(projectName) {
|
|
419
420
|
if (await this.exists()) {
|
|
@@ -495,6 +496,7 @@ export class PlanStore {
|
|
|
495
496
|
"resume.json",
|
|
496
497
|
"resume.*.json",
|
|
497
498
|
"generated/",
|
|
499
|
+
"handoff-archive/",
|
|
498
500
|
"",
|
|
499
501
|
].join("\n"), "utf-8");
|
|
500
502
|
}
|
|
@@ -643,44 +645,6 @@ export class PlanStore {
|
|
|
643
645
|
return { entries: [] };
|
|
644
646
|
}
|
|
645
647
|
}
|
|
646
|
-
async handoffExists() {
|
|
647
|
-
try {
|
|
648
|
-
await access(this.handoffPath());
|
|
649
|
-
return true;
|
|
650
|
-
}
|
|
651
|
-
catch {
|
|
652
|
-
return false;
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
async loadHandoff() {
|
|
656
|
-
try {
|
|
657
|
-
const [content, info] = await Promise.all([
|
|
658
|
-
readFile(this.handoffPath(), "utf-8"),
|
|
659
|
-
stat(this.handoffPath()),
|
|
660
|
-
]);
|
|
661
|
-
const createdAt = content.match(/^Created at:\s*(.+)$/m)?.[1]?.trim() ?? info.birthtime.toISOString();
|
|
662
|
-
const updatedAt = content.match(/^Updated at:\s*(.+)$/m)?.[1]?.trim() ?? info.mtime.toISOString();
|
|
663
|
-
return {
|
|
664
|
-
content,
|
|
665
|
-
createdAt,
|
|
666
|
-
updatedAt,
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
catch {
|
|
670
|
-
return null;
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
async saveHandoff(content) {
|
|
674
|
-
await atomicWriteText(this.handoffPath(), content);
|
|
675
|
-
await this.touchManifest();
|
|
676
|
-
}
|
|
677
|
-
async deleteHandoff() {
|
|
678
|
-
try {
|
|
679
|
-
await unlink(this.handoffPath());
|
|
680
|
-
}
|
|
681
|
-
catch { }
|
|
682
|
-
await this.touchManifest();
|
|
683
|
-
}
|
|
684
648
|
async appendActivity(type, ref, summary) {
|
|
685
649
|
const log = await this.loadActivityLog();
|
|
686
650
|
const id = `act-${log.entries.length + 1}-${type}`;
|
|
@@ -1128,7 +1092,7 @@ export class PlanStore {
|
|
|
1128
1092
|
const phase = await this.loadPhase(phaseId);
|
|
1129
1093
|
let cleared = null;
|
|
1130
1094
|
if (phase.status === "done" && phase.handoff !== "") {
|
|
1131
|
-
await this.
|
|
1095
|
+
await this.clearPhaseHandoff(phaseId, "phase-done");
|
|
1132
1096
|
const features = await this.loadFeatures();
|
|
1133
1097
|
const feature = features.features.find((f) => f.id === phase.featureId);
|
|
1134
1098
|
cleared = formatPhaseRef(phase.number, feature?.number);
|
|
@@ -1246,10 +1210,76 @@ export class PlanStore {
|
|
|
1246
1210
|
const now = new Date().toISOString();
|
|
1247
1211
|
await this.updatePhase(phaseId, (phase) => ({ ...phase, handoff: text, handoffUpdatedAt: now }));
|
|
1248
1212
|
}
|
|
1249
|
-
/**
|
|
1250
|
-
*
|
|
1251
|
-
|
|
1252
|
-
|
|
1213
|
+
/** Directory where cleared handoff content is archived as .md files
|
|
1214
|
+
* (gitignored). Keeps the phase JSON lean while making past handoffs
|
|
1215
|
+
* recoverable + human-readable. */
|
|
1216
|
+
handoffArchiveDir() {
|
|
1217
|
+
return join(this.root, "handoff-archive");
|
|
1218
|
+
}
|
|
1219
|
+
/** Mark the phase handoff as read/acknowledged on recap (sets handoffReadAt).
|
|
1220
|
+
* Does NOT clear the handoff — content is kept until a task starts or the
|
|
1221
|
+
* phase completes, so a restart between read and resume does not lose it. */
|
|
1222
|
+
async markHandoffRead(phaseId) {
|
|
1223
|
+
await this.updatePhase(phaseId, (phase) => ({ ...phase, handoffReadAt: nowISO() }));
|
|
1224
|
+
}
|
|
1225
|
+
/** One-time import of a legacy .planner/HANDOFF.md file (file-based handoff
|
|
1226
|
+
* from before F004) into the entity-scoped phase.handoff. Idempotent: if the
|
|
1227
|
+
* file is absent or empty, no-op. If it exists + non-empty + the target phase
|
|
1228
|
+
* has no handoff, writes the content onto the current in-progress phase (or
|
|
1229
|
+
* the first phase if none in-progress) with an "imported" handoffHistory entry,
|
|
1230
|
+
* then renames the file to HANDOFF.md.bak so it won't re-import. If the target
|
|
1231
|
+
* already has a handoff, the entity-scoped one wins and the file is just .bak'd. */
|
|
1232
|
+
async importLegacyHandoffFile() {
|
|
1233
|
+
const filePath = join(this.root, "HANDOFF.md");
|
|
1234
|
+
const content = await readFile(filePath, "utf-8").catch(() => null);
|
|
1235
|
+
if (content === null)
|
|
1236
|
+
return { imported: false };
|
|
1237
|
+
if (content.trim() === "") {
|
|
1238
|
+
await rename(filePath, filePath + ".bak").catch(() => { });
|
|
1239
|
+
return { imported: false };
|
|
1240
|
+
}
|
|
1241
|
+
const phases = await this.loadAllPhases();
|
|
1242
|
+
const target = phases.find((p) => p.status === "in-progress") ?? phases[0] ?? null;
|
|
1243
|
+
if (!target)
|
|
1244
|
+
return { imported: false }; // no phases yet — leave file for a later run
|
|
1245
|
+
if ((target.handoff ?? "") === "") {
|
|
1246
|
+
await this.setPhaseHandoff(target.id, content + "\n\n<!-- imported from legacy .planner/HANDOFF.md -->\n");
|
|
1247
|
+
await this.updatePhase(target.id, (p) => ({
|
|
1248
|
+
...p,
|
|
1249
|
+
handoffHistory: [{ file: "(legacy HANDOFF.md)", clearedAt: nowISO(), reason: "imported" }, ...(p.handoffHistory ?? [])].slice(0, 5),
|
|
1250
|
+
}));
|
|
1251
|
+
}
|
|
1252
|
+
await rename(filePath, filePath + ".bak").catch(() => { });
|
|
1253
|
+
const features = await this.loadFeatures();
|
|
1254
|
+
const feat = features.features.find((f) => f.id === target.featureId);
|
|
1255
|
+
return { imported: true, phaseRef: formatPhaseRef(target.number, feat?.number) };
|
|
1256
|
+
}
|
|
1257
|
+
/** Clear the handoff for a phase, archiving its content first. The handoff
|
|
1258
|
+
* markdown is written to .planner/handoff-archive/<phaseId>-<ISO>.md and a
|
|
1259
|
+
* metadata entry { file, clearedAt, reason } is prepended to handoffHistory
|
|
1260
|
+
* (capped at 5; oldest file is deleted when trimmed). handoffUpdatedAt is
|
|
1261
|
+
* left unchanged as an audit trail. If the handoff is empty, this is a no-op.
|
|
1262
|
+
* reason: "task-started" | "phase-done" | "manual" | "superseded" | "imported". */
|
|
1263
|
+
async clearPhaseHandoff(phaseId, reason = "manual") {
|
|
1264
|
+
const phase = await this.loadPhase(phaseId).catch(() => null);
|
|
1265
|
+
if (!phase || phase.handoff === "")
|
|
1266
|
+
return; // nothing to archive
|
|
1267
|
+
const clearedAt = nowISO();
|
|
1268
|
+
const safeTs = clearedAt.replace(/[:.]/g, "-");
|
|
1269
|
+
const archiveDir = this.handoffArchiveDir();
|
|
1270
|
+
await mkdir(archiveDir, { recursive: true }).catch(() => { });
|
|
1271
|
+
const fileName = `${phaseId}-${safeTs}.md`;
|
|
1272
|
+
const filePath = join(archiveDir, fileName);
|
|
1273
|
+
await atomicWriteText(filePath, phase.handoff);
|
|
1274
|
+
const entry = { file: `handoff-archive/${fileName}`, clearedAt, reason };
|
|
1275
|
+
// Cap history at 5: prepend new entry, drop oldest (and delete its file).
|
|
1276
|
+
const trimmed = [entry, ...(phase.handoffHistory ?? [])].slice(0, 5);
|
|
1277
|
+
const dropped = (phase.handoffHistory ?? []).slice(4); // entries beyond index 4 after prepend
|
|
1278
|
+
for (const d of dropped) {
|
|
1279
|
+
if (d?.file)
|
|
1280
|
+
await unlink(join(this.root, d.file)).catch(() => { });
|
|
1281
|
+
}
|
|
1282
|
+
await this.updatePhase(phaseId, (p) => ({ ...p, handoff: "", handoffHistory: trimmed }));
|
|
1253
1283
|
}
|
|
1254
1284
|
/** List all phases that have a non-empty handoff, newest first, with a
|
|
1255
1285
|
* human-readable composite ref (P00x or P00x(F00x)) and a first-line excerpt. */
|
package/dist/schema.d.ts
CHANGED
|
@@ -719,6 +719,21 @@ export declare const TaskSchema: z.ZodEffects<z.ZodObject<{
|
|
|
719
719
|
startedAt?: string | undefined;
|
|
720
720
|
completedAt?: string | undefined;
|
|
721
721
|
}>;
|
|
722
|
+
export declare const HandoffHistoryEntrySchema: z.ZodObject<{
|
|
723
|
+
/** Relative path under .planner/handoff-archive/ (e.g. <phaseId>-<ISO>.md). */
|
|
724
|
+
file: z.ZodDefault<z.ZodString>;
|
|
725
|
+
clearedAt: z.ZodString;
|
|
726
|
+
/** Why the handoff was cleared: "task-started" | "phase-done" | "manual" | "superseded" | "imported". */
|
|
727
|
+
reason: z.ZodDefault<z.ZodString>;
|
|
728
|
+
}, "strip", z.ZodTypeAny, {
|
|
729
|
+
file: string;
|
|
730
|
+
clearedAt: string;
|
|
731
|
+
reason: string;
|
|
732
|
+
}, {
|
|
733
|
+
clearedAt: string;
|
|
734
|
+
file?: string | undefined;
|
|
735
|
+
reason?: string | undefined;
|
|
736
|
+
}>;
|
|
722
737
|
export declare const PhaseSchema: z.ZodObject<{
|
|
723
738
|
id: z.ZodString;
|
|
724
739
|
featureId: z.ZodOptional<z.ZodString>;
|
|
@@ -1049,6 +1064,30 @@ export declare const PhaseSchema: z.ZodObject<{
|
|
|
1049
1064
|
updatedAt: z.ZodString;
|
|
1050
1065
|
handoff: z.ZodDefault<z.ZodString>;
|
|
1051
1066
|
handoffUpdatedAt: z.ZodDefault<z.ZodString>;
|
|
1067
|
+
/** When the handoff was last read/acknowledged on recap (ISO). Non-empty means
|
|
1068
|
+
* the agent has seen it; the recap won't re-prompt every turn. The handoff
|
|
1069
|
+
* content is KEPT until a task in the phase starts (auto-clear) or the phase
|
|
1070
|
+
* completes — so a restart between read and resume does not lose context. */
|
|
1071
|
+
handoffReadAt: z.ZodDefault<z.ZodString>;
|
|
1072
|
+
/** Metadata for recently-cleared handoffs (newest-first, capped at 5). The
|
|
1073
|
+
* full content lives as .md files in .planner/handoff-archive/ (gitignored);
|
|
1074
|
+
* this array only holds the pointer + clear reason + timestamp, so the phase
|
|
1075
|
+
* JSON stays lean and the archive is recoverable. */
|
|
1076
|
+
handoffHistory: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
1077
|
+
/** Relative path under .planner/handoff-archive/ (e.g. <phaseId>-<ISO>.md). */
|
|
1078
|
+
file: z.ZodDefault<z.ZodString>;
|
|
1079
|
+
clearedAt: z.ZodString;
|
|
1080
|
+
/** Why the handoff was cleared: "task-started" | "phase-done" | "manual" | "superseded" | "imported". */
|
|
1081
|
+
reason: z.ZodDefault<z.ZodString>;
|
|
1082
|
+
}, "strip", z.ZodTypeAny, {
|
|
1083
|
+
file: string;
|
|
1084
|
+
clearedAt: string;
|
|
1085
|
+
reason: string;
|
|
1086
|
+
}, {
|
|
1087
|
+
clearedAt: string;
|
|
1088
|
+
file?: string | undefined;
|
|
1089
|
+
reason?: string | undefined;
|
|
1090
|
+
}>, "many">>;
|
|
1052
1091
|
}, "strip", z.ZodTypeAny, {
|
|
1053
1092
|
number: number;
|
|
1054
1093
|
dependencies: string[];
|
|
@@ -1130,6 +1169,12 @@ export declare const PhaseSchema: z.ZodObject<{
|
|
|
1130
1169
|
}[];
|
|
1131
1170
|
handoff: string;
|
|
1132
1171
|
handoffUpdatedAt: string;
|
|
1172
|
+
handoffReadAt: string;
|
|
1173
|
+
handoffHistory: {
|
|
1174
|
+
file: string;
|
|
1175
|
+
clearedAt: string;
|
|
1176
|
+
reason: string;
|
|
1177
|
+
}[];
|
|
1133
1178
|
featureId?: string | undefined;
|
|
1134
1179
|
}, {
|
|
1135
1180
|
number: number;
|
|
@@ -1213,6 +1258,12 @@ export declare const PhaseSchema: z.ZodObject<{
|
|
|
1213
1258
|
}[] | undefined;
|
|
1214
1259
|
handoff?: string | undefined;
|
|
1215
1260
|
handoffUpdatedAt?: string | undefined;
|
|
1261
|
+
handoffReadAt?: string | undefined;
|
|
1262
|
+
handoffHistory?: {
|
|
1263
|
+
clearedAt: string;
|
|
1264
|
+
file?: string | undefined;
|
|
1265
|
+
reason?: string | undefined;
|
|
1266
|
+
}[] | undefined;
|
|
1216
1267
|
}>;
|
|
1217
1268
|
export declare const FeatureSchema: z.ZodObject<{
|
|
1218
1269
|
id: z.ZodString;
|
|
@@ -2347,6 +2398,30 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
|
|
|
2347
2398
|
updatedAt: z.ZodString;
|
|
2348
2399
|
handoff: z.ZodDefault<z.ZodString>;
|
|
2349
2400
|
handoffUpdatedAt: z.ZodDefault<z.ZodString>;
|
|
2401
|
+
/** When the handoff was last read/acknowledged on recap (ISO). Non-empty means
|
|
2402
|
+
* the agent has seen it; the recap won't re-prompt every turn. The handoff
|
|
2403
|
+
* content is KEPT until a task in the phase starts (auto-clear) or the phase
|
|
2404
|
+
* completes — so a restart between read and resume does not lose context. */
|
|
2405
|
+
handoffReadAt: z.ZodDefault<z.ZodString>;
|
|
2406
|
+
/** Metadata for recently-cleared handoffs (newest-first, capped at 5). The
|
|
2407
|
+
* full content lives as .md files in .planner/handoff-archive/ (gitignored);
|
|
2408
|
+
* this array only holds the pointer + clear reason + timestamp, so the phase
|
|
2409
|
+
* JSON stays lean and the archive is recoverable. */
|
|
2410
|
+
handoffHistory: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
2411
|
+
/** Relative path under .planner/handoff-archive/ (e.g. <phaseId>-<ISO>.md). */
|
|
2412
|
+
file: z.ZodDefault<z.ZodString>;
|
|
2413
|
+
clearedAt: z.ZodString;
|
|
2414
|
+
/** Why the handoff was cleared: "task-started" | "phase-done" | "manual" | "superseded" | "imported". */
|
|
2415
|
+
reason: z.ZodDefault<z.ZodString>;
|
|
2416
|
+
}, "strip", z.ZodTypeAny, {
|
|
2417
|
+
file: string;
|
|
2418
|
+
clearedAt: string;
|
|
2419
|
+
reason: string;
|
|
2420
|
+
}, {
|
|
2421
|
+
clearedAt: string;
|
|
2422
|
+
file?: string | undefined;
|
|
2423
|
+
reason?: string | undefined;
|
|
2424
|
+
}>, "many">>;
|
|
2350
2425
|
}, "strip", z.ZodTypeAny, {
|
|
2351
2426
|
number: number;
|
|
2352
2427
|
dependencies: string[];
|
|
@@ -2428,6 +2503,12 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
|
|
|
2428
2503
|
}[];
|
|
2429
2504
|
handoff: string;
|
|
2430
2505
|
handoffUpdatedAt: string;
|
|
2506
|
+
handoffReadAt: string;
|
|
2507
|
+
handoffHistory: {
|
|
2508
|
+
file: string;
|
|
2509
|
+
clearedAt: string;
|
|
2510
|
+
reason: string;
|
|
2511
|
+
}[];
|
|
2431
2512
|
featureId?: string | undefined;
|
|
2432
2513
|
}, {
|
|
2433
2514
|
number: number;
|
|
@@ -2511,6 +2592,12 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
|
|
|
2511
2592
|
}[] | undefined;
|
|
2512
2593
|
handoff?: string | undefined;
|
|
2513
2594
|
handoffUpdatedAt?: string | undefined;
|
|
2595
|
+
handoffReadAt?: string | undefined;
|
|
2596
|
+
handoffHistory?: {
|
|
2597
|
+
clearedAt: string;
|
|
2598
|
+
file?: string | undefined;
|
|
2599
|
+
reason?: string | undefined;
|
|
2600
|
+
}[] | undefined;
|
|
2514
2601
|
}>, "many">;
|
|
2515
2602
|
}, "strip", z.ZodTypeAny, {
|
|
2516
2603
|
features: {
|
|
@@ -2676,6 +2763,12 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
|
|
|
2676
2763
|
}[];
|
|
2677
2764
|
handoff: string;
|
|
2678
2765
|
handoffUpdatedAt: string;
|
|
2766
|
+
handoffReadAt: string;
|
|
2767
|
+
handoffHistory: {
|
|
2768
|
+
file: string;
|
|
2769
|
+
clearedAt: string;
|
|
2770
|
+
reason: string;
|
|
2771
|
+
}[];
|
|
2679
2772
|
featureId?: string | undefined;
|
|
2680
2773
|
}[];
|
|
2681
2774
|
}, {
|
|
@@ -2843,6 +2936,12 @@ export declare const PlanWorkspaceSchema: z.ZodObject<{
|
|
|
2843
2936
|
}[] | undefined;
|
|
2844
2937
|
handoff?: string | undefined;
|
|
2845
2938
|
handoffUpdatedAt?: string | undefined;
|
|
2939
|
+
handoffReadAt?: string | undefined;
|
|
2940
|
+
handoffHistory?: {
|
|
2941
|
+
clearedAt: string;
|
|
2942
|
+
file?: string | undefined;
|
|
2943
|
+
reason?: string | undefined;
|
|
2944
|
+
}[] | undefined;
|
|
2846
2945
|
}[];
|
|
2847
2946
|
}>;
|
|
2848
2947
|
export type Timestamp = z.infer<typeof TimestampSchema>;
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,eAAe,aAAwB,CAAC;AACrD,eAAO,MAAM,UAAU,aAAiD,CAAC;AAEzE,eAAO,MAAM,kBAAkB;;;;;;;;;EAG7B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;EAK7B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmBhC,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS5B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;EAM9B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAE5B,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;EAMzB,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;EAI9B,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;EAOjC,CAAC;AAEH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAexB,CAAC;AAEH,eAAO,MAAM,mBAAmB,yGAAuG,CAAC;AACxI,eAAO,MAAM,gBAAgB,yGAAuG,CAAC;AACrI,eAAO,MAAM,iBAAiB,+HAA6H,CAAC;AAC5J,eAAO,MAAM,uBAAuB,yGAAuG,CAAC;AAC5I,eAAO,MAAM,mBAAmB,yGAAuG,CAAC;AAExI,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;EAOxB,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;EAI9B,CAAC;AAIH,wEAAwE;AACxE,eAAO,MAAM,8BAA8B,aAEzC,CAAC;AAEH;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK7E;AAED,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;EAO/B,CAAC;AAEH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BpB,CAAC;AAEJ,eAAO,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.js
CHANGED
|
@@ -163,6 +163,13 @@ export const TaskSchema = z.object({
|
|
|
163
163
|
: { ...item, id: item.id || createChecklistItemId(task.id, index + 1, item.title), title: item.title.trim(), checked: item.checked ?? false })
|
|
164
164
|
.filter((item) => item.title.length > 0),
|
|
165
165
|
}));
|
|
166
|
+
export const HandoffHistoryEntrySchema = z.object({
|
|
167
|
+
/** Relative path under .planner/handoff-archive/ (e.g. <phaseId>-<ISO>.md). */
|
|
168
|
+
file: z.string().default(""),
|
|
169
|
+
clearedAt: TimestampSchema,
|
|
170
|
+
/** Why the handoff was cleared: "task-started" | "phase-done" | "manual" | "superseded" | "imported". */
|
|
171
|
+
reason: z.string().default(""),
|
|
172
|
+
});
|
|
166
173
|
export const PhaseSchema = z.object({
|
|
167
174
|
id: z.string(),
|
|
168
175
|
featureId: z.string().optional(),
|
|
@@ -195,6 +202,16 @@ export const PhaseSchema = z.object({
|
|
|
195
202
|
updatedAt: TimestampSchema,
|
|
196
203
|
handoff: z.string().default(""),
|
|
197
204
|
handoffUpdatedAt: z.string().default(""),
|
|
205
|
+
/** When the handoff was last read/acknowledged on recap (ISO). Non-empty means
|
|
206
|
+
* the agent has seen it; the recap won't re-prompt every turn. The handoff
|
|
207
|
+
* content is KEPT until a task in the phase starts (auto-clear) or the phase
|
|
208
|
+
* completes — so a restart between read and resume does not lose context. */
|
|
209
|
+
handoffReadAt: z.string().default(""),
|
|
210
|
+
/** Metadata for recently-cleared handoffs (newest-first, capped at 5). The
|
|
211
|
+
* full content lives as .md files in .planner/handoff-archive/ (gitignored);
|
|
212
|
+
* this array only holds the pointer + clear reason + timestamp, so the phase
|
|
213
|
+
* JSON stays lean and the archive is recoverable. */
|
|
214
|
+
handoffHistory: z.array(HandoffHistoryEntrySchema).default([]),
|
|
198
215
|
});
|
|
199
216
|
export const FeatureSchema = z.object({
|
|
200
217
|
id: z.string(),
|
package/package.json
CHANGED