mainflow 0.21.0 → 0.23.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,10 +1,10 @@
1
1
  #!/usr/bin/env node
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-BsLqiI0_.js";
3
- import { F as parseRef, I as refExample } from "./dist-BrotcvKi.js";
2
+ import { $ as reopenIssue, A as addAction, C as setCriterionText, D as updateTask, E as supersedeDecision, F as setSessionArchived, J as appendOps, L as setSessionStatus, M as handoffSession, O as sessionBriefing, P as readSession, Q as planIssue, S as setCriterion, V as updateAction, X as createFinding, Y as closeIssue, Z as createIssue, _ as createTask, b as setArchived, et as setFindingArchived, f as acceptDecision, ft as WriteError, g as createMainline, gt as findHubRoot, h as createDecision, it as loadHub, l as migrateHub, lt as MAINLINE_FILE, m as addRepo, nt as setIssueArchived, p as addHub, rt as HubError, tt as setFindingStatus, ut as TASK_FILE, v as dropChecklistItem, w as setMainlineStatus, x as setChecklistItem, y as initHub, z as startSession } from "./host-DkaCYnnc.js";
3
+ import { I as parseRef, L as refExample } from "./dist-Dy45myuN.js";
4
4
  import { t as listProgress } from "./progress-1BfPg2zN.js";
5
5
  import { COMMAND_SPECS, OPTIONS } from "./facts.js";
6
- import { runHook } from "./hooks.js";
7
- import { l as CLI_VERSION, r as launchUi, s as startUiInBackground } from "./launch-CBXoFsFC.js";
6
+ import { hookFallback, parsePayload, runHook } from "./hooks.js";
7
+ import { l as CLI_VERSION, r as launchUi, s as startUiInBackground } from "./launch-DOX1TTGx.js";
8
8
  import { readFileSync, realpathSync } from "node:fs";
9
9
  import { join, relative } from "node:path";
10
10
  import { fileURLToPath } from "node:url";
