@momentiq/dark-factory-cli 0.7.0-alpha.9 → 1.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/dist/cli.js +383 -29
- package/dist/cli.js.map +1 -1
- package/dist/handoff/accept-verb.d.ts +31 -0
- package/dist/handoff/accept-verb.d.ts.map +1 -0
- package/dist/handoff/accept-verb.js +165 -0
- package/dist/handoff/accept-verb.js.map +1 -0
- package/dist/handoff/args.d.ts +19 -0
- package/dist/handoff/args.d.ts.map +1 -0
- package/dist/handoff/args.js +68 -0
- package/dist/handoff/args.js.map +1 -0
- package/dist/handoff/assignees.d.ts +37 -0
- package/dist/handoff/assignees.d.ts.map +1 -0
- package/dist/handoff/assignees.js +46 -0
- package/dist/handoff/assignees.js.map +1 -0
- package/dist/handoff/handoff-verb.d.ts +34 -0
- package/dist/handoff/handoff-verb.d.ts.map +1 -0
- package/dist/handoff/handoff-verb.js +451 -0
- package/dist/handoff/handoff-verb.js.map +1 -0
- package/dist/handoff/handoffs-verb.d.ts +29 -0
- package/dist/handoff/handoffs-verb.d.ts.map +1 -0
- package/dist/handoff/handoffs-verb.js +84 -0
- package/dist/handoff/handoffs-verb.js.map +1 -0
- package/dist/handoff/index.d.ts +14 -143
- package/dist/handoff/index.d.ts.map +1 -1
- package/dist/handoff/index.js +28 -647
- package/dist/handoff/index.js.map +1 -1
- package/dist/handoff/iso.d.ts +9 -0
- package/dist/handoff/iso.d.ts.map +1 -0
- package/dist/handoff/iso.js +47 -0
- package/dist/handoff/iso.js.map +1 -0
- package/dist/handoff/links.d.ts +48 -0
- package/dist/handoff/links.d.ts.map +1 -0
- package/dist/handoff/links.js +211 -0
- package/dist/handoff/links.js.map +1 -0
- package/dist/handoff/markers.d.ts +50 -0
- package/dist/handoff/markers.d.ts.map +1 -0
- package/dist/handoff/markers.js +121 -0
- package/dist/handoff/markers.js.map +1 -0
- package/dist/handoff/ports.d.ts +126 -0
- package/dist/handoff/ports.d.ts.map +1 -0
- package/dist/handoff/ports.js +34 -0
- package/dist/handoff/ports.js.map +1 -0
- package/dist/handoff/real-clients.d.ts +43 -0
- package/dist/handoff/real-clients.d.ts.map +1 -0
- package/dist/handoff/real-clients.js +291 -0
- package/dist/handoff/real-clients.js.map +1 -0
- package/dist/handoff/rehydrate-core.d.ts +55 -0
- package/dist/handoff/rehydrate-core.d.ts.map +1 -0
- package/dist/handoff/rehydrate-core.js +243 -0
- package/dist/handoff/rehydrate-core.js.map +1 -0
- package/dist/handoff/rehydrate-render.d.ts +3 -0
- package/dist/handoff/rehydrate-render.d.ts.map +1 -0
- package/dist/handoff/rehydrate-render.js +107 -0
- package/dist/handoff/rehydrate-render.js.map +1 -0
- package/dist/handoff/rehydrate-verb.d.ts +31 -0
- package/dist/handoff/rehydrate-verb.d.ts.map +1 -0
- package/dist/handoff/rehydrate-verb.js +111 -0
- package/dist/handoff/rehydrate-verb.js.map +1 -0
- package/dist/handoff/scrub.d.ts +99 -0
- package/dist/handoff/scrub.d.ts.map +1 -0
- package/dist/handoff/scrub.js +127 -0
- package/dist/handoff/scrub.js.map +1 -0
- package/dist/handoff/strip-control.d.ts +4 -0
- package/dist/handoff/strip-control.d.ts.map +1 -0
- package/dist/handoff/strip-control.js +16 -0
- package/dist/handoff/strip-control.js.map +1 -0
- package/dist/mcp/server.d.ts +0 -8
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +1 -5
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/handoff.d.ts +16 -8
- package/dist/mcp/tools/handoff.d.ts.map +1 -1
- package/dist/mcp/tools/handoff.js +253 -303
- package/dist/mcp/tools/handoff.js.map +1 -1
- package/package.json +2 -2
- package/dist/handoff/cli.d.ts +0 -5
- package/dist/handoff/cli.d.ts.map +0 -1
- package/dist/handoff/cli.js +0 -227
- package/dist/handoff/cli.js.map +0 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type GhClient } from "./ports.js";
|
|
2
|
+
import { type RehydrateData } from "./rehydrate-core.js";
|
|
3
|
+
export interface RunAcceptOptions {
|
|
4
|
+
readonly issue: number;
|
|
5
|
+
readonly gh: GhClient;
|
|
6
|
+
}
|
|
7
|
+
export interface RunAcceptResult {
|
|
8
|
+
readonly issueNumber: number;
|
|
9
|
+
/**
|
|
10
|
+
* Rehydrate data — CLI prints via renderRehydrateText (Task 10), MCP
|
|
11
|
+
* returns as structuredContent.
|
|
12
|
+
*/
|
|
13
|
+
readonly rehydrate: RehydrateData;
|
|
14
|
+
/**
|
|
15
|
+
* Success-path operator info lines (each printed to stderr by the CLI).
|
|
16
|
+
* Contains exactly the two `log "…"` lines from accept.sh that fire on
|
|
17
|
+
* the success path: the assign confirmation (step 5) and the close
|
|
18
|
+
* confirmation (step 7). Fatal paths put their guidance in the thrown
|
|
19
|
+
* HandoffError message, not here.
|
|
20
|
+
*/
|
|
21
|
+
readonly logs: readonly string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Take the baton on a handoff issue. Validates → refuses-on-other →
|
|
25
|
+
* strict-rehydrates → drift-checks → assigns → verifies → closes. Any failure
|
|
26
|
+
* before step 5 leaves the issue untouched on the stack; a failure between
|
|
27
|
+
* step 5 and step 7 leaves the issue open + assigned-to-@me (re-run is
|
|
28
|
+
* idempotent via the updatedAt close-failure retry path in step 4).
|
|
29
|
+
*/
|
|
30
|
+
export declare function runAccept(opts: RunAcceptOptions): Promise<RunAcceptResult>;
|
|
31
|
+
//# sourceMappingURL=accept-verb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accept-verb.d.ts","sourceRoot":"","sources":["../../src/handoff/accept-verb.ts"],"names":[],"mappings":"AA6CA,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAI9E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,eAAe,CAAC,CAiJ1B"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// packages/cli/src/handoff/accept-verb.ts
|
|
2
|
+
//
|
|
3
|
+
// /accept atomic chain. PORT FROM dark-factory-platform .claude/skills/handoff/
|
|
4
|
+
// scripts/accept.sh@a6f711b (147 LOC). All read-only work precedes mutations;
|
|
5
|
+
// a failed step leaves the issue unassigned + open on the stack.
|
|
6
|
+
//
|
|
7
|
+
// gh call sequence (the load-bearing race-safety contract — Task 18 asserts
|
|
8
|
+
// this order via FakeGhClient.calls()):
|
|
9
|
+
// step 1: gh.issueView(issue) — slot 1 (body-bearing)
|
|
10
|
+
// ↓
|
|
11
|
+
// gh.apiUserLogin() — lazy; first assigneesStatus(…, me)
|
|
12
|
+
// triggers it, matching bash which
|
|
13
|
+
// calls `me_login` inside the FIRST
|
|
14
|
+
// assignees_status (lib.sh:88) → not
|
|
15
|
+
// before step 1. Eager resolution would
|
|
16
|
+
// reorder apiUserLogin ahead of
|
|
17
|
+
// issueView and break step-1-failure
|
|
18
|
+
// tests + the call-sequence assertion.
|
|
19
|
+
// step 3: gh.issueView(issue) — slot 2 (body-bearing; inside
|
|
20
|
+
// deriveRehydrateData)
|
|
21
|
+
// + per-linked-item gh.prView or gh.issueView (linked-item counter)
|
|
22
|
+
// step 4: gh.issueViewSlim(issue) — slot 1 (body-less)
|
|
23
|
+
// step 5: gh.issueAssignMe(issue)
|
|
24
|
+
// step 6: gh.issueViewSlim(issue) — slot 2 (body-less)
|
|
25
|
+
// step 7: gh.issueClose(issue)
|
|
26
|
+
//
|
|
27
|
+
// Both step 4 and step 6 use issueViewSlim — that's a separate counter from
|
|
28
|
+
// the body-bearing slots (validates step 1 vs the strict-rehydrate step 3).
|
|
29
|
+
//
|
|
30
|
+
// Error-path discipline (advisor catch #1): bash prints `warn "…"` to stderr
|
|
31
|
+
// BEFORE `exit 1`, so the operator sees the detailed guidance ("re-run
|
|
32
|
+
// /accept", "coordinate with @other", etc.). The HandoffError shape is
|
|
33
|
+
// frozen to the v1 surface (message + savedNotePath) for the Tasks 11-22
|
|
34
|
+
// window — there is no field to carry an extra "log line" past a throw. So
|
|
35
|
+
// on every fatal path we throw the DETAILED message itself, and `logs` only
|
|
36
|
+
// carries the two SUCCESS-path info lines that actually reach the return
|
|
37
|
+
// (matches handoff-verb.ts's discipline).
|
|
38
|
+
import { assigneesOtherCsv, assigneesStatus, MeLoginCache, } from "./assignees.js";
|
|
39
|
+
import { validateLatestBlock } from "./markers.js";
|
|
40
|
+
import { HandoffError } from "./ports.js";
|
|
41
|
+
import { deriveRehydrateData } from "./rehydrate-core.js";
|
|
42
|
+
const HANDOFF_LABEL = "handoff";
|
|
43
|
+
/**
|
|
44
|
+
* Take the baton on a handoff issue. Validates → refuses-on-other →
|
|
45
|
+
* strict-rehydrates → drift-checks → assigns → verifies → closes. Any failure
|
|
46
|
+
* before step 5 leaves the issue untouched on the stack; a failure between
|
|
47
|
+
* step 5 and step 7 leaves the issue open + assigned-to-@me (re-run is
|
|
48
|
+
* idempotent via the updatedAt close-failure retry path in step 4).
|
|
49
|
+
*/
|
|
50
|
+
export async function runAccept(opts) {
|
|
51
|
+
const { issue, gh } = opts;
|
|
52
|
+
const logs = [];
|
|
53
|
+
const meCache = new MeLoginCache();
|
|
54
|
+
// --- step 1: validate (read-only) ----------------------------------------
|
|
55
|
+
let view;
|
|
56
|
+
try {
|
|
57
|
+
view = await gh.issueView(issue);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
throw new HandoffError(`could not fetch issue #${issue} (gh error) — not mutating.`);
|
|
61
|
+
}
|
|
62
|
+
if (view.state === "CLOSED") {
|
|
63
|
+
throw new HandoffError(`issue #${issue} is closed — the handoff was already accepted.`);
|
|
64
|
+
}
|
|
65
|
+
// (view.state is typed `"OPEN" | "CLOSED"` so the bash's catch-all
|
|
66
|
+
// "unexpected state" branch is unreachable here; left out.)
|
|
67
|
+
const hasHandoffLabel = view.labels.some((l) => l.name === HANDOFF_LABEL);
|
|
68
|
+
if (!hasHandoffLabel) {
|
|
69
|
+
throw new HandoffError(`issue #${issue} is not a handoff issue (no \`${HANDOFF_LABEL}\` label).`);
|
|
70
|
+
}
|
|
71
|
+
// The body MUST carry a parseable LATEST agent-context block (matches the
|
|
72
|
+
// extractor `do_rehydrate` uses — see bash:64-70). A stale-valid + new-
|
|
73
|
+
// malformed body would pass a first-block check yet render no reasoning,
|
|
74
|
+
// and accept would close the handoff while losing the artifact it's
|
|
75
|
+
// supposed to preserve.
|
|
76
|
+
if (!validateLatestBlock(view.body)) {
|
|
77
|
+
throw new HandoffError(`issue #${issue} has no parseable agent-context block in the body (missing, malformed, or reversed markers — or the latest block is malformed) — refusing to accept (and close) a handoff with no reasoning artifact. Use \`/handoff ${issue}\` to add a well-formed note first.`);
|
|
78
|
+
}
|
|
79
|
+
// Lazy me-login (advisor catch #2): bash calls `me_login` inside the FIRST
|
|
80
|
+
// `assignees_status` call (lib.sh:88), which happens AFTER the step-1
|
|
81
|
+
// issue view. Resolving here — after the view + body validation, just
|
|
82
|
+
// before we need it — matches bash's gh call order. Eager resolution at
|
|
83
|
+
// the top of runAccept would reorder apiUserLogin ahead of issueView in
|
|
84
|
+
// FakeGhClient.calls() AND fire on step-1-failure paths where bash never
|
|
85
|
+
// hits `gh api user`.
|
|
86
|
+
const me = await meCache.resolve(gh);
|
|
87
|
+
const initialAssignees = view.assignees;
|
|
88
|
+
const initialUpdatedAt = view.updatedAt;
|
|
89
|
+
const initialStatus = assigneesStatus(initialAssignees, me);
|
|
90
|
+
// --- step 2: refuse if assigned to @other --------------------------------
|
|
91
|
+
if (initialStatus === "other") {
|
|
92
|
+
const others = assigneesOtherCsv(initialAssignees, me);
|
|
93
|
+
throw new HandoffError(`issue #${issue} is currently assigned to @${others} — coordinate with them or ask them to re-handoff (which un-assigns).`);
|
|
94
|
+
}
|
|
95
|
+
// --- step 3: strict rehydrate (read-only — abort the chain on failure) ---
|
|
96
|
+
// deriveRehydrateData internally calls gh.issueView (slot 2, body-bearing)
|
|
97
|
+
// then sequentially per linked item. It throws HandoffError on the
|
|
98
|
+
// wholesale issueView failure path; we add the strict linkFailures>0 gate.
|
|
99
|
+
const rehydrate = await deriveRehydrateData(issue, gh);
|
|
100
|
+
if (rehydrate.linkFailures > 0) {
|
|
101
|
+
throw new HandoffError(`rehydrate failed for #${issue} — leaving on the stack; no mutation.`);
|
|
102
|
+
}
|
|
103
|
+
// --- step 4: pre-assign drift check --------------------------------------
|
|
104
|
+
// issueViewSlim — separate seam from the body-bearing issueView slots
|
|
105
|
+
// above. Slot 1 of the SLIM counter.
|
|
106
|
+
let driftView;
|
|
107
|
+
try {
|
|
108
|
+
driftView = await gh.issueViewSlim(issue);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
throw new HandoffError(`could not re-fetch issue #${issue} for drift check — not mutating.`);
|
|
112
|
+
}
|
|
113
|
+
if (driftView.state !== "OPEN") {
|
|
114
|
+
throw new HandoffError(`issue #${issue} changed between validation and assign — state is now ${driftView.state}. Another receiver may have claimed it; re-run \`/accept\` to retry against the new state, or check \`/handoffs\` for the current stack.`);
|
|
115
|
+
}
|
|
116
|
+
const driftStatus = assigneesStatus(driftView.assignees, me);
|
|
117
|
+
if (driftStatus === "other") {
|
|
118
|
+
throw new HandoffError(`issue #${issue} changed between validation and assign — another receiver may have claimed it; re-run \`/accept\` to retry against the new state, or check \`/handoffs\` for the current stack.`);
|
|
119
|
+
}
|
|
120
|
+
// updatedAt drift handling. The close-failure retry path is the ONLY one
|
|
121
|
+
// allowed to pass with updatedAt drift, and it requires INITIAL was
|
|
122
|
+
// already @me (assign happened on a previous attempt, close failed). If
|
|
123
|
+
// INITIAL was empty and updatedAt drifted to @me, that's a concurrent
|
|
124
|
+
// assignment by another tool/automation/same-account session — abort, do
|
|
125
|
+
// not proceed on the stale validate snapshot.
|
|
126
|
+
if (driftView.updatedAt !== initialUpdatedAt) {
|
|
127
|
+
if (initialStatus === "me" && driftStatus === "me") {
|
|
128
|
+
// close-failure retry path — pass through (do nothing)
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
throw new HandoffError(`issue #${issue} was edited between validation and assign (initial=${initialStatus}, drift=${driftStatus}) — re-run \`/accept\` to retry against the new state.`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// --- step 5: assign @me (mutation 1) -------------------------------------
|
|
135
|
+
try {
|
|
136
|
+
await gh.issueAssignMe(issue);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
throw new HandoffError(`couldn't assign @me on #${issue} (gh error) — leaving on the stack.`);
|
|
140
|
+
}
|
|
141
|
+
logs.push(`accepted #${issue} — assigned to you.`);
|
|
142
|
+
// --- step 6: post-assign verify (multi-assignee abort path) --------------
|
|
143
|
+
// Slot 2 of the SLIM counter.
|
|
144
|
+
let postView;
|
|
145
|
+
try {
|
|
146
|
+
postView = await gh.issueViewSlim(issue);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
throw new HandoffError(`couldn't verify post-assign state on #${issue} — leaving open + assigned; don't close.`);
|
|
150
|
+
}
|
|
151
|
+
if (assigneesStatus(postView.assignees, me) !== "me") {
|
|
152
|
+
const others = assigneesOtherCsv(postView.assignees, me);
|
|
153
|
+
throw new HandoffError(`collision detected after assign — issue #${issue} now has assignees [${others}]. Do not close. Coordinate with the other receiver(s); the operator may \`gh issue edit ${issue} --remove-assignee <other>\` or hand the baton off explicitly.`);
|
|
154
|
+
}
|
|
155
|
+
// --- step 7: close (Commitment 10 — handoff event complete) --------------
|
|
156
|
+
try {
|
|
157
|
+
await gh.issueClose(issue);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
throw new HandoffError(`assigned but not closed — re-run \`/accept ${issue}\` to complete the close, or close manually as an operator-acknowledged exception.`);
|
|
161
|
+
}
|
|
162
|
+
logs.push(`closed #${issue} — handoff event complete.`);
|
|
163
|
+
return { issueNumber: issue, rehydrate, logs };
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=accept-verb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accept-verb.js","sourceRoot":"","sources":["../../src/handoff/accept-verb.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate a positive-integer issue number string → number. Empty/undefined
|
|
3
|
+
* returns undefined (the "no issue arg" path several verbs allow).
|
|
4
|
+
*
|
|
5
|
+
* Mirrors bash require_issue_number (lib.sh:46-53):
|
|
6
|
+
* case "${1:-}" in
|
|
7
|
+
* "") return 0 ;; # empty allowed
|
|
8
|
+
* 0|0*) die "issue must be a positive integer (got: '$1')." ;;
|
|
9
|
+
* *[!0-9]*) die "issue must be a positive integer (got: '$1')." ;;
|
|
10
|
+
* *) return 0 ;;
|
|
11
|
+
* esac
|
|
12
|
+
*
|
|
13
|
+
* Note: the bash impl also rejects leading-whitespace ("42 " or " 42") because
|
|
14
|
+
* `case` does literal matching. We match that by rejecting any string that is
|
|
15
|
+
* not solely digits.
|
|
16
|
+
*/
|
|
17
|
+
export declare function requireIssueNumber(input: string | undefined): number | undefined;
|
|
18
|
+
export declare function requireSafeArgs(args: readonly string[]): void;
|
|
19
|
+
//# sourceMappingURL=args.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/handoff/args.ts"],"names":[],"mappings":"AAiBA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAWhF;AAkBD,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAS7D"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// packages/cli/src/handoff/args.ts
|
|
2
|
+
//
|
|
3
|
+
// Argument validators. PORT FROM dark-factory-platform lib.sh@a6f711b:
|
|
4
|
+
// require_issue_number (lines 46-53)
|
|
5
|
+
// require_safe_args (lines 61-75)
|
|
6
|
+
//
|
|
7
|
+
// requireSafeArgs is defense-in-depth: in the TS CLI, real argv kills the
|
|
8
|
+
// shell-metacharacter injection vector that the bash .md/$ARGUMENTS surface
|
|
9
|
+
// was defending against (the operator's payload reaches process.argv as a
|
|
10
|
+
// string and cannot re-enter a shell). We keep the allow-list anyway because:
|
|
11
|
+
// (a) MCP tool callers might pass any string, and rejecting metacharacters
|
|
12
|
+
// early is cheap defense-in-depth, and
|
|
13
|
+
// (b) it preserves byte-equivalent error messages with the bash impl for
|
|
14
|
+
// the ~6 ported payload-rejection tests, simplifying the case-map.
|
|
15
|
+
import { HandoffError } from "./ports.js";
|
|
16
|
+
/**
|
|
17
|
+
* Validate a positive-integer issue number string → number. Empty/undefined
|
|
18
|
+
* returns undefined (the "no issue arg" path several verbs allow).
|
|
19
|
+
*
|
|
20
|
+
* Mirrors bash require_issue_number (lib.sh:46-53):
|
|
21
|
+
* case "${1:-}" in
|
|
22
|
+
* "") return 0 ;; # empty allowed
|
|
23
|
+
* 0|0*) die "issue must be a positive integer (got: '$1')." ;;
|
|
24
|
+
* *[!0-9]*) die "issue must be a positive integer (got: '$1')." ;;
|
|
25
|
+
* *) return 0 ;;
|
|
26
|
+
* esac
|
|
27
|
+
*
|
|
28
|
+
* Note: the bash impl also rejects leading-whitespace ("42 " or " 42") because
|
|
29
|
+
* `case` does literal matching. We match that by rejecting any string that is
|
|
30
|
+
* not solely digits.
|
|
31
|
+
*/
|
|
32
|
+
export function requireIssueNumber(input) {
|
|
33
|
+
if (input === undefined || input === "")
|
|
34
|
+
return undefined;
|
|
35
|
+
// Reject 0 (case: "0") and leading-zero (case: "0*").
|
|
36
|
+
if (input === "0" || /^0/.test(input)) {
|
|
37
|
+
throw new HandoffError(`issue must be a positive integer (got: '${input}').`);
|
|
38
|
+
}
|
|
39
|
+
// Reject anything other than bare ASCII digits.
|
|
40
|
+
if (!/^[0-9]+$/.test(input)) {
|
|
41
|
+
throw new HandoffError(`issue must be a positive integer (got: '${input}').`);
|
|
42
|
+
}
|
|
43
|
+
return Number(input);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Allow-list per bash require_safe_args (lib.sh:61-75). Excludes shell
|
|
47
|
+
* metacharacters (`;$()'`|&><!*[]`) while permitting URL-safe chars (?=%+~)
|
|
48
|
+
* for --link URLs like https://github.com/o/r/pull/N?tab=files.
|
|
49
|
+
*
|
|
50
|
+
* Per the bash pattern `*[!a-zA-Z0-9_/#:.,@?=%+~\ -]*`, the allowed set is:
|
|
51
|
+
* alphanumeric (a-zA-Z0-9)
|
|
52
|
+
* _ / # : . , @ ? = % + ~
|
|
53
|
+
* space
|
|
54
|
+
* - (placed at end to avoid range interpretation)
|
|
55
|
+
*
|
|
56
|
+
* Throws HandoffError with the same message wording as bash on a disallowed
|
|
57
|
+
* character.
|
|
58
|
+
*/
|
|
59
|
+
const SAFE_ARG_PATTERN = /^[a-zA-Z0-9_/#:.,@?=%+~ \-]*$/;
|
|
60
|
+
export function requireSafeArgs(args) {
|
|
61
|
+
for (const arg of args) {
|
|
62
|
+
if (!SAFE_ARG_PATTERN.test(arg)) {
|
|
63
|
+
throw new HandoffError("argument contains disallowed characters: refusing for safety " +
|
|
64
|
+
"(allowed: alphanumeric / # : . , @ - _ ? = % + ~ space).");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=args.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/handoff/args.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,EAAE;AACF,uEAAuE;AACvE,wCAAwC;AACxC,wCAAwC;AACxC,EAAE;AACF,0EAA0E;AAC1E,4EAA4E;AAC5E,0EAA0E;AAC1E,8EAA8E;AAC9E,6EAA6E;AAC7E,6CAA6C;AAC7C,2EAA2E;AAC3E,yEAAyE;AAEzE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAyB;IAC1D,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAC1D,sDAAsD;IACtD,IAAI,KAAK,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,YAAY,CAAC,2CAA2C,KAAK,KAAK,CAAC,CAAC;IAChF,CAAC;IACD,gDAAgD;IAChD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,YAAY,CAAC,2CAA2C,KAAK,KAAK,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,gBAAgB,GAAG,+BAA+B,CAAC;AAEzD,MAAM,UAAU,eAAe,CAAC,IAAuB;IACrD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,YAAY,CACpB,+DAA+D;gBAC7D,0DAA0D,CAC7D,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { GhClient } from "./ports.js";
|
|
2
|
+
/**
|
|
3
|
+
* Classification of an issue's assignees set against @me:
|
|
4
|
+
* - "empty" → no assignees (available on the stack)
|
|
5
|
+
* - "me" → exactly [@me] (same-actor update / close-failure retry)
|
|
6
|
+
* - "other" → any non-empty set ≠ [@me] (refuse/abort per §4.1, §4.3 step 4)
|
|
7
|
+
*
|
|
8
|
+
* Note: "[@me, @me]" (a duplicated @me — defensive against GitHub returning
|
|
9
|
+
* duplicates) classifies as "other" because cardinality must be 1 for "me".
|
|
10
|
+
*/
|
|
11
|
+
export type ClaimStatus = "empty" | "me" | "other";
|
|
12
|
+
/**
|
|
13
|
+
* Classify an assignees array. PORT FROM bash assignees_status (lib.sh:88-96):
|
|
14
|
+
* count == 0 → "empty"
|
|
15
|
+
* count == 1 && [@me] → "me"
|
|
16
|
+
* otherwise → "other"
|
|
17
|
+
*/
|
|
18
|
+
export declare function assigneesStatus(assignees: ReadonlyArray<{
|
|
19
|
+
login: string;
|
|
20
|
+
}>, meLogin: string): ClaimStatus;
|
|
21
|
+
/**
|
|
22
|
+
* Comma-joined list of assignees with @me filtered out. Used by refuse-
|
|
23
|
+
* messages ("currently assigned to @<csv>"). Preserves input order.
|
|
24
|
+
* PORT FROM bash assignees_other_csv (lib.sh:99-103).
|
|
25
|
+
*/
|
|
26
|
+
export declare function assigneesOtherCsv(assignees: ReadonlyArray<{
|
|
27
|
+
login: string;
|
|
28
|
+
}>, meLogin: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Cache for the @me login lookup. PORT FROM bash me_login + ME_LOGIN_CACHE
|
|
31
|
+
* (lib.sh:34-41). One API hit per process; cached across repeated callers.
|
|
32
|
+
*/
|
|
33
|
+
export declare class MeLoginCache {
|
|
34
|
+
private cache;
|
|
35
|
+
resolve(gh: GhClient): Promise<string>;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=assignees.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assignees.d.ts","sourceRoot":"","sources":["../../src/handoff/assignees.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,aAAa,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAC3C,OAAO,EAAE,MAAM,GACd,WAAW,CAOb;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,aAAa,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAC3C,OAAO,EAAE,MAAM,GACd,MAAM,CAKR;AAED;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAqB;IAC5B,OAAO,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;CAM7C"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// packages/cli/src/handoff/assignees.ts
|
|
2
|
+
//
|
|
3
|
+
// Assignees status + me-login cache. PORT FROM dark-factory-platform
|
|
4
|
+
// .claude/skills/handoff/scripts/lib.sh@a6f711b lines 32-41 (me_login + cache),
|
|
5
|
+
// 88-103 (assignees_status + assignees_other_csv).
|
|
6
|
+
/**
|
|
7
|
+
* Classify an assignees array. PORT FROM bash assignees_status (lib.sh:88-96):
|
|
8
|
+
* count == 0 → "empty"
|
|
9
|
+
* count == 1 && [@me] → "me"
|
|
10
|
+
* otherwise → "other"
|
|
11
|
+
*/
|
|
12
|
+
export function assigneesStatus(assignees, meLogin) {
|
|
13
|
+
if (assignees.length === 0)
|
|
14
|
+
return "empty";
|
|
15
|
+
// Length-checked, but TS with noUncheckedIndexedAccess needs the explicit
|
|
16
|
+
// binding to narrow `assignees[0]` from `T | undefined` to `T`.
|
|
17
|
+
const first = assignees[0];
|
|
18
|
+
if (assignees.length === 1 && first !== undefined && first.login === meLogin)
|
|
19
|
+
return "me";
|
|
20
|
+
return "other";
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Comma-joined list of assignees with @me filtered out. Used by refuse-
|
|
24
|
+
* messages ("currently assigned to @<csv>"). Preserves input order.
|
|
25
|
+
* PORT FROM bash assignees_other_csv (lib.sh:99-103).
|
|
26
|
+
*/
|
|
27
|
+
export function assigneesOtherCsv(assignees, meLogin) {
|
|
28
|
+
return assignees
|
|
29
|
+
.map((a) => a.login)
|
|
30
|
+
.filter((l) => l !== meLogin)
|
|
31
|
+
.join(",");
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Cache for the @me login lookup. PORT FROM bash me_login + ME_LOGIN_CACHE
|
|
35
|
+
* (lib.sh:34-41). One API hit per process; cached across repeated callers.
|
|
36
|
+
*/
|
|
37
|
+
export class MeLoginCache {
|
|
38
|
+
cache;
|
|
39
|
+
async resolve(gh) {
|
|
40
|
+
if (this.cache === undefined) {
|
|
41
|
+
this.cache = await gh.apiUserLogin();
|
|
42
|
+
}
|
|
43
|
+
return this.cache;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=assignees.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assignees.js","sourceRoot":"","sources":["../../src/handoff/assignees.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,EAAE;AACF,qEAAqE;AACrE,gFAAgF;AAChF,mDAAmD;AAenD;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,SAA2C,EAC3C,OAAe;IAEf,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IAC3C,0EAA0E;IAC1E,gEAAgE;IAChE,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1F,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAA2C,EAC3C,OAAe;IAEf,OAAO,SAAS;SACb,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;SACnB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC;SAC5B,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,YAAY;IACf,KAAK,CAAqB;IAClC,KAAK,CAAC,OAAO,CAAC,EAAY;QACxB,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC7B,IAAI,CAAC,KAAK,GAAG,MAAM,EAAE,CAAC,YAAY,EAAE,CAAC;QACvC,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;CACF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type Clock, type GhClient, type GitClient } from "./ports.js";
|
|
2
|
+
export interface RunHandoffOptions {
|
|
3
|
+
readonly noteStdin: string;
|
|
4
|
+
readonly issue?: number;
|
|
5
|
+
readonly link?: readonly string[];
|
|
6
|
+
readonly unlink?: readonly string[];
|
|
7
|
+
readonly forceNew?: boolean;
|
|
8
|
+
readonly gh: GhClient;
|
|
9
|
+
readonly git: GitClient;
|
|
10
|
+
readonly clock: Clock;
|
|
11
|
+
}
|
|
12
|
+
export interface RunHandoffResult {
|
|
13
|
+
readonly issueNumber: number;
|
|
14
|
+
readonly noteUrl: string;
|
|
15
|
+
readonly created: boolean;
|
|
16
|
+
/** Operator-facing warn/log lines (each line printed to stderr by the CLI). */
|
|
17
|
+
readonly logs: readonly string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Put a work-stream on the handoff stack. Upserts the operator's
|
|
21
|
+
* marker-bounded rehydration note as a dedicated handoff GitHub Issue body,
|
|
22
|
+
* maintains its Linked-items section + label + leaves it unassigned. NO
|
|
23
|
+
* `git push` (Decision D4).
|
|
24
|
+
*
|
|
25
|
+
* Phases mirror the bash:
|
|
26
|
+
* A: validate stdin — markers + secret-scrub, no gh
|
|
27
|
+
* B: dirty worktree — warn, not refuse
|
|
28
|
+
* C: resolve target — explicit issue OR no-arg eligibility partition
|
|
29
|
+
* D: linked-items — extract → --link (with title scrub) → --unlink → auto-link
|
|
30
|
+
* E: compose new note — strip operator's stale section, inject script section
|
|
31
|
+
* F: PATCH or CREATE — with pre-PATCH race-safety drift check
|
|
32
|
+
*/
|
|
33
|
+
export declare function runHandoff(opts: RunHandoffOptions): Promise<RunHandoffResult>;
|
|
34
|
+
//# sourceMappingURL=handoff-verb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoff-verb.d.ts","sourceRoot":"","sources":["../../src/handoff/handoff-verb.ts"],"names":[],"mappings":"AAqDA,OAAO,EAAgB,KAAK,KAAK,EAAE,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AASrF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAyGD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAsW3B"}
|