@1aboveio/skills 0.18.0 → 0.19.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 +2 -2
- package/package.json +1 -1
- package/runtime/skills/distribution/generated/recipes.json +43 -23
- package/runtime/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +1 -1
- package/runtime/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +23 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +65 -15
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +1 -1
- package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +23 -1
- package/skills/engineering/resolve-issues/SKILL.md +1 -1
- package/skills/engineering/resolve-issues/generated/workflow-repair-policy.json +55 -11
- package/skills/engineering/resolve-issues/scripts/run-state.mjs +4 -4
- package/skills/engineering/resolve-release/references/related-skills.md +1 -0
- package/skills/engineering/rush-issues/LICENSE +3 -0
- package/skills/engineering/rush-issues/SKILL.md +179 -0
- package/skills/engineering/rush-issues/agents/openai.yaml +9 -0
- package/skills/engineering/rush-issues/evals/evals.json +65 -0
- package/skills/engineering/rush-issues/references/canary.md +45 -0
- package/skills/engineering/rush-issues/references/cicd.md +37 -0
- package/skills/engineering/rush-issues/references/combine.md +51 -0
- package/skills/engineering/rush-issues/references/expire.md +55 -0
- package/skills/engineering/rush-issues/references/exploration.md +53 -0
- package/skills/engineering/rush-issues/references/implementation.md +66 -0
- package/skills/engineering/rush-issues/references/preflight.md +31 -0
- package/skills/engineering/rush-issues/references/profiling.md +78 -0
- package/skills/engineering/rush-issues/references/review.md +48 -0
- package/skills/engineering/rush-issues/references/shared-modules.md +66 -0
- package/skills/engineering/rush-issues/references/task-plan.md +110 -0
- package/skills/engineering/rush-issues/scripts/discover-models.mjs +9 -0
- package/skills/engineering/rush-issues/scripts/model-catalog.mjs +9 -0
- package/skills/engineering/rush-issues/scripts/preflight-models.mjs +466 -0
- package/skills/engineering/rush-release/LICENSE +3 -0
- package/skills/engineering/rush-release/SKILL.md +99 -0
- package/skills/engineering/rush-release/agents/openai.yaml +8 -0
- package/skills/engineering/rush-release/evals/evals.json +44 -0
- package/skills/engineering/rush-release/references/candidate.md +30 -0
- package/skills/engineering/rush-release/references/cut.md +66 -0
- package/skills/engineering/rush-release/references/preflight.md +47 -0
- package/skills/engineering/rush-release/references/publish.md +100 -0
- package/skills/engineering/rush-release/scripts/apply.mjs +185 -0
- package/skills/engineering/rush-release/scripts/green-head.mjs +231 -0
- package/skills/engineering/rush-release/scripts/plan.mjs +264 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Changelog + SemVer plan from baseline tag..SHA.
|
|
3
|
+
//
|
|
4
|
+
// Usage:
|
|
5
|
+
// node plan.mjs --sha <sha> [--trunk main] [--json] [--strict]
|
|
6
|
+
//
|
|
7
|
+
// Exit: 0 ok · 1 git/inspect failed · 2 usage
|
|
8
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
9
|
+
import { join } from 'node:path'
|
|
10
|
+
import { spawnSync } from 'node:child_process'
|
|
11
|
+
import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
|
|
12
|
+
|
|
13
|
+
const SEMVER_RE = /^v?(\d+)\.(\d+)\.(\d+)$/
|
|
14
|
+
const TITLE_RE = /^\s*([a-zA-Z]+)(?:\(([^)]*)\))?(!)?:\s*(.+)$/
|
|
15
|
+
const SECTION_BY_TYPE = {
|
|
16
|
+
feat: 'Added',
|
|
17
|
+
fix: 'Fixed',
|
|
18
|
+
perf: 'Changed',
|
|
19
|
+
refactor: 'Changed',
|
|
20
|
+
revert: 'Removed',
|
|
21
|
+
security: 'Security',
|
|
22
|
+
}
|
|
23
|
+
const INTERNAL_TYPES = new Set(['build', 'chore', 'ci', 'docs', 'style', 'test'])
|
|
24
|
+
const SECTION_ORDER = ['Breaking', 'Added', 'Changed', 'Removed', 'Fixed', 'Security']
|
|
25
|
+
|
|
26
|
+
export function parseSemver(input) {
|
|
27
|
+
const match = SEMVER_RE.exec(String(input || '').trim())
|
|
28
|
+
if (!match) return null
|
|
29
|
+
return { major: Number(match[1]), minor: Number(match[2]), patch: Number(match[3]) }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function formatVersion(version) {
|
|
33
|
+
return `${version.major}.${version.minor}.${version.patch}`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function compareVersions(left, right) {
|
|
37
|
+
for (const key of ['major', 'minor', 'patch']) {
|
|
38
|
+
if (left[key] !== right[key]) return left[key] < right[key] ? -1 : 1
|
|
39
|
+
}
|
|
40
|
+
return 0
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function latestReleaseTag(tags) {
|
|
44
|
+
const parsed = tags
|
|
45
|
+
.map((tag) => ({ tag, version: parseSemver(tag) }))
|
|
46
|
+
.filter((item) => item.version)
|
|
47
|
+
if (parsed.length === 0) return null
|
|
48
|
+
parsed.sort((left, right) => compareVersions(left.version, right.version))
|
|
49
|
+
return parsed[parsed.length - 1]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function classifySubject(subject, body = '') {
|
|
53
|
+
const title = String(subject || '').trim()
|
|
54
|
+
const match = TITLE_RE.exec(title)
|
|
55
|
+
const type = match ? match[1].toLowerCase() : 'other'
|
|
56
|
+
const breaking = Boolean(match && match[3]) || /(^|\n)BREAKING[ -]CHANGE:/.test(body)
|
|
57
|
+
const internal = INTERNAL_TYPES.has(type)
|
|
58
|
+
const section = breaking ? 'Breaking' : internal ? null : (SECTION_BY_TYPE[type] || 'Changed')
|
|
59
|
+
return {
|
|
60
|
+
title,
|
|
61
|
+
type,
|
|
62
|
+
breaking,
|
|
63
|
+
internal,
|
|
64
|
+
section,
|
|
65
|
+
scope: match ? match[2] || null : null,
|
|
66
|
+
summary: match ? match[4] : title,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function bumpFor(changes, current, { strict = false } = {}) {
|
|
71
|
+
const zeroVer = !strict && current.major === 0
|
|
72
|
+
if (changes.some((change) => change.breaking)) {
|
|
73
|
+
return { bump: zeroVer ? 'minor' : 'major', reason: 'breaking change in the release content' }
|
|
74
|
+
}
|
|
75
|
+
if (changes.some((change) => change.type === 'feat')) {
|
|
76
|
+
return { bump: 'minor', reason: 'new feature in the release content' }
|
|
77
|
+
}
|
|
78
|
+
return { bump: 'patch', reason: 'fixes and internal changes only' }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function nextVersion(current, bump) {
|
|
82
|
+
if (bump === 'major') return { major: current.major + 1, minor: 0, patch: 0 }
|
|
83
|
+
if (bump === 'minor') return { major: current.major, minor: current.minor + 1, patch: 0 }
|
|
84
|
+
return { major: current.major, minor: current.minor, patch: current.patch + 1 }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function renderChangelog({ version, date, changes }) {
|
|
88
|
+
const groups = new Map(SECTION_ORDER.map((name) => [name, []]))
|
|
89
|
+
for (const change of changes) {
|
|
90
|
+
if (!change.section) continue
|
|
91
|
+
const list = groups.get(change.section)
|
|
92
|
+
if (list) list.push(change)
|
|
93
|
+
}
|
|
94
|
+
const lines = [`## [${version}] - ${date}`]
|
|
95
|
+
for (const name of SECTION_ORDER) {
|
|
96
|
+
const items = groups.get(name)
|
|
97
|
+
if (!items.length) continue
|
|
98
|
+
lines.push('', `### ${name}`)
|
|
99
|
+
for (const item of items) lines.push(`- ${item.summary}`)
|
|
100
|
+
}
|
|
101
|
+
if (lines.length === 1) {
|
|
102
|
+
lines.push('', '- See the commits included in this tag.')
|
|
103
|
+
}
|
|
104
|
+
return `${lines.join('\n')}\n`
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function discoverVersionFiles(root) {
|
|
108
|
+
const files = []
|
|
109
|
+
for (const name of ['package.json', 'package-lock.json', 'pyproject.toml', 'VERSION']) {
|
|
110
|
+
if (existsSync(join(root, name))) files.push(name)
|
|
111
|
+
}
|
|
112
|
+
return files
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function currentVersionFromFiles(root, files = discoverVersionFiles(root)) {
|
|
116
|
+
if (files.includes('package.json')) {
|
|
117
|
+
const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
|
|
118
|
+
const parsed = parseSemver(pkg.version)
|
|
119
|
+
if (parsed) return formatVersion(parsed)
|
|
120
|
+
}
|
|
121
|
+
if (files.includes('pyproject.toml')) {
|
|
122
|
+
const text = readFileSync(join(root, 'pyproject.toml'), 'utf8')
|
|
123
|
+
const match = text.match(/^version\s*=\s*"([^"]+)"/m)
|
|
124
|
+
const parsed = match && parseSemver(match[1])
|
|
125
|
+
if (parsed) return formatVersion(parsed)
|
|
126
|
+
}
|
|
127
|
+
if (files.includes('VERSION')) {
|
|
128
|
+
const parsed = parseSemver(readFileSync(join(root, 'VERSION'), 'utf8'))
|
|
129
|
+
if (parsed) return formatVersion(parsed)
|
|
130
|
+
}
|
|
131
|
+
return null
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function git(args, cwd) {
|
|
135
|
+
const result = spawnSync('git', args, { encoding: 'utf8', cwd })
|
|
136
|
+
if (result.status !== 0) {
|
|
137
|
+
const err = new Error(result.stderr.trim() || `git ${args.join(' ')} failed`)
|
|
138
|
+
err.exitCode = 1
|
|
139
|
+
throw err
|
|
140
|
+
}
|
|
141
|
+
return result.stdout
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function versionFilesAt(sha, cwd) {
|
|
145
|
+
const tree = new Set(git(['ls-tree', '-r', '--name-only', sha], cwd)
|
|
146
|
+
.split('\n')
|
|
147
|
+
.map((line) => line.trim())
|
|
148
|
+
.filter(Boolean))
|
|
149
|
+
return ['package.json', 'package-lock.json', 'pyproject.toml', 'VERSION']
|
|
150
|
+
.filter((name) => tree.has(name))
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function versionFromGitFiles(sha, files, cwd) {
|
|
154
|
+
const contents = (path) => git(['show', `${sha}:${path}`], cwd)
|
|
155
|
+
if (files.includes('package.json')) {
|
|
156
|
+
const parsed = parseSemver(JSON.parse(contents('package.json')).version)
|
|
157
|
+
if (parsed) return formatVersion(parsed)
|
|
158
|
+
}
|
|
159
|
+
if (files.includes('pyproject.toml')) {
|
|
160
|
+
const match = contents('pyproject.toml').match(/^version\s*=\s*"([^"]+)"/m)
|
|
161
|
+
const parsed = match && parseSemver(match[1])
|
|
162
|
+
if (parsed) return formatVersion(parsed)
|
|
163
|
+
}
|
|
164
|
+
if (files.includes('VERSION')) {
|
|
165
|
+
const parsed = parseSemver(contents('VERSION'))
|
|
166
|
+
if (parsed) return formatVersion(parsed)
|
|
167
|
+
}
|
|
168
|
+
return null
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function parseLog(stdout) {
|
|
172
|
+
return stdout
|
|
173
|
+
.split('\u001e')
|
|
174
|
+
.map((chunk) => chunk.trim())
|
|
175
|
+
.filter(Boolean)
|
|
176
|
+
.map((chunk) => {
|
|
177
|
+
const [subject, ...rest] = chunk.split('\n')
|
|
178
|
+
return classifySubject(subject, rest.join('\n'))
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function planRelease({ sha, trunk = 'main', strict = false, cwd = process.cwd(), now = new Date() } = {}) {
|
|
183
|
+
if (!sha) {
|
|
184
|
+
const err = new Error('usage: plan.mjs --sha <sha> [--trunk main] [--json] [--strict]')
|
|
185
|
+
err.exitCode = 2
|
|
186
|
+
throw err
|
|
187
|
+
}
|
|
188
|
+
git(['rev-parse', '--verify', sha], cwd)
|
|
189
|
+
const tags = git(['tag', '--merged', sha, '--list', 'v*'], cwd)
|
|
190
|
+
.split('\n')
|
|
191
|
+
.map((line) => line.trim())
|
|
192
|
+
.filter((tag) => parseSemver(tag))
|
|
193
|
+
const baseline = latestReleaseTag(tags)
|
|
194
|
+
const range = baseline ? `${baseline.tag}..${sha}` : sha
|
|
195
|
+
const logArgs = baseline
|
|
196
|
+
? ['log', range, '--first-parent', '--format=%s%n%b%x1e']
|
|
197
|
+
: ['log', sha, '--first-parent', '-n', '50', '--format=%s%n%b%x1e']
|
|
198
|
+
const changes = parseLog(git(logArgs, cwd))
|
|
199
|
+
const files = versionFilesAt(sha, cwd)
|
|
200
|
+
const fileVersion = versionFromGitFiles(sha, files, cwd)
|
|
201
|
+
const current = parseSemver(fileVersion) || baseline?.version || { major: 0, minor: 0, patch: 0 }
|
|
202
|
+
const { bump, reason } = bumpFor(changes, current, { strict })
|
|
203
|
+
const next = nextVersion(current, bump)
|
|
204
|
+
const version = formatVersion(next)
|
|
205
|
+
const date = now.toISOString().slice(0, 10)
|
|
206
|
+
return {
|
|
207
|
+
trunk,
|
|
208
|
+
sha,
|
|
209
|
+
baselineTag: baseline?.tag || null,
|
|
210
|
+
currentVersion: formatVersion(current),
|
|
211
|
+
bump,
|
|
212
|
+
reason,
|
|
213
|
+
nextVersion: version,
|
|
214
|
+
tag: `v${version}`,
|
|
215
|
+
versionFiles: files,
|
|
216
|
+
changes,
|
|
217
|
+
date,
|
|
218
|
+
changelogMarkdown: renderChangelog({ version, date, changes }),
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function parseArgs(argv) {
|
|
223
|
+
const opts = { json: false, strict: false, trunk: 'main', sha: null }
|
|
224
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
225
|
+
const arg = argv[i]
|
|
226
|
+
if (arg === '--json') opts.json = true
|
|
227
|
+
else if (arg === '--strict') opts.strict = true
|
|
228
|
+
else if (arg === '--sha') opts.sha = argv[++i]
|
|
229
|
+
else if (arg === '--trunk') opts.trunk = argv[++i]
|
|
230
|
+
else if (arg === '--help' || arg === '-h') opts.help = true
|
|
231
|
+
else {
|
|
232
|
+
const err = new Error(`unknown argument: ${arg}`)
|
|
233
|
+
err.exitCode = 2
|
|
234
|
+
throw err
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (opts.help) return opts
|
|
238
|
+
if (!opts.sha || !opts.trunk) {
|
|
239
|
+
const err = new Error('usage: plan.mjs --sha <sha> [--trunk main] [--json] [--strict]')
|
|
240
|
+
err.exitCode = 2
|
|
241
|
+
throw err
|
|
242
|
+
}
|
|
243
|
+
return opts
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function main(argv = process.argv.slice(2)) {
|
|
247
|
+
const opts = parseArgs(argv)
|
|
248
|
+
if (opts.help) {
|
|
249
|
+
process.stdout.write('plan.mjs --sha <sha> [--trunk main] [--json] [--strict]\n')
|
|
250
|
+
return 0
|
|
251
|
+
}
|
|
252
|
+
const result = planRelease(opts)
|
|
253
|
+
process.stdout.write(opts.json ? `${JSON.stringify(result, null, 2)}\n` : `${result.tag}\n${result.changelogMarkdown}`)
|
|
254
|
+
return 0
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (isMainModule(import.meta.url)) {
|
|
258
|
+
try {
|
|
259
|
+
process.exitCode = main()
|
|
260
|
+
} catch (error) {
|
|
261
|
+
process.stderr.write(`${error.message}\n`)
|
|
262
|
+
process.exitCode = error.exitCode || 1
|
|
263
|
+
}
|
|
264
|
+
}
|