mainflow 0.12.0 → 0.13.1

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.js CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { $ as HubError, A as readSession, C as setMainlineStatus, D as addAction, E as updateTask, F as startSession, G as closeIssue, J as planIssue, K as createFinding, L as updateAction, N as setSessionStatus, Q as setIssueArchived, R as sessionBriefing, S as setCriterion, T as supersedeDecision, W as appendOps, X as setFindingArchived, Y as reopenIssue, Z as setFindingStatus, _ as createTask, at as MAINLINE_FILE, b as setArchived, ct as WriteError, dt as findHubRoot, et as loadHub, f as acceptDecision, g as createMainline, h as createDecision, j as setSessionArchived, l as migrateHub, m as addRepo, ot as TASK_FILE, p as addHub, q as createIssue, v as dropChecklistItem, x as setChecklistItem, y as initHub } from "./host-Dwf1wLMd.js";
2
+ import { $ as setFindingStatus, A as addAction, C as setCriterionText, D as updateTask, E as supersedeDecision, F as setSessionArchived, J as createFinding, K as appendOps, L as setSessionStatus, M as handoffSession, O as sessionBriefing, P as readSession, Q as setFindingArchived, S as setCriterion, V as updateAction, X as planIssue, Y as createIssue, Z as reopenIssue, _ as createTask, b as setArchived, ct as TASK_FILE, et as setIssueArchived, f as acceptDecision, g as createMainline, h as createDecision, l as migrateHub, m as addRepo, nt as loadHub, p as addHub, pt as findHubRoot, q as closeIssue, st as MAINLINE_FILE, tt as HubError, ut as WriteError, v as dropChecklistItem, w as setMainlineStatus, x as setChecklistItem, y as initHub, z as startSession } from "./host-mlpW8M1s.js";
3
+ import { N as parseRef, P as refExample } from "./dist-BQmXEfAf.js";
3
4
  import { t as listProgress } from "./progress-1BfPg2zN.js";
4
5
  import { COMMAND_SPECS, OPTIONS } from "./facts.js";
5
6
  import { runHook } from "./hooks.js";
6
- import { l as CLI_VERSION, r as launchUi, s as startUiInBackground } from "./launch-D5K564tw.js";
7
+ import { l as CLI_VERSION, r as launchUi, s as startUiInBackground } from "./launch-CJYDVfE9.js";
7
8
  import { readFileSync, realpathSync } from "node:fs";
8
9
  import { join, relative } from "node:path";
9
10
  import { fileURLToPath } from "node:url";
@@ -54,6 +55,10 @@ const RUNS = {
54
55
  });
55
56
  return done(`started ${session.id}${session.actions.length ? ` with ${session.actions.length} carried actions` : ""}`);
56
57
  },
