nightralph 0.0.52 → 0.0.54
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 +145 -16
- package/dist/display.js +80 -62
- package/dist/display.js.map +2 -2
- package/dist/index.js +2099 -314
- package/dist/index.js.map +4 -4
- package/dist/learnings.js +356 -0
- package/dist/learnings.js.map +7 -0
- package/dist/meta.json +234 -14
- package/dist/orchestrator.js +528 -142
- 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/retry.js +83 -0
- package/dist/retry.js.map +7 -0
- package/dist/review.js +662 -0
- package/dist/review.js.map +7 -0
- package/dist/rules.js +54 -0
- package/dist/rules.js.map +7 -0
- package/dist/src/display.d.ts +35 -33
- package/dist/src/display.d.ts.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/learnings.d.ts +68 -0
- package/dist/src/learnings.d.ts.map +1 -0
- package/dist/src/orchestrator.d.ts +24 -11
- 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/retry.d.ts +26 -0
- package/dist/src/retry.d.ts.map +1 -0
- package/dist/src/review.d.ts +127 -0
- package/dist/src/review.d.ts.map +1 -0
- package/dist/src/rules.d.ts +10 -0
- package/dist/src/rules.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/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 +5 -4
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { type EffortLevel } from './retry.js';
|
|
2
|
+
import type { AgentResult } from './orchestrator.js';
|
|
3
|
+
export declare const FINDINGS_FILE = "findings.md";
|
|
4
|
+
export declare const FINDINGS_PATH: string;
|
|
5
|
+
export declare const NO_FINDINGS = "No findings.";
|
|
6
|
+
export declare const REVIEW_EXCLUDE = ".scratch";
|
|
7
|
+
export declare const REVIEW_MARKER = "---- REVIEW INSTRUCTIONS ----";
|
|
8
|
+
export declare const FIX_MARKER = "---- REVIEW FINDINGS ----";
|
|
9
|
+
export declare function reviewExcludeFor(repoRoot: string, reviewPath: string): string;
|
|
10
|
+
export declare function reviewDiffCommand(from: string, to: string, exclude?: string): string;
|
|
11
|
+
export declare function renderReviewPrompt(opts: {
|
|
12
|
+
specBody: string;
|
|
13
|
+
from: string;
|
|
14
|
+
to: string;
|
|
15
|
+
exclude?: string;
|
|
16
|
+
}): string;
|
|
17
|
+
export type FixFailure = 'install' | 'exit' | 'no-commits' | 'tests' | 'merge';
|
|
18
|
+
export declare const FIX_FAILURE_TEXT: Record<FixFailure, string>;
|
|
19
|
+
export declare function renderFixPrompt(opts: {
|
|
20
|
+
specBody: string;
|
|
21
|
+
findings: ReadonlyArray<Finding>;
|
|
22
|
+
testCmd: string;
|
|
23
|
+
installCmd?: string;
|
|
24
|
+
previous?: {
|
|
25
|
+
reason: FixFailure;
|
|
26
|
+
notes: string | null;
|
|
27
|
+
};
|
|
28
|
+
}): string;
|
|
29
|
+
export declare const REVIEW_STATUSES: readonly ["clean", "fixed", "unresolved", "review-failed"];
|
|
30
|
+
export type ReviewStatus = typeof REVIEW_STATUSES[number];
|
|
31
|
+
export type Finding = {
|
|
32
|
+
summary: string;
|
|
33
|
+
detail: string;
|
|
34
|
+
checked: boolean;
|
|
35
|
+
};
|
|
36
|
+
export type FixRecord = {
|
|
37
|
+
attempts: number;
|
|
38
|
+
maxAttempts: number;
|
|
39
|
+
effort?: string;
|
|
40
|
+
exitCode: number | null;
|
|
41
|
+
result: string;
|
|
42
|
+
notes: string | null;
|
|
43
|
+
};
|
|
44
|
+
export type ReviewEntryInput = {
|
|
45
|
+
from: string;
|
|
46
|
+
to: string;
|
|
47
|
+
status: ReviewStatus;
|
|
48
|
+
now: Date;
|
|
49
|
+
findings: Finding[];
|
|
50
|
+
fix?: FixRecord;
|
|
51
|
+
};
|
|
52
|
+
export type ReviewEntry = {
|
|
53
|
+
from: string;
|
|
54
|
+
to: string;
|
|
55
|
+
status: ReviewStatus;
|
|
56
|
+
findings: Finding[];
|
|
57
|
+
body: string;
|
|
58
|
+
};
|
|
59
|
+
export type ReviewOutcome = {
|
|
60
|
+
status: ReviewStatus | 'skipped';
|
|
61
|
+
findings: number;
|
|
62
|
+
fixed: number;
|
|
63
|
+
};
|
|
64
|
+
export declare function parseFindings(text: string): Finding[] | null;
|
|
65
|
+
export declare function markFixed(findings: ReadonlyArray<Finding>, completedItems: ReadonlyArray<string>): Finding[];
|
|
66
|
+
export declare function renderReviewEntry(input: ReviewEntryInput): string;
|
|
67
|
+
export declare function parseReviewEntries(text: string): ReviewEntry[];
|
|
68
|
+
export declare function selectReviewBase(entries: ReadonlyArray<ReviewEntry>, opts: {
|
|
69
|
+
isAncestor: (sha: string) => boolean;
|
|
70
|
+
forkPoint: string | null;
|
|
71
|
+
launchCommit: string;
|
|
72
|
+
}): string;
|
|
73
|
+
export declare function renderReviewSummary(outcome: ReviewOutcome, reviewPath: string): string | null;
|
|
74
|
+
export type SpawnAgentFn = (opts: {
|
|
75
|
+
prompt: string;
|
|
76
|
+
agentCmd: string;
|
|
77
|
+
model: string | null;
|
|
78
|
+
timeout: number;
|
|
79
|
+
logPath: string;
|
|
80
|
+
cwd?: string;
|
|
81
|
+
label?: string;
|
|
82
|
+
maxTurns?: number;
|
|
83
|
+
effort?: EffortLevel;
|
|
84
|
+
onLine?: (line: string) => void;
|
|
85
|
+
}) => Promise<AgentResult>;
|
|
86
|
+
export type ReviewDeps = {
|
|
87
|
+
spawnAgent: SpawnAgentFn;
|
|
88
|
+
registry?: Set<number>;
|
|
89
|
+
};
|
|
90
|
+
export type ReviewPassOpts = {
|
|
91
|
+
repoRoot: string;
|
|
92
|
+
repoName: string;
|
|
93
|
+
baseBranch: string;
|
|
94
|
+
reviewPath: string;
|
|
95
|
+
forkedFrom?: string;
|
|
96
|
+
launchCommit: string;
|
|
97
|
+
specBody: string;
|
|
98
|
+
agentCmd: string;
|
|
99
|
+
model: string | null;
|
|
100
|
+
timeout: number;
|
|
101
|
+
logsDir: string;
|
|
102
|
+
testCmd: string;
|
|
103
|
+
installCmd?: string;
|
|
104
|
+
maxTurns?: number;
|
|
105
|
+
effort?: EffortLevel;
|
|
106
|
+
retries: number;
|
|
107
|
+
retryDelay: number;
|
|
108
|
+
log: (msg: string) => void;
|
|
109
|
+
now?: () => Date;
|
|
110
|
+
};
|
|
111
|
+
export declare function runReviewer(opts: ReviewPassOpts, deps: ReviewDeps, range: {
|
|
112
|
+
from: string;
|
|
113
|
+
to: string;
|
|
114
|
+
}): Promise<Finding[] | null>;
|
|
115
|
+
export type FixResult = {
|
|
116
|
+
merged: boolean;
|
|
117
|
+
findings: Finding[];
|
|
118
|
+
attempts: number;
|
|
119
|
+
effort?: EffortLevel;
|
|
120
|
+
exitCode: number | null;
|
|
121
|
+
result: string;
|
|
122
|
+
notes: string | null;
|
|
123
|
+
};
|
|
124
|
+
export declare function runFixer(opts: ReviewPassOpts, deps: ReviewDeps, findings: ReadonlyArray<Finding>): Promise<FixResult>;
|
|
125
|
+
export declare function readReviewEntries(path: string): ReviewEntry[];
|
|
126
|
+
export declare function runReviewPass(opts: ReviewPassOpts, deps: ReviewDeps): Promise<ReviewOutcome>;
|
|
127
|
+
//# sourceMappingURL=review.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/review.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function readDocsRule(subject: string): string;
|
|
2
|
+
export declare function installRule(installCmd?: string): string[];
|
|
3
|
+
export declare function testGateRule(testCmd: string, subject: string): string;
|
|
4
|
+
export declare const LINT_RULE: string;
|
|
5
|
+
export declare function integrityRule(unit: string): string;
|
|
6
|
+
export declare const CONFINEMENT_RULES: string[];
|
|
7
|
+
export declare function commitRule(unit: string): string;
|
|
8
|
+
export declare function notesRule(unit: string, subject: string): string;
|
|
9
|
+
export declare function agentsMdRule(subject: string): string;
|
|
10
|
+
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/rules.ts"],"names":[],"mappings":"AAMA,wBAAgB,YAAY,CAAE,OAAO,EAAC,MAAM,GAAE,MAAM,CAOnD;AAID,wBAAgB,WAAW,CAAE,UAAU,CAAC,EAAC,MAAM,GAAE,MAAM,EAAE,CAUxD;AAED,wBAAgB,YAAY,CAAE,OAAO,EAAC,MAAM,EAAE,OAAO,EAAC,MAAM,GAAE,MAAM,CAenE;AAED,eAAO,MAAM,SAAS,QACQ,CAAA;AAE9B,wBAAgB,aAAa,CAAE,IAAI,EAAC,MAAM,GAAE,MAAM,CAQjD;AAED,eAAO,MAAM,iBAAiB,EAAC,MAAM,EAIpC,CAAA;AAED,wBAAgB,UAAU,CAAE,IAAI,EAAC,MAAM,GAAE,MAAM,CAI9C;AAED,wBAAgB,SAAS,CAAE,IAAI,EAAC,MAAM,EAAE,OAAO,EAAC,MAAM,GAAE,MAAM,CAU7D;AAED,wBAAgB,YAAY,CAAE,OAAO,EAAC,MAAM,GAAE,MAAM,CAenD"}
|
package/dist/src/testcmd.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export type TestResult = {
|
|
|
3
3
|
output: string;
|
|
4
4
|
};
|
|
5
5
|
export declare function detectTestCmd(projectDir: string): string | null;
|
|
6
|
+
export declare function detectInstallCmd(projectDir: string): string | null;
|
|
6
7
|
export declare function runTestCmd(opts: {
|
|
7
8
|
cmd: string;
|
|
8
9
|
cwd: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testcmd.d.ts","sourceRoot":"","sources":["../../src/testcmd.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,UAAU,GAAG;IACrB,QAAQ,EAAC,MAAM,CAAA;IACf,MAAM,EAAC,MAAM,CAAA;CAChB,CAAA;AAID,wBAAgB,aAAa,CAAE,UAAU,EAAC,MAAM,GAAE,MAAM,GAAC,IAAI,CAa5D;AAED,wBAAgB,UAAU,CAAE,IAAI,EAAC;IAC7B,GAAG,EAAC,MAAM,CAAA;IACV,GAAG,EAAC,MAAM,CAAA;IACV,OAAO,EAAC,MAAM,CAAA;IACd,QAAQ,CAAC,EAAC,GAAG,CAAC,MAAM,CAAC,CAAA;CACxB,GAAE,OAAO,CAAC,UAAU,CAAC,CAwDrB"}
|
|
1
|
+
{"version":3,"file":"testcmd.d.ts","sourceRoot":"","sources":["../../src/testcmd.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,UAAU,GAAG;IACrB,QAAQ,EAAC,MAAM,CAAA;IACf,MAAM,EAAC,MAAM,CAAA;CAChB,CAAA;AAID,wBAAgB,aAAa,CAAE,UAAU,EAAC,MAAM,GAAE,MAAM,GAAC,IAAI,CAa5D;AAOD,wBAAgB,gBAAgB,CAAE,UAAU,EAAC,MAAM,GAAE,MAAM,GAAC,IAAI,CAW/D;AAED,wBAAgB,UAAU,CAAE,IAAI,EAAC;IAC7B,GAAG,EAAC,MAAM,CAAA;IACV,GAAG,EAAC,MAAM,CAAA;IACV,OAAO,EAAC,MAAM,CAAA;IACd,QAAQ,CAAC,EAAC,GAAG,CAAC,MAAM,CAAC,CAAA;CACxB,GAAE,OAAO,CAAC,UAAU,CAAC,CAwDrB"}
|
package/dist/src/worktree.d.ts
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import type { Ticket } from './orchestrator.js';
|
|
2
2
|
export declare function getRepoRoot(): string;
|
|
3
3
|
export declare function getRepoName(): string;
|
|
4
|
-
export declare function getCurrentBranch(): string;
|
|
4
|
+
export declare function getCurrentBranch(cwd?: string): string;
|
|
5
|
+
export declare function localBranchExists(repoRoot: string, name: string): boolean;
|
|
6
|
+
export declare function getDefaultBranch(repoRoot: string): string | null;
|
|
7
|
+
export declare function integrationBranchName(features: ReadonlyArray<string>): string;
|
|
8
|
+
export declare function planIntegrationBranch(current: string, defaultBranch: string | null, features: ReadonlyArray<string>): string | null;
|
|
9
|
+
export type IntegrationPlan = {
|
|
10
|
+
branch: string;
|
|
11
|
+
created: boolean;
|
|
12
|
+
resumed: boolean;
|
|
13
|
+
behind: number;
|
|
14
|
+
fastForwarded: boolean;
|
|
15
|
+
launchBranch: string;
|
|
16
|
+
forkedFrom: string | null;
|
|
17
|
+
};
|
|
18
|
+
export declare function prepareIntegrationBranch(repoRoot: string, features: ReadonlyArray<string>): IntegrationPlan;
|
|
19
|
+
export declare function describeIntegrationBranch(repoRoot: string, features: ReadonlyArray<string>): string;
|
|
5
20
|
export declare function buildBranchName(repoName: string, ticket: Ticket): string;
|
|
6
21
|
export declare function createWorktree(opts: {
|
|
7
22
|
repoRoot: string;
|
|
@@ -12,10 +27,24 @@ export declare function createWorktree(opts: {
|
|
|
12
27
|
worktreePath: string;
|
|
13
28
|
branchName: string;
|
|
14
29
|
}>;
|
|
30
|
+
export declare function createNamedWorktree(opts: {
|
|
31
|
+
repoRoot: string;
|
|
32
|
+
repoName: string;
|
|
33
|
+
baseBranch: string;
|
|
34
|
+
name: string;
|
|
35
|
+
}): Promise<{
|
|
36
|
+
worktreePath: string;
|
|
37
|
+
branchName: string;
|
|
38
|
+
}>;
|
|
15
39
|
export declare function removeWorktree(worktreePath: string, opts?: {
|
|
16
40
|
force?: boolean;
|
|
17
41
|
}): Promise<void>;
|
|
42
|
+
export declare function deleteBranch(repoRoot: string, name: string): Promise<void>;
|
|
18
43
|
export declare function hasNewCommits(repoRoot: string, base: string, branch: string): boolean;
|
|
44
|
+
export declare function headCommit(cwd: string): string;
|
|
45
|
+
export declare function mergeBase(cwd: string, a: string, b: string): string | null;
|
|
46
|
+
export declare function isAncestor(cwd: string, ancestor: string, of: string): boolean;
|
|
47
|
+
export declare function hasChangesOutside(cwd: string, from: string, to: string, excludeDir: string): boolean;
|
|
19
48
|
export declare function isDirty(cwd: string): boolean;
|
|
20
49
|
export declare function commitDirty(cwd: string, message?: string): Promise<boolean>;
|
|
21
50
|
export declare function listWorktrees(repoRoot: string): Promise<Array<string>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worktree.d.ts","sourceRoot":"","sources":["../../src/worktree.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAI/C,wBAAgB,WAAW,IAAI,MAAM,CAOpC;AAED,wBAAgB,WAAW,IAAI,MAAM,CAOpC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"worktree.d.ts","sourceRoot":"","sources":["../../src/worktree.ts"],"names":[],"mappings":"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"}
|
package/dist/testcmd.js
CHANGED
|
@@ -16,6 +16,18 @@ function detectTestCmd(projectDir) {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
__name(detectTestCmd, "detectTestCmd");
|
|
19
|
+
function detectInstallCmd(projectDir) {
|
|
20
|
+
const has = /* @__PURE__ */ __name((name) => existsSync(join(projectDir, name)), "has");
|
|
21
|
+
if (!has("package.json")) return null;
|
|
22
|
+
if (has("pnpm-lock.yaml")) {
|
|
23
|
+
return "pnpm install --prefer-offline --frozen-lockfile";
|
|
24
|
+
}
|
|
25
|
+
if (has("package-lock.json")) {
|
|
26
|
+
return "npm ci --prefer-offline --no-audit --no-fund";
|
|
27
|
+
}
|
|
28
|
+
return "npm install --prefer-offline --no-audit --no-fund --no-package-lock";
|
|
29
|
+
}
|
|
30
|
+
__name(detectInstallCmd, "detectInstallCmd");
|
|
19
31
|
function runTestCmd(opts) {
|
|
20
32
|
return new Promise((resolve) => {
|
|
21
33
|
let settled = false;
|
|
@@ -70,6 +82,7 @@ function runTestCmd(opts) {
|
|
|
70
82
|
}
|
|
71
83
|
__name(runTestCmd, "runTestCmd");
|
|
72
84
|
export {
|
|
85
|
+
detectInstallCmd,
|
|
73
86
|
detectTestCmd,
|
|
74
87
|
runTestCmd
|
|
75
88
|
};
|
package/dist/testcmd.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/testcmd.ts"],
|
|
4
|
-
"sourcesContent": ["import { spawn } from 'node:child_process'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nexport type TestResult = {\n exitCode:number\n output:string\n}\n\n// Returns the command to run the project's tests, or null\n// when the project does not declare one.\nexport function detectTestCmd (projectDir:string):string|null {\n const pkgPath = join(projectDir, 'package.json')\n if (!existsSync(pkgPath)) return null\n try {\n const pkg = JSON.parse(\n readFileSync(pkgPath, 'utf8')\n ) as { scripts?:Record<string, string> }\n return pkg.scripts?.test ?\n 'npm test' :\n null\n } catch {\n return null\n }\n}\n\nexport function runTestCmd (opts:{\n cmd:string\n cwd:string\n timeout:number\n registry?:Set<number>\n}):Promise<TestResult> {\n return new Promise(resolve => {\n let settled = false\n const child = spawn('sh', ['-c', opts.cmd], {\n cwd:opts.cwd,\n stdio:['ignore', 'pipe', 'pipe'],\n detached:true,\n })\n\n if (child.pid !== undefined && opts.registry) {\n opts.registry.add(child.pid)\n }\n\n const chunks:string[] = []\n child.stdout.on('data', d => chunks.push(String(d)))\n child.stderr.on('data', d => chunks.push(String(d)))\n\n const timer = setTimeout(() => {\n if (child.pid) {\n try {\n process.kill(-child.pid, 'SIGKILL')\n } catch {\n child.kill('SIGKILL')\n }\n } else {\n child.kill('SIGKILL')\n }\n }, opts.timeout * 1000)\n\n child.on('error', err => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n if (child.pid !== undefined && opts.registry) {\n opts.registry.delete(child.pid)\n }\n chunks.push(String(err))\n resolve({\n exitCode:1,\n output:chunks.join(''),\n })\n })\n\n child.on('close', code => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n if (child.pid !== undefined && opts.registry) {\n opts.registry.delete(child.pid)\n }\n resolve({\n exitCode:code ?? 1,\n output:chunks.join(''),\n })\n })\n })\n}\n"],
|
|
5
|
-
"mappings": ";;AAAA,SAAS,aAAa;AACtB,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY;AASd,SAAS,cAAe,YAA+B;AAC1D,QAAM,UAAU,KAAK,YAAY,cAAc;AAC/C,MAAI,CAAC,WAAW,OAAO,EAAG,QAAO;AACjC,MAAI;AACA,UAAM,MAAM,KAAK;AAAA,MACb,aAAa,SAAS,MAAM;AAAA,IAChC;AACA,WAAO,IAAI,SAAS,OAChB,aACA;AAAA,EACR,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAbgB;
|
|
4
|
+
"sourcesContent": ["import { spawn } from 'node:child_process'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nexport type TestResult = {\n exitCode:number\n output:string\n}\n\n// Returns the command to run the project's tests, or null\n// when the project does not declare one.\nexport function detectTestCmd (projectDir:string):string|null {\n const pkgPath = join(projectDir, 'package.json')\n if (!existsSync(pkgPath)) return null\n try {\n const pkg = JSON.parse(\n readFileSync(pkgPath, 'utf8')\n ) as { scripts?:Record<string, string> }\n return pkg.scripts?.test ?\n 'npm test' :\n null\n } catch {\n return null\n }\n}\n\n// Returns the command that installs dependencies in a fresh\n// worktree, or null when the project has no package.json.\n// Follows the project's lockfile, so the worktree gets the\n// same dependency tree as the main checkout, and never\n// writes a lockfile a ticket branch would then commit.\nexport function detectInstallCmd (projectDir:string):string|null {\n const has = (name:string) => existsSync(join(projectDir, name))\n if (!has('package.json')) return null\n if (has('pnpm-lock.yaml')) {\n return 'pnpm install --prefer-offline --frozen-lockfile'\n }\n if (has('package-lock.json')) {\n return 'npm ci --prefer-offline --no-audit --no-fund'\n }\n return 'npm install --prefer-offline --no-audit ' +\n '--no-fund --no-package-lock'\n}\n\nexport function runTestCmd (opts:{\n cmd:string\n cwd:string\n timeout:number\n registry?:Set<number>\n}):Promise<TestResult> {\n return new Promise(resolve => {\n let settled = false\n const child = spawn('sh', ['-c', opts.cmd], {\n cwd:opts.cwd,\n stdio:['ignore', 'pipe', 'pipe'],\n detached:true,\n })\n\n if (child.pid !== undefined && opts.registry) {\n opts.registry.add(child.pid)\n }\n\n const chunks:string[] = []\n child.stdout.on('data', d => chunks.push(String(d)))\n child.stderr.on('data', d => chunks.push(String(d)))\n\n const timer = setTimeout(() => {\n if (child.pid) {\n try {\n process.kill(-child.pid, 'SIGKILL')\n } catch {\n child.kill('SIGKILL')\n }\n } else {\n child.kill('SIGKILL')\n }\n }, opts.timeout * 1000)\n\n child.on('error', err => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n if (child.pid !== undefined && opts.registry) {\n opts.registry.delete(child.pid)\n }\n chunks.push(String(err))\n resolve({\n exitCode:1,\n output:chunks.join(''),\n })\n })\n\n child.on('close', code => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n if (child.pid !== undefined && opts.registry) {\n opts.registry.delete(child.pid)\n }\n resolve({\n exitCode:code ?? 1,\n output:chunks.join(''),\n })\n })\n })\n}\n"],
|
|
5
|
+
"mappings": ";;AAAA,SAAS,aAAa;AACtB,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY;AASd,SAAS,cAAe,YAA+B;AAC1D,QAAM,UAAU,KAAK,YAAY,cAAc;AAC/C,MAAI,CAAC,WAAW,OAAO,EAAG,QAAO;AACjC,MAAI;AACA,UAAM,MAAM,KAAK;AAAA,MACb,aAAa,SAAS,MAAM;AAAA,IAChC;AACA,WAAO,IAAI,SAAS,OAChB,aACA;AAAA,EACR,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAbgB;AAoBT,SAAS,iBAAkB,YAA+B;AAC7D,QAAM,MAAM,wBAAC,SAAgB,WAAW,KAAK,YAAY,IAAI,CAAC,GAAlD;AACZ,MAAI,CAAC,IAAI,cAAc,EAAG,QAAO;AACjC,MAAI,IAAI,gBAAgB,GAAG;AACvB,WAAO;AAAA,EACX;AACA,MAAI,IAAI,mBAAmB,GAAG;AAC1B,WAAO;AAAA,EACX;AACA,SAAO;AAEX;AAXgB;AAaT,SAAS,WAAY,MAKL;AACnB,SAAO,IAAI,QAAQ,aAAW;AAC1B,QAAI,UAAU;AACd,UAAM,QAAQ,MAAM,MAAM,CAAC,MAAM,KAAK,GAAG,GAAG;AAAA,MACxC,KAAI,KAAK;AAAA,MACT,OAAM,CAAC,UAAU,QAAQ,MAAM;AAAA,MAC/B,UAAS;AAAA,IACb,CAAC;AAED,QAAI,MAAM,QAAQ,UAAa,KAAK,UAAU;AAC1C,WAAK,SAAS,IAAI,MAAM,GAAG;AAAA,IAC/B;AAEA,UAAM,SAAkB,CAAC;AACzB,UAAM,OAAO,GAAG,QAAQ,OAAK,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC;AACnD,UAAM,OAAO,GAAG,QAAQ,OAAK,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC;AAEnD,UAAM,QAAQ,WAAW,MAAM;AAC3B,UAAI,MAAM,KAAK;AACX,YAAI;AACA,kBAAQ,KAAK,CAAC,MAAM,KAAK,SAAS;AAAA,QACtC,QAAQ;AACJ,gBAAM,KAAK,SAAS;AAAA,QACxB;AAAA,MACJ,OAAO;AACH,cAAM,KAAK,SAAS;AAAA,MACxB;AAAA,IACJ,GAAG,KAAK,UAAU,GAAI;AAEtB,UAAM,GAAG,SAAS,SAAO;AACrB,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,KAAK;AAClB,UAAI,MAAM,QAAQ,UAAa,KAAK,UAAU;AAC1C,aAAK,SAAS,OAAO,MAAM,GAAG;AAAA,MAClC;AACA,aAAO,KAAK,OAAO,GAAG,CAAC;AACvB,cAAQ;AAAA,QACJ,UAAS;AAAA,QACT,QAAO,OAAO,KAAK,EAAE;AAAA,MACzB,CAAC;AAAA,IACL,CAAC;AAED,UAAM,GAAG,SAAS,UAAQ;AACtB,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,KAAK;AAClB,UAAI,MAAM,QAAQ,UAAa,KAAK,UAAU;AAC1C,aAAK,SAAS,OAAO,MAAM,GAAG;AAAA,MAClC;AACA,cAAQ;AAAA,QACJ,UAAS,QAAQ;AAAA,QACjB,QAAO,OAAO,KAAK,EAAE;AAAA,MACzB,CAAC;AAAA,IACL,CAAC;AAAA,EACL,CAAC;AACL;AA7DgB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
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
|