mainflow 0.38.0 → 0.39.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 refExample, Q as stampMs, U as parseRef, W as parseUntil, j as UNTIL_FORMS, n as CLOSED_STATUS } from "./dist-CZYL5PJ4.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-aYx3DYrv.js";
2
+ import { $ as stampMs, G as parseUntil, K as refExample, M as UNTIL_FORMS, W as parseRef, n as CLOSED_STATUS } from "./dist-zSabglPq.js";
3
+ import { $ as unavailableHubs, C as loadSessions, Ct as DATA_DIR, G as loadHub, H as checkHubName, K as sectionText, Q as remoteMembers, U as HubError, V as applyOperation, X as memberOfGroup, _t as TASK_FILE, b as duplicateClaimLines, bt as findHubRoot, d as migrateHub, et as nowMinute, f as retroReport, gt as MAINLINE_FILE, h as RETRY_RULES, m as receiverFromEnv, nt as nodeStorage, ot as MAX_FILE_BYTES, p as retroReportFromReceiver, u as groupPlan, v as initHub, vt as requireSlug, w as readSession, wt as WriteError, x as sessionBriefing, y as untilState } from "./host-CIOd2WSh.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-1oQ8lA9C.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-DJARDCM0.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-D38sNgXM.js";
8
+ import { f as CLI_VERSION, i as launchUi, l as startUiInBackground, r as exitCodeFor, u as watchHubGone } from "./launch-DJ944-bI.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";
@@ -1098,6 +1098,7 @@ const RUNS = {
1098
1098
  return done(`${shown.length} issues`);
1099
1099
  },
1100
1100
  "group list": ({ root, opts }) => printGroups(loadHub(root), opts.all ?? false),
