@produck/agent-toolkit 0.8.1 → 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 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
 
@@ -4,23 +4,44 @@ import {
4
4
  printEnforceNodeBaselineHelp,
5
5
  runEnforceNodeBaseline,
6
6
  } from './command/enforce-node-baseline/index.mjs';
7
- import { printPreflightHelp, runPreflight } from './command/preflight/index.mjs';
8
- import { printRunCaptureHelp, runCapture } from './command/run-capture/index.mjs';
9
- import { printSummarizeHelp, runSummarize } from './command/summarize-log/index.mjs';
10
- import { printSyncCoverageHelp, runSyncCoverage } from './command/sync-coverage/index.mjs';
11
- import { printSyncInstallHelp, runSyncInstall } from './command/sync-install/index.mjs';
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 { printSyncFormatHelp, runSyncFormat } from './command/sync-format/index.mjs';
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 { printSyncPublishHelp, runSyncPublish } from './command/sync-publish/index.mjs';
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 (command === '--help' || command === '-h' || (!command && hasFlag(options, '--help'))) {
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(PACKAGE_ROOT, 'publish-assets/instructions/produck');
11
- const SOURCE_TOOLING_BASELINE_PATH = path.resolve(SOURCE_DIR, 'tooling-version-baseline.json');
12
- const OUTPUT_TOOLING_BASELINE_PATH = path.resolve(OUTPUT_DIR, 'tooling-version-baseline.json');
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 OUTPUT_GITATTRIBUTES_PATH = path.resolve(PACKAGE_ROOT, 'publish-assets/gitattributes');
17
- const OUTPUT_GITIGNORE_PATH = path.resolve(PACKAGE_ROOT, 'publish-assets/gitignore');
18
- const OUTPUT_PRETTIERIGNORE_PATH = path.resolve(PACKAGE_ROOT, 'publish-assets/prettierignore');
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(`Missing tooling baseline source file: ${SOURCE_TOOLING_BASELINE_PATH}`);
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(fs.readFileSync(SOURCE_TOOLING_BASELINE_PATH, 'utf8'));
92
+ baseline = JSON.parse(
93
+ fs.readFileSync(SOURCE_TOOLING_BASELINE_PATH, 'utf8'),
94
+ );
58
95
  } catch {
59
- throw new Error(`Invalid tooling baseline JSON: ${SOURCE_TOOLING_BASELINE_PATH}`);
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(`Invalid tooling baseline schemaVersion in: ${SOURCE_TOOLING_BASELINE_PATH}`);
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(`Invalid tools.c8.version in: ${SOURCE_TOOLING_BASELINE_PATH}`);
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(`Invalid tools.lerna.version in: ${SOURCE_TOOLING_BASELINE_PATH}`);
118
+ throw new Error(
119
+ `Invalid tools.lerna.version in: ${SOURCE_TOOLING_BASELINE_PATH}`,
120
+ );
76
121
  }
77
122
 
78
- if (typeof coverageScriptTemplate !== 'string' || coverageScriptTemplate.trim() === '') {
79
- throw new Error(`Invalid coverage.scriptTemplate in: ${SOURCE_TOOLING_BASELINE_PATH}`);
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(PACKAGE_ROOT, '../eslint-rules/package.json');
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(fs.readFileSync(eslintRulesPkgPath, 'utf8'));
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
- if (!fs.existsSync(OUTPUT_DIR)) {
126
- return;
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(`Missing source .gitattributes: ${SOURCE_GITATTRIBUTES_PATH}`);
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(`Missing source .prettierignore: ${SOURCE_PRETTIERIGNORE_PATH}`);
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(`Generated ${OUTPUT_GITIGNORE_PATH} from ${SOURCE_GITIGNORE_PATH}\n`);
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 (!trimmed) {
20
- return null;
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 = ['preflight', '--cwd', cwd, '--require', 'package.json'];
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 = ['produck:install', 'test', 'produck:coverage', 'produck:lint'];
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(json, REQUIRED_WORKSPACE_FIELDS, REQUIRED_WORKSPACE_SCRIPTS);
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(options, '--check-workspace-package-json', '');
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(cwd, checkWorkspacePackageJson);
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('Blocked command containing pipe. Use --allow-pipe if needed.');
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' && pkg.scripts !== null && !Array.isArray(pkg.scripts);
22
- const missingScripts = scriptTypeValid ? findMissingKeys(scripts, requiredScripts) : [];
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.map((entry) => String(entry)).filter((entry) => GLOB_TOKEN_PATTERN.test(entry))
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;