58
+ "session handoff": ({ root, args, opts }) => {
59
+ const session = handoffSession(root, sessionOf(opts), args[0]);
60
+ return done(`${session.id} took over ${session.actions.filter((a) => a.status === "todo" || a.status === "doing").length} actions from ${args[0]}`);
61
+ },
57
62
  "session show": ({ root, args, opts }) => {
58
63
  const id = sessionOf(opts, args[0]);
59
64
  const session = readSession(root, id);
@@ -155,6 +160,13 @@ const RUNS = {
155
160
  setCriterion(root, args[0], false);
156
161
  return done(`unchecked ${args[0]}`);
157
162
  },
163
+ "criterion set": ({ root, args, opts }) => {
164
+ setCriterionText(root, args[0], {
165
+ text: opts.text,
166
+ doneWhen: opts["done-when"]
167
+ });
168
+ return done(`updated ${args[0]}`);
169
+ },
158
170
  "task set": ({ root, args, opts }) => {
159
171
  const blockedBy = opts.reason ? {
160
172
  reason: opts.reason,
@@ -172,6 +184,18 @@ const RUNS = {
172
184
  });
173
185
  return done(`updated ${args[0]} (${t.status})`);
174
186
  },
187
+ "task show": ({ root, args }) => {
188
+ const { mainline, task } = findTask(loadHub(root), args[0]);
189
+ console.log(`${mainline.id}/${task.id} ${task.status.padEnd(8)} ${task.kind} ${task.title}`);
190
+ if (task.checklist.length === 0) console.log(" (no checklist items)");
191
+ for (const c of task.checklist) {
192
+ const mark = c.done ? "[x]" : "[ ]";
193
+ const state = c.dropped ? " (dropped)" : "";
194
+ console.log(` ${mark} ${c.id} ${c.text}${state}`);
195
+ }
196
+ for (const link of task.links ?? []) console.log(` ${link.rel} → ${link.to}${link.note ? ` ${link.note}` : ""}`);
197
+ return done(`${task.checklist.length} checklist items`);
198
+ },
175
199
  "item check": ({ root, args, opts }) => {
176
200
  setChecklistItem(root, args[0], true, opts.evidence);
177
201
  return done(`checked ${args[0]}`);
@@ -216,13 +240,18 @@ const RUNS = {
216
240
  return done(`logged ${entry.at} ${entry.operation}`);
217
241
  },
218
242
  "ops list": ({ root, opts }) => {
219
- const shown = loadHub(root).ops.filter((o) => !opts.month || o.at.startsWith(opts.month));
243
+ const shown = loadHub(root).ops.filter((o) => (!opts.month || o.at.startsWith(opts.month)) && (!opts.ref || o.ref === opts.ref));
220
244
  for (const o of shown) console.log(` ${o.at} ${o.target} ${o.operation} ${o.result}${o.ref ? ` ${o.ref}` : ""}`);
221
245
  return done(`${shown.length} operations`);
222
246
  },
223
247
  "decision supersede": ({ root, args, opts }) => {
224
248
  const d = supersedeDecision(root, args[0], required(opts, "by", "--by d002"));
225
249
  return done(`superseded ${args[0]} by ${d.supersededBy}`);
250
+ },
251
+ "decision list": ({ root, args }) => {
252
+ const mainline = findMainline(loadHub(root), args[0]);
253
+ for (const d of mainline.decisions) console.log(` ${mainline.id}/${d.id} ${d.status.padEnd(10)} ${d.title}`);
254
+ return done(`${mainline.decisions.length} decisions`);
226
255
  }
227
256
  };
228
257
  const COMMANDS = Object.fromEntries(Object.keys(COMMAND_SPECS).map((name) => [name, {
@@ -300,6 +329,24 @@ function sessionOf(opts, arg) {
300
329
  return id;
301
330
  }
302
331
  const list = (value) => (value ?? "").split(",").map((v) => v.trim()).filter(Boolean);
332
+ /** The mainline a reference names; throws with the same message resolve.ts gives when it is missing. */
333
+ function findMainline(hub, id) {
334
+ const mainline = hub.mainlines.find((m) => m.id === id);
335
+ if (!mainline) throw new WriteError(`mainline ${id} not found; run "mainflow status --all" to list mainlines`);
336
+ return mainline;
337
+ }
338
+ /** The task a `m/t` reference names, with the mainline it sits in. */
339
+ function findTask(hub, refInput) {
340
+ const ref = parseRef(refInput);
341
+ if (ref?.kind !== "task") throw new WriteError(`expected a task reference like ${refExample("task")}, got "${refInput}"`);
342
+ const mainline = findMainline(hub, ref.mainline);
343
+ const task = mainline.tasks.find((t) => t.id === ref.task);
344
+ if (!task) throw new WriteError(`task ${refInput} not found`);
345
+ return {
346
+ mainline,
347
+ task
348
+ };
349
+ }
303
350
  function required(opts, name, example) {
304
351
  const value = opts[name];
305
352
  if (!value) throw new WriteError(`--${name} is required, e.g. ${example}`);
package/dist/facts.d.ts CHANGED
@@ -73,6 +73,12 @@ export declare const COMMAND_SPECS: {
73
73
  readonly args: 0;
74
74
  readonly needsHub: true;
75
75
  };
76
+ readonly 'session handoff': {
77
+ readonly usage: 'session handoff <session> [--session <session>]';
78
+ readonly summary: 'Take over the unfinished actions of another session into this one';
79
+ readonly args: 1;
80
+ readonly needsHub: true;
81
+ };
76
82
  readonly 'session show': {
77
83
  readonly usage: 'session show [session]';
78
84
  readonly summary: 'Print the briefing a hook would give this session';
@@ -183,12 +189,24 @@ export declare const COMMAND_SPECS: {
183
189
  readonly args: 1;
184
190
  readonly needsHub: true;
185
191
  };
192
+ readonly 'criterion set': {
193
+ readonly usage: 'criterion set <m#r> [--text <text>] [--done-when <text>]';
194
+ readonly summary: 'Rewrite a criterion\'s text or doneWhen; done stays as it is (use criterion check/uncheck)';
195
+ readonly args: 1;
196
+ readonly needsHub: true;
197
+ };
186
198
  readonly 'task set': {
187
199
  readonly usage: 'task set <m/t> [--status] [--reason --waiting-for --signal] [--repo] [--branch --worktree --base --merge]';
188
200
  readonly summary: 'Change status, repository or branch state; --status blocked needs --reason';
189
201
  readonly args: 1;
190
202
  readonly needsHub: true;
191
203
  };
204
+ readonly 'task show': {
205
+ readonly usage: 'task show <m/t>';
206
+ readonly summary: 'Print a task: status, kind, checklist and links';
207
+ readonly args: 1;
208
+ readonly needsHub: true;
209
+ };
192
210
  readonly 'item check': {
193
211
  readonly usage: 'item check <m/t#c> --evidence <evidence>';
194
212
  readonly summary: 'Check an item with evidence';
@@ -280,8 +298,8 @@ export declare const COMMAND_SPECS: {
280
298
  readonly needsHub: true;
281
299
  };
282
300
  readonly 'ops list': {
283
- readonly usage: 'ops list [--month YYYY-MM]';
284
- readonly summary: 'List logged external operations';
301
+ readonly usage: 'ops list [--month YYYY-MM] [--ref <ref>]';
302
+ readonly summary: 'List logged external operations, optionally filtered by month or by the exact ref that was logged';
285
303
  readonly args: 0;
286
304
  readonly needsHub: true;
287
305
  };
@@ -291,6 +309,12 @@ export declare const COMMAND_SPECS: {
291
309
  readonly args: 1;
292
310
  readonly needsHub: true;
293
311
  };
312
+ readonly 'decision list': {
313
+ readonly usage: 'decision list <m>';
314
+ readonly summary: 'List a mainline\'s decisions';
315
+ readonly args: 1;
316
+ readonly needsHub: true;
317
+ };
294
318
  };
295
319
  export type CommandName = keyof typeof COMMAND_SPECS;
296
320
  export declare const OPTIONS: {
@@ -340,6 +364,9 @@ export declare const OPTIONS: {
340
364
  readonly text: {
341
365
  readonly type: 'string';
342
366
  };
367
+ readonly 'done-when': {
368
+ readonly type: 'string';
369
+ };
343
370
  readonly serves: {
344
371
  readonly type: 'string';
345
372
  };
package/dist/facts.js CHANGED
@@ -49,6 +49,12 @@ const COMMAND_SPECS = {
49
49
  args: 0,
50
50
  needsHub: true
51
51
  },
52
+ "session handoff": {
53
+ usage: "session handoff <session> [--session <session>]",
54
+ summary: "Take over the unfinished actions of another session into this one",
55
+ args: 1,
56
+ needsHub: true
57
+ },
52
58
  "session show": {
53
59
  usage: "session show [session]",
54
60
  summary: "Print the briefing a hook would give this session",
@@ -159,12 +165,24 @@ const COMMAND_SPECS = {
159
165
  args: 1,
160
166
  needsHub: true
161
167
  },
168
+ "criterion set": {
169
+ usage: "criterion set <m#r> [--text <text>] [--done-when <text>]",
170
+ summary: "Rewrite a criterion's text or doneWhen; done stays as it is (use criterion check/uncheck)",
171
+ args: 1,
172
+ needsHub: true
173
+ },
162
174
  "task set": {
163
175
  usage: "task set <m/t> [--status] [--reason --waiting-for --signal] [--repo] [--branch --worktree --base --merge]",
164
176
  summary: "Change status, repository or branch state; --status blocked needs --reason",
165
177
  args: 1,
166
178
  needsHub: true
167
179
  },
180
+ "task show": {
181
+ usage: "task show <m/t>",
182
+ summary: "Print a task: status, kind, checklist and links",
183
+ args: 1,
184
+ needsHub: true
185
+ },
168
186
  "item check": {
169
187
  usage: "item check <m/t#c> --evidence <evidence>",
170
188
  summary: "Check an item with evidence",
@@ -256,8 +274,8 @@ const COMMAND_SPECS = {
256
274
  needsHub: true
257
275
  },
258
276
  "ops list": {
259
- usage: "ops list [--month YYYY-MM]",
260
- summary: "List logged external operations",
277
+ usage: "ops list [--month YYYY-MM] [--ref <ref>]",
278
+ summary: "List logged external operations, optionally filtered by month or by the exact ref that was logged",
261
279
  args: 0,
262
280
  needsHub: true
263
281
  },
@@ -266,6 +284,12 @@ const COMMAND_SPECS = {
266
284
  summary: "Mark a decision as replaced by another",
267
285
  args: 1,
268
286
  needsHub: true
287
+ },
288
+ "decision list": {
289
+ usage: "decision list <m>",
290
+ summary: "List a mainline's decisions",
291
+ args: 1,
292
+ needsHub: true
269
293
  }
270
294
  };
271
295
  const OPTIONS = {
@@ -287,6 +311,7 @@ const OPTIONS = {
287
311
  "delegated-from": { type: "string" },
288
312
  "handoff-from": { type: "string" },
289
313
  text: { type: "string" },
314
+ "done-when": { type: "string" },
290
315
  serves: { type: "string" },
291
316
  before: { type: "string" },
292
317
  note: { type: "string" },
package/dist/hooks.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as HubError, A as readSession, B as decorateReply, H as headerLine, M as setSessionInfo, N as setSessionStatus, O as ensureSession, P as settleSession, R as sessionBriefing, U as mayDecorate, V as endsWithMarker, dt as findHubRoot, et as loadHub, ft as worktreesOf, i as hostName, lt as DATA_DIR, ut as currentBranch, z as turnReminder } from "./host-Dwf1wLMd.js";
1
+ import { G as mayDecorate, H as decorateReply, I as setSessionInfo, L as setSessionStatus, O as sessionBriefing, P as readSession, R as settleSession, U as endsWithMarker, W as headerLine, dt as DATA_DIR, ft as currentBranch, i as hostName, j as ensureSession, k as turnReminder, mt as worktreesOf, nt as loadHub, pt as findHubRoot, tt as HubError } from "./host-mlpW8M1s.js";
2
2
  import { appendFileSync, existsSync, readFileSync } from "node:fs";
3
3
  import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
4
4
  //#region src/hooks.ts
@@ -75,21 +75,45 @@ const displayReply = (root, { payload }) => {
75
75
  displayContent: shown
76
76
  } });
77
77
  };
78
+ /** Mainline ids a reply mentions, e.g. m001 in `m001/t003`; not inside a longer word or path. */
79
+ const MAINLINE_MENTION = /(?<![\w/#.[-])(m\d{3})(?![\w\]])/g;
80
+ /** The open mainlines the text mentions; loads the hub only when the text mentions one. */
81
+ function openMainlinesIn(root, text) {
82
+ const mentioned = new Set([...text.matchAll(MAINLINE_MENTION)].map((m) => m[1]));
83
+ if (mentioned.size === 0) return [];
84
+ return loadHub(root, { remotes: false }).mainlines.filter((m) => mentioned.has(m.id) && !m.archivedAt && (m.status === "active" || m.status === "planning")).map((m) => m.id);
85
+ }
86
+ /**
87
+ * What to ask for when a turn ends without its header line: the header itself when an action is in progress, or
88
+ * an action first when the queue is empty but the reply talks about an open mainline, since the header only names
89
+ * the session's own action in progress. Null when there is nothing to ask for.
90
+ */
91
+ function missingHeaderRequest(root, { payload, cli }) {
92
+ if (!payload.session_id || payload.stop_hook_active) return null;
93
+ const message = payload.last_assistant_message ?? "";
94
+ if (endsWithMarker(message)) return null;
95
+ const session = readSession(root, claudeCodeSessionId(payload.session_id));
96
+ const header = headerLine(session);
97
+ if (header) return `[mainflow] 這一輪最後一則回覆的最後一行不是標頭。請只回一則內容為「${header}」的訊息,不加其他文字。`;
98
+ if (session && session.actions.length > 0) return null;
99
+ const mainlines = openMainlinesIn(root, message);
100
+ if (mainlines.length === 0) return null;
101
+ return `[mainflow] 這一輪的回覆提到 ${mainlines.join("、")},但這場會話沒有行動,所以回覆結尾沒有標頭。這一輪若在推進它,先用 ${cli} action add "<做法>" --serves <m/t#c> 列出行動並用 ${cli} action set <a編號> --status doing 標為進行中,或用 ${cli} session handoff <會話> 接手上一場沒做完的行動;再只回一則內容為「▸ <m/t> · <a編號>」的訊息,不加其他文字。只是提到、沒有推進時,不必處理。`;
102
+ }
78
103
  /**
79
- * Stop: settle the session, then ask for the header line when a session with an action in progress ended its
80
- * turn without one. Only the model knows which message closes a turn, so the header is its to write; this makes
81
- * a forgotten one cost a single extra message. stop_hook_active stops it from asking twice in a row.
104
+ * Stop: ask for what the turn's header needs, then settle the session. Only the model knows which message closes a
105
+ * turn, so the header is its to write; this makes a forgotten one cost a single extra message. stop_hook_active
106
+ * stops it from asking twice in a row. A session asked to list an action is not settled yet: settling removes a
107
+ * session with no actions, and the action it is asked to add needs the session to exist.
82
108
  */
83
109
  const settleAndCheckMarker = (root, context) => {
84
- settleOnEnd(root, context);
85
- const { payload } = context;
86
- if (!payload.session_id || payload.stop_hook_active || endsWithMarker(payload.last_assistant_message ?? "")) return null;
87
- const header = headerLine(readSession(root, claudeCodeSessionId(payload.session_id)));
88
- if (!header) return null;
89
- return JSON.stringify({ hookSpecificOutput: {
110
+ const request = missingHeaderRequest(root, context);
111
+ const session = context.payload.session_id ? readSession(root, claudeCodeSessionId(context.payload.session_id)) : null;
112
+ if (!request || session && session.actions.length > 0) settleOnEnd(root, context);
113
+ return request ? JSON.stringify({ hookSpecificOutput: {
90
114
  hookEventName: "Stop",
91
- additionalContext: `[mainflow] 這一輪最後一則回覆的最後一行不是標頭。請只回一則內容為「${header}」的訊息,不加其他文字。`
92
- } });
115
+ additionalContext: request
116
+ } }) : null;
93
117
  };
94
118
  const CLAUDE_CODE_EVENTS = {
95
119
  "session-start": (root, { payload, envFile, cli, startUi }) => {
@@ -32,10 +32,11 @@ function currentBranch(cwd) {
32
32
  }
33
33
  /**
34
34
  * The hub for `cwd`: the first directory upwards holding `.mainflow/config.yaml`, except that a linked
35
- * worktree's checkout of a hub resolves to the same hub in the main working tree. A worktree is another
36
- * checkout of the same repository, so without this every worktree would write its own copy of the hub and
37
- * the copies would conflict on merge. A different hub nested inside a worktree (a scratch hub under tmp/,
38
- * say) has no counterpart in the main tree and stays as found.
35
+ * worktree's checkout of a hub at the repository root resolves to the same hub in the main working tree.
36
+ * A worktree is another checkout of the same repository, so without this every worktree would write its
37
+ * own copy of the hub and the copies would conflict on merge. A hub below the root (a sample hub under
38
+ * examples/, a scratch hub under tmp/) stays as found: it is data the worktree's own branch owns, and
39
+ * mapping it would let tests and migrations in the worktree write the main tree's copy.
39
40
  */
40
41
  function findHubRoot(cwd) {
41
42
  let found = null;
@@ -48,8 +49,8 @@ function findHubRoot(cwd) {
48
49
  }
49
50
  const trees = worktreesOf(cwd);
50
51
  if (!trees || trees.main === trees.current) return found;
51
- const counterpart = join(trees.main, relative(trees.current, found ?? trees.current));
52
- return isHub(counterpart) ? counterpart : found;
52
+ if (found && found !== trees.current) return found;
53
+ return isHub(trees.main) ? trees.main : found;
53
54
  }
54
55
  function subdirs(dir) {
55
56
  if (!existsSync(dir)) return [];
@@ -846,7 +847,7 @@ function checkReferences(input) {
846
847
  });
847
848
  }
848
849
  }
849
- const isOpen = (kind, status) => !CLOSED_STATUS[kind].includes(status);
850
+ const isOpen$2 = (kind, status) => !CLOSED_STATUS[kind].includes(status);
850
851
  /**
851
852
  * What a reader without the conversation needs to pick the work up: every open task says what it is for
852
853
  * and when it is done, and every open criterion or item says what has to be true to check it. Closed
@@ -871,10 +872,10 @@ function contentGaps(mainline, tasks) {
871
872
  });
872
873
  });
873
874
  };
874
- const mainlineOpen = isOpen("mainline", mainline.status);
875
+ const mainlineOpen = isOpen$2("mainline", mainline.status);
875
876
  list(mainline.file, "criteria", mainline.criteria, mainlineOpen);
876
877
  for (const task of tasks) {
877
- const open = mainlineOpen && isOpen("task", task.status);
878
+ const open = mainlineOpen && isOpen$2("task", task.status);
878
879
  list(task.file, "checklist", task.checklist, open);
879
880
  if (!open) continue;
880
881
  for (const section of TASK_REQUIRED_SECTIONS) if (!sectionText(task.body, section)) gaps.push({
@@ -1138,82 +1139,6 @@ function decorateReply(text, context) {
1138
1139
  /** Cheap test for text that decorateReply could change, so a display hook can skip loading the hub. */
1139
1140
  const mayDecorate = (text) => /m\d{3}|[if]\d{3}|\.mainflow\/mainlines/.test(text) || endsWithMarker(text);
1140
1141
  //#endregion
1141
- //#region ../core/dist/briefing.js
1142
- function describe(root, ref) {
1143
- const parsed = parseRef(ref);
1144
- if (parsed?.kind !== "checklistItem") return null;
1145
- try {
1146
- const mainline = readFrontMatter(mainlineFile(root, parsed.mainline), mainlineSchema).data;
1147
- const task = readFrontMatter(taskFile(root, parsed.mainline, parsed.task), taskSchema).data;
1148
- const item = task.checklist.find((c) => c.id === parsed.item);
1149
- if (!item) return null;
1150
- return {
1151
- ref,
1152
- text: item.text,
1153
- checked: item.done,
1154
- where: `${mainline.id} ${mainline.title} › ${task.id} ${task.title}`
1155
- };
1156
- } catch {
1157
- return null;
1158
- }
1159
- }
1160
- /** Problems in a session's queue that the executor should fix before going on, each with the command that fixes it. */
1161
- function queueWarnings(root, session, cli) {
1162
- const warnings = [];
1163
- const doing = session.actions.filter((a) => a.status === "doing");
1164
- if (doing.length > 1) warnings.push(`同時有 ${doing.length} 個進行中的行動(${doing.map((a) => a.id).join("、")})。一次只推進一個,其餘改回 todo。`);
1165
- for (const action of doing) {
1166
- const served = action.serves.map((ref) => describe(root, ref));
1167
- if (served.some((s) => s === null)) warnings.push(`${action.id} 服務的項目有的已不存在,用 ${cli} action set ${action.id} --serves <m/t#c> 改正。`);
1168
- else if (served.length > 0 && served.every((s) => s.checked)) warnings.push(`${action.id} 服務的項目都已勾選。做完了就執行 ${cli} action set ${action.id} --status done。`);
1169
- }
1170
- const next = session.actions.find((a) => a.status === "todo");
1171
- if (doing.length === 0 && next) warnings.push(`下一個行動是 ${next.id}。開始前先執行 ${cli} action set ${next.id} --status doing。`);
1172
- return warnings;
1173
- }
1174
- const current = (session) => session.actions.find((a) => a.status === "doing");
1175
- const servedLine = (root, action) => action.serves.map((ref) => {
1176
- const s = describe(root, ref);
1177
- return s ? `${ref} ${s.text}(${s.where})` : `${ref}(找不到這個項目)`;
1178
- }).join(";") || "沒有指定服務的 checklist 項目";
1179
- /** Full briefing for the start of a session, after resuming, clearing or compacting. */
1180
- function sessionBriefing(root, session, options) {
1181
- const { cli } = options;
1182
- const lines = [`[mainflow] 這場會話的 id 是 ${session.id}${options.envNote ? `(${options.envNote})` : ""}。`];
1183
- const doing = current(session);
1184
- const upcoming = session.actions.filter((a) => a.status === "todo").slice(0, 3);
1185
- if (session.actions.length === 0) {
1186
- const active = loadHub(root).mainlines.filter((m) => !m.archivedAt && (m.status === "active" || m.status === "planning"));
1187
- lines.push("這場會話還沒有行動。");
1188
- if (active.length > 0) {
1189
- lines.push("進行中的主脈絡:");
1190
- for (const m of active) lines.push(`- ${m.id} ${m.title}`);
1191
- lines.push(`要推進其中一個時,先用 ${cli} status 與它的任務 checklist 決定要做什麼,再列出行動。`);
1192
- }
1193
- } else {
1194
- lines.push(doing ? `目前行動:${doing.id} ${doing.do}` : "目前沒有進行中的行動。");
1195
- if (doing) lines.push(`服務:${servedLine(root, doing)}`);
1196
- if (upcoming.length > 0) lines.push(`接下來:${upcoming.map((a) => `${a.id} ${a.do}`).join("、")}`);
1197
- }
1198
- const warnings = queueWarnings(root, session, cli);
1199
- if (warnings.length > 0) lines.push("需要處理:", ...warnings.map((w) => `- ${w}`));
1200
- lines.push("工作方式:", `- 開始一件新工作前,先列進佇列:${cli} action add "<做法>" --serves <m/t#c>。`, `- 開始做標 doing,做完標 done:${cli} action set <a編號> --status doing。`, `- 勾選 checklist 要附證據:${cli} item check <m/t#c> --evidence <commit、檔案:行號或指令輸出>。`, `- 內容直接寫進檔案:意圖或需求寫進 mainline.md 的內文,任務的目標與完成定義寫進 task.md,選擇寫成決策(${cli} new decision <m> <slug> --title <標題> 建檔後再寫內文)。`, `- 改完 .mainflow/ 的檔案跑 ${cli} check,把它列出的問題修到沒有。`, "- 用完其他 skill 或子流程後,回到上面的目前行動再繼續。", "- 要開主脈絡、拆任務、記決策、延後或收尾時,用 mainflow skill,它依序帶過並告訴你資訊該寫在哪裡。");
1201
- return lines.join("\n");
1202
- }
1203
- /** One or two lines for every user turn; null when the session has no actions to anchor to. */
1204
- function turnReminder(root, session, options) {
1205
- if (session.actions.length === 0) return null;
1206
- const doing = current(session);
1207
- const open = session.actions.filter((a) => a.status === "todo" || a.status === "doing");
1208
- const head = doing ? `[mainflow] 目前行動 ${doing.id} ${doing.do},服務 ${doing.serves.join("、") || "(未指定)"}。這一輪的工作若不屬於它,先調整佇列再做。` : open.length === 0 ? `[mainflow] ${session.id} 的行動都已結束。繼續工作前先用 ${options.cli} action add 列出下一個行動。` : `[mainflow] ${session.id} 目前沒有進行中的行動。`;
1209
- const header = headerLine(session);
1210
- return [
1211
- head,
1212
- ...header ? [`- 這一輪最後一則回覆的最後一行寫「${header}」,回覆範例等其他內容都放在它前面。有顯示 hook 時,它會換成標題與連結。`] : [],
1213
- ...queueWarnings(root, session, options.cli).map((w) => `- ${w}`)
1214
- ].join("\n");
1215
- }
1216
- //#endregion
1217
1142
  //#region ../core/dist/sessions.js
1218
1143
  const sessionsDir = (root) => join(root, DATA_DIR, "local", "sessions");
1219
1144
  const sessionFile = (root, id) => join(sessionsDir(root), `${id}.yaml`);
@@ -1290,8 +1215,44 @@ function requireServes(root, serves) {
1290
1215
  if (!readFrontMatter(taskFile(root, parsed.mainline, parsed.task), taskSchema).data.checklist.some((c) => c.id === parsed.item)) throw new WriteError(`checklist item ${ref} not found`);
1291
1216
  }
1292
1217
  }
1218
+ const isOpen$1 = (a) => a.status === "todo" || a.status === "doing";
1219
+ /**
1220
+ * The previous session's unfinished actions, numbered after `existing`, with the previous session marked
1221
+ * handed-off. Each carried action is dropped in the previous session with a note naming its new reference,
1222
+ * so the work stays open in one place only and the old session still shows where it went. Runs inside the
1223
+ * caller's lock.
1224
+ */
1225
+ function takeUnfinished(root, from, to, existing) {
1226
+ const previousFile = requireSession(root, from);
1227
+ const previous = readYaml(previousFile, sessionSchema).data;
1228
+ const ids = [...existing];
1229
+ const carried = [];
1230
+ const at = nowMinute();
1231
+ const left = previous.actions.map((a) => {
1232
+ if (!isOpen$1(a)) return a;
1233
+ const id = nextId("a", ids);
1234
+ ids.push(id);
1235
+ carried.push({
1236
+ ...a,
1237
+ id
1238
+ });
1239
+ const handedOff = `handed off to ${to}#${id}`;
1240
+ return {
1241
+ ...a,
1242
+ status: "dropped",
1243
+ note: a.note ? `${a.note}; ${handedOff}` : handedOff,
1244
+ doneAt: at
1245
+ };
1246
+ });
1247
+ writeAtomic(previousFile, toYaml(validate(sessionSchema, {
1248
+ ...previous,
1249
+ status: "handed-off",
1250
+ actions: left
1251
+ }, previousFile)));
1252
+ return carried;
1253
+ }
1293
1254
  /**
1294
- * Start a session. Taking over from another session copies its unfinished actions into this one and marks
1255
+ * Start a session. Taking over from another session moves its unfinished actions into this one and marks
1295
1256
  * the other session handed-off, so the queue continues where it stopped.
1296
1257
  */
1297
1258
  function startSession(root, input) {
@@ -1301,19 +1262,7 @@ function startSession(root, input) {
1301
1262
  const file = sessionFile(root, id);
1302
1263
  if (existsSync(file)) throw new WriteError(`session ${id} already exists`);
1303
1264
  if (input.delegatedFrom) requireSession(root, input.delegatedFrom);
1304
- let carried = [];
1305
- if (input.handoffFrom) {
1306
- const previousFile = requireSession(root, input.handoffFrom);
1307
- const previous = readYaml(previousFile, sessionSchema).data;
1308
- carried = previous.actions.filter((a) => a.status === "todo" || a.status === "doing").map((a, i) => ({
1309
- ...a,
1310
- id: `a${String(i + 1).padStart(3, "0")}`
1311
- }));
1312
- writeAtomic(previousFile, toYaml(validate(sessionSchema, {
1313
- ...previous,
1314
- status: "handed-off"
1315
- }, previousFile)));
1316
- }
1265
+ const carried = input.handoffFrom ? takeUnfinished(root, input.handoffFrom, id, []) : [];
1317
1266
  const session = validate(sessionSchema, {
1318
1267
  id,
1319
1268
  agent: input.agent,
@@ -1351,6 +1300,32 @@ function ensureSession(root, input) {
1351
1300
  created: false
1352
1301
  };
1353
1302
  }
1303
+ /**
1304
+ * Take over another session's unfinished actions into a session that already exists, such as one a host hook
1305
+ * created when the conversation opened. The receiving session must have nothing unfinished of its own, so the
1306
+ * carried queue is not mixed into work already under way; its finished actions stay and the carried ones are
1307
+ * numbered after them.
1308
+ */
1309
+ function handoffSession(root, id, from) {
1310
+ if (id === from) throw new WriteError(`session ${id} cannot take over from itself`);
1311
+ return withLock(root, () => {
1312
+ const file = requireSession(root, id);
1313
+ const session = readYaml(file, sessionSchema).data;
1314
+ if (session.handoffFrom && session.handoffFrom !== from) throw new WriteError(`session ${id} already took over from ${session.handoffFrom}`);
1315
+ const open = session.actions.filter(isOpen$1);
1316
+ if (open.length > 0) throw new WriteError(`session ${id} has unfinished actions (${open.map((a) => a.id).join(", ")}); finish or drop them before taking over from ${from}`);
1317
+ if (!readYaml(requireSession(root, from), sessionSchema).data.actions.some(isOpen$1)) throw new WriteError(`session ${from} has no unfinished actions to take over`);
1318
+ const carried = takeUnfinished(root, from, id, session.actions.map((a) => a.id));
1319
+ const next = validate(sessionSchema, {
1320
+ ...session,
1321
+ status: "active",
1322
+ handoffFrom: from,
1323
+ actions: [...session.actions, ...carried]
1324
+ }, file);
1325
+ writeAtomic(file, toYaml(next));
1326
+ return next;
1327
+ });
1328
+ }
1354
1329
  /** Where a session's transcript is on disk: a platform records an absolute path, a hub may record one relative to its root. */
1355
1330
  function transcriptPath(root, transcript) {
1356
1331
  return isAbsolute(transcript) ? transcript : join(root, transcript);
@@ -1455,6 +1430,106 @@ function updateAction(root, ref, patch) {
1455
1430
  return updated;
1456
1431
  }
1457
1432
  //#endregion
1433
+ //#region ../core/dist/briefing.js
1434
+ function describe(root, ref) {
1435
+ const parsed = parseRef(ref);
1436
+ if (parsed?.kind !== "checklistItem") return null;
1437
+ try {
1438
+ const mainline = readFrontMatter(mainlineFile(root, parsed.mainline), mainlineSchema).data;
1439
+ const task = readFrontMatter(taskFile(root, parsed.mainline, parsed.task), taskSchema).data;
1440
+ const item = task.checklist.find((c) => c.id === parsed.item);
1441
+ if (!item) return null;
1442
+ return {
1443
+ ref,
1444
+ text: item.text,
1445
+ checked: item.done,
1446
+ where: `${mainline.id} ${mainline.title} › ${task.id} ${task.title}`
1447
+ };
1448
+ } catch {
1449
+ return null;
1450
+ }
1451
+ }
1452
+ /** Problems in a session's queue that the executor should fix before going on, each with the command that fixes it. */
1453
+ function queueWarnings(root, session, cli) {
1454
+ const warnings = [];
1455
+ const doing = session.actions.filter((a) => a.status === "doing");
1456
+ if (doing.length > 1) warnings.push(`同時有 ${doing.length} 個進行中的行動(${doing.map((a) => a.id).join("、")})。一次只推進一個,其餘改回 todo。`);
1457
+ for (const action of doing) {
1458
+ const served = action.serves.map((ref) => describe(root, ref));
1459
+ if (served.some((s) => s === null)) warnings.push(`${action.id} 服務的項目有的已不存在,用 ${cli} action set ${action.id} --serves <m/t#c> 改正。`);
1460
+ else if (served.length > 0 && served.every((s) => s.checked)) warnings.push(`${action.id} 服務的項目都已勾選。做完了就執行 ${cli} action set ${action.id} --status done。`);
1461
+ }
1462
+ const next = session.actions.find((a) => a.status === "todo");
1463
+ if (doing.length === 0 && next) warnings.push(`下一個行動是 ${next.id}。開始前先執行 ${cli} action set ${next.id} --status doing。`);
1464
+ return warnings;
1465
+ }
1466
+ const current = (session) => session.actions.find((a) => a.status === "doing");
1467
+ const isOpen = (a) => a.status === "todo" || a.status === "doing";
1468
+ /** Mainlines a session could be pushing forward. */
1469
+ const openMainlines = (root) => loadHub(root).mainlines.filter((m) => !m.archivedAt && (m.status === "active" || m.status === "planning"));
1470
+ /** How recently a session's transcript must have been written for the session to count as possibly still running. */
1471
+ const RUNNING_WINDOW_MS = 9e5;
1472
+ /** Whether the session's transcript was written within the running window; false when no transcript is recorded or readable. */
1473
+ function mayBeRunning(root, session) {
1474
+ if (!session.transcript) return false;
1475
+ try {
1476
+ return Date.now() - statSync(transcriptPath(root, session.transcript)).mtimeMs < RUNNING_WINDOW_MS;
1477
+ } catch {
1478
+ return false;
1479
+ }
1480
+ }
1481
+ /**
1482
+ * Lines naming the other top-level sessions that still hold unfinished actions, with the command that takes them
1483
+ * over into this one. Subagent sessions are left out: their work returns to the session that delegated it.
1484
+ */
1485
+ function handoffLines(root, session, cli) {
1486
+ const others = loadSessions(root).sessions.filter((s) => s.id !== session.id && !s.delegatedFrom && !s.archivedAt && (s.status === "active" || s.status === "paused") && s.actions.some(isOpen));
1487
+ if (others.length === 0) return [];
1488
+ return [`其他會話還有沒做完的行動。要接續的是其中一場已經停下的工作時,先用 ${cli} session handoff <會話> 把它的行動接進這場會話,再開始做:`, ...others.map((s) => `- ${s.id}${mayBeRunning(root, s) ? "(15 分鐘內還有寫入對話紀錄,可能還在執行,不要接手)" : ""}:${s.actions.filter(isOpen).map((a) => `${a.id} ${a.do}(${a.status},服務 ${a.serves.join("、") || "未指定"})`).join(";")}`)];
1489
+ }
1490
+ const servedLine = (root, action) => action.serves.map((ref) => {
1491
+ const s = describe(root, ref);
1492
+ return s ? `${ref} ${s.text}(${s.where})` : `${ref}(找不到這個項目)`;
1493
+ }).join(";") || "沒有指定服務的 checklist 項目";
1494
+ /** Full briefing for the start of a session, after resuming, clearing or compacting. */
1495
+ function sessionBriefing(root, session, options) {
1496
+ const { cli } = options;
1497
+ const lines = [`[mainflow] 這場會話的 id 是 ${session.id}${options.envNote ? `(${options.envNote})` : ""}。`];
1498
+ const doing = current(session);
1499
+ const upcoming = session.actions.filter((a) => a.status === "todo").slice(0, 3);
1500
+ if (session.actions.length === 0) {
1501
+ const active = openMainlines(root);
1502
+ lines.push("這場會話還沒有行動。");
1503
+ if (active.length > 0) {
1504
+ lines.push("進行中的主脈絡:");
1505
+ for (const m of active) lines.push(`- ${m.id} ${m.title}`);
1506
+ lines.push(`要推進其中一個時,先用 ${cli} status 與它的任務 checklist 決定要做什麼,再列出行動。`);
1507
+ }
1508
+ } else {
1509
+ lines.push(doing ? `目前行動:${doing.id} ${doing.do}` : "目前沒有進行中的行動。");
1510
+ if (doing) lines.push(`服務:${servedLine(root, doing)}`);
1511
+ if (upcoming.length > 0) lines.push(`接下來:${upcoming.map((a) => `${a.id} ${a.do}`).join("、")}`);
1512
+ }
1513
+ if (!session.actions.some(isOpen)) lines.push(...handoffLines(root, session, cli));
1514
+ const warnings = queueWarnings(root, session, cli);
1515
+ if (warnings.length > 0) lines.push("需要處理:", ...warnings.map((w) => `- ${w}`));
1516
+ lines.push("工作方式:", `- 開始一件新工作前,先列進佇列:${cli} action add "<做法>" --serves <m/t#c>。`, `- 開始做標 doing,做完標 done:${cli} action set <a編號> --status doing。`, `- 勾選 checklist 要附證據:${cli} item check <m/t#c> --evidence <commit、檔案:行號或指令輸出>。`, `- 內容直接寫進檔案:意圖或需求寫進 mainline.md 的內文,任務的目標與完成定義寫進 task.md,選擇寫成決策(${cli} new decision <m> <slug> --title <標題> 建檔後再寫內文)。`, `- 改完 .mainflow/ 的檔案跑 ${cli} check,把它列出的問題修到沒有。`, "- 用完其他 skill 或子流程後,回到上面的目前行動再繼續。", "- 要開主脈絡、拆任務、記決策、延後或收尾時,用 mainflow skill,它依序帶過並告訴你資訊該寫在哪裡。");
1517
+ return lines.join("\n");
1518
+ }
1519
+ /** One or two lines for every user turn; null when the session has no actions and the hub has no open mainline. */
1520
+ function turnReminder(root, session, options) {
1521
+ if (session.actions.length === 0) return openMainlines(root).length > 0 ? `[mainflow] ${session.id} 還沒有行動。這一輪要推進主脈絡時,先用 ${options.cli} action add 列出行動並標為 doing,或用 ${options.cli} session handoff <會話> 接手上一場沒做完的行動,再開始做。回覆結尾的標頭只看這場會話進行中的行動。` : null;
1522
+ const doing = current(session);
1523
+ const open = session.actions.filter((a) => a.status === "todo" || a.status === "doing");
1524
+ const head = doing ? `[mainflow] 目前行動 ${doing.id} ${doing.do},服務 ${doing.serves.join("、") || "(未指定)"}。這一輪的工作若不屬於它,先調整佇列再做。` : open.length === 0 ? `[mainflow] ${session.id} 的行動都已結束。繼續工作前先用 ${options.cli} action add 列出下一個行動。` : `[mainflow] ${session.id} 目前沒有進行中的行動。`;
1525
+ const header = headerLine(session);
1526
+ return [
1527
+ head,
1528
+ ...header ? [`- 這一輪最後一則回覆的最後一行寫「${header}」,回覆範例等其他內容都放在它前面。有顯示 hook 時,它會換成標題與連結。`] : [],
1529
+ ...queueWarnings(root, session, options.cli).map((w) => `- ${w}`)
1530
+ ].join("\n");
1531
+ }
1532
+ //#endregion
1458
1533
  //#region ../core/dist/write.js
1459
1534
  function checkTaskLinks(root, mainline, task) {
1460
1535
  if (task.repo && !(task.repo in readConfig(root).repos)) throw new WriteError(`repo "${task.repo}" is not listed in .mainflow/config.yaml; add it under repos first`);
@@ -1583,6 +1658,17 @@ function setCriterion(root, refInput, done, evidence, expectedVersion) {
1583
1658
  setListItem(criterion, done, evidence);
1584
1659
  }, expectedVersion);
1585
1660
  }
1661
+ /** Rewrite a criterion's text or doneWhen, leaving done and evidence as they are. */
1662
+ function setCriterionText(root, refInput, patch, expectedVersion) {
1663
+ const ref = requireRef(refInput, "criterion");
1664
+ if (patch.text === void 0 && patch.doneWhen === void 0) throw new WriteError(`${refInput} needs --text or --done-when, e.g. --text "CLI can create mainlines"`);
1665
+ return mutateFrontMatter(root, mainlineFile(root, ref.mainline), mainlineSchema, (m) => {
1666
+ const criterion = m.criteria.find((c) => c.id === ref.criterion);
1667
+ if (!criterion) throw new WriteError(`criterion ${refInput} not found`);
1668
+ if (patch.text !== void 0) criterion.text = patch.text;
1669
+ if (patch.doneWhen !== void 0) criterion.doneWhen = patch.doneWhen;
1670
+ }, expectedVersion);
1671
+ }
1586
1672
  /** Check or uncheck a criterion or checklist item; checking clears a drop. */
1587
1673
  function setListItem(item, done, evidence) {
1588
1674
  item.done = done;
@@ -2054,4 +2140,4 @@ function openInApp(hub, target, closeAfterMs = 150) {
2054
2140
  return true;
2055
2141
  }
2056
2142
  //#endregion
2057
- export { HubError as $, readSession as A, decorateReply as B, setMainlineStatus as C, addAction as D, updateTask as E, startSession as F, closeIssue as G, headerLine as H, transcriptPath as I, planIssue as J, createFinding as K, updateAction as L, setSessionInfo as M, setSessionStatus as N, ensureSession as O, settleSession as P, setIssueArchived as Q, sessionBriefing as R, setCriterion as S, supersedeDecision as T, mayDecorate as U, endsWithMarker as V, appendOps as W, setFindingArchived as X, reopenIssue as Y, setFindingStatus as Z, createTask as _, openInApp as a, MAINLINE_FILE as at, setArchived as b, syncHost as c, WriteError as ct, setArchivedMany as d, findHubRoot as dt, loadHub as et, acceptDecision as f, worktreesOf as ft, createMainline as g, createDecision as h, hostName as i, refIndex as it, setSessionArchived as j, loadSessions as k, migrateHub as l, DATA_DIR as lt, addRepo as m, DEFAULT_UI_PORT as n, mainlineEdges as nt, removeHostLink as o, TASK_FILE as ot, addHub as p, createIssue as q, caddySites as r, linksInto as rt, siteBlock as s, ConflictError as st, APP_BUNDLE_ID as t, dependencyGraph as tt, ARCHIVE_KINDS as u, currentBranch as ut, dropChecklistItem as v, setMainlineTitle as w, setChecklistItem as x, initHub as y, turnReminder as z };
2143
+ export { setFindingStatus as $, addAction as A, transcriptPath as B, setCriterionText as C, updateTask as D, supersedeDecision as E, setSessionArchived as F, mayDecorate as G, decorateReply as H, setSessionInfo as I, createFinding as J, appendOps as K, setSessionStatus as L, handoffSession as M, loadSessions as N, sessionBriefing as O, readSession as P, setFindingArchived as Q, settleSession as R, setCriterion as S, setMainlineTitle as T, endsWithMarker as U, updateAction as V, headerLine as W, planIssue as X, createIssue as Y, reopenIssue as Z, createTask as _, openInApp as a, linksInto as at, setArchived as b, syncHost as c, TASK_FILE as ct, setArchivedMany as d, DATA_DIR as dt, setIssueArchived as et, acceptDecision as f, currentBranch as ft, createMainline as g, createDecision as h, hostName as i, mainlineEdges as it, ensureSession as j, turnReminder as k, migrateHub as l, ConflictError as lt, addRepo as m, worktreesOf as mt, DEFAULT_UI_PORT as n, loadHub as nt, removeHostLink as o, refIndex as ot, addHub as p, findHubRoot as pt, closeIssue as q, caddySites as r, dependencyGraph as rt, siteBlock as s, MAINLINE_FILE as st, APP_BUNDLE_ID as t, HubError as tt, ARCHIVE_KINDS as u, WriteError as ut, dropChecklistItem as v, setMainlineStatus as w, setChecklistItem as x, initHub as y, startSession as z };
package/dist/host.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as openInApp, c as syncHost, i as hostName, n as DEFAULT_UI_PORT, o as removeHostLink, r as caddySites, s as siteBlock, t as APP_BUNDLE_ID } from "./host-Dwf1wLMd.js";
1
+ import { a as openInApp, c as syncHost, i as hostName, n as DEFAULT_UI_PORT, o as removeHostLink, r as caddySites, s as siteBlock, t as APP_BUNDLE_ID } from "./host-mlpW8M1s.js";
2
2
  export { APP_BUNDLE_ID, DEFAULT_UI_PORT, caddySites, hostName, openInApp, removeHostLink, siteBlock, syncHost };
@@ -1,4 +1,4 @@
1
- import { c as syncHost, ct as WriteError, i as hostName, lt as DATA_DIR, n as DEFAULT_UI_PORT, o as removeHostLink } from "./host-Dwf1wLMd.js";
1
+ import { c as syncHost, dt as DATA_DIR, i as hostName, n as DEFAULT_UI_PORT, o as removeHostLink, ut as WriteError } from "./host-mlpW8M1s.js";
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { spawn, spawnSync } from "node:child_process";
@@ -176,7 +176,7 @@ async function launchUi(root, hub, configured, options) {
176
176
  }
177
177
  let server;
178
178
  if (!reuse) {
179
- const { serveUi } = await import("./ui-CLMrfnhR.js");
179
+ const { serveUi } = await import("./ui-BbIFSMKR.js");
180
180
  server = await serveUi(root, choice.port);
181
181
  writeUiRecord(root, {
182
182
  port: choice.port,
package/dist/launch.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as portCandidates, c as watchHubGone, i as networkProbe, n as choosePort, o as readUiRecord, r as launchUi, s as startUiInBackground, t as PORT_SPAN } from "./launch-D5K564tw.js";
1
+ import { a as portCandidates, c as watchHubGone, i as networkProbe, n as choosePort, o as readUiRecord, r as launchUi, s as startUiInBackground, t as PORT_SPAN } from "./launch-CJYDVfE9.js";
2
2
  export { PORT_SPAN, choosePort, launchUi, networkProbe, portCandidates, readUiRecord, startUiInBackground, watchHubGone };
@@ -1,6 +1,6 @@
1
- import { A as readSession, C as setMainlineStatus, E as updateTask, I as transcriptPath, Q as setIssueArchived, S as setCriterion, X as setFindingArchived, a as openInApp, b as setArchived, ct as WriteError, d as setArchivedMany, et as loadHub, it as refIndex, j as setSessionArchived, k as loadSessions, lt as DATA_DIR, nt as mainlineEdges, rt as linksInto, st as ConflictError, tt as dependencyGraph, u as ARCHIVE_KINDS, w as setMainlineTitle, x as setChecklistItem } from "./host-Dwf1wLMd.js";
1
+ import { B as transcriptPath, D as updateTask, F as setSessionArchived, N as loadSessions, P as readSession, Q as setFindingArchived, S as setCriterion, T as setMainlineTitle, a as openInApp, at as linksInto, b as setArchived, d as setArchivedMany, dt as DATA_DIR, et as setIssueArchived, it as mainlineEdges, lt as ConflictError, nt as loadHub, ot as refIndex, rt as dependencyGraph, u as ARCHIVE_KINDS, ut as WriteError, w as setMainlineStatus, x as setChecklistItem } from "./host-mlpW8M1s.js";
2
2
  import { t as listProgress } from "./progress-1BfPg2zN.js";
3
- import { l as CLI_VERSION } from "./launch-D5K564tw.js";
3
+ import { l as CLI_VERSION } from "./launch-CJYDVfE9.js";
4
4
  import { createRequire } from "node:module";
5
5
  import { closeSync, existsSync, openSync, readFileSync, readSync, statSync, watch } from "node:fs";
6
6
  import { basename, dirname, extname, join, normalize, relative, sep } from "node:path";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mainflow",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "description": "本機多 AI 協作的主脈絡任務管理:CLI 與介面",
5
5
  "license": "MIT",
6
6
  "type": "module",