@produck/agent-toolkit 0.6.0 → 0.8.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.
Files changed (36) hide show
  1. package/README.md +74 -43
  2. package/bin/agent-toolkit.mjs +26 -33
  3. package/bin/build-publish-assets.mjs +54 -5
  4. package/bin/command/enforce-node-baseline/help.txt +12 -10
  5. package/bin/command/enforce-node-baseline/index.mjs +23 -15
  6. package/bin/command/main/help.txt +6 -5
  7. package/bin/command/preflight/help.txt +1 -1
  8. package/bin/command/preflight/index.mjs +1 -1
  9. package/bin/command/{sync-coverage-script → sync-coverage}/help.txt +2 -1
  10. package/bin/command/{sync-coverage-script → sync-coverage}/index.mjs +116 -19
  11. package/bin/command/sync-editorconfig/index.mjs +10 -153
  12. package/bin/command/{sync-prettier-config → sync-format}/help.txt +4 -2
  13. package/bin/command/sync-format/index.mjs +222 -0
  14. package/bin/command/{sync-workspace-config → sync-git}/help.txt +10 -6
  15. package/bin/command/sync-git/index.mjs +424 -0
  16. package/bin/command/sync-install/help.txt +14 -0
  17. package/bin/command/{sync-prettier-config → sync-install}/index.mjs +26 -50
  18. package/bin/command/sync-instructions/index.mjs +2 -22
  19. package/bin/command/{sync-eslint-config → sync-lint}/help.txt +2 -2
  20. package/bin/command/{sync-eslint-config → sync-lint}/index.mjs +3 -4
  21. package/bin/command/sync-publish/help.txt +18 -0
  22. package/bin/command/sync-publish/index.mjs +157 -0
  23. package/bin/command/validate-commit-msg/index.mjs +30 -2
  24. package/package.json +3 -5
  25. package/publish-assets/gitattributes +5 -0
  26. package/publish-assets/gitignore +137 -0
  27. package/publish-assets/instructions/produck/00-produck-base.instructions.md +44 -58
  28. package/publish-assets/instructions/produck/10-produck-node.instructions.md +59 -82
  29. package/publish-assets/instructions/produck/12-produck-test.instructions.md +94 -0
  30. package/publish-assets/instructions/produck/15-produck-workspace.instructions.md +17 -50
  31. package/publish-assets/instructions/produck/20-produck-commit.instructions.md +2 -2
  32. package/publish-assets/instructions/produck/tooling-version-baseline.json +14 -2
  33. package/publish-assets/prettierignore +2 -0
  34. package/bin/command/sync-husky-hooks/help.txt +0 -14
  35. package/bin/command/sync-husky-hooks/index.mjs +0 -89
  36. package/bin/command/sync-workspace-config/index.mjs +0 -290
