mainflow 0.35.0 → 0.37.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.
package/dist/cli.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { G as parseRef, K as parseUntil, N as UNTIL_FORMS, et as stampMs, n as CLOSED_STATUS, q as refExample } from "./dist-BdvWGTsb.js";
3
- import { B as applyOperation, C as readSession, H as HubError, Q as nodeStorage, S as loadSessions, V as checkHubName, W as loadHub, X as nowMinute, _ as initHub, _t as DATA_DIR, b as sessionBriefing, d as retroReport, dt as TASK_FILE, f as retroReportFromReceiver, ft as requireSlug, m as RETRY_RULES, mt as findHubRoot, nt as MAX_FILE_BYTES, p as receiverFromEnv, u as migrateHub, ut as MAINLINE_FILE, v as untilState, vt as WriteError, y as duplicateClaimLines } from "./host-bnHYIwuY.js";
2
+ import { G as parseRef, K as parseUntil, N as UNTIL_FORMS, et as stampMs, n as CLOSED_STATUS, q as refExample } from "./dist-ud09dzZo.js";
3
+ import { $ as nowMinute, B as applyOperation, C as readSession, Ct as WriteError, H as HubError, Q as unavailableHubs, S as loadSessions, St as DATA_DIR, V as checkHubName, W as loadHub, Y as memberOfGroup, Z as remoteMembers, _ as initHub, _t as requireSlug, at as MAX_FILE_BYTES, b as sessionBriefing, d as retroReport, f as retroReportFromReceiver, gt as TASK_FILE, ht as MAINLINE_FILE, m as RETRY_RULES, p as receiverFromEnv, tt as nodeStorage, u as migrateHub, v as untilState, y as duplicateClaimLines, yt as findHubRoot } from "./host-lY8ytTbw.js";
4
4
  import { t as listProgress } from "./progress-1BfPg2zN.js";
5
- import { C as ServerError, D as syncSettings, E as hubName, O as SETTINGS_FILE, S as ServerApi, T as earlierToken, _ as readDaemon, a as remoteMode, b as readRemote, c as planAnnounce, d as toolCallOf, f as startDaemon, g as probeDaemon, h as callDaemon, i as queuedCount, j as syncPaths, l as planFetch, m as DaemonUnavailable, n as hubWriter, o as fetchDenial, p as DaemonRunning, r as writtenLines, s as hookAnswer, t as CONFIRM_MS, u as planFlush, v as silentHint, w as ENV, x as oversizedFiles } from "./operation-DVcJgKM9.js";
5
+ import { C as ServerError, D as syncSettings, E as hubName, O as SETTINGS_FILE, S as ServerApi, T as earlierToken, _ as readDaemon, a as remoteMode, b as readRemote, c as planAnnounce, d as toolCallOf, f as startDaemon, g as probeDaemon, h as callDaemon, i as queuedCount, j as syncPaths, l as planFetch, m as DaemonUnavailable, n as hubWriter, o as fetchDenial, p as DaemonRunning, r as writtenLines, s as hookAnswer, t as CONFIRM_MS, u as planFlush, v as silentHint, w as ENV, x as oversizedFiles } from "./operation-BZ7CHB6J.js";
6
6
  import { COMMAND_SPECS, OPTIONS } from "./facts.js";
7
7
  import { hookFallback, parsePayload, runHook } from "./hooks.js";
8
- import { f as CLI_VERSION, i as launchUi, l as startUiInBackground, r as exitCodeFor, u as watchHubGone } from "./launch-D80CYPcm.js";
8
+ import { f as CLI_VERSION, i as launchUi, l as startUiInBackground, r as exitCodeFor, u as watchHubGone } from "./launch-3HLHjQF0.js";
9
9
  import { randomBytes } from "node:crypto";
