@produck/agent-toolkit 0.8.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -22
- package/bin/agent-toolkit.mjs +33 -8
- package/bin/build-publish-assets.mjs +132 -24
- package/bin/command/enforce-node-baseline/index.mjs +18 -5
- package/bin/command/preflight/index.mjs +20 -4
- package/bin/command/run-capture/index.mjs +7 -1
- package/bin/command/shared/workspace-validation.mjs +9 -3
- package/bin/command/sync-coverage/index.mjs +103 -48
- package/bin/command/sync-coverage/required-c8-config.json +15 -0
- package/bin/command/sync-editorconfig/index.mjs +2 -1
- package/bin/command/sync-format/index.mjs +92 -37
- package/bin/command/sync-git/index.mjs +97 -35
- package/bin/command/sync-install/index.mjs +10 -3
- package/bin/command/sync-instructions/index.mjs +35 -10
- package/bin/command/sync-lint/eslint.config.template.mjs +32 -0
- package/bin/command/sync-lint/index.mjs +63 -32
- package/bin/command/sync-publish/help.txt +4 -2
- package/bin/command/sync-publish/index.mjs +126 -35
- package/bin/command/validate-commit-msg/index.mjs +46 -25
- package/package.json +5 -3
- package/publish-assets/eslint.config.template.mjs +32 -0
- package/publish-assets/gitignore +3 -0
- package/publish-assets/instructions/produck/20-produck-commit.instructions.md +3 -3
- package/publish-assets/instructions/produck/stale.instructions.md +1 -0
- package/publish-assets/instructions/produck/tooling-version-baseline.json +36 -1
- package/publish-assets/lerna.json +14 -0
- package/publish-assets/prettierrc +11 -0
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Central CLI toolkit for organization-level AI execution workflows.
|
|
|
7
7
|
For a new or existing downstream repository that has not yet applied the
|
|
8
8
|
organization baseline, run:
|
|
9
9
|
|
|
10
|
-
```
|
|
10
|
+
```bash
|
|
11
11
|
npm create @produck/agent-toolkit@latest
|
|
12
12
|
```
|
|
13
13
|
|
|
@@ -38,7 +38,7 @@ After running, add the persistent enforcement entry to the repository
|
|
|
38
38
|
|
|
39
39
|
Then future enforcement runs via:
|
|
40
40
|
|
|
41
|
-
```
|
|
41
|
+
```bash
|
|
42
42
|
npm run produck:baseline
|
|
43
43
|
```
|
|
44
44
|
|
|
@@ -61,13 +61,13 @@ npm run produck:baseline
|
|
|
61
61
|
|
|
62
62
|
Run default mandatory baseline flow in downstream repository root:
|
|
63
63
|
|
|
64
|
-
```
|
|
64
|
+
```bash
|
|
65
65
|
npm exec -- agent-toolkit
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
Equivalent explicit form:
|
|
69
69
|
|
|
70
|
-
```
|
|
70
|
+
```bash
|
|
71
71
|
npm exec -- agent-toolkit enforce-node-baseline --cwd .
|
|
72
72
|
```
|
|
73
73
|
|
|
@@ -104,56 +104,56 @@ Add to downstream repository root `package.json` for one-command enforcement:
|
|
|
104
104
|
|
|
105
105
|
Then run:
|
|
106
106
|
|
|
107
|
-
```
|
|
107
|
+
```bash
|
|
108
108
|
npm run produck:baseline
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
Dry-run to preview changes without writing files:
|
|
112
112
|
|
|
113
|
-
```
|
|
113
|
+
```bash
|
|
114
114
|
npm exec -- agent-toolkit enforce-node-baseline --cwd . --dry-run
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
Check-only mode to validate without writing:
|
|
118
118
|
|
|
119
|
-
```
|
|
119
|
+
```bash
|
|
120
120
|
npm exec -- agent-toolkit enforce-node-baseline --cwd . --check
|
|
121
121
|
```
|
|
122
122
|
|
|
123
123
|
Validate monorepo root `package.json` scripts and workspace structure:
|
|
124
124
|
|
|
125
|
-
```
|
|
125
|
+
```bash
|
|
126
126
|
npm exec -- agent-toolkit preflight --cwd . --check-workspace-package-json package.json
|
|
127
127
|
```
|
|
128
128
|
|
|
129
129
|
Run preflight with required-file and directory guards:
|
|
130
130
|
|
|
131
|
-
```
|
|
131
|
+
```bash
|
|
132
132
|
npm exec -- agent-toolkit preflight --cwd . --require package.json --ensure-dir logs
|
|
133
133
|
```
|
|
134
134
|
|
|
135
135
|
Capture long output safely:
|
|
136
136
|
|
|
137
|
-
```
|
|
137
|
+
```bash
|
|
138
138
|
npm exec -- agent-toolkit run-capture --cwd . --cmd "npm run test" --out logs/test.log
|
|
139
139
|
```
|
|
140
140
|
|
|
141
141
|
Summarize captured output:
|
|
142
142
|
|
|
143
|
-
```
|
|
143
|
+
```bash
|
|
144
144
|
npm exec -- agent-toolkit summarize-log --file logs/test.log --match "FAIL|ERROR"
|
|
145
145
|
```
|
|
146
146
|
|
|
147
147
|
Deploy organization coverage script and pinned local c8 devDependency to workspace packages:
|
|
148
148
|
|
|
149
|
-
```
|
|
149
|
+
```bash
|
|
150
150
|
npm exec -- agent-toolkit sync-coverage --cwd .
|
|
151
151
|
```
|
|
152
152
|
|
|
153
153
|
This command also enforces `scripts.test` in each workspace package.
|
|
154
154
|
If missing, it generates:
|
|
155
155
|
|
|
156
|
-
```
|
|
156
|
+
```bash
|
|
157
157
|
node -e "console.log('No tests configured')"
|
|
158
158
|
```
|
|
159
159
|
|
|
@@ -161,7 +161,7 @@ node -e "console.log('No tests configured')"
|
|
|
161
161
|
|
|
162
162
|
Deploy organization format config and script baseline to repository root:
|
|
163
163
|
|
|
164
|
-
```
|
|
164
|
+
```bash
|
|
165
165
|
npm exec -- agent-toolkit sync-format --cwd .
|
|
166
166
|
```
|
|
167
167
|
|
|
@@ -171,7 +171,7 @@ Prettier, so a separate root `format` script is not required.
|
|
|
171
171
|
|
|
172
172
|
Deploy organization lint config and script baseline to repository root:
|
|
173
173
|
|
|
174
|
-
```
|
|
174
|
+
```bash
|
|
175
175
|
npm exec -- agent-toolkit sync-lint --cwd .
|
|
176
176
|
```
|
|
177
177
|
|
|
@@ -182,7 +182,7 @@ array.
|
|
|
182
182
|
|
|
183
183
|
Deploy root and workspace coverage config plus root `c8` devDependency:
|
|
184
184
|
|
|
185
|
-
```
|
|
185
|
+
```bash
|
|
186
186
|
npm exec -- agent-toolkit sync-coverage --cwd .
|
|
187
187
|
```
|
|
188
188
|
|
|
@@ -195,7 +195,7 @@ branches, functions, lines, and statements.
|
|
|
195
195
|
|
|
196
196
|
Deploy root git attributes baseline:
|
|
197
197
|
|
|
198
|
-
```
|
|
198
|
+
```bash
|
|
199
199
|
npm exec -- agent-toolkit sync-git --cwd .
|
|
200
200
|
```
|
|
201
201
|
|
|
@@ -206,7 +206,7 @@ This command manages root `.gitattributes`, `.husky/pre-commit`,
|
|
|
206
206
|
|
|
207
207
|
Deploy root install script baseline:
|
|
208
208
|
|
|
209
|
-
```
|
|
209
|
+
```bash
|
|
210
210
|
npm exec -- agent-toolkit sync-install --cwd .
|
|
211
211
|
```
|
|
212
212
|
|
|
@@ -215,7 +215,7 @@ This command manages `scripts.produck:install` with value
|
|
|
215
215
|
|
|
216
216
|
Deploy root publish script baseline:
|
|
217
217
|
|
|
218
|
-
```
|
|
218
|
+
```bash
|
|
219
219
|
npm exec -- agent-toolkit sync-publish --cwd .
|
|
220
220
|
```
|
|
221
221
|
|
|
@@ -227,13 +227,13 @@ otherwise it falls back to `lerna publish`.
|
|
|
227
227
|
|
|
228
228
|
Validate commit message format:
|
|
229
229
|
|
|
230
|
-
```
|
|
230
|
+
```bash
|
|
231
231
|
npm exec -- agent-toolkit validate-commit-msg --file .git/COMMIT_EDITMSG
|
|
232
232
|
```
|
|
233
233
|
|
|
234
234
|
Manual per-repository instruction distribution (write .github/instructions/produck/\*.instructions.md):
|
|
235
235
|
|
|
236
|
-
```
|
|
236
|
+
```bash
|
|
237
237
|
npm exec -- agent-toolkit sync-instructions --cwd .
|
|
238
238
|
```
|
|
239
239
|
|
|
@@ -246,7 +246,7 @@ Legacy repository bootstrap behavior:
|
|
|
246
246
|
|
|
247
247
|
Use organization source directory instead of built-in assets:
|
|
248
248
|
|
|
249
|
-
```
|
|
249
|
+
```bash
|
|
250
250
|
npm exec -- agent-toolkit sync-instructions --cwd . --source path/to/org/.github/distribution/produck --force --prune
|
|
251
251
|
```
|
|
252
252
|
|
package/bin/agent-toolkit.mjs
CHANGED
|
@@ -4,23 +4,44 @@ import {
|
|
|
4
4
|
printEnforceNodeBaselineHelp,
|
|
5
5
|
runEnforceNodeBaseline,
|
|
6
6
|
} from './command/enforce-node-baseline/index.mjs';
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
printPreflightHelp,
|
|
9
|
+
runPreflight,
|
|
10
|
+
} from './command/preflight/index.mjs';
|
|
11
|
+
import {
|
|
12
|
+
printRunCaptureHelp,
|
|
13
|
+
runCapture,
|
|
14
|
+
} from './command/run-capture/index.mjs';
|
|
15
|
+
import {
|
|
16
|
+
printSummarizeHelp,
|
|
17
|
+
runSummarize,
|
|
18
|
+
} from './command/summarize-log/index.mjs';
|
|
19
|
+
import {
|
|
20
|
+
printSyncCoverageHelp,
|
|
21
|
+
runSyncCoverage,
|
|
22
|
+
} from './command/sync-coverage/index.mjs';
|
|
23
|
+
import {
|
|
24
|
+
printSyncInstallHelp,
|
|
25
|
+
runSyncInstall,
|
|
26
|
+
} from './command/sync-install/index.mjs';
|
|
12
27
|
import {
|
|
13
28
|
printSyncInstructionsHelp,
|
|
14
29
|
runSyncInstructions,
|
|
15
30
|
} from './command/sync-instructions/index.mjs';
|
|
16
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
printSyncFormatHelp,
|
|
33
|
+
runSyncFormat,
|
|
34
|
+
} from './command/sync-format/index.mjs';
|
|
17
35
|
import {
|
|
18
36
|
printSyncEditorconfigHelp,
|
|
19
37
|
runSyncEditorconfig,
|
|
20
38
|
} from './command/sync-editorconfig/index.mjs';
|
|
21
39
|
import { printSyncLintHelp, runSyncLint } from './command/sync-lint/index.mjs';
|
|
22
40
|
import { printSyncGitHelp, runSyncGit } from './command/sync-git/index.mjs';
|
|
23
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
printSyncPublishHelp,
|
|
43
|
+
runSyncPublish,
|
|
44
|
+
} from './command/sync-publish/index.mjs';
|
|
24
45
|
import { hasFlag, parseCommonArgs } from './command/shared/args.mjs';
|
|
25
46
|
import {
|
|
26
47
|
printValidateCommitMsgHelp,
|
|
@@ -98,7 +119,11 @@ function main() {
|
|
|
98
119
|
const command = parsed.positional[0] || '';
|
|
99
120
|
const options = parsed.options;
|
|
100
121
|
|
|
101
|
-
if (
|
|
122
|
+
if (
|
|
123
|
+
command === '--help' ||
|
|
124
|
+
command === '-h' ||
|
|
125
|
+
(!command && hasFlag(options, '--help'))
|
|
126
|
+
) {
|
|
102
127
|
printMainHelp();
|
|
103
128
|
process.exit(0);
|
|
104
129
|
}
|
|
@@ -7,15 +7,48 @@ const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
|
7
7
|
const PACKAGE_ROOT = path.resolve(SCRIPT_DIR, '..');
|
|
8
8
|
const REPO_ROOT = path.resolve(PACKAGE_ROOT, '../..');
|
|
9
9
|
const SOURCE_DIR = path.resolve(REPO_ROOT, '.github/distribution/produck');
|
|
10
|
-
const OUTPUT_DIR = path.resolve(
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
const OUTPUT_DIR = path.resolve(
|
|
11
|
+
PACKAGE_ROOT,
|
|
12
|
+
'publish-assets/instructions/produck',
|
|
13
|
+
);
|
|
14
|
+
const SOURCE_TOOLING_BASELINE_PATH = path.resolve(
|
|
15
|
+
SOURCE_DIR,
|
|
16
|
+
'tooling-version-baseline.json',
|
|
17
|
+
);
|
|
18
|
+
const OUTPUT_TOOLING_BASELINE_PATH = path.resolve(
|
|
19
|
+
OUTPUT_DIR,
|
|
20
|
+
'tooling-version-baseline.json',
|
|
21
|
+
);
|
|
13
22
|
const SOURCE_GITATTRIBUTES_PATH = path.resolve(REPO_ROOT, '.gitattributes');
|
|
14
23
|
const SOURCE_GITIGNORE_PATH = path.resolve(REPO_ROOT, '.gitignore');
|
|
24
|
+
const SOURCE_PRETTIERRC_PATH = path.resolve(REPO_ROOT, '.prettierrc');
|
|
15
25
|
const SOURCE_PRETTIERIGNORE_PATH = path.resolve(REPO_ROOT, '.prettierignore');
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
26
|
+
const SOURCE_LERNA_PATH = path.resolve(REPO_ROOT, 'lerna.json');
|
|
27
|
+
const SOURCE_ESLINT_CONFIG_PATH = path.resolve(REPO_ROOT, 'eslint.config.mjs');
|
|
28
|
+
const OUTPUT_ESLINT_CONFIG_TEMPLATE_PATH = path.resolve(
|
|
29
|
+
PACKAGE_ROOT,
|
|
30
|
+
'publish-assets/eslint.config.template.mjs',
|
|
31
|
+
);
|
|
32
|
+
const OUTPUT_GITATTRIBUTES_PATH = path.resolve(
|
|
33
|
+
PACKAGE_ROOT,
|
|
34
|
+
'publish-assets/gitattributes',
|
|
35
|
+
);
|
|
36
|
+
const OUTPUT_GITIGNORE_PATH = path.resolve(
|
|
37
|
+
PACKAGE_ROOT,
|
|
38
|
+
'publish-assets/gitignore',
|
|
39
|
+
);
|
|
40
|
+
const OUTPUT_PRETTIERRC_PATH = path.resolve(
|
|
41
|
+
PACKAGE_ROOT,
|
|
42
|
+
'publish-assets/prettierrc',
|
|
43
|
+
);
|
|
44
|
+
const OUTPUT_PRETTIERIGNORE_PATH = path.resolve(
|
|
45
|
+
PACKAGE_ROOT,
|
|
46
|
+
'publish-assets/prettierignore',
|
|
47
|
+
);
|
|
48
|
+
const OUTPUT_LERNA_PATH = path.resolve(
|
|
49
|
+
PACKAGE_ROOT,
|
|
50
|
+
'publish-assets/lerna.json',
|
|
51
|
+
);
|
|
19
52
|
const LEGACY_OUTPUT_PATH = path.resolve(
|
|
20
53
|
PACKAGE_ROOT,
|
|
21
54
|
'publish-assets/instructions/org.instructions.md',
|
|
@@ -49,14 +82,20 @@ function validateSourceFile(fileName, text) {
|
|
|
49
82
|
|
|
50
83
|
function readAndValidateToolingBaseline() {
|
|
51
84
|
if (!fs.existsSync(SOURCE_TOOLING_BASELINE_PATH)) {
|
|
52
|
-
throw new Error(
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Missing tooling baseline source file: ${SOURCE_TOOLING_BASELINE_PATH}`,
|
|
87
|
+
);
|
|
53
88
|
}
|
|
54
89
|
|
|
55
90
|
let baseline;
|
|
56
91
|
try {
|
|
57
|
-
baseline = JSON.parse(
|
|
92
|
+
baseline = JSON.parse(
|
|
93
|
+
fs.readFileSync(SOURCE_TOOLING_BASELINE_PATH, 'utf8'),
|
|
94
|
+
);
|
|
58
95
|
} catch {
|
|
59
|
-
throw new Error(
|
|
96
|
+
throw new Error(
|
|
97
|
+
`Invalid tooling baseline JSON: ${SOURCE_TOOLING_BASELINE_PATH}`,
|
|
98
|
+
);
|
|
60
99
|
}
|
|
61
100
|
|
|
62
101
|
const c8Version = baseline?.tools?.c8?.version;
|
|
@@ -64,27 +103,42 @@ function readAndValidateToolingBaseline() {
|
|
|
64
103
|
const coverageScriptTemplate = baseline?.coverage?.scriptTemplate;
|
|
65
104
|
|
|
66
105
|
if (typeof baseline.schemaVersion !== 'number') {
|
|
67
|
-
throw new Error(
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Invalid tooling baseline schemaVersion in: ${SOURCE_TOOLING_BASELINE_PATH}`,
|
|
108
|
+
);
|
|
68
109
|
}
|
|
69
110
|
|
|
70
111
|
if (typeof c8Version !== 'string' || c8Version.trim() === '') {
|
|
71
|
-
throw new Error(
|
|
112
|
+
throw new Error(
|
|
113
|
+
`Invalid tools.c8.version in: ${SOURCE_TOOLING_BASELINE_PATH}`,
|
|
114
|
+
);
|
|
72
115
|
}
|
|
73
116
|
|
|
74
117
|
if (typeof lernaVersion !== 'string' || lernaVersion.trim() === '') {
|
|
75
|
-
throw new Error(
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Invalid tools.lerna.version in: ${SOURCE_TOOLING_BASELINE_PATH}`,
|
|
120
|
+
);
|
|
76
121
|
}
|
|
77
122
|
|
|
78
|
-
if (
|
|
79
|
-
|
|
123
|
+
if (
|
|
124
|
+
typeof coverageScriptTemplate !== 'string' ||
|
|
125
|
+
coverageScriptTemplate.trim() === ''
|
|
126
|
+
) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Invalid coverage.scriptTemplate in: ${SOURCE_TOOLING_BASELINE_PATH}`,
|
|
129
|
+
);
|
|
80
130
|
}
|
|
81
131
|
|
|
82
|
-
const eslintRulesPkgPath = path.resolve(
|
|
132
|
+
const eslintRulesPkgPath = path.resolve(
|
|
133
|
+
PACKAGE_ROOT,
|
|
134
|
+
'../eslint-rules/package.json',
|
|
135
|
+
);
|
|
83
136
|
if (fs.existsSync(eslintRulesPkgPath)) {
|
|
84
|
-
const eslintRulesPkg = JSON.parse(
|
|
137
|
+
const eslintRulesPkg = JSON.parse(
|
|
138
|
+
fs.readFileSync(eslintRulesPkgPath, 'utf8'),
|
|
139
|
+
);
|
|
85
140
|
const version = eslintRulesPkg.version;
|
|
86
141
|
if (typeof version === 'string' && version.trim()) {
|
|
87
|
-
if (!baseline.tools) baseline.tools = {};
|
|
88
142
|
baseline.tools['@produck/eslint-rules'] = {
|
|
89
143
|
version,
|
|
90
144
|
policy: 'pinned',
|
|
@@ -121,11 +175,21 @@ function readSourceEntries() {
|
|
|
121
175
|
});
|
|
122
176
|
}
|
|
123
177
|
|
|
178
|
+
function generateEslintConfigTemplate() {
|
|
179
|
+
return normalize(
|
|
180
|
+
fs
|
|
181
|
+
.readFileSync(SOURCE_ESLINT_CONFIG_PATH, 'utf8')
|
|
182
|
+
.replace(
|
|
183
|
+
/from\s+['"]\.\/packages\/eslint-rules\/src\/index\.mjs['"]/g,
|
|
184
|
+
'from \'@produck/eslint-rules\'',
|
|
185
|
+
),
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
124
189
|
function cleanStaleManagedFiles(expectedNames) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const existing = fs.readdirSync(OUTPUT_DIR).filter((name) => name.endsWith('.instructions.md'));
|
|
190
|
+
const existing = fs
|
|
191
|
+
.readdirSync(OUTPUT_DIR)
|
|
192
|
+
.filter((name) => name.endsWith('.instructions.md'));
|
|
129
193
|
for (const name of existing) {
|
|
130
194
|
if (expectedNames.has(name)) {
|
|
131
195
|
continue;
|
|
@@ -158,13 +222,28 @@ function run() {
|
|
|
158
222
|
);
|
|
159
223
|
|
|
160
224
|
if (!fs.existsSync(SOURCE_GITATTRIBUTES_PATH)) {
|
|
161
|
-
throw new Error(
|
|
225
|
+
throw new Error(
|
|
226
|
+
`Missing source .gitattributes: ${SOURCE_GITATTRIBUTES_PATH}`,
|
|
227
|
+
);
|
|
162
228
|
}
|
|
163
229
|
if (!fs.existsSync(SOURCE_GITIGNORE_PATH)) {
|
|
164
230
|
throw new Error(`Missing source .gitignore: ${SOURCE_GITIGNORE_PATH}`);
|
|
165
231
|
}
|
|
232
|
+
if (!fs.existsSync(SOURCE_PRETTIERRC_PATH)) {
|
|
233
|
+
throw new Error(`Missing source .prettierrc: ${SOURCE_PRETTIERRC_PATH}`);
|
|
234
|
+
}
|
|
166
235
|
if (!fs.existsSync(SOURCE_PRETTIERIGNORE_PATH)) {
|
|
167
|
-
throw new Error(
|
|
236
|
+
throw new Error(
|
|
237
|
+
`Missing source .prettierignore: ${SOURCE_PRETTIERIGNORE_PATH}`,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
if (!fs.existsSync(SOURCE_LERNA_PATH)) {
|
|
241
|
+
throw new Error(`Missing source lerna.json: ${SOURCE_LERNA_PATH}`);
|
|
242
|
+
}
|
|
243
|
+
if (!fs.existsSync(SOURCE_ESLINT_CONFIG_PATH)) {
|
|
244
|
+
throw new Error(
|
|
245
|
+
`Missing source eslint.config.mjs: ${SOURCE_ESLINT_CONFIG_PATH}`,
|
|
246
|
+
);
|
|
168
247
|
}
|
|
169
248
|
|
|
170
249
|
fs.writeFileSync(
|
|
@@ -181,7 +260,18 @@ function run() {
|
|
|
181
260
|
normalize(fs.readFileSync(SOURCE_GITIGNORE_PATH, 'utf8')),
|
|
182
261
|
'utf8',
|
|
183
262
|
);
|
|
184
|
-
process.stdout.write(
|
|
263
|
+
process.stdout.write(
|
|
264
|
+
`Generated ${OUTPUT_GITIGNORE_PATH} from ${SOURCE_GITIGNORE_PATH}\n`,
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
fs.writeFileSync(
|
|
268
|
+
OUTPUT_PRETTIERRC_PATH,
|
|
269
|
+
normalize(fs.readFileSync(SOURCE_PRETTIERRC_PATH, 'utf8')),
|
|
270
|
+
'utf8',
|
|
271
|
+
);
|
|
272
|
+
process.stdout.write(
|
|
273
|
+
`Generated ${OUTPUT_PRETTIERRC_PATH} from ${SOURCE_PRETTIERRC_PATH}\n`,
|
|
274
|
+
);
|
|
185
275
|
|
|
186
276
|
fs.writeFileSync(
|
|
187
277
|
OUTPUT_PRETTIERIGNORE_PATH,
|
|
@@ -192,6 +282,24 @@ function run() {
|
|
|
192
282
|
`Generated ${OUTPUT_PRETTIERIGNORE_PATH} from ${SOURCE_PRETTIERIGNORE_PATH}\n`,
|
|
193
283
|
);
|
|
194
284
|
|
|
285
|
+
fs.writeFileSync(
|
|
286
|
+
OUTPUT_LERNA_PATH,
|
|
287
|
+
normalize(fs.readFileSync(SOURCE_LERNA_PATH, 'utf8')),
|
|
288
|
+
'utf8',
|
|
289
|
+
);
|
|
290
|
+
process.stdout.write(
|
|
291
|
+
`Generated ${OUTPUT_LERNA_PATH} from ${SOURCE_LERNA_PATH}\n`,
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
fs.writeFileSync(
|
|
295
|
+
OUTPUT_ESLINT_CONFIG_TEMPLATE_PATH,
|
|
296
|
+
generateEslintConfigTemplate(),
|
|
297
|
+
'utf8',
|
|
298
|
+
);
|
|
299
|
+
process.stdout.write(
|
|
300
|
+
`Generated ${OUTPUT_ESLINT_CONFIG_TEMPLATE_PATH} from ${SOURCE_ESLINT_CONFIG_PATH}\n`,
|
|
301
|
+
);
|
|
302
|
+
|
|
195
303
|
cleanStaleManagedFiles(expectedNames);
|
|
196
304
|
|
|
197
305
|
if (fs.existsSync(LEGACY_OUTPUT_PATH)) {
|
|
@@ -16,15 +16,15 @@ export function printEnforceNodeBaselineHelp() {
|
|
|
16
16
|
|
|
17
17
|
function parseJsonOrNull(text) {
|
|
18
18
|
const trimmed = text.trim();
|
|
19
|
-
if (
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
// JSON.parse throws if subcommand stdout is unexpectedly non-JSON (e.g. process
|
|
20
|
+
// crash). Tests always receive structured JSON reports from subcommands.
|
|
21
|
+
/* c8 ignore start */
|
|
23
22
|
try {
|
|
24
23
|
return JSON.parse(trimmed);
|
|
25
24
|
} catch {
|
|
26
25
|
return null;
|
|
27
26
|
}
|
|
27
|
+
/* c8 ignore stop */
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function runToolkitSubcommand(cwd, args) {
|
|
@@ -33,9 +33,13 @@ function runToolkitSubcommand(cwd, args) {
|
|
|
33
33
|
encoding: 'utf8',
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
+
// spawnSync returns null stdout/stderr and a non-numeric status when the child
|
|
37
|
+
// is killed by a signal. Normal test runs never trigger this condition.
|
|
38
|
+
/* c8 ignore start */
|
|
36
39
|
const stdout = String(result.stdout || '');
|
|
37
40
|
const stderr = String(result.stderr || '');
|
|
38
41
|
const status = typeof result.status === 'number' ? result.status : 1;
|
|
42
|
+
/* c8 ignore stop */
|
|
39
43
|
|
|
40
44
|
return {
|
|
41
45
|
args,
|
|
@@ -56,6 +60,9 @@ function buildStepReport(name, stepResult) {
|
|
|
56
60
|
status: stepResult.status,
|
|
57
61
|
ok: stepResult.ok,
|
|
58
62
|
report: stepResult.report,
|
|
63
|
+
// The false branch (raw stdout) is taken only when the subcommand produces no
|
|
64
|
+
// parseable JSON report. Tests always receive structured JSON output.
|
|
65
|
+
/* c8 ignore next */
|
|
59
66
|
stdout: hasParsedReport ? '' : stepResult.stdout,
|
|
60
67
|
stderr: stepResult.stderr,
|
|
61
68
|
};
|
|
@@ -98,7 +105,13 @@ export function runEnforceNodeBaseline(options) {
|
|
|
98
105
|
syncInstructionsArgs.push('--dry-run');
|
|
99
106
|
}
|
|
100
107
|
|
|
101
|
-
const preflightArgs = [
|
|
108
|
+
const preflightArgs = [
|
|
109
|
+
'preflight',
|
|
110
|
+
'--cwd',
|
|
111
|
+
cwd,
|
|
112
|
+
'--require',
|
|
113
|
+
'package.json',
|
|
114
|
+
];
|
|
102
115
|
if (mode !== 'sync') {
|
|
103
116
|
preflightArgs.push('--check-workspace-package-json', 'package.json');
|
|
104
117
|
}
|
|
@@ -9,7 +9,12 @@ import { validateWorkspaceShape } from '../shared/workspace-validation.mjs';
|
|
|
9
9
|
const COMMAND_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
const HELP_FILE = path.resolve(COMMAND_DIR, 'help.txt');
|
|
11
11
|
const REQUIRED_WORKSPACE_FIELDS = ['private', 'workspaces', 'scripts'];
|
|
12
|
-
const REQUIRED_WORKSPACE_SCRIPTS = [
|
|
12
|
+
const REQUIRED_WORKSPACE_SCRIPTS = [
|
|
13
|
+
'produck:install',
|
|
14
|
+
'test',
|
|
15
|
+
'produck:coverage',
|
|
16
|
+
'produck:lint',
|
|
17
|
+
];
|
|
13
18
|
|
|
14
19
|
export function printPreflightHelp() {
|
|
15
20
|
printTextResource(HELP_FILE);
|
|
@@ -43,7 +48,11 @@ function validateWorkspacePackageJson(cwd, checkPath) {
|
|
|
43
48
|
return check;
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
const shape = validateWorkspaceShape(
|
|
51
|
+
const shape = validateWorkspaceShape(
|
|
52
|
+
json,
|
|
53
|
+
REQUIRED_WORKSPACE_FIELDS,
|
|
54
|
+
REQUIRED_WORKSPACE_SCRIPTS,
|
|
55
|
+
);
|
|
47
56
|
check.missingFields = shape.missingFields;
|
|
48
57
|
check.scriptTypeValid = shape.scriptTypeValid;
|
|
49
58
|
check.missingScripts = shape.missingScripts;
|
|
@@ -57,7 +66,11 @@ export function runPreflight(options) {
|
|
|
57
66
|
const cwd = path.resolve(getSingle(options, '--cwd', process.cwd()));
|
|
58
67
|
const requireTargets = options['--require'] || [];
|
|
59
68
|
const ensureDirs = options['--ensure-dir'] || [];
|
|
60
|
-
const checkWorkspacePackageJson = getSingle(
|
|
69
|
+
const checkWorkspacePackageJson = getSingle(
|
|
70
|
+
options,
|
|
71
|
+
'--check-workspace-package-json',
|
|
72
|
+
'',
|
|
73
|
+
);
|
|
61
74
|
const dryRun = hasFlag(options, '--dry-run');
|
|
62
75
|
const jsonFile = getSingle(options, '--json', '');
|
|
63
76
|
|
|
@@ -99,7 +112,10 @@ export function runPreflight(options) {
|
|
|
99
112
|
}
|
|
100
113
|
|
|
101
114
|
if (checkWorkspacePackageJson) {
|
|
102
|
-
const workspaceCheck = validateWorkspacePackageJson(
|
|
115
|
+
const workspaceCheck = validateWorkspacePackageJson(
|
|
116
|
+
cwd,
|
|
117
|
+
checkWorkspacePackageJson,
|
|
118
|
+
);
|
|
103
119
|
report.workspacePackageJson = workspaceCheck;
|
|
104
120
|
if (!workspaceCheck.ok) {
|
|
105
121
|
report.ok = false;
|
|
@@ -26,7 +26,9 @@ export function runCapture(options) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
if (!allowPipe && cmd.includes('|')) {
|
|
29
|
-
console.error(
|
|
29
|
+
console.error(
|
|
30
|
+
'Blocked command containing pipe. Use --allow-pipe if needed.',
|
|
31
|
+
);
|
|
30
32
|
process.exit(2);
|
|
31
33
|
}
|
|
32
34
|
|
|
@@ -62,9 +64,13 @@ export function runCapture(options) {
|
|
|
62
64
|
outStream.write(chunk);
|
|
63
65
|
});
|
|
64
66
|
|
|
67
|
+
// With shell: true the shell binary is always available, so the 'error' event
|
|
68
|
+
// (OS-level spawn failure) cannot be triggered in tests.
|
|
69
|
+
/* c8 ignore start */
|
|
65
70
|
child.on('error', (error) => {
|
|
66
71
|
outStream.write(`\n[agent-toolkit] spawn error: ${error.message}\n`);
|
|
67
72
|
});
|
|
73
|
+
/* c8 ignore stop */
|
|
68
74
|
|
|
69
75
|
child.on('close', (code, signal) => {
|
|
70
76
|
const endAt = Date.now();
|
|
@@ -18,12 +18,18 @@ export function validateWorkspaceShape(pkg, requiredFields, requiredScripts) {
|
|
|
18
18
|
const missingFields = findMissingKeys(pkg, requiredFields);
|
|
19
19
|
const scripts = toObjectRecord(pkg.scripts);
|
|
20
20
|
const scriptTypeValid =
|
|
21
|
-
typeof pkg.scripts === 'object' &&
|
|
22
|
-
|
|
21
|
+
typeof pkg.scripts === 'object' &&
|
|
22
|
+
pkg.scripts !== null &&
|
|
23
|
+
!Array.isArray(pkg.scripts);
|
|
24
|
+
const missingScripts = scriptTypeValid
|
|
25
|
+
? findMissingKeys(scripts, requiredScripts)
|
|
26
|
+
: [];
|
|
23
27
|
|
|
24
28
|
const workspacesIsArray = Array.isArray(pkg.workspaces);
|
|
25
29
|
const wildcardWorkspaces = workspacesIsArray
|
|
26
|
-
? pkg.workspaces
|
|
30
|
+
? pkg.workspaces
|
|
31
|
+
.map((entry) => String(entry))
|
|
32
|
+
.filter((entry) => GLOB_TOKEN_PATTERN.test(entry))
|
|
27
33
|
: ['<non-array-workspaces>'];
|
|
28
34
|
|
|
29
35
|
const privateIsTrue = pkg.private === true;
|