mainflow 0.24.0 → 0.26.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 +119 -13
- package/dist/{dist-Dy45myuN.js → dist-DlmecRLf.js} +110 -4
- package/dist/facts.d.ts +30 -4
- package/dist/facts.js +30 -5
- package/dist/grant-commands.d.ts +1 -1
- package/dist/grant-commands.js +1 -1
- package/dist/hooks.js +3 -3
- package/dist/{host-2a_9-kKv.js → host-Dr0bCHd8.js} +251 -9
- package/dist/host.js +1 -1
- package/dist/{index-CV30LkNZ.d.ts → index-D4IIwDGE.d.ts} +18 -1
- package/dist/{launch-Bk3UgS-o.js → launch-lnD-Z3hb.js} +2 -2
- package/dist/launch.js +1 -1
- package/dist/{ui-Cngo5j6g.js → ui-Cz8suWal.js} +2 -2
- package/dist/web/assets/{index-Cx_Rtc3B.js → index-xrrh30zr.js} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as
|
|
3
|
-
import {
|
|
2
|
+
import { $ as createIssue, A as sessionBriefing, C as setCriterionText, D as updateTask, E as supersedeDecision, F as loadSessions, I as readSession, L as setSessionArchived, M as addAction, O as untilState, P as handoffSession, Q as createFinding, S as setCriterion, U as updateAction, V as startSession, X as appendOps, Z as closeIssue, _ as createTask, at as HubError, b as setArchived, dt as MAINLINE_FILE, et as planIssue, f as acceptDecision, ft as TASK_FILE, g as createMainline, h as createDecision, it as setIssueArchived, k as duplicateClaimLines, l as migrateHub, m as addRepo, mt as WriteError, nt as setFindingArchived, ot as loadHub, p as addHub, rt as setFindingStatus, tt as reopenIssue, v as dropChecklistItem, vt as findHubRoot, w as setMainlineStatus, x as setChecklistItem, y as initHub, z as setSessionStatus } from "./host-Dr0bCHd8.js";
|
|
3
|
+
import { B as parseUntil, O as UNTIL_FORMS, V as refExample, z as parseRef } from "./dist-DlmecRLf.js";
|
|
4
4
|
import { t as listProgress } from "./progress-1BfPg2zN.js";
|
|
5
5
|
import { COMMAND_SPECS, OPTIONS } from "./facts.js";
|
|
6
6
|
import { hookFallback, parsePayload, runHook } from "./hooks.js";
|
|
7
|
-
import { l as CLI_VERSION, r as launchUi, s as startUiInBackground } from "./launch-
|
|
7
|
+
import { l as CLI_VERSION, r as launchUi, s as startUiInBackground } from "./launch-lnD-Z3hb.js";
|
|
8
8
|
import { readFileSync, realpathSync } from "node:fs";
|
|
9
9
|
import { join, relative } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
@@ -15,9 +15,10 @@ const RUNS = {
|
|
|
15
15
|
status: ({ root, opts }) => printStatus(loadHub(root), opts.all ?? false),
|
|
16
16
|
check: ({ root }) => {
|
|
17
17
|
const hub = loadHub(root);
|
|
18
|
+
const problems = [...hub.problems, ...loadSessions(root).problems];
|
|
18
19
|
printProblems(hub.warnings, "!");
|
|
19
|
-
if (
|
|
20
|
-
printProblems(
|
|
20
|
+
if (problems.length === 0) return done(`✓ ${hub.mainlines.length} mainlines, no problems${hub.warnings.length ? `, ${hub.warnings.length} ${hub.warnings.length === 1 ? "warning" : "warnings"}` : ""}`);
|
|
21
|
+
printProblems(problems);
|
|
21
22
|
return 1;
|
|
22
23
|
},
|
|
23
24
|
migrate: ({ root, opts }) => {
|
|
@@ -69,21 +70,34 @@ const RUNS = {
|
|
|
69
70
|
"session end": ({ root, args, opts }) => done(`ended ${setSessionStatus(root, sessionOf(opts, args[0]), "ended").id}`),
|
|
70
71
|
"session archive": ({ root, args }) => done(`archived ${setSessionArchived(root, args[0], true).id}`),
|
|
71
72
|
"session unarchive": ({ root, args }) => done(`unarchived ${setSessionArchived(root, args[0], false).id}`),
|
|
72
|
-
"action add": ({ root, args, opts }) =>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
"action add": ({ root, args, opts }) => {
|
|
74
|
+
const ref = addAction(root, sessionOf(opts), {
|
|
75
|
+
do: args[0],
|
|
76
|
+
serves: list(opts.serves),
|
|
77
|
+
before: opts.before
|
|
78
|
+
});
|
|
79
|
+
done(`created ${ref}`);
|
|
80
|
+
return remindDuplicates(root, ref);
|
|
81
|
+
},
|
|
77
82
|
"action set": ({ root, args, opts }) => {
|
|
78
|
-
const ref =
|
|
79
|
-
|
|
83
|
+
const ref = actionRef(opts, args[0]);
|
|
84
|
+
const blockedBy = opts.reason ? {
|
|
85
|
+
reason: opts.reason,
|
|
86
|
+
...opts["waiting-for"] ? { waitingFor: opts["waiting-for"] } : {},
|
|
87
|
+
...opts.until ? { until: opts.until } : {}
|
|
88
|
+
} : void 0;
|
|
89
|
+
if (!blockedBy && (opts["waiting-for"] || opts.until)) throw new WriteError("--waiting-for and --until need --reason, e.g. --status blocked --reason \"等 schema 定案\"");
|
|
90
|
+
done(`updated ${ref} (${updateAction(root, ref, {
|
|
80
91
|
status: opts.status,
|
|
81
92
|
do: opts.text,
|
|
82
93
|
serves: opts.serves === void 0 ? void 0 : list(opts.serves),
|
|
83
94
|
before: opts.before,
|
|
84
|
-
note: opts.note
|
|
95
|
+
note: opts.note,
|
|
96
|
+
blockedBy
|
|
85
97
|
}).status})`);
|
|
98
|
+
return opts.status === "doing" ? remindDuplicates(root, ref) : 0;
|
|
86
99
|
},
|
|
100
|
+
wait: ({ root, args, opts }) => waitUntil(root, args[0], opts),
|
|
87
101
|
hook: ({ args }) => {
|
|
88
102
|
let input = "";
|
|
89
103
|
try {
|
|
@@ -341,6 +355,98 @@ function sessionOf(opts, arg) {
|
|
|
341
355
|
if (!id) throw new WriteError("no session given; pass --session <id> or set MAINFLOW_SESSION (the SessionStart hook sets it)");
|
|
342
356
|
return id;
|
|
343
357
|
}
|
|
358
|
+
/** An action named as a{NNN} in the current session, or as {session}#a{NNN}. */
|
|
359
|
+
const actionRef = (opts, arg) => /^a\d{3}$/.test(arg) ? `${sessionOf(opts)}#${arg}` : arg;
|
|
360
|
+
/** Print a line for each running session already doing what this action serves; a reminder, so the exit code stays 0. */
|
|
361
|
+
function remindDuplicates(root, ref) {
|
|
362
|
+
const hash = ref.lastIndexOf("#");
|
|
363
|
+
for (const line of duplicateClaimLines(root, ref.slice(0, hash), ref.slice(hash + 1))) console.log(line);
|
|
364
|
+
return 0;
|
|
365
|
+
}
|
|
366
|
+
/** Exit codes of `wait`: the condition holds, the time ran out, or there is no condition to wait for. */
|
|
367
|
+
const WAIT_EXIT = {
|
|
368
|
+
met: 0,
|
|
369
|
+
timedOut: 1,
|
|
370
|
+
unusable: 2
|
|
371
|
+
};
|
|
372
|
+
const WAIT_DEFAULTS = {
|
|
373
|
+
timeout: 1800,
|
|
374
|
+
interval: 10
|
|
375
|
+
};
|
|
376
|
+
const UNTIL_HINT = UNTIL_FORMS.map((f) => f.format).join(", ");
|
|
377
|
+
/** Block the process for a while; wait runs synchronously like every other command. */
|
|
378
|
+
const sleep = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
379
|
+
function seconds(value, fallback, name, zero) {
|
|
380
|
+
if (value === void 0) return fallback;
|
|
381
|
+
const n = Number(value);
|
|
382
|
+
if (value.trim() === "" || !Number.isFinite(n) || n < 0 || !zero && n === 0) throw new WriteError(`--${name} takes ${zero ? "zero or more" : "more than zero"} seconds, got "${value}"`);
|
|
383
|
+
return n;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Poll a condition until it holds or the time runs out. Given an action, its `until` is read again on each
|
|
387
|
+
* round, so a condition changed while waiting is the one checked; once the action leaves blocked or loses its
|
|
388
|
+
* `until`, there is nothing left to wait for. A condition that can no longer come true ends the wait at once.
|
|
389
|
+
*/
|
|
390
|
+
function waitUntil(root, arg, opts) {
|
|
391
|
+
let timeout;
|
|
392
|
+
let interval;
|
|
393
|
+
let ref;
|
|
394
|
+
let text;
|
|
395
|
+
let condition;
|
|
396
|
+
const unusable = (message) => {
|
|
397
|
+
console.error(`✗ ${message}`);
|
|
398
|
+
return WAIT_EXIT.unusable;
|
|
399
|
+
};
|
|
400
|
+
/** The action's current `until`, or the reason it has none. */
|
|
401
|
+
const untilOf = (actionRef) => {
|
|
402
|
+
const hash = actionRef.lastIndexOf("#");
|
|
403
|
+
let session;
|
|
404
|
+
try {
|
|
405
|
+
session = readSession(root, actionRef.slice(0, hash));
|
|
406
|
+
} catch (error) {
|
|
407
|
+
return { error: error.message };
|
|
408
|
+
}
|
|
409
|
+
const action = session?.actions.find((a) => a.id === actionRef.slice(hash + 1));
|
|
410
|
+
if (!action) return { error: `action ${actionRef} not found` };
|
|
411
|
+
return action.blockedBy?.until ?? { error: `${actionRef} has no until condition (it is ${action.status}); give it one with action set ${arg} --status blocked --reason <reason> --until <condition>, where the condition is one of ${UNTIL_HINT}` };
|
|
412
|
+
};
|
|
413
|
+
try {
|
|
414
|
+
timeout = seconds(opts.timeout, WAIT_DEFAULTS.timeout, "timeout", true);
|
|
415
|
+
interval = seconds(opts.interval, WAIT_DEFAULTS.interval, "interval", false);
|
|
416
|
+
if (arg && opts.until) return unusable("give an action or --until, not both");
|
|
417
|
+
if (!arg && !opts.until) return unusable(`name the action to wait for, e.g. wait a002, or pass --until <condition>, one of ${UNTIL_HINT}`);
|
|
418
|
+
if (arg) {
|
|
419
|
+
ref = actionRef(opts, arg);
|
|
420
|
+
if (!/^[^#]+#a\d{3}$/.test(ref)) return unusable(`expected an action like a002 or claude-code-1a2b3c4d#a002, got "${arg}"`);
|
|
421
|
+
const until = untilOf(ref);
|
|
422
|
+
if (typeof until !== "string") return unusable(until.error);
|
|
423
|
+
text = until;
|
|
424
|
+
} else text = opts.until;
|
|
425
|
+
const parsed = parseUntil(text);
|
|
426
|
+
if (!parsed) return unusable(`"${text}" is not a condition; use one of ${UNTIL_HINT}`);
|
|
427
|
+
condition = parsed;
|
|
428
|
+
} catch (error) {
|
|
429
|
+
if (error instanceof WriteError) return unusable(error.message);
|
|
430
|
+
throw error;
|
|
431
|
+
}
|
|
432
|
+
const deadline = Date.now() + timeout * 1e3;
|
|
433
|
+
for (;;) {
|
|
434
|
+
const state = untilState(root, condition);
|
|
435
|
+
if (state.met) return done(`✓ ${text}: ${state.detail}`);
|
|
436
|
+
if (state.unreachable) return unusable(`${text} can no longer hold: ${state.detail}`);
|
|
437
|
+
const left = deadline - Date.now();
|
|
438
|
+
if (left <= 0) {
|
|
439
|
+
console.error(`✗ still waiting after ${timeout}s for ${text}: ${state.detail}`);
|
|
440
|
+
return WAIT_EXIT.timedOut;
|
|
441
|
+
}
|
|
442
|
+
sleep(Math.min(interval * 1e3, left));
|
|
443
|
+
if (!ref) continue;
|
|
444
|
+
const next = untilOf(ref);
|
|
445
|
+
if (typeof next !== "string") return unusable(`stopped waiting: ${next.error}`);
|
|
446
|
+
text = next;
|
|
447
|
+
condition = parseUntil(next);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
344
450
|
const list = (value) => (value ?? "").split(",").map((v) => v.trim()).filter(Boolean);
|
|
345
451
|
/** The mainline a reference names; throws with the same message resolve.ts gives when it is missing. */
|
|
346
452
|
function findMainline(hub, id) {
|
|
@@ -43,7 +43,7 @@ const GRANT_TARGET_FORMS = {
|
|
|
43
43
|
};
|
|
44
44
|
const KEBAB = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
45
45
|
/** A git branch name, `*` standing for any run of characters. */
|
|
46
|
-
const BRANCH = /^(?!\/)(?!.*\/\/)(?!.*\.\.)[A-Za-z0-9._/*-]+(?<!\/)$/;
|
|
46
|
+
const BRANCH$1 = /^(?!\/)(?!.*\/\/)(?!.*\.\.)[A-Za-z0-9._/*-]+(?<!\/)$/;
|
|
47
47
|
/** npm package names: lowercase, optionally scoped. */
|
|
48
48
|
const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/;
|
|
49
49
|
/** A tag pattern: no whitespace, `*` and `?` as in a shell glob. */
|
|
@@ -62,7 +62,7 @@ function parseGrantTarget(action, target) {
|
|
|
62
62
|
const repo = colon < 0 ? target : target.slice(0, colon);
|
|
63
63
|
const branch = colon < 0 ? void 0 : target.slice(colon + 1);
|
|
64
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`);
|
|
65
|
+
if (branch !== void 0 && !BRANCH$1.test(branch)) return fail(action, `"${branch}" is not a branch name`);
|
|
66
66
|
return {
|
|
67
67
|
ok: true,
|
|
68
68
|
target: {
|
|
@@ -805,16 +805,122 @@ const taskSchema = z.object({
|
|
|
805
805
|
message: "a task cannot depend on itself"
|
|
806
806
|
});
|
|
807
807
|
});
|
|
808
|
+
/** A session id: lowercase letters, digits and hyphens, e.g. claude-code-1a2b3c4d. */
|
|
809
|
+
const SESSION_ID = /^[a-z0-9][a-z0-9-]*$/;
|
|
810
|
+
/** An action in some session, `{sessionId}#a{NNN}`. */
|
|
811
|
+
const ACTION_REF = /^([a-z0-9][a-z0-9-]*)#(a\d{3})$/;
|
|
812
|
+
/** Split an action reference into its session and action ids, or return null when the text is not one. */
|
|
813
|
+
function parseActionRef(input) {
|
|
814
|
+
const m = ACTION_REF.exec(input);
|
|
815
|
+
return m ? {
|
|
816
|
+
session: m[1],
|
|
817
|
+
action: m[2]
|
|
818
|
+
} : null;
|
|
819
|
+
}
|
|
820
|
+
/** The action a blocked action waits for, when its whole `waitingFor` text is an action reference; otherwise undefined. */
|
|
821
|
+
const waitingForAction = (waitingFor) => waitingFor && parseActionRef(waitingFor) ? waitingFor : void 0;
|
|
822
|
+
/** The condition forms, one example each; facts and error messages read them from here. */
|
|
823
|
+
const UNTIL_FORMS = [
|
|
824
|
+
{
|
|
825
|
+
kind: "commit",
|
|
826
|
+
format: "commit:{branch}:{text}",
|
|
827
|
+
example: "commit:main:feat(core): 行動阻塞"
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
kind: "action-done",
|
|
831
|
+
format: "action-done:{sessionId}#a{NNN}",
|
|
832
|
+
example: "action-done:claude-code-1a2b3c4d#a003"
|
|
833
|
+
},
|
|
834
|
+
{
|
|
835
|
+
kind: "file",
|
|
836
|
+
format: "file:{relative path}",
|
|
837
|
+
example: "file:packages/cli/dist/cli.js"
|
|
838
|
+
}
|
|
839
|
+
];
|
|
840
|
+
const UNTIL_EXAMPLES = UNTIL_FORMS.map((f) => f.format).join(", ");
|
|
841
|
+
/** A branch name git log can take as a revision; a leading hyphen would read as an option. */
|
|
842
|
+
const BRANCH = /^[^\s:~^?*[\\-][^\s:~^?*[\\]*$/;
|
|
843
|
+
/** Parse a wait condition, or return null when it matches none of the forms. */
|
|
844
|
+
function parseUntil(input) {
|
|
845
|
+
const colon = input.indexOf(":");
|
|
846
|
+
if (colon < 0) return null;
|
|
847
|
+
const kind = input.slice(0, colon);
|
|
848
|
+
const argument = input.slice(colon + 1);
|
|
849
|
+
switch (kind) {
|
|
850
|
+
case "commit": {
|
|
851
|
+
const split = argument.indexOf(":");
|
|
852
|
+
const branch = argument.slice(0, split);
|
|
853
|
+
const text = argument.slice(split + 1);
|
|
854
|
+
return split > 0 && BRANCH.test(branch) && !branch.includes("..") && text.length > 0 ? {
|
|
855
|
+
kind,
|
|
856
|
+
branch,
|
|
857
|
+
text
|
|
858
|
+
} : null;
|
|
859
|
+
}
|
|
860
|
+
case "action-done": {
|
|
861
|
+
const ref = parseActionRef(argument);
|
|
862
|
+
return ref ? {
|
|
863
|
+
kind,
|
|
864
|
+
...ref
|
|
865
|
+
} : null;
|
|
866
|
+
}
|
|
867
|
+
case "file": return argument.length > 0 && !argument.startsWith("/") ? {
|
|
868
|
+
kind,
|
|
869
|
+
path: argument
|
|
870
|
+
} : null;
|
|
871
|
+
default: return null;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Why a blocked action cannot move, what it waits for, and the condition a program checks to end the wait.
|
|
876
|
+
* `waitingFor` is free text, another session's action such as claude-code-1a2b3c4d#a003, or a hub reference
|
|
877
|
+
* such as m031/t002 when the whole text is one.
|
|
878
|
+
*/
|
|
879
|
+
const actionBlockSchema = z.object({
|
|
880
|
+
reason: text,
|
|
881
|
+
waitingFor: text.optional(),
|
|
882
|
+
until: z.string().refine((s) => parseUntil(s) !== null, `expected a condition like ${UNTIL_EXAMPLES}`).optional()
|
|
883
|
+
});
|
|
808
884
|
const actionSchema = z.object({
|
|
809
885
|
id: id("action"),
|
|
810
886
|
do: z.string().min(1),
|
|
811
887
|
status: z.enum(WORK_STATUS),
|
|
812
888
|
serves: z.array(z.string().regex(CHECKLIST_REF)),
|
|
813
889
|
note: text.optional(),
|
|
890
|
+
/** Required while blocked, absent otherwise */
|
|
891
|
+
blockedBy: actionBlockSchema.optional(),
|
|
814
892
|
/** Set when the action turns doing and done; the transcript trace attributes tool use by these */
|
|
815
893
|
startedAt: datetime.optional(),
|
|
816
894
|
doneAt: datetime.optional()
|
|
895
|
+
}).superRefine((a, ctx) => {
|
|
896
|
+
if (a.status === "blocked" && !a.blockedBy) ctx.addIssue({
|
|
897
|
+
code: "custom",
|
|
898
|
+
path: ["blockedBy"],
|
|
899
|
+
message: "a blocked action must say what blocks it"
|
|
900
|
+
});
|
|
901
|
+
if (a.status !== "blocked" && a.blockedBy) ctx.addIssue({
|
|
902
|
+
code: "custom",
|
|
903
|
+
path: ["blockedBy"],
|
|
904
|
+
message: `only a blocked action carries blockedBy; this one is ${a.status}`
|
|
905
|
+
});
|
|
817
906
|
});
|
|
907
|
+
/** The reason a blocked action written before `blockedBy` existed is given when it carries no note either. */
|
|
908
|
+
const LEGACY_BLOCK_REASON = "(舊資料未寫原因)";
|
|
909
|
+
/**
|
|
910
|
+
* Session files written before `blockedBy` existed hold blocked actions without one. Reading fills it from the
|
|
911
|
+
* action's note, so those files stay readable and the next write stores the filled form. `actionSchema` itself
|
|
912
|
+
* stays strict, so a new block still has to say what blocks it.
|
|
913
|
+
*/
|
|
914
|
+
function withLegacyBlock(input) {
|
|
915
|
+
if (typeof input !== "object" || input === null) return input;
|
|
916
|
+
const action = input;
|
|
917
|
+
if (action.status !== "blocked" || action.blockedBy !== void 0) return input;
|
|
918
|
+
const reason = typeof action.note === "string" && action.note.length > 0 ? action.note : LEGACY_BLOCK_REASON;
|
|
919
|
+
return {
|
|
920
|
+
...action,
|
|
921
|
+
blockedBy: { reason }
|
|
922
|
+
};
|
|
923
|
+
}
|
|
818
924
|
const sessionSchema = z.object({
|
|
819
925
|
id: z.string().min(1),
|
|
820
926
|
agent: z.string().min(1),
|
|
@@ -823,7 +929,7 @@ const sessionSchema = z.object({
|
|
|
823
929
|
delegatedFrom: z.string().min(1).nullable(),
|
|
824
930
|
handoffFrom: z.string().min(1).nullable(),
|
|
825
931
|
cwd: z.string().min(1),
|
|
826
|
-
actions: z.array(actionSchema),
|
|
932
|
+
actions: z.array(z.preprocess(withLegacyBlock, actionSchema)),
|
|
827
933
|
/** Where the platform keeps the conversation, for tracing and for opening it */
|
|
828
934
|
transcript: text.optional(),
|
|
829
935
|
worktree: text.optional(),
|
|
@@ -904,4 +1010,4 @@ function parseOpsLine(line) {
|
|
|
904
1010
|
return parsed.success ? parsed.data : null;
|
|
905
1011
|
}
|
|
906
1012
|
//#endregion
|
|
907
|
-
export {
|
|
1013
|
+
export { grantRepo as $, configSchema as A, parseUntil as B, SLUG as C, TASK_SECTIONS as D, TASK_REQUIRED_SECTIONS as E, mainlineSchema as F, waitingForAction as G, refMainline as H, opsEntrySchema as I, GRANT_FORMS as J, waitingForRef as K, parseActionRef as L, findingSchema as M, formatOpsEntry as N, UNTIL_FORMS as O, issueSchema as P, grantForm as Q, parseOpsLine as R, SESSION_STATUS as S, SURVEY_SECTION as T, sessionSchema as U, refExample as V, taskSchema as W, describeRequest as X, GRANT_TARGET_FORMS as Y, grantCovers as Z, MERGE_STATES as _, DECISION_SECTIONS as a, RESERVED_HUB_NAMES as b, FINDING_SECTIONS as c, ID_PATTERNS as d, parseGrantTarget as et, ISSUE_SECTIONS as f, MAINLINE_STATUS as g, MAINLINE_SECTIONS as h, DECISION_BASES as i, decisionSchema as j, WORK_STATUS as k, FINDING_STATUS as l, LINK_RELS as m, CLOSED_STATUS as n, DECISION_STATUS as o, ISSUE_STATUS as p, GRANT_ACTIONS as q, DECIDERS as r, DEFAULT_CATEGORIES as s, CHECKLIST_REF as t, FORMAT_VERSION as u, PRIORITIES as v, SURVEY_LINES as w, SESSION_ID as x, REF_EXAMPLES as y, parseRef as z };
|
package/dist/facts.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as SurveyLine, i as SLUG, n as FORMAT_VERSION, r as ID_PATTERNS, t as CHECKLIST_REF } from "./index-
|
|
1
|
+
import { a as SurveyLine, i as SLUG, n as FORMAT_VERSION, o as Until, r as ID_PATTERNS, t as CHECKLIST_REF } from "./index-D4IIwDGE.js";
|
|
2
2
|
//#region src/facts.d.ts
|
|
3
3
|
export interface CommandSpec {
|
|
4
4
|
usage: string;
|
|
@@ -98,16 +98,23 @@ export declare const COMMAND_SPECS: {
|
|
|
98
98
|
};
|
|
99
99
|
readonly 'action add': {
|
|
100
100
|
readonly usage: 'action add <text> --serves <m/t#c>[,<m/t#c>] [--before <a>] [--session <session>]';
|
|
101
|
-
readonly summary: 'Add an action to the session queue';
|
|
101
|
+
readonly summary: 'Add an action to the session queue; prints a reminder when another session active in the last 15 minutes is doing an item it serves';
|
|
102
102
|
readonly args: 1;
|
|
103
103
|
readonly needsHub: true;
|
|
104
104
|
};
|
|
105
105
|
readonly 'action set': {
|
|
106
|
-
readonly usage: 'action set <a|session#a> [--status] [--text] [--serves] [--before <a>] [--note <text>] [--session <session>]';
|
|
107
|
-
readonly summary: 'Change, reorder or finish an action';
|
|
106
|
+
readonly usage: 'action set <a|session#a> [--status] [--reason --waiting-for --until] [--text] [--serves] [--before <a>] [--note <text>] [--session <session>]';
|
|
107
|
+
readonly summary: 'Change, reorder or finish an action; --status blocked needs --reason, and leaving blocked clears it. --status doing reminds like action add. A handed-off action keeps its note';
|
|
108
108
|
readonly args: 1;
|
|
109
109
|
readonly needsHub: true;
|
|
110
110
|
};
|
|
111
|
+
readonly wait: {
|
|
112
|
+
readonly usage: 'wait [a|session#a] [--until <condition>] [--timeout <seconds>] [--interval <seconds>] [--session <session>]';
|
|
113
|
+
readonly summary: 'Wait until an action\'s until condition, or the --until one, holds: exits 0 when it does, 1 when the timeout (1800 s) runs out, 2 when there is no usable condition, the action leaves blocked, or the condition can no longer hold; checks every 10 s';
|
|
114
|
+
readonly args: 0;
|
|
115
|
+
readonly optionalArgs: 1;
|
|
116
|
+
readonly needsHub: true;
|
|
117
|
+
};
|
|
111
118
|
readonly hook: {
|
|
112
119
|
readonly usage: 'hook <platform> <event>';
|
|
113
120
|
readonly summary: 'Handle a host hook event read from stdin, e.g. hook claude-code session-start';
|
|
@@ -373,6 +380,15 @@ export declare const OPTIONS: {
|
|
|
373
380
|
readonly signal: {
|
|
374
381
|
readonly type: 'string';
|
|
375
382
|
};
|
|
383
|
+
readonly until: {
|
|
384
|
+
readonly type: 'string';
|
|
385
|
+
};
|
|
386
|
+
readonly timeout: {
|
|
387
|
+
readonly type: 'string';
|
|
388
|
+
};
|
|
389
|
+
readonly interval: {
|
|
390
|
+
readonly type: 'string';
|
|
391
|
+
};
|
|
376
392
|
readonly branch: {
|
|
377
393
|
readonly type: 'string';
|
|
378
394
|
};
|
|
@@ -476,6 +492,15 @@ export declare const REFERENCES: readonly [{
|
|
|
476
492
|
readonly format: '{sessionId}#a{NNN}';
|
|
477
493
|
readonly example: 'claude-code-1a2b3c4d#a003';
|
|
478
494
|
}];
|
|
495
|
+
/**
|
|
496
|
+
* Conditions a blocked action's `until` takes and `wait` checks, one example each: a commit on a branch whose
|
|
497
|
+
* message contains the text (git log runs at the hub root), an action that is done, a path relative to the hub root that exists (`..` reaches sibling repositories).
|
|
498
|
+
*/
|
|
499
|
+
export declare const UNTIL: readonly {
|
|
500
|
+
kind: Until['kind'];
|
|
501
|
+
format: string;
|
|
502
|
+
example: string;
|
|
503
|
+
}[];
|
|
479
504
|
/** Relation types a document's links take; readers derive the reverse direction. */
|
|
480
505
|
export declare const LINKS: {
|
|
481
506
|
readonly rels: readonly ["depends-on", "relates", "split-from"];
|
|
@@ -557,6 +582,7 @@ export declare const FIELDS: {
|
|
|
557
582
|
readonly criterion: FieldSpec[];
|
|
558
583
|
readonly checklistItem: readonly [...FieldSpec[], FieldSpec];
|
|
559
584
|
readonly grant: readonly [FieldSpec, FieldSpec, FieldSpec, FieldSpec];
|
|
585
|
+
readonly action: readonly [FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec, FieldSpec];
|
|
560
586
|
readonly link: readonly [FieldSpec, FieldSpec, FieldSpec];
|
|
561
587
|
};
|
|
562
588
|
//#endregion
|
package/dist/facts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { C as SLUG, D as TASK_SECTIONS, E as TASK_REQUIRED_SECTIONS, J as GRANT_FORMS, O as UNTIL_FORMS, S as SESSION_STATUS, T as SURVEY_SECTION, Y as GRANT_TARGET_FORMS, _ as MERGE_STATES, a as DECISION_SECTIONS, c as FINDING_SECTIONS, d as ID_PATTERNS, f as ISSUE_SECTIONS, g as MAINLINE_STATUS, h as MAINLINE_SECTIONS, i as DECISION_BASES, k as WORK_STATUS, l as FINDING_STATUS, m as LINK_RELS, n as CLOSED_STATUS, o as DECISION_STATUS, p as ISSUE_STATUS, q as GRANT_ACTIONS, r as DECIDERS, s as DEFAULT_CATEGORIES, t as CHECKLIST_REF, u as FORMAT_VERSION, v as PRIORITIES, w as SURVEY_LINES } from "./dist-DlmecRLf.js";
|
|
2
2
|
//#region src/facts.ts
|
|
3
3
|
const COMMAND_SPECS = {
|
|
4
4
|
init: {
|
|
@@ -89,16 +89,23 @@ const COMMAND_SPECS = {
|
|
|
89
89
|
},
|
|
90
90
|
"action add": {
|
|
91
91
|
usage: "action add <text> --serves <m/t#c>[,<m/t#c>] [--before <a>] [--session <session>]",
|
|
92
|
-
summary: "Add an action to the session queue",
|
|
92
|
+
summary: "Add an action to the session queue; prints a reminder when another session active in the last 15 minutes is doing an item it serves",
|
|
93
93
|
args: 1,
|
|
94
94
|
needsHub: true
|
|
95
95
|
},
|
|
96
96
|
"action set": {
|
|
97
|
-
usage: "action set <a|session#a> [--status] [--text] [--serves] [--before <a>] [--note <text>] [--session <session>]",
|
|
98
|
-
summary: "Change, reorder or finish an action",
|
|
97
|
+
usage: "action set <a|session#a> [--status] [--reason --waiting-for --until] [--text] [--serves] [--before <a>] [--note <text>] [--session <session>]",
|
|
98
|
+
summary: "Change, reorder or finish an action; --status blocked needs --reason, and leaving blocked clears it. --status doing reminds like action add. A handed-off action keeps its note",
|
|
99
99
|
args: 1,
|
|
100
100
|
needsHub: true
|
|
101
101
|
},
|
|
102
|
+
wait: {
|
|
103
|
+
usage: "wait [a|session#a] [--until <condition>] [--timeout <seconds>] [--interval <seconds>] [--session <session>]",
|
|
104
|
+
summary: "Wait until an action's until condition, or the --until one, holds: exits 0 when it does, 1 when the timeout (1800 s) runs out, 2 when there is no usable condition, the action leaves blocked, or the condition can no longer hold; checks every 10 s",
|
|
105
|
+
args: 0,
|
|
106
|
+
optionalArgs: 1,
|
|
107
|
+
needsHub: true
|
|
108
|
+
},
|
|
102
109
|
hook: {
|
|
103
110
|
usage: "hook <platform> <event>",
|
|
104
111
|
summary: "Handle a host hook event read from stdin, e.g. hook claude-code session-start",
|
|
@@ -319,6 +326,9 @@ const OPTIONS = {
|
|
|
319
326
|
reason: { type: "string" },
|
|
320
327
|
"waiting-for": { type: "string" },
|
|
321
328
|
signal: { type: "string" },
|
|
329
|
+
until: { type: "string" },
|
|
330
|
+
timeout: { type: "string" },
|
|
331
|
+
interval: { type: "string" },
|
|
322
332
|
branch: { type: "string" },
|
|
323
333
|
worktree: { type: "string" },
|
|
324
334
|
base: { type: "string" },
|
|
@@ -408,6 +418,11 @@ const REFERENCES = [
|
|
|
408
418
|
example: "claude-code-1a2b3c4d#a003"
|
|
409
419
|
}
|
|
410
420
|
];
|
|
421
|
+
/**
|
|
422
|
+
* Conditions a blocked action's `until` takes and `wait` checks, one example each: a commit on a branch whose
|
|
423
|
+
* message contains the text (git log runs at the hub root), an action that is done, a path relative to the hub root that exists (`..` reaches sibling repositories).
|
|
424
|
+
*/
|
|
425
|
+
const UNTIL = UNTIL_FORMS;
|
|
411
426
|
/** Relation types a document's links take; readers derive the reverse direction. */
|
|
412
427
|
const LINKS = { rels: LINK_RELS };
|
|
413
428
|
/**
|
|
@@ -562,6 +577,16 @@ const FIELDS = {
|
|
|
562
577
|
field("target", true, "授權的目標,格式依動作而定,見 GRANTS.targets;倉庫名稱需在 config.yaml 登記"),
|
|
563
578
|
field("note", false, "一句話說明")
|
|
564
579
|
],
|
|
580
|
+
action: [
|
|
581
|
+
field("id", true, "會話內不重複的編號 a{NNN},由 action add 配發"),
|
|
582
|
+
field("do", true, "要做的事,一句話"),
|
|
583
|
+
field("status", true, "狀態值見 STATUSES,用 action set 改"),
|
|
584
|
+
field("serves", true, "推進的 checklist 項目參照清單,可以是空的"),
|
|
585
|
+
field("note", false, "一句備註"),
|
|
586
|
+
field("blockedBy", false, "卡住時的原因 reason、等待對象 waitingFor 與程式可判斷的條件 until,blocked 時必填,其他狀態不得有;等待對象整串是其他會話的行動(例如 claude-code-1a2b3c4d#a003)時,對方的簡報會列出有人在等它;條件格式見 UNTIL"),
|
|
587
|
+
field("startedAt", false, "轉為 doing 時由 CLI 寫入"),
|
|
588
|
+
field("doneAt", false, "轉為 done 或 dropped 時由 CLI 寫入")
|
|
589
|
+
],
|
|
565
590
|
link: [
|
|
566
591
|
field("rel", true, "關係類型:depends-on、relates、split-from,反向由讀者推出"),
|
|
567
592
|
field("to", true, "對方的參照,不能在同一條主脈絡內;其他 hub 的加 {hub}: 前綴"),
|
|
@@ -569,4 +594,4 @@ const FIELDS = {
|
|
|
569
594
|
]
|
|
570
595
|
};
|
|
571
596
|
//#endregion
|
|
572
|
-
export { CHECKLIST_REF, CLASSIFICATION, CLOSED_STATUSES, COMMAND_SPECS, FIELDS, FORMAT_VERSION, GRANTS, ID_PATTERNS, LAYOUT, LINKS, OPTIONS, REFERENCES, SECTIONS, SLUG, STATUSES, SURVEY };
|
|
597
|
+
export { CHECKLIST_REF, CLASSIFICATION, CLOSED_STATUSES, COMMAND_SPECS, FIELDS, FORMAT_VERSION, GRANTS, ID_PATTERNS, LAYOUT, LINKS, OPTIONS, REFERENCES, SECTIONS, SLUG, STATUSES, SURVEY, UNTIL };
|
package/dist/grant-commands.d.ts
CHANGED
package/dist/grant-commands.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { J as GRANT_FORMS } from "./dist-DlmecRLf.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.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { A as sessionBriefing, B as settleSession, F as loadSessions, G as matchGrants, I as readSession, J as headerLine, K as decorateReply, N as ensureSession, R as setSessionInfo, W as grantScope, X as appendOps, Y as mayDecorate, _t as currentBranch, at as HubError, gt as DATA_DIR, ht as readYaml, i as hostName, j as turnReminder, ot as loadHub, q as endsWithMarker, vt as findHubRoot, yt as worktreesOf, z as setSessionStatus } from "./host-Dr0bCHd8.js";
|
|
2
|
+
import { A as configSchema, X as describeRequest } from "./dist-DlmecRLf.js";
|
|
3
3
|
import { resolveFixed, scanCommand, scopedCwd } from "./grant-commands.js";
|
|
4
4
|
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
|
5
5
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
@@ -320,7 +320,7 @@ function checkAfterWrite(root, { payload, cli }) {
|
|
|
320
320
|
}
|
|
321
321
|
let problems;
|
|
322
322
|
try {
|
|
323
|
-
problems = loadHub(hub).problems;
|
|
323
|
+
problems = [...loadHub(hub).problems, ...loadSessions(hub).problems];
|
|
324
324
|
} catch (error) {
|
|
325
325
|
problems = error instanceof HubError ? [...error.problems, {
|
|
326
326
|
file: `${DATA_DIR}/config.yaml`,
|