@nick848/fet 1.1.2 → 1.1.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/cli/index.js +183 -4
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2008,6 +2008,26 @@ async function proxyCommand(ctx, command, args) {
|
|
|
2008
2008
|
await artifactWorkflowCommand(ctx, command, args);
|
|
2009
2009
|
return;
|
|
2010
2010
|
}
|
|
2011
|
+
if (command === "apply") {
|
|
2012
|
+
await applyWorkflowCommand(ctx, args);
|
|
2013
|
+
return;
|
|
2014
|
+
}
|
|
2015
|
+
if (command === "explore") {
|
|
2016
|
+
await exploreWorkflowCommand(ctx, args);
|
|
2017
|
+
return;
|
|
2018
|
+
}
|
|
2019
|
+
if (command === "sync") {
|
|
2020
|
+
await syncWorkflowCommand(ctx, args);
|
|
2021
|
+
return;
|
|
2022
|
+
}
|
|
2023
|
+
if (command === "bulk-archive") {
|
|
2024
|
+
await bulkArchiveWorkflowCommand(ctx, args);
|
|
2025
|
+
return;
|
|
2026
|
+
}
|
|
2027
|
+
if (command === "onboard") {
|
|
2028
|
+
await onboardWorkflowCommand(ctx);
|
|
2029
|
+
return;
|
|
2030
|
+
}
|
|
2011
2031
|
const openSpecArgs = stripFetOptions(args);
|
|
2012
2032
|
const runState = {};
|
|
2013
2033
|
await withProjectLock(ctx.projectRoot, { command, cwd: ctx.cwd, fetVersion: ctx.fetVersion }, async () => {
|
|
@@ -2082,6 +2102,161 @@ async function proxyCommand(ctx, command, args) {
|
|
|
2082
2102
|
data: graphContext ? { graphContext } : void 0
|
|
2083
2103
|
});
|
|
2084
2104
|
}
|
|
2105
|
+
async function applyWorkflowCommand(ctx, args) {
|
|
2106
|
+
const changeId = await requireChangeId(ctx);
|
|
2107
|
+
const runState = {};
|
|
2108
|
+
await withProjectLock(ctx.projectRoot, { command: "apply", cwd: ctx.cwd, fetVersion: ctx.fetVersion }, async () => {
|
|
2109
|
+
await assertOpenSpecCommandSupported(ctx, "status", "apply");
|
|
2110
|
+
await assertOpenSpecCommandSupported(ctx, "instructions", "apply");
|
|
2111
|
+
runState.graphContext = await buildWorkflowGraphContext(ctx, {
|
|
2112
|
+
command: "apply",
|
|
2113
|
+
args: ["tasks", "--change", changeId],
|
|
2114
|
+
changeId
|
|
2115
|
+
});
|
|
2116
|
+
const status = await readOpenSpecStatus(ctx, changeId);
|
|
2117
|
+
const tasks = status.artifacts?.find((artifact) => artifact.id === "tasks");
|
|
2118
|
+
const warnings = tasks && tasks.status !== "complete" && tasks.status !== "ready" ? [`OpenSpec reports tasks artifact as "${tasks.status}". Finish planning artifacts before implementation if tasks.md is missing.`] : void 0;
|
|
2119
|
+
const instructions = await runInstructions(ctx, changeId, "tasks");
|
|
2120
|
+
await syncWorkflowState(ctx, "apply", changeId, {
|
|
2121
|
+
openSpecCommand: "instructions",
|
|
2122
|
+
openSpecArgs: ["tasks", "--change", changeId],
|
|
2123
|
+
exitCode: instructions.exitCode,
|
|
2124
|
+
phaseStatus: "in_progress"
|
|
2125
|
+
});
|
|
2126
|
+
ctx.output.result({
|
|
2127
|
+
ok: true,
|
|
2128
|
+
command: "apply",
|
|
2129
|
+
summary: `fet apply prepared implementation instructions for change "${changeId}".`,
|
|
2130
|
+
warnings: [...runState.graphContext?.warnings ?? [], ...warnings ?? []],
|
|
2131
|
+
nextSteps: [
|
|
2132
|
+
`Read openspec/changes/${changeId}/tasks.md and the instructions output.`,
|
|
2133
|
+
"Implement pending tasks and update task checkboxes only after the work is done.",
|
|
2134
|
+
`Run fet verify --change ${changeId}`
|
|
2135
|
+
],
|
|
2136
|
+
data: {
|
|
2137
|
+
changeId,
|
|
2138
|
+
instructions: instructions.data,
|
|
2139
|
+
status,
|
|
2140
|
+
graphContext: runState.graphContext
|
|
2141
|
+
}
|
|
2142
|
+
});
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
async function exploreWorkflowCommand(ctx, args) {
|
|
2146
|
+
const openSpecArgs = stripFetOptions(args);
|
|
2147
|
+
const changeId = ctx.changeId ?? (openSpecArgs[0] && isKebabId(openSpecArgs[0]) ? openSpecArgs[0] : null);
|
|
2148
|
+
const graphContext = await buildWorkflowGraphContext(ctx, {
|
|
2149
|
+
command: "explore",
|
|
2150
|
+
args: openSpecArgs,
|
|
2151
|
+
changeId
|
|
2152
|
+
});
|
|
2153
|
+
ctx.output.result({
|
|
2154
|
+
ok: true,
|
|
2155
|
+
command: "explore",
|
|
2156
|
+
summary: "fet explore is an IDE-guided workflow for shaping OpenSpec changes.",
|
|
2157
|
+
warnings: graphContext.warnings,
|
|
2158
|
+
nextSteps: [
|
|
2159
|
+
"Discuss the requirement, constraints, and acceptance criteria with the user.",
|
|
2160
|
+
changeId ? `Run fet continue --change ${changeId} when ready to create the next artifact.` : "Run fet propose <change-id-or-description> when ready to create a change."
|
|
2161
|
+
],
|
|
2162
|
+
data: { changeId, args: openSpecArgs, graphContext }
|
|
2163
|
+
});
|
|
2164
|
+
}
|
|
2165
|
+
async function syncWorkflowCommand(ctx, args) {
|
|
2166
|
+
const changeId = await requireChangeId(ctx);
|
|
2167
|
+
const openSpecArgs = stripFetOptions(args);
|
|
2168
|
+
await withProjectLock(ctx.projectRoot, { command: "sync", cwd: ctx.cwd, fetVersion: ctx.fetVersion }, async () => {
|
|
2169
|
+
await assertVerifiedChange(ctx, changeId);
|
|
2170
|
+
await assertOpenSpecCommandSupported(ctx, "validate", "sync");
|
|
2171
|
+
const validateArgs = [changeId, "--type", "change", ...openSpecArgs.filter((arg) => arg !== changeId)];
|
|
2172
|
+
const result = await ctx.openSpec.run("validate", validateArgs, { cwd: ctx.projectRoot, stdio: ctx.json ? "pipe" : "inherit" });
|
|
2173
|
+
if (result.exitCode !== 0) {
|
|
2174
|
+
throw new FetError({
|
|
2175
|
+
code: "OPENSPEC_COMMAND_FAILED" /* OpenSpecCommandFailed */,
|
|
2176
|
+
message: "OpenSpec validate failed during fet sync.",
|
|
2177
|
+
details: result,
|
|
2178
|
+
recoverable: true
|
|
2179
|
+
});
|
|
2180
|
+
}
|
|
2181
|
+
await syncWorkflowState(ctx, "sync", changeId, {
|
|
2182
|
+
openSpecCommand: "validate",
|
|
2183
|
+
openSpecArgs: validateArgs,
|
|
2184
|
+
exitCode: result.exitCode,
|
|
2185
|
+
phaseStatus: "done"
|
|
2186
|
+
});
|
|
2187
|
+
ctx.output.result({
|
|
2188
|
+
ok: true,
|
|
2189
|
+
command: "sync",
|
|
2190
|
+
summary: `fet sync validated change "${changeId}". OpenSpec 1.3.x applies spec updates during archive.`,
|
|
2191
|
+
nextSteps: [`Run fet archive --change ${changeId} when you are ready to archive and update main specs.`],
|
|
2192
|
+
data: ctx.json ? { changeId, validate: result } : { changeId }
|
|
2193
|
+
});
|
|
2194
|
+
});
|
|
2195
|
+
}
|
|
2196
|
+
async function bulkArchiveWorkflowCommand(ctx, args) {
|
|
2197
|
+
const openSpecArgs = stripFetOptions(args);
|
|
2198
|
+
const targets = [.../* @__PURE__ */ new Set([...ctx.changeId ? [ctx.changeId] : [], ...openSpecArgs.filter((arg) => !arg.startsWith("-"))])];
|
|
2199
|
+
if (!targets.length) {
|
|
2200
|
+
throw new FetError({
|
|
2201
|
+
code: "INVALID_ARGUMENTS" /* InvalidArguments */,
|
|
2202
|
+
message: "fet bulk-archive requires explicit change ids with the current OpenSpec CLI.",
|
|
2203
|
+
suggestedCommand: "fet bulk-archive --change <change-id>"
|
|
2204
|
+
});
|
|
2205
|
+
}
|
|
2206
|
+
await withProjectLock(ctx.projectRoot, { command: "bulk-archive", cwd: ctx.cwd, fetVersion: ctx.fetVersion }, async () => {
|
|
2207
|
+
await assertOpenSpecCommandSupported(ctx, "archive", "bulk-archive");
|
|
2208
|
+
const archived = [];
|
|
2209
|
+
for (const changeId of targets) {
|
|
2210
|
+
await assertVerifiedChange(ctx, changeId);
|
|
2211
|
+
const changelogEntry = await createChangelogEntry(ctx.projectRoot, changeId);
|
|
2212
|
+
const result = await ctx.openSpec.run("archive", [changeId], { cwd: ctx.projectRoot, stdio: ctx.json ? "pipe" : "inherit" });
|
|
2213
|
+
if (result.exitCode !== 0) {
|
|
2214
|
+
throw new FetError({
|
|
2215
|
+
code: "OPENSPEC_COMMAND_FAILED" /* OpenSpecCommandFailed */,
|
|
2216
|
+
message: `OpenSpec archive failed for "${changeId}".`,
|
|
2217
|
+
details: result,
|
|
2218
|
+
recoverable: true
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
await appendChangelog(ctx.projectRoot, changelogEntry);
|
|
2222
|
+
archived.push(changeId);
|
|
2223
|
+
}
|
|
2224
|
+
const inspection = await ctx.openSpec.inspectProject(ctx.projectRoot);
|
|
2225
|
+
const state = await ctx.stateStore.getOrCreateGlobal();
|
|
2226
|
+
state.openChangeIds = inspection.changes;
|
|
2227
|
+
if (state.activeChangeId && !inspection.changes.includes(state.activeChangeId)) {
|
|
2228
|
+
state.activeChangeId = null;
|
|
2229
|
+
}
|
|
2230
|
+
state.verifyAuthorization = null;
|
|
2231
|
+
await ctx.stateStore.writeGlobal(state);
|
|
2232
|
+
ctx.output.result({
|
|
2233
|
+
ok: true,
|
|
2234
|
+
command: "bulk-archive",
|
|
2235
|
+
summary: `fet bulk-archive archived ${archived.length} change(s) via OpenSpec archive.`,
|
|
2236
|
+
nextSteps: inspection.changes.length ? [`Remaining open changes: ${inspection.changes.join(", ")}`] : void 0,
|
|
2237
|
+
data: { archived, openChangeIds: inspection.changes }
|
|
2238
|
+
});
|
|
2239
|
+
});
|
|
2240
|
+
}
|
|
2241
|
+
async function onboardWorkflowCommand(ctx) {
|
|
2242
|
+
const inspection = await ctx.openSpec.inspectProject(ctx.projectRoot);
|
|
2243
|
+
const state = await ctx.stateStore.getOrCreateGlobal();
|
|
2244
|
+
state.openChangeIds = inspection.changes;
|
|
2245
|
+
if (state.activeChangeId && !inspection.changes.includes(state.activeChangeId)) {
|
|
2246
|
+
state.activeChangeId = inspection.changes.length === 1 ? inspection.changes[0] ?? null : null;
|
|
2247
|
+
}
|
|
2248
|
+
await ctx.stateStore.writeGlobal(state);
|
|
2249
|
+
ctx.output.result({
|
|
2250
|
+
ok: true,
|
|
2251
|
+
command: "onboard",
|
|
2252
|
+
summary: "fet onboard loaded local FET/OpenSpec workflow context.",
|
|
2253
|
+
nextSteps: [
|
|
2254
|
+
inspection.changes.length ? `Open changes: ${inspection.changes.join(", ")}` : "No open changes found. Run fet propose <change-id-or-description> to start one.",
|
|
2255
|
+
"Use fet continue to prepare planning artifacts, fet apply for implementation instructions, fet verify before archive."
|
|
2256
|
+
],
|
|
2257
|
+
data: { activeChangeId: state.activeChangeId, openChangeIds: inspection.changes, archivedChangeIds: inspection.archived }
|
|
2258
|
+
});
|
|
2259
|
+
}
|
|
2085
2260
|
async function artifactWorkflowCommand(ctx, command, args) {
|
|
2086
2261
|
const openSpecArgs = stripFetOptions(args);
|
|
2087
2262
|
const runState = {};
|
|
@@ -2267,9 +2442,10 @@ async function syncWorkflowState(ctx, command, changeId, lastOpenSpecCommand) {
|
|
|
2267
2442
|
}
|
|
2268
2443
|
const changeInspection = await ctx.openSpec.inspectChange(ctx.projectRoot, changeId);
|
|
2269
2444
|
const changeState = await ctx.stateStore.getOrCreateChange(changeId, phaseByCommand[command] ?? "propose");
|
|
2270
|
-
|
|
2271
|
-
changeState.
|
|
2272
|
-
|
|
2445
|
+
const phase = phaseByCommand[command] ?? "propose";
|
|
2446
|
+
changeState.currentPhase = phase;
|
|
2447
|
+
changeState.phases[phase] = {
|
|
2448
|
+
status: lastOpenSpecCommand.phaseStatus ?? "in_progress",
|
|
2273
2449
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2274
2450
|
};
|
|
2275
2451
|
changeState.lastOpenSpecCommand = {
|
|
@@ -2479,6 +2655,9 @@ async function assertVerified(ctx) {
|
|
|
2479
2655
|
suggestedCommand: "fet verify --done --change <change-id>"
|
|
2480
2656
|
});
|
|
2481
2657
|
}
|
|
2658
|
+
await assertVerifiedChange(ctx, changeId);
|
|
2659
|
+
}
|
|
2660
|
+
async function assertVerifiedChange(ctx, changeId) {
|
|
2482
2661
|
const change = await ctx.stateStore.readChange(changeId);
|
|
2483
2662
|
const inspection = await ctx.openSpec.inspectProject(ctx.projectRoot);
|
|
2484
2663
|
if (!inspection.changes.includes(changeId)) {
|
|
@@ -2969,7 +3148,7 @@ var FET_WORKFLOW_COMMANDS = [
|
|
|
2969
3148
|
"onboard"
|
|
2970
3149
|
];
|
|
2971
3150
|
var FET_GRAPH_COMMANDS = ["graph-status", "graph-setup", "graph-init", "graph-refresh", "graph-doctor", "graph-handoff"];
|
|
2972
|
-
var FET_ADAPTER_COMMANDS = [...FET_WORKFLOW_COMMANDS, "fill-context", "passthrough", ...FET_GRAPH_COMMANDS];
|
|
3151
|
+
var FET_ADAPTER_COMMANDS = [...FET_WORKFLOW_COMMANDS, "update", "fill-context", "passthrough", ...FET_GRAPH_COMMANDS];
|
|
2973
3152
|
function renderFetAdapterUsage(command, args = "[...args]") {
|
|
2974
3153
|
if (command.startsWith("graph-")) {
|
|
2975
3154
|
const subcommand = command.slice("graph-".length);
|