1101
+ "group show": ({ root, args }) => printGroup(loadHub(root), loadSessions(root).sessions, args[0]),
1101
1102
  "finding list": ({ root, opts }) => {
1102
1103
  const shown = loadHub(root).findings.filter((f) => opts.all || f.status === "valid");
1103
1104
  for (const f of shown) console.log(` ${f.id} ${f.created} ${f.title}${f.labels?.length ? ` [${f.labels.join(", ")}]` : ""}`);
@@ -1377,6 +1378,35 @@ function printGroups(hub, all) {
1377
1378
  if (missing.length) console.log(` note: hub ${missing.join(", ")} not loaded, so their members are not listed`);
1378
1379
  return done(`${shown.length} groups`);
1379
1380
  }
1381
+ /**
1382
+ * One group: goal, criteria, grants, then its members of this hub in the order their depends-on links give, each
1383
+ * with the actions in progress or blocked that serve it. Members in other hubs are listed by `group list`.
1384
+ */
1385
+ function printGroup(hub, sessions, idInput) {
1386
+ const ref = parseRef(idInput);
1387
+ if (ref?.kind !== "group") throw new WriteError(`expected a group reference like ${refExample("group")}, got "${idInput}"`);
1388
+ const g = hub.groups.find((x) => x.id === ref.group);
1389
+ if (!g) throw new WriteError(`group ${ref.group} not found; run "mainflow group list --all" to list groups`);
1390
+ console.log(`${g.id} ${g.status.padEnd(4)} ${g.priority ?? "P2"} ${g.title}${g.trigger ? ` (時機:${g.trigger})` : ""}`);
1391
+ const goal = sectionText(g.body, "目標");
1392
+ console.log(` 目標:${goal ? goal.replace(/\s+/g, " ") : "(未填)"}`);
1393
+ const criteria = g.criteria ?? [];
1394
+ console.log(` 條件:${criteria.length ? "" : "(無)"}`);
1395
+ for (const c of criteria) console.log(` ${c.dropped ? "[-]" : c.done ? "[x]" : "[ ]"} ${g.id}#${c.id} ${c.text}${c.dropped ? " (dropped)" : ""}`);
1396
+ const grants = g.grants ?? [];
1397
+ console.log(` 授權:${grants.length ? "" : "(無)"}`);
1398
+ for (const x of grants) console.log(` ${g.id}#${x.id} ${x.action} ${x.target}`);
1399
+ const plan = groupPlan(hub, sessions, g.id);
1400
+ const work = (list) => list.map((a) => `${a.session}#${a.action}(${a.status})`).join("、");
1401
+ console.log(` 成員:${plan.members.length ? "(依 depends-on 排序)" : "(無)"}`);
1402
+ plan.members.forEach((m, i) => {
1403
+ const waits = m.dependsOn.length ? ` 依賴 ${m.dependsOn.join("、")}` : "";
1404
+ const active = m.active.length ? ` 進行中:${work(m.active)}` : "";
1405
+ console.log(` ${i + 1}. ${m.ref} ${m.kind.padEnd(8)} ${m.status.padEnd(8)} ${m.title}${waits}${active}`);
1406
+ });
1407
+ if (plan.active.length) console.log(` 服務組條件的行動:${work(plan.active)}`);
1408
+ return done(`${plan.members.length} members`);
1409
+ }
1380
1410
  function printProblems(problems, mark = "✗") {
1381
1411
  for (const problem of problems) console.error(`${mark} ${problem.file}${problem.field ? ` › ${problem.field}` : ""}: ${problem.message}`);
1382
1412
  }
@@ -381,8 +381,11 @@ const ID_PATTERNS = {
381
381
  grant: /^g\d{3}$/
382
382
  };
383
383
  const CHECKLIST_REF = /^m\d{3}\/t\d{3}#c\d{3}$/;
384
- /** What an action may serve: a checklist item, or a mainline's criterion while the mainline has no task for it yet. */
385
- const SERVES_REF = /^m\d{3}(?:\/t\d{3}#c\d{3}|#r\d{3})$/;
384
+ /**
385
+ * What an action may serve: a checklist item, or a mainline's criterion while the mainline has no task for it yet,
386
+ * or a group's criterion (`w017#r001`).
387
+ */
388
+ const SERVES_REF = /^(?:m\d{3}(?:\/t\d{3}#c\d{3}|#r\d{3})|w\d{3}#r\d{3})$/;
386
389
  const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
387
390
  const DATE = /^\d{4}-\d{2}-\d{2}$/;
388
391
  /** A hub name: kebab-case, also the first label of {hub}.mainflow.localhost and the prefix of a cross-hub reference. */
@@ -410,6 +413,11 @@ const REF_FORMS = [
410
413
  pattern: /^(m\d{3})#(r\d{3})$/,
411
414
  example: "m001#r001"
412
415
  },
416
+ {
417
+ kind: "groupCriterion",
418
+ pattern: /^(w\d{3})#(r\d{3})$/,
419
+ example: "w001#r001"
420
+ },
413
421
  {
414
422
  kind: "decision",
415
423
  pattern: /^(m\d{3})\/(d\d{3})$/,
@@ -482,6 +490,11 @@ function parseLocal(input) {
482
490
  mainline: a,
483
491
  criterion: b
484
492
  };
493
+ case "groupCriterion": return {
494
+ kind: "groupCriterion",
495
+ group: a,
496
+ criterion: b
497
+ };
485
498
  case "decision": return {
486
499
  kind: "decision",
487
500
  mainline: a,
@@ -623,8 +636,13 @@ const FINDING_SECTIONS = [
623
636
  "影響",
624
637
  "做法"
625
638
  ];
626
- /** 說明 says what the group gathers and why; its first paragraph is the summary the interface shows on the group's heading. */
627
- const GROUP_SECTIONS = ["說明"];
639
+ /**
640
+ * 目標 says what the group's work is meant to achieve; 說明 says what the group gathers and its scope, and its first
641
+ * paragraph is the summary the interface shows on the group's heading.
642
+ */
643
+ const GROUP_SECTIONS = ["目標", "說明"];
644
+ /** The group section whose first paragraph is the summary. */
645
+ const GROUP_SUMMARY_SECTION = "說明";
628
646
  const SURVEY_SECTION = "現況查核";
629
647
  const SURVEY_LINES = [
630
648
  {
@@ -749,8 +767,9 @@ const costSchema = z.object({
749
767
  note: text.optional()
750
768
  });
751
769
  /**
752
- * One thing the mainline authorizes ahead of time: an action plus the target it covers. It applies only while
753
- * a session's action in progress serves this mainline and the mainline is still open.
770
+ * One thing a mainline or a group authorizes ahead of time: an action plus the target it covers. It applies only while
771
+ * a session's action in progress serves this mainline (or a mainline of this group, or the group's own criterion)
772
+ * and the mainline or group is still open.
754
773
  */
755
774
  const grantSchema = z.object({
756
775
  id: id("grant"),
@@ -1055,7 +1074,11 @@ const groupSchema = z.object({
1055
1074
  priority,
1056
1075
  ...documentShape,
1057
1076
  /** When to take the group's work up, in a phrase, e.g. 量測修正時 */
1058
- trigger: text.optional()
1077
+ trigger: text.optional(),
1078
+ /** What has to be true for the group's work as a whole to be done; the same shape as a mainline's, referenced as w001#r001 */
1079
+ criteria: z.array(criterionSchema).optional(),
1080
+ /** Granted ahead of time to the work of every member mainline, and to actions serving the group's own criteria; referenced as w001#g001 */
1081
+ grants: z.array(grantSchema).optional()
1059
1082
  });
1060
1083
  /** One line of ops/{YYYY-MM}.log: an external operation, append-only. */
1061
1084
  const opsEntrySchema = z.object({
@@ -1087,4 +1110,4 @@ function parseOpsLine(line) {
1087
1110
  return parsed.success ? parsed.data : null;
1088
1111
  }
1089
1112
  //#endregion
1090
- export { GRANT_ACTIONS as $, TASK_SECTIONS as A, opsEntrySchema as B, SERVES_REF as C, SURVEY_LINES as D, SLUG as E, findingSchema as F, refExample as G, parseOpsLine as H, formatOpsEntry as I, taskSchema as J, refMainline as K, groupSchema as L, WORK_STATUS as M, configSchema as N, SURVEY_SECTION as O, decisionSchema as P, stampMs as Q, issueSchema as R, RESERVED_HUB_NAMES as S, SESSION_STATUS as T, parseRef as U, parseActionRef as V, parseUntil as W, waitingForRef as X, waitingForAction as Y, OFFSET as Z, MAINLINE_SECTIONS as _, DECISION_SECTIONS as a, grantRepo as at, PRIORITIES as b, FINDING_SECTIONS as c, GROUP_SECTIONS as d, GRANT_FORMS as et, GROUP_STATUS as f, LINK_RELS as g, ISSUE_STATUS as h, DECISION_BASES as i, grantForm as it, UNTIL_FORMS as j, TASK_REQUIRED_SECTIONS as k, FINDING_STATUS as l, ISSUE_SECTIONS as m, CLOSED_STATUS as n, describeRequest as nt, DECISION_STATUS as o, parseGrantTarget as ot, ID_PATTERNS as p, sessionSchema as q, DECIDERS as r, grantCovers as rt, DEFAULT_CATEGORIES as s, CHECKLIST_REF as t, GRANT_TARGET_FORMS as tt, FORMAT_VERSION as u, MAINLINE_STATUS as v, SESSION_ID as w, REF_EXAMPLES as x, MERGE_STATES as y, mainlineSchema as z };
1113
+ export { stampMs as $, TASK_REQUIRED_SECTIONS as A, mainlineSchema as B, RESERVED_HUB_NAMES as C, SLUG as D, SESSION_STATUS as E, decisionSchema as F, parseUntil as G, parseActionRef as H, findingSchema as I, sessionSchema as J, refExample as K, formatOpsEntry as L, UNTIL_FORMS as M, WORK_STATUS as N, SURVEY_LINES as O, configSchema as P, OFFSET as Q, groupSchema as R, REF_EXAMPLES as S, SESSION_ID as T, parseOpsLine as U, opsEntrySchema as V, parseRef as W, waitingForAction as X, taskSchema as Y, waitingForRef as Z, LINK_RELS as _, DECISION_SECTIONS as a, grantForm as at, MERGE_STATES as b, FINDING_SECTIONS as c, GROUP_SECTIONS as d, GRANT_ACTIONS as et, GROUP_STATUS as f, ISSUE_STATUS as g, ISSUE_SECTIONS as h, DECISION_BASES as i, grantCovers as it, TASK_SECTIONS as j, SURVEY_SECTION as k, FINDING_STATUS as l, ID_PATTERNS as m, CLOSED_STATUS as n, GRANT_TARGET_FORMS as nt, DECISION_STATUS as o, grantRepo as ot, GROUP_SUMMARY_SECTION as p, refMainline as q, DECIDERS as r, describeRequest as rt, DEFAULT_CATEGORIES as s, parseGrantTarget as st, CHECKLIST_REF as t, GRANT_FORMS as tt, FORMAT_VERSION as u, MAINLINE_SECTIONS as v, SERVES_REF as w, PRIORITIES as x, MAINLINE_STATUS as y, issueSchema as z };
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-BH9gpiap.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-Dk4fCI7q.js";
2
2
  //#region src/facts.d.ts
3
3
  export interface CommandSpec {
4
4
  usage: string;
@@ -99,8 +99,8 @@ export declare const COMMAND_SPECS: {
99
99
  readonly needsHub: true;
100
100
  };
101
101
  readonly 'action add': {
102
- readonly usage: 'action add <text> --serves <m/t#c|m#r>[,...] [--before <a>] [--session <session>]';
103
- readonly summary: 'Add an action to the session queue; --serves names checklist items, or criteria while the mainline has no task for them. Prints a reminder when another session active in the last 15 minutes is doing what it serves';
102
+ readonly usage: 'action add <text> --serves <m/t#c|m#r|w#r>[,...] [--before <a>] [--session <session>]';
103
+ readonly summary: 'Add an action to the session queue; --serves names checklist items, or criteria while the mainline has no task for them, or a group\'s criterion (w#r) for work that belongs to the group as a whole. Prints a reminder when another session active in the last 15 minutes is doing what it serves';
104
104
  readonly args: 1;
105
105
  readonly needsHub: true;
106
106
  };
@@ -161,7 +161,13 @@ export declare const COMMAND_SPECS: {
161
161
  };
162
162
  readonly 'group set': {
163
163
  readonly usage: 'group set <w> [--status open|done] [--priority P0|P1|P2|P3] [--trigger <when>] [--title <title>]';
164
- readonly summary: 'Change a group\'s status, priority, trigger or title; an empty --trigger "" removes it. Relations between groups go in links in the file';
164
+ readonly summary: 'Change a group\'s status, priority, trigger or title; an empty --trigger "" removes it. --status done needs every criterion met or dropped and every mainline and issue of this hub in the group finished. Relations between groups go in links in the file';
165
+ readonly args: 1;
166
+ readonly needsHub: true;
167
+ };
168
+ readonly 'group show': {
169
+ readonly usage: 'group show <w>';
170
+ readonly summary: 'Print a group: goal, criteria, grants, and its members in the order their depends-on links give, each with the sessions whose actions in progress or blocked serve it';
165
171
  readonly args: 1;
166
172
  readonly needsHub: true;
167
173
  };
@@ -190,25 +196,25 @@ export declare const COMMAND_SPECS: {
190
196
  readonly needsHub: true;
191
197
  };
192
198
  readonly 'criterion add': {
193
- readonly usage: 'criterion add <m> <text> --done-when <text>';
194
- readonly summary: 'Add an open criterion at the end of the mainline\'s criteria with the next r id';
199
+ readonly usage: 'criterion add <m|w> <text> --done-when <text>';
200
+ readonly summary: 'Add an open criterion at the end of the mainline\'s or group\'s criteria with the next r id';
195
201
  readonly args: 2;
196
202
  readonly needsHub: true;
197
203
  };
198
204
  readonly 'criterion check': {
199
- readonly usage: 'criterion check <m#r> --evidence <evidence>';
205
+ readonly usage: 'criterion check <m#r|w#r> --evidence <evidence>';
200
206
  readonly summary: 'Mark a criterion as met';
201
207
  readonly args: 1;
202
208
  readonly needsHub: true;
203
209
  };
204
210
  readonly 'criterion uncheck': {
205
- readonly usage: 'criterion uncheck <m#r>';
211
+ readonly usage: 'criterion uncheck <m#r|w#r>';
206
212
  readonly summary: 'Mark a criterion as not met';
207
213
  readonly args: 1;
208
214
  readonly needsHub: true;
209
215
  };
210
216
  readonly 'criterion set': {
211
- readonly usage: 'criterion set <m#r> [--text <text>] [--done-when <text>]';
217
+ readonly usage: 'criterion set <m#r|w#r> [--text <text>] [--done-when <text>]';
212
218
  readonly summary: 'Rewrite a criterion\'s text or doneWhen; done stays as it is (use criterion check/uncheck)';
213
219
  readonly args: 1;
214
220
  readonly needsHub: true;
@@ -571,6 +577,10 @@ export declare const REFERENCES: readonly [{
571
577
  readonly target: 'criterion';
572
578
  readonly format: 'm{NNN}#r{NNN}';
573
579
  readonly example: 'm001#r001';
580
+ }, {
581
+ readonly target: 'group criterion';
582
+ readonly format: 'w{NNN}#r{NNN}';
583
+ readonly example: 'w001#r001';
574
584
  }, {
575
585
  readonly target: 'decision';
576
586
  readonly format: 'm{NNN}/d{NNN}';
@@ -629,8 +639,10 @@ export declare const LINKS: {
629
639
  readonly rels: readonly ["depends-on", "relates", "split-from"];
630
640
  };
631
641
  /**
632
- * What a mainline's grants authorize: each action with the form of its target. A grant applies only while the
633
- * session's action in progress serves the mainline and the mainline is active or planning. The PreToolUse hook
642
+ * What a mainline's or group's grants authorize: each action with the form of its target. A mainline's grant applies
643
+ * only while the session's action in progress serves the mainline and the mainline is active or planning. A group's
644
+ * grant applies while the group is open and the action serves one of the group's mainlines (of this hub) or the
645
+ * group's own criterion. The PreToolUse hook
634
646
  * allows `publish` and `release` only when the command is written in the fixed form in `forms`; the other
635
647
  * actions are for the executor to read.
636
648
  */
@@ -681,7 +693,7 @@ export declare const SECTIONS: {
681
693
  readonly decision: readonly ["背景", "選項", "建議", "決定", "理由"];
682
694
  readonly issue: readonly ["背景", "要做的事", "完成條件"];
683
695
  readonly finding: readonly ["發現", "影響", "做法"];
684
- readonly group: readonly ["說明"];
696
+ readonly group: readonly ["目標", "說明"];
685
697
  };
686
698
  /**
687
699
  * The mainline section that records what was checked before tasks were written, one `- {label}:{text}`
@@ -705,7 +717,7 @@ export declare const FIELDS: {
705
717
  readonly decision: readonly [...FieldSpec[], FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec];
706
718
  readonly issue: readonly [...FieldSpec[], FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec];
707
719
  readonly finding: readonly [...FieldSpec[], FieldSpec, FieldSpec, FieldSpec];
708
- readonly group: readonly [...FieldSpec[], FieldSpec];
720
+ readonly group: readonly [...FieldSpec[], FieldSpec, FieldSpec, FieldSpec];
709
721
  readonly criterion: FieldSpec[];
710
722
  readonly checklistItem: readonly [...FieldSpec[], FieldSpec];
711
723
  readonly grant: readonly [FieldSpec, FieldSpec, FieldSpec, FieldSpec];
package/dist/facts.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as GRANT_ACTIONS, A as TASK_SECTIONS, C as SERVES_REF, D as SURVEY_LINES, E as SLUG, M as WORK_STATUS, O as SURVEY_SECTION, T as SESSION_STATUS, _ as MAINLINE_SECTIONS, a as DECISION_SECTIONS, b as PRIORITIES, c as FINDING_SECTIONS, d as GROUP_SECTIONS, et as GRANT_FORMS, f as GROUP_STATUS, g as LINK_RELS, h as ISSUE_STATUS, i as DECISION_BASES, j as UNTIL_FORMS, k as TASK_REQUIRED_SECTIONS, l as FINDING_STATUS, m as ISSUE_SECTIONS, n as CLOSED_STATUS, o as DECISION_STATUS, p as ID_PATTERNS, r as DECIDERS, s as DEFAULT_CATEGORIES, t as CHECKLIST_REF, tt as GRANT_TARGET_FORMS, u as FORMAT_VERSION, v as MAINLINE_STATUS, y as MERGE_STATES } from "./dist-CZYL5PJ4.js";
1
+ import { A as TASK_REQUIRED_SECTIONS, D as SLUG, E as SESSION_STATUS, M as UNTIL_FORMS, N as WORK_STATUS, O as SURVEY_LINES, _ as LINK_RELS, a as DECISION_SECTIONS, b as MERGE_STATES, c as FINDING_SECTIONS, d as GROUP_SECTIONS, et as GRANT_ACTIONS, f as GROUP_STATUS, g as ISSUE_STATUS, h as ISSUE_SECTIONS, i as DECISION_BASES, j as TASK_SECTIONS, k as SURVEY_SECTION, l as FINDING_STATUS, m as ID_PATTERNS, n as CLOSED_STATUS, nt as GRANT_TARGET_FORMS, o as DECISION_STATUS, r as DECIDERS, s as DEFAULT_CATEGORIES, t as CHECKLIST_REF, tt as GRANT_FORMS, u as FORMAT_VERSION, v as MAINLINE_SECTIONS, w as SERVES_REF, x as PRIORITIES, y as MAINLINE_STATUS } from "./dist-zSabglPq.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 = {
@@ -89,8 +89,8 @@ const COMMAND_SPECS = {
89
89
  needsHub: true
90
90
  },
91
91
  "action add": {
92
- usage: "action add <text> --serves <m/t#c|m#r>[,...] [--before <a>] [--session <session>]",
93
- summary: "Add an action to the session queue; --serves names checklist items, or criteria while the mainline has no task for them. Prints a reminder when another session active in the last 15 minutes is doing what it serves",
92
+ usage: "action add <text> --serves <m/t#c|m#r|w#r>[,...] [--before <a>] [--session <session>]",
93
+ summary: "Add an action to the session queue; --serves names checklist items, or criteria while the mainline has no task for them, or a group's criterion (w#r) for work that belongs to the group as a whole. Prints a reminder when another session active in the last 15 minutes is doing what it serves",
94
94
  args: 1,
95
95
  needsHub: true
96
96
  },
@@ -151,7 +151,13 @@ const COMMAND_SPECS = {
151
151
  },
152
152
  "group set": {
153
153
  usage: "group set <w> [--status open|done] [--priority P0|P1|P2|P3] [--trigger <when>] [--title <title>]",
154
- summary: "Change a group's status, priority, trigger or title; an empty --trigger \"\" removes it. Relations between groups go in links in the file",
154
+ summary: "Change a group's status, priority, trigger or title; an empty --trigger \"\" removes it. --status done needs every criterion met or dropped and every mainline and issue of this hub in the group finished. Relations between groups go in links in the file",
155
+ args: 1,
156
+ needsHub: true
157
+ },
158
+ "group show": {
159
+ usage: "group show <w>",
160
+ summary: "Print a group: goal, criteria, grants, and its members in the order their depends-on links give, each with the sessions whose actions in progress or blocked serve it",
155
161
  args: 1,
156
162
  needsHub: true
157
163
  },
@@ -180,25 +186,25 @@ const COMMAND_SPECS = {
180
186
  needsHub: true
181
187
  },
182
188
  "criterion add": {
183
- usage: "criterion add <m> <text> --done-when <text>",
184
- summary: "Add an open criterion at the end of the mainline's criteria with the next r id",
189
+ usage: "criterion add <m|w> <text> --done-when <text>",
190
+ summary: "Add an open criterion at the end of the mainline's or group's criteria with the next r id",
185
191
  args: 2,
186
192
  needsHub: true
187
193
  },
188
194
  "criterion check": {
189
- usage: "criterion check <m#r> --evidence <evidence>",
195
+ usage: "criterion check <m#r|w#r> --evidence <evidence>",
190
196
  summary: "Mark a criterion as met",
191
197
  args: 1,
192
198
  needsHub: true
193
199
  },
194
200
  "criterion uncheck": {
195
- usage: "criterion uncheck <m#r>",
201
+ usage: "criterion uncheck <m#r|w#r>",
196
202
  summary: "Mark a criterion as not met",
197
203
  args: 1,
198
204
  needsHub: true
199
205
  },
200
206
  "criterion set": {
201
- usage: "criterion set <m#r> [--text <text>] [--done-when <text>]",
207
+ usage: "criterion set <m#r|w#r> [--text <text>] [--done-when <text>]",
202
208
  summary: "Rewrite a criterion's text or doneWhen; done stays as it is (use criterion check/uncheck)",
203
209
  args: 1,
204
210
  needsHub: true
@@ -485,6 +491,11 @@ const REFERENCES = [
485
491
  format: "m{NNN}#r{NNN}",
486
492
  example: "m001#r001"
487
493
  },
494
+ {
495
+ target: "group criterion",
496
+ format: "w{NNN}#r{NNN}",
497
+ example: "w001#r001"
498
+ },
488
499
  {
489
500
  target: "decision",
490
501
  format: "m{NNN}/d{NNN}",
@@ -543,8 +554,10 @@ const UNTIL = UNTIL_FORMS;
543
554
  /** Relation types a document's links take; readers derive the reverse direction. */
544
555
  const LINKS = { rels: LINK_RELS };
545
556
  /**
546
- * What a mainline's grants authorize: each action with the form of its target. A grant applies only while the
547
- * session's action in progress serves the mainline and the mainline is active or planning. The PreToolUse hook
557
+ * What a mainline's or group's grants authorize: each action with the form of its target. A mainline's grant applies
558
+ * only while the session's action in progress serves the mainline and the mainline is active or planning. A group's
559
+ * grant applies while the group is open and the action serves one of the group's mainlines (of this hub) or the
560
+ * group's own criterion. The PreToolUse hook
548
561
  * allows `publish` and `release` only when the command is written in the fixed form in `forms`; the other
549
562
  * actions are for the executor to read.
550
563
  */
@@ -691,7 +704,12 @@ const FIELDS = {
691
704
  field("reviewAfter", false, "到這天要重新確認它還成立"),
692
705
  field("archivedAt", false, "封存的日期,和狀態分開")
693
706
  ],
694
- group: [...shared(...EVERY), field("trigger", false, "什麼時機做這組的事,一句話,例如「量測修正時」")],
707
+ group: [
708
+ ...shared(...EVERY),
709
+ field("trigger", false, "什麼時機做這組的事,一句話,例如「量測修正時」"),
710
+ field("criteria", false, "這組的解決條件清單,每條的欄位見 criterion,參照寫 w{NNN}#r{NNN};組改成 done 前要全部已勾或已放棄"),
711
+ field("grants", false, "預先授權清單,每條的欄位見 grant,參照寫 w{NNN}#g{NNN};組是 open 時,對成員主脈絡的行動和服務這組解決條件的行動生效")
712
+ ],
695
713
  criterion: LIST_ITEM,
696
714
  checklistItem: [...LIST_ITEM, field("serves", false, "這個項目推進的解決條件,r{NNN} 清單")],
697
715
  grant: [
@@ -704,7 +722,7 @@ const FIELDS = {
704
722
  field("id", true, "會話內不重複的編號 a{NNN},由 action add 配發"),
705
723
  field("do", true, "要做的事,一句話"),
706
724
  field("status", true, "狀態值見 STATUSES,用 action set 改"),
707
- field("serves", true, "推進的 checklist 項目(m{NNN}/t{NNN}#c{NNN})或解決條件(m{NNN}#r{NNN})參照清單,可以是空的;主脈絡還沒拆任務時指向解決條件"),
725
+ field("serves", true, "推進的 checklist 項目(m{NNN}/t{NNN}#c{NNN})或解決條件(主脈絡 m{NNN}#r{NNN}、組 w{NNN}#r{NNN})參照清單,可以是空的;主脈絡還沒拆任務時指向解決條件"),
708
726
  field("note", false, "一句備註"),
709
727
  field("blockedBy", false, "卡住時的原因 reason、等待對象 waitingFor 與程式可判斷的條件 until,blocked 時必填,其他狀態不得有;等待對象整串是其他會話的行動(例如 claude-code-1a2b3c4d#a003)時,對方的簡報會列出有人在等它;條件格式見 UNTIL"),
710
728
  field("startedAt", false, "轉為 doing 時由 CLI 寫入"),
@@ -1,4 +1,4 @@
1
- import "./index-BH9gpiap.js";
1
+ import "./index-Dk4fCI7q.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 { et as GRANT_FORMS } from "./dist-CZYL5PJ4.js";
1
+ import { tt as GRANT_FORMS } from "./dist-zSabglPq.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-BH9gpiap.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-Dk4fCI7q.js";
2
2
  import { z } from "zod";
3
3
  //#region ../core/dist/ids/allocators.d.ts
4
4
  /** The letters ids start with. */
@@ -154,7 +154,7 @@ declare function setMainlineArchived(hub: HubReader, input: {
154
154
  archived: boolean;
155
155
  expectedVersion?: string;
156
156
  }, ctx: OpContext): OpResult<Mainline>;
157
- /** Add an open criterion at the end of the mainline's list, numbered by the context's allocator. */
157
+ /** Add an open criterion at the end of the list of the mainline or group in `mainline`, numbered by the context's allocator. */
158
158
  declare function addCriterion(hub: HubReader, input: ListItemInput & {
159
159
  mainline: string;
160
160
  }, ctx: OpContext): OpResult<{
@@ -165,7 +165,7 @@ declare function setCriterion(hub: HubReader, input: {
165
165
  done: boolean;
166
166
  evidence?: string;
167
167
  expectedVersion?: string;
168
- }, ctx: OpContext): OpResult<Mainline>;
168
+ }, ctx: OpContext): OpResult<Mainline | Group>;
169
169
  /** What `criterion set` rewrites; at least one must be given. `done` stays as it is; use criterion check/uncheck for that. */
170
170
  interface CriterionTextPatch {
171
171
  text?: string;
@@ -176,7 +176,7 @@ declare function setCriterionText(hub: HubReader, input: {
176
176
  ref: string;
177
177
  patch: CriterionTextPatch;
178
178
  expectedVersion?: string;
179
- }): OpResult<Mainline>;
179
+ }): OpResult<Mainline | Group>;
180
180
  //#endregion
181
181
  //#region ../core/dist/ops/tasks.d.ts
182
182
  interface TaskInput {
package/dist/hooks.js CHANGED
@@ -1,6 +1,6 @@
1
- import { N as configSchema, nt as describeRequest } from "./dist-CZYL5PJ4.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-aYx3DYrv.js";
3
- import { n as hubWriter, r as writtenLines } from "./operation-1oQ8lA9C.js";
1
+ import { P as configSchema, rt as describeRequest } from "./dist-zSabglPq.js";
2
+ import { A as headerLine, C as loadSessions, Ct as DATA_DIR, D as matchGrants, E as grantScope, G as loadHub, O as decorateReply, S as turnReminder, U as HubError, bt as findHubRoot, ht as hubFormatVersion, i as hostName, j as mayDecorate, k as endsWithMarker, mt as formatMismatch, nt as nodeStorage, tt as readYaml, w as readSession, x as sessionBriefing, xt as worktreesOf, yt as currentBranch } from "./host-CIOd2WSh.js";
3
+ import { n as hubWriter, r as writtenLines } from "./operation-DJARDCM0.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";
@@ -301,7 +301,7 @@ function grantableCall(payload) {
301
301
  }
302
302
  /** Outside a hub, or when deciding failed: ask for a grantable call, stay silent for any other. */
303
303
  function askWhenGrantable({ payload }, failure) {
304
- const reason = failure.kind === "outside-hub" ? "不在 mainflow hub 裡,沒有主脈絡授權可用" : failure.kind === "error" ? `判斷授權時出錯:${failure.message}` : "hook 讀不到 PreToolUse 的輸入";
304
+ const reason = failure.kind === "outside-hub" ? "不在 mainflow hub 裡,沒有授權可用" : failure.kind === "error" ? `判斷授權時出錯:${failure.message}` : "hook 讀不到 PreToolUse 的輸入";
305
305
  if (failure.kind === "unreadable-input") return askBecause(reason);
306
306
  try {
307
307
  return grantableCall(payload) ? askBecause(reason) : null;
@@ -318,7 +318,7 @@ async function logGranted(write, scope, call, request, grant, warn) {
318
318
  target: request.action === "publish" ? `npm ${request.name}` : `github release ${request.repos.join("/")}`,
319
319
  operation: opsText(call.text),
320
320
  result: `授權放行 ${grant.ref}(${describeRequest(request)})`,
321
- ref: scope.served[grant.mainline]
321
+ ref: scope.served[grant.owner]
322
322
  });
323
323
  } catch (error) {
324
324
  warn?.(`mainflow hook claude-code pre-tool-use: allowed by ${grant.ref} but could not write the ops log: ${error.message}`);
@@ -353,7 +353,7 @@ async function decideByGrants(root, { payload, warn, write }) {
353
353
  if (!match.ok) return askBecause(match.reason);
354
354
  const { grant } = match.matches[0];
355
355
  await logGranted(write, scoped.scope, scan.call, resolved.request, grant, warn);
356
- return permission("allow", `主脈絡授權 ${grant.ref} 涵蓋 ${describeRequest(resolved.request)}`);
356
+ return permission("allow", `授權 ${grant.ref} 涵蓋 ${describeRequest(resolved.request)}`);
357
357
  }
358
358
  /** How many problems outside the written file the reminder lists before it only gives the count. */
359
359
  const OTHER_PROBLEMS_SHOWN = 5;