@1aboveio/skills 1.0.8 → 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.
- package/.codex-plugin/plugin.json +13 -0
- package/.codex-plugin/resources.json +1827 -0
- package/README.md +22 -0
- package/package.json +9 -2
- package/skills/app-dev/airflow/SKILL.md +1 -1
- package/skills/app-dev/airflow/release-check.mjs +3 -1
- package/skills/app-dev/android-foundation/SKILL.md +1 -1
- package/skills/app-dev/android-foundation/release-check.mjs +3 -1
- package/skills/app-dev/app-debug/SKILL.md +1 -1
- package/skills/app-dev/app-debug/release-check.mjs +3 -1
- package/skills/app-dev/better-auth/SKILL.md +1 -1
- package/skills/app-dev/better-auth/release-check.mjs +3 -1
- package/skills/app-dev/data-catalog/SKILL.md +1 -1
- package/skills/app-dev/data-catalog/release-check.mjs +3 -1
- package/skills/app-dev/data-quality/SKILL.md +1 -1
- package/skills/app-dev/data-quality/release-check.mjs +3 -1
- package/skills/app-dev/ios-foundation/SKILL.md +1 -1
- package/skills/app-dev/ios-foundation/release-check.mjs +3 -1
- package/skills/app-dev/monorepo/SKILL.md +1 -1
- package/skills/app-dev/monorepo/release-check.mjs +3 -1
- package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
- package/skills/app-dev/nextjs-fullstack/release-check.mjs +3 -1
- package/skills/app-dev/prisma-setup/SKILL.md +1 -1
- package/skills/app-dev/prisma-setup/release-check.mjs +3 -1
- package/skills/app-dev/pyspark/SKILL.md +1 -1
- package/skills/app-dev/pyspark/release-check.mjs +3 -1
- package/skills/app-dev/python-backend/SKILL.md +1 -1
- package/skills/app-dev/python-backend/release-check.mjs +3 -1
- package/skills/app-dev/shadcn/SKILL.md +1 -1
- package/skills/app-dev/shadcn/release-check.mjs +3 -1
- package/skills/app-dev/zod-v4/SKILL.md +1 -1
- package/skills/app-dev/zod-v4/release-check.mjs +3 -1
- package/skills/cicd/cloud-build/SKILL.md +1 -1
- package/skills/cicd/cloud-build/release-check.mjs +3 -1
- package/skills/cicd/cloud-debug/SKILL.md +1 -1
- package/skills/cicd/cloud-debug/release-check.mjs +3 -1
- package/skills/cicd/cloud-deploy/SKILL.md +1 -1
- package/skills/cicd/cloud-deploy/release-check.mjs +3 -1
- package/skills/cicd/docker/SKILL.md +1 -1
- package/skills/cicd/docker/release-check.mjs +3 -1
- package/skills/cicd/github-actions/SKILL.md +1 -1
- package/skills/cicd/github-actions/release-check.mjs +3 -1
- package/skills/cicd/github-flow/SKILL.md +1 -1
- package/skills/cicd/github-flow/release-check.mjs +3 -1
- package/skills/cicd/google-cloud/SKILL.md +1 -1
- package/skills/cicd/google-cloud/release-check.mjs +3 -1
- package/skills/cicd/mergify/SKILL.md +1 -1
- package/skills/cicd/mergify/release-check.mjs +3 -1
- package/skills/cicd/podman/SKILL.md +1 -1
- package/skills/cicd/podman/release-check.mjs +3 -1
- package/skills/content/article-review/SKILL.md +1 -1
- package/skills/content/article-review/release-check.mjs +3 -1
- package/skills/content/article-to-image/SKILL.md +1 -1
- package/skills/content/article-to-image/release-check.mjs +3 -1
- package/skills/content/flowchart-svg/SKILL.md +1 -1
- package/skills/content/flowchart-svg/release-check.mjs +3 -1
- package/skills/content/image-gen/SKILL.md +1 -1
- package/skills/content/image-gen/release-check.mjs +3 -1
- package/skills/content/mermaid/SKILL.md +1 -1
- package/skills/content/mermaid/release-check.mjs +3 -1
- package/skills/content/wechat-article-writer/SKILL.md +1 -1
- package/skills/content/wechat-article-writer/release-check.mjs +3 -1
- package/skills/content/wechat-publisher/SKILL.md +1 -1
- package/skills/content/wechat-publisher/release-check.mjs +3 -1
- package/skills/content/wechat-search/SKILL.md +1 -1
- package/skills/content/wechat-search/release-check.mjs +3 -1
- package/skills/content/xai-search/SKILL.md +88 -13
- package/skills/content/xai-search/release-check.mjs +3 -1
- package/skills/content/xai-search/scripts/xai_search.sh +125 -149
- package/skills/core/distill-writing/SKILL.md +1 -1
- package/skills/core/distill-writing/release-check.mjs +3 -1
- package/skills/core/doc-to-md/SKILL.md +1 -1
- package/skills/core/doc-to-md/release-check.mjs +3 -1
- package/skills/core/editorial-review/SKILL.md +1 -1
- package/skills/core/editorial-review/release-check.mjs +3 -1
- package/skills/core/house-style/SKILL.md +1 -1
- package/skills/core/house-style/release-check.mjs +3 -1
- package/skills/core/plain-language/SKILL.md +1 -1
- package/skills/core/plain-language/release-check.mjs +3 -1
- package/skills/core/polish-cn/SKILL.md +1 -1
- package/skills/core/polish-cn/release-check.mjs +3 -1
- package/skills/core/reg-parser/SKILL.md +1 -1
- package/skills/core/reg-parser/release-check.mjs +3 -1
- package/skills/core/terminology/SKILL.md +1 -1
- package/skills/core/terminology/release-check.mjs +3 -1
- package/skills/core/write-doc/SKILL.md +1 -1
- package/skills/core/write-doc/release-check.mjs +3 -1
- package/skills/engineering/better-review/SKILL.md +1 -1
- package/skills/engineering/better-review/release-check.mjs +3 -1
- package/skills/engineering/contributing/SKILL.md +1 -1
- package/skills/engineering/contributing/release-check.mjs +3 -1
- package/skills/engineering/e2e-test/SKILL.md +1 -1
- package/skills/engineering/e2e-test/release-check.mjs +3 -1
- package/skills/engineering/engineering-runtime/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
- package/skills/engineering/engineering-runtime/release-check.mjs +3 -1
- package/skills/engineering/engineering-runtime/scripts/installation-resolver.mjs +191 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +51 -10
- package/skills/engineering/ensure-coverage/SKILL.md +1 -1
- package/skills/engineering/ensure-coverage/release-check.mjs +3 -1
- package/skills/engineering/golden-path/SKILL.md +1 -1
- package/skills/engineering/golden-path/release-check.mjs +3 -1
- package/skills/engineering/harness-runtime/SKILL.md +1 -1
- package/skills/engineering/harness-runtime/release-check.mjs +3 -1
- package/skills/engineering/resolve-issues/SKILL.md +1 -1
- package/skills/engineering/resolve-issues/references/preflight.md +7 -0
- package/skills/engineering/resolve-issues/references/shared-modules.md +27 -0
- package/skills/engineering/resolve-issues/references/task-plan.md +5 -1
- package/skills/engineering/resolve-issues/release-check.mjs +3 -1
- package/skills/engineering/resolve-issues/scripts/active-installation.mjs +32 -0
- package/skills/engineering/resolve-issues/scripts/install-hooks.mjs +3 -0
- package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +17 -1
- package/skills/engineering/resolve-release/SKILL.md +1 -1
- package/skills/engineering/resolve-release/release-check.mjs +3 -1
- package/skills/engineering/rush-issues/SKILL.md +1 -1
- package/skills/engineering/rush-issues/release-check.mjs +3 -1
- package/skills/engineering/rush-release/SKILL.md +1 -1
- package/skills/engineering/rush-release/release-check.mjs +3 -1
- package/skills/engineering/test-quality/SKILL.md +1 -1
- package/skills/engineering/test-quality/release-check.mjs +3 -1
- package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
- package/skills/risk-compliance/compliance-audit/release-check.mjs +3 -1
- package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
- package/skills/risk-compliance/contract-compare/release-check.mjs +3 -1
- package/skills/risk-compliance/contract-review/SKILL.md +1 -1
- package/skills/risk-compliance/contract-review/release-check.mjs +3 -1
- package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
- package/skills/risk-compliance/doc-authenticity/release-check.mjs +3 -1
- package/skills/risk-compliance/fraud-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/fraud-analysis/release-check.mjs +3 -1
- package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/payment-analysis/release-check.mjs +3 -1
- package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
- package/skills/risk-compliance/similar-domain/release-check.mjs +3 -1
- package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
- package/skills/risk-compliance/tld-detection/release-check.mjs +3 -1
- package/skills/app-dev/android-foundation/scripts/device-run.test.mjs +0 -436
- package/skills/app-dev/data-catalog/scripts/data-catalog.test.mjs +0 -36
- package/skills/app-dev/data-quality/scripts/data-quality.test.mjs +0 -12
- package/skills/cicd/github-actions/scripts/ci-run-audit.test.mjs +0 -637
- package/skills/cicd/github-flow/scripts/flow.test.mjs +0 -114
- package/skills/cicd/github-flow/scripts/persist-conventions.test.mjs +0 -108
- package/skills/cicd/mergify/scripts/audit.test.mjs +0 -201
- package/skills/cicd/mergify/scripts/entrypoints.test.mjs +0 -106
- package/skills/cicd/mergify/scripts/pr-620-mergify-comments.fixture.json +0 -30
- package/skills/cicd/mergify/scripts/stack-push.test.mjs +0 -233
- package/skills/cicd/mergify/scripts/watch-pr-delivery.test.mjs +0 -1826
- package/skills/content/image-gen/scripts/credentials.test.mjs +0 -212
- package/skills/content/image-gen/scripts/options.test.mjs +0 -245
- package/skills/content/xai-search/tests/smoke_test.sh +0 -36
- package/skills/core/polish-cn/scripts/polish-cn.test.mjs +0 -244
- package/skills/core/reg-parser/scripts/reg-parser.test.mjs +0 -165
- package/skills/engineering/better-review/scripts/pr-context.test.mjs +0 -168
- package/skills/engineering/better-review/scripts/rereview-scope.test.mjs +0 -587
- package/skills/engineering/better-review/scripts/scan-diff.test.mjs +0 -414
- package/skills/engineering/better-review/scripts/vendor-sync.test.mjs +0 -96
- package/skills/engineering/contributing/scripts/persist-conventions.test.mjs +0 -118
- package/skills/engineering/e2e-test/scripts/presentation-checks.test.mjs +0 -106
- package/skills/engineering/e2e-test/scripts/presentation-perceivability.test.mjs +0 -117
- package/skills/engineering/e2e-test/scripts/presentation-reachability.test.mjs +0 -69
- package/skills/engineering/e2e-test/scripts/presentation-render-health.test.mjs +0 -98
- package/skills/engineering/e2e-test/scripts/presentation-sweep.test.mjs +0 -71
- package/skills/engineering/e2e-test/scripts/presentation-temporal.test.mjs +0 -59
- package/skills/engineering/e2e-test/scripts/project-route-manifest.test.mjs +0 -76
- package/skills/engineering/engineering-runtime/engineering-runtime.test.mjs +0 -68
- package/skills/engineering/engineering-runtime/scripts/invocation-policy.test.mjs +0 -95
- package/skills/engineering/engineering-runtime/scripts/main-module.test.mjs +0 -288
- package/skills/engineering/engineering-runtime/scripts/required-check-plan.test.mjs +0 -96
- package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.test.mjs +0 -96
- package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.test.mjs +0 -47
- package/skills/engineering/ensure-coverage/scripts/ci-audit.test.mjs +0 -86
- package/skills/engineering/ensure-coverage/scripts/coverage-ledger.test.mjs +0 -447
- package/skills/engineering/ensure-coverage/scripts/design-parity.test.mjs +0 -552
- package/skills/engineering/ensure-coverage/scripts/lint-tests.test.mjs +0 -289
- package/skills/engineering/ensure-coverage/scripts/mock-policy-config.test.mjs +0 -119
- package/skills/engineering/golden-path/scripts/golden-path.test.mjs +0 -1457
- package/skills/engineering/harness-runtime/discover-models.test.mjs +0 -363
- package/skills/engineering/harness-runtime/harness-runtime.test.mjs +0 -967
- package/skills/engineering/harness-runtime/model-catalog-seed.test.mjs +0 -829
- package/skills/engineering/harness-runtime/model-catalog.test.mjs +0 -366
- package/skills/engineering/resolve-issues/scripts/cleanup-runs.test.mjs +0 -280
- package/skills/engineering/resolve-issues/scripts/dropbox.test.mjs +0 -346
- package/skills/engineering/resolve-issues/scripts/findings-reuse.test.mjs +0 -88
- package/skills/engineering/resolve-issues/scripts/github-reviews.test.mjs +0 -264
- package/skills/engineering/resolve-issues/scripts/install-hooks.test.mjs +0 -79
- package/skills/engineering/resolve-issues/scripts/issue-tree.test.mjs +0 -107
- package/skills/engineering/resolve-issues/scripts/manual-invocation.test.mjs +0 -14
- package/skills/engineering/resolve-issues/scripts/next-actions-capacity.test.mjs +0 -149
- package/skills/engineering/resolve-issues/scripts/next-actions.test.mjs +0 -915
- package/skills/engineering/resolve-issues/scripts/plan-sync.test.mjs +0 -156
- package/skills/engineering/resolve-issues/scripts/preflight-models.test.mjs +0 -448
- package/skills/engineering/resolve-issues/scripts/ready-check.test.mjs +0 -217
- package/skills/engineering/resolve-issues/scripts/rehydrate.test.mjs +0 -91
- package/skills/engineering/resolve-issues/scripts/review-verdict.test.mjs +0 -1060
- package/skills/engineering/resolve-issues/scripts/size-check.test.mjs +0 -458
- package/skills/engineering/resolve-release/scripts/apply.test.mjs +0 -99
- package/skills/engineering/resolve-release/scripts/dark-flags.test.mjs +0 -100
- package/skills/engineering/resolve-release/scripts/flow.test.mjs +0 -340
- package/skills/engineering/resolve-release/scripts/green-head.test.mjs +0 -101
- package/skills/engineering/resolve-release/scripts/npm-version-absence.test.mjs +0 -426
- package/skills/engineering/resolve-release/scripts/plan-npm.test.mjs +0 -659
- package/skills/engineering/resolve-release/scripts/plan-toml-boundary.test.mjs +0 -317
- package/skills/engineering/resolve-release/scripts/plan-uv.test.mjs +0 -291
- package/skills/engineering/resolve-release/scripts/plan.test.mjs +0 -428
- package/skills/engineering/test-quality/scripts/lint-tests.test.mjs +0 -591
- package/skills/engineering/test-quality/scripts/mock-policy-config.test.mjs +0 -119
- package/skills/engineering/test-quality/scripts/persist-conventions.test.mjs +0 -104
- package/skills/risk-compliance/compliance-audit/scripts/compliance-audit.test.mjs +0 -14
- package/skills/risk-compliance/doc-authenticity/manual-invocation.test.mjs +0 -12
- package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.test.mjs +0 -212
- package/skills/risk-compliance/tld-detection/scripts/detect_ladders.test.mjs +0 -14
- package/skills/risk-compliance/tld-detection/scripts/detect_streams.test.mjs +0 -14
- package/skills/risk-compliance/tld-detection/tests/test_detect_ladders.py +0 -134
- 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
|
-
})
|