@the-open-engine/zeroshot 6.39.0 → 6.39.1
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 +6 -0
- package/cli/index.js +60 -17
- package/lib/detached-startup.d.ts +1 -0
- package/lib/detached-startup.js +2 -1
- package/lib/start-cluster-environment.d.ts +1 -0
- package/lib/start-cluster-run-options.d.ts +1 -0
- package/lib/start-cluster-run-options.js +1 -0
- package/npm-shrinkwrap.json +6 -8
- package/package.json +3 -3
- package/scripts/rust-distribution.js +13 -31
- package/src/agents/git-pusher-template.js +17 -34
- package/src/copy-containment.js +191 -0
- package/src/copy-containment.ts +259 -0
- package/src/copy-worker.js +29 -30
- package/src/copy-worker.ts +43 -30
- package/src/isolation-manager.js +59 -20
- package/src/legacy-lib/detached-startup.ts +3 -1
- package/src/legacy-lib/start-cluster-environment.ts +1 -0
- package/src/legacy-lib/start-cluster-run-options.ts +2 -0
- package/src/orchestrator.js +13 -3
- package/src/pr-body-template.js +71 -0
package/README.md
CHANGED
|
@@ -106,6 +106,7 @@ zeroshot run <input> # issue, URL, markdown file, or inline text
|
|
|
106
106
|
zeroshot run 123 --docker # container isolation
|
|
107
107
|
zeroshot run 123 --pr # worktree + pull request
|
|
108
108
|
zeroshot run 123 --ship # worktree + PR + merge after approval
|
|
109
|
+
zeroshot run 123 --pr --pr-body $'## Summary\n\nCustom text\n\n{{issue_reference}}'
|
|
109
110
|
zeroshot run 123 -d # background run
|
|
110
111
|
|
|
111
112
|
zeroshot list # tasks and clusters (--json)
|
|
@@ -120,6 +121,11 @@ zeroshot settings # effective settings
|
|
|
120
121
|
zeroshot agents list # available agents
|
|
121
122
|
```
|
|
122
123
|
|
|
124
|
+
`--pr-body` supplies a deterministic pull-request body for `--pr` and `--ship` runs. The
|
|
125
|
+
template supports `{{issue_number}}`, `{{issue_title}}`, and `{{issue_reference}}`; all three
|
|
126
|
+
expand to empty text for tasks without an issue, so manual runs never emit `Closes #unknown`.
|
|
127
|
+
The unrendered template is retained for detached and resumed runs.
|
|
128
|
+
|
|
123
129
|
</details>
|
|
124
130
|
|
|
125
131
|
<details>
|
package/cli/index.js
CHANGED
|
@@ -107,6 +107,8 @@ const {
|
|
|
107
107
|
runLegacyUpdateIfRequested,
|
|
108
108
|
} = require('./lib/update-checker');
|
|
109
109
|
const { checkBinDirOnPath, printPathWarning } = require('../lib/path-check');
|
|
110
|
+
const { quoteShellArgument } = require('../lib/git-remote-utils');
|
|
111
|
+
const { renderPullRequestBody, resolveIssueContext } = require('../src/pr-body-template');
|
|
110
112
|
const { StatusFooter, AGENT_STATE, ACTIVE_STATES } = require('../src/status-footer');
|
|
111
113
|
const { EVENT_COPY, formatMergeStatus } = require('./event-copy');
|
|
112
114
|
|
|
@@ -2012,9 +2014,25 @@ function buildContextSummary({
|
|
|
2012
2014
|
return contextSummary;
|
|
2013
2015
|
}
|
|
2014
2016
|
|
|
2015
|
-
function
|
|
2016
|
-
const
|
|
2017
|
-
|
|
2017
|
+
function buildDefaultFinishPrBody({ taskText, issueNumber, issueTitle }) {
|
|
2018
|
+
const issueReference = renderPullRequestBody(undefined, { issueNumber, issueTitle });
|
|
2019
|
+
return [
|
|
2020
|
+
issueReference,
|
|
2021
|
+
issueReference ? '' : null,
|
|
2022
|
+
'## Summary',
|
|
2023
|
+
`${String(taskText || 'Unknown task').slice(0, 200)}...`,
|
|
2024
|
+
'',
|
|
2025
|
+
'## Changes',
|
|
2026
|
+
'- Implementation complete',
|
|
2027
|
+
'- All validations addressed',
|
|
2028
|
+
'',
|
|
2029
|
+
'🤖 Generated with zeroshot finish',
|
|
2030
|
+
]
|
|
2031
|
+
.filter((line) => line !== null)
|
|
2032
|
+
.join('\n');
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
const FINISH_MERGE_STEP = `
|
|
2018
2036
|
8. MERGE THE PR - THIS IS MANDATORY:
|
|
2019
2037
|
\`\`\`bash
|
|
2020
2038
|
gh pr merge --merge --auto
|
|
@@ -2029,7 +2047,29 @@ function buildCompletionPrompt({ contextSummary, taskText, issueNumber, issueTit
|
|
|
2029
2047
|
|
|
2030
2048
|
REPEAT UNTIL MERGED. DO NOT GIVE UP.`;
|
|
2031
2049
|
|
|
2032
|
-
|
|
2050
|
+
function buildFinishCommandArguments({ taskText, issueNumber, issueTitle, prBody }) {
|
|
2051
|
+
const issueContext = resolveIssueContext({ issueNumber, issueTitle });
|
|
2052
|
+
const resolvedTitle = issueContext.issueTitle;
|
|
2053
|
+
const resolvedBody =
|
|
2054
|
+
typeof prBody === 'string'
|
|
2055
|
+
? renderPullRequestBody(prBody, { issueNumber, issueTitle: resolvedTitle })
|
|
2056
|
+
: buildDefaultFinishPrBody({ taskText, issueNumber, issueTitle: resolvedTitle });
|
|
2057
|
+
return {
|
|
2058
|
+
commitMessage: quoteShellArgument(resolvedTitle || 'feat: implement task'),
|
|
2059
|
+
branch: quoteShellArgument(
|
|
2060
|
+
issueContext.issueNumber !== 'unknown'
|
|
2061
|
+
? `issue-${issueContext.issueNumber}`
|
|
2062
|
+
: 'feature/implementation'
|
|
2063
|
+
),
|
|
2064
|
+
prTitle: quoteShellArgument(resolvedTitle),
|
|
2065
|
+
prBody: quoteShellArgument(resolvedBody),
|
|
2066
|
+
};
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
function buildCompletionPrompt({ contextSummary, taskText, issueNumber, issueTitle, prBody }) {
|
|
2070
|
+
const args = buildFinishCommandArguments({ taskText, issueNumber, issueTitle, prBody });
|
|
2071
|
+
|
|
2072
|
+
return `# YOUR MISSION: CREATE PR AND MERGE IT
|
|
2033
2073
|
|
|
2034
2074
|
${contextSummary}
|
|
2035
2075
|
|
|
@@ -2050,12 +2090,12 @@ You are the FINISHER. Your ONLY job is to take this cluster's work and push it a
|
|
|
2050
2090
|
2. COMMIT ALL CHANGES - Stage and commit everything:
|
|
2051
2091
|
\`\`\`bash
|
|
2052
2092
|
git add .
|
|
2053
|
-
git commit -m
|
|
2093
|
+
git commit -m ${args.commitMessage}
|
|
2054
2094
|
\`\`\`
|
|
2055
2095
|
|
|
2056
2096
|
3. CREATE BRANCH - Use issue number if available:
|
|
2057
2097
|
\`\`\`bash
|
|
2058
|
-
|
|
2098
|
+
git checkout -b ${args.branch}
|
|
2059
2099
|
\`\`\`
|
|
2060
2100
|
|
|
2061
2101
|
4. PUSH TO REMOTE:
|
|
@@ -2065,16 +2105,7 @@ You are the FINISHER. Your ONLY job is to take this cluster's work and push it a
|
|
|
2065
2105
|
|
|
2066
2106
|
5. CREATE PULL REQUEST:
|
|
2067
2107
|
\`\`\`bash
|
|
2068
|
-
gh pr create --title
|
|
2069
|
-
|
|
2070
|
-
## Summary
|
|
2071
|
-
${taskText.slice(0, 200)}...
|
|
2072
|
-
|
|
2073
|
-
## Changes
|
|
2074
|
-
- Implementation complete
|
|
2075
|
-
- All validations addressed
|
|
2076
|
-
|
|
2077
|
-
🤖 Generated with zeroshot finish"
|
|
2108
|
+
gh pr create --title ${args.prTitle} --body ${args.prBody}
|
|
2078
2109
|
\`\`\`
|
|
2079
2110
|
|
|
2080
2111
|
6. GET PR URL:
|
|
@@ -2083,7 +2114,7 @@ ${taskText.slice(0, 200)}...
|
|
|
2083
2114
|
\`\`\`
|
|
2084
2115
|
|
|
2085
2116
|
7. OUTPUT THE PR URL - Print it clearly so user can see it
|
|
2086
|
-
${
|
|
2117
|
+
${FINISH_MERGE_STEP}
|
|
2087
2118
|
|
|
2088
2119
|
## RULES
|
|
2089
2120
|
|
|
@@ -2707,6 +2738,10 @@ program
|
|
|
2707
2738
|
'Full automation: worktree isolation + PR + auto-merge (use --docker for Docker)'
|
|
2708
2739
|
)
|
|
2709
2740
|
.option('--pr-base <branch>', 'Target branch for PRs (default: repo default branch)')
|
|
2741
|
+
.option(
|
|
2742
|
+
'--pr-body <template>',
|
|
2743
|
+
'PR body template; supports {{issue_number}}, {{issue_title}}, and {{issue_reference}}'
|
|
2744
|
+
)
|
|
2710
2745
|
.option('--merge-queue', 'Use GitHub merge queue instead of direct merge')
|
|
2711
2746
|
.option(
|
|
2712
2747
|
'--close-issue <mode>',
|
|
@@ -3714,6 +3749,10 @@ program
|
|
|
3714
3749
|
.helpGroup('Control:')
|
|
3715
3750
|
.description('Take existing cluster and create completion-focused task (creates PR and merges)')
|
|
3716
3751
|
.option('-y, --yes', 'Skip confirmation if cluster is running')
|
|
3752
|
+
.option(
|
|
3753
|
+
'--pr-body <template>',
|
|
3754
|
+
'PR body template; supports {{issue_number}}, {{issue_title}}, and {{issue_reference}}'
|
|
3755
|
+
)
|
|
3717
3756
|
.action(async (id, options) => {
|
|
3718
3757
|
try {
|
|
3719
3758
|
const orchestrator = await getOrchestrator();
|
|
@@ -3732,6 +3771,7 @@ program
|
|
|
3732
3771
|
taskText: context.taskText,
|
|
3733
3772
|
issueNumber: context.issueNumber,
|
|
3734
3773
|
issueTitle: context.issueTitle,
|
|
3774
|
+
prBody: options.prBody ?? cluster.prOptions?.prBody,
|
|
3735
3775
|
});
|
|
3736
3776
|
printCompletionPromptPreview(completionPrompt);
|
|
3737
3777
|
|
|
@@ -6190,6 +6230,9 @@ module.exports = {
|
|
|
6190
6230
|
isStartupUpdateEligible,
|
|
6191
6231
|
handleNoArgumentInvocation,
|
|
6192
6232
|
shouldRunInitialSetup,
|
|
6233
|
+
extractFinishContext,
|
|
6234
|
+
buildDefaultFinishPrBody,
|
|
6235
|
+
buildCompletionPrompt,
|
|
6193
6236
|
resolveRunMode,
|
|
6194
6237
|
killRunningClusters,
|
|
6195
6238
|
};
|
package/lib/detached-startup.js
CHANGED
|
@@ -117,9 +117,10 @@ async function registerDetachedSetupCluster({ clusterId, pid, storageDir, logPat
|
|
|
117
117
|
setupStartedAt: Date.now(),
|
|
118
118
|
setupStage: 'starting',
|
|
119
119
|
autoPr: plan.delivery !== 'none',
|
|
120
|
-
prOptions:
|
|
120
|
+
prOptions: plan.delivery !== 'none'
|
|
121
121
|
? {
|
|
122
122
|
prBase: runOptions.prBase,
|
|
123
|
+
prBody: typeof runOptions.prBody === 'string' ? runOptions.prBody : null,
|
|
123
124
|
mergeQueue: runOptions.mergeQueue || false,
|
|
124
125
|
closeIssue: runOptions.closeIssue || null,
|
|
125
126
|
autoMerge: plan.autoMerge,
|
|
@@ -89,6 +89,7 @@ function buildStartOptionsFromPlan({ clusterId, plan, options, settings, provide
|
|
|
89
89
|
containerHome: optionalValue(options.containerHome),
|
|
90
90
|
forceProvider: optionalValue(forceProvider),
|
|
91
91
|
prBase: environment ? resolvePrBase(options) : optionalValue(options.prBase),
|
|
92
|
+
prBody: typeof options.prBody === 'string' ? options.prBody : undefined,
|
|
92
93
|
mergeQueue: environment ? resolveMergeQueue(options) : optionalValue(options.mergeQueue),
|
|
93
94
|
closeIssue: environment ? resolveCloseIssue(options) : optionalValue(options.closeIssue),
|
|
94
95
|
ship: plan.delivery === 'ship',
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@the-open-engine/zeroshot",
|
|
3
|
-
"version": "6.39.
|
|
3
|
+
"version": "6.39.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@the-open-engine/zeroshot",
|
|
9
|
-
"version": "6.39.
|
|
9
|
+
"version": "6.39.1",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"bun": "1.3.14",
|
|
18
18
|
"chalk": "^4.1.2",
|
|
19
19
|
"commander": "^14.0.2",
|
|
20
|
+
"js-yaml": "^4.3.1",
|
|
20
21
|
"node-pty": "^1.1.0",
|
|
21
22
|
"omelette": "^0.4.17",
|
|
22
23
|
"open": "^10.1.0",
|
|
@@ -43,7 +44,6 @@
|
|
|
43
44
|
"eslint-plugin-sonarjs": "^3.0.5",
|
|
44
45
|
"eslint-plugin-unused-imports": "^4.3.0",
|
|
45
46
|
"husky": "^9.1.7",
|
|
46
|
-
"js-yaml": "^4.2.0",
|
|
47
47
|
"jscpd": "^3.5.10",
|
|
48
48
|
"lint-staged": "^16.2.7",
|
|
49
49
|
"mocha": "^11.7.5",
|
|
@@ -5155,7 +5155,6 @@
|
|
|
5155
5155
|
"version": "2.0.1",
|
|
5156
5156
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
|
5157
5157
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
|
5158
|
-
"dev": true,
|
|
5159
5158
|
"license": "Python-2.0"
|
|
5160
5159
|
},
|
|
5161
5160
|
"node_modules/argv-formatter": {
|
|
@@ -9177,10 +9176,9 @@
|
|
|
9177
9176
|
"license": "MIT"
|
|
9178
9177
|
},
|
|
9179
9178
|
"node_modules/js-yaml": {
|
|
9180
|
-
"version": "4.
|
|
9181
|
-
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.
|
|
9182
|
-
"integrity": "sha512-
|
|
9183
|
-
"dev": true,
|
|
9179
|
+
"version": "4.3.1",
|
|
9180
|
+
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
|
|
9181
|
+
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
|
|
9184
9182
|
"funding": [
|
|
9185
9183
|
{
|
|
9186
9184
|
"type": "github",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@the-open-engine/zeroshot",
|
|
3
|
-
"version": "6.39.
|
|
3
|
+
"version": "6.39.1",
|
|
4
4
|
"description": "Independent executor–verifier orchestration for software changes.",
|
|
5
5
|
"main": "src/orchestrator.js",
|
|
6
6
|
"bin": {
|
|
@@ -204,6 +204,7 @@
|
|
|
204
204
|
"bun": "1.3.14",
|
|
205
205
|
"chalk": "^4.1.2",
|
|
206
206
|
"commander": "^14.0.2",
|
|
207
|
+
"js-yaml": "^4.3.1",
|
|
207
208
|
"node-pty": "^1.1.0",
|
|
208
209
|
"omelette": "^0.4.17",
|
|
209
210
|
"open": "^10.1.0",
|
|
@@ -229,7 +230,6 @@
|
|
|
229
230
|
"eslint-plugin-sonarjs": "^3.0.5",
|
|
230
231
|
"eslint-plugin-unused-imports": "^4.3.0",
|
|
231
232
|
"husky": "^9.1.7",
|
|
232
|
-
"js-yaml": "^4.2.0",
|
|
233
233
|
"jscpd": "^3.5.10",
|
|
234
234
|
"lint-staged": "^16.2.7",
|
|
235
235
|
"mocha": "^11.7.5",
|
|
@@ -244,7 +244,7 @@
|
|
|
244
244
|
},
|
|
245
245
|
"overrides": {
|
|
246
246
|
"cosmiconfig": {
|
|
247
|
-
"js-yaml": "4.
|
|
247
|
+
"js-yaml": "4.3.1"
|
|
248
248
|
},
|
|
249
249
|
"adm-zip": "0.6.0",
|
|
250
250
|
"sharp": "0.35.3",
|
|
@@ -325,10 +325,7 @@ function stagedLockDependencies(cargoLock, workspaceCargoToml) {
|
|
|
325
325
|
|
|
326
326
|
function stageCargoLock(cargoLock, version, workspaceCargoToml) {
|
|
327
327
|
const targetPackage = workspaceLockPackage(cargoLock);
|
|
328
|
-
let stagedPackage = targetPackage.text.replace(
|
|
329
|
-
/^(version = ")[^"]+(")$/m,
|
|
330
|
-
`$1${version}$2`
|
|
331
|
-
);
|
|
328
|
+
let stagedPackage = targetPackage.text.replace(/^(version = ")[^"]+(")$/m, `$1${version}$2`);
|
|
332
329
|
for (const dependency of stagedLockDependencies(cargoLock, workspaceCargoToml)) {
|
|
333
330
|
const dependencyPattern = new RegExp(
|
|
334
331
|
`^(\\s*")${escapeRegExp(dependency.name)}(?: [^"]+)?(",\\r?)$`,
|
|
@@ -339,10 +336,7 @@ function stageCargoLock(cargoLock, version, workspaceCargoToml) {
|
|
|
339
336
|
`RUST_VERSION_STAGE_FAILED: Cargo.lock zeroshot-rust entry has no ${dependency.name} dependency`
|
|
340
337
|
);
|
|
341
338
|
}
|
|
342
|
-
stagedPackage = stagedPackage.replace(
|
|
343
|
-
dependencyPattern,
|
|
344
|
-
`$1${dependency.reference}$2`
|
|
345
|
-
);
|
|
339
|
+
stagedPackage = stagedPackage.replace(dependencyPattern, `$1${dependency.reference}$2`);
|
|
346
340
|
}
|
|
347
341
|
return (
|
|
348
342
|
cargoLock.slice(0, targetPackage.start) +
|
|
@@ -359,10 +353,7 @@ function verifyStagedCargoLock(cargoLock, version, workspaceCargoToml) {
|
|
|
359
353
|
);
|
|
360
354
|
}
|
|
361
355
|
for (const dependency of stagedLockDependencies(cargoLock, workspaceCargoToml)) {
|
|
362
|
-
const dependencyPattern = new RegExp(
|
|
363
|
-
`^\\s*"${escapeRegExp(dependency.reference)}",\\r?$`,
|
|
364
|
-
'm'
|
|
365
|
-
);
|
|
356
|
+
const dependencyPattern = new RegExp(`^\\s*"${escapeRegExp(dependency.reference)}",\\r?$`, 'm');
|
|
366
357
|
if (!dependencyPattern.test(targetPackage.text)) {
|
|
367
358
|
throw new Error(
|
|
368
359
|
`${VERSION_ERROR}: Cargo.lock zeroshot-rust dependency ${dependency.name} is not coupled to ${dependency.version}`
|
|
@@ -400,8 +391,7 @@ function stageVersion(
|
|
|
400
391
|
function checkVersionCoupling(tag, cargoToml, cargoLock, workspaceCargoToml) {
|
|
401
392
|
const useRepositoryFiles = cargoToml === undefined;
|
|
402
393
|
const manifest =
|
|
403
|
-
cargoToml ??
|
|
404
|
-
fs.readFileSync(path.join(repositoryRoot, 'zeroshot-rust', 'Cargo.toml'), 'utf8');
|
|
394
|
+
cargoToml ?? fs.readFileSync(path.join(repositoryRoot, 'zeroshot-rust', 'Cargo.toml'), 'utf8');
|
|
405
395
|
const releaseVersion = normalizeVersion(tag);
|
|
406
396
|
const manifestVersion = cargoVersion(manifest);
|
|
407
397
|
if (releaseVersion !== manifestVersion) {
|
|
@@ -416,8 +406,7 @@ function checkVersionCoupling(tag, cargoToml, cargoLock, workspaceCargoToml) {
|
|
|
416
406
|
: undefined);
|
|
417
407
|
if (lock !== undefined) {
|
|
418
408
|
const workspace =
|
|
419
|
-
workspaceCargoToml ??
|
|
420
|
-
fs.readFileSync(path.join(repositoryRoot, 'Cargo.toml'), 'utf8');
|
|
409
|
+
workspaceCargoToml ?? fs.readFileSync(path.join(repositoryRoot, 'Cargo.toml'), 'utf8');
|
|
421
410
|
verifyStagedCargoLock(lock, releaseVersion, workspace);
|
|
422
411
|
}
|
|
423
412
|
return releaseVersion;
|
|
@@ -483,9 +472,7 @@ const SCRIPT_INSTALL_CONTRACTS = Object.freeze([
|
|
|
483
472
|
]);
|
|
484
473
|
|
|
485
474
|
function invokesRustDistribution(step) {
|
|
486
|
-
return (
|
|
487
|
-
typeof step.run === 'string' && RUST_DISTRIBUTION_INVOCATION.test(step.run)
|
|
488
|
-
);
|
|
475
|
+
return typeof step.run === 'string' && RUST_DISTRIBUTION_INVOCATION.test(step.run);
|
|
489
476
|
}
|
|
490
477
|
|
|
491
478
|
function checkScriptInstall(job, { jobName, installName, command, checkoutRef }) {
|
|
@@ -507,9 +494,7 @@ function checkScriptInstall(job, { jobName, installName, command, checkoutRef })
|
|
|
507
494
|
checkout.with.repository !== '${{ github.repository }}') ||
|
|
508
495
|
checkout.with?.ref !== checkoutRef
|
|
509
496
|
) {
|
|
510
|
-
failIntegrity(
|
|
511
|
-
`${jobName} must checkout expected current repository source at workspace root`
|
|
512
|
-
);
|
|
497
|
+
failIntegrity(`${jobName} must checkout expected current repository source at workspace root`);
|
|
513
498
|
}
|
|
514
499
|
const installIndex = job.steps.indexOf(install);
|
|
515
500
|
const checkoutIndex = job.steps.indexOf(checkout);
|
|
@@ -745,11 +730,12 @@ function hasValidSri(integrity) {
|
|
|
745
730
|
}
|
|
746
731
|
|
|
747
732
|
function checkScriptDependencies(packageManifest, packageLock) {
|
|
748
|
-
|
|
733
|
+
// js-yaml is a runtime dependency: lib/compose-utils.js requires it from the published package.
|
|
734
|
+
const directSpec = packageManifest.dependencies?.['js-yaml'];
|
|
749
735
|
if (typeof directSpec !== 'string' || directSpec.length === 0) {
|
|
750
|
-
failIntegrity('rust-distribution.js requires a direct js-yaml
|
|
736
|
+
failIntegrity('rust-distribution.js requires a direct js-yaml dependency');
|
|
751
737
|
}
|
|
752
|
-
const lockSpec = packageLock.packages?.['']?.
|
|
738
|
+
const lockSpec = packageLock.packages?.['']?.dependencies?.['js-yaml'];
|
|
753
739
|
if (lockSpec !== directSpec) {
|
|
754
740
|
failIntegrity('package-lock root js-yaml spec must match package.json');
|
|
755
741
|
}
|
|
@@ -773,12 +759,8 @@ function checkRepository(
|
|
|
773
759
|
shimTargets = JSON.parse(
|
|
774
760
|
fs.readFileSync(path.join(repositoryRoot, 'npm', 'zeroshot-rust', 'targets.json'), 'utf8')
|
|
775
761
|
),
|
|
776
|
-
packageManifest = JSON.parse(
|
|
777
|
-
|
|
778
|
-
),
|
|
779
|
-
packageLock = JSON.parse(
|
|
780
|
-
fs.readFileSync(path.join(repositoryRoot, 'package-lock.json'), 'utf8')
|
|
781
|
-
)
|
|
762
|
+
packageManifest = JSON.parse(fs.readFileSync(path.join(repositoryRoot, 'package.json'), 'utf8')),
|
|
763
|
+
packageLock = JSON.parse(fs.readFileSync(path.join(repositoryRoot, 'package-lock.json'), 'utf8'))
|
|
782
764
|
) {
|
|
783
765
|
let document;
|
|
784
766
|
try {
|
|
@@ -277,6 +277,7 @@ return hasSufficientEvidence;`;
|
|
|
277
277
|
const { readRepoSettings } = require('../../lib/repo-settings');
|
|
278
278
|
const { normalizeGitRemoteName, quoteShellArgument } = require('../../lib/git-remote-utils');
|
|
279
279
|
const { resolveRequiredQualityGates } = require('../quality-gates');
|
|
280
|
+
const { renderPullRequestBody, resolveIssueContext } = require('../pr-body-template');
|
|
280
281
|
|
|
281
282
|
function getSafeBranchName(value) {
|
|
282
283
|
if (typeof value !== 'string') {
|
|
@@ -314,35 +315,6 @@ function normalizeCloseIssueMode(value) {
|
|
|
314
315
|
return null;
|
|
315
316
|
}
|
|
316
317
|
|
|
317
|
-
function normalizeIssueNumber(value) {
|
|
318
|
-
const candidate = typeof value === 'number' ? String(value) : value;
|
|
319
|
-
if (typeof candidate !== 'string') return 'unknown';
|
|
320
|
-
const trimmed = candidate.trim();
|
|
321
|
-
return /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(trimmed) ? trimmed : 'unknown';
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function normalizeIssueTitle(value) {
|
|
325
|
-
if (typeof value !== 'string' || value.trim() === '') return 'Implementation';
|
|
326
|
-
const normalized = [...value]
|
|
327
|
-
.map((character) => {
|
|
328
|
-
const codePoint = character.codePointAt(0);
|
|
329
|
-
return codePoint < 0x20 || codePoint === 0x7f ? ' ' : character;
|
|
330
|
-
})
|
|
331
|
-
.join('')
|
|
332
|
-
.trim();
|
|
333
|
-
return normalized || 'Implementation';
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
function resolveIssueContext(options) {
|
|
337
|
-
const issueNumber = normalizeIssueNumber(options.issueNumber);
|
|
338
|
-
const issueTitle = normalizeIssueTitle(options.issueTitle);
|
|
339
|
-
const issueReference =
|
|
340
|
-
options.includeIssueReference === false || issueNumber === 'unknown'
|
|
341
|
-
? ''
|
|
342
|
-
: `Closes #${issueNumber}`;
|
|
343
|
-
return { issueNumber, issueTitle, issueReference };
|
|
344
|
-
}
|
|
345
|
-
|
|
346
318
|
/**
|
|
347
319
|
* Resolve GitHub configuration from CLI options and repo settings.
|
|
348
320
|
* Priority: CLI options > repo settings (.zeroshot/settings.json) > defaults
|
|
@@ -355,6 +327,7 @@ function resolveIssueContext(options) {
|
|
|
355
327
|
* @param {string|number} [options.issueNumber] - Typed issue identifier for prompt commands
|
|
356
328
|
* @param {string} [options.issueTitle] - Typed issue title for prompt commands
|
|
357
329
|
* @param {boolean} [options.includeIssueReference] - Include the closing reference in PR text
|
|
330
|
+
* @param {string} [options.prBody] - Literal PR body template with supported issue tokens
|
|
358
331
|
* @returns {Object} Resolved configuration
|
|
359
332
|
*/
|
|
360
333
|
function resolveGitHubConfig(options = {}) {
|
|
@@ -385,16 +358,23 @@ function resolveGitHubConfig(options = {}) {
|
|
|
385
358
|
options.autoMerge === true ||
|
|
386
359
|
(options.autoMerge !== false && parseBool(repoGithub.autoMerge) === true);
|
|
387
360
|
|
|
361
|
+
const issueContext = resolveIssueContext(options);
|
|
362
|
+
|
|
388
363
|
return {
|
|
389
364
|
prBase,
|
|
390
365
|
useMergeQueue,
|
|
391
366
|
closeIssueMode,
|
|
392
367
|
autoMerge,
|
|
393
368
|
gitRemote,
|
|
394
|
-
issueContext
|
|
369
|
+
issueContext,
|
|
370
|
+
prBody: renderPullRequestBody(options.prBody, options),
|
|
395
371
|
};
|
|
396
372
|
}
|
|
397
373
|
|
|
374
|
+
function resolvedPrBody(config, issueContext) {
|
|
375
|
+
return typeof config.prBody === 'string' ? config.prBody : issueContext.issueReference;
|
|
376
|
+
}
|
|
377
|
+
|
|
398
378
|
/**
|
|
399
379
|
* Generate platform-specific configuration based on resolved GitHub config.
|
|
400
380
|
*
|
|
@@ -406,13 +386,15 @@ function getPlatformConfig(platform, config = {}) {
|
|
|
406
386
|
const { prBase, useMergeQueue, closeIssueMode, autoMerge, gitRemote } = config;
|
|
407
387
|
const issueContext = config.issueContext || resolveIssueContext({});
|
|
408
388
|
const issueTitleArgument = quoteShellArgument(`feat: ${issueContext.issueTitle}`);
|
|
409
|
-
const
|
|
389
|
+
const prBodyArgument = quoteShellArgument(resolvedPrBody(config, issueContext));
|
|
410
390
|
|
|
411
391
|
const PLATFORM_CONFIGS = {
|
|
412
392
|
github: {
|
|
413
393
|
prName: 'PR',
|
|
414
394
|
prNameLower: 'pull request',
|
|
415
|
-
createCmd:
|
|
395
|
+
createCmd:
|
|
396
|
+
`gh pr create${prBase ? ` --base ${prBase}` : ''} ` +
|
|
397
|
+
`--title ${issueTitleArgument} --body ${prBodyArgument}`,
|
|
416
398
|
mergeCmd: useMergeQueue
|
|
417
399
|
? `PR_ID="$(timeout 30 gh pr view --json id --jq .id)"
|
|
418
400
|
gh api graphql -f query='mutation($id:ID!){enqueuePullRequest(input:{pullRequestId:$id}){mergeQueueEntry{state}}}' -f id="$PR_ID"
|
|
@@ -434,7 +416,7 @@ for i in $(seq 1 90); do if timeout 30 gh pr view --json mergedAt --jq .mergedAt
|
|
|
434
416
|
gitlab: {
|
|
435
417
|
prName: 'MR',
|
|
436
418
|
prNameLower: 'merge request',
|
|
437
|
-
createCmd: `glab mr create --title ${issueTitleArgument} --description ${
|
|
419
|
+
createCmd: `glab mr create --title ${issueTitleArgument} --description ${prBodyArgument}`,
|
|
438
420
|
mergeCmd: 'glab mr merge --auto-merge',
|
|
439
421
|
mergeFallbackCmd: 'glab mr merge',
|
|
440
422
|
prUrlExample: 'https://gitlab.com/owner/repo/-/merge_requests/123',
|
|
@@ -447,7 +429,7 @@ for i in $(seq 1 90); do if timeout 30 gh pr view --json mergedAt --jq .mergedAt
|
|
|
447
429
|
'azure-devops': {
|
|
448
430
|
prName: 'PR',
|
|
449
431
|
prNameLower: 'pull request',
|
|
450
|
-
createCmd: `az repos pr create --title ${issueTitleArgument} --description ${
|
|
432
|
+
createCmd: `az repos pr create --title ${issueTitleArgument} --description ${prBodyArgument}`,
|
|
451
433
|
mergeCmd: 'az repos pr update --id <PR_ID> --auto-complete true',
|
|
452
434
|
mergeFallbackCmd: 'az repos pr update --id <PR_ID> --status completed',
|
|
453
435
|
prUrlExample: 'https://dev.azure.com/org/project/_git/repo/pullrequest/123',
|
|
@@ -803,6 +785,7 @@ If blocked before creating a ${prName}, output:
|
|
|
803
785
|
* @param {string|number} [options.issueNumber] - Typed issue identifier for prompt commands
|
|
804
786
|
* @param {string} [options.issueTitle] - Typed issue title for prompt commands
|
|
805
787
|
* @param {boolean} [options.includeIssueReference] - Include the closing reference in PR text
|
|
788
|
+
* @param {string} [options.prBody] - Literal PR body template with supported issue tokens
|
|
806
789
|
* @param {Array} [options.requiredQualityGates] - Required handoff quality gates
|
|
807
790
|
* @param {boolean} [options.autoMerge] - Merge the PR (--ship). False stops after PR creation (--pr).
|
|
808
791
|
* @returns {Object} Agent configuration object
|