@produck/agent-toolkit 0.1.0 → 0.1.3

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
@@ -40,12 +40,43 @@ npm --prefix tools/agent-toolkit run pack:check
40
40
 
41
41
  ## Manual publish
42
42
 
43
- Dry-run publish:
43
+ Lerna-like release flow (recommended):
44
44
 
45
- npm --prefix tools/agent-toolkit run publish:dry-run
45
+ 0) Interactive mode (TTY):
46
+
47
+ npm --prefix tools/agent-toolkit run release
48
+
49
+ Interactive prompts let you choose:
50
+
51
+ - version level: patch/minor/major
52
+ - action: dry-run or publish
53
+ - vcs mode: commit+tag / commit only / no commit+no tag
54
+
55
+ Default choices:
56
+
57
+ - patch
58
+ - dry-run
59
+ - commit + tag
60
+
61
+ Interactive mode handles both:
46
62
 
47
- Publish latest:
63
+ - version bump level (patch/minor/major)
64
+ - action mode (dry-run or publish)
65
+ - auto commit and tag after dry-run
48
66
 
67
+ Non-interactive flags:
68
+
69
+ - `npm --prefix tools/agent-toolkit run release -- patch --publish`
70
+ - `npm --prefix tools/agent-toolkit run release -- patch --no-tag`
71
+ - `npm --prefix tools/agent-toolkit run release -- patch --no-commit --no-tag`
72
+
73
+ Note:
74
+
75
+ - Release requires a clean working tree in `tools/agent-toolkit` before start.
76
+
77
+ Low-level commands (optional):
78
+
79
+ npm --prefix tools/agent-toolkit run publish:dry-run
49
80
  npm --prefix tools/agent-toolkit run publish:latest
50
81
 
51
82
  ## GitHub workflow
