@isaacriehm/cairn-core 0.9.8 → 0.10.0

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.
Files changed (72) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/hooks/runners/stop.js +105 -0
  3. package/dist/hooks/runners/stop.js.map +1 -1
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +1 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/mcp/errors.d.ts +1 -1
  8. package/dist/mcp/errors.js.map +1 -1
  9. package/dist/mcp/schemas.d.ts +101 -0
  10. package/dist/mcp/schemas.js +92 -0
  11. package/dist/mcp/schemas.js.map +1 -1
  12. package/dist/mcp/tools/index.js +19 -0
  13. package/dist/mcp/tools/index.js.map +1 -1
  14. package/dist/mcp/tools/mission-accept-draft.d.ts +16 -0
  15. package/dist/mcp/tools/mission-accept-draft.js +102 -0
  16. package/dist/mcp/tools/mission-accept-draft.js.map +1 -0
  17. package/dist/mcp/tools/mission-advance.d.ts +8 -0
  18. package/dist/mcp/tools/mission-advance.js +124 -0
  19. package/dist/mcp/tools/mission-advance.js.map +1 -0
  20. package/dist/mcp/tools/mission-close.d.ts +8 -0
  21. package/dist/mcp/tools/mission-close.js +48 -0
  22. package/dist/mcp/tools/mission-close.js.map +1 -0
  23. package/dist/mcp/tools/mission-get.d.ts +6 -0
  24. package/dist/mcp/tools/mission-get.js +64 -0
  25. package/dist/mcp/tools/mission-get.js.map +1 -0
  26. package/dist/mcp/tools/mission-reopen.d.ts +13 -0
  27. package/dist/mcp/tools/mission-reopen.js +56 -0
  28. package/dist/mcp/tools/mission-reopen.js.map +1 -0
  29. package/dist/mcp/tools/mission-resume.d.ts +15 -0
  30. package/dist/mcp/tools/mission-resume.js +219 -0
  31. package/dist/mcp/tools/mission-resume.js.map +1 -0
  32. package/dist/mcp/tools/mission-resync-accept.d.ts +19 -0
  33. package/dist/mcp/tools/mission-resync-accept.js +192 -0
  34. package/dist/mcp/tools/mission-resync-accept.js.map +1 -0
  35. package/dist/mcp/tools/mission-resync.d.ts +20 -0
  36. package/dist/mcp/tools/mission-resync.js +113 -0
  37. package/dist/mcp/tools/mission-resync.js.map +1 -0
  38. package/dist/mcp/tools/mission-start.d.ts +8 -0
  39. package/dist/mcp/tools/mission-start.js +76 -0
  40. package/dist/mcp/tools/mission-start.js.map +1 -0
  41. package/dist/mcp/tools/task-create.d.ts +2 -0
  42. package/dist/mcp/tools/task-create.js +40 -2
  43. package/dist/mcp/tools/task-create.js.map +1 -1
  44. package/dist/missions/cli.d.ts +106 -0
  45. package/dist/missions/cli.js +279 -0
  46. package/dist/missions/cli.js.map +1 -0
  47. package/dist/missions/cursor.d.ts +40 -0
  48. package/dist/missions/cursor.js +99 -0
  49. package/dist/missions/cursor.js.map +1 -0
  50. package/dist/missions/draft.d.ts +28 -0
  51. package/dist/missions/draft.js +112 -0
  52. package/dist/missions/draft.js.map +1 -0
  53. package/dist/missions/index.d.ts +9 -0
  54. package/dist/missions/index.js +10 -0
  55. package/dist/missions/index.js.map +1 -0
  56. package/dist/missions/task-link.d.ts +51 -0
  57. package/dist/missions/task-link.js +155 -0
  58. package/dist/missions/task-link.js.map +1 -0
  59. package/dist/session-start/build.d.ts +1 -1
  60. package/dist/session-start/build.js +58 -1
  61. package/dist/session-start/build.js.map +1 -1
  62. package/dist/status-line/format.d.ts +15 -1
  63. package/dist/status-line/format.js +12 -1
  64. package/dist/status-line/format.js.map +1 -1
  65. package/dist/status-line/index.d.ts +1 -1
  66. package/dist/status-line/index.js +1 -1
  67. package/dist/status-line/index.js.map +1 -1
  68. package/dist/status-line/reader.js +33 -2
  69. package/dist/status-line/reader.js.map +1 -1
  70. package/dist/tasks/lifecycle.js +11 -0
  71. package/dist/tasks/lifecycle.js.map +1 -1
  72. package/package.json +2 -2
