axstack 0.9.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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +132 -0
  3. package/bin/axstack.js +396 -0
  4. package/docs/installation.md +239 -0
  5. package/docs/workflows.md +220 -0
  6. package/package.json +40 -0
  7. package/profiles/presets/claude-only.json +194 -0
  8. package/profiles/presets/codex-only.json +194 -0
  9. package/profiles/presets/mixed.json +194 -0
  10. package/skills/axstack/SKILL.md +81 -0
  11. package/skills/axstack/references/automations.md +368 -0
  12. package/skills/axstack/references/candidate-publication.md +45 -0
  13. package/skills/axstack/references/contracts.md +102 -0
  14. package/skills/axstack/references/lifecycle.md +137 -0
  15. package/skills/axstack/references/orca-runtime.md +109 -0
  16. package/skills/axstack/references/pr-shape.md +39 -0
  17. package/skills/axstack/references/routing.md +129 -0
  18. package/skills/axstack/references/run-record.md +109 -0
  19. package/skills/axstack-align/SKILL.md +121 -0
  20. package/skills/axstack-audit/SKILL.md +137 -0
  21. package/skills/axstack-audit/references/record.md +28 -0
  22. package/skills/axstack-debug/SKILL.md +157 -0
  23. package/skills/axstack-debug/references/packet.md +80 -0
  24. package/skills/axstack-explain/SKILL.md +66 -0
  25. package/skills/axstack-explain/references/visual-qa.md +15 -0
  26. package/skills/axstack-implement/SKILL.md +164 -0
  27. package/skills/axstack-improve/SKILL.md +69 -0
  28. package/skills/axstack-relay/SKILL.md +102 -0
  29. package/skills/axstack-research/SKILL.md +57 -0
  30. package/skills/axstack-research/references/checklist.md +25 -0
  31. package/skills/axstack-review/SKILL.md +343 -0
  32. package/skills/axstack-spec/SKILL.md +67 -0
  33. package/skills/axstack-tickets/SKILL.md +86 -0
  34. package/skills/axstack-watch/SKILL.md +160 -0
  35. package/skills/axstack-watch/references/repair-publication.md +69 -0
  36. package/skills/axstack-watch/references/watch-runtime.md +60 -0
  37. package/src/capabilities.js +138 -0
  38. package/src/claude-settings.js +230 -0
  39. package/src/installer.js +980 -0
  40. package/src/instructions.js +100 -0
  41. package/src/locations.js +43 -0
  42. package/src/manifest.js +251 -0
  43. package/src/posixpath.js +108 -0
  44. package/src/roles.js +142 -0
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: axstack-tickets
3
+ description: When an approved capability needs executable tasks, use axstack-tickets to map work and track lifecycle state.
4
+ ---
5
+
6
+ # Tickets
7
+
8
+ Produce an executable capability map tied to the exact approved spec revision.
9
+ Keep user-visible capabilities in the selected store, keep implementation detail
10
+ in the repository, reconcile lifecycle state, and stop before implementation.
11
+
12
+ Before mapping, load [Standing contracts](../axstack/references/contracts.md).
13
+ Follow its required edge to [Shared lifecycle](../axstack/references/lifecycle.md),
14
+ including the lifecycle audit hook. Read the
15
+ [PR-shape policy](../axstack/references/pr-shape.md) before sizing tasks. Read the
16
+ [Orca runtime boundary](../axstack/references/orca-runtime.md) immediately before
17
+ an actual checker dispatch, not for ordinary mapping or state reconciliation.
18
+
19
+ ## Procedure
20
+
21
+ 1. **Pin scope and storage.** Confirm the approved spec identity before mapping.
22
+ Hold on unapproved or materially changed scope; a still-valid approval is
23
+ never repeated. Use the explicitly selected Markdown or Linear store. Record
24
+ the exact approved spec revision and selected store.
25
+
26
+ 2. **Preflight the selected store.** Markdown mode works independently. In
27
+ Linear mode, check the actual session's required MCP tools and document
28
+ access. Missing access is an actionable setup gap: preserve the selected
29
+ store, record the gap, and stop affected work. Proceed only with verified
30
+ access; a recorded gap never switches stores.
31
+
32
+ 3. **Map capabilities to execution.** In Linear, issues represent user-visible
33
+ capabilities; one capability may span several tasks and PRs. Keep detailed
34
+ execution breakdowns in the repository. For every capability, derive
35
+ acceptance checks from the pinned spec and identify internal tasks,
36
+ dependencies, PR ownership, and worktrees. For each task the driver records
37
+ one theme and a coarse size estimate from the ownership, interface, and
38
+ dependency map. A task estimated in the exception band is assessed for a
39
+ split at mapping time and split where a green, atomic, reviewable split
40
+ exists. If the driver judges it inseparable, record the coarse planning
41
+ rationale with the task; actual measurement and exception evidence follow in
42
+ the implement receipt. Mapping time requires no actual SHAs or line counts.
43
+ Every capability ends with the fields below and an explicit dependency list.
44
+ These routine mapping and split choices are autonomous driver decisions
45
+ within the approved spec; size alone never requires user approval.
46
+
47
+ The driver performs every Linear mutation. Other roles return proposed
48
+ changes and evidence to the driver.
49
+
50
+ ## Template: capability to task map
51
+
52
+ ```text
53
+ Spec: <approved revision>
54
+ Capability: <Markdown ref or Linear issue URL> <title>
55
+ Internal task: <task> -> <PR owner> -> <worktree>
56
+ Theme: <one behavior or component>
57
+ Size est: <coarse band estimate>
58
+ Acceptance: <checks from approved spec rev>
59
+ Depends: <task IDs or none>
60
+ ```
61
+
62
+ 4. **Reconcile lifecycle state.** Compare each run capability with its required
63
+ PR merge evidence and acceptance outputs. A reviewed but unmerged capability
64
+ stays **In Review**; mark Done only after all required PRs merge and the
65
+ capability's acceptance checks pass. Resolve each observed mismatch with
66
+ either a verified driver-owned update or a drift report.
67
+
68
+ The checker reports run-scoped discrepancies with evidence and never mutates
69
+ Linear. The driver independently verifies that evidence before applying an
70
+ update. Spec or acceptance changes and conflicting state are decisions, not
71
+ routine sync repairs. With no configured `axstack-checker` profile, hold
72
+ checker dispatch; the driver checks directly or records the gap. Never launch
73
+ a provider default.
74
+
75
+ ### Template: drift report
76
+
77
+ ```text
78
+ Capability: <issue>
79
+ Observed: <Linear state> vs <PR merge SHAs/URLs + acceptance outputs>
80
+ Recommendation: <move to In Review | other> (driver verifies first)
81
+ ```
82
+
83
+ 5. **Return the mapping.** Report the pinned spec revision, selected store, map
84
+ references, mutations performed by the driver, recorded gaps, and unresolved
85
+ decisions. Stop with a map ready for lifecycle continuation; implementation
86
+ has not started.
@@ -0,0 +1,160 @@
1
+ ---
2
+ name: axstack-watch
3
+ description: When babysitting an existing PR, use axstack-watch to monitor or maintain it within bounded authority.
4
+ ---
5
+
6
+ # Watch
7
+
8
+ Leave each adopted PR with one accountable owner, current readiness evidence,
9
+ and a bounded watch that ends cleanly or preserves enough state to resume.
10
+
11
+ Before acting, load [Standing contracts](../axstack/references/contracts.md).
12
+ Its required edge loads [Shared lifecycle](../axstack/references/lifecycle.md),
13
+ including the end-of-run audit hook. Reach other references only at the steps
14
+ that name them.
15
+
16
+ Preserve any explicitly named PR, repository, or peer scope. For broad
17
+ discovery of the user's own PRs (such as “my” or “our” PRs), run
18
+ `gh api user --jq .login` on the execution host, then select open PRs authored
19
+ by that login in the named or current repository. Never hardcode or guess the
20
+ username; a missing or failed authenticated-login lookup is a concrete blocker.
21
+ The authenticated human login selects PRs. Runtime session IDs coordinate work
22
+ only and establish neither human identity nor write, reply, or merge authority.
23
+ When the session is the Orca driver automation, also load
24
+ [Automation sessions](../axstack/references/automations.md): it is the owner
25
+ for every PR it handles, and its gate and allowlist bound every mutation.
26
+
27
+ ## 1. Adopt and reconcile
28
+
29
+ Start from actual state. Reconcile the PR's remote head and base, ownership,
30
+ existing Orca Tasks, Dispatches, sessions, private run record, and watch registrations. Reuse the
31
+ live owner and watch; uncertain state holds new registrations until resolved.
32
+
33
+ For an existing own PR, read the
34
+ [proportional scope identities](../axstack/references/routing.md#proportional-scope-identity),
35
+ verify writable ownership and user maintenance authority, then snapshot the
36
+ accepted maintenance intent once: authorized scope, actual head and base,
37
+ current owner, actual author provenance, and watch state. Check authoring
38
+ session evidence; the orchestrator identity is not author evidence, and never
39
+ assume an author for an imported own PR. It needs no new spec, ticket, or repeated
40
+ approval. Monitoring-only adoption grants no repair or reply authority.
41
+
42
+ Adoption is settled when the record names one persistent owner, one watch, the
43
+ exact PR revision and base, and the applicable authority snapshot. If write
44
+ authority is unverified, record the hold and continue read-only.
45
+
46
+ ## 2. Fix the operating mode
47
+
48
+ Choose one mode from the user's authority and record it before dispatch:
49
+
50
+ - **Observation-only:** reconcile and report CI, reviews, and PR state. It
51
+ dispatches no author and sends no reply. This restriction dominates every
52
+ repair path, including obvious fixes after changed heads or feedback.
53
+ - **Peer:** observe and report a colleague's PR. Peer mode never repairs.
54
+ - **Authorized maintenance:** repair an adopted own PR only within the accepted
55
+ maintenance snapshot and publication authority.
56
+
57
+ Every later wake must be classifiable from this recorded mode without inferring
58
+ new authority.
59
+
60
+ ## 3. Start the bounded watch
61
+
62
+ Read-only checks and updates to the already-owned local record need no runtime
63
+ load. When the watch needs a new owner or automated observation, first read
64
+ [Watch runtime](references/watch-runtime.md) and then
65
+ [Orca runtime](../axstack/references/orca-runtime.md). Reconcile before creating
66
+ anything. The user lifted the native-watch hold by user decision: the 5 min driver
67
+ automation is a mutating owner for the PRs it handles and the hourly watchdog
68
+ stays independent and read-only. The driver is the automation session itself,
69
+ with no `axstack-monitor` or `axstack-owner` role row; `axstack-monitor` stays
70
+ an optional read-only observer that never sends. One read-only PR observation
71
+ needs neither.
72
+
73
+ For standalone adoption, materialize `axstack-owner` only when no live owner
74
+ exists. Once it exists, the current chat is not a competing coordinator. Only
75
+ the owner launches the writer, reviewers, monitor, and watchdog. Workers create
76
+ no children or recursive teams, and the adoption watcher is never the writer.
77
+
78
+ A live watch has verified role and timer receipts, handshakes, watched scope,
79
+ wake ownership, and a common expiry. A missing runtime capability is a setup gap,
80
+ not a reason to invent a call or create a duplicate registration. Wait through
81
+ native wake-ups; no model remains active between events.
82
+
83
+ ## 4. Route each wake
84
+
85
+ Re-read the remote head and base, then reconcile the event against acknowledged
86
+ IDs and the recorded mode. A changed head, CI result, or review comment is an
87
+ event, not repair authority. Stale or ambiguous observations authorize nothing.
88
+
89
+ Observation-only and peer wakes produce a read-only report and stop. For an
90
+ authorized maintenance wake that may require a repair or public reply, read and
91
+ follow [Repair and publication](references/repair-publication.md). An
92
+ automation session repairs in a per-PR child worktree created through
93
+ `orca-cli`; its driver worktree never checks out a PR branch.
94
+
95
+ ### Feedback routing
96
+
97
+ New work routes only under its confirmed scope identity: an approved spec and
98
+ matching ticket map for substantial work, or a snapshotted **small-change
99
+ intent** for small work. An adopted own PR instead uses its accepted maintenance
100
+ snapshot. Missing, stale, or materially changed identity holds repair routing
101
+ while monitoring continues. Accepted fixes return to the same original author
102
+ session only when the run itself launched that session and evidence allows,
103
+ then receive refreshed review under the authored mode rule before publication.
104
+ For an adopted own PR under the automation, the original authoring session is
105
+ not a run-launched session: the repair author is the automation session
106
+ (Claude/Opus) or a dispatched `axstack-author` (Sol), and the authored-review
107
+ pairing follows the recorded actual provenance of that repair, not the PR's
108
+ historical author. Unknown, mixed, or unsupported author provenance
109
+ that cannot establish the eligible configured reviewer is an exact gap to
110
+ report to the user, not permission to invent a pairing or model fallback.
111
+
112
+ A handled wake has an acknowledged event ID, an observation or action bound to
113
+ the current revision, and a recorded hold or next owner where work remains.
114
+
115
+ When a new actionable event is eligible under a recorded `Notification policy`,
116
+ the owner may use the optional [axstack-relay](../axstack-relay/SKILL.md).
117
+ The monitor never sends, and `axstack-watchdog` never mutates GitHub and
118
+ performs exactly one kind of send, a gate-authorized automation-health
119
+ escalation recorded in `watchdog.json`; absent policy or failed relay uses the
120
+ current Orca conversation and leaves every existing hold open.
121
+
122
+ ## 5. State readiness precisely
123
+
124
+ The owner checks current required checks, all feedback, approvals, mergeability,
125
+ and exact-revision receipts before any merge-ready statement. API errors leave
126
+ readiness `UNKNOWN`; review approval alone is not merge-ready. Merge-ready is an
127
+ observed state distinct from merged, and the human merges by default.
128
+
129
+ ## 6. End and preserve continuity
130
+
131
+ End early when all required PRs merge, or at cancel or the shared default 24h
132
+ deadline. In every case, stop and verify all owned registrations. The deadline
133
+ also stops timers for open PRs; never silently renew them.
134
+
135
+ At every end condition, leave the compact state below in the private run record
136
+ and report it in the current chat, even when work remains. Expiry grants neither
137
+ silent renewal nor ownership-transfer authority. Under an automation, expiry
138
+ marks the PR `expired` in the record and sidecar; an `expired` PR is never
139
+ silently re-adopted and is skipped until the user re-arms it.
140
+
141
+ Transfer ownership through the runtime-owned Orca handoff route only when the
142
+ user explicitly requests it. Before transfer, follow the lifecycle-owned
143
+ preflight for native capability availability, the configured role, and explicit
144
+ recipient acceptance. A failed or incomplete preflight preserves the current
145
+ owner and reports the gap; never invent a native command or infer acceptance.
146
+
147
+ ```text
148
+ Record: <progress.md path>
149
+ PR: <URL> rev <sha> base <sha>
150
+ Owner: <profile + session> Worktree: <path>
151
+ Scope: <approved rev, small-change intent, or maintenance snapshot>
152
+ Capability: <issue + lifecycle state>
153
+ CI/review: <current states + evidence refs>
154
+ Watch: <automation ids or stopped registration receipts + expiry>
155
+ Remaining: <next actions + owner>
156
+ Resume: <known commands or verified refs needed to reconcile from this revision>
157
+ ```
158
+
159
+ The watch ends only when registrations are stopped, receipts are recorded, and
160
+ the PR is either merged or represented by this resumable state.
@@ -0,0 +1,69 @@
1
+ # Repair and publication
2
+
3
+ Read this only for authorized maintenance of an adopted own PR. Observation-only
4
+ and peer modes stop with a report before this branch.
5
+
6
+ ## 1. Confirm the repair boundary
7
+
8
+ Re-read the accepted maintenance snapshot, writable ownership, publication
9
+ authority, remote head and base, and current feedback. Route an accepted fix to
10
+ the original author session only when the run itself launched that session and
11
+ evidence permits. For an adopted own PR under the automation, the repair
12
+ author is the automation session (Claude/Opus) or a dispatched
13
+ `axstack-author` (Sol); record that repair's actual provenance before
14
+ selecting the reviewer, because the authored-review pairing follows the actual
15
+ provenance of the repair, never the PR's historical author. A missing,
16
+ stale, or materially changed boundary holds the repair while read-only
17
+ monitoring continues.
18
+
19
+ Record the exact defect, allowed files and actions, current revision, feedback
20
+ IDs, and actual author without expanding scope. If actual author information is
21
+ unknown, mixed, or unsupported and cannot establish an eligible configured
22
+ reviewer, report that exact gap and ask the user; do not assume an author from
23
+ the importing owner or orchestrator.
24
+
25
+ ## 2. Produce a reviewable candidate
26
+
27
+ The author prepares the smallest in-scope repair and the exact public reply
28
+ bodies, each keyed to its feedback ID and bound to the candidate revision. The
29
+ candidate is committed locally in the per-PR child worktree and reviewed in
30
+ authored mode at its local SHA; nothing is pushed for review. Both code and
31
+ reply bodies receive the one complete eligible non-author/non-owner review
32
+ required by the authored review rule in `axstack-review`.
33
+
34
+ Publication stays held until the current authored review receipt covers the
35
+ exact new revision and base, all six angles, applicable acceptance, the reply
36
+ body identities, and every affected boundary, with no unresolved material
37
+ finding or urgent hold. Under an automation the escalation gate of
38
+ [Automation sessions](../../axstack/references/automations.md) also runs on
39
+ the local SHA: publication additionally requires the gate to return `proceed`
40
+ with no unresolved validated blocking finding, and a push before the gate
41
+ settles is forbidden.
42
+
43
+ ## 3. Revalidate immediately before publication
44
+
45
+ Confirm fresh remote head and base, feedback freshness, reply body identity,
46
+ and the exact revision covered by the mode-required review receipt. Before a history
47
+ rewrite, confirm the expected-old SHA; a mismatch holds publication.
48
+
49
+ All publication inputs must still match their reviewed values at the final
50
+ readback.
51
+
52
+ ## 4. Publish idempotently
53
+
54
+ Use `gh stack` for the adopted PR only, preserving unrelated stack entries.
55
+ Bind the operation to the exact reviewed revision, then verify the submission
56
+ receipt and remote state.
57
+
58
+ An automation session pushes fast-forward only: run the section 3 publication
59
+ readback immediately before the push, then `git push` to the PR branch with no
60
+ lease or force, and no `gh stack` sync or restack from an automation. A
61
+ non-fast-forward remote is a recorded hold, never a rewrite.
62
+
63
+ If the send outcome is unknown, inspect remote IDs, bodies, and actor before any
64
+ retry. Remain blocked while the outcome is ambiguous; retry only after
65
+ confirming the intended operation is absent.
66
+
67
+ Publication ends with a remote receipt proving that the reviewed revision and
68
+ exact replies landed once, or a recorded hold naming the unmatched input and
69
+ next owner.
@@ -0,0 +1,60 @@
1
+ # Watch runtime
2
+
3
+ Read this before starting, resuming, or stopping automated PR observation.
4
+
5
+ ## Accepted policy
6
+
7
+ The PR owner remains accountable throughout one shared default 24-hour window.
8
+ `axstack-monitor` and `axstack-watchdog` are independent, read-only roles, not
9
+ authors, reviewers, repliers, or owners.
10
+
11
+ - **Monitor:** `axstack-monitor` is an optional read-only observer that reads
12
+ GitHub, all PR feedback, and latest checks every five minutes, persists event
13
+ IDs, wakes the owner only for a new actionable event, and never sends.
14
+ - **Watchdog:** `axstack-watchdog` reads only automation health, handshake
15
+ state, and snapshot freshness hourly and never mutates GitHub; it may perform
16
+ exactly one kind of send, a gate-authorized automation-health escalation
17
+ recorded in `watchdog.json`, and otherwise reports a verified health failure
18
+ to the owner.
19
+
20
+ Healthy observations are snapshot-only and update quietly; they wake neither owner nor
21
+ driver. Both roles deduplicate event IDs. Uncertain delivery is reconciled
22
+ before retry. Restart reuses prior watch identity rather than registering a
23
+ duplicate. The shared deadline ends earlier on completion or cancellation and
24
+ is never silently renewed.
25
+
26
+ ## Native Orca automations
27
+
28
+ Load the version-matched Orca automation guidance through the shared
29
+ [runtime boundary](../../axstack/references/orca-runtime.md). The verified
30
+ native automation schema supports provider selection, but model, effort, and
31
+ permission pinning are unsupported, and its schedule parser cannot preserve
32
+ the accepted bounded expiry by itself. Requested role values or a post-launch
33
+ self-report are not effective launch evidence.
34
+
35
+ The user lifted the native-watch hold by user decision on 2026-09-16. The accepted
36
+ contract now has a new shape: the driver automation is a mutating owner for the
37
+ PRs it handles, not an independent read-only monitor, and the watchdog keeps
38
+ the independent read-only health contract. The driver is the automation
39
+ session itself, with no `axstack-monitor` or `axstack-owner` role row
40
+ materialized for it. The driver records its own model
41
+ identity on every tick and the watchdog compares it with the expected model; a
42
+ mismatch is a safety hold, never a silent substitution. The bounded expiry is
43
+ enforced by the run record's watch deadline, not by the schedule parser. Still
44
+ introduce no custom scheduler or polling loop and use no legacy runtime
45
+ fallback. The session-level contract lives in
46
+ [Automation sessions](../../axstack/references/automations.md).
47
+
48
+ ## Preserve the contract under automation
49
+
50
+ An automation session preserves the roles, five-minute/hourly cadences, quiet
51
+ healthy behavior, deduplication, handshake, watched scope, wake owner, and
52
+ shared expiry. Test active expiry, missed final ticks, restart, duplicate
53
+ ticks, cancellation, session-reuse fallback, and final cleanup before enabling.
54
+ A firing timestamp proves neither delivery nor work advancement. An unrequested
55
+ fallback session reconciles ownership and never becomes owner silently.
56
+
57
+ At every end condition, stop all task-owned registrations, verify runtime
58
+ cleanup receipts, and capture remaining work as resumable state. Removing watch
59
+ coverage, weakening role discipline, or changing the deadline requires a
60
+ material specification revision; it is not an implementation workaround.
@@ -0,0 +1,138 @@
1
+ // Host capability checks. `exec` is injected so tests never touch a live
2
+ // runtime. Resolve Orca exactly once and reuse it: a failed choice never
3
+ // triggers a fallback to another binary.
4
+ export const BUN_FLOOR = '1.3.14';
5
+
6
+ export const PROBE_LIMITATIONS = [
7
+ 'A host binary probe cannot prove each agent session\'s Linear MCP access; skill prompts perform a session preflight instead.',
8
+ 'A host binary probe cannot prove model availability or quotas; an unavailable or exhausted model pauses affected work until the user decides.',
9
+ 'Stored role model, effort, and permission intent does not prove Orca launch parity or a successful agent execution.',
10
+ ];
11
+
12
+ const CHECK_LABELS = {
13
+ bun: 'bun >= 1.3.14 runtime',
14
+ git: 'git CLI',
15
+ gh: 'gh CLI',
16
+ 'gh-stack': 'gh stack extension',
17
+ 'orca-binary': 'resolved Orca CLI',
18
+ 'orca-runtime': 'Orca runtime connection',
19
+ 'orca-orchestration-guide': 'Orca orchestration guide capability',
20
+ 'orca-cli-guide': 'Orca CLI guide capability',
21
+ };
22
+
23
+ // The real commands behind each probe. gh-stack runs the actual
24
+ // `gh stack --help`: a "stack" substring in `gh extension list` output is not
25
+ // proof the extension command works.
26
+ export const PROBE_COMMANDS = {
27
+ git: ['git', ['--version']],
28
+ gh: ['gh', ['--version']],
29
+ 'gh-stack': ['gh', ['stack', '--help']],
30
+ };
31
+
32
+ export function resolveOrcaExecutable({ env = Bun.env, platform = process.platform } = {}) {
33
+ if (typeof env.ORCA_CLI_COMMAND === 'string' && env.ORCA_CLI_COMMAND.trim() !== '') {
34
+ return env.ORCA_CLI_COMMAND.trim();
35
+ }
36
+ if (typeof env.ORCA_DEV_REPO_ROOT === 'string' && env.ORCA_DEV_REPO_ROOT.trim() !== '') {
37
+ return 'orca-dev';
38
+ }
39
+ const managed = Boolean(env.ORCA_TERMINAL_HANDLE || env.ORCA_WORKTREE_ID);
40
+ if (platform === 'linux' && !managed) return 'orca-ide';
41
+ return 'orca';
42
+ }
43
+
44
+ function orcaCommand(name, executable) {
45
+ if (name === 'orca-binary') return [executable, ['--version']];
46
+ if (name === 'orca-runtime') return [executable, ['status', '--json']];
47
+ if (name === 'orca-orchestration-guide') {
48
+ return [executable, ['skills', 'get', 'orchestration', '--json']];
49
+ }
50
+ if (name === 'orca-cli-guide') return [executable, ['skills', 'get', 'orca-cli', '--json']];
51
+ return null;
52
+ }
53
+
54
+ function validateOrcaOutput(name, stdout) {
55
+ if (name === 'orca-binary') return { ok: true, stdout };
56
+ let parsed;
57
+ try {
58
+ parsed = JSON.parse(stdout);
59
+ } catch {
60
+ return { ok: false, stdout: 'invalid JSON response' };
61
+ }
62
+ if (name === 'orca-runtime') {
63
+ const runtime = parsed?.result?.runtime;
64
+ const ready = parsed?.ok === true && runtime?.state === 'ready' &&
65
+ runtime?.reachable === true && runtime?.connectionState === 'connected';
66
+ return { ok: ready, stdout: ready ? 'ready and connected' : 'runtime is not ready and connected' };
67
+ }
68
+ const expected = name === 'orca-cli-guide' ? 'orca-cli' : 'orchestration';
69
+ const ready = parsed?.name === expected && typeof parsed?.markdown === 'string' && parsed.markdown.length > 0;
70
+ return { ok: ready, stdout: ready ? `${expected} guide available` : `${expected} guide unavailable` };
71
+ }
72
+
73
+ // Pure semver-floor comparison over numeric prefix segments ("1.3.14" style;
74
+ // trailing build metadata is ignored).
75
+ export function meetsFloor(version, floor = BUN_FLOOR) {
76
+ const nums = (s) => String(s).split('.').map((n) => Number.parseInt(n, 10));
77
+ const [v, f] = [nums(version), nums(floor)];
78
+ for (let i = 0; i < Math.max(v.length, f.length); i++) {
79
+ const a = Number.isInteger(v[i]) ? v[i] : 0;
80
+ const b = Number.isInteger(f[i]) ? f[i] : 0;
81
+ if (a !== b) return a > b;
82
+ }
83
+ return true;
84
+ }
85
+
86
+ export async function runRealCheck(name, { orcaExecutable = resolveOrcaExecutable() } = {}) {
87
+ if (name === 'bun') {
88
+ const version = Bun.version;
89
+ return { ok: meetsFloor(version), stdout: `v${version}` };
90
+ }
91
+ const [cmd, args] = orcaCommand(name, orcaExecutable) ?? PROBE_COMMANDS[name];
92
+ try {
93
+ const result = Bun.spawnSync([cmd, ...args], {
94
+ stdout: 'pipe',
95
+ stderr: 'pipe',
96
+ timeout: 10000,
97
+ });
98
+ if (result.exitCode === 0) {
99
+ const stdout = result.stdout.toString().trim();
100
+ return name.startsWith('orca-') ? validateOrcaOutput(name, stdout) : { ok: true, stdout };
101
+ }
102
+ const detail = (result.stderr.toString().trim() || result.stdout.toString().trim()).slice(0, 120);
103
+ return { ok: false, stdout: detail || `exit ${result.exitCode}` };
104
+ } catch (err) {
105
+ return { ok: false, stdout: err?.code ?? 'not found' };
106
+ }
107
+ }
108
+
109
+ export async function checkCapabilities(exec, resolution = {}) {
110
+ const orcaExecutable = resolveOrcaExecutable(resolution);
111
+ const names = [
112
+ 'bun', 'git', 'gh', 'gh-stack', 'orca-binary', 'orca-runtime',
113
+ 'orca-orchestration-guide', 'orca-cli-guide',
114
+ ];
115
+ const checks = [];
116
+ for (const name of names) {
117
+ let result;
118
+ try {
119
+ result = await exec(name, { orcaExecutable });
120
+ } catch (err) {
121
+ result = { ok: false, stdout: err?.message ?? 'error' };
122
+ }
123
+ const ok = !!result?.ok;
124
+ const baseLabel = CHECK_LABELS[name] ?? name;
125
+ checks.push({
126
+ name,
127
+ label: !ok && name.startsWith('orca-')
128
+ ? `${baseLabel} via ${orcaExecutable}`
129
+ : baseLabel,
130
+ ok,
131
+ detail: ok
132
+ ? String(result?.stdout ?? '').trim().slice(0, 120) || 'found'
133
+ : String(result?.stdout ?? result?.detail ?? '').trim().slice(0, 120) || 'not found',
134
+ });
135
+ }
136
+ const gaps = checks.filter((c) => !c.ok).map((c) => `missing ${c.label}`);
137
+ return { checks, gaps, limitations: [...PROBE_LIMITATIONS] };
138
+ }