@the-open-engine/zeroshot 6.5.0 → 6.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@the-open-engine/zeroshot",
3
- "version": "6.5.0",
3
+ "version": "6.6.0",
4
4
  "description": "Multi-agent orchestration engine for Claude, Codex, and Gemini",
5
5
  "main": "src/orchestrator.js",
6
6
  "bin": {
@@ -71,6 +71,8 @@
71
71
  "check": "npm run typecheck && npm run lint",
72
72
  "check:all": "npm run check && npm run deadcode:all",
73
73
  "release": "semantic-release",
74
+ "release:preflight": "node scripts/release-preflight.js",
75
+ "release:assert-published": "node scripts/assert-release-published.js",
74
76
  "prepublishOnly": "npm run lint && npm run typecheck && npm run check:agent-cli-provider:ci",
75
77
  "prepare": "npm run build:agent-cli-provider && husky"
76
78
  },
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { execFileSync } = require('child_process');
4
+
5
+ function run(command, args) {
6
+ return execFileSync(command, args, { encoding: 'utf8' }).trim();
7
+ }
8
+
9
+ function packageName() {
10
+ return require('../package.json').name;
11
+ }
12
+
13
+ function npmLatest(name) {
14
+ return JSON.parse(run('npm', ['view', name, 'dist-tags.latest', '--json']));
15
+ }
16
+
17
+ function tagsPointingAtHead() {
18
+ run('git', ['fetch', '--tags', '--force']);
19
+ return run('git', ['tag', '--points-at', 'HEAD', '--list', 'v[0-9]*'])
20
+ .split(/\r?\n/)
21
+ .map((tag) => tag.trim())
22
+ .filter(Boolean);
23
+ }
24
+
25
+ function main() {
26
+ const name = packageName();
27
+ const latest = npmLatest(name);
28
+ const expectedTag = `v${latest}`;
29
+ const headTags = tagsPointingAtHead();
30
+
31
+ console.log(`npm latest for ${name}: ${latest}`);
32
+ console.log(`tags on HEAD: ${headTags.join(', ') || '(none)'}`);
33
+
34
+ if (!headTags.includes(expectedTag)) {
35
+ throw new Error(`expected ${expectedTag} to point at HEAD after release`);
36
+ }
37
+
38
+ console.log(`Release publication verified: ${name}@${latest}`);
39
+ }
40
+
41
+ if (require.main === module) {
42
+ try {
43
+ main();
44
+ } catch (error) {
45
+ console.error(`Release publication check failed: ${error.message}`);
46
+ process.exit(1);
47
+ }
48
+ }
49
+
50
+ module.exports = {
51
+ npmLatest,
52
+ tagsPointingAtHead,
53
+ };
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const https = require('https');
5
+ const { execFileSync } = require('child_process');
6
+
7
+ const RELEASE_ORDER = ['patch', 'minor', 'major'];
8
+ const REQUIRED_PLUGINS = [
9
+ '@semantic-release/commit-analyzer',
10
+ '@semantic-release/release-notes-generator',
11
+ '@semantic-release/npm',
12
+ '@semantic-release/github',
13
+ ];
14
+ const FORBIDDEN_EFFECTIVE_PLUGINS = new Set([
15
+ '@semantic-release/changelog',
16
+ '@semantic-release/git',
17
+ ]);
18
+
19
+ function normalizePlugin(plugin) {
20
+ return Array.isArray(plugin) ? plugin[0] : plugin;
21
+ }
22
+
23
+ function releaseRank(type) {
24
+ return RELEASE_ORDER.indexOf(type);
25
+ }
26
+
27
+ function maxReleaseType(current, candidate) {
28
+ if (!candidate) return current;
29
+ if (!current) return candidate;
30
+ return releaseRank(candidate) > releaseRank(current) ? candidate : current;
31
+ }
32
+
33
+ function analyzeMessage(message) {
34
+ const firstLine = String(message || '')
35
+ .split(/\r?\n/, 1)[0]
36
+ .trim();
37
+ if (!firstLine) return null;
38
+ if (/BREAKING CHANGE:|BREAKING-CHANGE:/m.test(message)) return 'major';
39
+
40
+ const separator = firstLine.indexOf(': ');
41
+ if (separator <= 0) return null;
42
+
43
+ const header = firstLine.slice(0, separator);
44
+ const breaking = header.endsWith('!');
45
+ const typeAndScope = breaking ? header.slice(0, -1) : header;
46
+ const scopeStart = typeAndScope.indexOf('(');
47
+ const type = scopeStart === -1 ? typeAndScope : typeAndScope.slice(0, scopeStart);
48
+
49
+ if (!/^[a-z][a-z0-9-]*$/i.test(type)) return null;
50
+ if (breaking) return 'major';
51
+
52
+ switch (type) {
53
+ case 'release':
54
+ case 'feat':
55
+ return 'minor';
56
+ case 'fix':
57
+ case 'perf':
58
+ return 'patch';
59
+ default:
60
+ return null;
61
+ }
62
+ }
63
+
64
+ function getPluginNames(releaseConfig) {
65
+ return (releaseConfig.plugins || []).map(normalizePlugin);
66
+ }
67
+
68
+ function validateReleaseConfig(packageJson) {
69
+ const releaseConfig = packageJson.release;
70
+ if (!releaseConfig || typeof releaseConfig !== 'object') {
71
+ throw new Error(
72
+ 'package.json#release is required so it takes precedence over stale .releaserc files'
73
+ );
74
+ }
75
+
76
+ const branches = Array.isArray(releaseConfig.branches)
77
+ ? releaseConfig.branches
78
+ : [releaseConfig.branches].filter(Boolean);
79
+ if (!branches.includes('main')) {
80
+ throw new Error('package.json#release.branches must include main');
81
+ }
82
+
83
+ const pluginNames = getPluginNames(releaseConfig);
84
+ for (const required of REQUIRED_PLUGINS) {
85
+ if (!pluginNames.includes(required)) {
86
+ throw new Error(`package.json#release.plugins is missing ${required}`);
87
+ }
88
+ }
89
+
90
+ for (const forbidden of FORBIDDEN_EFFECTIVE_PLUGINS) {
91
+ if (pluginNames.includes(forbidden)) {
92
+ throw new Error(
93
+ `${forbidden} must not be in the effective release config for protected main`
94
+ );
95
+ }
96
+ }
97
+
98
+ const npmPlugin = releaseConfig.plugins.find(
99
+ (plugin) => normalizePlugin(plugin) === '@semantic-release/npm'
100
+ );
101
+ const npmOptions = Array.isArray(npmPlugin) ? npmPlugin[1] || {} : {};
102
+ if (npmOptions.npmPublish !== true) {
103
+ throw new Error('@semantic-release/npm must publish to npm');
104
+ }
105
+
106
+ return pluginNames;
107
+ }
108
+
109
+ function readJson(path) {
110
+ return JSON.parse(fs.readFileSync(path, 'utf8'));
111
+ }
112
+
113
+ function git(args) {
114
+ return execFileSync('git', args, { encoding: 'utf8' }).trim();
115
+ }
116
+
117
+ function latestReleaseTag() {
118
+ return git(['describe', '--tags', '--abbrev=0', '--match', 'v[0-9]*']);
119
+ }
120
+
121
+ function commitMessagesSince(tag) {
122
+ const output = git(['log', '--format=%B%x1e', `${tag}..HEAD`]);
123
+ return output
124
+ .split('\x1e')
125
+ .map((message) => message.trim())
126
+ .filter(Boolean);
127
+ }
128
+
129
+ function prTitleFromEvent() {
130
+ const eventPath = process.env.GITHUB_EVENT_PATH;
131
+ if (!eventPath || !fs.existsSync(eventPath)) return null;
132
+ const event = readJson(eventPath);
133
+ return event.pull_request?.title || null;
134
+ }
135
+
136
+ function prNumberFromMergeQueueRef() {
137
+ const refName = process.env.GITHUB_REF_NAME || process.env.GITHUB_REF || '';
138
+ const match = refName.match(/(?:^|\/)pr-(\d+)-/);
139
+ return match ? match[1] : null;
140
+ }
141
+
142
+ function githubJson(path) {
143
+ const token = process.env.GITHUB_TOKEN;
144
+ const repository = process.env.GITHUB_REPOSITORY;
145
+ if (!token || !repository) return Promise.resolve(null);
146
+
147
+ return new Promise((resolve, reject) => {
148
+ const request = https.request(
149
+ {
150
+ hostname: 'api.github.com',
151
+ path: `/repos/${repository}${path}`,
152
+ headers: {
153
+ Accept: 'application/vnd.github+json',
154
+ Authorization: `Bearer ${token}`,
155
+ 'User-Agent': 'zeroshot-release-preflight',
156
+ 'X-GitHub-Api-Version': '2022-11-28',
157
+ },
158
+ },
159
+ (response) => {
160
+ let body = '';
161
+ response.setEncoding('utf8');
162
+ response.on('data', (chunk) => {
163
+ body += chunk;
164
+ });
165
+ response.on('end', () => {
166
+ if (response.statusCode < 200 || response.statusCode >= 300) {
167
+ reject(new Error(`GitHub API ${path} returned ${response.statusCode}: ${body}`));
168
+ return;
169
+ }
170
+ resolve(JSON.parse(body));
171
+ });
172
+ }
173
+ );
174
+ request.on('error', reject);
175
+ request.end();
176
+ });
177
+ }
178
+
179
+ async function prTitleFromMergeQueue() {
180
+ const number = prNumberFromMergeQueueRef();
181
+ if (!number) return null;
182
+ const pull = await githubJson(`/pulls/${number}`);
183
+ return pull?.title || null;
184
+ }
185
+
186
+ async function releaseSignal() {
187
+ const tag = latestReleaseTag();
188
+ const messages = commitMessagesSince(tag);
189
+ let releaseType = null;
190
+ for (const message of messages) {
191
+ releaseType = maxReleaseType(releaseType, analyzeMessage(message));
192
+ }
193
+
194
+ let title = prTitleFromEvent();
195
+ if (!title) title = await prTitleFromMergeQueue();
196
+ releaseType = maxReleaseType(releaseType, analyzeMessage(title));
197
+
198
+ return {
199
+ latestTag: tag,
200
+ commitCount: messages.length,
201
+ prTitle: title,
202
+ releaseType,
203
+ };
204
+ }
205
+
206
+ async function main() {
207
+ const packageJson = readJson('package.json');
208
+ const pluginNames = validateReleaseConfig(packageJson);
209
+ const signal = await releaseSignal();
210
+
211
+ console.log(`Effective release plugins: ${pluginNames.join(', ')}`);
212
+ console.log(`Latest release tag: ${signal.latestTag}`);
213
+ console.log(`Commits since tag: ${signal.commitCount}`);
214
+ if (signal.prTitle) console.log(`Release PR title: ${signal.prTitle}`);
215
+
216
+ if (!signal.releaseType) {
217
+ throw new Error(
218
+ 'Release promotion would publish nothing; use a release-worthy PR title/commit'
219
+ );
220
+ }
221
+
222
+ console.log(`Release preflight passed: ${signal.releaseType}`);
223
+ }
224
+
225
+ if (require.main === module) {
226
+ main().catch((error) => {
227
+ console.error(`Release preflight failed: ${error.message}`);
228
+ process.exit(1);
229
+ });
230
+ }
231
+
232
+ module.exports = {
233
+ analyzeMessage,
234
+ maxReleaseType,
235
+ validateReleaseConfig,
236
+ };