heyspex 0.1.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.
- package/README.md +157 -0
- package/bin/hs.mjs +1028 -0
- package/bin/postinstall.mjs +88 -0
- package/dev-factory/lib/fleet-readiness.mjs +274 -0
- package/dev-factory/lib/fleet-state.mjs +250 -0
- package/dev-factory/lib/sufficiency-score.mjs +441 -0
- package/dist/cli/hs-check.mjs +747 -0
- package/dist/cli/hs-claim.mjs +541 -0
- package/dist/cli/hs-connect.mjs +3436 -0
- package/dist/cli/hs-defer.mjs +8455 -0
- package/dist/cli/hs-env.mjs +36845 -0
- package/dist/cli/hs-init.mjs +1145 -0
- package/dist/cli/hs-install.mjs +1390 -0
- package/dist/cli/hs-join.mjs +38553 -0
- package/dist/cli/hs-vault-pull.mjs +7782 -0
- package/dist/cli/hs-vault.mjs +4166 -0
- package/dist/cli/hs-workspace.mjs +2898 -0
- package/dist/cli-templates/customer-vault/specs/entity-model.md +1735 -0
- package/dist/cli-templates/customer-vault/specs/protocols/canonical-frontmatter-templates.md +531 -0
- package/dist/cli-templates/customer-vault/specs/protocols/memory-protocol.md +48 -0
- package/dist/cli-templates/customer-vault/specs/protocols/spec-hierarchy.md +548 -0
- package/docs/mcp-client-config.md +200 -0
- package/package.json +124 -0
- package/scripts/check-mcp-freshness.mjs +379 -0
- package/scripts/dev-server-status.mjs +722 -0
- package/scripts/gateway-skills-lib.mjs +421 -0
- package/scripts/lib/console-url-resolver.mjs +152 -0
- package/scripts/lib/dev-mode.mjs +369 -0
- package/scripts/lib/hs-link.mjs +154 -0
- package/scripts/lib/hs-source-commit.mjs +207 -0
- package/scripts/lib/hs-source-log.mjs +366 -0
- package/scripts/lib/hs-source-preflight.mjs +402 -0
- package/scripts/lib/hs-source-push.mjs +307 -0
- package/scripts/lib/hs-source-secret-scan.mjs +263 -0
- package/scripts/lib/standalone-doctor.mjs +89 -0
- package/scripts/vault-id-lib.mjs +173 -0
- package/vendor/heyspex-mcp/alignment-gate.mjs +1347 -0
- package/vendor/heyspex-mcp/blocker-resolver.mjs +891 -0
- package/vendor/heyspex-mcp/breakdown-ladder.mjs +309 -0
- package/vendor/heyspex-mcp/breakdown-source.mjs +236 -0
- package/vendor/heyspex-mcp/breakdown-writer.mjs +524 -0
- package/vendor/heyspex-mcp/brief-observability.mjs +67 -0
- package/vendor/heyspex-mcp/canvas-inbox.mjs +130 -0
- package/vendor/heyspex-mcp/capture-active-rehydrate.mjs +76 -0
- package/vendor/heyspex-mcp/capture-buffer-patches.mjs +147 -0
- package/vendor/heyspex-mcp/capture-channel.mjs +577 -0
- package/vendor/heyspex-mcp/capture-console-client.mjs +290 -0
- package/vendor/heyspex-mcp/capture-focus.mjs +1366 -0
- package/vendor/heyspex-mcp/capture-kind-suggest.mjs +121 -0
- package/vendor/heyspex-mcp/capture-outbox.mjs +239 -0
- package/vendor/heyspex-mcp/capture-stage-gate.mjs +160 -0
- package/vendor/heyspex-mcp/cli.mjs +15754 -0
- package/vendor/heyspex-mcp/config-settings.mjs +419 -0
- package/vendor/heyspex-mcp/console-fetch.mjs +342 -0
- package/vendor/heyspex-mcp/defer-spec.mjs +527 -0
- package/vendor/heyspex-mcp/deferred-reclaim.mjs +284 -0
- package/vendor/heyspex-mcp/design-tools.mjs +449 -0
- package/vendor/heyspex-mcp/dev-factory-bridge.mjs +66 -0
- package/vendor/heyspex-mcp/diagnose-skeleton.mjs +114 -0
- package/vendor/heyspex-mcp/fleet-queue-tools.mjs +241 -0
- package/vendor/heyspex-mcp/hooks-engine.mjs +399 -0
- package/vendor/heyspex-mcp/login-handoff.mjs +438 -0
- package/vendor/heyspex-mcp/mcp-subprocess.mjs +136 -0
- package/vendor/heyspex-mcp/mvp-mode.mjs +101 -0
- package/vendor/heyspex-mcp/next-resolution.mjs +240 -0
- package/vendor/heyspex-mcp/next-review.mjs +548 -0
- package/vendor/heyspex-mcp/peer-presence.mjs +430 -0
- package/vendor/heyspex-mcp/plan-ground-conflicts.mjs +321 -0
- package/vendor/heyspex-mcp/plan-ground.mjs +2001 -0
- package/vendor/heyspex-mcp/plan-ingest.mjs +476 -0
- package/vendor/heyspex-mcp/plan-push.mjs +1826 -0
- package/vendor/heyspex-mcp/plan-review.mjs +1419 -0
- package/vendor/heyspex-mcp/preflight-gate.mjs +0 -0
- package/vendor/heyspex-mcp/procedure-tools.mjs +970 -0
- package/vendor/heyspex-mcp/promote-capture.mjs +1280 -0
- package/vendor/heyspex-mcp/promote-diagnose.mjs +187 -0
- package/vendor/heyspex-mcp/promote-research-to-diagnose.mjs +338 -0
- package/vendor/heyspex-mcp/promote-research.mjs +1553 -0
- package/vendor/heyspex-mcp/promotion-helpers.mjs +280 -0
- package/vendor/heyspex-mcp/read-path.mjs +138 -0
- package/vendor/heyspex-mcp/recipe-tools.mjs +258 -0
- package/vendor/heyspex-mcp/regen-tasks.mjs +326 -0
- package/vendor/heyspex-mcp/register-spec.mjs +417 -0
- package/vendor/heyspex-mcp/research-focus.mjs +476 -0
- package/vendor/heyspex-mcp/research-tools.mjs +109 -0
- package/vendor/heyspex-mcp/research-type-suggest.mjs +112 -0
- package/vendor/heyspex-mcp/resolution-focus.mjs +45 -0
- package/vendor/heyspex-mcp/review-focus.mjs +55 -0
- package/vendor/heyspex-mcp/review-rollup.mjs +47 -0
- package/vendor/heyspex-mcp/run-front.mjs +60 -0
- package/vendor/heyspex-mcp/scheduler.mjs +813 -0
- package/vendor/heyspex-mcp/seat-identity.mjs +281 -0
- package/vendor/heyspex-mcp/settings-tools.mjs +119 -0
- package/vendor/heyspex-mcp/spec-id-lib.mjs +790 -0
- package/vendor/heyspex-mcp/split-research.mjs +832 -0
- package/vendor/heyspex-mcp/submit-for-review.mjs +190 -0
- package/vendor/heyspex-mcp/summary-contract.mjs +162 -0
- package/vendor/heyspex-mcp/test-helpers.mjs +43 -0
- package/vendor/heyspex-mcp/validate-linkage.mjs +574 -0
- package/vendor/heyspex-mcp/vault-resolve.mjs +297 -0
- package/vendor/heyspex-mcp/vault-settings.mjs +87 -0
- package/vendor/heyspex-mcp/verify-reproduction.mjs +314 -0
- package/vendor/heyspex-mcp/why-now.mjs +64 -0
- package/vendor/heyspex-mcp/yaml-scalar.mjs +34 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic capture `kind` → suggested research `research_type` mapping
|
|
3
|
+
* — [[HS-MVC1-EPIC-deterministic-type-assist-suggest-resear]] /
|
|
4
|
+
* [[HS-MVC1-TASK-deterministic-research-type-assist-sugge]].
|
|
5
|
+
*
|
|
6
|
+
* HS-120I ([[HS-120I-EPIC-research-type-field-the-declared-purpose]]) added
|
|
7
|
+
* the required `research_type:` field on research docs and left a deliberate
|
|
8
|
+
* seam — `resolveResearchType(providedValue, suggestedType)` in
|
|
9
|
+
* `promote-capture.mjs` — for a future kind-based suggestion. This module
|
|
10
|
+
* IS that seam's inference: a small, pure, dependency-free function so both
|
|
11
|
+
* the promotion flow (`promote-capture.mjs`) and any future validation path
|
|
12
|
+
* can import the same mapping without duplicating it.
|
|
13
|
+
*
|
|
14
|
+
* Capture `kind:` enum (6 values, locked by
|
|
15
|
+
* [[HS-DLQI-EPIC-capture-rename-migration-idea-to-capture]] Milestone A —
|
|
16
|
+
* see [[entity-model]] §capture): `idea | bug-fix | clip | reference | question |
|
|
17
|
+
* feedback`. `feedback` was added by
|
|
18
|
+
* [[HS-I5DV-EPIC-feedback-capture-type-deterministic]] ([[HS-48ZZ-TASK]]).
|
|
19
|
+
*
|
|
20
|
+
* research `research_type:` enum (5 values — see [[entity-model]] §research /
|
|
21
|
+
* `schemas/heyspex-frontmatter.schema.json`): `new-feature | enhancement |
|
|
22
|
+
* diagnose | code-audit | reference`.
|
|
23
|
+
*
|
|
24
|
+
* Mapping (deliberately NOT 1:1 — the two vocabularies overlap only at
|
|
25
|
+
* `reference`):
|
|
26
|
+
* - `bug-fix` → `diagnose` (a defect capture becomes diagnose-purpose research)
|
|
27
|
+
* - `idea` → `new-feature` primary, `enhancement` alternative (an idea
|
|
28
|
+
* capture could seed either a net-new feature or an
|
|
29
|
+
* enhancement to something existing — BOTH are surfaced,
|
|
30
|
+
* never silently collapsed to one; `new-feature` is picked
|
|
31
|
+
* as the single value a caller writes when it must pick one)
|
|
32
|
+
* - `reference`→ `reference` (the one kind/type name that coincide)
|
|
33
|
+
* - `clip` → `reference` (a saved excerpt is reference-purpose research)
|
|
34
|
+
* - `question` → `reference` (an open question has no dedicated
|
|
35
|
+
* research_type value; `reference` is the safe default —
|
|
36
|
+
* same as an unknown/unrecognized kind, below)
|
|
37
|
+
* - `feedback` → `reference` (feedback is not inherently research-shaped;
|
|
38
|
+
* `reference` is the same safe default used for
|
|
39
|
+
* `question`/`clip` — [[HS-48ZZ-TASK]])
|
|
40
|
+
* - unknown/missing kind → `reference` (safe default, never throws)
|
|
41
|
+
*
|
|
42
|
+
* `code-audit` has no capture `kind` that maps to it deterministically — it
|
|
43
|
+
* is reachable only via explicit human/agent selection, never inferred here.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/** The 6-value capture `kind:` enum ([[entity-model]] §capture). */
|
|
47
|
+
export const CAPTURE_KIND_VALUES = [
|
|
48
|
+
'idea',
|
|
49
|
+
'bug-fix',
|
|
50
|
+
'clip',
|
|
51
|
+
'reference',
|
|
52
|
+
'question',
|
|
53
|
+
'feedback',
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
/** The 5-value research `research_type:` enum ([[entity-model]] §research). */
|
|
57
|
+
export const RESEARCH_TYPE_VALUES = [
|
|
58
|
+
'new-feature',
|
|
59
|
+
'enhancement',
|
|
60
|
+
'diagnose',
|
|
61
|
+
'code-audit',
|
|
62
|
+
'reference',
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
/** Safe fallback for an unknown/missing/unmapped capture `kind`. */
|
|
66
|
+
export const DEFAULT_SUGGESTED_RESEARCH_TYPE = 'reference';
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @typedef {object} ResearchTypeSuggestion
|
|
70
|
+
* @property {string} primary - the single research_type a caller should default to.
|
|
71
|
+
* @property {string[]} alternatives - other plausible research_type values for this
|
|
72
|
+
* kind, surfaced (not silently discarded) for a future UI to offer as a choice.
|
|
73
|
+
* @property {boolean} known - true when `kind` matched a known enum value;
|
|
74
|
+
* false when it fell through to the safe default.
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
/** @type {Record<string, { primary: string, alternatives: string[] }>} */
|
|
78
|
+
const KIND_TO_RESEARCH_TYPE = {
|
|
79
|
+
'bug-fix': { primary: 'diagnose', alternatives: [] },
|
|
80
|
+
idea: { primary: 'new-feature', alternatives: ['enhancement'] },
|
|
81
|
+
reference: { primary: 'reference', alternatives: [] },
|
|
82
|
+
clip: { primary: 'reference', alternatives: [] },
|
|
83
|
+
question: { primary: 'reference', alternatives: [] },
|
|
84
|
+
feedback: { primary: 'reference', alternatives: [] },
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Full suggestion (primary + alternatives + known-ness) for a capture `kind`.
|
|
89
|
+
* Pure and deterministic: same input always yields the same output, no I/O,
|
|
90
|
+
* never throws. An unrecognized/null/undefined `kind` returns the safe
|
|
91
|
+
* default (`reference`) with `known: false` rather than throwing, so callers
|
|
92
|
+
* never need a try/catch around it.
|
|
93
|
+
* @param {unknown} kind
|
|
94
|
+
* @returns {ResearchTypeSuggestion}
|
|
95
|
+
*/
|
|
96
|
+
export function suggestResearchTypesForKind(kind) {
|
|
97
|
+
if (typeof kind === 'string' && Object.prototype.hasOwnProperty.call(KIND_TO_RESEARCH_TYPE, kind)) {
|
|
98
|
+
const entry = KIND_TO_RESEARCH_TYPE[kind];
|
|
99
|
+
return { primary: entry.primary, alternatives: [...entry.alternatives], known: true };
|
|
100
|
+
}
|
|
101
|
+
return { primary: DEFAULT_SUGGESTED_RESEARCH_TYPE, alternatives: [], known: false };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Convenience accessor: just the single `research_type` value a caller
|
|
106
|
+
* should pre-fill/pass as `suggestedType` to `resolveResearchType()`.
|
|
107
|
+
* @param {unknown} kind
|
|
108
|
+
* @returns {string}
|
|
109
|
+
*/
|
|
110
|
+
export function suggestResearchType(kind) {
|
|
111
|
+
return suggestResearchTypesForKind(kind).primary;
|
|
112
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolution-on-deck — the focus PIN for `/hs-resolve`.
|
|
3
|
+
* [[HS-EPIC-RMCP-hs-resolve-mcp-tool-and-skill]] · [[HS-EPIC-RSRF-resolution-surface-api-and-pin]]
|
|
4
|
+
*
|
|
5
|
+
* UNLIKE `review-focus.mjs` (which reads/writes `.heyspex/review-focus.yaml`),
|
|
6
|
+
* the resolution pin lives in Neon (`resolution_focus_pin`, keyed by the
|
|
7
|
+
* content-derived `item_key`). The vendor MCP has no Postgres client, so it
|
|
8
|
+
* reaches the pin ONLY through console HTTP — exactly as it reaches
|
|
9
|
+
* active-claims. There is ONE pin concept: this module and the console surface
|
|
10
|
+
* both go through `POST/GET/DELETE /api/resolution/focus`, never a second
|
|
11
|
+
* yaml writer. See [[no-fallbacks-by-default]] / [[console-unreachable-is-dev-only]].
|
|
12
|
+
*
|
|
13
|
+
* Every function takes the bound `consoleFetch` (from `makeConsoleFetch` in
|
|
14
|
+
* cli.mjs) so it stays unit-testable with a stub, and returns the raw
|
|
15
|
+
* `consoleFetch` result `{ ok, status, body, url, error?, errorKind? }` — the
|
|
16
|
+
* caller decides how to surface a transport failure.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const FOCUS_PATH = '/api/resolution/focus';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Read the current resolution focus pin.
|
|
23
|
+
* @returns the consoleFetch result; on success `result.body.pin` is the pin row
|
|
24
|
+
* (or null when nothing is pinned).
|
|
25
|
+
*/
|
|
26
|
+
export function loadResolutionFocus(consoleFetch) {
|
|
27
|
+
return consoleFetch(FOCUS_PATH);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Pin a resolution item by its content-derived `itemKey`. POSTs to the SAME
|
|
32
|
+
* canonical writer the console surface uses.
|
|
33
|
+
* @param {(path: string, opts?: object) => Promise<object>} consoleFetch
|
|
34
|
+
* @param {{ itemKey: string, reason?: string }} input
|
|
35
|
+
*/
|
|
36
|
+
export function writeResolutionFocus(consoleFetch, { itemKey, reason } = {}) {
|
|
37
|
+
const body = { item_key: itemKey };
|
|
38
|
+
if (reason) body.reason = reason;
|
|
39
|
+
return consoleFetch(FOCUS_PATH, { method: 'POST', body });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Clear the resolution focus pin (reverts the surface to the top-ranked auto-pick). */
|
|
43
|
+
export function clearResolutionFocus(consoleFetch) {
|
|
44
|
+
return consoleFetch(FOCUS_PATH, { method: 'DELETE' });
|
|
45
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review-on-deck — pin file (`.heyspex/review-focus.yaml`) and `/hs-review` flow.
|
|
3
|
+
* [[HS-TASK-RVDC-heyspex-review-on-deck-mcp-and-hs-review]] · [[HS-RES-CLR4-close-out-review-screen-jtbd-architecture]] §J16/J17
|
|
4
|
+
*
|
|
5
|
+
* Mirrors `.heyspex/focus.yaml` (build pipeline) and `.heyspex/research-focus.yaml`
|
|
6
|
+
* (research pipeline). Honored unconditionally when present, same semantics as
|
|
7
|
+
* [[focus-yaml-always-wins-in-hs-next]].
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
readFileSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
existsSync,
|
|
14
|
+
mkdirSync,
|
|
15
|
+
rmSync,
|
|
16
|
+
} from 'node:fs';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { parseSimpleFocusYaml } from './capture-focus.mjs';
|
|
19
|
+
|
|
20
|
+
export function reviewFocusPath(vaultRoot) {
|
|
21
|
+
return join(vaultRoot, '.heyspex', 'review-focus.yaml');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function loadReviewFocus(vaultRoot) {
|
|
25
|
+
const p = reviewFocusPath(vaultRoot);
|
|
26
|
+
if (!existsSync(p)) return null;
|
|
27
|
+
try {
|
|
28
|
+
return parseSimpleFocusYaml(readFileSync(p, 'utf-8'));
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function writeReviewFocus(vaultRoot, { specId, reason }) {
|
|
35
|
+
if (!specId) {
|
|
36
|
+
return { error: 'MISSING_SPEC_ID', message: 'spec_id is required' };
|
|
37
|
+
}
|
|
38
|
+
const dir = join(vaultRoot, '.heyspex');
|
|
39
|
+
mkdirSync(dir, { recursive: true });
|
|
40
|
+
const lines = [
|
|
41
|
+
'# HeySpex review-on-deck — MCP /hs-review ([[HS-TASK-RVDC-heyspex-review-on-deck-mcp-and-hs-review]])',
|
|
42
|
+
`spec_id: ${specId}`,
|
|
43
|
+
`pinned_at: ${new Date().toISOString()}`,
|
|
44
|
+
];
|
|
45
|
+
if (reason) lines.push(`reason: ${JSON.stringify(reason)}`);
|
|
46
|
+
writeFileSync(reviewFocusPath(vaultRoot), `${lines.join('\n')}\n`, 'utf8');
|
|
47
|
+
return { success: true };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function clearReviewFocus(vaultRoot) {
|
|
51
|
+
const p = reviewFocusPath(vaultRoot);
|
|
52
|
+
if (!existsSync(p)) return { existed: false };
|
|
53
|
+
rmSync(p);
|
|
54
|
+
return { existed: true };
|
|
55
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parent rollup classification — JS mirror of `classifyParentRollup` in
|
|
3
|
+
* `spex-console/src/lib/review-mode.ts`. Keep in sync when changing grain rules.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const KIND_RANK = { task: 0, epic: 1, milestone: 2 };
|
|
7
|
+
|
|
8
|
+
const REVIEW_AWAITING = new Set(['in_review', 'review_pending']);
|
|
9
|
+
|
|
10
|
+
export function reviewLevelForGrain(grain) {
|
|
11
|
+
if (grain === 'milestone' || grain === 'epic' || grain === 'task') return grain;
|
|
12
|
+
return 'task';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function rollupOptsFromMvpState(mvp) {
|
|
16
|
+
const mvpEnabled = mvp?.enabled === true;
|
|
17
|
+
return { reviewLevel: reviewLevelForGrain(mvpEnabled ? mvp?.review_grain : undefined) };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isBelowReviewLevel(kind, reviewLevel) {
|
|
21
|
+
return (KIND_RANK[kind] ?? 0) < (KIND_RANK[reviewLevel] ?? 0);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isUnitComplete(childStatuses) {
|
|
25
|
+
if (!childStatuses.length) return false;
|
|
26
|
+
let closed = 0;
|
|
27
|
+
let reviewed = 0;
|
|
28
|
+
let openish = 0;
|
|
29
|
+
for (const raw of childStatuses) {
|
|
30
|
+
const s = String(raw ?? '').toLowerCase();
|
|
31
|
+
if (s === 'closed') closed++;
|
|
32
|
+
else if (REVIEW_AWAITING.has(s)) reviewed++;
|
|
33
|
+
else if (s === 'open' || s === 'in_progress') openish++;
|
|
34
|
+
}
|
|
35
|
+
if (closed === childStatuses.length) return true;
|
|
36
|
+
return openish === 0 && reviewed > 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function classifyParentRollup(childStatuses, childKind, reviewLevel) {
|
|
40
|
+
if (isBelowReviewLevel(childKind, reviewLevel) && isUnitComplete(childStatuses)) {
|
|
41
|
+
return 'ready_for_close';
|
|
42
|
+
}
|
|
43
|
+
const hasAwaiting = childStatuses.some((s) =>
|
|
44
|
+
REVIEW_AWAITING.has(String(s ?? '').toLowerCase()),
|
|
45
|
+
);
|
|
46
|
+
return hasAwaiting ? 'reviewer_blocked' : 'ready_for_close';
|
|
47
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What's Next run-front tokens — shared by `heyspex_next_on_deck`, `hs next`,
|
|
3
|
+
* and slash-command parsers. Canonical values match the console `RunFront` type.
|
|
4
|
+
*
|
|
5
|
+
* @see [[whats-next-fronts-launch-grain]] · spex-console/src/hooks/use-run-front.ts
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** @typedef {'handheld' | 'assisted' | 'autonomous'} RunFront */
|
|
9
|
+
|
|
10
|
+
export const RUN_FRONTS = /** @type {const} */ (['handheld', 'assisted', 'autonomous']);
|
|
11
|
+
|
|
12
|
+
/** @type {Record<string, RunFront>} */
|
|
13
|
+
const FRONT_ALIASES = {
|
|
14
|
+
// Handheld — default /hs-next path (human in the loop, one task at a time).
|
|
15
|
+
handheld: 'handheld',
|
|
16
|
+
'hand-held': 'handheld',
|
|
17
|
+
// Assisted — dispatcher pauses at gates (human on the loop).
|
|
18
|
+
assisted: 'assisted',
|
|
19
|
+
assist: 'assisted',
|
|
20
|
+
// Autonomous — unattended dispatcher run (human over the loop).
|
|
21
|
+
autonomous: 'autonomous',
|
|
22
|
+
auto: 'autonomous',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Normalize a user-facing front token to a canonical `RunFront`.
|
|
27
|
+
* @param {string | undefined | null} token
|
|
28
|
+
* @returns {RunFront | null}
|
|
29
|
+
*/
|
|
30
|
+
export function normalizeRunFrontToken(token) {
|
|
31
|
+
if (token == null || token === '') return null;
|
|
32
|
+
const key = String(token).trim().toLowerCase();
|
|
33
|
+
return FRONT_ALIASES[key] ?? null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {string | undefined | null} token
|
|
38
|
+
* @returns {boolean}
|
|
39
|
+
*/
|
|
40
|
+
export function isRunFrontToken(token) {
|
|
41
|
+
return normalizeRunFrontToken(token) != null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** @returns {string} */
|
|
45
|
+
export function runFrontCliHint() {
|
|
46
|
+
return 'handheld (default) | assist | auto';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Whether `/hs-next` should skip the yes/proceed/confirm handshake and claim
|
|
51
|
+
* immediately. True for autonomous front (`/hs-next auto`, `/hs-next-auto`) and
|
|
52
|
+
* for explicit `auto_claim` (console launch injection / `--auto-claim` flag).
|
|
53
|
+
*
|
|
54
|
+
* @param {Record<string, unknown>} [args]
|
|
55
|
+
* @returns {boolean}
|
|
56
|
+
*/
|
|
57
|
+
export function shouldAutoClaimOnDeck(args = {}) {
|
|
58
|
+
if (args.auto_claim === true || args.auto_claim === 'true') return true;
|
|
59
|
+
return normalizeRunFrontToken(args.front) === 'autonomous';
|
|
60
|
+
}
|