@@ -85,16 +85,24 @@ const RUNS = {
85
85
  }).status})`);
86
86
  },
87
87
  hook: ({ args }) => {
88
- let payload = {};
88
+ let input = "";
89
89
  try {
90
- payload = JSON.parse(readFileSync(0, "utf8") || "{}");
91
- } catch {
92
- console.error("mainflow hook: stdin is not JSON; nothing to do");
90
+ input = readFileSync(0, "utf8");
91
+ } catch {}
92
+ const payload = parsePayload(input);
93
+ if (!payload) {
94
+ console.error("mainflow hook: stdin is not a JSON object; nothing to do");
95
+ const fallback = hookFallback(args[0], args[1], {
96
+ payload: {},
97
+ cli: CLI
98
+ }, { kind: "unreadable-input" });
99
+ if (fallback) console.log(fallback);
93
100
  return 0;
94
101
  }
95
102
  const { stdout, error } = runHook(args[0], args[1], {
96
103
  payload,
97
104
  envFile: process.env.CLAUDE_ENV_FILE,
105
+ projectDir: process.env.CLAUDE_PROJECT_DIR,
98
106
  cli: CLI,
99
107
  startUi: startUiInBackground
100
108
  });
@@ -125,6 +133,7 @@ const RUNS = {
125
133
  title: required(opts, "title", "--title \"以文字檔為準\""),
126
134
  status: opts.status,
127
135
  by: opts.by,
136
+ basis: opts.basis,
128
137
  affects: opts.affects ? list(opts.affects) : void 0
129
138
  });
130
139
  return done(`created ${ref} ${relative(root, file)}`);
@@ -204,7 +213,10 @@ const RUNS = {
204
213
  setChecklistItem(root, args[0], false);
205
214
  return done(`unchecked ${args[0]}`);
206
215
  },
207
- "decision accept": ({ root, args, opts }) => done(`accepted ${args[0]} (${acceptDecision(root, args[0], opts.by).decidedAt})`),
216
+ "decision accept": ({ root, args, opts }) => done(`accepted ${args[0]} (${acceptDecision(root, args[0], {
217
+ by: opts.by,
218
+ basis: opts.basis
219
+ }).decidedAt})`),
208
220
  "item drop": ({ root, args, opts }) => {
209
221
  dropChecklistItem(root, args[0], {
210
222
  supersededBy: opts.by,
@@ -1,4 +1,349 @@
1
1
  import { z } from "zod";
2
+ //#region ../schema/dist/grants.js
3
+ /** What a mainline can authorize ahead of time. */
4
+ const GRANT_ACTIONS = [
5
+ "commit",
6
+ "push",
7
+ "merge-pr",
8
+ "publish",
9
+ "release",
10
+ "install-prompts",
11
+ "sync-hubs"
12
+ ];
13
+ /** How each action's target is written, for error messages and the framework facts. */
14
+ const GRANT_TARGET_FORMS = {
15
+ commit: {
16
+ form: "{repo}[:{branch}]",
17
+ example: "mainflow:main"
18
+ },
19
+ push: {
20
+ form: "{repo}[:{branch}]",
21
+ example: "mainflow:main"
22
+ },
23
+ "merge-pr": {
24
+ form: "{repo}[:{branch}]",
25
+ example: "mainflow:main"
26
+ },
27
+ publish: {
28
+ form: "{package}@{semver range}",
29
+ example: "mainflow@0.21.x"
30
+ },
31
+ release: {
32
+ form: "{repo}@{tag pattern}",
33
+ example: "mainflow@v0.21.*"
34
+ },
35
+ "install-prompts": {
36
+ form: "{unit}",
37
+ example: "user"
38
+ },
39
+ "sync-hubs": {
40
+ form: "{hub} or *",
41
+ example: "carlos-space"
42
+ }
43
+ };
44
+ const KEBAB = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
45
+ /** A git branch name, `*` standing for any run of characters. */
46
+ const BRANCH = /^(?!\/)(?!.*\/\/)(?!.*\.\.)[A-Za-z0-9._/*-]+(?<!\/)$/;
47
+ /** npm package names: lowercase, optionally scoped. */
48
+ const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/;
49
+ /** A tag pattern: no whitespace, `*` and `?` as in a shell glob. */
50
+ const TAG_PATTERN = /^[^\s~^:\\[]+$/;
51
+ const fail = (action, why) => ({
52
+ ok: false,
53
+ message: `${why}; a ${action} target is written ${GRANT_TARGET_FORMS[action].form}, e.g. ${GRANT_TARGET_FORMS[action].example}`
54
+ });
55
+ /** Split a target by its action's form, or say what is wrong with it. */
56
+ function parseGrantTarget(action, target) {
57
+ switch (action) {
58
+ case "commit":
59
+ case "push":
60
+ case "merge-pr": {
61
+ const colon = target.indexOf(":");
62
+ const repo = colon < 0 ? target : target.slice(0, colon);
63
+ const branch = colon < 0 ? void 0 : target.slice(colon + 1);
64
+ if (!repo) return fail(action, "the repo name is missing");
65
+ if (branch !== void 0 && !BRANCH.test(branch)) return fail(action, `"${branch}" is not a branch name`);
66
+ return {
67
+ ok: true,
68
+ target: {
69
+ action,
70
+ repo,
71
+ ...branch !== void 0 ? { branch } : {}
72
+ }
73
+ };
74
+ }
75
+ case "publish": {
76
+ const at = target.lastIndexOf("@");
77
+ if (at <= 0) return fail(action, "the version range is missing");
78
+ const name = target.slice(0, at);
79
+ const range = target.slice(at + 1);
80
+ if (!range.trim()) return fail(action, "the version range is missing");
81
+ if (!PACKAGE_NAME.test(name)) return fail(action, `"${name}" is not an npm package name`);
82
+ if (!parseRange(range)) return fail(action, `"${range}" is not a semver range this CLI reads (exact, x-range, ^, ~, comparators, ||)`);
83
+ return {
84
+ ok: true,
85
+ target: {
86
+ action,
87
+ name,
88
+ range
89
+ }
90
+ };
91
+ }
92
+ case "release": {
93
+ const at = target.indexOf("@");
94
+ if (at <= 0) return fail(action, "the tag pattern is missing");
95
+ const repo = target.slice(0, at);
96
+ const tag = target.slice(at + 1);
97
+ if (!TAG_PATTERN.test(tag)) return fail(action, `"${tag}" is not a tag pattern`);
98
+ return {
99
+ ok: true,
100
+ target: {
101
+ action,
102
+ repo,
103
+ tag
104
+ }
105
+ };
106
+ }
107
+ case "install-prompts": return KEBAB.test(target) ? {
108
+ ok: true,
109
+ target: {
110
+ action,
111
+ unit: target
112
+ }
113
+ } : fail(action, `"${target}" is not a unit name`);
114
+ case "sync-hubs": return target === "*" || KEBAB.test(target) ? {
115
+ ok: true,
116
+ target: {
117
+ action,
118
+ hub: target
119
+ }
120
+ } : fail(action, `"${target}" is not a hub name`);
121
+ }
122
+ }
123
+ /** The repository a target names, for the actions that name one. */
124
+ const grantRepo = (target) => "repo" in target ? target.repo : void 0;
125
+ /** Whether a grant's target covers the request. Only the actions a hook can resolve have requests. */
126
+ function grantCovers(target, request) {
127
+ if (target.action !== request.action) return false;
128
+ if (target.action === "publish" && request.action === "publish") return target.name === request.name && satisfies(request.version, target.range);
129
+ if (target.action === "release" && request.action === "release") return request.repos.includes(target.repo) && globMatch(target.tag, request.tag);
130
+ return false;
131
+ }
132
+ /**
133
+ * The one way each hook-enforced action may be written for a grant to allow it; the hook asks for any other
134
+ * writing, since what a free-form shell call publishes cannot be predicted reliably.
135
+ */
136
+ const GRANT_FORMS = {
137
+ publish: "在套件目錄單獨執行 `pnpm publish --ignore-scripts` 或 `npm publish --ignore-scripts`(可加 --access public|restricted、--tag <名稱>、--dry-run、--provenance,pnpm 另可加 --no-git-checks)",
138
+ release: "單獨執行 `gh release create <tag> -R <owner/repo>`(可加 --title、--notes、--generate-notes、--draft、--prerelease、--verify-tag)"
139
+ };
140
+ /** The fixed form a grant allows, filled in with its target; undefined for actions the hook does not allow. */
141
+ function grantForm(target) {
142
+ if (target.action === "publish") return `在 ${target.name} 的套件目錄單獨執行 \`pnpm publish --ignore-scripts\` 或 \`npm publish --ignore-scripts\``;
143
+ if (target.action === "release") return `單獨執行 \`gh release create <符合 ${target.tag} 的 tag> -R <${target.repo} 的 GitHub owner/repo>\``;
144
+ }
145
+ /** How much of a request a reason or an ops entry names. */
146
+ const DESCRIBED = 200;
147
+ /** One line naming a request, for reasons and the ops log. */
148
+ function describeRequest(request) {
149
+ const text = request.action === "publish" ? `publish ${request.name}@${request.version}` : `release ${request.repos.join("/") || "(unknown repo)"}@${request.tag}`;
150
+ return text.length > DESCRIBED ? `${text.slice(0, DESCRIBED)}…` : text;
151
+ }
152
+ /** Whether `text` matches a pattern where `*` is any run of characters and `?` one character, neither crossing `/`. */
153
+ function globMatch(pattern, text) {
154
+ const source = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]");
155
+ return new RegExp(`^${source}$`).test(text);
156
+ }
157
+ const VERSION = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
158
+ const PARTIAL = /^v?(\d+|[xX*])(?:\.(\d+|[xX*]))?(?:\.(\d+|[xX*]))?(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
159
+ const preParts = (pre) => pre ? pre.split(".").map((p) => /^\d+$/.test(p) ? Number(p) : p) : [];
160
+ function parseVersion(text) {
161
+ const m = VERSION.exec(text.trim());
162
+ return m ? {
163
+ major: Number(m[1]),
164
+ minor: Number(m[2]),
165
+ patch: Number(m[3]),
166
+ pre: preParts(m[4])
167
+ } : null;
168
+ }
169
+ function compareVersions(a, b) {
170
+ for (const key of [
171
+ "major",
172
+ "minor",
173
+ "patch"
174
+ ]) if (a[key] !== b[key]) return a[key] - b[key];
175
+ if (a.pre.length === 0 || b.pre.length === 0) return (a.pre.length === 0 ? 1 : 0) - (b.pre.length === 0 ? 1 : 0);
176
+ for (let i = 0; i < Math.max(a.pre.length, b.pre.length); i++) {
177
+ const x = a.pre[i];
178
+ const y = b.pre[i];
179
+ if (x === void 0) return -1;
180
+ if (y === void 0) return 1;
181
+ if (x === y) continue;
182
+ if (typeof x === "number" && typeof y === "number") return x - y;
183
+ if (typeof x === "number") return -1;
184
+ if (typeof y === "number") return 1;
185
+ return x < y ? -1 : 1;
186
+ }
187
+ return 0;
188
+ }
189
+ const v = (major, minor, patch, pre = []) => ({
190
+ major,
191
+ minor,
192
+ patch,
193
+ pre
194
+ });
195
+ /** The lowest version above every release of the given line, e.g. 2.0.0-0 above 1.x. */
196
+ const floor = (major, minor, patch) => v(major, minor, patch, [0]);
197
+ function parsePartial(text) {
198
+ if (text === "" || text === "*" || text === "x" || text === "X") return { pre: [] };
199
+ const m = PARTIAL.exec(text);
200
+ if (!m) return null;
201
+ const num = (s) => s === void 0 || /^[xX*]$/.test(s) ? void 0 : Number(s);
202
+ const major = num(m[1]);
203
+ const minor = major === void 0 ? void 0 : num(m[2]);
204
+ const patch = minor === void 0 ? void 0 : num(m[3]);
205
+ if (m[4] && patch === void 0) return null;
206
+ return {
207
+ major,
208
+ minor,
209
+ patch,
210
+ pre: preParts(m[4])
211
+ };
212
+ }
213
+ /** The comparators one range item stands for, or null when it cannot be read. */
214
+ function comparators(item) {
215
+ const m = /^(\^|~>?|<=|>=|<|>|=)?\s*(.*)$/.exec(item);
216
+ const op = m[1] ?? "";
217
+ const p = parsePartial(m[2]);
218
+ if (!p) return null;
219
+ const { major, minor, patch, pre } = p;
220
+ const low = v(major ?? 0, minor ?? 0, patch ?? 0, pre);
221
+ switch (op) {
222
+ case "":
223
+ case "=":
224
+ if (major === void 0) return [];
225
+ if (minor === void 0) return [{
226
+ op: ">=",
227
+ version: low
228
+ }, {
229
+ op: "<",
230
+ version: floor(major + 1, 0, 0)
231
+ }];
232
+ if (patch === void 0) return [{
233
+ op: ">=",
234
+ version: low
235
+ }, {
236
+ op: "<",
237
+ version: floor(major, minor + 1, 0)
238
+ }];
239
+ return [{
240
+ op: "=",
241
+ version: low
242
+ }];
243
+ case "^": {
244
+ if (major === void 0) return [];
245
+ const high = major > 0 || minor === void 0 ? floor(major + 1, 0, 0) : minor > 0 || patch === void 0 ? floor(0, minor + 1, 0) : floor(0, 0, patch + 1);
246
+ return [{
247
+ op: ">=",
248
+ version: low
249
+ }, {
250
+ op: "<",
251
+ version: high
252
+ }];
253
+ }
254
+ case "~":
255
+ case "~>": {
256
+ if (major === void 0) return [];
257
+ const high = minor === void 0 ? floor(major + 1, 0, 0) : floor(major, minor + 1, 0);
258
+ return [{
259
+ op: ">=",
260
+ version: low
261
+ }, {
262
+ op: "<",
263
+ version: high
264
+ }];
265
+ }
266
+ case ">=": return major === void 0 ? [] : [{
267
+ op: ">=",
268
+ version: low
269
+ }];
270
+ case "<": return major === void 0 ? [{
271
+ op: "<",
272
+ version: floor(0, 0, 0)
273
+ }] : [{
274
+ op: "<",
275
+ version: patch === void 0 ? floor(major, minor ?? 0, 0) : low
276
+ }];
277
+ case ">":
278
+ if (major === void 0) return [{
279
+ op: "<",
280
+ version: floor(0, 0, 0)
281
+ }];
282
+ if (minor === void 0) return [{
283
+ op: ">=",
284
+ version: v(major + 1, 0, 0)
285
+ }];
286
+ if (patch === void 0) return [{
287
+ op: ">=",
288
+ version: v(major, minor + 1, 0)
289
+ }];
290
+ return [{
291
+ op: ">",
292
+ version: low
293
+ }];
294
+ case "<=":
295
+ if (major === void 0) return [];
296
+ if (minor === void 0) return [{
297
+ op: "<",
298
+ version: floor(major + 1, 0, 0)
299
+ }];
300
+ if (patch === void 0) return [{
301
+ op: "<",
302
+ version: floor(major, minor + 1, 0)
303
+ }];
304
+ return [{
305
+ op: "<=",
306
+ version: low
307
+ }];
308
+ }
309
+ return null;
310
+ }
311
+ /** A range as alternatives of comparator sets, or null when any part of it cannot be read. */
312
+ function parseRange(range) {
313
+ const alternatives = range.trim().split(/\s*\|\|\s*/);
314
+ const sets = [];
315
+ for (const alt of alternatives) {
316
+ if (alt.trim() === "") return null;
317
+ if (/\s-\s/.test(alt)) return null;
318
+ const items = alt.trim().replace(/(\^|~>?|<=|>=|<|>|=)\s+/g, "$1").split(/\s+/).filter(Boolean);
319
+ const set = [];
320
+ for (const item of items) {
321
+ const parsed = comparators(item);
322
+ if (!parsed) return null;
323
+ set.push(...parsed);
324
+ }
325
+ sets.push(set);
326
+ }
327
+ return sets;
328
+ }
329
+ const holds = (c, version) => {
330
+ const d = compareVersions(version, c.version);
331
+ return c.op === "=" ? d === 0 : c.op === "<" ? d < 0 : c.op === "<=" ? d <= 0 : c.op === ">" ? d > 0 : d >= 0;
332
+ };
333
+ /** The upper bounds this file adds for x-ranges, caret and tilde; they name no prerelease of their own. */
334
+ const isFloor = (c) => c.op === "<" && c.version.pre.length === 1 && c.version.pre[0] === 0;
335
+ /** Whether `version` satisfies `range`; false when either cannot be read. */
336
+ function satisfies(version, range) {
337
+ const parsed = parseVersion(version);
338
+ const sets = parseRange(range);
339
+ if (!parsed || !sets) return false;
340
+ return sets.some((set) => {
341
+ if (!set.every((c) => holds(c, parsed))) return false;
342
+ if (parsed.pre.length === 0) return true;
343
+ return set.some((c) => c.version.pre.length > 0 && !isFloor(c) && c.version.major === parsed.major && c.version.minor === parsed.minor && c.version.patch === parsed.patch);
344
+ });
345
+ }
346
+ //#endregion
2
347
  //#region ../schema/dist/index.js
3
348
  const FORMAT_VERSION = 2;
4
349
  const ID_PATTERNS = {
@@ -9,7 +354,8 @@ const ID_PATTERNS = {
9
354
  decision: /^d\d{3}$/,
10
355
  action: /^a\d{3}$/,
11
356
  issue: /^i\d{3}$/,
12
- finding: /^f\d{3}$/
357
+ finding: /^f\d{3}$/,
358
+ grant: /^g\d{3}$/
13
359
  };
14
360
  const CHECKLIST_REF = /^m\d{3}\/t\d{3}#c\d{3}$/;
15
361
  const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
@@ -162,6 +508,8 @@ const DECIDERS = [
162
508
  "ai",
163
509
  "relay"
164
510
  ];
511
+ /** How the decider settled it: by naming an option, or by going with the recommendation without naming one */
512
+ const DECISION_BASES = ["explicit", "recommended"];
165
513
  const MERGE_STATES = [
166
514
  "open",
167
515
  "merged",
@@ -357,6 +705,23 @@ const costSchema = z.object({
357
705
  actual: z.number().nonnegative().optional(),
358
706
  note: text.optional()
359
707
  });
708
+ /**
709
+ * One thing the mainline authorizes ahead of time: an action plus the target it covers. It applies only while
710
+ * a session's action in progress serves this mainline and the mainline is still open.
711
+ */
712
+ const grantSchema = z.object({
713
+ id: id("grant"),
714
+ action: z.enum(GRANT_ACTIONS),
715
+ target: text,
716
+ note: text.optional()
717
+ }).superRefine((g, ctx) => {
718
+ const parsed = parseGrantTarget(g.action, g.target);
719
+ if (!parsed.ok) ctx.addIssue({
720
+ code: "custom",
721
+ path: ["target"],
722
+ message: parsed.message
723
+ });
724
+ });
360
725
  const mainlineSchema = z.object({
361
726
  id: id("mainline"),
362
727
  title: text,
@@ -371,7 +736,8 @@ const mainlineSchema = z.object({
371
736
  amount: z.number().positive(),
372
737
  unit: text
373
738
  }).optional(),
374
- costs: z.array(costSchema).optional()
739
+ costs: z.array(costSchema).optional(),
740
+ grants: z.array(grantSchema).optional()
375
741
  });
376
742
  const decisionSchema = z.object({
377
743
  id: id("decision"),
@@ -382,6 +748,7 @@ const decisionSchema = z.object({
382
748
  decidedAt: dateOrTime.optional(),
383
749
  supersededBy: id("decision").optional(),
384
750
  by: z.enum(DECIDERS).optional(),
751
+ basis: z.enum(DECISION_BASES).optional(),
385
752
  /** Tasks and items the decision shapes; while it is proposed, the checklist items among them wait for it */
386
753
  affects: z.array(anyRef).optional(),
387
754
  /** Whether the choice can be undone cheaply later */
@@ -537,4 +904,4 @@ function parseOpsLine(line) {
537
904
  return parsed.success ? parsed.data : null;
538
905
  }
539
906
  //#endregion
540
- export { formatOpsEntry as A, waitingForRef as B, SURVEY_SECTION as C, configSchema as D, WORK_STATUS as E, parseRef as F, refExample as I, refMainline as L, mainlineSchema as M, opsEntrySchema as N, decisionSchema as O, parseOpsLine as P, sessionSchema as R, SURVEY_LINES as S, TASK_SECTIONS as T, PRIORITIES as _, DECISION_STATUS as a, SESSION_STATUS as b, FINDING_STATUS as c, ISSUE_SECTIONS as d, ISSUE_STATUS as f, MERGE_STATES as g, MAINLINE_STATUS as h, DECISION_SECTIONS as i, issueSchema as j, findingSchema as k, FORMAT_VERSION as l, MAINLINE_SECTIONS as m, CLOSED_STATUS as n, DEFAULT_CATEGORIES as o, LINK_RELS as p, DECIDERS as r, FINDING_SECTIONS as s, CHECKLIST_REF as t, ID_PATTERNS as u, REF_EXAMPLES as v, TASK_REQUIRED_SECTIONS as w, SLUG as x, RESERVED_HUB_NAMES as y, taskSchema as z };
907
+ export { findingSchema as A, taskSchema as B, SURVEY_LINES as C, WORK_STATUS as D, TASK_SECTIONS as E, parseOpsLine as F, describeRequest as G, GRANT_ACTIONS as H, parseRef as I, grantRepo as J, grantCovers as K, refExample as L, issueSchema as M, mainlineSchema as N, configSchema as O, opsEntrySchema as P, refMainline as R, SLUG as S, TASK_REQUIRED_SECTIONS as T, GRANT_FORMS as U, waitingForRef as V, GRANT_TARGET_FORMS as W, parseGrantTarget as Y, MERGE_STATES as _, DECISION_SECTIONS as a, RESERVED_HUB_NAMES as b, FINDING_SECTIONS as c, ID_PATTERNS as d, ISSUE_SECTIONS as f, MAINLINE_STATUS as g, MAINLINE_SECTIONS as h, DECISION_BASES as i, formatOpsEntry as j, decisionSchema as k, FINDING_STATUS as l, LINK_RELS as m, CLOSED_STATUS as n, DECISION_STATUS as o, ISSUE_STATUS as p, grantForm as q, DECIDERS as r, DEFAULT_CATEGORIES as s, CHECKLIST_REF as t, FORMAT_VERSION as u, PRIORITIES as v, SURVEY_SECTION as w, SESSION_STATUS as x, REF_EXAMPLES as y, sessionSchema as z };
package/dist/facts.d.ts CHANGED
@@ -1,28 +1,4 @@
1
- import { z } from "zod";
2
- //#region ../schema/dist/index.d.ts
3
- export declare const FORMAT_VERSION = 2;
4
- export declare const ID_PATTERNS: {
5
- readonly mainline: RegExp;
6
- readonly task: RegExp;
7
- readonly checklistItem: RegExp;
8
- readonly criterion: RegExp;
9
- readonly decision: RegExp;
10
- readonly action: RegExp;
11
- readonly issue: RegExp;
12
- readonly finding: RegExp;
13
- };
14
- export declare const CHECKLIST_REF: RegExp;
15
- export declare const SLUG: RegExp;
16
- /** One source the survey checks: the line's label, what it holds, and how it may be left unchecked. */
17
- interface SurveyLine {
18
- label: string;
19
- holds: string;
20
- /** The word that marks the source as not checked, followed by the reason, e.g. 不適用:{理由}. Absent when the source is always checked. */
21
- skip?: string;
22
- /** Text a checked line must contain, e.g. the environment a spike ran in. */
23
- requires?: string;
24
- }
25
- //#endregion
1
+ import { a as SurveyLine, i as SLUG, n as FORMAT_VERSION, r as ID_PATTERNS, t as CHECKLIST_REF } from "./index-CV30LkNZ.js";
26
2
  //#region src/facts.d.ts
27
3
  export interface CommandSpec {
28
4
  usage: string;
@@ -151,7 +127,7 @@ export declare const COMMAND_SPECS: {
151
127
  readonly needsHub: true;
152
128
  };
153
129
  readonly 'new decision': {
154
- readonly usage: 'new decision <m> <slug> --title <title> [--status proposed] [--by user|ai|relay] [--affects <m/t or m/t#c>,...]';
130
+ readonly usage: 'new decision <m> <slug> --title <title> [--status proposed] [--by user|ai|relay] [--basis explicit|recommended] [--affects <m/t or m/t#c>,...]';
155
131
  readonly summary: 'Create a decision file; proposed while the question is still open';
156
132
  readonly args: 2;
157
133
  readonly needsHub: true;
@@ -229,7 +205,7 @@ export declare const COMMAND_SPECS: {
229
205
  readonly needsHub: true;
230
206
  };
231
207
  readonly 'decision accept': {
232
- readonly usage: 'decision accept <m/d> [--by user|ai|relay]';
208
+ readonly usage: 'decision accept <m/d> [--by user|ai|relay] [--basis explicit|recommended]';
233
209
  readonly summary: 'Settle a proposed decision; the items it affects stop waiting for it';
234
210
  readonly args: 1;
235
211
  readonly needsHub: true;
@@ -348,6 +324,9 @@ export declare const OPTIONS: {
348
324
  readonly by: {
349
325
  readonly type: 'string';
350
326
  };
327
+ readonly basis: {
328
+ readonly type: 'string';
329
+ };
351
330
  readonly port: {
352
331
  readonly type: 'string';
353
332
  };
@@ -457,6 +436,7 @@ export declare const STATUSES: {
457
436
  readonly session: readonly ["active", "paused", "ended", "handed-off"];
458
437
  readonly merge: readonly ["open", "merged", "abandoned"];
459
438
  readonly decider: readonly ["user", "ai", "relay"];
439
+ readonly basis: readonly ["explicit", "recommended"];
460
440
  };
461
441
  /** Reference formats, one example each, in the order a reader meets them. */
462
442
  export declare const REFERENCES: readonly [{
@@ -500,6 +480,24 @@ export declare const REFERENCES: readonly [{
500
480
  export declare const LINKS: {
501
481
  readonly rels: readonly ["depends-on", "relates", "split-from"];
502
482
  };
483
+ /**
484
+ * What a mainline's grants authorize: each action with the form of its target. A grant applies only while the
485
+ * session's action in progress serves the mainline and the mainline is active or planning. The PreToolUse hook
486
+ * allows `publish` and `release` only when the command is written in the fixed form in `forms`; the other
487
+ * actions are for the executor to read.
488
+ */
489
+ export declare const GRANTS: {
490
+ readonly actions: readonly ["commit", "push", "merge-pr", "publish", "release", "install-prompts", "sync-hubs"];
491
+ readonly targets: Record<"commit" | "install-prompts" | "merge-pr" | "publish" | "push" | "release" | "sync-hubs", {
492
+ form: string;
493
+ example: string;
494
+ }>;
495
+ readonly enforcedByHook: readonly ['publish', 'release'];
496
+ readonly forms: {
497
+ readonly publish: '在套件目錄單獨執行 `pnpm publish --ignore-scripts` 或 `npm publish --ignore-scripts`(可加 --access public|restricted、--tag <名稱>、--dry-run、--provenance,pnpm 另可加 --no-git-checks)';
498
+ readonly release: '單獨執行 `gh release create <tag> -R <owner/repo>`(可加 --title、--notes、--generate-notes、--draft、--prerelease、--verify-tag)';
499
+ };
500
+ };
503
501
  /** Where each kind of entry lives under the hub root. */
504
502
  export declare const LAYOUT: {
505
503
  readonly config: '.mainflow/config.yaml';
@@ -551,13 +549,15 @@ export interface FieldSpec {
551
549
  }
552
550
  /** Every front matter field of each document and list entry; facts.test keeps it equal to the schema. */
553
551
  export declare const FIELDS: {
554
- readonly mainline: readonly [...FieldSpec[], FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec];
552
+ readonly mainline: readonly [...FieldSpec[], FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec];
555
553
  readonly task: readonly [...FieldSpec[], FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec];
556
- readonly decision: readonly [...FieldSpec[], FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec];
554
+ readonly decision: readonly [...FieldSpec[], FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec];
557
555
  readonly issue: readonly [...FieldSpec[], FieldSpec, FieldSpec, FieldSpec, FieldSpec];
558
556
  readonly finding: readonly [...FieldSpec[], FieldSpec, FieldSpec, FieldSpec];
559
557
  readonly criterion: FieldSpec[];
560
558
  readonly checklistItem: readonly [...FieldSpec[], FieldSpec];
559
+ readonly grant: readonly [FieldSpec, FieldSpec, FieldSpec, FieldSpec];
561
560
  readonly link: readonly [FieldSpec, FieldSpec, FieldSpec];
562
561
  };
563
- //#endregion
562
+ //#endregion
563
+ export { CHECKLIST_REF, FORMAT_VERSION, ID_PATTERNS, SLUG };