@1aboveio/skills 0.18.0 → 0.19.1

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