@1aboveio/skills 1.0.7 → 1.0.9

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 (224) hide show
  1. package/.codex-plugin/plugin.json +13 -0
  2. package/.codex-plugin/resources.json +1827 -0
  3. package/README.md +22 -0
  4. package/package.json +9 -2
  5. package/skills/app-dev/airflow/SKILL.md +1 -1
  6. package/skills/app-dev/airflow/release-check.mjs +3 -1
  7. package/skills/app-dev/android-foundation/SKILL.md +92 -34
  8. package/skills/app-dev/android-foundation/assets/android-ci-job.yml +13 -7
  9. package/skills/app-dev/android-foundation/references/ci.md +23 -13
  10. package/skills/app-dev/android-foundation/references/delivery-focus.md +29 -0
  11. package/skills/app-dev/android-foundation/references/diagnosis.md +136 -0
  12. package/skills/app-dev/android-foundation/references/gotchas.md +20 -11
  13. package/skills/app-dev/android-foundation/references/setup.md +15 -6
  14. package/skills/app-dev/android-foundation/release-check.mjs +3 -1
  15. package/skills/app-dev/android-foundation/scripts/device-run.sh +148 -42
  16. package/skills/app-dev/app-debug/SKILL.md +1 -1
  17. package/skills/app-dev/app-debug/release-check.mjs +3 -1
  18. package/skills/app-dev/better-auth/SKILL.md +1 -1
  19. package/skills/app-dev/better-auth/release-check.mjs +3 -1
  20. package/skills/app-dev/data-catalog/SKILL.md +1 -1
  21. package/skills/app-dev/data-catalog/release-check.mjs +3 -1
  22. package/skills/app-dev/data-quality/SKILL.md +1 -1
  23. package/skills/app-dev/data-quality/release-check.mjs +3 -1
  24. package/skills/app-dev/ios-foundation/SKILL.md +1 -1
  25. package/skills/app-dev/ios-foundation/release-check.mjs +3 -1
  26. package/skills/app-dev/monorepo/SKILL.md +1 -1
  27. package/skills/app-dev/monorepo/release-check.mjs +3 -1
  28. package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
  29. package/skills/app-dev/nextjs-fullstack/release-check.mjs +3 -1
  30. package/skills/app-dev/prisma-setup/SKILL.md +1 -1
  31. package/skills/app-dev/prisma-setup/release-check.mjs +3 -1
  32. package/skills/app-dev/pyspark/SKILL.md +1 -1
  33. package/skills/app-dev/pyspark/release-check.mjs +3 -1
  34. package/skills/app-dev/python-backend/SKILL.md +1 -1
  35. package/skills/app-dev/python-backend/release-check.mjs +3 -1
  36. package/skills/app-dev/shadcn/SKILL.md +1 -1
  37. package/skills/app-dev/shadcn/release-check.mjs +3 -1
  38. package/skills/app-dev/zod-v4/SKILL.md +1 -1
  39. package/skills/app-dev/zod-v4/release-check.mjs +3 -1
  40. package/skills/cicd/cloud-build/SKILL.md +1 -1
  41. package/skills/cicd/cloud-build/release-check.mjs +3 -1
  42. package/skills/cicd/cloud-debug/SKILL.md +1 -1
  43. package/skills/cicd/cloud-debug/release-check.mjs +3 -1
  44. package/skills/cicd/cloud-deploy/SKILL.md +1 -1
  45. package/skills/cicd/cloud-deploy/release-check.mjs +3 -1
  46. package/skills/cicd/docker/SKILL.md +1 -1
  47. package/skills/cicd/docker/release-check.mjs +3 -1
  48. package/skills/cicd/github-actions/SKILL.md +1 -1
  49. package/skills/cicd/github-actions/release-check.mjs +3 -1
  50. package/skills/cicd/github-flow/SKILL.md +1 -1
  51. package/skills/cicd/github-flow/release-check.mjs +3 -1
  52. package/skills/cicd/google-cloud/SKILL.md +1 -1
  53. package/skills/cicd/google-cloud/release-check.mjs +3 -1
  54. package/skills/cicd/mergify/SKILL.md +1 -1
  55. package/skills/cicd/mergify/release-check.mjs +3 -1
  56. package/skills/cicd/podman/SKILL.md +1 -1
  57. package/skills/cicd/podman/release-check.mjs +3 -1
  58. package/skills/content/article-review/SKILL.md +1 -1
  59. package/skills/content/article-review/release-check.mjs +3 -1
  60. package/skills/content/article-to-image/SKILL.md +1 -1
  61. package/skills/content/article-to-image/release-check.mjs +3 -1
  62. package/skills/content/flowchart-svg/SKILL.md +1 -1
  63. package/skills/content/flowchart-svg/release-check.mjs +3 -1
  64. package/skills/content/image-gen/SKILL.md +1 -1
  65. package/skills/content/image-gen/release-check.mjs +3 -1
  66. package/skills/content/mermaid/SKILL.md +1 -1
  67. package/skills/content/mermaid/release-check.mjs +3 -1
  68. package/skills/content/wechat-article-writer/SKILL.md +1 -1
  69. package/skills/content/wechat-article-writer/release-check.mjs +3 -1
  70. package/skills/content/wechat-publisher/SKILL.md +1 -1
  71. package/skills/content/wechat-publisher/release-check.mjs +3 -1
  72. package/skills/content/wechat-search/SKILL.md +1 -1
  73. package/skills/content/wechat-search/release-check.mjs +3 -1
  74. package/skills/content/xai-search/SKILL.md +88 -13
  75. package/skills/content/xai-search/release-check.mjs +3 -1
  76. package/skills/content/xai-search/scripts/xai_search.sh +125 -149
  77. package/skills/core/distill-writing/SKILL.md +1 -1
  78. package/skills/core/distill-writing/release-check.mjs +3 -1
  79. package/skills/core/doc-to-md/SKILL.md +1 -1
  80. package/skills/core/doc-to-md/release-check.mjs +3 -1
  81. package/skills/core/editorial-review/SKILL.md +1 -1
  82. package/skills/core/editorial-review/release-check.mjs +3 -1
  83. package/skills/core/house-style/SKILL.md +1 -1
  84. package/skills/core/house-style/release-check.mjs +3 -1
  85. package/skills/core/plain-language/SKILL.md +1 -1
  86. package/skills/core/plain-language/release-check.mjs +3 -1
  87. package/skills/core/polish-cn/SKILL.md +1 -1
  88. package/skills/core/polish-cn/release-check.mjs +3 -1
  89. package/skills/core/reg-parser/SKILL.md +1 -1
  90. package/skills/core/reg-parser/release-check.mjs +3 -1
  91. package/skills/core/terminology/SKILL.md +1 -1
  92. package/skills/core/terminology/release-check.mjs +3 -1
  93. package/skills/core/write-doc/SKILL.md +1 -1
  94. package/skills/core/write-doc/release-check.mjs +3 -1
  95. package/skills/engineering/better-review/SKILL.md +1 -1
  96. package/skills/engineering/better-review/release-check.mjs +3 -1
  97. package/skills/engineering/contributing/SKILL.md +1 -1
  98. package/skills/engineering/contributing/release-check.mjs +3 -1
  99. package/skills/engineering/e2e-test/SKILL.md +1 -1
  100. package/skills/engineering/e2e-test/release-check.mjs +3 -1
  101. package/skills/engineering/engineering-runtime/SKILL.md +1 -1
  102. package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
  103. package/skills/engineering/engineering-runtime/release-check.mjs +3 -1
  104. package/skills/engineering/engineering-runtime/scripts/installation-resolver.mjs +191 -0
  105. package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +51 -10
  106. package/skills/engineering/ensure-coverage/SKILL.md +1 -1
  107. package/skills/engineering/ensure-coverage/release-check.mjs +3 -1
  108. package/skills/engineering/golden-path/SKILL.md +1 -1
  109. package/skills/engineering/golden-path/release-check.mjs +3 -1
  110. package/skills/engineering/harness-runtime/SKILL.md +1 -1
  111. package/skills/engineering/harness-runtime/release-check.mjs +3 -1
  112. package/skills/engineering/resolve-issues/SKILL.md +11 -20
  113. package/skills/engineering/resolve-issues/references/delivery-progress.md +63 -0
  114. package/skills/engineering/resolve-issues/references/expire.md +24 -11
  115. package/skills/engineering/resolve-issues/references/implementation.md +9 -1
  116. package/skills/engineering/resolve-issues/references/preflight.md +7 -0
  117. package/skills/engineering/resolve-issues/references/shared-modules.md +27 -0
  118. package/skills/engineering/resolve-issues/references/task-plan.md +12 -5
  119. package/skills/engineering/resolve-issues/release-check.mjs +3 -1
  120. package/skills/engineering/resolve-issues/scripts/active-installation.mjs +32 -0
  121. package/skills/engineering/resolve-issues/scripts/install-hooks.mjs +3 -0
  122. package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +17 -1
  123. package/skills/engineering/resolve-release/SKILL.md +1 -1
  124. package/skills/engineering/resolve-release/release-check.mjs +3 -1
  125. package/skills/engineering/rush-issues/SKILL.md +5 -2
  126. package/skills/engineering/rush-issues/release-check.mjs +3 -1
  127. package/skills/engineering/rush-release/SKILL.md +1 -1
  128. package/skills/engineering/rush-release/release-check.mjs +3 -1
  129. package/skills/engineering/test-quality/SKILL.md +1 -1
  130. package/skills/engineering/test-quality/release-check.mjs +3 -1
  131. package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
  132. package/skills/risk-compliance/compliance-audit/release-check.mjs +3 -1
  133. package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
  134. package/skills/risk-compliance/contract-compare/release-check.mjs +3 -1
  135. package/skills/risk-compliance/contract-review/SKILL.md +1 -1
  136. package/skills/risk-compliance/contract-review/release-check.mjs +3 -1
  137. package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
  138. package/skills/risk-compliance/doc-authenticity/release-check.mjs +3 -1
  139. package/skills/risk-compliance/fraud-analysis/SKILL.md +1 -1
  140. package/skills/risk-compliance/fraud-analysis/release-check.mjs +3 -1
  141. package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
  142. package/skills/risk-compliance/payment-analysis/release-check.mjs +3 -1
  143. package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
  144. package/skills/risk-compliance/similar-domain/release-check.mjs +3 -1
  145. package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
  146. package/skills/risk-compliance/tld-detection/release-check.mjs +3 -1
  147. package/skills/app-dev/android-foundation/scripts/device-run.test.mjs +0 -172
  148. package/skills/app-dev/data-catalog/scripts/data-catalog.test.mjs +0 -36
  149. package/skills/app-dev/data-quality/scripts/data-quality.test.mjs +0 -12
  150. package/skills/cicd/github-actions/scripts/ci-run-audit.test.mjs +0 -637
  151. package/skills/cicd/github-flow/scripts/flow.test.mjs +0 -114
  152. package/skills/cicd/github-flow/scripts/persist-conventions.test.mjs +0 -108
  153. package/skills/cicd/mergify/scripts/audit.test.mjs +0 -201
  154. package/skills/cicd/mergify/scripts/entrypoints.test.mjs +0 -106
  155. package/skills/cicd/mergify/scripts/pr-620-mergify-comments.fixture.json +0 -30
  156. package/skills/cicd/mergify/scripts/stack-push.test.mjs +0 -233
  157. package/skills/cicd/mergify/scripts/watch-pr-delivery.test.mjs +0 -1826
  158. package/skills/content/image-gen/scripts/credentials.test.mjs +0 -212
  159. package/skills/content/image-gen/scripts/options.test.mjs +0 -245
  160. package/skills/content/xai-search/tests/smoke_test.sh +0 -36
  161. package/skills/core/polish-cn/scripts/polish-cn.test.mjs +0 -244
  162. package/skills/core/reg-parser/scripts/reg-parser.test.mjs +0 -165
  163. package/skills/engineering/better-review/scripts/pr-context.test.mjs +0 -168
  164. package/skills/engineering/better-review/scripts/rereview-scope.test.mjs +0 -587
  165. package/skills/engineering/better-review/scripts/scan-diff.test.mjs +0 -414
  166. package/skills/engineering/better-review/scripts/vendor-sync.test.mjs +0 -96
  167. package/skills/engineering/contributing/scripts/persist-conventions.test.mjs +0 -118
  168. package/skills/engineering/e2e-test/scripts/presentation-checks.test.mjs +0 -106
  169. package/skills/engineering/e2e-test/scripts/presentation-perceivability.test.mjs +0 -117
  170. package/skills/engineering/e2e-test/scripts/presentation-reachability.test.mjs +0 -69
  171. package/skills/engineering/e2e-test/scripts/presentation-render-health.test.mjs +0 -98
  172. package/skills/engineering/e2e-test/scripts/presentation-sweep.test.mjs +0 -71
  173. package/skills/engineering/e2e-test/scripts/presentation-temporal.test.mjs +0 -59
  174. package/skills/engineering/e2e-test/scripts/project-route-manifest.test.mjs +0 -76
  175. package/skills/engineering/engineering-runtime/engineering-runtime.test.mjs +0 -68
  176. package/skills/engineering/engineering-runtime/scripts/invocation-policy.test.mjs +0 -95
  177. package/skills/engineering/engineering-runtime/scripts/main-module.test.mjs +0 -288
  178. package/skills/engineering/engineering-runtime/scripts/required-check-plan.test.mjs +0 -96
  179. package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.test.mjs +0 -96
  180. package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.test.mjs +0 -47
  181. package/skills/engineering/ensure-coverage/scripts/ci-audit.test.mjs +0 -86
  182. package/skills/engineering/ensure-coverage/scripts/coverage-ledger.test.mjs +0 -447
  183. package/skills/engineering/ensure-coverage/scripts/design-parity.test.mjs +0 -552
  184. package/skills/engineering/ensure-coverage/scripts/lint-tests.test.mjs +0 -289
  185. package/skills/engineering/ensure-coverage/scripts/mock-policy-config.test.mjs +0 -119
  186. package/skills/engineering/golden-path/scripts/golden-path.test.mjs +0 -1457
  187. package/skills/engineering/harness-runtime/discover-models.test.mjs +0 -363
  188. package/skills/engineering/harness-runtime/harness-runtime.test.mjs +0 -967
  189. package/skills/engineering/harness-runtime/model-catalog-seed.test.mjs +0 -829
  190. package/skills/engineering/harness-runtime/model-catalog.test.mjs +0 -366
  191. package/skills/engineering/resolve-issues/scripts/cleanup-runs.test.mjs +0 -280
  192. package/skills/engineering/resolve-issues/scripts/dropbox.test.mjs +0 -346
  193. package/skills/engineering/resolve-issues/scripts/findings-reuse.test.mjs +0 -88
  194. package/skills/engineering/resolve-issues/scripts/github-reviews.test.mjs +0 -264
  195. package/skills/engineering/resolve-issues/scripts/install-hooks.test.mjs +0 -79
  196. package/skills/engineering/resolve-issues/scripts/issue-tree.test.mjs +0 -107
  197. package/skills/engineering/resolve-issues/scripts/manual-invocation.test.mjs +0 -14
  198. package/skills/engineering/resolve-issues/scripts/next-actions-capacity.test.mjs +0 -149
  199. package/skills/engineering/resolve-issues/scripts/next-actions.test.mjs +0 -915
  200. package/skills/engineering/resolve-issues/scripts/plan-sync.test.mjs +0 -156
  201. package/skills/engineering/resolve-issues/scripts/preflight-models.test.mjs +0 -448
  202. package/skills/engineering/resolve-issues/scripts/ready-check.test.mjs +0 -217
  203. package/skills/engineering/resolve-issues/scripts/rehydrate.test.mjs +0 -91
  204. package/skills/engineering/resolve-issues/scripts/review-verdict.test.mjs +0 -1060
  205. package/skills/engineering/resolve-issues/scripts/size-check.test.mjs +0 -458
  206. package/skills/engineering/resolve-release/scripts/apply.test.mjs +0 -99
  207. package/skills/engineering/resolve-release/scripts/dark-flags.test.mjs +0 -100
  208. package/skills/engineering/resolve-release/scripts/flow.test.mjs +0 -340
  209. package/skills/engineering/resolve-release/scripts/green-head.test.mjs +0 -101
  210. package/skills/engineering/resolve-release/scripts/npm-version-absence.test.mjs +0 -426
  211. package/skills/engineering/resolve-release/scripts/plan-npm.test.mjs +0 -659
  212. package/skills/engineering/resolve-release/scripts/plan-toml-boundary.test.mjs +0 -317
  213. package/skills/engineering/resolve-release/scripts/plan-uv.test.mjs +0 -291
  214. package/skills/engineering/resolve-release/scripts/plan.test.mjs +0 -428
  215. package/skills/engineering/test-quality/scripts/lint-tests.test.mjs +0 -591
  216. package/skills/engineering/test-quality/scripts/mock-policy-config.test.mjs +0 -119
  217. package/skills/engineering/test-quality/scripts/persist-conventions.test.mjs +0 -104
  218. package/skills/risk-compliance/compliance-audit/scripts/compliance-audit.test.mjs +0 -14
  219. package/skills/risk-compliance/doc-authenticity/manual-invocation.test.mjs +0 -12
  220. package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.test.mjs +0 -212
  221. package/skills/risk-compliance/tld-detection/scripts/detect_ladders.test.mjs +0 -14
  222. package/skills/risk-compliance/tld-detection/scripts/detect_streams.test.mjs +0 -14
  223. package/skills/risk-compliance/tld-detection/tests/test_detect_ladders.py +0 -134
  224. package/skills/risk-compliance/tld-detection/tests/test_detect_streams.py +0 -520
