feat(FN-4124): surface mission interview drafts across mission tooling

- Add dashboard API routes and mission interview store support to list and inspect draft missions
- Update Mission Manager UI to surface draft interview sessions with styling and regression coverage
- Extend CLI mission commands and extension tools to expose draft listings consistently
- Document the draft surfacing behavior and include a changeset for the published CLI package

Fusion-Task-Id: FN-4124
This commit is contained in:
Fusion
2026-05-12 19:38:36 -07:00
committed by gsxdsm
parent e6b1108067
commit af39d474d5
19 changed files with 912 additions and 26 deletions

View File

@@ -2020,20 +2020,36 @@ export default function kbExtension(pi: ExtensionAPI) {
promptGuidelines: [
"Use to see all missions and their current status",
"Missions are grouped by status (active, planning, complete, etc.)",
"Drafts represent unfinished mission interview sessions; fn_mission_show does not work on draft IDs because no mission row exists yet",
"Use before fn_mission_show to find a specific mission ID",
],
parameters: Type.Object({}),
parameters: Type.Object({
includeDrafts: Type.Optional(Type.Boolean({ description: "Include in-flight mission interview drafts (default: true)" })),
}),
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
const store = await getStore(ctx.cwd);
const missionStore = store.getMissionStore();
const includeDrafts = params.includeDrafts ?? true;
const missions = missionStore.listMissions();
const drafts = includeDrafts
? (store.getDatabase()
.prepare(
`SELECT id, title, status, updatedAt
FROM ai_sessions
WHERE type = 'mission_interview'
AND status IN ('generating', 'awaiting_input', 'error')
AND COALESCE(archived, 0) = 0
ORDER BY updatedAt DESC`,
)
.all() as Array<{ id: string; title: string; status: "generating" | "awaiting_input" | "error"; updatedAt: string }>)
: [];
if (missions.length === 0) {
if (missions.length === 0 && drafts.length === 0) {
return {
content: [{ type: "text", text: "No missions yet." }],
details: { count: 0 },
details: { count: 0, drafts: [] },
};
}
@@ -2048,8 +2064,17 @@ export default function kbExtension(pi: ExtensionAPI) {
const lines: string[] = [];
lines.push(`Missions (${missions.length})`);
lines.push(
`Summary: active ${summary.active}, planning ${summary.planning}, blocked ${summary.blocked}, complete ${summary.complete}, archived ${summary.archived}\n`,
`Summary: active ${summary.active}, planning ${summary.planning}, blocked ${summary.blocked}, complete ${summary.complete}, archived ${summary.archived}`,
);
lines.push("");
if (drafts.length > 0) {
lines.push(`Drafts (${drafts.length})`);
for (const draft of drafts) {
lines.push(`${draft.id}: ${draft.title} (draft · interview ${draft.status})`);
}
lines.push("");
}
for (const mission of missions) {
const statusIcon = mission.status === "complete" ? "✓" : mission.status === "active" ? "●" : mission.status === "blocked" ? "⚠" : "○";
@@ -2059,7 +2084,11 @@ export default function kbExtension(pi: ExtensionAPI) {
return {
content: [{ type: "text", text: lines.join("\n") }],
details: { count: missions.length, missions: missions.map((m) => ({ id: m.id, title: m.title, status: m.status })) },
details: {
count: missions.length,
missions: missions.map((m) => ({ id: m.id, title: m.title, status: m.status })),
drafts: drafts.map((draft) => ({ id: draft.id, title: draft.title, status: draft.status, updatedAt: draft.updatedAt })),
},
};
},
});