poe-code 4.0.49 → 4.0.51
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/cli/commands/plan.js +52 -15
- package/dist/cli/commands/plan.js.map +1 -1
- package/dist/index.js +182 -68
- package/dist/index.js.map +2 -2
- package/dist/metafile.json +1 -1
- package/package.json +1 -1
- package/packages/plan-browser/dist/actions.d.ts +3 -0
- package/packages/plan-browser/dist/actions.js +20 -0
- package/packages/plan-browser/dist/browser.d.ts +1 -0
- package/packages/plan-browser/dist/browser.js +2 -0
- package/packages/plan-browser/dist/discovery.d.ts +1 -0
- package/packages/plan-browser/dist/discovery.js +48 -13
- package/packages/plan-browser/dist/explorer-config.d.ts +1 -0
- package/packages/plan-browser/dist/explorer-config.js +63 -39
- package/packages/plan-browser/dist/index.d.ts +1 -1
- package/packages/plan-browser/dist/index.js +1 -1
package/package.json
CHANGED
|
@@ -21,6 +21,9 @@ export declare function editPlan(absolutePath: string, options: {
|
|
|
21
21
|
declare function archiveSelectedPlan(entry: Pick<{
|
|
22
22
|
absolutePath: string;
|
|
23
23
|
}, "absolutePath">, fs: ActionFs): Promise<string>;
|
|
24
|
+
export declare function unarchivePlan(entry: Pick<{
|
|
25
|
+
absolutePath: string;
|
|
26
|
+
}, "absolutePath">, fs: ActionFs): Promise<string>;
|
|
24
27
|
export declare function savePlanForLater(entry: Pick<{
|
|
25
28
|
absolutePath: string;
|
|
26
29
|
format: PlanFormat;
|
|
@@ -102,6 +102,26 @@ async function archiveSelectedPlan(entry, fs) {
|
|
|
102
102
|
}
|
|
103
103
|
return archivedPath;
|
|
104
104
|
}
|
|
105
|
+
export async function unarchivePlan(entry, fs) {
|
|
106
|
+
rejectPlanMetaDocument(entry.absolutePath, "unarchive");
|
|
107
|
+
const archiveDir = path.dirname(entry.absolutePath);
|
|
108
|
+
if (path.basename(archiveDir) !== "archive") {
|
|
109
|
+
throw new Error(`Plan is not archived: ${entry.absolutePath}`);
|
|
110
|
+
}
|
|
111
|
+
await rejectSymbolicLink(archiveDir, fs, "unarchive plan through");
|
|
112
|
+
const destinationPath = path.join(path.dirname(archiveDir), path.basename(entry.absolutePath));
|
|
113
|
+
try {
|
|
114
|
+
await fs.readFile(destinationPath, "utf8");
|
|
115
|
+
throw new Error(`Unarchive destination already exists: ${destinationPath}`);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
if (!hasErrorCode(error, "ENOENT")) {
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
await fs.rename(entry.absolutePath, destinationPath);
|
|
123
|
+
return destinationPath;
|
|
124
|
+
}
|
|
105
125
|
export async function savePlanForLater(entry, fs, options = {}) {
|
|
106
126
|
const reason = entry.savedForLater?.reason?.trim() || options.reason?.trim();
|
|
107
127
|
if (!reason) {
|
|
@@ -8,6 +8,7 @@ export declare function runPlanBrowser(options: {
|
|
|
8
8
|
projectConfigPath: string;
|
|
9
9
|
fs: DiscoveryFs & Partial<ActionFs>;
|
|
10
10
|
kind?: PlanKind;
|
|
11
|
+
archived?: boolean;
|
|
11
12
|
variables?: Record<string, string | undefined>;
|
|
12
13
|
runExplorerImpl?: RunExplorerImpl;
|
|
13
14
|
}): Promise<void>;
|
|
@@ -9,6 +9,7 @@ export async function runPlanBrowser(options) {
|
|
|
9
9
|
projectConfigPath: options.projectConfigPath,
|
|
10
10
|
fs: options.fs,
|
|
11
11
|
kind: options.kind,
|
|
12
|
+
archived: options.archived,
|
|
12
13
|
variables: options.variables
|
|
13
14
|
});
|
|
14
15
|
const plans = await discover();
|
|
@@ -26,6 +27,7 @@ export async function runPlanBrowser(options) {
|
|
|
26
27
|
}
|
|
27
28
|
const config = buildPlanExplorerConfig({
|
|
28
29
|
plans,
|
|
30
|
+
archived: options.archived,
|
|
29
31
|
fs: options.fs,
|
|
30
32
|
variables: options.variables ?? process.env,
|
|
31
33
|
homeDir: options.homeDir,
|
|
@@ -108,7 +108,7 @@ function toPlanKind(value, filePath) {
|
|
|
108
108
|
}
|
|
109
109
|
throw new Error(`${filePath}: unsupported frontmatter kind ${JSON.stringify(value)}`);
|
|
110
110
|
}
|
|
111
|
-
function classifyPlanKind(content, filePath) {
|
|
111
|
+
function classifyPlanKind(content, filePath, archived = false) {
|
|
112
112
|
if (isYamlPlanFile(filePath)) {
|
|
113
113
|
parsePlan(content);
|
|
114
114
|
return "pipeline";
|
|
@@ -117,7 +117,19 @@ function classifyPlanKind(content, filePath) {
|
|
|
117
117
|
if (data === undefined) {
|
|
118
118
|
return "plan";
|
|
119
119
|
}
|
|
120
|
-
|
|
120
|
+
if (data.kind === undefined)
|
|
121
|
+
return "plan";
|
|
122
|
+
if (archived && data.kind === "archived-pipeline-plan")
|
|
123
|
+
return "plan";
|
|
124
|
+
if (archived) {
|
|
125
|
+
try {
|
|
126
|
+
return toPlanKind(data.kind, filePath);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return "plan";
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return toPlanKind(data.kind, filePath);
|
|
121
133
|
}
|
|
122
134
|
function readPlanReadiness(content, filePath) {
|
|
123
135
|
const value = splitFrontmatter(content, filePath).data?.readiness;
|
|
@@ -142,6 +154,14 @@ async function discoverSharedPlans(options) {
|
|
|
142
154
|
if (canonicalDir !== path.resolve(absoluteDir)) {
|
|
143
155
|
throw new Error(`Plan directory must not be a symbolic link: ${displayDir}`);
|
|
144
156
|
}
|
|
157
|
+
if (options.archived === true) {
|
|
158
|
+
return discoverPlanDirectoryEntries({
|
|
159
|
+
...options,
|
|
160
|
+
absoluteDir: path.join(absoluteDir, "archive"),
|
|
161
|
+
displayDir: path.join(displayDir, "archive"),
|
|
162
|
+
savedForLaterDirectory: false
|
|
163
|
+
});
|
|
164
|
+
}
|
|
145
165
|
const activePlans = await discoverPlanDirectoryEntries({
|
|
146
166
|
...options,
|
|
147
167
|
absoluteDir,
|
|
@@ -199,20 +219,35 @@ async function discoverPlanDirectoryEntries(options) {
|
|
|
199
219
|
}
|
|
200
220
|
const displayPath = path.join(options.displayDir, name);
|
|
201
221
|
const content = await options.fs.readFile(absolutePath, "utf8");
|
|
202
|
-
|
|
222
|
+
let kind = classifyPlanKind(content, displayPath, options.archived);
|
|
223
|
+
const savedForLater = options.savedForLaterDirectory
|
|
224
|
+
? (readSavedForLaterMetadata(content, displayPath) ?? {})
|
|
225
|
+
: readSavedForLaterMetadata(content, displayPath);
|
|
226
|
+
let metadata;
|
|
227
|
+
try {
|
|
228
|
+
metadata = await readPlanMetadata({
|
|
229
|
+
kind,
|
|
230
|
+
absolutePath,
|
|
231
|
+
path: displayPath,
|
|
232
|
+
fs: options.fs,
|
|
233
|
+
content
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
if (!options.archived || kind === "plan")
|
|
238
|
+
throw error;
|
|
239
|
+
kind = "plan";
|
|
240
|
+
metadata = await readPlanMetadata({
|
|
241
|
+
kind,
|
|
242
|
+
absolutePath,
|
|
243
|
+
path: displayPath,
|
|
244
|
+
fs: options.fs,
|
|
245
|
+
content
|
|
246
|
+
});
|
|
247
|
+
}
|
|
203
248
|
if (options.kind && kind !== options.kind) {
|
|
204
249
|
continue;
|
|
205
250
|
}
|
|
206
|
-
const savedForLater = options.savedForLaterDirectory
|
|
207
|
-
? readSavedForLaterMetadata(content, displayPath) ?? {}
|
|
208
|
-
: readSavedForLaterMetadata(content, displayPath);
|
|
209
|
-
const metadata = await readPlanMetadata({
|
|
210
|
-
kind,
|
|
211
|
-
absolutePath,
|
|
212
|
-
path: displayPath,
|
|
213
|
-
fs: options.fs,
|
|
214
|
-
content
|
|
215
|
-
});
|
|
216
251
|
plans.push({
|
|
217
252
|
path: displayPath,
|
|
218
253
|
absolutePath,
|
|
@@ -2,6 +2,7 @@ import type { ExplorerConfig } from "../../toolcraft-design/dist/index.js";
|
|
|
2
2
|
import type { ActionFs, DiscoveryFs, PlanEntry } from "./types.js";
|
|
3
3
|
export interface BuildPlanExplorerConfigOptions {
|
|
4
4
|
plans: PlanEntry[];
|
|
5
|
+
archived?: boolean;
|
|
5
6
|
fs: ActionFs & DiscoveryFs;
|
|
6
7
|
variables: Record<string, string | undefined>;
|
|
7
8
|
homeDir?: string;
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { formatPlanReadinessLabel } from "@poe-code/agent-harness-tools";
|
|
3
3
|
import { normalizeExplorerConfig } from "toolcraft-design";
|
|
4
|
-
import { archivePlan, deletePlan, editFile, restorePlanFromLater, savePlanForLater, setPlanReadiness } from "./actions.js";
|
|
4
|
+
import { archivePlan, deletePlan, editFile, restorePlanFromLater, savePlanForLater, setPlanReadiness, unarchivePlan } from "./actions.js";
|
|
5
5
|
import { loadPlanPreviewMarkdown } from "./format.js";
|
|
6
6
|
export function buildPlanExplorerConfig(options) {
|
|
7
7
|
const loadDetailMarkdown = options.loadDetailMarkdown ?? loadPlanPreviewMarkdown;
|
|
8
8
|
const promptSaveReason = options.promptSaveReason;
|
|
9
9
|
let plans = options.plans;
|
|
10
|
-
let rows = toRows(plans);
|
|
10
|
+
let rows = toRows(plans, options.archived);
|
|
11
11
|
let entryByRowId = toEntryMap(plans);
|
|
12
12
|
let preserveOrderOnNextRefresh = false;
|
|
13
13
|
async function refresh() {
|
|
14
14
|
const refreshedPlans = await options.onRefresh();
|
|
15
15
|
plans = preserveOrderOnNextRefresh ? preservePlanOrder(plans, refreshedPlans) : refreshedPlans;
|
|
16
16
|
preserveOrderOnNextRefresh = false;
|
|
17
|
-
rows = toRows(plans);
|
|
17
|
+
rows = toRows(plans, options.archived);
|
|
18
18
|
entryByRowId = toEntryMap(plans);
|
|
19
19
|
}
|
|
20
20
|
const actions = [
|
|
@@ -108,6 +108,22 @@ export function buildPlanExplorerConfig(options) {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
},
|
|
111
|
+
{
|
|
112
|
+
id: "unarchive",
|
|
113
|
+
accelerator: "a",
|
|
114
|
+
label: "Unarchive",
|
|
115
|
+
handler: async (ctx) => {
|
|
116
|
+
const entry = getEntry(entryByRowId, ctx.row.id);
|
|
117
|
+
await unarchivePlan(entry, options.fs);
|
|
118
|
+
try {
|
|
119
|
+
await ctx.refresh();
|
|
120
|
+
ctx.toast(`Unarchived ${path.basename(entry.path)}`, "info");
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
ctx.toast(`Unarchived ${path.basename(entry.path)}; refresh failed`, "info");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
111
127
|
{
|
|
112
128
|
id: "delete",
|
|
113
129
|
accelerator: "x",
|
|
@@ -154,41 +170,49 @@ export function buildPlanExplorerConfig(options) {
|
|
|
154
170
|
}
|
|
155
171
|
],
|
|
156
172
|
refresh,
|
|
157
|
-
actions
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
actions: options.archived
|
|
174
|
+
? actions.filter((action) => action.id === "edit" || action.id === "unarchive" || action.id === "delete")
|
|
175
|
+
: actions.filter((action) => action.id !== "unarchive"),
|
|
176
|
+
...(options.archived
|
|
177
|
+
? {}
|
|
178
|
+
: {
|
|
179
|
+
reorder: {
|
|
180
|
+
onReorder: async (orderedIds, ctx) => {
|
|
181
|
+
if (ctx === undefined) {
|
|
182
|
+
throw new Error("Plan reorder context is required");
|
|
183
|
+
}
|
|
184
|
+
const orderedEntries = orderedIds.map((id) => getEntry(entryByRowId, id));
|
|
185
|
+
const movedIndex = orderedIds.indexOf(ctx.movedId);
|
|
186
|
+
const originalIndex = createRowIds(plans).indexOf(ctx.movedId);
|
|
187
|
+
if (movedIndex < 0 || originalIndex < 0) {
|
|
188
|
+
throw new Error(`Plan row is no longer available: ${ctx.movedId}`);
|
|
189
|
+
}
|
|
190
|
+
const moved = orderedEntries[movedIndex];
|
|
191
|
+
const swapTarget = orderedEntries[originalIndex];
|
|
192
|
+
if (swapTarget === undefined || swapTarget.readiness !== moved.readiness) {
|
|
193
|
+
throw new Error("Plans can only be reordered within the same readiness group");
|
|
194
|
+
}
|
|
195
|
+
if (isSavedForLaterEntry(swapTarget) !== isSavedForLaterEntry(moved)) {
|
|
196
|
+
throw new Error("Active and saved-for-later plans cannot be reordered together");
|
|
197
|
+
}
|
|
198
|
+
const previousCandidate = orderedEntries[movedIndex - 1];
|
|
199
|
+
const nextCandidate = orderedEntries[movedIndex + 1];
|
|
200
|
+
const previous = sameOrderingGroup(previousCandidate, moved)
|
|
201
|
+
? previousCandidate
|
|
202
|
+
: undefined;
|
|
203
|
+
const next = sameOrderingGroup(nextCandidate, moved) ? nextCandidate : undefined;
|
|
204
|
+
const [movedStat, previousStat, nextStat] = await Promise.all([
|
|
205
|
+
options.fs.stat(moved.absolutePath),
|
|
206
|
+
previous === undefined ? undefined : options.fs.stat(previous.absolutePath),
|
|
207
|
+
next === undefined ? undefined : options.fs.stat(next.absolutePath)
|
|
208
|
+
]);
|
|
209
|
+
const updatedAt = resolveMovedTimestamp(previousStat?.mtimeMs, nextStat?.mtimeMs);
|
|
210
|
+
await options.fs.utimes(moved.absolutePath, new Date(movedStat.atimeMs ?? movedStat.mtimeMs), new Date(updatedAt));
|
|
211
|
+
await ctx.refresh();
|
|
212
|
+
ctx.toast(`Reordered ${path.basename(moved.path)}`, "info");
|
|
213
|
+
}
|
|
176
214
|
}
|
|
177
|
-
|
|
178
|
-
const nextCandidate = orderedEntries[movedIndex + 1];
|
|
179
|
-
const previous = sameOrderingGroup(previousCandidate, moved) ? previousCandidate : undefined;
|
|
180
|
-
const next = sameOrderingGroup(nextCandidate, moved) ? nextCandidate : undefined;
|
|
181
|
-
const [movedStat, previousStat, nextStat] = await Promise.all([
|
|
182
|
-
options.fs.stat(moved.absolutePath),
|
|
183
|
-
previous === undefined ? undefined : options.fs.stat(previous.absolutePath),
|
|
184
|
-
next === undefined ? undefined : options.fs.stat(next.absolutePath)
|
|
185
|
-
]);
|
|
186
|
-
const updatedAt = resolveMovedTimestamp(previousStat?.mtimeMs, nextStat?.mtimeMs);
|
|
187
|
-
await options.fs.utimes(moved.absolutePath, new Date(movedStat.atimeMs ?? movedStat.mtimeMs), new Date(updatedAt));
|
|
188
|
-
await ctx.refresh();
|
|
189
|
-
ctx.toast(`Reordered ${path.basename(moved.path)}`, "info");
|
|
190
|
-
}
|
|
191
|
-
},
|
|
215
|
+
}),
|
|
192
216
|
multiSelect: false,
|
|
193
217
|
emptyHint: "No plans found"
|
|
194
218
|
});
|
|
@@ -239,14 +263,14 @@ function abbreviateHome(filePath, homeDir) {
|
|
|
239
263
|
}
|
|
240
264
|
return `~${path.sep}${relative}`;
|
|
241
265
|
}
|
|
242
|
-
function toRows(plans) {
|
|
266
|
+
function toRows(plans, archived = false) {
|
|
243
267
|
const rowIds = createRowIds(plans);
|
|
244
268
|
return plans.map((entry, index) => ({
|
|
245
269
|
id: rowIds[index],
|
|
246
270
|
title: formatPlanReadinessLabel(path.basename(entry.path), entry.readiness),
|
|
247
271
|
subtitle: formatSubtitle(entry),
|
|
248
272
|
badge: { text: entry.typeLabel },
|
|
249
|
-
group: isSavedForLaterEntry(entry) ? "Saved for later" : "Active"
|
|
273
|
+
group: archived ? "Archived" : isSavedForLaterEntry(entry) ? "Saved for later" : "Active"
|
|
250
274
|
}));
|
|
251
275
|
}
|
|
252
276
|
function formatSubtitle(entry) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { discoverAllPlans } from "./discovery.js";
|
|
2
|
-
export { archivePlan, deletePlan, editFile, editPlan, resolveEditor, restorePlanFromLater, savePlanForLater, setPlanReadiness } from "./actions.js";
|
|
2
|
+
export { archivePlan, deletePlan, editFile, editPlan, resolveEditor, restorePlanFromLater, savePlanForLater, setPlanReadiness, unarchivePlan } from "./actions.js";
|
|
3
3
|
export { buildPlanExplorerConfig } from "./explorer-config.js";
|
|
4
4
|
export { deriveMarkdownTitle, formatExperimentDetail, formatPipelinePlanMarkdown, formatPipelineProgress, formatRalphDetail, formatSuperintendentDetail, getLastExperimentState, loadPlanPreviewMarkdown, readExperimentState, readPlanMetadata, readSavedForLaterMetadata, writeSavedForLaterReason } from "./format.js";
|
|
5
5
|
export { runPlanBrowser } from "./browser.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { discoverAllPlans } from "./discovery.js";
|
|
2
|
-
export { archivePlan, deletePlan, editFile, editPlan, resolveEditor, restorePlanFromLater, savePlanForLater, setPlanReadiness } from "./actions.js";
|
|
2
|
+
export { archivePlan, deletePlan, editFile, editPlan, resolveEditor, restorePlanFromLater, savePlanForLater, setPlanReadiness, unarchivePlan } from "./actions.js";
|
|
3
3
|
export { buildPlanExplorerConfig } from "./explorer-config.js";
|
|
4
4
|
export { deriveMarkdownTitle, formatExperimentDetail, formatPipelinePlanMarkdown, formatPipelineProgress, formatRalphDetail, formatSuperintendentDetail, getLastExperimentState, loadPlanPreviewMarkdown, readExperimentState, readPlanMetadata, readSavedForLaterMetadata, writeSavedForLaterReason } from "./format.js";
|
|
5
5
|
export { runPlanBrowser } from "./browser.js";
|