@@ -1,217 +0,0 @@
1
- import { test } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import { spawnSync } from 'node:child_process'
4
- import { chmodSync, mkdtempSync, writeFileSync } from 'node:fs'
5
- import { tmpdir } from 'node:os'
6
- import { dirname, join } from 'node:path'
7
- import { fileURLToPath } from 'node:url'
8
-
9
- import { DEFAULT_LABEL, ReadyCheckError, parseArgs, parseIssueRef, readyCheck, treeFetchers } from './ready-check.mjs'
10
-
11
- const script = join(dirname(fileURLToPath(import.meta.url)), 'ready-check.mjs')
12
-
13
- // A small in-memory GitHub: issues keyed "owner/name#n", sub-issues as API-shaped children.
14
- function fakeGithub(issues, subIssues = {}) {
15
- return {
16
- fetchIssue: ({ repo, number }) => {
17
- const issue = issues[`${repo}#${number}`]
18
- if (!issue) throw new Error(`HTTP 404 ${repo}#${number}`)
19
- return issue
20
- },
21
- fetchSubIssues: ({ repo, number }) => (subIssues[`${repo}#${number}`] || []).map((ref) => {
22
- const [childRepo, n] = ref.split('#')
23
- return { number: Number(n), repository_url: `https://api.github.com/repos/${childRepo}` }
24
- }),
25
- }
26
- }
27
- const issue = (labels, state = 'open', title = 't') => ({ state, title, labels: labels.map((name) => ({ name })) })
28
-
29
- test('the default label is ready-for-agent', () => {
30
- assert.equal(DEFAULT_LABEL, 'ready-for-agent')
31
- })
32
-
33
- test('an issue and all its sub-issues labeled ready passes', () => {
34
- const gh = fakeGithub(
35
- { 'o/r#1': issue(['epic', 'ready-for-agent']), 'o/r#2': issue(['ready-for-agent']), 'o/r#3': issue(['Ready-For-Agent']) },
36
- { 'o/r#1': ['o/r#2', 'o/r#3'] },
37
- )
38
- const result = readyCheck([{ repo: 'o/r', number: 1 }], gh)
39
- assert.equal(result.ready, true)
40
- assert.equal(result.checked.length, 3, 'label match ignores case')
41
- })
42
-
43
- test('an epic without the label passes when all its open sub-issues are ready', () => {
44
- const gh = fakeGithub(
45
- { 'o/r#1': issue(['epic']), 'o/r#2': issue(['ready-for-agent']), 'o/r#3': issue(['epic']), 'o/r#4': issue(['ready-for-agent']) },
46
- { 'o/r#1': ['o/r#2', 'o/r#3'], 'o/r#3': ['o/r#4'] },
47
- )
48
- const result = readyCheck([{ repo: 'o/r', number: 1 }], gh)
49
- assert.equal(result.ready, true)
50
- assert.deepEqual(result.checked.filter((c) => c.exempt).map((c) => c.ref), ['o/r#1', 'o/r#3'], 'nested epics are exempt too')
51
- })
52
-
53
- test('an epic without the label still reports a sub-issue that is not ready', () => {
54
- const gh = fakeGithub({ 'o/r#1': issue(['epic']), 'o/r#2': issue(['triage']) }, { 'o/r#1': ['o/r#2'] })
55
- const result = readyCheck([{ repo: 'o/r', number: 1 }], gh)
56
- assert.equal(result.ready, false)
57
- assert.deepEqual(result.missing.map((m) => m.ref), ['o/r#2'])
58
- })
59
-
60
- test('an open issue whose sub-issues are all closed is a unit again: a rollup that needs the label', () => {
61
- // #1343 was held, its prerequisite #1659 was filed under it and closed: the old check
62
- // called #1343 resolved and the run dropped its remaining work.
63
- const gh = fakeGithub(
64
- { 'o/r#1': issue(['epic']), 'o/r#2': issue([], 'closed'), 'o/r#3': issue(['ready-for-agent']), 'o/r#4': issue([], 'closed') },
65
- { 'o/r#1': ['o/r#2'], 'o/r#3': ['o/r#4'] },
66
- )
67
- const result = readyCheck([{ repo: 'o/r', number: 1 }, { repo: 'o/r', number: 3 }], gh)
68
- assert.equal(result.ready, false)
69
- assert.deepEqual(result.missing.map((m) => m.ref), ['o/r#1'])
70
- assert.deepEqual(result.rollups.map((r) => r.ref), ['o/r#1', 'o/r#3'])
71
- })
72
-
73
- test('an epic whose only open sub-issue is a rollup stays a container', () => {
74
- const gh = fakeGithub(
75
- { 'o/r#1': issue(['epic']), 'o/r#2': issue(['ready-for-agent']), 'o/r#3': issue([], 'closed') },
76
- { 'o/r#1': ['o/r#2'], 'o/r#2': ['o/r#3'] },
77
- )
78
- const result = readyCheck([{ repo: 'o/r', number: 1 }], gh)
79
- assert.equal(result.ready, true)
80
- assert.equal(result.checked.find((c) => c.ref === 'o/r#1').exempt, 'has open sub-issues')
81
- assert.deepEqual(result.rollups.map((r) => r.ref), ['o/r#2'])
82
- })
83
-
84
- test('a plain issue with no sub-issues is a unit, not a rollup', () => {
85
- const gh = fakeGithub({ 'o/r#1': issue([]) })
86
- const result = readyCheck([{ repo: 'o/r', number: 1 }], gh)
87
- assert.equal(result.ready, false)
88
- assert.deepEqual(result.rollups, [])
89
- })
90
-
91
- test('an epic with an unreadable sub-issue is neither exempt nor a rollup', () => {
92
- const gh = fakeGithub({ 'o/r#1': issue(['epic']) }, { 'o/r#1': ['o/r#404'] })
93
- const result = readyCheck([{ repo: 'o/r', number: 1 }], gh)
94
- assert.equal(result.ready, false)
95
- assert.deepEqual(result.missing.map((m) => m.ref), ['o/r#1'])
96
- assert.deepEqual(result.unreadable.map((m) => m.ref), ['o/r#404'])
97
- const partly = fakeGithub({ 'o/r#1': issue(['epic']), 'o/r#2': issue([], 'closed') }, { 'o/r#1': ['o/r#2', 'o/r#404'] })
98
- assert.deepEqual(readyCheck([{ repo: 'o/r', number: 1 }], partly).rollups, [], 'one closed plus one unreadable is not a rollup')
99
- })
100
-
101
- test('tree fetchers answer from the GraphQL tree and fall back to REST for the rest', () => {
102
- const tree = { nodes: {
103
- 'o/r#1': { key: 'o/r#1', state: 'open', title: 'epic', labels: ['epic'], children: ['o/r#2', 'x/y#9'] },
104
- 'o/r#2': { key: 'o/r#2', state: 'open', title: 'a', labels: ['ready-for-agent'], children: [] },
105
- 'x/y#9': { key: 'x/y#9', state: 'open', title: 'deep', labels: ['ready-for-agent'], children: null },
106
- } }
107
- const calls = []
108
- const rest = fakeGithub({ 'x/y#10': issue(['ready-for-agent']) }, { 'x/y#9': ['x/y#10'] })
109
- const fallback = { fetchIssue: (r) => { calls.push(`issue ${r.repo}#${r.number}`); return rest.fetchIssue(r) }, fetchSubIssues: (r) => { calls.push(`subs ${r.repo}#${r.number}`); return rest.fetchSubIssues(r) } }
110
- const result = readyCheck([{ repo: 'o/r', number: 1 }], treeFetchers([tree], fallback))
111
- assert.equal(result.ready, true)
112
- assert.deepEqual(calls, ['subs x/y#9', 'issue x/y#10', 'subs x/y#10'], 'only the part the tree did not cover hits REST')
113
- })
114
-
115
- test('a sub-issue shared by two epics makes both containers', () => {
116
- const gh = fakeGithub(
117
- { 'o/r#1': issue(['epic']), 'o/r#2': issue(['epic']), 'o/r#3': issue(['ready-for-agent']) },
118
- { 'o/r#1': ['o/r#3'], 'o/r#2': ['o/r#3'] },
119
- )
120
- assert.equal(readyCheck([{ repo: 'o/r', number: 1 }, { repo: 'o/r', number: 2 }], gh).ready, true)
121
- })
122
-
123
- test('a sub-issue at any depth, in another repository, without the label is reported', () => {
124
- const gh = fakeGithub(
125
- { 'o/r#1': issue(['ready-for-agent']), 'o/r#2': issue(['ready-for-agent']), 'x/y#9': issue(['bug']) },
126
- { 'o/r#1': ['o/r#2'], 'o/r#2': ['x/y#9'] },
127
- )
128
- const result = readyCheck([{ repo: 'o/r', number: 1 }], gh)
129
- assert.equal(result.ready, false)
130
- assert.deepEqual(result.missing, [{ ref: 'x/y#9', title: 't', labels: ['bug'], parent: 'o/r#2' }])
131
- assert.equal(result.checked.find((c) => c.ref === 'x/y#9').depth, 2)
132
- })
133
-
134
- test('closed sub-issues are skipped; a closed named issue is still checked', () => {
135
- const gh = fakeGithub(
136
- { 'o/r#1': issue(['ready-for-agent']), 'o/r#2': issue([], 'closed'), 'o/r#5': issue([], 'closed') },
137
- { 'o/r#1': ['o/r#2'] },
138
- )
139
- assert.equal(readyCheck([{ repo: 'o/r', number: 1 }], gh).ready, true)
140
- assert.equal(readyCheck([{ repo: 'o/r', number: 5 }], gh).ready, false)
141
- })
142
-
143
- test('an unreadable issue or sub-issue list fails closed', () => {
144
- const gh = fakeGithub({ 'o/r#1': issue(['ready-for-agent']) }, { 'o/r#1': ['o/r#404'] })
145
- const result = readyCheck([{ repo: 'o/r', number: 1 }], gh)
146
- assert.equal(result.ready, false)
147
- assert.equal(result.unreadable[0].ref, 'o/r#404')
148
- const brokenList = { ...gh, fetchSubIssues: () => { throw new Error('HTTP 502') } }
149
- assert.equal(readyCheck([{ repo: 'o/r', number: 1 }], brokenList).ready, false)
150
- })
151
-
152
- test('a sub-issue loop is walked once', () => {
153
- const gh = fakeGithub(
154
- { 'o/r#1': issue(['ready-for-agent']), 'o/r#2': issue(['ready-for-agent']) },
155
- { 'o/r#1': ['o/r#2'], 'o/r#2': ['o/r#1'] },
156
- )
157
- assert.equal(readyCheck([{ repo: 'o/r', number: 1 }], gh).checked.length, 2)
158
- })
159
-
160
- test('a custom label is honored', () => {
161
- const gh = fakeGithub({ 'o/r#1': issue(['agent-ok']) })
162
- assert.equal(readyCheck([{ repo: 'o/r', number: 1 }], { ...gh, label: 'agent-ok' }).ready, true)
163
- assert.equal(readyCheck([{ repo: 'o/r', number: 1 }], gh).ready, false)
164
- })
165
-
166
- test('issue references accept owner/name#n, URLs, and bare numbers with a repo', () => {
167
- assert.deepEqual(parseIssueRef('o/r#12'), { repo: 'o/r', number: 12 })
168
- assert.deepEqual(parseIssueRef('https://github.com/o/r/issues/7'), { repo: 'o/r', number: 7 })
169
- assert.deepEqual(parseIssueRef('#3', 'o/r'), { repo: 'o/r', number: 3 })
170
- assert.throws(() => parseIssueRef('3'), ReadyCheckError)
171
- assert.throws(() => parseArgs([]), ReadyCheckError)
172
- assert.throws(() => parseArgs(['--bogus']), ReadyCheckError)
173
- })
174
-
175
- // ---- CLI against a fake `gh` on PATH ----
176
-
177
- function withFakeGh(responses) {
178
- const dir = mkdtempSync(join(tmpdir(), 'ready-check-gh-'))
179
- const cases = Object.entries(responses)
180
- .map(([path, body]) => ` "${path}") cat <<'EOF'\n${JSON.stringify(body)}\nEOF\n ;;`)
181
- .join('\n')
182
- // gh api [--paginate --slurp] <path>: the path is the last argument.
183
- writeFileSync(join(dir, 'gh'), `#!/bin/sh\nfor last; do :; done\ncase "$last" in\n${cases}\n *) echo "HTTP 404" >&2; exit 1 ;;\nesac\n`)
184
- chmodSync(join(dir, 'gh'), 0o755)
185
- return { ...process.env, PATH: `${dir}:${process.env.PATH}` }
186
- }
187
-
188
- test('CLI exits 0 with the outcome in the output, 2 on bad input; --exit-outcome restores the old codes', () => {
189
- const env = withFakeGh({
190
- 'repos/o/r/issues/1': issue(['ready-for-agent']),
191
- 'repos/o/r/issues/1/sub_issues': [[{ number: 2, repository_url: 'https://api.github.com/repos/o/r' }]],
192
- 'repos/o/r/issues/2': issue(['triage'], 'open', 'Add login'),
193
- 'repos/o/r/issues/2/sub_issues': [[]],
194
- 'repos/o/r/issues/3': issue(['ready-for-agent']),
195
- 'repos/o/r/issues/3/sub_issues': [[]],
196
- })
197
- const ok = spawnSync(process.execPath, [script, '--issue', 'o/r#3'], { env, encoding: 'utf8' })
198
- assert.equal(ok.status, 0, ok.stderr)
199
- assert.equal(JSON.parse(ok.stdout).outcome, 'ready')
200
- const unlabeled = spawnSync(process.execPath, [script, '--repo', 'o/r', '--issue', '1'], { env, encoding: 'utf8' })
201
- assert.equal(unlabeled.status, 0, unlabeled.stderr)
202
- assert.equal(JSON.parse(unlabeled.stdout).outcome, 'unlabeled')
203
- assert.deepEqual(JSON.parse(unlabeled.stdout).missing, [{ ref: 'o/r#2', title: 'Add login', labels: ['triage'], parent: 'o/r#1' }])
204
- const named = spawnSync(process.execPath, [script, '--issue', 'o/r#2'], { env, encoding: 'utf8' })
205
- assert.equal(named.status, 0, named.stderr)
206
- assert.equal(JSON.parse(named.stdout).missing[0].parent, null)
207
- const unreadable = spawnSync(process.execPath, [script, '--issue', 'o/r#2', '--issue', 'o/r#404'], { env, encoding: 'utf8' })
208
- assert.equal(unreadable.status, 0, unreadable.stderr)
209
- assert.equal(JSON.parse(unreadable.stdout).outcome, 'unreadable', 'an unreadable issue outranks an unlabeled one')
210
- const legacy = (...args) => spawnSync(process.execPath, [script, '--exit-outcome', ...args], { env, encoding: 'utf8' }).status
211
- assert.equal(legacy('--issue', 'o/r#3'), 0)
212
- assert.equal(legacy('--repo', 'o/r', '--issue', '1'), 3)
213
- assert.equal(legacy('--issue', 'o/r#2', '--issue', 'o/r#404'), 1)
214
- assert.equal(legacy(), 2)
215
- const usage = spawnSync(process.execPath, [script], { env, encoding: 'utf8' })
216
- assert.equal(usage.status, 2)
217
- })
@@ -1,91 +0,0 @@
1
- import { test } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import { spawnSync } from 'node:child_process'
4
- import fs from 'node:fs'
5
- import os from 'node:os'
6
- import path from 'node:path'
7
- import { fileURLToPath } from 'node:url'
8
-
9
- import { activeRuns, message, ACTIVE_HOURS } from './rehydrate.mjs'
10
- import { RULES, SKILL_PATH } from './next-actions.mjs'
11
-
12
- const SCRIPT = path.join(path.dirname(fileURLToPath(import.meta.url)), 'rehydrate.mjs')
13
-
14
- function repo(runs) {
15
- const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'rehydrate-'))
16
- for (const [name, plan, ageHours = 0] of runs) {
17
- const dir = path.join(cwd, '.resolve-issues', 'rush', name)
18
- fs.mkdirSync(dir, { recursive: true })
19
- const file = path.join(dir, 'task-plan.json')
20
- fs.writeFileSync(file, typeof plan === 'string' ? plan : JSON.stringify(plan))
21
- const at = new Date(Date.now() - ageHours * 3_600_000)
22
- fs.utimesSync(file, at, at)
23
- }
24
- return cwd
25
- }
26
-
27
- test('only a recent run with an open unit counts; finished, stale, and unreadable runs do not', () => {
28
- const cwd = repo([
29
- ['live', { skill: 'rush-issues', planRevision: 267, units: [{ id: '1', status: 'succeeded' }, { id: '2', status: 'in-flight' }, { id: '3', status: 'blocked' }] }, 1],
30
- ['done', { units: [{ id: '1', status: 'succeeded' }, { id: '2', status: 'cancelled' }] }],
31
- ['stale', { units: [{ id: '1', status: 'in-flight' }] }, ACTIVE_HOURS + 1],
32
- ['broken', '{not json'],
33
- ])
34
- const runs = activeRuns(cwd)
35
- assert.deepEqual(runs, [{ dir: path.join(cwd, '.resolve-issues', 'rush', 'live'), skill: 'rush-issues', revision: 267, open: 2 }])
36
- assert.deepEqual(activeRuns(fs.mkdtempSync(path.join(os.tmpdir(), 'rehydrate-none-'))), [], 'no .resolve-issues: nothing')
37
- const unit = path.join(cwd, '.resolve-issues', 'rush', 'live', 'units', '7')
38
- fs.mkdirSync(unit, { recursive: true })
39
- assert.deepEqual(activeRuns(unit), runs, 'a coordinator standing in a unit workspace still finds its run')
40
- })
41
-
42
- test('the message names both skills for a rush run, each run with its next-actions command, and the rules', () => {
43
- const dir = '/repo/.resolve-issues/rush/r1'
44
- const rush = path.join(path.dirname(path.dirname(SKILL_PATH)), 'rush-issues', 'SKILL.md')
45
- const text = message([{ dir, skill: 'rush-issues', revision: 5, open: 3 }], { rushSkillPath: SKILL_PATH })
46
- assert.match(text, /read each of these in full, with no line limit/)
47
- assert.ok(text.includes(`- ${SKILL_PATH}\n`))
48
- assert.ok(text.includes(`next-actions.mjs --run ${dir} --capacity-json '<fresh snapshot JSON>'`))
49
- assert.match(text, /Read subagent_settings now/)
50
- assert.match(text, /no default worker count/)
51
- assert.ok(text.endsWith(RULES))
52
- const plain = message([{ dir, skill: 'resolve-issues', revision: 5, open: 3 }], { rushSkillPath: rush })
53
- assert.ok(!plain.includes('rush-issues/SKILL.md'), 'a resolve-issues run reads only its own skill')
54
- assert.equal(message([]), '')
55
- })
56
-
57
- test('each recovered run refreshes capacity separately instead of reusing one snapshot', () => {
58
- const text = message([
59
- { dir: '/repo/.resolve-issues/rush/first', skill: 'resolve-issues', revision: 1, open: 6 },
60
- { dir: '/repo/.resolve-issues/rush/second', skill: 'resolve-issues', revision: 2, open: 3 },
61
- ])
62
- const commands = text.split('\n').filter(line => line.includes('next-actions.mjs --run'))
63
- assert.equal(commands.length, 2)
64
- for (const command of commands) {
65
- assert.match(command, /refresh subagent_settings immediately before node/)
66
- assert.match(command, /--capacity-json '<fresh snapshot JSON>'/)
67
- }
68
- })
69
-
70
- test('as a hook it reads cwd from stdin, prints SessionStart context, and prints nothing without a run', () => {
71
- const cwd = repo([['live', { skill: 'resolve-issues', units: [{ id: '1', status: 'pending' }] }]])
72
- const run = (input) => spawnSync(process.execPath, [SCRIPT, '--hook'], { input, encoding: 'utf8', cwd: os.tmpdir() })
73
- const out = run(JSON.stringify({ hook_event_name: 'SessionStart', source: 'compact', cwd }))
74
- assert.equal(out.status, 0)
75
- const parsed = JSON.parse(out.stdout)
76
- assert.equal(parsed.hookSpecificOutput.hookEventName, 'SessionStart')
77
- assert.match(parsed.hookSpecificOutput.additionalContext, /Runs in progress here:/)
78
- const empty = run(JSON.stringify({ cwd: fs.mkdtempSync(path.join(os.tmpdir(), 'rehydrate-empty-')) }))
79
- assert.equal(empty.status, 0)
80
- assert.equal(empty.stdout, '')
81
- assert.equal(run('not json').status, 0, 'bad input never blocks a session')
82
- })
83
-
84
- test('rush-issues/rehydrate.mjs prints the same message, for a harness that runs the owner skill\'s script', () => {
85
- const cwd = repo([['live', { skill: 'rush-issues', units: [{ id: '1', status: 'in-flight' }] }]])
86
- const rush = path.join(path.dirname(path.dirname(SKILL_PATH)), 'rush-issues', 'rehydrate.mjs')
87
- const out = spawnSync(process.execPath, [rush, '--cwd', cwd], { encoding: 'utf8' })
88
- assert.equal(out.status, 0)
89
- assert.equal(out.stdout, `${message(activeRuns(cwd))}\n`)
90
- assert.match(out.stdout, /rush-issues\/SKILL\.md/)
91
- })