@@ -0,0 +1,102 @@
1
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
2
+ import { isAbsolute, resolve } from "node:path";
3
+ import { appendMissionJournal, deriveMissionId, findActiveMission, initialPhaseProgress, missionRuntimeDir, missionsGroundRoot, nextPendingPhase, writeMissionSpec, writeMissionState, writeRoadmap, } from "@isaacriehm/cairn-state";
4
+ import { requireBootstrap } from "../bootstrap-guard.js";
5
+ import { mcpError } from "../errors.js";
6
+ import { missionAcceptDraftInput } from "../schemas.js";
7
+ async function handler(ctx, input) {
8
+ const block = requireBootstrap(ctx.repoRoot);
9
+ if (block !== null)
10
+ return block;
11
+ if (findActiveMission(ctx.repoRoot) !== null) {
12
+ return mcpError("MISSION_ALREADY_ACTIVE", "An active mission already exists. Close or abort it before starting another.");
13
+ }
14
+ const absSpec = isAbsolute(input.spec_path)
15
+ ? input.spec_path
16
+ : resolve(ctx.repoRoot, input.spec_path);
17
+ if (!absSpec.startsWith(ctx.repoRoot)) {
18
+ return mcpError("PATH_OUTSIDE_REPO", `${input.spec_path} resolves outside the repo`);
19
+ }
20
+ if (!existsSync(absSpec)) {
21
+ return mcpError("FILE_NOT_FOUND", `Spec doc not found: ${input.spec_path}`);
22
+ }
23
+ let specSource;
24
+ try {
25
+ specSource = readFileSync(absSpec, "utf8");
26
+ }
27
+ catch (err) {
28
+ return mcpError("INTERNAL_ERROR", `Failed to read spec doc: ${err instanceof Error ? err.message : String(err)}`);
29
+ }
30
+ const startedAt = new Date().toISOString();
31
+ const missionId = deriveMissionId({
32
+ title: input.title,
33
+ spec_path: input.spec_path,
34
+ started_at: startedAt,
35
+ });
36
+ const phases = input.phases.map((p) => ({
37
+ id: p.id,
38
+ title: p.title,
39
+ depends_on: p.depends_on ?? [],
40
+ exit_criteria: p.exit_criteria,
41
+ ...(p.exit_gate !== undefined ? { exit_gate: p.exit_gate } : {}),
42
+ }));
43
+ const frontmatter = {
44
+ mission_id: missionId,
45
+ title: input.title,
46
+ spec_path: input.spec_path,
47
+ created_at: startedAt,
48
+ exit_gate: input.exit_gate,
49
+ phases,
50
+ };
51
+ // Roadmap dir + per-clone runtime dir.
52
+ mkdirSync(missionsGroundRoot(ctx.repoRoot), { recursive: true });
53
+ mkdirSync(missionRuntimeDir(ctx.repoRoot, missionId), { recursive: true });
54
+ writeRoadmap(ctx.repoRoot, missionId, frontmatter, `# Mission — ${input.title}\n\nSpec: \`${input.spec_path}\`. Phase list above is the canonical structure.\n`);
55
+ writeMissionSpec(ctx.repoRoot, missionId, specSource);
56
+ const phaseProgress = initialPhaseProgress(frontmatter);
57
+ const firstPhase = nextPendingPhase(frontmatter, {
58
+ mission_id: missionId,
59
+ started_at: startedAt,
60
+ cursor: { active_phase: null, active_phase_started_at: null },
61
+ phase_progress: phaseProgress,
62
+ outcome: "active",
63
+ });
64
+ if (firstPhase !== null) {
65
+ phaseProgress[firstPhase.id] = {
66
+ state: "in_progress",
67
+ task_ids: [],
68
+ };
69
+ }
70
+ writeMissionState(ctx.repoRoot, missionId, {
71
+ mission_id: missionId,
72
+ started_at: startedAt,
73
+ cursor: {
74
+ active_phase: firstPhase?.id ?? null,
75
+ active_phase_started_at: firstPhase !== null ? startedAt : null,
76
+ },
77
+ phase_progress: phaseProgress,
78
+ outcome: "active",
79
+ });
80
+ // Empty journal + initial entry.
81
+ appendMissionJournal(ctx.repoRoot, missionId, {
82
+ ts: startedAt,
83
+ kind: "started",
84
+ detail: `${phases.length} phases, exit_gate=${input.exit_gate}`,
85
+ });
86
+ return {
87
+ ok: true,
88
+ mission_id: missionId,
89
+ roadmap_path: `.cairn/ground/missions/${missionId}/roadmap.md`,
90
+ state_path: `.cairn/missions/${missionId}/state.json`,
91
+ spec_path: `.cairn/missions/${missionId}/spec.md`,
92
+ cursor: { active_phase: firstPhase?.id ?? null },
93
+ total_phases: phases.length,
94
+ };
95
+ }
96
+ export const missionAcceptDraftTool = {
97
+ name: "cairn_mission_accept_draft",
98
+ description: "Commit an operator-approved roadmap draft. Generates the mission id (MIS-<slug>-<hash7>), writes `.cairn/ground/missions/<id>/roadmap.md` (committed), `.cairn/missions/<id>/state.json` + `spec.md` (per-clone), and seeds the mission journal. Sets the cursor to the first phase whose `depends_on` is empty.",
99
+ inputSchema: missionAcceptDraftInput,
100
+ handler,
101
+ };
102
+ //# sourceMappingURL=mission-accept-draft.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mission-accept-draft.js","sourceRoot":"","sources":["../../../src/mcp/tools/mission-accept-draft.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ToolDef } from "./types.js";
2
+ interface Input {
3
+ phase_id: string;
4
+ choice: "exit" | "not_yet" | "defer" | "force" | "drop";
5
+ defer_hours?: number;
6
+ }
7
+ export declare const missionAdvanceTool: ToolDef<Input>;
8
+ export {};
@@ -0,0 +1,124 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { appendMissionJournal, findActiveMission, readMissionState, readRoadmap, writeMissionState, } from "@isaacriehm/cairn-state";
4
+ import { requireBootstrap } from "../bootstrap-guard.js";
5
+ import { mcpError } from "../errors.js";
6
+ import { missionAdvanceInput } from "../schemas.js";
7
+ import { advanceMissionPhase as advancePhase } from "../../missions/index.js";
8
+ function writeMissionPhaseDefer(repoRoot, missionId, phaseId, hours) {
9
+ const now = new Date();
10
+ const until = new Date(now.getTime() + hours * 60 * 60 * 1000);
11
+ const payload = {
12
+ mission_id: missionId,
13
+ phase_id: phaseId,
14
+ deferred_at: now.toISOString(),
15
+ deferred_until: until.toISOString(),
16
+ };
17
+ const path = join(repoRoot, ".cairn", ".mission-phase-deferred-until");
18
+ mkdirSync(dirname(path), { recursive: true });
19
+ writeFileSync(path, JSON.stringify(payload, null, 2), "utf8");
20
+ return until.toISOString();
21
+ }
22
+ async function handler(ctx, input) {
23
+ const block = requireBootstrap(ctx.repoRoot);
24
+ if (block !== null)
25
+ return block;
26
+ const missionId = findActiveMission(ctx.repoRoot);
27
+ if (missionId === null) {
28
+ return mcpError("MISSION_NOT_FOUND", "No active mission");
29
+ }
30
+ const roadmap = readRoadmap(ctx.repoRoot, missionId);
31
+ const state = readMissionState(ctx.repoRoot, missionId);
32
+ if (roadmap === null || state === null) {
33
+ return mcpError("MISSION_NOT_FOUND", `Mission ${missionId} state or roadmap unreadable`);
34
+ }
35
+ const phaseInRoadmap = roadmap.frontmatter.phases.some((p) => p.id === input.phase_id);
36
+ // choice=drop is the drift resolver — phase_id intentionally NOT in
37
+ // roadmap.md anymore (operator deleted it mid-mission). Removes the
38
+ // phase from phase_progress and journals the drift resolution.
39
+ if (input.choice === "drop") {
40
+ if (phaseInRoadmap) {
41
+ return mcpError("VALIDATION_FAILED", `phase ${input.phase_id} is still in roadmap.md; choice=drop only resolves drifted ids. Use choice=exit/force to advance an in-roadmap phase.`);
42
+ }
43
+ if (!(input.phase_id in state.phase_progress)) {
44
+ return mcpError("MISSION_PHASE_NOT_FOUND", `phase ${input.phase_id} not present in phase_progress (nothing to drop)`);
45
+ }
46
+ const taskIds = state.phase_progress[input.phase_id]?.task_ids ?? [];
47
+ delete state.phase_progress[input.phase_id];
48
+ writeMissionState(ctx.repoRoot, missionId, state);
49
+ appendMissionJournal(ctx.repoRoot, missionId, {
50
+ ts: new Date().toISOString(),
51
+ kind: "drift-detected",
52
+ phase_id: input.phase_id,
53
+ detail: `dropped — ${taskIds.length} graduated task(s) orphaned`,
54
+ });
55
+ return {
56
+ ok: true,
57
+ action: "dropped",
58
+ mission_id: missionId,
59
+ phase_id: input.phase_id,
60
+ orphaned_task_ids: taskIds,
61
+ };
62
+ }
63
+ if (!phaseInRoadmap) {
64
+ return mcpError("MISSION_PHASE_NOT_FOUND", `phase ${input.phase_id} not in roadmap`);
65
+ }
66
+ if (input.choice === "not_yet") {
67
+ appendMissionJournal(ctx.repoRoot, missionId, {
68
+ ts: new Date().toISOString(),
69
+ kind: "phase-deferred",
70
+ phase_id: input.phase_id,
71
+ detail: "operator chose not_yet",
72
+ });
73
+ return { ok: true, action: "deferred", mission_id: missionId, phase_id: input.phase_id };
74
+ }
75
+ if (input.choice === "defer") {
76
+ const hours = input.defer_hours ?? 24;
77
+ const deferredUntil = writeMissionPhaseDefer(ctx.repoRoot, missionId, input.phase_id, hours);
78
+ appendMissionJournal(ctx.repoRoot, missionId, {
79
+ ts: new Date().toISOString(),
80
+ kind: "phase-deferred",
81
+ phase_id: input.phase_id,
82
+ detail: `defer ${hours}h`,
83
+ });
84
+ return {
85
+ ok: true,
86
+ action: "deferred",
87
+ mission_id: missionId,
88
+ phase_id: input.phase_id,
89
+ deferred_until: deferredUntil,
90
+ };
91
+ }
92
+ // choice === "exit" || "force"
93
+ if (input.choice === "exit") {
94
+ const progress = state.phase_progress[input.phase_id];
95
+ if (progress === undefined || progress.task_ids.length === 0) {
96
+ return mcpError("VALIDATION_FAILED", `phase ${input.phase_id} has no linked tasks. Pass choice="force" to advance an empty phase.`);
97
+ }
98
+ }
99
+ const result = advancePhase(ctx.repoRoot, missionId, input.phase_id);
100
+ if (!result.ok) {
101
+ return mcpError(result.code === "PHASE_NOT_FOUND"
102
+ ? "MISSION_PHASE_NOT_FOUND"
103
+ : result.code === "MISSION_NOT_FOUND"
104
+ ? "MISSION_NOT_FOUND"
105
+ : "VALIDATION_FAILED", result.message);
106
+ }
107
+ return {
108
+ ok: true,
109
+ action: result.closed ? "closed" : "advanced",
110
+ mission_id: missionId,
111
+ phase_advanced: result.phase_advanced,
112
+ next_phase: result.next_phase?.id ?? null,
113
+ next_phase_title: result.next_phase?.title ?? null,
114
+ progress: { done: result.donePhases, total: result.totalPhases },
115
+ closed: result.closed,
116
+ };
117
+ }
118
+ export const missionAdvanceTool = {
119
+ name: "cairn_mission_advance",
120
+ description: "Resolve a phase-exit prompt (or manually advance the mission). `choice=exit` marks the named phase done and advances the cursor; refused when the phase has zero linked tasks (use force). `choice=force` advances even when the phase has zero linked tasks. `choice=not_yet` keeps the cursor — next code-change task gets attached to the same phase. `choice=defer` suppresses the phase-exit prompt for `defer_hours` (default 24). `choice=drop` removes a drifted phase id from `phase_progress` (the id was deleted from roadmap.md mid-mission); refuses if the phase is still present in roadmap.md. When the advance hits the last pending phase, the mission is auto-closed and archived.",
121
+ inputSchema: missionAdvanceInput,
122
+ handler,
123
+ };
124
+ //# sourceMappingURL=mission-advance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mission-advance.js","sourceRoot":"","sources":["../../../src/mcp/tools/mission-advance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ToolDef } from "./types.js";
2
+ interface Input {
3
+ mission_id: string;
4
+ outcome: "done" | "aborted";
5
+ reason?: string;
6
+ }
7
+ export declare const missionCloseTool: ToolDef<Input>;
8
+ export {};
@@ -0,0 +1,48 @@
1
+ import { appendMissionJournal, archiveMission, locateMission, readMissionState, writeMissionState, } from "@isaacriehm/cairn-state";
2
+ import { requireBootstrap } from "../bootstrap-guard.js";
3
+ import { mcpError } from "../errors.js";
4
+ import { missionCloseInput } from "../schemas.js";
5
+ async function handler(ctx, input) {
6
+ const block = requireBootstrap(ctx.repoRoot);
7
+ if (block !== null)
8
+ return block;
9
+ const scope = locateMission(ctx.repoRoot, input.mission_id);
10
+ if (scope !== "active") {
11
+ return mcpError("MISSION_NOT_FOUND", `Mission ${input.mission_id} not active (scope=${scope ?? "missing"})`);
12
+ }
13
+ const state = readMissionState(ctx.repoRoot, input.mission_id);
14
+ if (state === null) {
15
+ return mcpError("MISSION_NOT_FOUND", `state.json missing for ${input.mission_id}`);
16
+ }
17
+ const closedAt = new Date().toISOString();
18
+ state.outcome = input.outcome;
19
+ state.cursor.active_phase = null;
20
+ state.cursor.active_phase_started_at = null;
21
+ state.closed_at = closedAt;
22
+ if (input.outcome === "aborted" && input.reason !== undefined) {
23
+ state.abort_reason = input.reason;
24
+ }
25
+ writeMissionState(ctx.repoRoot, input.mission_id, state);
26
+ appendMissionJournal(ctx.repoRoot, input.mission_id, {
27
+ ts: closedAt,
28
+ kind: "closed",
29
+ detail: input.outcome === "aborted"
30
+ ? `aborted${input.reason !== undefined ? `: ${input.reason}` : ""}`
31
+ : "manual close",
32
+ });
33
+ archiveMission(ctx.repoRoot, input.mission_id);
34
+ return {
35
+ ok: true,
36
+ mission_id: input.mission_id,
37
+ outcome: input.outcome,
38
+ closed_at: closedAt,
39
+ archived_to: `.cairn/ground/missions/_done/${input.mission_id}/`,
40
+ };
41
+ }
42
+ export const missionCloseTool = {
43
+ name: "cairn_mission_close",
44
+ description: "Close a mission and archive its dirs. `outcome=done` is the manual analogue of auto-close on last phase complete; `outcome=aborted` is the operator-driven cancel path (with optional `reason`). Roadmap moves to `.cairn/ground/missions/_done/<id>/`; per-clone runtime moves to `.cairn/missions/_done/<id>/`. Reversible via cairn_mission_reopen.",
45
+ inputSchema: missionCloseInput,
46
+ handler,
47
+ };
48
+ //# sourceMappingURL=mission-close.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mission-close.js","sourceRoot":"","sources":["../../../src/mcp/tools/mission-close.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AASlD,KAAK,UAAU,OAAO,CAAC,GAAe,EAAE,KAAY;IAClD,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAEjC,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5D,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,OAAO,QAAQ,CACb,mBAAmB,EACnB,WAAW,KAAK,CAAC,UAAU,sBAAsB,KAAK,IAAI,SAAS,GAAG,CACvE,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC/D,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,OAAO,QAAQ,CAAC,mBAAmB,EAAE,0BAA0B,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IACrF,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1C,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC9B,KAAK,CAAC,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC;IACjC,KAAK,CAAC,MAAM,CAAC,uBAAuB,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,SAAS,GAAG,QAAQ,CAAC;IAC3B,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9D,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC;IACpC,CAAC;IACD,iBAAiB,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAEzD,oBAAoB,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,EAAE;QACnD,EAAE,EAAE,QAAQ;QACZ,IAAI,EAAE,QAAQ;QACd,MAAM,EACJ,KAAK,CAAC,OAAO,KAAK,SAAS;YACzB,CAAC,CAAC,UAAU,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;YACnE,CAAC,CAAC,cAAc;KACrB,CAAC,CAAC;IAEH,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAE/C,OAAO;QACL,EAAE,EAAE,IAAI;QACR,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS,EAAE,QAAQ;QACnB,WAAW,EAAE,gCAAgC,KAAK,CAAC,UAAU,GAAG;KACjE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAmB;IAC9C,IAAI,EAAE,qBAAqB;IAC3B,WAAW,EACT,wVAAwV;IAC1V,WAAW,EAAE,iBAAiB;IAC9B,OAAO;CACR,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ToolDef } from "./types.js";
2
+ interface Input {
3
+ mission_id?: string;
4
+ }
5
+ export declare const missionGetTool: ToolDef<Input>;
6
+ export {};
@@ -0,0 +1,64 @@
1
+ import { countDonePhases, detectRoadmapDrift, findActiveMission, locateMission, readMissionState, readRoadmap, } from "@isaacriehm/cairn-state";
2
+ import { requireBootstrap } from "../bootstrap-guard.js";
3
+ import { mcpError } from "../errors.js";
4
+ import { missionGetInput } from "../schemas.js";
5
+ async function handler(ctx, input) {
6
+ const block = requireBootstrap(ctx.repoRoot);
7
+ if (block !== null)
8
+ return block;
9
+ const missionId = input.mission_id ?? findActiveMission(ctx.repoRoot);
10
+ if (missionId === null) {
11
+ return { ok: true, active: false };
12
+ }
13
+ const scope = locateMission(ctx.repoRoot, missionId);
14
+ if (scope === null) {
15
+ return mcpError("MISSION_NOT_FOUND", `Mission ${missionId} not found in active or done sets`);
16
+ }
17
+ // Reading roadmap from `_done/` happens via `locateMission` returning
18
+ // "done"; the schema-validated reader looks at the live roadmap path
19
+ // for active missions only. For now `mission_get` is scoped to active
20
+ // missions; closed-mission inspection is a separate `cairn timeline`
21
+ // surface.
22
+ if (scope === "done") {
23
+ return mcpError("MISSION_NOT_FOUND", `Mission ${missionId} is archived (use cairn_mission_reopen to inspect)`);
24
+ }
25
+ const roadmap = readRoadmap(ctx.repoRoot, missionId);
26
+ const state = readMissionState(ctx.repoRoot, missionId);
27
+ if (roadmap === null || state === null) {
28
+ return mcpError("MISSION_NOT_FOUND", `Mission ${missionId} state or roadmap unreadable`);
29
+ }
30
+ const drift = detectRoadmapDrift(roadmap.frontmatter, state);
31
+ const cursorPhaseId = state.cursor.active_phase;
32
+ const cursorPhase = cursorPhaseId === null
33
+ ? null
34
+ : roadmap.frontmatter.phases.find((p) => p.id === cursorPhaseId) ?? null;
35
+ return {
36
+ ok: true,
37
+ active: true,
38
+ mission_id: missionId,
39
+ title: roadmap.frontmatter.title,
40
+ spec_path: roadmap.frontmatter.spec_path,
41
+ exit_gate: roadmap.frontmatter.exit_gate,
42
+ cursor: {
43
+ active_phase: cursorPhaseId,
44
+ active_phase_started_at: state.cursor.active_phase_started_at,
45
+ active_phase_title: cursorPhase?.title ?? null,
46
+ active_phase_exit_criteria: cursorPhase?.exit_criteria ?? null,
47
+ active_phase_exit_gate: cursorPhase?.exit_gate ?? roadmap.frontmatter.exit_gate,
48
+ },
49
+ progress: {
50
+ done: countDonePhases(state),
51
+ total: roadmap.frontmatter.phases.length,
52
+ },
53
+ drift_phase_ids: drift,
54
+ phase_progress: state.phase_progress,
55
+ phases: roadmap.frontmatter.phases,
56
+ };
57
+ }
58
+ export const missionGetTool = {
59
+ name: "cairn_mission_get",
60
+ description: "Return the active mission's state — title, spec_path, exit_gate, cursor (active_phase + exit_criteria), N/M phase progress, full phase_progress map, the ordered phase array, and any drift_phase_ids that no longer appear in roadmap.md (operator removed them mid-mission). Returns `{ active: false }` when no active mission exists.",
61
+ inputSchema: missionGetInput,
62
+ handler,
63
+ };
64
+ //# sourceMappingURL=mission-get.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mission-get.js","sourceRoot":"","sources":["../../../src/mcp/tools/mission-get.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `cairn_mission_reopen` — un-archive a closed mission. Moves the
3
+ * `_done/<id>/` dirs back to live locations and clears the
4
+ * `outcome: done|aborted` stamp so the cursor (whatever it was at
5
+ * close) drives the next session. Does NOT re-execute graduated
6
+ * phase tasks.
7
+ */
8
+ import type { ToolDef } from "./types.js";
9
+ interface Input {
10
+ mission_id: string;
11
+ }
12
+ export declare const missionReopenTool: ToolDef<Input>;
13
+ export {};
@@ -0,0 +1,56 @@
1
+ /**
2
+ * `cairn_mission_reopen` — un-archive a closed mission. Moves the
3
+ * `_done/<id>/` dirs back to live locations and clears the
4
+ * `outcome: done|aborted` stamp so the cursor (whatever it was at
5
+ * close) drives the next session. Does NOT re-execute graduated
6
+ * phase tasks.
7
+ */
8
+ import { appendMissionJournal, findActiveMission, locateMission, readMissionState, restoreMission, writeMissionState, } from "@isaacriehm/cairn-state";
9
+ import { requireBootstrap } from "../bootstrap-guard.js";
10
+ import { mcpError } from "../errors.js";
11
+ import { missionReopenInput } from "../schemas.js";
12
+ async function handler(ctx, input) {
13
+ const block = requireBootstrap(ctx.repoRoot);
14
+ if (block !== null)
15
+ return block;
16
+ const scope = locateMission(ctx.repoRoot, input.mission_id);
17
+ if (scope === null) {
18
+ return mcpError("MISSION_NOT_FOUND", `Mission ${input.mission_id} not found`);
19
+ }
20
+ if (scope === "active") {
21
+ return mcpError("VALIDATION_FAILED", `Mission ${input.mission_id} is already active (no reopen needed)`);
22
+ }
23
+ if (findActiveMission(ctx.repoRoot) !== null) {
24
+ return mcpError("MISSION_ALREADY_ACTIVE", "Another mission is already active; close it before reopening this one (one active mission per repo).");
25
+ }
26
+ const moved = restoreMission(ctx.repoRoot, input.mission_id);
27
+ if (!moved) {
28
+ return mcpError("MISSION_NOT_FOUND", `Could not move archived dirs for ${input.mission_id}`);
29
+ }
30
+ const state = readMissionState(ctx.repoRoot, input.mission_id);
31
+ if (state !== null) {
32
+ state.outcome = "active";
33
+ delete state.closed_at;
34
+ delete state.abort_reason;
35
+ writeMissionState(ctx.repoRoot, input.mission_id, state);
36
+ }
37
+ const reopenedAt = new Date().toISOString();
38
+ appendMissionJournal(ctx.repoRoot, input.mission_id, {
39
+ ts: reopenedAt,
40
+ kind: "reopened",
41
+ detail: `cursor: ${state?.cursor.active_phase ?? "(none)"}`,
42
+ });
43
+ return {
44
+ ok: true,
45
+ mission_id: input.mission_id,
46
+ cursor: state?.cursor ?? null,
47
+ reopened_at: reopenedAt,
48
+ };
49
+ }
50
+ export const missionReopenTool = {
51
+ name: "cairn_mission_reopen",
52
+ description: "Un-archive a closed mission. Moves `.cairn/ground/missions/_done/<id>/` and `.cairn/missions/_done/<id>/` back to their live locations, resets `outcome` to active, and preserves the cursor at close-time. Refuses when another mission is already active. Does NOT re-execute graduated phase tasks.",
53
+ inputSchema: missionReopenInput,
54
+ handler,
55
+ };
56
+ //# sourceMappingURL=mission-reopen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mission-reopen.js","sourceRoot":"","sources":["../../../src/mcp/tools/mission-reopen.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAOnD,KAAK,UAAU,OAAO,CAAC,GAAe,EAAE,KAAY;IAClD,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAEjC,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5D,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,OAAO,QAAQ,CAAC,mBAAmB,EAAE,WAAW,KAAK,CAAC,UAAU,YAAY,CAAC,CAAC;IAChF,CAAC;IACD,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,OAAO,QAAQ,CACb,mBAAmB,EACnB,WAAW,KAAK,CAAC,UAAU,uCAAuC,CACnE,CAAC;IACJ,CAAC;IAED,IAAI,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC;QAC7C,OAAO,QAAQ,CACb,wBAAwB,EACxB,sGAAsG,CACvG,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC7D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,QAAQ,CAAC,mBAAmB,EAAE,oCAAoC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IAC/F,CAAC;IAED,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC/D,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,KAAK,CAAC,OAAO,GAAG,QAAQ,CAAC;QACzB,OAAO,KAAK,CAAC,SAAS,CAAC;QACvB,OAAO,KAAK,CAAC,YAAY,CAAC;QAC1B,iBAAiB,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5C,oBAAoB,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,EAAE;QACnD,EAAE,EAAE,UAAU;QACd,IAAI,EAAE,UAAU;QAChB,MAAM,EAAE,WAAW,KAAK,EAAE,MAAM,CAAC,YAAY,IAAI,QAAQ,EAAE;KAC5D,CAAC,CAAC;IAEH,OAAO;QACL,EAAE,EAAE,IAAI;QACR,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,IAAI;QAC7B,WAAW,EAAE,UAAU;KACxB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAmB;IAC/C,IAAI,EAAE,sBAAsB;IAC5B,WAAW,EACT,wSAAwS;IAC1S,WAAW,EAAE,kBAAkB;IAC/B,OAAO;CACR,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * `cairn_mission_resume` — return the mission-frame priming body the
3
+ * `cairn-resume` skill chains in front of the existing task journal
4
+ * frame after `/clear`.
5
+ *
6
+ * Budget: ≤ 2500 tokens total (~10000 chars, conservative). Phase
7
+ * spec slice hard-capped at 1500 tokens (~6000 chars); falls back to
8
+ * title + exit_criteria + first paragraph when over.
9
+ */
10
+ import type { ToolDef } from "./types.js";
11
+ interface Input {
12
+ mission_id?: string;
13
+ }
14
+ export declare const missionResumeTool: ToolDef<Input>;
15
+ export {};