@produck/agent-toolkit 0.7.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.
- package/bin/build-publish-assets.mjs +26 -5
- package/bin/command/sync-format/help.txt +3 -1
- package/bin/command/sync-format/index.mjs +40 -2
- package/bin/command/sync-instructions/index.mjs +2 -22
- package/bin/command/sync-publish/index.mjs +1 -1
- package/package.json +2 -2
- package/publish-assets/instructions/produck/00-produck-base.instructions.md +44 -58
- package/publish-assets/instructions/produck/10-produck-node.instructions.md +16 -52
- package/publish-assets/instructions/produck/12-produck-test.instructions.md +94 -0
- package/publish-assets/instructions/produck/15-produck-workspace.instructions.md +8 -38
- package/publish-assets/instructions/produck/tooling-version-baseline.json +6 -1
- package/publish-assets/prettierignore +2 -0
|
@@ -12,8 +12,10 @@ const SOURCE_TOOLING_BASELINE_PATH = path.resolve(SOURCE_DIR, 'tooling-version-b
|
|
|
12
12
|
const OUTPUT_TOOLING_BASELINE_PATH = path.resolve(OUTPUT_DIR, 'tooling-version-baseline.json');
|
|
13
13
|
const SOURCE_GITATTRIBUTES_PATH = path.resolve(REPO_ROOT, '.gitattributes');
|
|
14
14
|
const SOURCE_GITIGNORE_PATH = path.resolve(REPO_ROOT, '.gitignore');
|
|
15
|
+
const SOURCE_PRETTIERIGNORE_PATH = path.resolve(REPO_ROOT, '.prettierignore');
|
|
15
16
|
const OUTPUT_GITATTRIBUTES_PATH = path.resolve(PACKAGE_ROOT, 'publish-assets/gitattributes');
|
|
16
17
|
const OUTPUT_GITIGNORE_PATH = path.resolve(PACKAGE_ROOT, 'publish-assets/gitignore');
|
|
18
|
+
const OUTPUT_PRETTIERIGNORE_PATH = path.resolve(PACKAGE_ROOT, 'publish-assets/prettierignore');
|
|
17
19
|
const LEGACY_OUTPUT_PATH = path.resolve(
|
|
18
20
|
PACKAGE_ROOT,
|
|
19
21
|
'publish-assets/instructions/org.instructions.md',
|
|
@@ -77,12 +79,19 @@ function readAndValidateToolingBaseline() {
|
|
|
77
79
|
throw new Error(`Invalid coverage.scriptTemplate in: ${SOURCE_TOOLING_BASELINE_PATH}`);
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
const eslintRulesPkgPath = path.resolve(PACKAGE_ROOT, '../eslint-rules/package.json');
|
|
83
|
+
if (fs.existsSync(eslintRulesPkgPath)) {
|
|
84
|
+
const eslintRulesPkg = JSON.parse(fs.readFileSync(eslintRulesPkgPath, 'utf8'));
|
|
85
|
+
const version = eslintRulesPkg.version;
|
|
86
|
+
if (typeof version === 'string' && version.trim()) {
|
|
87
|
+
if (!baseline.tools) baseline.tools = {};
|
|
88
|
+
baseline.tools['@produck/eslint-rules'] = {
|
|
89
|
+
version,
|
|
90
|
+
policy: 'pinned',
|
|
91
|
+
allowLatest: false,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
84
94
|
}
|
|
85
|
-
|
|
86
95
|
return `${JSON.stringify(baseline, null, 2)}\n`;
|
|
87
96
|
}
|
|
88
97
|
|
|
@@ -154,6 +163,9 @@ function run() {
|
|
|
154
163
|
if (!fs.existsSync(SOURCE_GITIGNORE_PATH)) {
|
|
155
164
|
throw new Error(`Missing source .gitignore: ${SOURCE_GITIGNORE_PATH}`);
|
|
156
165
|
}
|
|
166
|
+
if (!fs.existsSync(SOURCE_PRETTIERIGNORE_PATH)) {
|
|
167
|
+
throw new Error(`Missing source .prettierignore: ${SOURCE_PRETTIERIGNORE_PATH}`);
|
|
168
|
+
}
|
|
157
169
|
|
|
158
170
|
fs.writeFileSync(
|
|
159
171
|
OUTPUT_GITATTRIBUTES_PATH,
|
|
@@ -171,6 +183,15 @@ function run() {
|
|
|
171
183
|
);
|
|
172
184
|
process.stdout.write(`Generated ${OUTPUT_GITIGNORE_PATH} from ${SOURCE_GITIGNORE_PATH}\n`);
|
|
173
185
|
|
|
186
|
+
fs.writeFileSync(
|
|
187
|
+
OUTPUT_PRETTIERIGNORE_PATH,
|
|
188
|
+
normalize(fs.readFileSync(SOURCE_PRETTIERIGNORE_PATH, 'utf8')),
|
|
189
|
+
'utf8',
|
|
190
|
+
);
|
|
191
|
+
process.stdout.write(
|
|
192
|
+
`Generated ${OUTPUT_PRETTIERIGNORE_PATH} from ${SOURCE_PRETTIERIGNORE_PATH}\n`,
|
|
193
|
+
);
|
|
194
|
+
|
|
174
195
|
cleanStaleManagedFiles(expectedNames);
|
|
175
196
|
|
|
176
197
|
if (fs.existsSync(LEGACY_OUTPUT_PATH)) {
|
|
@@ -4,9 +4,11 @@ Usage:
|
|
|
4
4
|
|
|
5
5
|
Behavior:
|
|
6
6
|
- Applies organization-required root format script:
|
|
7
|
-
- scripts.produck:format = prettier --write .
|
|
7
|
+
- scripts.produck:format = prettier --write . --ignore-path .prettierignore --ignore-path .gitignore
|
|
8
8
|
- Applies organization-required root Prettier config file:
|
|
9
9
|
- .prettierrc
|
|
10
|
+
- Applies organization-required root Prettier ignore file:
|
|
11
|
+
- .prettierignore (prettier-specific patterns; .gitignore is also used via --ignore-path)
|
|
10
12
|
|
|
11
13
|
Rules:
|
|
12
14
|
- --check validates without writing and exits non-zero on mismatch
|
|
@@ -14,10 +14,17 @@ const TOOLING_BASELINE_CANDIDATE_PATHS = [
|
|
|
14
14
|
path.resolve(PACKAGE_ROOT, 'publish-assets/instructions/produck/tooling-version-baseline.json'),
|
|
15
15
|
];
|
|
16
16
|
const PRETTIER_CONFIG_FILE = '.prettierrc';
|
|
17
|
+
const PRETTIER_IGNORE_FILE = '.prettierignore';
|
|
17
18
|
const REQUIRED_PRETTIER_DEV_DEPENDENCY_KEY = 'prettier';
|
|
18
19
|
|
|
20
|
+
const PRETTIER_IGNORE_SOURCE_CANDIDATE_PATHS = [
|
|
21
|
+
path.resolve(REPO_ROOT, '.prettierignore'),
|
|
22
|
+
path.resolve(PACKAGE_ROOT, 'publish-assets/prettierignore'),
|
|
23
|
+
];
|
|
24
|
+
|
|
19
25
|
const REQUIRED_FORMAT_SCRIPT_KEY = 'produck:format';
|
|
20
|
-
const REQUIRED_FORMAT_SCRIPT_VALUE =
|
|
26
|
+
const REQUIRED_FORMAT_SCRIPT_VALUE =
|
|
27
|
+
'prettier --write . --ignore-path .prettierignore --ignore-path .gitignore';
|
|
21
28
|
const REQUIRED_PRETTIER_CONFIG = `${JSON.stringify(
|
|
22
29
|
{
|
|
23
30
|
semi: true,
|
|
@@ -33,6 +40,23 @@ const REQUIRED_PRETTIER_CONFIG = `${JSON.stringify(
|
|
|
33
40
|
2,
|
|
34
41
|
)}\n`;
|
|
35
42
|
|
|
43
|
+
function loadPrettierIgnoreContent() {
|
|
44
|
+
const sourcePath = PRETTIER_IGNORE_SOURCE_CANDIDATE_PATHS.find((p) => fs.existsSync(p));
|
|
45
|
+
|
|
46
|
+
if (!sourcePath) {
|
|
47
|
+
console.error('Org .prettierignore source not found in expected locations:');
|
|
48
|
+
for (const p of PRETTIER_IGNORE_SOURCE_CANDIDATE_PATHS) {
|
|
49
|
+
console.error(`- ${p}`);
|
|
50
|
+
}
|
|
51
|
+
process.exit(2);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
sourcePath,
|
|
56
|
+
content: fs.readFileSync(sourcePath, 'utf8'),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
36
60
|
export function printSyncFormatHelp() {
|
|
37
61
|
printTextResource(HELP_FILE);
|
|
38
62
|
}
|
|
@@ -101,6 +125,8 @@ export function runSyncFormat(options) {
|
|
|
101
125
|
const pkg = parseJsonFile(rootPackageJsonPath, 'Root package.json');
|
|
102
126
|
const toolingBaseline = loadToolingBaseline();
|
|
103
127
|
const requiredPrettierVersion = toolingBaseline.prettierVersion;
|
|
128
|
+
const { sourcePath: prettierIgnoreSourcePath, content: REQUIRED_PRETTIER_IGNORE_CONTENT } =
|
|
129
|
+
loadPrettierIgnoreContent();
|
|
104
130
|
const scripts =
|
|
105
131
|
pkg.scripts && typeof pkg.scripts === 'object' && !Array.isArray(pkg.scripts)
|
|
106
132
|
? { ...pkg.scripts }
|
|
@@ -122,14 +148,20 @@ export function runSyncFormat(options) {
|
|
|
122
148
|
: null;
|
|
123
149
|
|
|
124
150
|
const prettierConfigPath = path.resolve(cwd, PRETTIER_CONFIG_FILE);
|
|
151
|
+
const prettierIgnorePath = path.resolve(cwd, PRETTIER_IGNORE_FILE);
|
|
125
152
|
const previousPrettierConfig = readFileIfExists(prettierConfigPath);
|
|
153
|
+
const previousPrettierIgnore = readFileIfExists(prettierIgnorePath);
|
|
126
154
|
|
|
127
155
|
const matchesRequiredFormat = previousFormat === REQUIRED_FORMAT_SCRIPT_VALUE;
|
|
128
156
|
const matchesRequiredPrettierConfig = previousPrettierConfig === REQUIRED_PRETTIER_CONFIG;
|
|
129
157
|
const matchesRequiredPrettierDep = previousPrettierDep === requiredPrettierVersion;
|
|
158
|
+
const matchesRequiredPrettierIgnore = previousPrettierIgnore === REQUIRED_PRETTIER_IGNORE_CONTENT;
|
|
130
159
|
|
|
131
160
|
const requiresUpdate =
|
|
132
|
-
!matchesRequiredFormat ||
|
|
161
|
+
!matchesRequiredFormat ||
|
|
162
|
+
!matchesRequiredPrettierConfig ||
|
|
163
|
+
!matchesRequiredPrettierDep ||
|
|
164
|
+
!matchesRequiredPrettierIgnore;
|
|
133
165
|
|
|
134
166
|
if (mode === 'sync' && requiresUpdate) {
|
|
135
167
|
scripts[REQUIRED_FORMAT_SCRIPT_KEY] = REQUIRED_FORMAT_SCRIPT_VALUE;
|
|
@@ -140,6 +172,7 @@ export function runSyncFormat(options) {
|
|
|
140
172
|
|
|
141
173
|
fs.writeFileSync(rootPackageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8');
|
|
142
174
|
fs.writeFileSync(prettierConfigPath, REQUIRED_PRETTIER_CONFIG, 'utf8');
|
|
175
|
+
fs.writeFileSync(prettierIgnorePath, REQUIRED_PRETTIER_IGNORE_CONTENT, 'utf8');
|
|
143
176
|
}
|
|
144
177
|
|
|
145
178
|
const report = {
|
|
@@ -152,17 +185,22 @@ export function runSyncFormat(options) {
|
|
|
152
185
|
formatScriptKey: REQUIRED_FORMAT_SCRIPT_KEY,
|
|
153
186
|
formatScriptValue: REQUIRED_FORMAT_SCRIPT_VALUE,
|
|
154
187
|
prettierConfigPath: path.relative(cwd, prettierConfigPath),
|
|
188
|
+
prettierIgnorePath: path.relative(cwd, prettierIgnorePath),
|
|
189
|
+
prettierIgnoreSourcePath,
|
|
155
190
|
managedDevDependencies: { [REQUIRED_PRETTIER_DEV_DEPENDENCY_KEY]: requiredPrettierVersion },
|
|
156
191
|
},
|
|
157
192
|
status: {
|
|
158
193
|
matchesRequiredFormatBefore: matchesRequiredFormat,
|
|
159
194
|
matchesRequiredPrettierConfigBefore: matchesRequiredPrettierConfig,
|
|
160
195
|
matchesRequiredPrettierDepBefore: matchesRequiredPrettierDep,
|
|
196
|
+
matchesRequiredPrettierIgnoreBefore: matchesRequiredPrettierIgnore,
|
|
161
197
|
matchesRequiredFormatAfter: requiresUpdate && mode === 'sync' ? true : matchesRequiredFormat,
|
|
162
198
|
matchesRequiredPrettierConfigAfter:
|
|
163
199
|
requiresUpdate && mode === 'sync' ? true : matchesRequiredPrettierConfig,
|
|
164
200
|
matchesRequiredPrettierDepAfter:
|
|
165
201
|
requiresUpdate && mode === 'sync' ? true : matchesRequiredPrettierDep,
|
|
202
|
+
matchesRequiredPrettierIgnoreAfter:
|
|
203
|
+
requiresUpdate && mode === 'sync' ? true : matchesRequiredPrettierIgnore,
|
|
166
204
|
updated: requiresUpdate && mode === 'sync',
|
|
167
205
|
},
|
|
168
206
|
};
|
|
@@ -88,7 +88,7 @@ export function runSyncInstructions(options) {
|
|
|
88
88
|
const cwd = path.resolve(getSingle(options, '--cwd', process.cwd()));
|
|
89
89
|
const outArg = getSingle(options, '--out', DEFAULT_NAMESPACE_OUT_DIR);
|
|
90
90
|
const sourceArg = getSingle(options, '--source', '');
|
|
91
|
-
const force =
|
|
91
|
+
const force = true;
|
|
92
92
|
const dryRun = hasFlag(options, '--dry-run');
|
|
93
93
|
const prune = hasFlag(options, '--prune');
|
|
94
94
|
|
|
@@ -146,15 +146,6 @@ export function runSyncInstructions(options) {
|
|
|
146
146
|
if (outLooksLikeFile) {
|
|
147
147
|
const entry = entries[0];
|
|
148
148
|
const exists = fs.existsSync(outPath);
|
|
149
|
-
if (exists && !force) {
|
|
150
|
-
const current = fs.readFileSync(outPath, 'utf8');
|
|
151
|
-
if (current !== entry.content) {
|
|
152
|
-
console.error(`Target already exists: ${outPath}`);
|
|
153
|
-
console.error('Use --force to overwrite.');
|
|
154
|
-
process.exit(2);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
149
|
const report = {
|
|
159
150
|
mode: 'single-file',
|
|
160
151
|
cwd,
|
|
@@ -162,18 +153,16 @@ export function runSyncInstructions(options) {
|
|
|
162
153
|
source: sourceResolved,
|
|
163
154
|
outPath,
|
|
164
155
|
exists,
|
|
165
|
-
overwritten: exists
|
|
156
|
+
overwritten: exists,
|
|
166
157
|
dryRun,
|
|
167
158
|
prune: false,
|
|
168
159
|
initializedUserSpaceEntry: false,
|
|
169
160
|
userSpaceEntryPath: null,
|
|
170
161
|
};
|
|
171
|
-
|
|
172
162
|
if (dryRun) {
|
|
173
163
|
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
174
164
|
process.exit(0);
|
|
175
165
|
}
|
|
176
|
-
|
|
177
166
|
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
178
167
|
fs.writeFileSync(outPath, entry.content, 'utf8');
|
|
179
168
|
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
@@ -202,15 +191,6 @@ export function runSyncInstructions(options) {
|
|
|
202
191
|
}
|
|
203
192
|
|
|
204
193
|
const toWrite = planned.filter((item) => !unchanged.includes(item.targetPath));
|
|
205
|
-
const conflicts = toWrite.filter((item) => fs.existsSync(item.targetPath));
|
|
206
|
-
if (conflicts.length > 0 && !force) {
|
|
207
|
-
console.error('Some target files already exist and would change:');
|
|
208
|
-
for (const item of conflicts) {
|
|
209
|
-
console.error(`- ${item.targetPath}`);
|
|
210
|
-
}
|
|
211
|
-
console.error('Use --force to overwrite.');
|
|
212
|
-
process.exit(2);
|
|
213
|
-
}
|
|
214
194
|
|
|
215
195
|
const pruneDeletes = [];
|
|
216
196
|
if (prune && fs.existsSync(outDir)) {
|
|
@@ -11,7 +11,7 @@ const LERNA_CONFIG_FILE = 'lerna.json';
|
|
|
11
11
|
|
|
12
12
|
const REQUIRED_PUBLISH_CHECK_SCRIPT_KEY = 'produck:publish:check';
|
|
13
13
|
const REQUIRED_PUBLISH_CHECK_SCRIPT_VALUE =
|
|
14
|
-
'npm run produck:format && npm run produck:lint && npm run produck:coverage';
|
|
14
|
+
'npm run produck:install && npm run produck:format && npm run produck:lint && npm run produck:coverage';
|
|
15
15
|
const USER_PUBLISH_SCRIPT_KEY = 'publish';
|
|
16
16
|
const REQUIRED_PUBLISH_SCRIPT_KEY = 'produck:publish';
|
|
17
17
|
const REQUIRED_PUBLISH_SCRIPT_FALLBACK_VALUE = 'npm run produck:publish:check && lerna publish';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@produck/agent-toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Central CLI toolkit for organization AI execution workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -30,5 +30,5 @@
|
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"c8": "11.0.0"
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "a9e160249f664b28fa5971246e61d9e641c16586"
|
|
34
34
|
}
|
|
@@ -18,20 +18,26 @@ in the `produck` organization.
|
|
|
18
18
|
|
|
19
19
|
## Instruction source split
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
`.github/distribution/produck/*.instructions.md`
|
|
26
|
-
|
|
27
|
-
`.github/instructions/produck/*.instructions.md`
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
-
|
|
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
|
|
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
|
-
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
|
|
153
|
-
|
|
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-
|
|
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
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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).
|
|
@@ -29,8 +29,9 @@ Notes:
|
|
|
29
29
|
|
|
30
30
|
- Script key names are fixed and must match exactly.
|
|
31
31
|
- Keep the script key name `produck:install` (organization-reserved key).
|
|
32
|
-
-
|
|
33
|
-
`
|
|
32
|
+
- The required value for `produck:install` is governed by
|
|
33
|
+
`agent-toolkit sync-install`; see the Monorepo mode section below for the
|
|
34
|
+
canonical value.
|
|
34
35
|
- `publish` may be a no-op when repository-specific release workflow does not
|
|
35
36
|
use npm publishing.
|
|
36
37
|
- Coverage governance policy:
|
|
@@ -115,67 +116,30 @@ Central toolkit command role model:
|
|
|
115
116
|
interpreter workflows for parsing/filtering over brittle OS-shell pipelines.
|
|
116
117
|
- For human engineers, `run-capture` and `summarize-log` are optional helpers.
|
|
117
118
|
|
|
118
|
-
Test authoring baseline
|
|
119
|
+
Test authoring baseline:
|
|
119
120
|
|
|
120
|
-
-
|
|
121
|
-
and
|
|
122
|
-
- Execution MUST follow the **Single Entrypoint Rule**: always run tests via a
|
|
123
|
-
dedicated entrypoint (for example `test/index.mjs`) instead of targeting
|
|
124
|
-
individual files.
|
|
125
|
-
- Command-line execution MUST NOT use the `--test` flag. Use
|
|
126
|
-
`node <entrypoint>` directly.
|
|
127
|
-
- Each test case must be independently executable.
|
|
128
|
-
- Test cases must not depend on execution order or state from other cases.
|
|
129
|
-
- New test debugging should use local `only` mode for scoped regression.
|
|
130
|
-
- After debugging, remove all `only` markers before final validation.
|
|
131
|
-
|
|
132
|
-
Recommended local debug flow:
|
|
133
|
-
|
|
134
|
-
1. Add `{ only: true }` to the target `describe/it` and all ancestor
|
|
135
|
-
`describe` blocks.
|
|
136
|
-
2. Run `node --test-only test/index.mjs`.
|
|
137
|
-
3. Remove all `only` markers.
|
|
138
|
-
4. Run full regression via repository standard test command.
|
|
121
|
+
- See [Test Authoring Baseline](12-produck-test.instructions.md) for the full
|
|
122
|
+
test writing, structure, debug, and coverage workflow rules.
|
|
139
123
|
|
|
140
124
|
Script and output directory policy:
|
|
141
125
|
|
|
142
|
-
-
|
|
143
|
-
-
|
|
144
|
-
|
|
145
|
-
-
|
|
146
|
-
equivalent) and ignored by git.
|
|
147
|
-
- Temporary debug scripts should not be committed.
|
|
148
|
-
- `.github/` should not be used as a temporary script workspace.
|
|
126
|
+
- Follow script/output placement and lifecycle policy from
|
|
127
|
+
`00-produck-base.instructions.md`.
|
|
128
|
+
- For monorepo shared configuration and root workspace practices, follow
|
|
129
|
+
`15-produck-workspace.instructions.md`.
|
|
149
130
|
|
|
150
131
|
Required ignore baseline:
|
|
151
132
|
|
|
152
133
|
- Each Node.js repository must include a root `.gitignore`.
|
|
153
|
-
-
|
|
154
|
-
|
|
155
|
-
-
|
|
156
|
-
|
|
157
|
-
- The root `.gitignore` should at minimum ignore:
|
|
158
|
-
- `node_modules/`
|
|
159
|
-
- `coverage/`
|
|
160
|
-
- `.env`
|
|
161
|
-
- `.env.*`
|
|
162
|
-
- npm logs (for example `npm-debug.log*`)
|
|
163
|
-
- OS/editor noise (for example `.DS_Store`, `Thumbs.db`, `.vscode/` when
|
|
164
|
-
workspace settings are not intended to be shared)
|
|
134
|
+
- Baseline template and required minimum entries follow
|
|
135
|
+
`00-produck-base.instructions.md`.
|
|
136
|
+
- Monorepo centralization policy and workspace-specific ignore guidance follow
|
|
137
|
+
`15-produck-workspace.instructions.md`.
|
|
165
138
|
|
|
166
139
|
Team conventions for `.gitignore`:
|
|
167
140
|
|
|
168
|
-
-
|
|
169
|
-
|
|
170
|
-
- Do not remove baseline entries from the GitHub template unless repository
|
|
171
|
-
owners document a justified exception.
|
|
172
|
-
- Organization-approved team extension entries are:
|
|
173
|
-
- `*.ign*` (manually created local directories/files that should not be
|
|
174
|
-
committed)
|
|
175
|
-
- `*.gen*` (generated artifacts created by program execution, for example
|
|
176
|
-
during tests)
|
|
177
|
-
- Append these team entries under a dedicated team block at the end of the root
|
|
178
|
-
`.gitignore`.
|
|
141
|
+
- Team extension entries and placement rules follow
|
|
142
|
+
`00-produck-base.instructions.md`.
|
|
179
143
|
|
|
180
144
|
## Monorepo mode
|
|
181
145
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: '**'
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<!-- managed-by: @produck/agent-toolkit -->
|
|
6
|
+
<!-- source: .github/distribution/produck/12-produck-test.instructions.md -->
|
|
7
|
+
|
|
8
|
+
# Test Authoring Baseline
|
|
9
|
+
|
|
10
|
+
## Scope
|
|
11
|
+
|
|
12
|
+
- Applies to Node.js repositories in the organization.
|
|
13
|
+
- This document governs how tests are authored, structured, and executed.
|
|
14
|
+
- Repository-specific rules may add stricter requirements; they override this
|
|
15
|
+
baseline when they conflict.
|
|
16
|
+
|
|
17
|
+
## Framework
|
|
18
|
+
|
|
19
|
+
- MUST use Node.js standard library test runner (`node:test`) with `describe`
|
|
20
|
+
and `it`.
|
|
21
|
+
- Do not introduce a third-party test framework unless repository owners
|
|
22
|
+
explicitly document the justification.
|
|
23
|
+
|
|
24
|
+
## File structure
|
|
25
|
+
|
|
26
|
+
- Each package must have a dedicated test directory: `test/`.
|
|
27
|
+
- Tests are organized in individual files per subject area under `test/`.
|
|
28
|
+
- Each package MUST provide a single entrypoint file: `test/index.mjs`.
|
|
29
|
+
- The entrypoint imports all test files to be executed.
|
|
30
|
+
|
|
31
|
+
Example entrypoint:
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
import './feature-a.test.mjs';
|
|
35
|
+
import './feature-b.test.mjs';
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Single Entrypoint Rule (required)
|
|
39
|
+
|
|
40
|
+
- Execution MUST always go through the entrypoint file.
|
|
41
|
+
- Command-line execution MUST NOT use the `--test` flag. Use
|
|
42
|
+
`node <entrypoint>` directly.
|
|
43
|
+
- Correct: `node test/index.mjs`
|
|
44
|
+
- Wrong: `node --test` or `node --test test/feature-a.test.mjs`
|
|
45
|
+
- All tooling (scripts, CI, coverage) must target the entrypoint only.
|
|
46
|
+
|
|
47
|
+
## Test case rules
|
|
48
|
+
|
|
49
|
+
- Each test case must be independently executable.
|
|
50
|
+
- Test cases must not depend on execution order or shared mutable state from
|
|
51
|
+
other cases.
|
|
52
|
+
- Avoid global side effects that persist across test cases.
|
|
53
|
+
- Clean up resources (temp files, open handles) at the end of each test case.
|
|
54
|
+
|
|
55
|
+
## Naming conventions
|
|
56
|
+
|
|
57
|
+
- Test files use the `.test.mjs` suffix.
|
|
58
|
+
- Top-level `describe` label should match the subject being tested (for example
|
|
59
|
+
the module name or command name).
|
|
60
|
+
- `it` labels should describe the expected behavior in plain language.
|
|
61
|
+
|
|
62
|
+
## Local debug workflow (recommended)
|
|
63
|
+
|
|
64
|
+
When debugging a failing test case:
|
|
65
|
+
|
|
66
|
+
1. Add `{ only: true }` to the target `it` and all ancestor `describe` blocks.
|
|
67
|
+
2. Run `node --test-only test/index.mjs`.
|
|
68
|
+
3. Verify the issue is isolated.
|
|
69
|
+
4. Remove all `only` markers.
|
|
70
|
+
5. Run full regression: `node test/index.mjs`.
|
|
71
|
+
|
|
72
|
+
Rules for `only` mode:
|
|
73
|
+
|
|
74
|
+
- `--test-name-pattern` does NOT match tests nested inside `describe()` blocks
|
|
75
|
+
— it may silently run zero tests. Do not use it for focused runs.
|
|
76
|
+
- Always remove `only` markers before committing.
|
|
77
|
+
- Never commit a test file with `{ only: true }` present.
|
|
78
|
+
|
|
79
|
+
## Coverage
|
|
80
|
+
|
|
81
|
+
- Coverage is governed by `produck:coverage` script and `c8`.
|
|
82
|
+
- Scoped coverage (for partial branch verification during development):
|
|
83
|
+
`npm exec -- c8 --reporter=lcov --reporter=html --reporter=text-summary node --test-only test/index.mjs`
|
|
84
|
+
- Before merge or release, run full coverage via `npm run produck:coverage`.
|
|
85
|
+
- See [Node.js Baseline](10-produck-node.instructions.md) for coverage script
|
|
86
|
+
governance details.
|
|
87
|
+
|
|
88
|
+
## Regression discipline
|
|
89
|
+
|
|
90
|
+
- Before merge or release: restore executable test commands and fix all failing
|
|
91
|
+
tests.
|
|
92
|
+
- Temporary non-executable state or failing tests are allowed for intermediate
|
|
93
|
+
commits.
|
|
94
|
+
- Full regression MUST pass before a release commit is made.
|
|
@@ -11,13 +11,6 @@ applyTo: '**'
|
|
|
11
11
|
|
|
12
12
|
The Produck monorepo provides unified configuration across all packages for consistency and ease of maintenance.
|
|
13
13
|
|
|
14
|
-
## Distribution classification
|
|
15
|
-
|
|
16
|
-
This document is maintained directly as a downstream-distributable source.
|
|
17
|
-
|
|
18
|
-
- Authoritative path:
|
|
19
|
-
`.github/distribution/produck/15-produck-workspace.instructions.md`
|
|
20
|
-
|
|
21
14
|
## Shared Configurations
|
|
22
15
|
|
|
23
16
|
### 1. ESLint Configuration (`eslint.config.mjs`)
|
|
@@ -152,38 +145,15 @@ npm run produck:coverage
|
|
|
152
145
|
|
|
153
146
|
### Release & Coverage Tooling
|
|
154
147
|
|
|
155
|
-
-
|
|
156
|
-
-
|
|
148
|
+
- Release and coverage governance follows
|
|
149
|
+
`10-produck-node.instructions.md`.
|
|
150
|
+
- Source of truth for version pinning and script templates remains
|
|
157
151
|
`.github/distribution/produck/tooling-version-baseline.json`.
|
|
158
|
-
-
|
|
159
|
-
|
|
160
|
-
-
|
|
161
|
-
-
|
|
162
|
-
|
|
163
|
-
- Shared scripts/CI must not use unversioned `npx lerna` or `lerna@latest`.
|
|
164
|
-
- Wrapper scripts are allowed, but should keep parity with organization version
|
|
165
|
-
policy.
|
|
166
|
-
- Workspace subpackage coverage scripts are fully organization-governed.
|
|
167
|
-
- Deploy/repair coverage scripts via central remediation command:
|
|
168
|
-
`npm exec -- agent-toolkit sync-coverage --cwd .`.
|
|
169
|
-
- Root anti-drift local hook baseline is organization-governed.
|
|
170
|
-
- Deploy/repair root local hooks via central remediation command:
|
|
171
|
-
`npm exec -- agent-toolkit sync-git --cwd .`.
|
|
172
|
-
- Deployed coverage scripts use the `c8` version specified in
|
|
173
|
-
`tooling-version-baseline.json` for each governed workspace package.
|
|
174
|
-
- Deployed local hook baseline uses the `husky` version specified in
|
|
175
|
-
`tooling-version-baseline.json` for root `devDependencies.husky`.
|
|
176
|
-
- Deployed local hook baseline also pins root
|
|
177
|
-
`devDependencies.@produck/agent-toolkit` to the fixed version managed by the organization baseline.
|
|
178
|
-
- Shared scripts/CI must not use unversioned `npx c8` or `c8@latest`.
|
|
179
|
-
- `test` script implementation remains repository-defined and is not overwritten
|
|
180
|
-
by coverage remediation.
|
|
181
|
-
- Root `devDependencies.c8` is pinned at root by `agent-toolkit sync-git`;
|
|
182
|
-
workspace package `devDependencies.c8` is pinned per package by
|
|
183
|
-
`agent-toolkit sync-coverage`.
|
|
184
|
-
- Root local hooks may be bypassed intentionally by developers (for example via
|
|
185
|
-
`--no-verify`) and are treated as local strong guardrails rather than
|
|
186
|
-
immutable release gates.
|
|
152
|
+
- For monorepo remediation, use:
|
|
153
|
+
- `npm exec -- agent-toolkit sync-coverage --cwd .`
|
|
154
|
+
- `npm exec -- agent-toolkit sync-git --cwd .`
|
|
155
|
+
- Keep workspace wrappers and local scripts consistent with Node baseline
|
|
156
|
+
execution policy.
|
|
187
157
|
|
|
188
158
|
## Package Integration
|
|
189
159
|
|
|
@@ -21,10 +21,15 @@
|
|
|
21
21
|
"version": "3.8.3",
|
|
22
22
|
"policy": "pinned",
|
|
23
23
|
"allowLatest": false
|
|
24
|
+
},
|
|
25
|
+
"@produck/eslint-rules": {
|
|
26
|
+
"version": "0.3.5",
|
|
27
|
+
"policy": "pinned",
|
|
28
|
+
"allowLatest": false
|
|
24
29
|
}
|
|
25
30
|
},
|
|
26
31
|
"coverage": {
|
|
27
|
-
"scriptTemplate": "c8
|
|
32
|
+
"scriptTemplate": "c8 --reporter=lcov --reporter=html --reporter=text-summary npm test"
|
|
28
33
|
},
|
|
29
34
|
"enforce": {
|
|
30
35
|
"sharedScriptsDisallow": [
|