nightralph 0.0.53 → 0.0.56
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/README.md +181 -19
- package/dist/blockers.js +149 -0
- package/dist/blockers.js.map +7 -0
- package/dist/display.js +12 -4
- package/dist/display.js.map +2 -2
- package/dist/docs-templates/issue-tracker.md +9 -1
- package/dist/index.js +2735 -415
- package/dist/index.js.map +4 -4
- package/dist/learnings.js +360 -0
- package/dist/learnings.js.map +7 -0
- package/dist/meta.json +289 -16
- package/dist/orchestrator.js +753 -188
- package/dist/orchestrator.js.map +3 -3
- package/dist/preflight.js +34 -0
- package/dist/preflight.js.map +7 -0
- package/dist/progress.js +5 -4
- package/dist/progress.js.map +2 -2
- package/dist/resolve.js +98 -9
- package/dist/resolve.js.map +2 -2
- package/dist/retry.js +83 -0
- package/dist/retry.js.map +7 -0
- package/dist/review.js +669 -0
- package/dist/review.js.map +7 -0
- package/dist/rules.js +67 -0
- package/dist/rules.js.map +7 -0
- package/dist/skills/to-tickets/SKILL.md +5 -5
- package/dist/src/blockers.d.ts +45 -0
- package/dist/src/blockers.d.ts.map +1 -0
- package/dist/src/display.d.ts +9 -0
- package/dist/src/display.d.ts.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/learnings.d.ts +70 -0
- package/dist/src/learnings.d.ts.map +1 -0
- package/dist/src/orchestrator.d.ts +85 -28
- package/dist/src/orchestrator.d.ts.map +1 -1
- package/dist/src/preflight.d.ts +6 -0
- package/dist/src/preflight.d.ts.map +1 -0
- package/dist/src/progress.d.ts +1 -1
- package/dist/src/progress.d.ts.map +1 -1
- package/dist/src/resolve.d.ts +24 -0
- package/dist/src/resolve.d.ts.map +1 -1
- package/dist/src/retry.d.ts +26 -0
- package/dist/src/retry.d.ts.map +1 -0
- package/dist/src/review.d.ts +131 -0
- package/dist/src/review.d.ts.map +1 -0
- package/dist/src/rules.d.ts +11 -0
- package/dist/src/rules.d.ts.map +1 -0
- package/dist/src/stats.d.ts +43 -0
- package/dist/src/stats.d.ts.map +1 -0
- package/dist/src/testcmd.d.ts +1 -0
- package/dist/src/testcmd.d.ts.map +1 -1
- package/dist/src/worktree.d.ts +30 -1
- package/dist/src/worktree.d.ts.map +1 -1
- package/dist/stats.js +111 -0
- package/dist/stats.js.map +7 -0
- package/dist/testcmd.js +13 -0
- package/dist/testcmd.js.map +2 -2
- package/dist/worktree.js +236 -32
- package/dist/worktree.js.map +2 -2
- package/package.json +3 -2
package/dist/worktree.js
CHANGED
|
@@ -23,15 +23,138 @@ function getRepoName() {
|
|
|
23
23
|
return basename(dirname(resolve(commonDir)));
|
|
24
24
|
}
|
|
25
25
|
__name(getRepoName, "getRepoName");
|
|
26
|
-
function getCurrentBranch() {
|
|
26
|
+
function getCurrentBranch(cwd) {
|
|
27
27
|
const stdout = execFileSync(
|
|
28
28
|
"git",
|
|
29
29
|
["rev-parse", "--abbrev-ref", "HEAD"],
|
|
30
|
-
{ encoding: "utf8" }
|
|
30
|
+
{ cwd, encoding: "utf8" }
|
|
31
31
|
);
|
|
32
32
|
return stdout.trim();
|
|
33
33
|
}
|
|
34
34
|
__name(getCurrentBranch, "getCurrentBranch");
|
|
35
|
+
function localBranchExists(repoRoot, name) {
|
|
36
|
+
try {
|
|
37
|
+
execFileSync(
|
|
38
|
+
"git",
|
|
39
|
+
["show-ref", "--verify", "--quiet", `refs/heads/${name}`],
|
|
40
|
+
{ cwd: repoRoot, stdio: "ignore" }
|
|
41
|
+
);
|
|
42
|
+
return true;
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
__name(localBranchExists, "localBranchExists");
|
|
48
|
+
function getDefaultBranch(repoRoot) {
|
|
49
|
+
try {
|
|
50
|
+
const ref = execFileSync(
|
|
51
|
+
"git",
|
|
52
|
+
[
|
|
53
|
+
"symbolic-ref",
|
|
54
|
+
"--quiet",
|
|
55
|
+
"--short",
|
|
56
|
+
"refs/remotes/origin/HEAD"
|
|
57
|
+
],
|
|
58
|
+
{
|
|
59
|
+
cwd: repoRoot,
|
|
60
|
+
encoding: "utf8",
|
|
61
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
62
|
+
}
|
|
63
|
+
).trim();
|
|
64
|
+
if (ref) return ref.replace(/^origin\//, "");
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
for (const name of ["main", "master"]) {
|
|
68
|
+
if (localBranchExists(repoRoot, name)) return name;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
__name(getDefaultBranch, "getDefaultBranch");
|
|
73
|
+
function integrationBranchName(features) {
|
|
74
|
+
const distinct = [...new Set(features.filter(Boolean))];
|
|
75
|
+
return distinct.length === 1 ? `nightralph--${distinct[0]}` : "nightralph--all";
|
|
76
|
+
}
|
|
77
|
+
__name(integrationBranchName, "integrationBranchName");
|
|
78
|
+
function planIntegrationBranch(current, defaultBranch, features) {
|
|
79
|
+
if (!defaultBranch || current === "HEAD") return null;
|
|
80
|
+
if (current !== defaultBranch) return null;
|
|
81
|
+
return integrationBranchName(features);
|
|
82
|
+
}
|
|
83
|
+
__name(planIntegrationBranch, "planIntegrationBranch");
|
|
84
|
+
function gitText(cwd, args) {
|
|
85
|
+
return execFileSync("git", args, { cwd, encoding: "utf8" }).trim();
|
|
86
|
+
}
|
|
87
|
+
__name(gitText, "gitText");
|
|
88
|
+
function prepareIntegrationBranch(repoRoot, features) {
|
|
89
|
+
const current = getCurrentBranch(repoRoot);
|
|
90
|
+
const defaultBranch = getDefaultBranch(repoRoot);
|
|
91
|
+
const name = planIntegrationBranch(
|
|
92
|
+
current,
|
|
93
|
+
defaultBranch,
|
|
94
|
+
features
|
|
95
|
+
);
|
|
96
|
+
if (!name) {
|
|
97
|
+
const onIntegration = defaultBranch !== null && current === integrationBranchName(features);
|
|
98
|
+
return {
|
|
99
|
+
branch: current,
|
|
100
|
+
created: false,
|
|
101
|
+
resumed: false,
|
|
102
|
+
behind: 0,
|
|
103
|
+
fastForwarded: false,
|
|
104
|
+
launchBranch: current,
|
|
105
|
+
forkedFrom: onIntegration ? defaultBranch : null
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (isDirty(repoRoot)) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
"Working tree is dirty; commit or stash changes before running"
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
if (localBranchExists(repoRoot, name)) {
|
|
114
|
+
const contained = isAncestor(repoRoot, name, current);
|
|
115
|
+
gitText(repoRoot, ["checkout", "--quiet", name]);
|
|
116
|
+
let behind = Number(gitText(
|
|
117
|
+
repoRoot,
|
|
118
|
+
["rev-list", "--count", `${name}..${current}`]
|
|
119
|
+
));
|
|
120
|
+
const fastForwarded = contained && behind > 0;
|
|
121
|
+
if (fastForwarded) {
|
|
122
|
+
gitText(repoRoot, ["merge", "--ff-only", "--quiet", current]);
|
|
123
|
+
behind = 0;
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
branch: name,
|
|
127
|
+
created: false,
|
|
128
|
+
resumed: true,
|
|
129
|
+
behind,
|
|
130
|
+
fastForwarded,
|
|
131
|
+
launchBranch: current,
|
|
132
|
+
forkedFrom: defaultBranch
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
gitText(repoRoot, ["checkout", "--quiet", "-b", name]);
|
|
136
|
+
return {
|
|
137
|
+
branch: name,
|
|
138
|
+
created: true,
|
|
139
|
+
resumed: false,
|
|
140
|
+
behind: 0,
|
|
141
|
+
fastForwarded: false,
|
|
142
|
+
launchBranch: current,
|
|
143
|
+
forkedFrom: defaultBranch
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
__name(prepareIntegrationBranch, "prepareIntegrationBranch");
|
|
147
|
+
function describeIntegrationBranch(repoRoot, features) {
|
|
148
|
+
const current = getCurrentBranch(repoRoot);
|
|
149
|
+
const name = planIntegrationBranch(
|
|
150
|
+
current,
|
|
151
|
+
getDefaultBranch(repoRoot),
|
|
152
|
+
features
|
|
153
|
+
);
|
|
154
|
+
if (!name) return `Branch: merges into ${current}`;
|
|
155
|
+
return localBranchExists(repoRoot, name) ? `Branch: would resume ${name}` : `Branch: would create ${name} from ${current}`;
|
|
156
|
+
}
|
|
157
|
+
__name(describeIntegrationBranch, "describeIntegrationBranch");
|
|
35
158
|
async function branchExists(repoRoot, branchName) {
|
|
36
159
|
try {
|
|
37
160
|
await execFileAsync(
|
|
@@ -51,16 +174,20 @@ function buildBranchName(repoName, ticket) {
|
|
|
51
174
|
return `${repoName}--${prefix}${num}-${ticket.slug}`;
|
|
52
175
|
}
|
|
53
176
|
__name(buildBranchName, "buildBranchName");
|
|
54
|
-
async function
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
);
|
|
177
|
+
async function removeWorktreeNow(worktreePath, opts) {
|
|
178
|
+
const args = [
|
|
179
|
+
"-C",
|
|
180
|
+
worktreePath,
|
|
181
|
+
"worktree",
|
|
182
|
+
"remove"
|
|
183
|
+
];
|
|
184
|
+
if (opts?.force) args.push("--force");
|
|
185
|
+
args.push(worktreePath);
|
|
186
|
+
await execFileAsync("git", args);
|
|
187
|
+
}
|
|
188
|
+
__name(removeWorktreeNow, "removeWorktreeNow");
|
|
189
|
+
async function addWorktreeNow(opts) {
|
|
190
|
+
const { repoRoot, baseBranch, branchName, worktreePath } = opts;
|
|
64
191
|
if (existsSync(worktreePath)) {
|
|
65
192
|
try {
|
|
66
193
|
await execFileAsync(
|
|
@@ -100,36 +227,56 @@ async function createWorktreeNow(opts) {
|
|
|
100
227
|
);
|
|
101
228
|
return { worktreePath, branchName };
|
|
102
229
|
}
|
|
103
|
-
__name(
|
|
230
|
+
__name(addWorktreeNow, "addWorktreeNow");
|
|
104
231
|
let worktreeQueue = Promise.resolve();
|
|
105
|
-
function
|
|
106
|
-
const result = worktreeQueue.then(
|
|
232
|
+
function enqueue(fn) {
|
|
233
|
+
const result = worktreeQueue.then(fn);
|
|
107
234
|
worktreeQueue = result.catch(() => {
|
|
108
235
|
});
|
|
109
236
|
return result;
|
|
110
237
|
}
|
|
238
|
+
__name(enqueue, "enqueue");
|
|
239
|
+
function createWorktree(opts) {
|
|
240
|
+
const { repoRoot, repoName, ticket } = opts;
|
|
241
|
+
const num = String(ticket.num).padStart(2, "0");
|
|
242
|
+
const prefix = ticket.feature ? `${ticket.feature}-` : "";
|
|
243
|
+
return enqueue(() => addWorktreeNow({
|
|
244
|
+
repoRoot,
|
|
245
|
+
baseBranch: opts.baseBranch,
|
|
246
|
+
branchName: buildBranchName(repoName, ticket),
|
|
247
|
+
worktreePath: join(
|
|
248
|
+
repoRoot,
|
|
249
|
+
"..",
|
|
250
|
+
`${repoName}.${prefix}${num}-${ticket.slug}`
|
|
251
|
+
)
|
|
252
|
+
}));
|
|
253
|
+
}
|
|
111
254
|
__name(createWorktree, "createWorktree");
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
args.push(worktreePath);
|
|
121
|
-
await execFileAsync("git", args);
|
|
255
|
+
function createNamedWorktree(opts) {
|
|
256
|
+
const { repoRoot, repoName, name } = opts;
|
|
257
|
+
return enqueue(() => addWorktreeNow({
|
|
258
|
+
repoRoot,
|
|
259
|
+
baseBranch: opts.baseBranch,
|
|
260
|
+
branchName: `${repoName}--${name}`,
|
|
261
|
+
worktreePath: join(repoRoot, "..", `${repoName}.${name}`)
|
|
262
|
+
}));
|
|
122
263
|
}
|
|
123
|
-
__name(
|
|
264
|
+
__name(createNamedWorktree, "createNamedWorktree");
|
|
124
265
|
function removeWorktree(worktreePath, opts) {
|
|
125
|
-
|
|
126
|
-
() => removeWorktreeNow(worktreePath, opts)
|
|
127
|
-
);
|
|
128
|
-
worktreeQueue = result.catch(() => {
|
|
129
|
-
});
|
|
130
|
-
return result;
|
|
266
|
+
return enqueue(() => removeWorktreeNow(worktreePath, opts));
|
|
131
267
|
}
|
|
132
268
|
__name(removeWorktree, "removeWorktree");
|
|
269
|
+
function deleteBranch(repoRoot, name) {
|
|
270
|
+
return enqueue(async () => {
|
|
271
|
+
if (!await branchExists(repoRoot, name)) return;
|
|
272
|
+
await execFileAsync(
|
|
273
|
+
"git",
|
|
274
|
+
["branch", "-D", name],
|
|
275
|
+
{ cwd: repoRoot }
|
|
276
|
+
);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
__name(deleteBranch, "deleteBranch");
|
|
133
280
|
function hasNewCommits(repoRoot, base, branch) {
|
|
134
281
|
const stdout = execFileSync(
|
|
135
282
|
"git",
|
|
@@ -139,6 +286,51 @@ function hasNewCommits(repoRoot, base, branch) {
|
|
|
139
286
|
return Number(stdout.trim()) > 0;
|
|
140
287
|
}
|
|
141
288
|
__name(hasNewCommits, "hasNewCommits");
|
|
289
|
+
function headCommit(cwd) {
|
|
290
|
+
return gitText(cwd, ["rev-parse", "HEAD"]);
|
|
291
|
+
}
|
|
292
|
+
__name(headCommit, "headCommit");
|
|
293
|
+
function mergeBase(cwd, a, b) {
|
|
294
|
+
try {
|
|
295
|
+
return gitText(cwd, ["merge-base", a, b]) || null;
|
|
296
|
+
} catch {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
__name(mergeBase, "mergeBase");
|
|
301
|
+
function isAncestor(cwd, ancestor, of) {
|
|
302
|
+
try {
|
|
303
|
+
execFileSync(
|
|
304
|
+
"git",
|
|
305
|
+
["merge-base", "--is-ancestor", ancestor, of],
|
|
306
|
+
{ cwd, stdio: "ignore" }
|
|
307
|
+
);
|
|
308
|
+
return true;
|
|
309
|
+
} catch {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
__name(isAncestor, "isAncestor");
|
|
314
|
+
function hasChangesOutside(cwd, from, to, excludeDir) {
|
|
315
|
+
try {
|
|
316
|
+
execFileSync(
|
|
317
|
+
"git",
|
|
318
|
+
[
|
|
319
|
+
"diff",
|
|
320
|
+
"--quiet",
|
|
321
|
+
`${from}..${to}`,
|
|
322
|
+
"--",
|
|
323
|
+
".",
|
|
324
|
+
`:(exclude)${excludeDir}`
|
|
325
|
+
],
|
|
326
|
+
{ cwd, stdio: "ignore" }
|
|
327
|
+
);
|
|
328
|
+
return false;
|
|
329
|
+
} catch {
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
__name(hasChangesOutside, "hasChangesOutside");
|
|
142
334
|
function isDirty(cwd) {
|
|
143
335
|
const stdout = execFileSync(
|
|
144
336
|
"git",
|
|
@@ -183,13 +375,25 @@ __name(listWorktrees, "listWorktrees");
|
|
|
183
375
|
export {
|
|
184
376
|
buildBranchName,
|
|
185
377
|
commitDirty,
|
|
378
|
+
createNamedWorktree,
|
|
186
379
|
createWorktree,
|
|
380
|
+
deleteBranch,
|
|
381
|
+
describeIntegrationBranch,
|
|
187
382
|
getCurrentBranch,
|
|
383
|
+
getDefaultBranch,
|
|
188
384
|
getRepoName,
|
|
189
385
|
getRepoRoot,
|
|
386
|
+
hasChangesOutside,
|
|
190
387
|
hasNewCommits,
|
|
388
|
+
headCommit,
|
|
389
|
+
integrationBranchName,
|
|
390
|
+
isAncestor,
|
|
191
391
|
isDirty,
|
|
192
392
|
listWorktrees,
|
|
393
|
+
localBranchExists,
|
|
394
|
+
mergeBase,
|
|
395
|
+
planIntegrationBranch,
|
|
396
|
+
prepareIntegrationBranch,
|
|
193
397
|
removeWorktree
|
|
194
398
|
};
|
|
195
399
|
//# sourceMappingURL=worktree.js.map
|
package/dist/worktree.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/worktree.ts"],
|
|
4
|
-
"sourcesContent": ["import { execFile, execFileSync } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport { basename, dirname, join, resolve } from 'node:path'\nimport { existsSync } from 'node:fs'\nimport type { Ticket } from './orchestrator.js'\n\nconst execFileAsync = promisify(execFile)\n\nexport function getRepoRoot ():string {\n const stdout = execFileSync(\n 'git',\n ['rev-parse', '--show-toplevel'],\n { encoding:'utf8' }\n )\n return stdout.trim()\n}\n\nexport function getRepoName ():string {\n const commonDir = execFileSync(\n 'git',\n ['rev-parse', '--git-common-dir'],\n { encoding:'utf8' }\n ).trim()\n return basename(dirname(resolve(commonDir)))\n}\n\nexport function getCurrentBranch ():string {\n const stdout = execFileSync(\n 'git',\n ['rev-parse', '--abbrev-ref', 'HEAD'],\n { encoding:'utf8' }\n )\n return stdout.trim()\n}\n\nasync function branchExists (\n repoRoot:string,\n branchName:string\n):Promise<boolean> {\n try {\n await execFileAsync(\n 'git',\n ['show-ref', '--verify', '--quiet', `refs/heads/${branchName}`],\n { cwd:repoRoot }\n )\n return true\n } catch {\n return false\n }\n}\n\nexport function buildBranchName (\n repoName:string,\n ticket:Ticket\n):string {\n const num = String(ticket.num).padStart(2, '0')\n const prefix = ticket.feature ?\n `${ticket.feature}-` :\n ''\n return `${repoName}--${prefix}${num}-${ticket.slug}`\n}\n\nasync function createWorktreeNow (\n opts:{\n repoRoot:string\n repoName:string\n baseBranch:string\n ticket:Ticket\n }\n):Promise<{ worktreePath:string; branchName:string }> {\n const { repoRoot, repoName, baseBranch, ticket } = opts\n const branchName = buildBranchName(repoName, ticket)\n const num = String(ticket.num).padStart(2, '0')\n const prefix = ticket.feature ?\n `${ticket.feature}-` :\n ''\n const worktreePath = join(\n repoRoot,\n '..',\n `${repoName}.${prefix}${num}-${ticket.slug}`\n )\n\n if (existsSync(worktreePath)) {\n try {\n await execFileAsync(\n 'git',\n ['worktree', 'remove', '--force', worktreePath],\n { cwd:repoRoot }\n )\n } catch {\n // directory may not be a registered worktree anymore;\n // prune stale entries below and let worktree add fail\n // loudly if the path is still unusable\n }\n }\n\n try {\n await execFileAsync(\n 'git',\n ['worktree', 'prune'],\n { cwd:repoRoot }\n )\n } catch {\n // best-effort cleanup of stale worktree metadata\n }\n\n if (await branchExists(repoRoot, branchName)) {\n await execFileAsync(\n 'git',\n ['branch', '-D', branchName],\n { cwd:repoRoot }\n )\n }\n\n await execFileAsync(\n 'git',\n [\n 'worktree',\n 'add',\n '-b',\n branchName,\n worktreePath,\n baseBranch,\n ],\n { cwd:repoRoot }\n )\n\n return { worktreePath, branchName }\n}\n\nlet worktreeQueue:Promise<unknown> = Promise.resolve()\n\nexport function createWorktree (\n opts:{\n repoRoot:string\n repoName:string\n baseBranch:string\n ticket:Ticket\n }\n):Promise<{ worktreePath:string; branchName:string }> {\n const result = worktreeQueue.then(() => createWorktreeNow(opts))\n worktreeQueue = result.catch(() => {})\n return result\n}\n\nasync function removeWorktreeNow (\n worktreePath:string,\n opts?:{ force?:boolean }\n):Promise<void> {\n const args = [\n '-C', worktreePath,\n 'worktree', 'remove',\n ]\n if (opts?.force) args.push('--force')\n args.push(worktreePath)\n await execFileAsync('git', args)\n}\n\nexport function removeWorktree (\n worktreePath:string,\n opts?:{ force?:boolean }\n):Promise<void> {\n const result = worktreeQueue.then(\n () => removeWorktreeNow(worktreePath, opts)\n )\n worktreeQueue = result.catch(() => {})\n return result\n}\n\nexport function hasNewCommits (\n repoRoot:string,\n base:string,\n branch:string\n):boolean {\n const stdout = execFileSync(\n 'git',\n ['rev-list', '--count', `${base}..${branch}`],\n { cwd:repoRoot, encoding:'utf8' }\n )\n return Number(stdout.trim()) > 0\n}\n\nexport function isDirty (cwd:string):boolean {\n const stdout = execFileSync(\n 'git',\n ['status', '--porcelain'],\n { cwd, encoding:'utf8' }\n )\n return stdout.trim().length > 0\n}\n\nexport async function commitDirty (\n cwd:string,\n message?:string\n):Promise<boolean> {\n if (!isDirty(cwd)) return false\n await execFileAsync(\n 'git',\n ['add', '-A'],\n { cwd }\n )\n try {\n await execFileAsync(\n 'git',\n [\n 'commit', '-m',\n message ??\n 'nightralph: auto-commit ' +\n 'remaining changes',\n ],\n { cwd }\n )\n return true\n } catch {\n return false\n }\n}\n\nexport async function listWorktrees (\n repoRoot:string\n):Promise<Array<string>> {\n const { stdout } = await execFileAsync(\n 'git',\n ['worktree', 'list', '--porcelain'],\n { cwd:repoRoot }\n )\n return stdout\n .trim()\n .split('\\n')\n .filter(line => line.startsWith('worktree '))\n .map(line => line.substring('worktree '.length))\n}\n"],
|
|
5
|
-
"mappings": ";;AAAA,SAAS,UAAU,oBAAoB;AACvC,SAAS,iBAAiB;AAC1B,SAAS,UAAU,SAAS,MAAM,eAAe;AACjD,SAAS,kBAAkB;AAG3B,MAAM,gBAAgB,UAAU,QAAQ;AAEjC,SAAS,cAAsB;AAClC,QAAM,SAAS;AAAA,IACX;AAAA,IACA,CAAC,aAAa,iBAAiB;AAAA,IAC/B,EAAE,UAAS,OAAO;AAAA,EACtB;AACA,SAAO,OAAO,KAAK;AACvB;AAPgB;AAST,SAAS,cAAsB;AAClC,QAAM,YAAY;AAAA,IACd;AAAA,IACA,CAAC,aAAa,kBAAkB;AAAA,IAChC,EAAE,UAAS,OAAO;AAAA,EACtB,EAAE,KAAK;AACP,SAAO,SAAS,QAAQ,QAAQ,SAAS,CAAC,CAAC;AAC/C;AAPgB;AAST,SAAS,
|
|
4
|
+
"sourcesContent": ["import { execFile, execFileSync } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport { basename, dirname, join, resolve } from 'node:path'\nimport { existsSync } from 'node:fs'\nimport type { Ticket } from './orchestrator.js'\n\nconst execFileAsync = promisify(execFile)\n\nexport function getRepoRoot ():string {\n const stdout = execFileSync(\n 'git',\n ['rev-parse', '--show-toplevel'],\n { encoding:'utf8' }\n )\n return stdout.trim()\n}\n\nexport function getRepoName ():string {\n const commonDir = execFileSync(\n 'git',\n ['rev-parse', '--git-common-dir'],\n { encoding:'utf8' }\n ).trim()\n return basename(dirname(resolve(commonDir)))\n}\n\nexport function getCurrentBranch (cwd?:string):string {\n const stdout = execFileSync(\n 'git',\n ['rev-parse', '--abbrev-ref', 'HEAD'],\n { cwd, encoding:'utf8' }\n )\n return stdout.trim()\n}\n\nexport function localBranchExists (\n repoRoot:string,\n name:string\n):boolean {\n try {\n execFileSync(\n 'git',\n ['show-ref', '--verify', '--quiet', `refs/heads/${name}`],\n { cwd:repoRoot, stdio:'ignore' }\n )\n return true\n } catch {\n return false\n }\n}\n\n// The repo's default branch: what origin/HEAD points at, else a\n// local main or master, else null.\nexport function getDefaultBranch (repoRoot:string):string|null {\n try {\n const ref = execFileSync(\n 'git',\n [\n 'symbolic-ref', '--quiet', '--short',\n 'refs/remotes/origin/HEAD',\n ],\n {\n cwd:repoRoot,\n encoding:'utf8',\n stdio:['ignore', 'pipe', 'ignore'],\n }\n ).trim()\n if (ref) return ref.replace(/^origin\\//, '')\n } catch {\n // no origin/HEAD\n }\n for (const name of ['main', 'master']) {\n if (localBranchExists(repoRoot, name)) return name\n }\n return null\n}\n\n// Uses `--` rather than `/` for the same reason as\n// buildBranchName: refs/heads/X/Y cannot coexist with\n// refs/heads/X.\nexport function integrationBranchName (\n features:ReadonlyArray<string>\n):string {\n const distinct = [...new Set(features.filter(Boolean))]\n return distinct.length === 1 ?\n `nightralph--${distinct[0]}` :\n 'nightralph--all'\n}\n\n// The integration branch to use, or null to merge into the\n// current branch as before. Only a run launched from the default\n// branch gets one.\nexport function planIntegrationBranch (\n current:string,\n defaultBranch:string|null,\n features:ReadonlyArray<string>\n):string|null {\n if (!defaultBranch || current === 'HEAD') return null\n if (current !== defaultBranch) return null\n return integrationBranchName(features)\n}\n\nexport type IntegrationPlan = {\n // where this run's work lands\n branch:string\n created:boolean\n resumed:boolean\n // commits on the launch branch that `branch` lacks\n behind:number\n // true when a resumed branch that was already contained in the\n // launch branch was moved up to it\n fastForwarded:boolean\n launchBranch:string\n // the default branch when `branch` is an integration branch,\n // else null; the review falls back to its merge-base\n forkedFrom:string|null\n}\n\nfunction gitText (cwd:string, args:string[]):string {\n return execFileSync('git', args, { cwd, encoding:'utf8' }).trim()\n}\n\n// Enters the integration branch when the run was launched from\n// the default branch, creating it or resuming it. Callers must\n// scan tickets after this, since ticket statuses live on the\n// branch it checks out.\nexport function prepareIntegrationBranch (\n repoRoot:string,\n features:ReadonlyArray<string>\n):IntegrationPlan {\n const current = getCurrentBranch(repoRoot)\n const defaultBranch = getDefaultBranch(repoRoot)\n const name = planIntegrationBranch(\n current, defaultBranch, features\n )\n if (!name) {\n const onIntegration = defaultBranch !== null &&\n current === integrationBranchName(features)\n return {\n branch:current,\n created:false,\n resumed:false,\n behind:0,\n fastForwarded:false,\n launchBranch:current,\n forkedFrom:onIntegration ? defaultBranch : null,\n }\n }\n if (isDirty(repoRoot)) {\n throw new Error(\n 'Working tree is dirty; commit or stash ' +\n 'changes before running'\n )\n }\n if (localBranchExists(repoRoot, name)) {\n // Merged into the launch branch already: nothing of its own\n // to lose, so bring it up to date\n const contained = isAncestor(repoRoot, name, current)\n gitText(repoRoot, ['checkout', '--quiet', name])\n let behind = Number(gitText(\n repoRoot, ['rev-list', '--count', `${name}..${current}`]\n ))\n const fastForwarded = contained && behind > 0\n if (fastForwarded) {\n gitText(repoRoot, ['merge', '--ff-only', '--quiet', current])\n behind = 0\n }\n return {\n branch:name,\n created:false,\n resumed:true,\n behind,\n fastForwarded,\n launchBranch:current,\n forkedFrom:defaultBranch,\n }\n }\n gitText(repoRoot, ['checkout', '--quiet', '-b', name])\n return {\n branch:name,\n created:true,\n resumed:false,\n behind:0,\n fastForwarded:false,\n launchBranch:current,\n forkedFrom:defaultBranch,\n }\n}\n\n// What prepareIntegrationBranch would do, for --dry-run.\nexport function describeIntegrationBranch (\n repoRoot:string,\n features:ReadonlyArray<string>\n):string {\n const current = getCurrentBranch(repoRoot)\n const name = planIntegrationBranch(\n current, getDefaultBranch(repoRoot), features\n )\n if (!name) return `Branch: merges into ${current}`\n return localBranchExists(repoRoot, name) ?\n `Branch: would resume ${name}` :\n `Branch: would create ${name} from ${current}`\n}\n\nasync function branchExists (\n repoRoot:string,\n branchName:string\n):Promise<boolean> {\n try {\n await execFileAsync(\n 'git',\n ['show-ref', '--verify', '--quiet', `refs/heads/${branchName}`],\n { cwd:repoRoot }\n )\n return true\n } catch {\n return false\n }\n}\n\nexport function buildBranchName (\n repoName:string,\n ticket:Ticket\n):string {\n const num = String(ticket.num).padStart(2, '0')\n const prefix = ticket.feature ?\n `${ticket.feature}-` :\n ''\n return `${repoName}--${prefix}${num}-${ticket.slug}`\n}\n\nasync function removeWorktreeNow (\n worktreePath:string,\n opts?:{ force?:boolean }\n):Promise<void> {\n const args = [\n '-C', worktreePath,\n 'worktree', 'remove',\n ]\n if (opts?.force) args.push('--force')\n args.push(worktreePath)\n await execFileAsync('git', args)\n}\n\n// Creates a worktree on a fresh branch, first clearing any stale\n// worktree at the path and any leftover branch of the same name,\n// so re-running after a failed run is safe.\nasync function addWorktreeNow (opts:{\n repoRoot:string\n baseBranch:string\n branchName:string\n worktreePath:string\n}):Promise<{ worktreePath:string; branchName:string }> {\n const { repoRoot, baseBranch, branchName, worktreePath } = opts\n\n if (existsSync(worktreePath)) {\n try {\n await execFileAsync(\n 'git',\n ['worktree', 'remove', '--force', worktreePath],\n { cwd:repoRoot }\n )\n } catch {\n // directory may not be a registered worktree anymore;\n // prune stale entries below and let worktree add fail\n // loudly if the path is still unusable\n }\n }\n\n try {\n await execFileAsync(\n 'git',\n ['worktree', 'prune'],\n { cwd:repoRoot }\n )\n } catch {\n // best-effort cleanup of stale worktree metadata\n }\n\n if (await branchExists(repoRoot, branchName)) {\n await execFileAsync(\n 'git',\n ['branch', '-D', branchName],\n { cwd:repoRoot }\n )\n }\n\n await execFileAsync(\n 'git',\n [\n 'worktree',\n 'add',\n '-b',\n branchName,\n worktreePath,\n baseBranch,\n ],\n { cwd:repoRoot }\n )\n\n return { worktreePath, branchName }\n}\n\nlet worktreeQueue:Promise<unknown> = Promise.resolve()\n\n// Serializes every worktree and branch mutation module-wide, so\n// concurrent calls cannot race on shared git refs or on\n// .git/worktrees metadata.\nfunction enqueue<T> (fn:() => Promise<T>):Promise<T> {\n const result = worktreeQueue.then(fn)\n worktreeQueue = result.catch(() => {})\n return result\n}\n\nexport function createWorktree (\n opts:{\n repoRoot:string\n repoName:string\n baseBranch:string\n ticket:Ticket\n }\n):Promise<{ worktreePath:string; branchName:string }> {\n const { repoRoot, repoName, ticket } = opts\n const num = String(ticket.num).padStart(2, '0')\n const prefix = ticket.feature ?\n `${ticket.feature}-` :\n ''\n return enqueue(() => addWorktreeNow({\n repoRoot,\n baseBranch:opts.baseBranch,\n branchName:buildBranchName(repoName, ticket),\n worktreePath:join(\n repoRoot,\n '..',\n `${repoName}.${prefix}${num}-${ticket.slug}`\n ),\n }))\n}\n\n// A worktree that belongs to no ticket, e.g. the review pass:\n// `<repo>.<name>` on branch `<repo>--<name>`. Ticket branches\n// always carry a number, so the names cannot collide.\nexport function createNamedWorktree (\n opts:{\n repoRoot:string\n repoName:string\n baseBranch:string\n name:string\n }\n):Promise<{ worktreePath:string; branchName:string }> {\n const { repoRoot, repoName, name } = opts\n return enqueue(() => addWorktreeNow({\n repoRoot,\n baseBranch:opts.baseBranch,\n branchName:`${repoName}--${name}`,\n worktreePath:join(repoRoot, '..', `${repoName}.${name}`),\n }))\n}\n\nexport function removeWorktree (\n worktreePath:string,\n opts?:{ force?:boolean }\n):Promise<void> {\n return enqueue(() => removeWorktreeNow(worktreePath, opts))\n}\n\n// Deletes a local branch; a missing branch is not an error.\nexport function deleteBranch (\n repoRoot:string,\n name:string\n):Promise<void> {\n return enqueue(async () => {\n if (!(await branchExists(repoRoot, name))) return\n await execFileAsync(\n 'git', ['branch', '-D', name], { cwd:repoRoot }\n )\n })\n}\n\nexport function hasNewCommits (\n repoRoot:string,\n base:string,\n branch:string\n):boolean {\n const stdout = execFileSync(\n 'git',\n ['rev-list', '--count', `${base}..${branch}`],\n { cwd:repoRoot, encoding:'utf8' }\n )\n return Number(stdout.trim()) > 0\n}\n\nexport function headCommit (cwd:string):string {\n return gitText(cwd, ['rev-parse', 'HEAD'])\n}\n\nexport function mergeBase (\n cwd:string,\n a:string,\n b:string\n):string|null {\n try {\n return gitText(cwd, ['merge-base', a, b]) || null\n } catch {\n return null\n }\n}\n\n// False when `ancestor` is not an ancestor of `of`, or is not a\n// commit this repo knows.\nexport function isAncestor (\n cwd:string,\n ancestor:string,\n of:string\n):boolean {\n try {\n execFileSync(\n 'git',\n ['merge-base', '--is-ancestor', ancestor, of],\n { cwd, stdio:'ignore' }\n )\n return true\n } catch {\n return false\n }\n}\n\n// True when from..to changes any path outside `excludeDir`.\nexport function hasChangesOutside (\n cwd:string,\n from:string,\n to:string,\n excludeDir:string\n):boolean {\n try {\n execFileSync(\n 'git',\n [\n 'diff', '--quiet', `${from}..${to}`,\n '--', '.', `:(exclude)${excludeDir}`,\n ],\n { cwd, stdio:'ignore' }\n )\n return false\n } catch {\n return true\n }\n}\n\nexport function isDirty (cwd:string):boolean {\n const stdout = execFileSync(\n 'git',\n ['status', '--porcelain'],\n { cwd, encoding:'utf8' }\n )\n return stdout.trim().length > 0\n}\n\nexport async function commitDirty (\n cwd:string,\n message?:string\n):Promise<boolean> {\n if (!isDirty(cwd)) return false\n await execFileAsync(\n 'git',\n ['add', '-A'],\n { cwd }\n )\n try {\n await execFileAsync(\n 'git',\n [\n 'commit', '-m',\n message ??\n 'nightralph: auto-commit ' +\n 'remaining changes',\n ],\n { cwd }\n )\n return true\n } catch {\n return false\n }\n}\n\nexport async function listWorktrees (\n repoRoot:string\n):Promise<Array<string>> {\n const { stdout } = await execFileAsync(\n 'git',\n ['worktree', 'list', '--porcelain'],\n { cwd:repoRoot }\n )\n return stdout\n .trim()\n .split('\\n')\n .filter(line => line.startsWith('worktree '))\n .map(line => line.substring('worktree '.length))\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nightralph",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.56",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Orchestrator that runs a single-agent session per issue.",
|
|
6
6
|
"bin": {
|
|
@@ -17,11 +17,12 @@
|
|
|
17
17
|
"lint": "eslint \"./**/*.{ts,js}\"",
|
|
18
18
|
"start": "npm run build && node dist/index.js",
|
|
19
19
|
"build": "mkdir -p ./dist && rm -rf ./dist/* && npm run build-esm && npm run copy",
|
|
20
|
-
"build-esm": "esbuild src/index.ts --bundle --format=esm --platform=node --packages=external --metafile=dist/meta.json --keep-names --tsconfig=tsconfig.build.json --outfile=./dist/index.js --sourcemap && esbuild src/orchestrator.ts src/setup.ts src/resolve.ts src/worktree.ts src/progress.ts src/merge.ts src/display.ts src/testcmd.ts --format=esm --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --sourcemap && tsc --emitDeclarationOnly --project tsconfig.build.json",
|
|
20
|
+
"build-esm": "esbuild src/index.ts --bundle --format=esm --platform=node --packages=external --metafile=dist/meta.json --keep-names --tsconfig=tsconfig.build.json --outfile=./dist/index.js --sourcemap && esbuild src/orchestrator.ts src/setup.ts src/resolve.ts src/worktree.ts src/progress.ts src/merge.ts src/display.ts src/testcmd.ts src/preflight.ts src/learnings.ts src/retry.ts src/rules.ts src/review.ts src/blockers.ts src/stats.ts --format=esm --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --sourcemap && tsc --emitDeclarationOnly --project tsconfig.build.json",
|
|
21
21
|
"copy": "mkdir -p ./dist/skills ./dist/docs-templates && cp -r ./src/skills/* ./dist/skills && cp -r ./src/docs-templates/* ./dist/docs-templates",
|
|
22
22
|
"clone-skills": "bash scripts/clone-skills.sh",
|
|
23
23
|
"toc": "markdown-toc --maxdepth 4 -i README.md",
|
|
24
24
|
"preversion": "npm run lint",
|
|
25
|
+
"prepublishOnly": "npm run build",
|
|
25
26
|
"version": "npm run toc && git add README.md",
|
|
26
27
|
"postversion": "git push --follow-tags && npm publish",
|
|
27
28
|
"demo": "npm run build && node test/demo-display.mjs",
|