@@ -0,0 +1,157 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ import { getSingle, hasFlag } from '../shared/args.mjs';
6
+ import { printTextResource } from '../shared/text-resource.mjs';
7
+
8
+ const COMMAND_DIR = path.dirname(fileURLToPath(import.meta.url));
9
+ const HELP_FILE = path.resolve(COMMAND_DIR, 'help.txt');
10
+ const LERNA_CONFIG_FILE = 'lerna.json';
11
+
12
+ const REQUIRED_PUBLISH_CHECK_SCRIPT_KEY = 'produck:publish:check';
13
+ const REQUIRED_PUBLISH_CHECK_SCRIPT_VALUE =
14
+ 'npm run produck:install && npm run produck:format && npm run produck:lint && npm run produck:coverage';
15
+ const USER_PUBLISH_SCRIPT_KEY = 'publish';
16
+ const REQUIRED_PUBLISH_SCRIPT_KEY = 'produck:publish';
17
+ const REQUIRED_PUBLISH_SCRIPT_FALLBACK_VALUE = 'npm run produck:publish:check && lerna publish';
18
+
19
+ const REQUIRED_LERNA_DEFAULT_CONFIG = `${JSON.stringify(
20
+ {
21
+ $schema: 'node_modules/lerna/schemas/lerna-schema.json',
22
+ version: 'independent',
23
+ },
24
+ null,
25
+ 2,
26
+ )}\n`;
27
+
28
+ export function printSyncPublishHelp() {
29
+ printTextResource(HELP_FILE);
30
+ }
31
+
32
+ function parseJsonFile(filePath, label) {
33
+ try {
34
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
35
+ } catch {
36
+ console.error(`${label} is not valid JSON: ${filePath}`);
37
+ process.exit(2);
38
+ }
39
+ }
40
+
41
+ function buildRequiredPublishScriptValue(hasUserPublishScript) {
42
+ if (hasUserPublishScript) {
43
+ return 'npm run produck:publish:check && npm run publish --';
44
+ }
45
+
46
+ return REQUIRED_PUBLISH_SCRIPT_FALLBACK_VALUE;
47
+ }
48
+
49
+ export function runSyncPublish(options) {
50
+ const cwd = path.resolve(getSingle(options, '--cwd', process.cwd()));
51
+ const check = hasFlag(options, '--check');
52
+ const dryRun = hasFlag(options, '--dry-run') && !check;
53
+ const jsonFile = getSingle(options, '--json', '');
54
+ const mode = check ? 'check' : dryRun ? 'dry-run' : 'sync';
55
+
56
+ if (!fs.existsSync(cwd)) {
57
+ console.error(`CWD does not exist: ${cwd}`);
58
+ process.exit(2);
59
+ }
60
+
61
+ const lernaConfigPath = path.resolve(cwd, LERNA_CONFIG_FILE);
62
+ const lernaExistedBefore = fs.existsSync(lernaConfigPath);
63
+ let lernaDefaultCreated = false;
64
+
65
+ if (!lernaExistedBefore) {
66
+ if (mode === 'sync') {
67
+ fs.writeFileSync(lernaConfigPath, REQUIRED_LERNA_DEFAULT_CONFIG, 'utf8');
68
+ lernaDefaultCreated = true;
69
+ }
70
+ } else {
71
+ const lernaConfig = parseJsonFile(lernaConfigPath, 'lerna.json');
72
+
73
+ if (typeof lernaConfig.version !== 'string') {
74
+ console.error(`lerna.json must have a "version" field: ${lernaConfigPath}`);
75
+ process.exit(2);
76
+ }
77
+ }
78
+
79
+ const rootPackageJsonPath = path.resolve(cwd, 'package.json');
80
+ if (!fs.existsSync(rootPackageJsonPath)) {
81
+ console.error(`Root package.json does not exist: ${rootPackageJsonPath}`);
82
+ process.exit(2);
83
+ }
84
+
85
+ const pkg = parseJsonFile(rootPackageJsonPath, 'Root package.json');
86
+ const scripts =
87
+ pkg.scripts && typeof pkg.scripts === 'object' && !Array.isArray(pkg.scripts)
88
+ ? { ...pkg.scripts }
89
+ : {};
90
+ const hasUserPublishScript =
91
+ typeof scripts[USER_PUBLISH_SCRIPT_KEY] === 'string' &&
92
+ scripts[USER_PUBLISH_SCRIPT_KEY].trim() !== '';
93
+ const requiredPublishScriptValue = buildRequiredPublishScriptValue(hasUserPublishScript);
94
+
95
+ const previousPublishCheck =
96
+ typeof scripts[REQUIRED_PUBLISH_CHECK_SCRIPT_KEY] === 'string'
97
+ ? scripts[REQUIRED_PUBLISH_CHECK_SCRIPT_KEY]
98
+ : null;
99
+ const previousPublish =
100
+ typeof scripts[REQUIRED_PUBLISH_SCRIPT_KEY] === 'string'
101
+ ? scripts[REQUIRED_PUBLISH_SCRIPT_KEY]
102
+ : null;
103
+
104
+ const matchesRequiredPublishCheck = previousPublishCheck === REQUIRED_PUBLISH_CHECK_SCRIPT_VALUE;
105
+ const matchesRequiredPublish = previousPublish === requiredPublishScriptValue;
106
+ const lernaRequiresCreation = !lernaExistedBefore && !lernaDefaultCreated;
107
+ const requiresUpdate =
108
+ !matchesRequiredPublishCheck || !matchesRequiredPublish || lernaRequiresCreation;
109
+
110
+ if (mode === 'sync' && (!matchesRequiredPublishCheck || !matchesRequiredPublish)) {
111
+ scripts[REQUIRED_PUBLISH_CHECK_SCRIPT_KEY] = REQUIRED_PUBLISH_CHECK_SCRIPT_VALUE;
112
+ scripts[REQUIRED_PUBLISH_SCRIPT_KEY] = requiredPublishScriptValue;
113
+ pkg.scripts = scripts;
114
+ fs.writeFileSync(rootPackageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8');
115
+ }
116
+
117
+ const report = {
118
+ cwd,
119
+ mode,
120
+ ok: true,
121
+ lernaConfigPath,
122
+ rootPackageJsonPath,
123
+ required: {
124
+ publishCheckScriptKey: REQUIRED_PUBLISH_CHECK_SCRIPT_KEY,
125
+ publishCheckScriptValue: REQUIRED_PUBLISH_CHECK_SCRIPT_VALUE,
126
+ publishScriptKey: REQUIRED_PUBLISH_SCRIPT_KEY,
127
+ publishScriptValue: requiredPublishScriptValue,
128
+ },
129
+ status: {
130
+ hasUserPublishScript,
131
+ lernaExistedBefore,
132
+ lernaDefaultCreated,
133
+ matchesRequiredPublishCheckBefore: matchesRequiredPublishCheck,
134
+ matchesRequiredPublishCheckAfter:
135
+ !matchesRequiredPublishCheck && mode === 'sync' ? true : matchesRequiredPublishCheck,
136
+ matchesRequiredPublishBefore: matchesRequiredPublish,
137
+ matchesRequiredPublishAfter:
138
+ !matchesRequiredPublish && mode === 'sync' ? true : matchesRequiredPublish,
139
+ updated: requiresUpdate && mode === 'sync',
140
+ },
141
+ };
142
+
143
+ if (mode === 'check' && requiresUpdate) {
144
+ report.ok = false;
145
+ }
146
+
147
+ if (jsonFile) {
148
+ const outPath = path.resolve(cwd, jsonFile);
149
+ fs.mkdirSync(path.dirname(outPath), { recursive: true });
150
+ fs.writeFileSync(outPath, `${JSON.stringify(report, null, 2)}\n`, 'utf8');
151
+ }
152
+
153
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
154
+ if (!report.ok) {
155
+ process.exit(2);
156
+ }
157
+ }
@@ -13,6 +13,7 @@ const HELP_FILE = path.resolve(COMMAND_DIR, 'help.txt');
13
13
  const ROOT_PACKAGE_FILE = path.resolve(COMMAND_DIR, '../../../../../package.json');
14
14
  const WORKSPACE_SCOPE = 'workspace';
15
15
  const WILDCARD_SCOPE = '*';
16
+ const DEFAULT_COMMENT_CHAR = '#';
16
17
 
17
18
  export function printValidateCommitMsgHelp() {
18
19
  printTextResource(HELP_FILE);
@@ -170,6 +171,33 @@ function validateSectionFormat(lines, allowedSectionScopes = null) {
170
171
  return errors;
171
172
  }
172
173
 
174
+ function getCommentCharFromConfig() {
175
+ const configured = String(process.env.GIT_COMMENT_CHAR || '').trim();
176
+ return configured || DEFAULT_COMMENT_CHAR;
177
+ }
178
+
179
+ function normalizeCommitMessageLines(raw, commentChar = DEFAULT_COMMENT_CHAR) {
180
+ const normalizedRaw = raw.replace(/\r\n/g, '\n');
181
+ const rawLines = normalizedRaw.split('\n');
182
+ const lines = [];
183
+
184
+ for (const line of rawLines) {
185
+ if (commentChar && line.startsWith(commentChar)) {
186
+ continue;
187
+ }
188
+ lines.push(line);
189
+ }
190
+
191
+ while (lines.length > 0 && lines[0].trim() === '') {
192
+ lines.shift();
193
+ }
194
+ while (lines.length > 0 && lines[lines.length - 1].trim() === '') {
195
+ lines.pop();
196
+ }
197
+
198
+ return lines;
199
+ }
200
+
173
201
  export function runValidateCommitMsg(options) {
174
202
  const file = getSingle(options, '--file', '');
175
203
  if (!file) {
@@ -183,8 +211,8 @@ export function runValidateCommitMsg(options) {
183
211
  process.exit(2);
184
212
  }
185
213
 
186
- const raw = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
187
- const lines = raw.endsWith('\n') ? raw.slice(0, -1).split('\n') : raw.split('\n');
214
+ const raw = fs.readFileSync(filePath, 'utf8');
215
+ const lines = normalizeCommitMessageLines(raw, getCommentCharFromConfig());
188
216
 
189
217
  if (lines.length === 0 || (lines.length === 1 && lines[0].trim() === '')) {
190
218
  console.error('Commit message is empty');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@produck/agent-toolkit",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Central CLI toolkit for organization AI execution workflows",
5
5
  "type": "module",
6
6
  "repository": {
@@ -13,9 +13,7 @@
13
13
  },
14
14
  "scripts": {
15
15
  "prepack": "node ./bin/build-publish-assets.mjs",
16
- "test": "node --test test/index.mjs",
17
- "verify": "node ./bin/agent-toolkit.mjs --help && node ./bin/agent-toolkit.mjs preflight --cwd . --require package.json",
18
- "pack:check": "npm pack --dry-run",
16
+ "test": "node test/index.mjs",
19
17
  "produck:coverage": "c8 --reporter=lcov --reporter=html --reporter=text-summary npm test"
20
18
  },
21
19
  "files": [
@@ -32,5 +30,5 @@
32
30
  "devDependencies": {
33
31
  "c8": "11.0.0"
34
32
  },
35
- "gitHead": "8618167ffe15e39c5a7b350903f0fd8861902a62"
33
+ "gitHead": "a9e160249f664b28fa5971246e61d9e641c16586"
36
34
  }
@@ -0,0 +1,5 @@
1
+ * text=auto eol=lf
2
+
3
+ # Windows script entrypoints
4
+ *.bat text eol=crlf
5
+ *.cmd text eol=crlf
@@ -0,0 +1,137 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ lerna-debug.log*
8
+ .pnpm-debug.log*
9
+
10
+ # Diagnostic reports (https://nodejs.org/api/report.html)
11
+ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12
+
13
+ # Runtime data
14
+ pids
15
+ *.pid
16
+ *.seed
17
+ *.pid.lock
18
+
19
+ # Directory for instrumented libs generated by jscoverage/JSCover
20
+ lib-cov
21
+
22
+ # Coverage directory used by tools like istanbul
23
+ coverage
24
+ *.lcov
25
+
26
+ # nyc test coverage
27
+ .nyc_output
28
+
29
+ # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30
+ .grunt
31
+
32
+ # Bower dependency directory (https://bower.io/)
33
+ bower_components
34
+
35
+ # node-waf configuration
36
+ .lock-wscript
37
+
38
+ # Compiled binary addons (https://nodejs.org/api/addons.html)
39
+ build/Release
40
+
41
+ # Dependency directories
42
+ node_modules/
43
+ jspm_packages/
44
+
45
+ # Snowpack dependency directory (https://snowpack.dev/)
46
+ web_modules/
47
+
48
+ # TypeScript cache
49
+ *.tsbuildinfo
50
+
51
+ # Optional npm cache directory
52
+ .npm
53
+
54
+ # Optional eslint cache
55
+ .eslintcache
56
+
57
+ # Optional stylelint cache
58
+ .stylelintcache
59
+
60
+ # Microbundle cache
61
+ .rpt2_cache/
62
+ .rts2_cache_cjs/
63
+ .rts2_cache_es/
64
+ .rts2_cache_umd/
65
+
66
+ # Optional REPL history
67
+ .node_repl_history
68
+
69
+ # Output of 'npm pack'
70
+ *.tgz
71
+
72
+ # Yarn Integrity file
73
+ .yarn-integrity
74
+
75
+ # dotenv environment variable files
76
+ .env
77
+ .env.development.local
78
+ .env.test.local
79
+ .env.production.local
80
+ .env.local
81
+
82
+ # parcel-bundler cache (https://parceljs.org/)
83
+ .cache
84
+ .parcel-cache
85
+
86
+ # Next.js build output
87
+ .next
88
+ out
89
+
90
+ # Nuxt.js build / generate output
91
+ .nuxt
92
+ dist
93
+
94
+ # Gatsby files
95
+ .cache/
96
+ # Comment in the public line in if your project uses Gatsby and not Next.js
97
+ # https://nextjs.org/blog/next-9-1#public-directory-support
98
+ # public
99
+
100
+ # vuepress build output
101
+ .vuepress/dist
102
+
103
+ # vuepress v2.x temp and cache directory
104
+ .temp
105
+ .cache
106
+
107
+ # Docusaurus cache and generated files
108
+ .docusaurus
109
+
110
+ # Serverless directories
111
+ .serverless/
112
+
113
+ # FuseBox cache
114
+ .fusebox/
115
+
116
+ # DynamoDB Local files
117
+ .dynamodb/
118
+
119
+ # TernJS port file
120
+ .tern-port
121
+
122
+ # Stores VSCode versions used for testing VSCode extensions
123
+ .vscode-test
124
+
125
+ # yarn v2
126
+ .yarn/cache
127
+ .yarn/unplugged
128
+ .yarn/build-state.yml
129
+ .yarn/install-state.gz
130
+ .pnp.*
131
+
132
+ # Vscode
133
+ .vscode
134
+
135
+ # Specific ignored / generated.
136
+ *.ign*
137
+ *.gen*
@@ -18,20 +18,26 @@ in the `produck` organization.
18
18
 
19
19
  ## Instruction source split
20
20
 
21
- To separate organization-only governance from downstream-distributable
22
- baseline, policy sources are split as follows:
23
-
24
- - Downstream-distributable source:
25
- `.github/distribution/produck/*.instructions.md`
26
- - Organization-only source (not distributed):
27
- `.github/instructions/produck/*.instructions.md`
28
-
29
- Editing rule:
21
+ Path semantics differ between the upstream policy repository and downstream
22
+ repositories:
23
+
24
+ - Upstream policy repository (this `produck/.github` repo):
25
+ - `.github/distribution/produck/*.instructions.md` — canonical baseline,
26
+ distributed to downstream repositories.
27
+ - `.github/instructions/produck/*.instructions.md` — reserved for
28
+ organization-only governance that should NOT be distributed.
29
+ - Downstream repositories (consumers of the baseline):
30
+ - `.github/instructions/produck/*.instructions.md` — synced copy of the
31
+ upstream canonical baseline (managed by `agent-toolkit sync-instructions`).
32
+ - `.github/copilot-instructions.md` — repository-specific exceptions and
33
+ stricter local constraints.
34
+
35
+ Editing rule (upstream only):
30
36
 
31
37
  - Update downstream baseline rules directly in
32
38
  `.github/distribution/produck/*.instructions.md`.
33
- - Update organization-only workflow/governance in
34
- `.github/instructions/produck/`.
39
+ - Add organization-only governance under
40
+ `.github/instructions/produck/` only when it must not be distributed.
35
41
 
36
42
  ## Default expectations
37
43
 
@@ -134,27 +140,15 @@ max_line_length = 80
134
140
 
135
141
  ## Commit and PR conventions
136
142
 
137
- - Commit messages use bracketed tags: `[TAG] summary`.
138
- - Every non-empty commit message line must start with `[TAG]`.
139
- - Empty lines are not allowed between commit message lines.
140
- - Do not use untagged bullet lines in commit message body.
141
- - If details are needed, use additional tagged lines.
142
- - Do not keep summary as an untagged standalone line.
143
- - Recommended local validation:
144
- `npm exec -- agent-toolkit validate-commit-msg --file <message-file>`.
145
- - Commit precheck policy follows
143
+ - Commit and commit-precheck rules are defined in
146
144
  `.github/distribution/produck/20-produck-commit.instructions.md`.
147
- - Canonical source for commit tag/target whitelists, legacy mapping, and
148
- target syntax is
149
- `.github/distribution/produck/20-produck-commit.instructions.md`.
150
- - Do not redefine commit tag or target whitelists in other instruction files.
151
- - For non-monorepo repositories, use `[TAG] summary` directly (no
152
- package/workspace section headers).
153
- - Bracketed commit summaries should be in English
154
- - PR title format is repository-defined; no organization-level title format
155
- restriction
156
- - In PR descriptions, summarize what changed, why it changed, how it was
157
- validated, and any known risks or follow-up work
145
+ - Use commit message validator before commit and amend:
146
+ `npm exec -- agent-toolkit validate-commit-msg --file <message-file>`.
147
+ - Do not redefine commit tag, target, or monorepo section rules outside
148
+ `20-produck-commit.instructions.md`.
149
+ - PR title format is repository-defined (no organization-level restriction).
150
+ - PR descriptions should summarize what changed, why, validation, and known
151
+ risks or follow-up work.
158
152
 
159
153
  ## Terminal long-output protocol
160
154
 
@@ -255,7 +249,8 @@ locally in downstream repositories at a fixed version managed by the
255
249
  organization baseline.
256
250
 
257
251
  - Local install and pinned version are deployed by
258
- `agent-toolkit sync-husky-hooks`.
252
+ `agent-toolkit sync-git` (root devDependency pinning) and
253
+ `agent-toolkit sync-install` (root install script baseline).
259
254
  - Invocation uses the locally installed copy:
260
255
  `npm exec -- <bin> ...`.
261
256
  - Do not use `npm exec --package=<pkg>@latest` for routine invocations.
@@ -294,31 +289,21 @@ Rollback runbook (minimum):
294
289
 
295
290
  ### Recommended organization AI instruction template
296
291
 
297
- Use the following template text in organization AI instructions:
298
-
299
- - Use Chinese for discussion unless repository rules require another language.
300
- - Follow existing repository patterns; do not invent APIs, files, commands, or
301
- config keys.
302
- - Node-first execution policy:
303
- - Use Node scripts first for file/path/output processing.
304
- - For large output tasks, use two phases: capture full output, then analyze.
305
- - Avoid fragile shell pipeline post-processing for long-output commands.
306
- - Central package policy:
307
- - Central package is installed locally at a fixed organization-baseline version.
308
- - Invoke via `npm exec -- <bin> ...` to use the locally installed copy.
309
- - Print resolved package version before high-impact execution.
310
- - Use dry-run first for risky operations; keep rollback path to pinned version.
311
- - Commit message policy:
312
- - Every non-empty commit message line must start with `[TAG]`.
313
- - Empty lines are not allowed between commit message lines.
314
- - Use only allowed tags: `[INIT]`, `[ADD]`, `[REMOVE]`, `[FIX]`,
315
- `[REFACTOR]`, `[UPGRADE]`.
316
- - Optional target syntax is `[TAG] <target>: <summary>` with target in:
317
- `docs`, `test`, `ci`, `deps`, `api`, `schema`, `infra`, `fmt`.
318
- - Do not assume scripts from organization `.github` repository exist in target
319
- repositories.
320
- - If a repository provides stricter rules, repository rules override
321
- organization defaults.
292
+ When authoring organization-level AI instruction text (for example in
293
+ organization settings), reference the canonical sources in this baseline
294
+ instead of duplicating their content:
295
+
296
+ - Language and default expectations: see `Default expectations` and
297
+ `Language conventions` sections above.
298
+ - Node-first execution policy: see `Terminal long-output protocol` above.
299
+ - Central package policy: see `Central package execution policy` above.
300
+ - Commit message policy: see
301
+ [Commit Convention](20-produck-commit.instructions.md) (canonical source
302
+ for tag whitelist, target whitelist, and validator usage).
303
+ - Cross-repository assumptions: do not assume scripts from the organization
304
+ `.github` repository exist in target repositories.
305
+ - Precedence: repository-specific stricter rules override organization
306
+ defaults.
322
307
 
323
308
  ## Precedence
324
309
 
@@ -326,4 +311,5 @@ If a repository provides more specific instructions, follow the repository
326
311
  instructions over this organization baseline.
327
312
 
328
313
  For Node.js repositories, also follow [Node.js Initialization
329
- Baseline](10-produck-node.instructions.md).
314
+ Baseline](10-produck-node.instructions.md) and [Test Authoring
315
+ Baseline](12-produck-test.instructions.md).