@@ -0,0 +1,271 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ import readline from 'node:readline/promises';
6
+
7
+ const LEVELS = new Set(['patch', 'minor', 'major']);
8
+
9
+ function usage() {
10
+ console.log([
11
+ 'Usage:',
12
+ ' node ./bin/release.mjs',
13
+ ' node ./bin/release.mjs <patch|minor|major> [--publish] [--no-commit] [--no-tag]',
14
+ ' node ./bin/release.mjs --interactive',
15
+ '',
16
+ 'Behavior:',
17
+ ' 1) bump version (no git tag)',
18
+ ' 2) run verify',
19
+ ' 3) run publish:dry-run',
20
+ ' 4) auto commit version change (default)',
21
+ ' 5) auto create git tag (default)',
22
+ ' 6) optionally publish latest when --publish is set',
23
+ '',
24
+ 'Interactive mode defaults:',
25
+ ' - release level: patch',
26
+ ' - publish mode: dry-run',
27
+ ' - auto commit: enabled',
28
+ ' - auto tag: enabled',
29
+ ].join('\n'));
30
+ }
31
+
32
+ function runNpm(args) {
33
+ const result = spawnSync('npm', args, {
34
+ stdio: 'inherit',
35
+ cwd: process.cwd(),
36
+ shell: true,
37
+ });
38
+
39
+ if (result.error) {
40
+ console.error(`[release] failed to run npm ${args.join(' ')}:`);
41
+ console.error(result.error.message);
42
+ process.exit(1);
43
+ }
44
+
45
+ if (result.status !== 0) {
46
+ process.exit(result.status ?? 1);
47
+ }
48
+ }
49
+
50
+ function runGit(args) {
51
+ const result = spawnSync('git', args, {
52
+ stdio: 'inherit',
53
+ cwd: process.cwd(),
54
+ });
55
+
56
+ if (result.error) {
57
+ console.error(`[release] failed to run git ${args.join(' ')}:`);
58
+ console.error(result.error.message);
59
+ process.exit(1);
60
+ }
61
+
62
+ if (result.status !== 0) {
63
+ process.exit(result.status ?? 1);
64
+ }
65
+ }
66
+
67
+ function getDirtyFiles() {
68
+ const result = spawnSync('git', ['status', '--porcelain'], {
69
+ stdio: ['ignore', 'pipe', 'pipe'],
70
+ cwd: process.cwd(),
71
+ encoding: 'utf8',
72
+ });
73
+
74
+ if (result.error || result.status !== 0) {
75
+ console.error('[release] unable to check git status');
76
+ process.exit(1);
77
+ }
78
+
79
+ return result.stdout
80
+ .split(/\r?\n/)
81
+ .map((line) => line.trim())
82
+ .filter((line) => line.length > 0);
83
+ }
84
+
85
+ function ensureReleaseWorkspaceClean() {
86
+ const dirty = getDirtyFiles();
87
+ if (dirty.length === 0) {
88
+ return;
89
+ }
90
+
91
+ console.error('[release] working tree is not clean before release:');
92
+ for (const line of dirty) {
93
+ console.error(` ${line}`);
94
+ }
95
+ console.error('[release] commit/stash changes and retry');
96
+ process.exit(2);
97
+ }
98
+
99
+ function commitAndTag(version, shouldCommit, shouldTag) {
100
+ if (shouldCommit) {
101
+ const message = `[UPGRADE] <infra>: release @produck/agent-toolkit ${version}`;
102
+ console.log(`[release] commit version bump: ${message}`);
103
+ runGit(['add', 'package.json']);
104
+ runGit(['commit', '-m', message]);
105
+ }
106
+
107
+ if (shouldTag) {
108
+ const tag = `agent-toolkit-v${version}`;
109
+ console.log(`[release] create tag: ${tag}`);
110
+ runGit(['tag', '-a', tag, '-m', `[UPGRADE] <infra>: tag ${tag}`]);
111
+ }
112
+ }
113
+
114
+ function bumpPreview(version, level) {
115
+ const parts = version.split('.').map((v) => Number(v));
116
+ if (parts.length !== 3 || parts.some((v) => Number.isNaN(v))) {
117
+ return `unknown (${version})`;
118
+ }
119
+
120
+ const [major, minor, patch] = parts;
121
+ if (level === 'patch') {
122
+ return `${major}.${minor}.${patch + 1}`;
123
+ }
124
+ if (level === 'minor') {
125
+ return `${major}.${minor + 1}.0`;
126
+ }
127
+ return `${major + 1}.0.0`;
128
+ }
129
+
130
+ async function askInteractive(currentVersion) {
131
+ const rl = readline.createInterface({
132
+ input: process.stdin,
133
+ output: process.stdout,
134
+ });
135
+
136
+ try {
137
+ console.log(`[release] current version: ${currentVersion}`);
138
+ console.log('[release] select release level:');
139
+ console.log(` 1) patch (default) -> ${bumpPreview(currentVersion, 'patch')}`);
140
+ console.log(` 2) minor -> ${bumpPreview(currentVersion, 'minor')}`);
141
+ console.log(` 3) major -> ${bumpPreview(currentVersion, 'major')}`);
142
+
143
+ const levelAnswer = (
144
+ await rl.question('Choose level [1/2/3] (default: 1): ')
145
+ ).trim();
146
+
147
+ let level = 'patch';
148
+ if (levelAnswer === '2') {
149
+ level = 'minor';
150
+ } else if (levelAnswer === '3') {
151
+ level = 'major';
152
+ } else if (levelAnswer && levelAnswer !== '1') {
153
+ console.error(`Invalid level choice: ${levelAnswer}`);
154
+ process.exit(2);
155
+ }
156
+
157
+ console.log('[release] select publish mode:');
158
+ console.log(' 1) dry-run only (default)');
159
+ console.log(' 2) publish latest after dry-run');
160
+
161
+ const publishAnswer = (
162
+ await rl.question('Choose mode [1/2] (default: 1): ')
163
+ ).trim();
164
+
165
+ let shouldPublish = false;
166
+ if (publishAnswer === '2') {
167
+ shouldPublish = true;
168
+ } else if (publishAnswer && publishAnswer !== '1') {
169
+ console.error(`Invalid publish choice: ${publishAnswer}`);
170
+ process.exit(2);
171
+ }
172
+
173
+ console.log('[release] auto commit and tag settings:');
174
+ console.log(' 1) commit + tag (default)');
175
+ console.log(' 2) commit only');
176
+ console.log(' 3) no commit, no tag');
177
+
178
+ const vcsAnswer = (
179
+ await rl.question('Choose vcs mode [1/2/3] (default: 1): ')
180
+ ).trim();
181
+
182
+ let shouldCommit = true;
183
+ let shouldTag = true;
184
+ if (vcsAnswer === '2') {
185
+ shouldCommit = true;
186
+ shouldTag = false;
187
+ } else if (vcsAnswer === '3') {
188
+ shouldCommit = false;
189
+ shouldTag = false;
190
+ } else if (vcsAnswer && vcsAnswer !== '1') {
191
+ console.error(`Invalid vcs choice: ${vcsAnswer}`);
192
+ process.exit(2);
193
+ }
194
+
195
+ return { level, shouldPublish, shouldCommit, shouldTag };
196
+ } finally {
197
+ rl.close();
198
+ }
199
+ }
200
+
201
+ function runRelease(level, shouldPublish, shouldCommit, shouldTag) {
202
+ if (!LEVELS.has(level)) {
203
+ console.error(`Invalid release level: ${level}`);
204
+ usage();
205
+ process.exit(2);
206
+ }
207
+
208
+ ensureReleaseWorkspaceClean();
209
+
210
+ const pkgFile = path.resolve('package.json');
211
+ const before = JSON.parse(fs.readFileSync(pkgFile, 'utf8')).version;
212
+
213
+ console.log(`[release] bump version: ${level}`);
214
+ runNpm(['version', level, '--no-git-tag-version']);
215
+
216
+ const after = JSON.parse(fs.readFileSync(pkgFile, 'utf8')).version;
217
+ console.log(`[release] version ${before} -> ${after}`);
218
+
219
+ console.log('[release] verify toolkit');
220
+ runNpm(['run', 'verify']);
221
+
222
+ console.log('[release] publish dry-run');
223
+ runNpm(['run', 'publish:dry-run']);
224
+
225
+ commitAndTag(after, shouldCommit, shouldTag);
226
+
227
+ if (shouldPublish) {
228
+ console.log('[release] publish latest');
229
+ runNpm(['run', 'publish:latest']);
230
+ } else {
231
+ console.log('[release] publish skipped (interactive default: dry-run)');
232
+ }
233
+ }
234
+
235
+ const args = process.argv.slice(2);
236
+ if (args.includes('--help') || args.includes('-h')) {
237
+ usage();
238
+ process.exit(0);
239
+ }
240
+
241
+ const interactiveRequested = args.length === 0 || args.includes('--interactive');
242
+ if (interactiveRequested) {
243
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
244
+ console.error('Interactive mode requires a TTY.');
245
+ usage();
246
+ process.exit(2);
247
+ }
248
+
249
+ const pkgFile = path.resolve('package.json');
250
+ const currentVersion = JSON.parse(fs.readFileSync(pkgFile, 'utf8')).version;
251
+ const interactive = await askInteractive(currentVersion);
252
+ runRelease(
253
+ interactive.level,
254
+ interactive.shouldPublish,
255
+ interactive.shouldCommit,
256
+ interactive.shouldTag
257
+ );
258
+ process.exit(0);
259
+ }
260
+
261
+ const level = args[0];
262
+ const shouldPublish = args.includes('--publish');
263
+ const shouldCommit = !args.includes('--no-commit');
264
+ const shouldTag = !args.includes('--no-tag');
265
+
266
+ if (shouldTag && !shouldCommit) {
267
+ console.error('[release] --no-commit cannot be combined with tag enabled');
268
+ process.exit(2);
269
+ }
270
+
271
+ runRelease(level, shouldPublish, shouldCommit, shouldTag);
package/package.json CHANGED
@@ -1,21 +1,22 @@
1
1
  {
2
2
  "name": "@produck/agent-toolkit",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "Central CLI toolkit for organization AI execution workflows",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "https://github.com/produck/.github.git",
8
+ "url": "git+https://github.com/produck/.github.git",
9
9
  "directory": "tools/agent-toolkit"
10
10
  },
11
11
  "bin": {
12
- "agent-toolkit": "./bin/agent-toolkit.mjs"
12
+ "agent-toolkit": "bin/agent-toolkit.mjs"
13
13
  },
14
14
  "scripts": {
15
15
  "verify": "node ./bin/agent-toolkit.mjs --help && node ./bin/agent-toolkit.mjs preflight --cwd . --require package.json",
16
16
  "pack:check": "npm pack --dry-run",
17
17
  "publish:dry-run": "npm publish --dry-run --access public",
18
- "publish:latest": "npm publish --access public"
18
+ "publish:latest": "npm publish --access public",
19
+ "release": "node ./bin/release.mjs"
19
20
  },
20
21
  "files": [
21
22
  "bin"