heyspex 0.2.0 → 0.2.2
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/.claude-plugin/marketplace.json +14 -0
- package/bin/hs.mjs +181 -15
- package/dist/cli/hs-check.mjs +88 -46
- package/dist/cli/hs-claim.mjs +84 -39
- package/dist/cli/hs-connect.mjs +583 -129
- package/dist/cli/hs-defer.mjs +251 -30
- package/dist/cli/hs-dev.mjs +20 -8
- package/dist/cli/hs-env.mjs +62 -34
- package/dist/cli/hs-index-rebuild.mjs +288 -83
- package/dist/cli/hs-index-reconcile.mjs +397 -111
- package/dist/cli/hs-init.mjs +2143 -91
- package/dist/cli/hs-install.mjs +628 -106
- package/dist/cli/hs-join.mjs +686 -236
- package/dist/cli/hs-vault-pull.mjs +3189 -179
- package/dist/cli/hs-vault-push.mjs +6433 -0
- package/dist/cli/hs-vault.mjs +396 -178
- package/dist/cli/hs-workspace.mjs +289 -151
- package/dist/cli-templates/harness-commands/claude/hs-backfill.md +28 -28
- package/dist/cli-templates/harness-commands/claude/hs-capture.md +62 -62
- package/dist/cli-templates/harness-commands/claude/hs-defer.md +29 -29
- package/dist/cli-templates/harness-commands/claude/hs-ground.md +30 -28
- package/dist/cli-templates/harness-commands/claude/hs-next.md +29 -29
- package/dist/cli-templates/harness-commands/claude/hs-promote.md +70 -70
- package/dist/cli-templates/harness-commands/claude/hs-push.md +35 -35
- package/dist/cli-templates/harness-commands/claude/hs-qc.md +27 -27
- package/dist/cli-templates/harness-commands/claude/hs-resolve.md +23 -23
- package/dist/cli-templates/harness-commands/claude/hs-review.md +23 -23
- package/dist/cli-templates/harness-commands/claude/hs-view.md +28 -26
- package/dist/cli-templates/harness-commands/claude/kill-ports.md +32 -32
- package/dist/cli-templates/harness-commands/claude/prototype.md +91 -91
- package/dist/cli-templates/harness-commands/cursor/hs-backfill.md +22 -22
- package/dist/cli-templates/harness-commands/cursor/hs-capture.md +56 -56
- package/dist/cli-templates/harness-commands/cursor/hs-defer.md +23 -23
- package/dist/cli-templates/harness-commands/cursor/hs-ground.md +24 -22
- package/dist/cli-templates/harness-commands/cursor/hs-next.md +23 -23
- package/dist/cli-templates/harness-commands/cursor/hs-promote.md +64 -64
- package/dist/cli-templates/harness-commands/cursor/hs-push.md +29 -29
- package/dist/cli-templates/harness-commands/cursor/hs-qc.md +21 -21
- package/dist/cli-templates/harness-commands/cursor/hs-resolve.md +17 -17
- package/dist/cli-templates/harness-commands/cursor/hs-review.md +17 -17
- package/dist/cli-templates/harness-commands/cursor/hs-view.md +24 -22
- package/dist/cli-templates/harness-commands/cursor/kill-ports.md +26 -26
- package/dist/cli-templates/harness-commands/cursor/prototype.md +86 -86
- package/docs/copilot-plugin-install.md +177 -0
- package/docs/driving-heyspex-from-copilot.md +165 -0
- package/package.json +21 -2
- package/plugins/heyspex/.claude-plugin/plugin.json +12 -12
- package/plugins/heyspex/.cursor-plugin/plugin.json +15 -15
- package/plugins/heyspex/README.md +312 -0
- package/plugins/heyspex/bin/run-hook.mjs +20 -2
- package/plugins/heyspex/hooks/cursor-hooks.json +81 -0
- package/plugins/heyspex/hooks/hooks.json +94 -37
- package/scripts/claude-hooks/_intent-classifier-router.mjs +456 -410
- package/scripts/claude-hooks/_intent-classifier-semantic.mjs +5 -0
- package/scripts/claude-hooks/_run.mjs +316 -23
- package/scripts/claude-hooks/capture-execution-gate.mjs +51 -0
- package/scripts/claude-hooks/claim-liveness-shared.mjs +2 -0
- package/scripts/claude-hooks/diagnose-capture-reminder.mjs +11 -0
- package/scripts/claude-hooks/directive-obligation-gate.mjs +11 -0
- package/scripts/claude-hooks/inject-communication-style.mjs +20 -0
- package/scripts/claude-hooks/intent-classifier-trigger.mjs +279 -247
- package/scripts/claude-hooks/session-end.mjs +2 -0
- package/scripts/claude-hooks/vault-mirror-catchup.mjs +11 -0
- package/scripts/claude-hooks/vault-write-tier-guard.mjs +11 -0
- package/scripts/claude-hooks/wip-checkpoint.mjs +5 -1
- package/scripts/cursor-hooks/manifest.mjs +46 -0
- package/scripts/heyspex-mode.mjs +20 -0
- package/scripts/hs-mode.mjs +1 -1
- package/scripts/hs-view.mjs +180 -17
- package/scripts/install-claude-code-hooks.mjs +3 -1
- package/scripts/install-copilot-hooks.mjs +288 -0
- package/scripts/lib/console-url-resolver.mjs +3 -2
- package/scripts/lib/customer-kit.mjs +327 -10
- package/scripts/lib/dev-mode.mjs +7 -6
- package/scripts/lib/directive-obligations.mjs +349 -0
- package/scripts/lib/hook-dispositions.mjs +210 -0
- package/scripts/lib/hook-vault-header.mjs +30 -0
- package/scripts/lib/hs-source-branch.mjs +196 -0
- package/scripts/lib/hs-source-log.mjs +2 -1
- package/scripts/lib/hs-source-preflight.mjs +18 -4
- package/scripts/lib/hs-source-push.mjs +386 -356
- package/scripts/lib/window-placement.mjs +109 -0
- package/spexharness/src/adapters/claude-code.mjs +11 -3
- package/spexharness/src/adapters/copilot.mjs +236 -0
- package/spexharness/src/adapters/detect-harness.mjs +34 -0
- package/spexharness/src/gates/_shell-write-targets.mjs +77 -0
- package/spexharness/src/gates/deep-research-cost-gate.mjs +4 -3
- package/spexharness/src/gates/diagnose-capture-reminder.mjs +112 -0
- package/spexharness/src/gates/directive-obligation-gate.decide.mjs +132 -0
- package/spexharness/src/gates/directive-obligation-gate.mjs +183 -0
- package/spexharness/src/gates/vault-mirror-catchup.mjs +124 -0
- package/spexharness/src/gates/vault-write-tier-guard.decide.mjs +125 -0
- package/spexharness/src/gates/vault-write-tier-guard.mjs +42 -0
- package/spexharness/src/intent-classifier.mjs +4 -2
- package/spexharness/src/intent-directive.mjs +626 -541
- package/spexharness/src/rule-table.mjs +31 -1
- package/vendor/heyspex-mcp/account-switch.mjs +6 -5
- package/vendor/heyspex-mcp/capture-buffer-patches.mjs +31 -3
- package/vendor/heyspex-mcp/capture-console-client.mjs +37 -2
- package/vendor/heyspex-mcp/capture-focus.mjs +26 -2
- package/vendor/heyspex-mcp/capture-outbox.mjs +10 -4
- package/vendor/heyspex-mcp/capture-sources.mjs +195 -0
- package/vendor/heyspex-mcp/capture-tools.mjs +56 -11
- package/vendor/heyspex-mcp/claim-tools.mjs +25 -2
- package/vendor/heyspex-mcp/cli-verbs.mjs +19 -21
- package/vendor/heyspex-mcp/cli.mjs +7 -1
- package/vendor/heyspex-mcp/close-branch-report.mjs +102 -0
- package/vendor/heyspex-mcp/cloud-vault-writes.mjs +197 -7
- package/vendor/heyspex-mcp/console-fetch.mjs +192 -28
- package/vendor/heyspex-mcp/console-transport.mjs +7 -1
- package/vendor/heyspex-mcp/credentials.mjs +9 -8
- package/vendor/heyspex-mcp/dev-tools.mjs +7 -0
- package/vendor/heyspex-mcp/filing-tools.mjs +35 -0
- package/vendor/heyspex-mcp/fleet-queue-tools.mjs +2 -1
- package/vendor/heyspex-mcp/folder-trust-store.mjs +2 -1
- package/vendor/heyspex-mcp/http-transport.mjs +2 -0
- package/vendor/heyspex-mcp/list-tools.mjs +1 -1
- package/vendor/heyspex-mcp/mcp-config.mjs +14 -1
- package/vendor/heyspex-mcp/memory-tools.mjs +523 -523
- package/vendor/heyspex-mcp/obligation-backstop.mjs +103 -0
- package/vendor/heyspex-mcp/occupied-keys.mjs +1 -0
- package/vendor/heyspex-mcp/on-deck-brief.mjs +19 -2
- package/vendor/heyspex-mcp/on-deck-scheduler.mjs +1708 -1708
- package/vendor/heyspex-mcp/plan-push.mjs +3 -2
- package/vendor/heyspex-mcp/preflight-gate.mjs +1 -0
- package/vendor/heyspex-mcp/procedure-handlers.mjs +327 -325
- package/vendor/heyspex-mcp/procedure-tools.mjs +1181 -1159
- package/vendor/heyspex-mcp/promote-capture.mjs +2 -0
- package/vendor/heyspex-mcp/promote-research.mjs +6 -0
- package/vendor/heyspex-mcp/read-path.mjs +162 -6
- package/vendor/heyspex-mcp/request-context.mjs +25 -1
- package/vendor/heyspex-mcp/research-handlers.mjs +6 -4
- package/vendor/heyspex-mcp/roadmap-default.mjs +70 -0
- package/vendor/heyspex-mcp/session-owner.mjs +3 -2
- package/vendor/heyspex-mcp/session-tools.mjs +10 -0
- package/vendor/heyspex-mcp/spec-edit-tools.mjs +8 -1
- package/vendor/heyspex-mcp/split-research.mjs +3 -0
- package/vendor/heyspex-mcp/submit-for-review.mjs +18 -1
- package/vendor/heyspex-mcp/summary-write-tools.mjs +5 -1
- package/vendor/heyspex-mcp/test-helpers.mjs +18 -3
- package/vendor/heyspex-mcp/tool-catalog.mjs +7 -1
- package/vendor/heyspex-mcp/tool-dispatch.mjs +18 -0
- package/vendor/heyspex-mcp/vault-io.mjs +720 -724
- package/vendor/heyspex-mcp/vault-resolve.mjs +197 -9
- package/vendor/heyspex-mcp/whoami.mjs +2 -1
- package/vendor/heyspex-mcp/write-outbox.mjs +512 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* hs-source-branch.mjs — `hs source branch declare|show`, plus
|
|
4
|
+
* `resolvePushBranch` used by `hs source push` when no branch is typed
|
|
5
|
+
* ([[HS-G01N-CWPX-TASK-hs-source-declares-the-session-branch]], epic
|
|
6
|
+
* [[HS-B8CY-CWPX-EPIC-one-session-one-reviewable-change]]).
|
|
7
|
+
*
|
|
8
|
+
* The branch DECISION stays on the server: `GET /api/source/session-branch`
|
|
9
|
+
* runs the one shared resolver (`resolveSessionTargetBranch`,
|
|
10
|
+
* spex-console/src/lib/git-export/branch-resolver.ts) — the same function the
|
|
11
|
+
* cloud-write drain calls. This module never re-implements ref validation or
|
|
12
|
+
* the dated-branch rule; it only declares, asks, and falls back to the local
|
|
13
|
+
* HEAD when the console cannot be reached (and says so).
|
|
14
|
+
*
|
|
15
|
+
* The HTTP client is injectable (`fetchImpl`) so tests never touch a network;
|
|
16
|
+
* console URL + bearer reuse the same idiom as `hs-source-log.mjs`.
|
|
17
|
+
*/
|
|
18
|
+
import { spawnSync } from 'node:child_process';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
|
|
22
|
+
import { resolveConsoleUrl } from './console-url-resolver.mjs';
|
|
23
|
+
import { bearerHeaderFor } from './hs-source-log.mjs';
|
|
24
|
+
import { hookVaultHeaders } from './hook-vault-header.mjs';
|
|
25
|
+
|
|
26
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
export const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
28
|
+
|
|
29
|
+
const ENDPOINT = '/api/source/session-branch';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Current checked-out branch, or null (detached HEAD / git unavailable).
|
|
33
|
+
* @param {{ cwd?: string, run?: (cmd: string, args: string[], opts: object) => { status: number|null, stdout?: string } }} [opts]
|
|
34
|
+
* @returns {string | null}
|
|
35
|
+
*/
|
|
36
|
+
export function currentGitBranch({ cwd = process.cwd(), run = spawnSync } = {}) {
|
|
37
|
+
try {
|
|
38
|
+
const r = run('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, encoding: 'utf8' });
|
|
39
|
+
const out = (r.stdout ?? '').toString().trim();
|
|
40
|
+
if (r.status !== 0 || !out || out === 'HEAD') return null;
|
|
41
|
+
return out;
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function callConsole({ method, body, fetchImpl, consoleUrl, repoRoot }) {
|
|
48
|
+
const base = consoleUrl ?? resolveConsoleUrl({ repoRoot });
|
|
49
|
+
const headers = { ...bearerHeaderFor(base), ...hookVaultHeaders() };
|
|
50
|
+
if (body !== undefined) headers['content-type'] = 'application/json';
|
|
51
|
+
const res = await fetchImpl(`${base}${ENDPOINT}`, {
|
|
52
|
+
method,
|
|
53
|
+
headers,
|
|
54
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
55
|
+
});
|
|
56
|
+
let json = null;
|
|
57
|
+
try {
|
|
58
|
+
json = await res.json();
|
|
59
|
+
} catch {
|
|
60
|
+
json = null;
|
|
61
|
+
}
|
|
62
|
+
return { ok: res.ok, status: res.status, json, base };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Ask the console which branch this session's work should target.
|
|
67
|
+
* Never throws. `via: 'console'` carries the resolver's `source`
|
|
68
|
+
* ('session' | 'dated'); `via: 'git-head'` is the explicit offline fallback.
|
|
69
|
+
* @returns {Promise<{ branch: string, via: 'console'|'git-head', source?: string, note?: string } | { branch: null, via: 'none', note: string }>}
|
|
70
|
+
*/
|
|
71
|
+
export async function resolvePushBranch({
|
|
72
|
+
fetchImpl = fetch,
|
|
73
|
+
cwd = process.cwd(),
|
|
74
|
+
consoleUrl,
|
|
75
|
+
repoRoot,
|
|
76
|
+
run,
|
|
77
|
+
} = {}) {
|
|
78
|
+
try {
|
|
79
|
+
const r = await callConsole({ method: 'GET', fetchImpl, consoleUrl, repoRoot });
|
|
80
|
+
const branch = r.ok ? r.json?.target?.branch : null;
|
|
81
|
+
if (typeof branch === 'string' && branch) {
|
|
82
|
+
return { branch, via: 'console', source: r.json.target.source };
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
// fall through to the local fallback
|
|
86
|
+
}
|
|
87
|
+
const head = currentGitBranch({ cwd, run });
|
|
88
|
+
if (head) {
|
|
89
|
+
return {
|
|
90
|
+
branch: head,
|
|
91
|
+
via: 'git-head',
|
|
92
|
+
note: `console unreachable — pushing the checked-out branch \`${head}\` instead of the resolver's target`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return { branch: null, via: 'none', note: 'console unreachable and no checked-out branch to fall back to' };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Spec commits the cloud drain made for this session, from the same console
|
|
100
|
+
* endpoint ([[HS-OAD2-CWPX-TASK-hs-source-status-names-the-spec-commits]]).
|
|
101
|
+
* Never throws. `known: false` means the console could not answer — callers
|
|
102
|
+
* must NOT present that as "no commits".
|
|
103
|
+
* @returns {Promise<{ known: true, commits: Array<{repo:string,branch:string,commitSha:string,settledAt:string,specIds:string[]}>, basis?: string } | { known: false }>}
|
|
104
|
+
*/
|
|
105
|
+
export async function fetchDrainCommits({ fetchImpl = fetch, consoleUrl, repoRoot } = {}) {
|
|
106
|
+
try {
|
|
107
|
+
const r = await callConsole({ method: 'GET', fetchImpl, consoleUrl, repoRoot });
|
|
108
|
+
if (!r.ok || !Array.isArray(r.json?.drainCommits)) return { known: false };
|
|
109
|
+
return { known: true, commits: r.json.drainCommits, basis: r.json.window?.basis };
|
|
110
|
+
} catch {
|
|
111
|
+
return { known: false };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Pure formatter for the `hs source status` section.
|
|
117
|
+
* @param {Awaited<ReturnType<typeof fetchDrainCommits>>} result
|
|
118
|
+
* @returns {string[]}
|
|
119
|
+
*/
|
|
120
|
+
export function formatDrainCommits(result) {
|
|
121
|
+
const head = 'Cloud spec commits (this session):';
|
|
122
|
+
if (!result.known) return [head, ' unknown (console unreachable)'];
|
|
123
|
+
if (result.commits.length === 0) return [head, ' No cloud spec commits for this session.'];
|
|
124
|
+
const lines = [head];
|
|
125
|
+
for (const c of result.commits) {
|
|
126
|
+
lines.push(` ${c.branch} @ ${String(c.commitSha).slice(0, 7)} ${c.specIds.join(', ')}`);
|
|
127
|
+
}
|
|
128
|
+
if (result.basis === 'last_12h') lines.push(' (no branch declared — showing the last 12 hours)');
|
|
129
|
+
return lines;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* `hs source branch declare [<branch>]` / `hs source branch show`.
|
|
134
|
+
* @param {string[]} argv args after the `branch` verb
|
|
135
|
+
* @returns {Promise<number>} exit code
|
|
136
|
+
*/
|
|
137
|
+
export async function runBranchCommand(
|
|
138
|
+
argv,
|
|
139
|
+
{ fetchImpl = fetch, cwd = process.cwd(), consoleUrl, repoRoot, run, out = console.log, err = console.error } = {},
|
|
140
|
+
) {
|
|
141
|
+
const sub = argv[0] ?? 'show';
|
|
142
|
+
|
|
143
|
+
if (sub === 'show') {
|
|
144
|
+
try {
|
|
145
|
+
const r = await callConsole({ method: 'GET', fetchImpl, consoleUrl, repoRoot });
|
|
146
|
+
if (!r.ok || !r.json?.target) {
|
|
147
|
+
err(`hs source branch show: console answered ${r.status} — no target available.`);
|
|
148
|
+
return 1;
|
|
149
|
+
}
|
|
150
|
+
const t = r.json.target;
|
|
151
|
+
out(`target branch: ${t.branch} (${t.source === 'session' ? 'declared by this session' : 'dated fallback — no session branch declared'})`);
|
|
152
|
+
if (t.reason) out(`note: declared branch was refused — ${t.reason}`);
|
|
153
|
+
if (r.json.declaration) out(`declared ${r.json.declaration.declaredAt}, expires ${r.json.declaration.expiresAt}`);
|
|
154
|
+
return 0;
|
|
155
|
+
} catch (e) {
|
|
156
|
+
err(`hs source branch show: console unreachable (${e instanceof Error ? e.message : String(e)}).`);
|
|
157
|
+
return 1;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (sub === 'declare') {
|
|
162
|
+
const branch = argv[1] ?? currentGitBranch({ cwd, run });
|
|
163
|
+
if (!branch) {
|
|
164
|
+
err('hs source branch declare: no branch given and no checked-out branch (detached HEAD?).');
|
|
165
|
+
return 1;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const r = await callConsole({ method: 'POST', body: { branch }, fetchImpl, consoleUrl, repoRoot });
|
|
169
|
+
if (!r.ok) {
|
|
170
|
+
err(`hs source branch declare: refused — ${r.json?.reason ?? r.json?.error ?? `HTTP ${r.status}`}`);
|
|
171
|
+
return 1;
|
|
172
|
+
}
|
|
173
|
+
out(`declared session branch: ${r.json.declaration.branch} (expires ${r.json.declaration.expiresAt})`);
|
|
174
|
+
return 0;
|
|
175
|
+
} catch (e) {
|
|
176
|
+
err(`hs source branch declare: console unreachable (${e instanceof Error ? e.message : String(e)}).`);
|
|
177
|
+
return 1;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
err(`hs source branch: unknown subcommand \`${sub}\` — expected declare|show.`);
|
|
182
|
+
return 2;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function invokedDirectly() {
|
|
186
|
+
try {
|
|
187
|
+
return path.resolve(fileURLToPath(import.meta.url)) === path.resolve(process.argv[1] ?? '');
|
|
188
|
+
} catch {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (invokedDirectly()) {
|
|
194
|
+
// argv: [node, script, "branch", ...args]
|
|
195
|
+
runBranchCommand(process.argv.slice(3)).then((code) => process.exit(code));
|
|
196
|
+
}
|
|
@@ -30,6 +30,7 @@ import path from 'node:path';
|
|
|
30
30
|
import { fileURLToPath } from 'node:url';
|
|
31
31
|
|
|
32
32
|
import { readBearerToken } from '../../vendor/heyspex-mcp/credentials.mjs';
|
|
33
|
+
import { hookVaultHeaders } from './hook-vault-header.mjs';
|
|
33
34
|
import { resolveConsoleUrl } from './console-url-resolver.mjs';
|
|
34
35
|
|
|
35
36
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -131,7 +132,7 @@ export async function lookupConsoleSettlement({
|
|
|
131
132
|
|
|
132
133
|
let res;
|
|
133
134
|
try {
|
|
134
|
-
res = await fetchImpl(url, { headers: { ...bearerHeaderFor(resolvedUrl) } });
|
|
135
|
+
res = await fetchImpl(url, { headers: { ...bearerHeaderFor(resolvedUrl), ...hookVaultHeaders() } });
|
|
135
136
|
} catch (err) {
|
|
136
137
|
return {
|
|
137
138
|
found: false,
|
|
@@ -361,12 +361,26 @@ export function formatPreflightReport(plan) {
|
|
|
361
361
|
|
|
362
362
|
/**
|
|
363
363
|
* CLI entry for `hs source status`. Returns a process exit code; always 0
|
|
364
|
-
* (read-only report, never fails the invocation itself).
|
|
365
|
-
*
|
|
364
|
+
* (read-only report, never fails the invocation itself). Appends the "Cloud
|
|
365
|
+
* spec commits (this session)" section from the console
|
|
366
|
+
* ([[HS-OAD2-CWPX-TASK-hs-source-status-names-the-spec-commits]]); the
|
|
367
|
+
* console lookup is lazy-imported and injectable, and an unreachable console
|
|
368
|
+
* prints "unknown", never "none".
|
|
369
|
+
* @param {{ cwd?: string, fetchDrainCommitsFn?: () => Promise<any> }} [opts]
|
|
370
|
+
* @returns {Promise<number>}
|
|
366
371
|
*/
|
|
367
|
-
export function runStatusCommand({ cwd = process.cwd() } = {}) {
|
|
372
|
+
export async function runStatusCommand({ cwd = process.cwd(), fetchDrainCommitsFn } = {}) {
|
|
368
373
|
const status = getTreeStatus({ cwd });
|
|
369
374
|
console.log(formatStatusReport(status));
|
|
375
|
+
try {
|
|
376
|
+
const mod = await import('./hs-source-branch.mjs');
|
|
377
|
+
const result = await (fetchDrainCommitsFn ?? mod.fetchDrainCommits)();
|
|
378
|
+
console.log('');
|
|
379
|
+
console.log(mod.formatDrainCommits(result).join('\n'));
|
|
380
|
+
} catch {
|
|
381
|
+
console.log('');
|
|
382
|
+
console.log('Cloud spec commits (this session):\n unknown (console lookup unavailable)');
|
|
383
|
+
}
|
|
370
384
|
return 0;
|
|
371
385
|
}
|
|
372
386
|
|
|
@@ -398,5 +412,5 @@ function invokedDirectly() {
|
|
|
398
412
|
if (invokedDirectly()) {
|
|
399
413
|
const verb = process.argv[2];
|
|
400
414
|
if (verb === 'preflight') process.exit(runPreflightCommand());
|
|
401
|
-
else process.exit(
|
|
415
|
+
else runStatusCommand().then((code) => process.exit(code));
|
|
402
416
|
}
|