@pmelab/gtd 64.2.0 → 65.0.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/.claude-plugin/plugin.json +1 -1
- package/README.md +36 -26
- package/claude/hooks/assist.ts +9 -35
- package/claude/hooks/doors.ts +4 -4
- package/claude/hooks/git.ts +56 -0
- package/claude/hooks/handoff.ts +11 -11
- package/claude/hooks/jobs.ts +8 -8
- package/claude/hooks/pr.ts +8 -8
- package/claude/hooks/register.tsx +16 -78
- package/claude/hooks/tools.ts +7 -20
- package/claude/hooks/wording.ts +3 -3
- package/dist/chunks/{flows-dnIE_qbe.mjs → flows-DGs0W3HI.mjs} +19 -2
- package/dist/chunks/{testkit-BwCH8ky6.mjs → testkit-CgrLmpdJ.mjs} +741 -80
- package/dist/chunks/{wire-CaPJ9Tme.mjs → wire-BNS8ciNu.mjs} +5 -3
- package/dist/flows.mjs +1 -1
- package/dist/gtd.bundle.mjs +565 -114
- package/dist/gtd.core.mjs +132 -129
- package/dist/testkit/gherkin.mjs +1 -1
- package/dist/testkit.mjs +2 -2
- package/dist/web/main.js +2 -2
- package/dist/wire.mjs +1 -1
- package/dist/workflow-kit.mjs +1 -1
- package/package.json +1 -1
- package/src/cli/Cli.ts +5 -5
- package/src/cli/History.test.ts +10 -8
- package/src/cli/Metrics.test.ts +2 -1
- package/src/cli/Program.test.ts +4 -2
- package/src/cli/Program.ts +87 -5
- package/src/edge/Edge.test.ts +37 -16
- package/src/edge/Edge.ts +296 -31
- package/src/emit/GitScript.ts +116 -15
- package/src/emit/index.ts +7 -0
- package/src/flows/config.ts +7 -1
- package/src/flows/index.test.ts +1 -0
- package/src/flows/index.ts +2 -0
- package/src/flows/runtime.ts +32 -1
- package/src/platform/RetainedHistory.ts +3 -0
- package/src/platform/index.ts +1 -0
- package/src/replay/Replay.test.ts +9 -0
- package/src/replay/Replay.ts +59 -15
- package/src/replay/Trailers.ts +14 -0
- package/src/replay/index.ts +1 -1
- package/src/step/LandStep.ts +12 -0
- package/src/step/RepoSnapshot.ts +11 -0
- package/src/step/index.ts +1 -0
- package/src/step/planStart.test.ts +4 -3
- package/src/step/planStart.ts +7 -0
- package/src/step/planStep.ts +17 -12
- package/src/step/startBranch.ts +54 -0
- package/src/twins/EmittedScriptRecognizer.ts +117 -0
- package/src/twins/EmittedScripts.test.ts +147 -1
- package/src/twins/InMemRepo.ts +116 -0
- package/src/wire/Demand.ts +22 -10
- package/src/wire/constants.ts +3 -0
- package/src/wire/types.ts +4 -0
- package/src/workflow/Workflow.ts +1 -0
- package/src/workflow/compile.ts +2 -0
- package/src/workflow/load.ts +2 -0
- package/claude/hooks/ship.ts +0 -730
|
@@ -25,9 +25,8 @@ import type { Judgment } from "./judge"
|
|
|
25
25
|
import { catchFrom, throwTo } from "./handoff"
|
|
26
26
|
import { jobs } from "./jobs"
|
|
27
27
|
import { documents, readDocument } from "./schema"
|
|
28
|
-
import { READ_ONLY_GIT, ship } from "./ship"
|
|
29
28
|
import { withRequirements } from "./sketch"
|
|
30
|
-
import type {
|
|
29
|
+
import type { Done, GitIo } from "./git"
|
|
31
30
|
import { collectAnswer } from "./delegate"
|
|
32
31
|
import { decideCommand, decideContinue, decideJob, restId, TOOLS } from "./tools"
|
|
33
32
|
import type { JobName } from "./tools"
|
|
@@ -72,7 +71,7 @@ let isDriving = false
|
|
|
72
71
|
// bumped by every stop, so a queued continue that was mid-check sees it
|
|
73
72
|
let stopCount = 0
|
|
74
73
|
// subagents answering into a file: what each may touch (see tool.call)
|
|
75
|
-
const delegates = new Map<string, { file: string
|
|
74
|
+
const delegates = new Map<string, { file: string }>()
|
|
76
75
|
const ours = new Set<string>()
|
|
77
76
|
// What each turn agent may read and write; reset on every spawn and resume,
|
|
78
77
|
// since the steering file can differ per step.
|
|
@@ -295,8 +294,7 @@ async function wake($: $) {
|
|
|
295
294
|
const r = await read($, run)
|
|
296
295
|
const stop = r.stop
|
|
297
296
|
if (!stop || !r.rest) return
|
|
298
|
-
|
|
299
|
-
if (!wakes(stop, branch)) return
|
|
297
|
+
if (!wakes(stop)) return
|
|
300
298
|
const questions = stop.isJudge ? await openQuestions($) : undefined
|
|
301
299
|
const text = wakePrompt({
|
|
302
300
|
stop,
|
|
@@ -304,7 +302,6 @@ async function wake($: $) {
|
|
|
304
302
|
notes: r.notes,
|
|
305
303
|
isRepeat: r.isRepeat,
|
|
306
304
|
questions,
|
|
307
|
-
branch,
|
|
308
305
|
})
|
|
309
306
|
await update($, run, (x) => ({ ...x, notes: undefined }))
|
|
310
307
|
// Not from inside the hook that started the run: a prompt submitted there
|
|
@@ -321,16 +318,6 @@ async function openQuestions($: $) {
|
|
|
321
318
|
}
|
|
322
319
|
}
|
|
323
320
|
|
|
324
|
-
// The branch a finished process can open its pull request from, if any.
|
|
325
|
-
async function shippable($: $) {
|
|
326
|
-
const branch = (
|
|
327
|
-
await $.process.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], { cwd: root })
|
|
328
|
-
).stdout.trim()
|
|
329
|
-
if (["main", "master", "HEAD"].includes(branch)) return undefined
|
|
330
|
-
const summary = await $.process.run(["gtd", "summary"], { cwd: root })
|
|
331
|
-
return summary.exitCode === 0 ? branch : undefined
|
|
332
|
-
}
|
|
333
|
-
|
|
334
321
|
// A new process starts from the requirements written to the steering file an
|
|
335
322
|
// idle rest names; the opening beat captures them like a hand-edit.
|
|
336
323
|
async function begin($: $, requirements: string) {
|
|
@@ -440,8 +427,8 @@ function startJob(
|
|
|
440
427
|
$: $,
|
|
441
428
|
job: JobName,
|
|
442
429
|
isInTurn: boolean,
|
|
443
|
-
work: () => Promise<
|
|
444
|
-
then?: (outcome:
|
|
430
|
+
work: () => Promise<Done>,
|
|
431
|
+
then?: (outcome: Done) => Promise<void>,
|
|
445
432
|
) {
|
|
446
433
|
const answer = background.start(job, work, true, async (outcome) => {
|
|
447
434
|
if (then) await then(outcome)
|
|
@@ -457,7 +444,7 @@ function startJob(
|
|
|
457
444
|
}
|
|
458
445
|
|
|
459
446
|
async function throwNow($: $, target: string | undefined, isInTurn: boolean) {
|
|
460
|
-
const refused = decideJob(await read($, run)
|
|
447
|
+
const refused = decideJob(await read($, run))
|
|
461
448
|
if (refused) return refused
|
|
462
449
|
return startJob(
|
|
463
450
|
$,
|
|
@@ -474,7 +461,7 @@ async function throwNow($: $, target: string | undefined, isInTurn: boolean) {
|
|
|
474
461
|
content: b.content,
|
|
475
462
|
isIdle: isTrue(b.idle),
|
|
476
463
|
}
|
|
477
|
-
return throwTo(
|
|
464
|
+
return throwTo(gitIo($), rest, target, new Date().toISOString())
|
|
478
465
|
},
|
|
479
466
|
async (thrown) => {
|
|
480
467
|
if (!thrown.ok) return
|
|
@@ -490,7 +477,7 @@ async function throwNow($: $, target: string | undefined, isInTurn: boolean) {
|
|
|
490
477
|
|
|
491
478
|
async function enterNow($: $, door: string, args: string[]) {
|
|
492
479
|
if ((await read($, run)).isRunning) return "Stop the loop first: /gtd stop."
|
|
493
|
-
const entered = await enter(
|
|
480
|
+
const entered = await enter(gitIo($), door, args)
|
|
494
481
|
if (entered.ok) await start($)
|
|
495
482
|
return entered.text
|
|
496
483
|
}
|
|
@@ -511,7 +498,7 @@ async function prNow($: $) {
|
|
|
511
498
|
if (r.pr && !isReviewGate(r.stop)) {
|
|
512
499
|
return "Review threads from an earlier /gtd pr wait for the review's first review gate."
|
|
513
500
|
}
|
|
514
|
-
const io =
|
|
501
|
+
const io = gitIo($)
|
|
515
502
|
const pr = await currentPr(io)
|
|
516
503
|
if (typeof pr === "string") return pr
|
|
517
504
|
const threads = await unresolvedThreads(io, pr)
|
|
@@ -585,7 +572,7 @@ async function prRest($: $, stop: Stop) {
|
|
|
585
572
|
async function replyThreads($: $, stop: Stop) {
|
|
586
573
|
const pr = (await read($, run)).pr
|
|
587
574
|
if (!pr?.importedAt) return
|
|
588
|
-
const io =
|
|
575
|
+
const io = gitIo($)
|
|
589
576
|
const found = await decided(
|
|
590
577
|
io,
|
|
591
578
|
pr.importedAt,
|
|
@@ -618,7 +605,7 @@ async function replyThreads($: $, stop: Stop) {
|
|
|
618
605
|
// shown to the catcher first, never landed unseen.
|
|
619
606
|
async function catchNow($: $, ref: string) {
|
|
620
607
|
if ((await read($, run)).isRunning) return "Stop the loop first: /gtd stop."
|
|
621
|
-
const caught = await catchFrom(
|
|
608
|
+
const caught = await catchFrom(gitIo($), ref)
|
|
622
609
|
if (!caught.ok) return caught.text
|
|
623
610
|
await findRoot($)
|
|
624
611
|
const b = await docs($).next()
|
|
@@ -636,27 +623,11 @@ async function catchNow($: $, ref: string) {
|
|
|
636
623
|
return caught.text
|
|
637
624
|
}
|
|
638
625
|
|
|
639
|
-
// Ship's text turns need git to read the process, so they run as a subagent.
|
|
640
|
-
// Its answer is a file, not its hand-back: a hand-back never reaches a mod.
|
|
641
|
-
// `mayRun`: the only shell commands the subagent may run; none when absent.
|
|
642
626
|
type Delegate = {
|
|
643
627
|
name: string
|
|
644
628
|
description: string
|
|
645
629
|
system: string
|
|
646
630
|
tools: string[]
|
|
647
|
-
mayRun?: RegExp
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
const SHIPPER: Delegate = {
|
|
651
|
-
name: "shipper",
|
|
652
|
-
description:
|
|
653
|
-
"Writes commit messages and pull requests for gtd's ship, spawned by the gtd mod only",
|
|
654
|
-
system:
|
|
655
|
-
"You write commit messages and pull-request descriptions for gtd's ship command. Read the repository with git and the file tools as the instructions ask. Never modify the repository: no commits, no checkouts, no pushes.",
|
|
656
|
-
tools: ["Bash", "Read", "Grep", "Glob", "Write", "Skill"],
|
|
657
|
-
// It reads commit messages anyone on the branch wrote: read-only git, and
|
|
658
|
-
// no shell syntax to chain anything else onto it.
|
|
659
|
-
mayRun: READ_ONLY_GIT,
|
|
660
631
|
}
|
|
661
632
|
|
|
662
633
|
// gtd's llm judge persona; writing its answer file is its one tool.
|
|
@@ -693,7 +664,7 @@ async function delegate($: $, who: Delegate, prompt: string, model: string, deli
|
|
|
693
664
|
const id = spawned.agentId
|
|
694
665
|
if (!id) return undefined
|
|
695
666
|
ours.add(id)
|
|
696
|
-
delegates.set(id, { file
|
|
667
|
+
delegates.set(id, { file })
|
|
697
668
|
// A timed-out agent may still be running: it keeps its tool limits until its
|
|
698
669
|
// turn ends, where turn.complete drops them.
|
|
699
670
|
return collectAnswer({
|
|
@@ -704,11 +675,6 @@ async function delegate($: $, who: Delegate, prompt: string, model: string, deli
|
|
|
704
675
|
})
|
|
705
676
|
}
|
|
706
677
|
|
|
707
|
-
async function writer($: $, prompt: string) {
|
|
708
|
-
const model = (await $.env.get("GTD_PLANNERMODEL")) ?? "opus"
|
|
709
|
-
return delegate($, SHIPPER, prompt, model, "Your final answer, exactly as you would print it.")
|
|
710
|
-
}
|
|
711
|
-
|
|
712
678
|
// Where `gtd judge run` would pick its llm, a judge gate is put to a small
|
|
713
679
|
// model as that provider does, but as a subagent of this session. Any doubt leaves the
|
|
714
680
|
// gate to the person, which routes the workflow its cautious way.
|
|
@@ -729,39 +695,19 @@ async function judgeInSession($: $, j: Judgment) {
|
|
|
729
695
|
}
|
|
730
696
|
}
|
|
731
697
|
|
|
732
|
-
function
|
|
698
|
+
function gitIo($: $): GitIo {
|
|
733
699
|
return {
|
|
734
700
|
run: async (argv, stdin) => {
|
|
735
701
|
const r = await $.process.run(argv, { cwd: root, stdin, timeoutMs: TEN_MINUTES })
|
|
736
702
|
return { code: r.exitCode, out: r.stdout, err: r.stderr }
|
|
737
703
|
},
|
|
738
|
-
complete: (prompt) => writer($, prompt),
|
|
739
704
|
log: (line) => {
|
|
740
705
|
for (const l of line.split("\n")) $.ui.log(l)
|
|
741
706
|
if (background.busy()) $.ui.status(line.split("\n")[0]!)
|
|
742
707
|
},
|
|
743
|
-
today: () => new Date().toISOString().slice(0, 10),
|
|
744
708
|
}
|
|
745
709
|
}
|
|
746
710
|
|
|
747
|
-
async function shipNow($: $, isDry: boolean, isInTurn: boolean) {
|
|
748
|
-
const refused = decideJob(await read($, run), "ship")
|
|
749
|
-
if (refused) return refused
|
|
750
|
-
return startJob(
|
|
751
|
-
$,
|
|
752
|
-
"ship",
|
|
753
|
-
isInTurn,
|
|
754
|
-
async () => {
|
|
755
|
-
$.ui.status(isDry ? "previewing the pull request…" : "opening the pull request…")
|
|
756
|
-
return ship(shipIo($), isDry)
|
|
757
|
-
},
|
|
758
|
-
async (shipped) => {
|
|
759
|
-
// a failed ship leaves the run as it was; a dry run changes nothing
|
|
760
|
-
if (shipped.ok && !isDry) await dismiss($)
|
|
761
|
-
},
|
|
762
|
-
)
|
|
763
|
-
}
|
|
764
|
-
|
|
765
711
|
// Turns a missing CLI or a wrong directory into an instruction, not a stack trace.
|
|
766
712
|
// The plugin is gtd's own npm package, so it runs the gtd it ships and the mod
|
|
767
713
|
// and the CLI never disagree. A checkout without a build falls back to PATH.
|
|
@@ -986,8 +932,6 @@ async function command($: $, arg: string): Promise<string | undefined> {
|
|
|
986
932
|
return rest[0]
|
|
987
933
|
? catchNow($, rest[0])
|
|
988
934
|
: "Name the pull request or branch: /gtd catch <number|branch>."
|
|
989
|
-
case "ship":
|
|
990
|
-
return shipNow($, rest[0] === "-n" || rest[0] === "--dry-run", false)
|
|
991
935
|
case "":
|
|
992
936
|
return resume($)
|
|
993
937
|
default:
|
|
@@ -997,7 +941,7 @@ async function command($: $, arg: string): Promise<string | undefined> {
|
|
|
997
941
|
|
|
998
942
|
// A door wins over the bare `pr` verb; anything else is requirements.
|
|
999
943
|
async function routed($: $, verb: string, rest: string[], arg: string) {
|
|
1000
|
-
if (doorFor(await doors(
|
|
944
|
+
if (doorFor(await doors(gitIo($)), verb, rest)) return enterNow($, verb, rest)
|
|
1001
945
|
return verb === "pr" && rest.length === 0 ? prNow($) : begin($, arg)
|
|
1002
946
|
}
|
|
1003
947
|
|
|
@@ -1067,12 +1011,9 @@ function subagentDenial(agentId: string, e: Record<string, unknown>) {
|
|
|
1067
1011
|
if (denial) return denial
|
|
1068
1012
|
const delegated = delegates.get(agentId)
|
|
1069
1013
|
if (!delegated) return undefined
|
|
1070
|
-
if (e.tool === "Write" && e.file_path !== delegated.file) {
|
|
1014
|
+
if ((e.tool === "Write" && e.file_path !== delegated.file) || e.tool === "Bash") {
|
|
1071
1015
|
return `Write only your answer, to ${delegated.file}.`
|
|
1072
1016
|
}
|
|
1073
|
-
if (e.tool === "Bash" && !delegated.mayRun?.test(String(e.command).trim())) {
|
|
1074
|
-
return "Only read-only git commands, without shell syntax, are allowed here."
|
|
1075
|
-
}
|
|
1076
1017
|
return undefined
|
|
1077
1018
|
}
|
|
1078
1019
|
|
|
@@ -1111,7 +1052,7 @@ export const register: Register = (on) => {
|
|
|
1111
1052
|
name: "gtd",
|
|
1112
1053
|
description: "Drive gtd until it rests on you; pass requirements to start a new process",
|
|
1113
1054
|
argumentHint:
|
|
1114
|
-
"[requirements | <door> [args] | stop | status | ui |
|
|
1055
|
+
"[requirements | <door> [args] | stop | status | ui | pr | throw [@user] | catch <pr|branch>]",
|
|
1115
1056
|
immediate: true,
|
|
1116
1057
|
})
|
|
1117
1058
|
return next(e)
|
|
@@ -1191,9 +1132,6 @@ export const register: Register = (on) => {
|
|
|
1191
1132
|
on("tool.call", { tool: "mcp__gtd__handoff" }, async ($, e) =>
|
|
1192
1133
|
answer(await throwNow($, typeof e.to === "string" && e.to ? e.to : undefined, true), true),
|
|
1193
1134
|
)
|
|
1194
|
-
on("tool.call", { tool: "mcp__gtd__ship" }, async ($, e) =>
|
|
1195
|
-
answer(await shipNow($, e.dryRun === true, true), true),
|
|
1196
|
-
)
|
|
1197
1135
|
on("tool.call", { tool: "mcp__gtd__browser" }, async ($) => answer(await browse($), true))
|
|
1198
1136
|
|
|
1199
1137
|
on("agent.offer", ($, e, next) => (e.agent.startsWith("gtd:") ? { isOffered: false } : next(e)))
|
package/claude/hooks/tools.ts
CHANGED
|
@@ -10,7 +10,6 @@ export const TOOL = {
|
|
|
10
10
|
continue: "mcp__gtd__continue",
|
|
11
11
|
note: "mcp__gtd__note",
|
|
12
12
|
handoff: "mcp__gtd__handoff",
|
|
13
|
-
ship: "mcp__gtd__ship",
|
|
14
13
|
browser: "mcp__gtd__browser",
|
|
15
14
|
} as const
|
|
16
15
|
|
|
@@ -44,12 +43,6 @@ export const TOOLS = [
|
|
|
44
43
|
"Hands the gtd process to someone else: pushes the branch and opens or refreshes a draft pull request assigned to them. Call it only when the person asks. `to`: their GitHub username; omit it to leave the process for anyone.",
|
|
45
44
|
inputSchema: { type: "object", properties: { to: { type: "string" } } },
|
|
46
45
|
},
|
|
47
|
-
{
|
|
48
|
-
name: "ship",
|
|
49
|
-
description:
|
|
50
|
-
"Combines the finished gtd process into one commit, pushes it and opens or updates its pull request. Call it only after the person agreed. `dryRun` previews without writing anything.",
|
|
51
|
-
inputSchema: { type: "object", properties: { dryRun: { type: "boolean" } } },
|
|
52
|
-
},
|
|
53
46
|
{
|
|
54
47
|
name: "browser",
|
|
55
48
|
description:
|
|
@@ -71,21 +64,21 @@ export type ContinueIo = {
|
|
|
71
64
|
// the rest's own `gtd validate` script, run; ok when there is nothing to check
|
|
72
65
|
validate(): Promise<{ ok: boolean; out: string }>
|
|
73
66
|
judgment(): Promise<Judgment | undefined>
|
|
74
|
-
// a
|
|
67
|
+
// a hand-off running or queued: it writes the tree the driver would
|
|
75
68
|
busy?: JobName
|
|
76
69
|
}
|
|
77
70
|
|
|
78
|
-
export type JobName = "
|
|
71
|
+
export type JobName = "handoff"
|
|
79
72
|
|
|
80
|
-
export const JOB_NAME: Record<JobName, string> = {
|
|
73
|
+
export const JOB_NAME: Record<JobName, string> = { handoff: "hand-off" }
|
|
81
74
|
|
|
82
75
|
const busyText = (job: JobName) => `A ${JOB_NAME[job]} is still running: wait for its report.`
|
|
83
76
|
|
|
84
|
-
// Only these leave the tree alone (
|
|
77
|
+
// Only these leave the tree alone (throw refuses for itself); any
|
|
85
78
|
// other verb, unknown ones included, may switch branch or write .gtd/. `stop`
|
|
86
79
|
// is not here: `command` takes the bare word itself, and `stop <words>` is a
|
|
87
80
|
// requirement. `ui` is not: its page writes steering files.
|
|
88
|
-
const WHILE_BUSY = new Set(["status", "throw"
|
|
81
|
+
const WHILE_BUSY = new Set(["status", "throw"])
|
|
89
82
|
|
|
90
83
|
export function decideCommand(arg: string, busy: JobName | undefined): string | undefined {
|
|
91
84
|
if (!busy) return undefined
|
|
@@ -94,13 +87,9 @@ export function decideCommand(arg: string, busy: JobName | undefined): string |
|
|
|
94
87
|
}
|
|
95
88
|
|
|
96
89
|
// Pure: a refused job leaves the run, and so `continue`, as it was.
|
|
97
|
-
export function decideJob(run: Run
|
|
90
|
+
export function decideJob(run: Run): string | undefined {
|
|
98
91
|
if (run.isRunning) return "gtd is running a step: wait for it to rest."
|
|
99
92
|
if (run.pending) return "A continue is already waiting for this turn to end."
|
|
100
|
-
const kind = run.stop?.kind
|
|
101
|
-
if (job === "ship" && (kind === "gate" || kind === "stalled" || kind === "error")) {
|
|
102
|
-
return "gtd still waits on the person at this step: answer it with continue first."
|
|
103
|
-
}
|
|
104
93
|
return undefined
|
|
105
94
|
}
|
|
106
95
|
|
|
@@ -116,9 +105,7 @@ export async function decideContinue(
|
|
|
116
105
|
if (io.busy) return { refuse: busyText(io.busy) }
|
|
117
106
|
const stop = run.stop
|
|
118
107
|
if (!stop) return { refuse: "Nothing is waiting: gtd has no rest open. /gtd drives it." }
|
|
119
|
-
if (stop.kind === "done") {
|
|
120
|
-
return { refuse: `The process is finished: ${TOOL.ship} opens its pull request.` }
|
|
121
|
-
}
|
|
108
|
+
if (stop.kind === "done") return { refuse: "The process is finished: nothing waits here." }
|
|
122
109
|
if (rest !== undefined && rest !== run.rest) {
|
|
123
110
|
return {
|
|
124
111
|
refuse: "gtd has moved on from that step; it no longer waits there. Nothing was changed.",
|
package/claude/hooks/wording.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type { Stop } from "../types"
|
|
4
4
|
import type { Beat } from "./drive"
|
|
5
|
-
import type {
|
|
5
|
+
import type { Done } from "./git"
|
|
6
6
|
import { JOB_NAME } from "./tools"
|
|
7
7
|
import type { JobName } from "./tools"
|
|
8
8
|
|
|
@@ -61,8 +61,8 @@ export function pushText(stop: Stop, repo: string, url?: string) {
|
|
|
61
61
|
|
|
62
62
|
const capitalised = (s: string) => s[0]!.toUpperCase() + s.slice(1)
|
|
63
63
|
|
|
64
|
-
export const jobHeadline = (job: JobName, outcome:
|
|
64
|
+
export const jobHeadline = (job: JobName, outcome: Done) =>
|
|
65
65
|
`${outcome.ok ? "✔" : "✘"} ${capitalised(JOB_NAME[job])} ${outcome.ok ? "finished" : "failed"}`
|
|
66
66
|
|
|
67
|
-
export const jobPushText = (job: JobName, outcome:
|
|
67
|
+
export const jobPushText = (job: JobName, outcome: Done, repo: string) =>
|
|
68
68
|
`${repo}: ${JOB_NAME[job]} ${outcome.ok ? "finished" : "failed"}. ${outcome.text}`.trim()
|
|
@@ -60,6 +60,17 @@ const run = (name, body, options = {}) => ctx().step({
|
|
|
60
60
|
body,
|
|
61
61
|
options
|
|
62
62
|
});
|
|
63
|
+
/**
|
|
64
|
+
* Squashes HEAD's tree, with `.gtd/` as the user's branch has it, into one commit on the user's branch
|
|
65
|
+
* with `message`, and moves that branch there; HEAD stays on `gtd/<b>`.
|
|
66
|
+
* Resolves to the new commit's hash, or `undefined` when nothing shipped.
|
|
67
|
+
*/
|
|
68
|
+
const ship = (name, message, options = {}) => ctx().step({
|
|
69
|
+
kind: "ship",
|
|
70
|
+
name,
|
|
71
|
+
message,
|
|
72
|
+
options
|
|
73
|
+
});
|
|
63
74
|
/** A judge gate: resolves to what the judge answered, for the flow to decide on. */
|
|
64
75
|
const judge = (name, spec) => {
|
|
65
76
|
const { questions, evidence, ...options } = spec;
|
|
@@ -551,7 +562,8 @@ const CONFIG_KEYS = [
|
|
|
551
562
|
"modes",
|
|
552
563
|
"ui",
|
|
553
564
|
"skills",
|
|
554
|
-
"access"
|
|
565
|
+
"access",
|
|
566
|
+
"historyRemote"
|
|
555
567
|
];
|
|
556
568
|
const MODE_KEYS = ["format", "validate"];
|
|
557
569
|
const UI_KEYS = [
|
|
@@ -622,6 +634,10 @@ const configProblems = (value) => {
|
|
|
622
634
|
...unknownKeys(value, CONFIG_KEYS, []),
|
|
623
635
|
...value.modes === void 0 ? [] : recordOf(value.modes, ["modes"], "modes", modeProblems),
|
|
624
636
|
...value.ui === void 0 ? [] : uiProblems(value.ui, ["ui"]),
|
|
637
|
+
...value.historyRemote === void 0 || typeof value.historyRemote === "string" && value.historyRemote.trim() !== "" ? [] : [{
|
|
638
|
+
path: ["historyRemote"],
|
|
639
|
+
message: "must be a non-empty remote name"
|
|
640
|
+
}],
|
|
625
641
|
...value.skills === void 0 ? [] : recordOf(value.skills, ["skills"], "skill-name arrays", (v, path) => isStringArray(v) ? [] : [{
|
|
626
642
|
path,
|
|
627
643
|
message: "must be an array of skill names"
|
|
@@ -690,6 +706,7 @@ var flows_exports = /* @__PURE__ */ __exportAll({
|
|
|
690
706
|
sections: () => sections,
|
|
691
707
|
settings: () => settings,
|
|
692
708
|
settingsOf: () => settingsOf,
|
|
709
|
+
ship: () => ship,
|
|
693
710
|
showScript: () => showScript,
|
|
694
711
|
skillsFor: () => skillsFor,
|
|
695
712
|
start: () => start,
|
|
@@ -698,4 +715,4 @@ var flows_exports = /* @__PURE__ */ __exportAll({
|
|
|
698
715
|
writeScript: () => writeScript,
|
|
699
716
|
wrote: () => wrote
|
|
700
717
|
});
|
|
701
|
-
export { run as $, FLOWS_PROTOCOL as A, head as B, moveScript as C, revertScript as D, restoreScript as E, changesSince as F, mark as G, installContext as H, codeThreads as I, openQuestions as J, markedOptions as K, envSettings as L, accessFor as M, agent as N, showScript as O, changes as P, restart as Q, envSettingsOf as R, checkScript as S, removeScript as T, judge as U, human as V, kept as W, read as X, protocolMismatch as Y, refuse as Z, requireRevert as _, defineWorkflow as a,
|
|
718
|
+
export { run as $, FLOWS_PROTOCOL as A, head as B, moveScript as C, revertScript as D, restoreScript as E, changesSince as F, mark as G, installContext as H, codeThreads as I, openQuestions as J, markedOptions as K, envSettings as L, accessFor as M, agent as N, showScript as O, changes as P, restart as Q, envSettingsOf as R, checkScript as S, removeScript as T, judge as U, human as V, kept as W, read as X, protocolMismatch as Y, refuse as Z, requireRevert as _, defineWorkflow as a, ship as at, wrote as b, check as c, threads as ct, hasThreadFor as d, scope as et, judgeDiffShape as f, requireReplies as g, requireProgress as h, configure as i, settingsOf as it, FlowsProtocolError as j, writeScript as k, codeChanges as l, workflow as lt, requireAnswers as m, configProblems as n, sections as nt, isFactoryWorkflow as o, skillsFor as ot, numeric as p, matches as q, defineConfig as r, settings as rt, answered as s, start as st, flows_exports as t, sectionBodies as tt, displayPath as u, requireThreadsClosed as v, quote as w, applyScript as x, seamGate as y, glob as z };
|