mainflow 0.43.0-alpha.4 → 0.44.0-alpha.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 +7 -5
- package/dist/{dist-D5rZ0MQH.js → dist-CXrUfRQp.js} +111 -10
- package/dist/facts.d.ts +22 -5
- package/dist/facts.js +28 -7
- package/dist/grant-check.d.ts +45 -0
- package/dist/grant-check.js +572 -0
- package/dist/grant-commands.d.ts +31 -3
- package/dist/grant-commands.js +14 -4
- package/dist/hooks.d.ts +2 -18
- package/dist/hooks.js +103 -15
- package/dist/{host-BJvqZy-i.js → host-C74j2Qox.js} +174 -57
- package/dist/host.d.ts +1 -1
- package/dist/host.js +1 -1
- package/dist/{index-DRpamPge.d.ts → index-jq7lN8V8.d.ts} +9 -3
- package/dist/{index-BqPQp6TF.d.ts → index-l8QUG80k.d.ts} +11 -0
- package/dist/launch.d.ts +1 -1
- package/dist/launch.js +2 -2
- package/dist/native/mainflow-swap-darwin-arm64 +0 -0
- package/dist/native/mainflow-swap-darwin-x64 +0 -0
- package/dist/{operation-BhBbGauo.js → operation-BmCeGKHw.js} +344 -132
- package/dist/operation-BroV98VB.d.ts +19 -0
- package/dist/{ui-xxzmhAqF.js → ui-8NTeAfC7.js} +3 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { P as UNTIL_FORMS, X as parseUntil, Y as parseRef, Z as refExample, ct as stampMs, it as untilProblem, n as CLOSED_STATUS } from "./dist-
|
|
3
|
-
import { $t as
|
|
2
|
+
import { P as UNTIL_FORMS, X as parseUntil, Y as parseRef, Z as refExample, ct as stampMs, it as untilProblem, n as CLOSED_STATUS } from "./dist-CXrUfRQp.js";
|
|
3
|
+
import { $t as realPath, Bt as notRegisteredMessage, Ft as requireSlug, Gt as resolveHub, Ht as registerHub, It as hubAtRoot, Jt as nodeStorage, Kt as unregisterHub, Lt as hubHoldingFile, Mt as MAINLINE_FILE, O as loadSessions, Pt as TASK_FILE, Q as HubError, S as sessionBriefing, Ut as registeredName, Vt as readRegistry, Wt as registryFile, X as applyOperation, Xt as dataFile, Z as checkHubName, b as untilState, d as migrateHubData, f as migrateHub, ft as nowMinute, g as RETRY_RULES, gt as BLOB_HASH, h as receiverFromEnv, k as readSession, lt as remoteMembers, m as retroReportFromReceiver, nn as DATA_DIR, p as retroReport, qt as userDataDir, rn as WriteError, rt as sectionText, st as memberOfGroup, tt as loadHub, u as groupPlan, ut as unavailableHubs, vt as MAX_CHANGE_PAGE, x as duplicateClaimLines, y as initHub, yt as MAX_FILE_BYTES, zt as listHubs } from "./host-C74j2Qox.js";
|
|
4
4
|
import { t as listProgress } from "./progress-1BfPg2zN.js";
|
|
5
|
-
import { A as hubName, C as readRemote, D as Unreachable, E as ServerError, F as syncPaths, M as SETTINGS_FILE, O as ENV, T as ServerApi, _ as DaemonUnavailable, a as versionGate, b as readDaemon, c as oversizedFiles, d as planAnnounce, f as planFetch, g as DaemonRunning, h as startDaemon, i as requireWritableVersion, j as syncSettings, k as earlierToken, l as fetchDenial, m as toolCallOf, n as hubWriter, o as queuedCount, p as planFlush, r as writtenLines, s as remoteMode, t as CONFIRM_MS, u as hookAnswer, v as callDaemon, w as HubRewritingError, x as silentHint, y as probeDaemon } from "./operation-
|
|
5
|
+
import { A as hubName, C as readRemote, D as Unreachable, E as ServerError, F as syncPaths, M as SETTINGS_FILE, O as ENV, T as ServerApi, _ as DaemonUnavailable, a as versionGate, b as readDaemon, c as oversizedFiles, d as planAnnounce, f as planFetch, g as DaemonRunning, h as startDaemon, i as requireWritableVersion, j as syncSettings, k as earlierToken, l as fetchDenial, m as toolCallOf, n as hubWriter, o as queuedCount, p as planFlush, r as writtenLines, s as remoteMode, t as CONFIRM_MS, u as hookAnswer, v as callDaemon, w as HubRewritingError, x as silentHint, y as probeDaemon } from "./operation-BmCeGKHw.js";
|
|
6
6
|
import { COMMAND_SPECS, OPTIONS } from "./facts.js";
|
|
7
7
|
import { t as CLI_VERSION } from "./version-Dvv4jwF1.js";
|
|
8
8
|
import { hookFallback, parsePayload, runHook } from "./hooks.js";
|
|
9
|
+
import { runGrantCheck } from "./grant-check.js";
|
|
9
10
|
import { exitCodeFor, launchUi, networkProbe, readUiRecord, startUiInBackground, watchHubGone } from "./launch.js";
|
|
10
11
|
import { randomBytes } from "node:crypto";
|
|
11
12
|
import { existsSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
|
|
@@ -747,12 +748,12 @@ function window(s) {
|
|
|
747
748
|
if (scope.rule === "time") return `${stretches(s)}(對話紀錄沒有 mainflow 的輪次標頭,依行動時段)`;
|
|
748
749
|
const notes = joined([
|
|
749
750
|
scope.inferred > 0 && `${scope.inferred} 輪開始時沒有進行中的行動,依這輪開始或接手的行動${scope.inferredCost !== void 0 ? `(對話紀錄 ${usd(scope.inferredCost)})` : ""}`,
|
|
750
|
-
scope.inherited > 0 && `${scope.inherited}
|
|
751
|
+
scope.inherited > 0 && `${scope.inherited} 輪沒有標頭,沿用最近的標頭寫的行動、前一輪或派出子代理的那一輪`,
|
|
751
752
|
scope.shared > 0 && `${scope.shared} 輪的行動也服務別條主脈絡,只算給第一個參照`,
|
|
752
753
|
scope.switched > 0 && `${scope.switched} 輪中途開始了別條主脈絡的行動,整輪仍算在這裡`,
|
|
753
754
|
scope.beforeHeads > 0 && `標頭出現前的 ${scope.beforeHeads} 輪依行動時段`
|
|
754
755
|
]);
|
|
755
|
-
return `${int(scope.counted)} / ${int(scope.turns)}
|
|
756
|
+
return `${int(scope.counted)} / ${int(scope.turns)} 輪(依輪次的 mainflow 標頭${notes ? `;${notes}` : ""})`;
|
|
756
757
|
}
|
|
757
758
|
function nodeLine(n) {
|
|
758
759
|
const cost = joined([
|
|
@@ -1637,6 +1638,7 @@ const RUNS = {
|
|
|
1637
1638
|
return 0;
|
|
1638
1639
|
});
|
|
1639
1640
|
},
|
|
1641
|
+
"grant check": ({ cwd, opts }) => runGrantCheck(cwd, opts["dry-run"] ?? false),
|
|
1640
1642
|
"task show": ({ dirs, args }) => {
|
|
1641
1643
|
const { mainline, task } = findTask(loadHub(dirs), args[0]);
|
|
1642
1644
|
console.log(`${mainline.id}/${task.id} ${task.status.padEnd(8)} ${task.kind} ${task.title}`);
|
|
@@ -123,22 +123,32 @@ function parseGrantTarget(action, target) {
|
|
|
123
123
|
}
|
|
124
124
|
/** The repository a target names, for the actions that name one. */
|
|
125
125
|
const grantRepo = (target) => "repo" in target ? target.repo : void 0;
|
|
126
|
-
/** Whether a grant's target covers the request. Only the actions a hook can resolve have requests. */
|
|
126
|
+
/** Whether a grant's target covers the request. Only the actions a hook or grant check can resolve have requests. */
|
|
127
127
|
function grantCovers(target, request) {
|
|
128
128
|
if (target.action !== request.action) return false;
|
|
129
129
|
if (target.action === "publish" && request.action === "publish") return target.name === request.name && satisfies(request.version, target.range);
|
|
130
130
|
if (target.action === "release" && request.action === "release") return request.repos.includes(target.repo) && globMatch(target.tag, request.tag);
|
|
131
|
+
if (target.action === "install-prompts" && request.action === "install-prompts") return target.unit === request.unit;
|
|
132
|
+
if ((target.action === "commit" || target.action === "push" || target.action === "merge-pr") && (request.action === "commit" || request.action === "push" || request.action === "merge-pr")) {
|
|
133
|
+
if (!request.repos.includes(target.repo)) return false;
|
|
134
|
+
return target.branch === void 0 || request.branch !== null && globMatch(target.branch, request.branch);
|
|
135
|
+
}
|
|
131
136
|
return false;
|
|
132
137
|
}
|
|
133
138
|
/**
|
|
134
|
-
* The one way each
|
|
135
|
-
*
|
|
139
|
+
* The one way each action may be written for a grant to allow it; any other writing is not judged, since what a
|
|
140
|
+
* free-form shell call does cannot be predicted reliably. The PreToolUse hook allows publish and release in these
|
|
141
|
+
* forms; `mainflow grant check` allows all six.
|
|
136
142
|
*/
|
|
137
143
|
const GRANT_FORMS = {
|
|
144
|
+
commit: "單獨執行 `git commit …` 或 `git -C <目錄> commit …`,目錄或 cwd 要在 config.yaml 登記的倉庫或它的 worktree 裡",
|
|
145
|
+
push: "單獨執行 `git push [origin] [<分支>]`,可加 `-C <目錄>`、`-u` 或 `--set-upstream`,不帶分支時推目前分支;強制、刪除、`--mirror`、`--all`、`--tags` 與含 `:` 或 `+` 的 refspec 都不放行",
|
|
146
|
+
"merge-pr": "單獨執行 `gh pr merge [<編號>] [--squash|--merge|--rebase] [-R <owner/repo>]`,分支取 PR 的 base;帶 `--admin`、`-d`、`--delete-branch` 不放行",
|
|
138
147
|
publish: "在套件目錄單獨執行 `pnpm publish --ignore-scripts` 或 `npm publish --ignore-scripts`(可加 --access public|restricted、--tag <名稱>、--dry-run、--provenance,pnpm 另可加 --no-git-checks)",
|
|
139
|
-
release: "單獨執行 `gh release create <tag> -R <owner/repo>`(可加 --title、--notes、--generate-notes、--draft、--prerelease、--verify-tag)"
|
|
148
|
+
release: "單獨執行 `gh release create <tag> -R <owner/repo>`(可加 --title、--notes、--generate-notes、--draft、--prerelease、--verify-tag)",
|
|
149
|
+
"install-prompts": "單獨執行 `pnpm install:prompts`、`pnpm exec promptex build --install [<目錄>]` 或 `promptex build --install [<目錄>]`(可加 --unit <單元>),這次安裝的每個單元都要有授權;帶 --dry-run 不需授權"
|
|
140
150
|
};
|
|
141
|
-
/** The fixed form a grant
|
|
151
|
+
/** The fixed form the PreToolUse hook allows for a grant, filled in with its target; undefined for actions the hook does not allow. */
|
|
142
152
|
function grantForm(target) {
|
|
143
153
|
if (target.action === "publish") return `在 ${target.name} 的套件目錄單獨執行 \`pnpm publish --ignore-scripts\` 或 \`npm publish --ignore-scripts\``;
|
|
144
154
|
if (target.action === "release") return `單獨執行 \`gh release create <符合 ${target.tag} 的 tag> -R <${target.repo} 的 GitHub owner/repo>\``;
|
|
@@ -147,9 +157,70 @@ function grantForm(target) {
|
|
|
147
157
|
const DESCRIBED = 200;
|
|
148
158
|
/** One line naming a request, for reasons and the ops log. */
|
|
149
159
|
function describeRequest(request) {
|
|
150
|
-
const text =
|
|
160
|
+
const text = (() => {
|
|
161
|
+
switch (request.action) {
|
|
162
|
+
case "publish": return `publish ${request.name}@${request.version}`;
|
|
163
|
+
case "release": return `release ${request.repos.join("/") || "(unknown repo)"}@${request.tag}`;
|
|
164
|
+
case "install-prompts": return `install-prompts ${request.unit}`;
|
|
165
|
+
default: return `${request.action} ${request.repos.join("/") || "(unknown repo)"}${request.branch === null ? "(detached HEAD)" : `:${request.branch}`}`;
|
|
166
|
+
}
|
|
167
|
+
})();
|
|
151
168
|
return text.length > DESCRIBED ? `${text.slice(0, DESCRIBED)}…` : text;
|
|
152
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* What is wrong with a paths glob, or null when it is one this CLI reads: relative to the hub root, names joined by
|
|
172
|
+
* `/`, `*` and `?` inside a name, `**` as a whole name for any number of names, `..` only at the start to reach a
|
|
173
|
+
* sibling of the hub, `.` alone for the hub root.
|
|
174
|
+
*/
|
|
175
|
+
function pathPatternProblem(pattern) {
|
|
176
|
+
if (pattern === ".") return null;
|
|
177
|
+
if (pattern === "") return "a path pattern cannot be empty";
|
|
178
|
+
if (pattern.startsWith("/") || pattern.startsWith("~")) return `"${pattern}" is not relative to the hub root`;
|
|
179
|
+
if (/[\\[\]{}!]/.test(pattern) || hasControl(pattern)) return `"${pattern}" uses a character this CLI does not read in a path pattern (\\, [, ], {, }, ! or a control character); write names with *, ? and **`;
|
|
180
|
+
const names = pattern.split("/");
|
|
181
|
+
if (names.some((name) => name === "" || name === ".")) return `"${pattern}" has an empty name or a "." inside it; write it without a trailing, leading or doubled /`;
|
|
182
|
+
const firstOther = names.findIndex((name) => name !== "..");
|
|
183
|
+
if (firstOther === -1) return `"${pattern}" names only directories above the hub root`;
|
|
184
|
+
if (names.slice(firstOther).includes("..")) return `"${pattern}" has ".." after a name; ".." may only lead the pattern`;
|
|
185
|
+
if (names.some((name) => name.includes("**") && name !== "**")) return `"${pattern}" has ** inside a name; ** stands alone for any number of names`;
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
/** Whether text holds a control character: below space, or DEL. */
|
|
189
|
+
const hasControl = (text) => [...text].some((c) => c.charCodeAt(0) < 32 || c.charCodeAt(0) === 127);
|
|
190
|
+
/** Whether one name matches a pattern name; wildcards never match "." or "..". */
|
|
191
|
+
function nameMatch(pattern, name) {
|
|
192
|
+
if (pattern === name) return true;
|
|
193
|
+
if (name === "." || name === "..") return false;
|
|
194
|
+
const source = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
195
|
+
return new RegExp(`^${source}$`).test(name);
|
|
196
|
+
}
|
|
197
|
+
function namesMatch(pattern, names) {
|
|
198
|
+
if (pattern.length === 0) return names.length === 0;
|
|
199
|
+
const [head, ...rest] = pattern;
|
|
200
|
+
if (head === "**") {
|
|
201
|
+
for (let skip = 0; skip <= names.length; skip++) {
|
|
202
|
+
if (names.slice(0, skip).some((n) => n === "..")) break;
|
|
203
|
+
if (namesMatch(rest, names.slice(skip))) return true;
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
return names.length > 0 && nameMatch(head, names[0]) && namesMatch(rest, names.slice(1));
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Whether `path`, relative to the hub root with `/` between names ("" for the root), lies in a directory one of
|
|
211
|
+
* the patterns matches: the path itself or a directory above it matches.
|
|
212
|
+
*/
|
|
213
|
+
function pathInPatterns(patterns, path) {
|
|
214
|
+
const names = path === "" || path === "." ? [] : path.split("/");
|
|
215
|
+
const ups = names.findIndex((name) => name !== "..");
|
|
216
|
+
if (ups === -1 && names.length > 0) return false;
|
|
217
|
+
const shortest = ups > 0 ? ups + 1 : 0;
|
|
218
|
+
return patterns.some((pattern) => {
|
|
219
|
+
const wanted = pattern === "." ? [] : pattern.split("/");
|
|
220
|
+
for (let k = shortest; k <= names.length; k++) if (namesMatch(wanted, names.slice(0, k))) return true;
|
|
221
|
+
return false;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
153
224
|
/** Whether `text` matches a pattern where `*` is any run of characters and `?` one character, neither crossing `/`. */
|
|
154
225
|
function globMatch(pattern, text) {
|
|
155
226
|
const source = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]");
|
|
@@ -821,13 +892,41 @@ const costSchema = z.object({
|
|
|
821
892
|
/**
|
|
822
893
|
* One thing a mainline or a group authorizes ahead of time: an action plus the target it covers. It applies only while
|
|
823
894
|
* a session's action in progress serves this mainline (or a mainline of this group, or the group's own criterion)
|
|
824
|
-
* and the mainline or group is still open.
|
|
895
|
+
* and the mainline or group is still open, and, when it has a scope, while every limit the scope names holds.
|
|
825
896
|
*/
|
|
826
897
|
const grantSchema = z.object({
|
|
827
898
|
id: id("grant"),
|
|
828
899
|
action: z.enum(GRANT_ACTIONS),
|
|
829
900
|
target: text,
|
|
830
|
-
note: text.optional()
|
|
901
|
+
note: text.optional(),
|
|
902
|
+
/**
|
|
903
|
+
* Limits beyond the mainline or group, all of which must hold: a task of the mainline the action serves, a session,
|
|
904
|
+
* paths of the hub the call's directory lies in. Unknown keys are refused, since a misspelt one would widen the grant.
|
|
905
|
+
*/
|
|
906
|
+
scope: z.strictObject({
|
|
907
|
+
task: id("task").optional(),
|
|
908
|
+
session: z.string().min(1).optional(),
|
|
909
|
+
paths: z.array(z.string()).min(1).optional()
|
|
910
|
+
}).superRefine((scope, ctx) => {
|
|
911
|
+
if (scope.task === void 0 && scope.session === void 0 && scope.paths === void 0) ctx.addIssue({
|
|
912
|
+
code: "custom",
|
|
913
|
+
path: [],
|
|
914
|
+
message: "scope names no limit; write task, session or paths, or leave scope out"
|
|
915
|
+
});
|
|
916
|
+
if (scope.session !== void 0 && !SESSION_ID.test(scope.session)) ctx.addIssue({
|
|
917
|
+
code: "custom",
|
|
918
|
+
path: ["session"],
|
|
919
|
+
message: `"${scope.session}" is not a session id, e.g. claude-code-1a2b3c4d`
|
|
920
|
+
});
|
|
921
|
+
scope.paths?.forEach((pattern, i) => {
|
|
922
|
+
const problem = pathPatternProblem(pattern);
|
|
923
|
+
if (problem) ctx.addIssue({
|
|
924
|
+
code: "custom",
|
|
925
|
+
path: ["paths", i],
|
|
926
|
+
message: problem
|
|
927
|
+
});
|
|
928
|
+
});
|
|
929
|
+
}).optional()
|
|
831
930
|
}).superRefine((g, ctx) => {
|
|
832
931
|
const parsed = parseGrantTarget(g.action, g.target);
|
|
833
932
|
if (!parsed.ok) ctx.addIssue({
|
|
@@ -1066,8 +1165,10 @@ const sessionSchema = z.object({
|
|
|
1066
1165
|
description: text.optional(),
|
|
1067
1166
|
depth: z.number().int().nonnegative().optional(),
|
|
1068
1167
|
summary: text.optional(),
|
|
1069
|
-
/** Digest of the
|
|
1168
|
+
/** Digest of the whole turn reminder last sent, so the same reminder is not sent again */
|
|
1070
1169
|
reminderDigest: text.optional(),
|
|
1170
|
+
/** Digests of the check problems the hook after a write last handed over, so a problem already reported is not reported again */
|
|
1171
|
+
checkReported: z.array(text).optional(),
|
|
1071
1172
|
/** Hidden from the default views; independent of status, an ended session stays visible until archived */
|
|
1072
1173
|
archivedAt: date.optional()
|
|
1073
1174
|
});
|
|
@@ -1158,4 +1259,4 @@ function parseOpsLine(line) {
|
|
|
1158
1259
|
return parsed.success ? parsed.data : null;
|
|
1159
1260
|
}
|
|
1160
1261
|
//#endregion
|
|
1161
|
-
export { removedUntil as $, SURVEY_LINES as A, groupSchema as B, PRIORITIES as C, SESSION_ID as D, SERVES_REF as E, WORK_STATUS as F, mainlineSchema as G, isMainflowRange as H, configSchema as I, parseOpsLine as J, opsEntrySchema as K, decisionSchema as L, TASK_REQUIRED_SECTIONS as M, TASK_SECTIONS as N, SESSION_STATUS as O, UNTIL_FORMS as P, refMainline as Q, findingSchema as R, MERGE_STATES as S, RESERVED_HUB_NAMES as T, issueSchema as U, hubRegistrySchema as V, listedRepos as W, parseUntil as X, parseRef as Y, refExample as Z, ISSUE_STATUS as _, DECISION_SECTIONS as a, waitingForAction as at, MAINLINE_SECTIONS as b, FINDING_SECTIONS as c, stampMs as ct, GROUP_SECTIONS as d, GRANT_TARGET_FORMS as dt, repoPath as et, GROUP_STATUS as f, describeRequest as ft, ISSUE_SECTIONS as g,
|
|
1262
|
+
export { removedUntil as $, SURVEY_LINES as A, groupSchema as B, PRIORITIES as C, SESSION_ID as D, SERVES_REF as E, WORK_STATUS as F, mainlineSchema as G, isMainflowRange as H, configSchema as I, parseOpsLine as J, opsEntrySchema as K, decisionSchema as L, TASK_REQUIRED_SECTIONS as M, TASK_SECTIONS as N, SESSION_STATUS as O, UNTIL_FORMS as P, refMainline as Q, findingSchema as R, MERGE_STATES as S, RESERVED_HUB_NAMES as T, issueSchema as U, hubRegistrySchema as V, listedRepos as W, parseUntil as X, parseRef as Y, refExample as Z, ISSUE_STATUS as _, parseGrantTarget as _t, DECISION_SECTIONS as a, waitingForAction as at, MAINLINE_SECTIONS as b, FINDING_SECTIONS as c, stampMs as ct, GROUP_SECTIONS as d, GRANT_TARGET_FORMS as dt, repoPath as et, GROUP_STATUS as f, describeRequest as ft, ISSUE_SECTIONS as g, hasControl as gt, ID_PATTERNS as h, grantRepo as ht, DECISION_BASES as i, untilProblem as it, SURVEY_SECTION as j, SLUG as k, FINDING_STATUS as l, GRANT_ACTIONS as lt, HUB_NAME as m, grantForm as mt, CLOSED_STATUS as n, sessionSchema as nt, DECISION_STATUS as o, waitingForRef as ot, GROUP_SUMMARY_SECTION as p, grantCovers as pt, parseActionRef as q, DECIDERS as r, taskSchema as rt, DEFAULT_CATEGORIES as s, OFFSET as st, CHECKLIST_REF as t, repoPaths as tt, FORMAT_VERSION as u, GRANT_FORMS as ut, LINK_RELS as v, pathInPatterns as vt, REF_EXAMPLES as w, MAINLINE_STATUS as x, MAINFLOW_RANGE as y, formatOpsEntry 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-
|
|
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-l8QUG80k.js";
|
|
2
2
|
//#region src/facts.d.ts
|
|
3
3
|
export interface CommandSpec {
|
|
4
4
|
usage: string;
|
|
@@ -153,6 +153,12 @@ export declare const COMMAND_SPECS: {
|
|
|
153
153
|
readonly args: 2;
|
|
154
154
|
readonly needsHub: false;
|
|
155
155
|
};
|
|
156
|
+
readonly 'grant check': {
|
|
157
|
+
readonly usage: 'grant check [--dry-run]';
|
|
158
|
+
readonly summary: 'Judge whether a grant covers one tool call read from stdin as {"tool","input","cwd","sessionId"} (the shape of a Claude Code mod tool.check event) and print {"decision":"allow"|"none","reason","grant"}. Only a single Bash command in a fixed form (GRANTS.forms) can be allowed; anything else, or anything that cannot be read, is none. An allow is logged to the ops log unless --dry-run. Always exits 0';
|
|
159
|
+
readonly args: 0;
|
|
160
|
+
readonly needsHub: false;
|
|
161
|
+
};
|
|
156
162
|
readonly 'new mainline': {
|
|
157
163
|
readonly usage: 'new mainline <slug> --title <title> [--group <w>] [--priority P0|P1|P2|P3]';
|
|
158
164
|
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';
|
|
@@ -694,9 +700,10 @@ export declare const LINKS: {
|
|
|
694
700
|
* What a mainline's or group's grants authorize: each action with the form of its target. A mainline's grant applies
|
|
695
701
|
* only while the session's action in progress serves the mainline and the mainline is active or planning. A group's
|
|
696
702
|
* grant applies while the group is open and the action serves one of the group's mainlines (of this hub) or the
|
|
697
|
-
* group's own criterion. The PreToolUse hook
|
|
698
|
-
*
|
|
699
|
-
*
|
|
703
|
+
* group's own criterion. A grant's optional `scope` narrows it further (`scope`). The PreToolUse hook allows
|
|
704
|
+
* `publish` and `release` only when the command is written in the fixed form in `forms`; `mainflow grant check`
|
|
705
|
+
* judges every action in `judgedByGrantCheck` the same way. sync-hubs has no single-command form and is for the
|
|
706
|
+
* executor to read.
|
|
700
707
|
*/
|
|
701
708
|
export declare const GRANTS: {
|
|
702
709
|
readonly actions: readonly ["commit", "push", "merge-pr", "publish", "release", "install-prompts", "sync-hubs"];
|
|
@@ -705,9 +712,19 @@ export declare const GRANTS: {
|
|
|
705
712
|
example: string;
|
|
706
713
|
}>;
|
|
707
714
|
readonly enforcedByHook: readonly ['publish', 'release'];
|
|
715
|
+
readonly judgedByGrantCheck: readonly ['commit', 'push', 'merge-pr', 'publish', 'release', 'install-prompts'];
|
|
708
716
|
readonly forms: {
|
|
717
|
+
readonly commit: '單獨執行 `git commit …` 或 `git -C <目錄> commit …`,目錄或 cwd 要在 config.yaml 登記的倉庫或它的 worktree 裡';
|
|
718
|
+
readonly push: '單獨執行 `git push [origin] [<分支>]`,可加 `-C <目錄>`、`-u` 或 `--set-upstream`,不帶分支時推目前分支;強制、刪除、`--mirror`、`--all`、`--tags` 與含 `:` 或 `+` 的 refspec 都不放行';
|
|
719
|
+
readonly 'merge-pr': '單獨執行 `gh pr merge [<編號>] [--squash|--merge|--rebase] [-R <owner/repo>]`,分支取 PR 的 base;帶 `--admin`、`-d`、`--delete-branch` 不放行';
|
|
709
720
|
readonly publish: '在套件目錄單獨執行 `pnpm publish --ignore-scripts` 或 `npm publish --ignore-scripts`(可加 --access public|restricted、--tag <名稱>、--dry-run、--provenance,pnpm 另可加 --no-git-checks)';
|
|
710
721
|
readonly release: '單獨執行 `gh release create <tag> -R <owner/repo>`(可加 --title、--notes、--generate-notes、--draft、--prerelease、--verify-tag)';
|
|
722
|
+
readonly 'install-prompts': '單獨執行 `pnpm install:prompts`、`pnpm exec promptex build --install [<目錄>]` 或 `promptex build --install [<目錄>]`(可加 --unit <單元>),這次安裝的每個單元都要有授權;帶 --dry-run 不需授權';
|
|
723
|
+
};
|
|
724
|
+
readonly scope: {
|
|
725
|
+
readonly task: '任務編號 t{NNN},屬於授權所在的主脈絡;目前行動要服務這個任務的 checklist 項目。組的授權不能寫';
|
|
726
|
+
readonly session: 'mainflow 會話 id,例如 claude-code-1a2b3c4d;只有這場會話的呼叫適用';
|
|
727
|
+
readonly paths: '相對 hub 根目錄的 glob 清單;指令的工作目錄(git 的 -C、promptex 的目錄)要落在其中一個符合的目錄裡。* 與 ? 不跨 /,** 是任意幾層,.. 只能寫在開頭,. 代表 hub 根目錄';
|
|
711
728
|
};
|
|
712
729
|
};
|
|
713
730
|
/**
|
|
@@ -775,7 +792,7 @@ export declare const FIELDS: {
|
|
|
775
792
|
readonly group: readonly [...FieldSpec[], FieldSpec, FieldSpec, FieldSpec];
|
|
776
793
|
readonly criterion: FieldSpec[];
|
|
777
794
|
readonly checklistItem: readonly [...FieldSpec[], FieldSpec];
|
|
778
|
-
readonly grant: readonly [FieldSpec, FieldSpec, FieldSpec, FieldSpec];
|
|
795
|
+
readonly grant: readonly [FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec];
|
|
779
796
|
readonly action: readonly [FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec];
|
|
780
797
|
readonly link: readonly [FieldSpec, FieldSpec, FieldSpec];
|
|
781
798
|
};
|
package/dist/facts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as SURVEY_LINES, C as PRIORITIES, E as SERVES_REF, F as WORK_STATUS, M as TASK_REQUIRED_SECTIONS, N as TASK_SECTIONS, O as SESSION_STATUS, P as UNTIL_FORMS, S as MERGE_STATES, _ as ISSUE_STATUS, a as DECISION_SECTIONS, b as MAINLINE_SECTIONS, c as FINDING_SECTIONS, d as GROUP_SECTIONS, dt as GRANT_TARGET_FORMS, f as GROUP_STATUS, g as ISSUE_SECTIONS, h as ID_PATTERNS, i as DECISION_BASES, j as SURVEY_SECTION, k as SLUG, l as FINDING_STATUS, lt as GRANT_ACTIONS, n as CLOSED_STATUS, o as DECISION_STATUS, r as DECIDERS, s as DEFAULT_CATEGORIES, t as CHECKLIST_REF, u as FORMAT_VERSION, ut as GRANT_FORMS, v as LINK_RELS, x as MAINLINE_STATUS } from "./dist-
|
|
1
|
+
import { A as SURVEY_LINES, C as PRIORITIES, E as SERVES_REF, F as WORK_STATUS, M as TASK_REQUIRED_SECTIONS, N as TASK_SECTIONS, O as SESSION_STATUS, P as UNTIL_FORMS, S as MERGE_STATES, _ as ISSUE_STATUS, a as DECISION_SECTIONS, b as MAINLINE_SECTIONS, c as FINDING_SECTIONS, d as GROUP_SECTIONS, dt as GRANT_TARGET_FORMS, f as GROUP_STATUS, g as ISSUE_SECTIONS, h as ID_PATTERNS, i as DECISION_BASES, j as SURVEY_SECTION, k as SLUG, l as FINDING_STATUS, lt as GRANT_ACTIONS, n as CLOSED_STATUS, o as DECISION_STATUS, r as DECIDERS, s as DEFAULT_CATEGORIES, t as CHECKLIST_REF, u as FORMAT_VERSION, ut as GRANT_FORMS, v as LINK_RELS, x as MAINLINE_STATUS } from "./dist-CXrUfRQp.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 = {
|
|
@@ -143,6 +143,12 @@ const COMMAND_SPECS = {
|
|
|
143
143
|
args: 2,
|
|
144
144
|
needsHub: false
|
|
145
145
|
},
|
|
146
|
+
"grant check": {
|
|
147
|
+
usage: "grant check [--dry-run]",
|
|
148
|
+
summary: "Judge whether a grant covers one tool call read from stdin as {\"tool\",\"input\",\"cwd\",\"sessionId\"} (the shape of a Claude Code mod tool.check event) and print {\"decision\":\"allow\"|\"none\",\"reason\",\"grant\"}. Only a single Bash command in a fixed form (GRANTS.forms) can be allowed; anything else, or anything that cannot be read, is none. An allow is logged to the ops log unless --dry-run. Always exits 0",
|
|
149
|
+
args: 0,
|
|
150
|
+
needsHub: false
|
|
151
|
+
},
|
|
146
152
|
"new mainline": {
|
|
147
153
|
usage: "new mainline <slug> --title <title> [--group <w>] [--priority P0|P1|P2|P3]",
|
|
148
154
|
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",
|
|
@@ -603,15 +609,29 @@ const LINKS = { rels: LINK_RELS };
|
|
|
603
609
|
* What a mainline's or group's grants authorize: each action with the form of its target. A mainline's grant applies
|
|
604
610
|
* only while the session's action in progress serves the mainline and the mainline is active or planning. A group's
|
|
605
611
|
* grant applies while the group is open and the action serves one of the group's mainlines (of this hub) or the
|
|
606
|
-
* group's own criterion. The PreToolUse hook
|
|
607
|
-
*
|
|
608
|
-
*
|
|
612
|
+
* group's own criterion. A grant's optional `scope` narrows it further (`scope`). The PreToolUse hook allows
|
|
613
|
+
* `publish` and `release` only when the command is written in the fixed form in `forms`; `mainflow grant check`
|
|
614
|
+
* judges every action in `judgedByGrantCheck` the same way. sync-hubs has no single-command form and is for the
|
|
615
|
+
* executor to read.
|
|
609
616
|
*/
|
|
610
617
|
const GRANTS = {
|
|
611
618
|
actions: GRANT_ACTIONS,
|
|
612
619
|
targets: GRANT_TARGET_FORMS,
|
|
613
620
|
enforcedByHook: ["publish", "release"],
|
|
614
|
-
|
|
621
|
+
judgedByGrantCheck: [
|
|
622
|
+
"commit",
|
|
623
|
+
"push",
|
|
624
|
+
"merge-pr",
|
|
625
|
+
"publish",
|
|
626
|
+
"release",
|
|
627
|
+
"install-prompts"
|
|
628
|
+
],
|
|
629
|
+
forms: GRANT_FORMS,
|
|
630
|
+
scope: {
|
|
631
|
+
task: "任務編號 t{NNN},屬於授權所在的主脈絡;目前行動要服務這個任務的 checklist 項目。組的授權不能寫",
|
|
632
|
+
session: "mainflow 會話 id,例如 claude-code-1a2b3c4d;只有這場會話的呼叫適用",
|
|
633
|
+
paths: "相對 hub 根目錄的 glob 清單;指令的工作目錄(git 的 -C、promptex 的目錄)要落在其中一個符合的目錄裡。* 與 ? 不跨 /,** 是任意幾層,.. 只能寫在開頭,. 代表 hub 根目錄"
|
|
634
|
+
}
|
|
615
635
|
};
|
|
616
636
|
/**
|
|
617
637
|
* Where each kind of entry lives in the hub's data directory, written as messages name a file of the hub: `.mainflow/`
|
|
@@ -765,7 +785,8 @@ const FIELDS = {
|
|
|
765
785
|
field("id", true, "清單內不重複的編號 g{NNN}"),
|
|
766
786
|
field("action", true, "授權的動作:commit、push、merge-pr、publish、release、install-prompts、sync-hubs"),
|
|
767
787
|
field("target", true, "授權的目標,格式依動作而定,見 GRANTS.targets;倉庫名稱需在 config.yaml 登記"),
|
|
768
|
-
field("note", false, "一句話說明")
|
|
788
|
+
field("note", false, "一句話說明"),
|
|
789
|
+
field("scope", false, "再收窄的範圍,欄位 task、session、paths 見 GRANTS.scope;寫了的欄位全部成立才生效,沒寫時照主脈絡或組的生效條件")
|
|
769
790
|
],
|
|
770
791
|
action: [
|
|
771
792
|
field("id", true, "會話內不重複的編號 a{NNN},由 action add 配發"),
|
|
@@ -778,7 +799,7 @@ const FIELDS = {
|
|
|
778
799
|
field("doneAt", false, "轉為 done 或 dropped 時由 CLI 寫入")
|
|
779
800
|
],
|
|
780
801
|
link: [
|
|
781
|
-
field("rel", true, "關係類型:depends-on
|
|
802
|
+
field("rel", true, "關係類型:depends-on 表示要等對方完成才能推進,主脈絡記的才畫成主脈絡之間的線;relates 表示相關,含沿用對方的產物或知識出處;split-from 表示從對方拆出;反向由讀者推出"),
|
|
782
803
|
field("to", true, "對方的參照,不能在同一條主脈絡內,也可以是組 w{NNN};其他 hub 的加 {hub}: 前綴"),
|
|
783
804
|
field("note", false, "一句話說明這個關係")
|
|
784
805
|
]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { o as HubDirs } from "./index-jq7lN8V8.js";
|
|
2
|
+
import { t as HubWrite } from "./operation-BroV98VB.js";
|
|
3
|
+
//#region src/grant-check.d.ts
|
|
4
|
+
/** What grant check prints. `grants` lists every grant used when more than one was, as for an install of several units. */
|
|
5
|
+
export interface GrantVerdict {
|
|
6
|
+
decision: 'allow' | 'none';
|
|
7
|
+
reason: string;
|
|
8
|
+
grant?: string;
|
|
9
|
+
grants?: string[];
|
|
10
|
+
}
|
|
11
|
+
/** The base branch of a pull request, as `gh pr view [<number>] [-R <repo>] --json baseRefName` in `cwd` gives it; null when it cannot be read. */
|
|
12
|
+
export type PrBase = (query: {
|
|
13
|
+
cwd: string;
|
|
14
|
+
number?: string;
|
|
15
|
+
repo?: string;
|
|
16
|
+
}) => string | null;
|
|
17
|
+
/** The units `command args` in `cwd` would install, read from promptex's dry run; null when they cannot be read. */
|
|
18
|
+
export type ListUnits = (run: {
|
|
19
|
+
command: string;
|
|
20
|
+
args: string[];
|
|
21
|
+
cwd: string;
|
|
22
|
+
}) => string[] | null;
|
|
23
|
+
export interface GrantCheckContext {
|
|
24
|
+
/** The session's hub; null outside any */
|
|
25
|
+
dirs: HubDirs | null;
|
|
26
|
+
/** The writer for the ops log; the hub's writer, as a hook's, when left out */
|
|
27
|
+
write?: HubWrite;
|
|
28
|
+
/** Judge without writing the ops log */
|
|
29
|
+
dryRun?: boolean;
|
|
30
|
+
env?: NodeJS.ProcessEnv;
|
|
31
|
+
/** Lines for stderr, such as an ops log that could not be written */
|
|
32
|
+
warn?: (message: string) => void;
|
|
33
|
+
prBase?: PrBase;
|
|
34
|
+
listUnits?: ListUnits;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Split one simple command into the words the shell passes on: words separated by spaces, each made of plain
|
|
38
|
+
* characters, single-quoted text, and double-quoted text in which the shell expands nothing. Anything else throws.
|
|
39
|
+
*/
|
|
40
|
+
export declare function shellWords(command: string): string[];
|
|
41
|
+
/** Judge one call; any failure is `none`, so a caller that cannot be told otherwise keeps its own decision. */
|
|
42
|
+
export declare function grantCheck(input: unknown, ctx: GrantCheckContext): Promise<GrantVerdict>;
|
|
43
|
+
/** `mainflow grant check`: read the call from stdin, print the verdict as one JSON line, exit 0. */
|
|
44
|
+
export declare function runGrantCheck(cwd: string, dryRun: boolean): Promise<number>;
|
|
45
|
+
//#endregion
|