10
10
  import { existsSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
11
11
  import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
@@ -524,6 +524,18 @@ function retroText(report) {
524
524
  //#endregion
525
525
  //#region src/cli.ts
526
526
  const write = (spec) => spec;
527
+ /**
528
+ * A group of another hub (`other-hub:w001`) must exist there when that hub is on this machine; the operation only
529
+ * checks that the hub is registered, since a hub kept on another machine cannot be read from here.
530
+ */
531
+ const checkGroupOption = ({ root, opts }) => {
532
+ const ref = opts.group ? parseRef(opts.group) : null;
533
+ if (ref?.kind !== "group" || ref.hub === void 0) return;
534
+ const hub = loadHub(root);
535
+ if (ref.hub === hub.config.hub) return;
536
+ const remote = hub.remotes.find((r) => r.name === ref.hub);
537
+ if (remote?.hub && !remote.hub.groups.some((g) => g.id === ref.group)) throw new WriteError(`group ${ref.group} not found in hub "${ref.hub}"`);
538
+ };
527
539
  /** Where a document an operation created or changed is, as the commands print it: .mainflow/... */
528
540
  const inData = (...parts) => join(DATA_DIR, ...parts);
529
541
  const WRITES = {
@@ -651,6 +663,7 @@ const WRITES = {
651
663
  }
652
664
  }),
653
665
  "new mainline": write({
666
+ check: checkGroupOption,
654
667
  op: "mainline.new",
655
668
  input: ({ args, opts }) => ({
656
669
  slug: args[0],
@@ -687,6 +700,7 @@ const WRITES = {
687
700
  print: (_, { ref, file }) => done(`created ${ref} ${inData(file)}`)
688
701
  }),
689
702
  "new issue": write({
703
+ check: checkGroupOption,
690
704
  op: "issue.new",
691
705
  input: ({ args, opts }) => ({
692
706
  slug: args[0],
@@ -734,6 +748,7 @@ const WRITES = {
734
748
  print: (_, { id, file }) => done(`created ${id} ${inData(file)}`)
735
749
  }),
736
750
  "mainline set": write({
751
+ check: checkGroupOption,
737
752
  op: "mainline.set",
738
753
  input: ({ args, opts }) => ({
739
754
  id: args[0],
@@ -891,6 +906,7 @@ const WRITES = {
891
906
  print: (_, i) => done(`closed ${i.id}`)
892
907
  }),
893
908
  "issue set": write({
909
+ check: checkGroupOption,
894
910
  op: "issue.set",
895
911
  input: ({ args, opts }) => ({
896
912
  id: args[0],
@@ -1353,14 +1369,23 @@ function printStatus(hub, all) {
1353
1369
  */
1354
1370
  function printGroups(hub, all) {
1355
1371
  const rank = (p) => (p ?? "P2").slice(1);
1372
+ const remoteByGroup = remoteMembers(hub.remotes, hub.config.hub);
1356
1373
  const shown = hub.groups.filter((g) => all || g.status !== "done").sort((a, b) => rank(a.priority).localeCompare(rank(b.priority)) || a.id.localeCompare(b.id));
1357
1374
  for (const g of shown) {
1358
1375
  console.log(` ${g.id} ${g.status.padEnd(4)} ${g.priority ?? "P2"} ${g.title}${g.trigger ? ` (時機:${g.trigger})` : ""}`);
1359
- const mainlines = hub.mainlines.filter((m) => m.group === g.id && (all || !m.archivedAt && !CLOSED_STATUS.mainline.includes(m.status)));
1360
- const issues = hub.issues.filter((i) => i.group === g.id && (all || !i.archivedAt && i.status !== "closed"));
1376
+ const mainlines = hub.mainlines.filter((m) => memberOfGroup(m.group, g.id, hub.config.hub) && (all || !m.archivedAt && !CLOSED_STATUS.mainline.includes(m.status)));
1377
+ const issues = hub.issues.filter((i) => memberOfGroup(i.group, g.id, hub.config.hub) && (all || !i.archivedAt && i.status !== "closed"));
1361
1378
  for (const m of [...mainlines.filter((m) => m.role === "plan"), ...mainlines.filter((m) => m.role !== "plan")]) console.log(` ${m.id} ${(m.role ?? "impl").padEnd(4)} ${m.status.padEnd(8)} ${m.title}`);
1362
1379
  for (const i of issues) console.log(` ${i.id} issue ${i.status.padEnd(8)} ${i.title}`);
1380
+ const remote = (remoteByGroup.get(g.id) ?? []).filter((m) => all || !m.archived && m.open);
1381
+ for (const m of [
1382
+ ...remote.filter((m) => m.role === "plan"),
1383
+ ...remote.filter((m) => m.kind === "mainline" && m.role !== "plan"),
1384
+ ...remote.filter((m) => m.kind === "issue")
1385
+ ]) console.log(` ${m.ref} ${(m.kind === "issue" ? "issue" : m.role).padEnd(4)} ${m.status.padEnd(8)} ${m.title}`);
1363
1386
  }
1387
+ const missing = unavailableHubs(hub.remotes);
1388
+ if (missing.length) console.log(` note: hub ${missing.join(", ")} not loaded, so their members are not listed`);
1364
1389
  return done(`${shown.length} groups`);
1365
1390
  }
1366
1391
  function printProblems(problems, mark = "✗") {
@@ -699,8 +699,11 @@ const documentShape = {
699
699
  links: z.array(linkSchema).optional()
700
700
  };
701
701
  const priority = z.enum(PRIORITIES).optional();
702
- /** The hub-level group a mainline or an issue belongs to, at most one; work that goes together, apart from what depends on what */
703
- const group = id("group").optional();
702
+ /**
703
+ * The hub-level group a mainline or an issue belongs to, at most one; work that goes together, apart from what depends on what.
704
+ * It is a group of this hub (`w001`) or of another one (`other-hub:w001`), so work in one hub can join a group of another.
705
+ */
706
+ const group = z.string().refine((s) => parseRef(s)?.kind === "group", `expected a group like ${refExample("group")}, or one of another hub's like other-hub:${refExample("group")}`).optional();
704
707
  const evidenceRequired = (item, ctx) => {
705
708
  if (item.done && !item.evidence) ctx.addIssue({
706
709
  code: "custom",
@@ -889,6 +892,11 @@ const UNTIL_FORMS = [
889
892
  kind: "file",
890
893
  format: "file:{relative path}",
891
894
  example: "file:packages/cli/dist/cli.js"
895
+ },
896
+ {
897
+ kind: "mainline-done",
898
+ format: "mainline-done:m{NNN} or mainline-done:{hub}:m{NNN}",
899
+ example: "mainline-done:other-hub:m031"
892
900
  }
893
901
  ];
894
902
  const UNTIL_EXAMPLES = UNTIL_FORMS.map((f) => f.format).join(", ");
@@ -922,6 +930,10 @@ function parseUntil(input) {
922
930
  kind,
923
931
  path: argument
924
932
  } : null;
933
+ case "mainline-done": return parseRef(argument)?.kind === "mainline" ? {
934
+ kind,
935
+ mainline: argument
936
+ } : null;
925
937
  default: return null;
926
938
  }
927
939
  }
package/dist/facts.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { g as SurveyLine, l as ID_PATTERNS, m as SLUG, o as FORMAT_VERSION, p as SERVES_REF, r as CHECKLIST_REF, v as Until } from "./index-DgYB5u1f.js";
1
+ import { g as SurveyLine, l as ID_PATTERNS, m as SLUG, o as FORMAT_VERSION, p as SERVES_REF, r as CHECKLIST_REF, v as Until } from "./index-B32s8PHu.js";
2
2
  //#region src/facts.d.ts
3
3
  export interface CommandSpec {
4
4
  usage: string;
@@ -125,7 +125,7 @@ export declare const COMMAND_SPECS: {
125
125
  };
126
126
  readonly 'new mainline': {
127
127
  readonly usage: 'new mainline <slug> --title <title> [--group <w>] [--role plan|impl] [--priority P0|P1|P2|P3]';
128
- readonly summary: 'Create mainline.md with the next id and the section template; write the rest in the file. --group puts it in an existing group; --role plan makes it the mainline that splits the work, coordinates it and makes the final check (without --role it is an impl mainline)';
128
+ readonly summary: 'Create mainline.md with the next id and the section template; write the rest in the file. --group puts it in an existing group, or in one of another registered hub as {hub}:w001; --role plan makes it the mainline that splits the work, coordinates it and makes the final check (without --role it is an impl mainline)';
129
129
  readonly args: 1;
130
130
  readonly needsHub: true;
131
131
  };
@@ -143,7 +143,7 @@ export declare const COMMAND_SPECS: {
143
143
  };
144
144
  readonly 'new issue': {
145
145
  readonly usage: 'new issue <slug> --title <title> [--origin <ref>] [--labels a,b] [--group <w>] [--priority P0|P1|P2|P3]';
146
- readonly summary: 'Create an issue file for a follow-up that must outlive its mainline; --group puts it with the work it is taken up with';
146
+ readonly summary: 'Create an issue file for a follow-up that must outlive its mainline; --group puts it with the work it is taken up with, in this hub or another registered one ({hub}:w001)';
147
147
  readonly args: 1;
148
148
  readonly needsHub: true;
149
149
  };
@@ -167,13 +167,13 @@ export declare const COMMAND_SPECS: {
167
167
  };
168
168
  readonly 'group list': {
169
169
  readonly usage: 'group list [--all]';
170
- readonly summary: 'List open groups by priority with their trigger and members, plan mainlines first (--all includes done groups)';
170
+ readonly summary: 'List open groups by priority with their trigger and members, plan mainlines first; members in other hubs read {hub}:{id}, and a registered hub that is not loaded is noted (--all includes done groups)';
171
171
  readonly args: 0;
172
172
  readonly needsHub: true;
173
173
  };
174
174
  readonly 'mainline set': {
175
175
  readonly usage: 'mainline set <m> [--status <status>] [--group <w|none>] [--role plan|impl] [--priority P0|P1|P2|P3]';
176
- readonly summary: 'Change status, group, role or priority; done needs every criterion met and no proposed decision. A planning mainline becomes active by itself when work on it starts, so use --status for paused, done and dropped. --group none leaves the group';
176
+ readonly summary: 'Change status, group, role or priority; done needs every criterion met and no proposed decision. A planning mainline becomes active by itself when work on it starts, so use --status for paused, done and dropped. --group none leaves the group; {hub}:w001 joins a group of another registered hub';
177
177
  readonly args: 1;
178
178
  readonly needsHub: true;
179
179
  };
@@ -293,7 +293,7 @@ export declare const COMMAND_SPECS: {
293
293
  };
294
294
  readonly 'issue set': {
295
295
  readonly usage: 'issue set <i> [--group <w|none>] [--priority P0|P1|P2|P3]';
296
- readonly summary: 'Put an issue in a group, or take it out with --group none, and change its priority';
296
+ readonly summary: 'Put an issue in a group (of this hub, or {hub}:w001 of another registered one), or take it out with --group none, and change its priority';
297
297
  readonly args: 1;
298
298
  readonly needsHub: true;
299
299
  };
@@ -620,7 +620,7 @@ export declare const RETRO_FORMATS: {
620
620
  };
621
621
  /**
622
622
  * Conditions a blocked action's `until` takes and `wait` checks, one example each: a commit on a branch whose
623
- * message contains the text (git log runs at the hub root), an action that is done, a path relative to the hub root that exists (`..` reaches sibling repositories).
623
+ * message contains the text (git log runs at the hub root), an action that is done, a path relative to the hub root that exists (`..` reaches sibling repositories), a mainline of this hub or a registered hub that is done.
624
624
  */
625
625
  export declare const UNTIL: readonly {
626
626
  kind: Until['kind'];
package/dist/facts.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as SURVEY_SECTION, D as SESSION_STATUS, M as TASK_SECTIONS, N as UNTIL_FORMS, O as SLUG, P as WORK_STATUS, S as PRIORITIES, T as SERVES_REF, _ as LINK_RELS, a as DECISION_SECTIONS, b as MAINLINE_STATUS, c as DEFAULT_ROLE, d as FORMAT_VERSION, f as GROUP_SECTIONS, g as ISSUE_STATUS, h as ISSUE_SECTIONS, i as DECISION_BASES, j as TASK_REQUIRED_SECTIONS, k as SURVEY_LINES, l as FINDING_SECTIONS, m as ID_PATTERNS, n as CLOSED_STATUS, nt as GRANT_FORMS, o as DECISION_STATUS, p as GROUP_STATUS, r as DECIDERS, rt as GRANT_TARGET_FORMS, s as DEFAULT_CATEGORIES, t as CHECKLIST_REF, tt as GRANT_ACTIONS, u as FINDING_STATUS, v as MAINLINE_ROLES, x as MERGE_STATES, y as MAINLINE_SECTIONS } from "./dist-BdvWGTsb.js";
1
+ import { A as SURVEY_SECTION, D as SESSION_STATUS, M as TASK_SECTIONS, N as UNTIL_FORMS, O as SLUG, P as WORK_STATUS, S as PRIORITIES, T as SERVES_REF, _ as LINK_RELS, a as DECISION_SECTIONS, b as MAINLINE_STATUS, c as DEFAULT_ROLE, d as FORMAT_VERSION, f as GROUP_SECTIONS, g as ISSUE_STATUS, h as ISSUE_SECTIONS, i as DECISION_BASES, j as TASK_REQUIRED_SECTIONS, k as SURVEY_LINES, l as FINDING_SECTIONS, m as ID_PATTERNS, n as CLOSED_STATUS, nt as GRANT_FORMS, o as DECISION_STATUS, p as GROUP_STATUS, r as DECIDERS, rt as GRANT_TARGET_FORMS, s as DEFAULT_CATEGORIES, t as CHECKLIST_REF, tt as GRANT_ACTIONS, u as FINDING_STATUS, v as MAINLINE_ROLES, x as MERGE_STATES, y as MAINLINE_SECTIONS } from "./dist-ud09dzZo.js";
2
2
  import { a as LABEL_ROLES, i as FINDING_SEVERITIES, n as FINDING_CLASSES, r as FINDING_FIELDS, t as FINDINGS_FENCE } from "./retro-formats-ECy0gGOL.js";
3
3
  //#region src/facts.ts
4
4
  const COMMAND_SPECS = {
@@ -115,7 +115,7 @@ const COMMAND_SPECS = {
115
115
  },
116
116
  "new mainline": {
117
117
  usage: "new mainline <slug> --title <title> [--group <w>] [--role plan|impl] [--priority P0|P1|P2|P3]",
118
- summary: "Create mainline.md with the next id and the section template; write the rest in the file. --group puts it in an existing group; --role plan makes it the mainline that splits the work, coordinates it and makes the final check (without --role it is an impl mainline)",
118
+ summary: "Create mainline.md with the next id and the section template; write the rest in the file. --group puts it in an existing group, or in one of another registered hub as {hub}:w001; --role plan makes it the mainline that splits the work, coordinates it and makes the final check (without --role it is an impl mainline)",
119
119
  args: 1,
120
120
  needsHub: true
121
121
  },
@@ -133,7 +133,7 @@ const COMMAND_SPECS = {
133
133
  },
134
134
  "new issue": {
135
135
  usage: "new issue <slug> --title <title> [--origin <ref>] [--labels a,b] [--group <w>] [--priority P0|P1|P2|P3]",
136
- summary: "Create an issue file for a follow-up that must outlive its mainline; --group puts it with the work it is taken up with",
136
+ summary: "Create an issue file for a follow-up that must outlive its mainline; --group puts it with the work it is taken up with, in this hub or another registered one ({hub}:w001)",
137
137
  args: 1,
138
138
  needsHub: true
139
139
  },
@@ -157,13 +157,13 @@ const COMMAND_SPECS = {
157
157
  },
158
158
  "group list": {
159
159
  usage: "group list [--all]",
160
- summary: "List open groups by priority with their trigger and members, plan mainlines first (--all includes done groups)",
160
+ summary: "List open groups by priority with their trigger and members, plan mainlines first; members in other hubs read {hub}:{id}, and a registered hub that is not loaded is noted (--all includes done groups)",
161
161
  args: 0,
162
162
  needsHub: true
163
163
  },
164
164
  "mainline set": {
165
165
  usage: "mainline set <m> [--status <status>] [--group <w|none>] [--role plan|impl] [--priority P0|P1|P2|P3]",
166
- summary: "Change status, group, role or priority; done needs every criterion met and no proposed decision. A planning mainline becomes active by itself when work on it starts, so use --status for paused, done and dropped. --group none leaves the group",
166
+ summary: "Change status, group, role or priority; done needs every criterion met and no proposed decision. A planning mainline becomes active by itself when work on it starts, so use --status for paused, done and dropped. --group none leaves the group; {hub}:w001 joins a group of another registered hub",
167
167
  args: 1,
168
168
  needsHub: true
169
169
  },
@@ -283,7 +283,7 @@ const COMMAND_SPECS = {
283
283
  },
284
284
  "issue set": {
285
285
  usage: "issue set <i> [--group <w|none>] [--priority P0|P1|P2|P3]",
286
- summary: "Put an issue in a group, or take it out with --group none, and change its priority",
286
+ summary: "Put an issue in a group (of this hub, or {hub}:w001 of another registered one), or take it out with --group none, and change its priority",
287
287
  args: 1,
288
288
  needsHub: true
289
289
  },
@@ -538,7 +538,7 @@ const RETRO_FORMATS = {
538
538
  };
539
539
  /**
540
540
  * Conditions a blocked action's `until` takes and `wait` checks, one example each: a commit on a branch whose
541
- * message contains the text (git log runs at the hub root), an action that is done, a path relative to the hub root that exists (`..` reaches sibling repositories).
541
+ * message contains the text (git log runs at the hub root), an action that is done, a path relative to the hub root that exists (`..` reaches sibling repositories), a mainline of this hub or a registered hub that is done.
542
542
  */
543
543
  const UNTIL = UNTIL_FORMS;
544
544
  /** Relation types a document's links take; readers derive the reverse direction. */
@@ -654,7 +654,7 @@ const LIST_ITEM = [
654
654
  const FIELDS = {
655
655
  mainline: [
656
656
  ...shared(...EVERY),
657
- field("group", false, "所屬的組 w{NNN},一條主脈絡最多一組;組和依賴無關"),
657
+ field("group", false, "所屬的組 w{NNN},或其他 hub 的組 {hub}:w{NNN},一條主脈絡最多一組;組和依賴無關"),
658
658
  field("role", false, "plan 是拆分顆粒、整合調度與最終確認的規劃型,impl 是執行的實作型;沒填視為 impl"),
659
659
  field("due", false, "預計完成的日期"),
660
660
  field("archivedAt", false, "封存的日期,和狀態分開"),
@@ -690,7 +690,7 @@ const FIELDS = {
690
690
  ],
691
691
  issue: [
692
692
  ...shared(...EVERY),
693
- field("group", false, "所屬的組 w{NNN},一筆待辦最多一組;同組的待辦在同一個時機一起做"),
693
+ field("group", false, "所屬的組 w{NNN},或其他 hub 的組 {hub}:w{NNN},一筆待辦最多一組;同組的待辦在同一個時機一起做"),
694
694
  field("order", false, "待辦之間的順序"),
695
695
  field("due", false, "預計完成的日期"),
696
696
  field("plannedIn", false, "排進的任務,planned 時必填"),
@@ -1,4 +1,4 @@
1
- import "./index-DgYB5u1f.js";
1
+ import "./index-B32s8PHu.js";
2
2
  //#region ../schema/dist/grants.d.ts
3
3
  /** An operation about to run, resolved to what a grant is written against. */
4
4
  type GrantRequest = {
@@ -1,4 +1,4 @@
1
- import { nt as GRANT_FORMS } from "./dist-BdvWGTsb.js";
1
+ import { nt as GRANT_FORMS } from "./dist-ud09dzZo.js";
2
2
  import { closeSync, constants, fstatSync, openSync, readFileSync, realpathSync } from "node:fs";
3
3
  import { isAbsolute, join, relative, resolve } from "node:path";
4
4
  import { spawnSync } from "node:child_process";
package/dist/hooks.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as Task, a as Decision, c as Group, d as Mainline, f as OpsEntry, h as Session, i as ChecklistItem, n as ActionBlock, s as Finding, t as Action, u as Issue } from "./index-DgYB5u1f.js";
1
+ import { _ as Task, a as Decision, c as Group, d as Mainline, f as OpsEntry, h as Session, i as ChecklistItem, n as ActionBlock, s as Finding, t as Action, u as Issue } from "./index-B32s8PHu.js";
2
2
  import { z } from "zod";
3
3
  //#region ../core/dist/ids/allocators.d.ts
4
4
  /** The letters ids start with. */
package/dist/hooks.js CHANGED
@@ -1,6 +1,6 @@
1
- import { F as configSchema, it as describeRequest } from "./dist-BdvWGTsb.js";
2
- import { A as mayDecorate, C as readSession, D as decorateReply, E as matchGrants, H as HubError, O as endsWithMarker, S as loadSessions, T as grantScope, W as loadHub, Z as readYaml, _t as DATA_DIR, b as sessionBriefing, ht as worktreesOf, i as hostName, k as headerLine, mt as findHubRoot, pt as currentBranch, x as turnReminder } from "./host-bnHYIwuY.js";
3
- import { n as hubWriter, r as writtenLines } from "./operation-DVcJgKM9.js";
1
+ import { F as configSchema, it as describeRequest } from "./dist-ud09dzZo.js";
2
+ import { A as mayDecorate, C as readSession, D as decorateReply, E as matchGrants, H as HubError, O as endsWithMarker, S as loadSessions, St as DATA_DIR, T as grantScope, W as loadHub, b as sessionBriefing, bt as worktreesOf, et as readYaml, i as hostName, k as headerLine, mt as hubFormatVersion, pt as formatMismatch, tt as nodeStorage, vt as currentBranch, x as turnReminder, yt as findHubRoot } from "./host-lY8ytTbw.js";
3
+ import { n as hubWriter, r as writtenLines } from "./operation-BZ7CHB6J.js";
4
4
  import { resolveFixed, scanCommand, scopedCwd } from "./grant-commands.js";
5
5
  import { appendFileSync, existsSync, readFileSync } from "node:fs";
6
6
  import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
@@ -425,6 +425,18 @@ const PROJECT_HUB_EVENTS = /* @__PURE__ */ new Set(["pre-tool-use"]);
425
425
  function hubRootFor(event, ctx) {
426
426
  return (PROJECT_HUB_EVENTS.has(event) && ctx.projectDir && isAbsolute(ctx.projectDir) ? findHubRoot(ctx.projectDir) : null) ?? findHubRoot(typeof ctx.payload.cwd === "string" ? ctx.payload.cwd : process.cwd());
427
427
  }
428
+ /** Events whose output reaches the model before it works, so they carry the notice that the hub's data format does not match this CLI. */
429
+ const FORMAT_NOTICE_EVENTS = /* @__PURE__ */ new Set(["session-start", "user-prompt-submit"]);
430
+ /** What the model is told when the hub's data format is not the one this CLI reads and writes; null when it is. */
431
+ function formatNotice(root, cli) {
432
+ const found = hubFormatVersion(nodeStorage(root));
433
+ const reason = found === null ? null : formatMismatch(found);
434
+ if (found === null || reason === null) return null;
435
+ return {
436
+ text: found < 3 ? `[mainflow] 這個 hub 的資料格式是 ${found},這版 mainflow 讀寫的是 3,要先跑 ${cli} migrate 才能讀寫紀錄。` : `[mainflow] 這個 hub 的資料格式是 ${found},比這版 mainflow 讀寫的 3 新,要先在這個 hub 安裝對應版本的 mainflow 才能讀寫紀錄。`,
437
+ reason
438
+ };
439
+ }
428
440
  /** What an event prints when it cannot run its handler; null for the events that stay silent. */
429
441
  function hookFallback(platform, event, ctx, failure) {
430
442
  const fallback = HOOK_FALLBACKS[platform]?.[event];
@@ -461,6 +473,11 @@ async function runHook(platform, event, ctx) {
461
473
  for (const line of writtenLines(written)) if (line.startsWith("!")) warn(`mainflow hook ${platform} ${event}: ${line.slice(2)}`);
462
474
  return written;
463
475
  };
476
+ const notice = FORMAT_NOTICE_EVENTS.has(event) ? formatNotice(root, ctx.cli) : null;
477
+ if (notice) return {
478
+ stdout: notice.text,
479
+ error: errorText(`mainflow hook ${platform} ${event}: ${notice.reason}`)
480
+ };
464
481
  return {
465
482
  stdout: await handler(root, {
466
483
  ...ctx,