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.
Files changed (104) hide show
  1. package/README.md +157 -0
  2. package/bin/hs.mjs +1028 -0
  3. package/bin/postinstall.mjs +88 -0
  4. package/dev-factory/lib/fleet-readiness.mjs +274 -0
  5. package/dev-factory/lib/fleet-state.mjs +250 -0
  6. package/dev-factory/lib/sufficiency-score.mjs +441 -0
  7. package/dist/cli/hs-check.mjs +747 -0
  8. package/dist/cli/hs-claim.mjs +541 -0
  9. package/dist/cli/hs-connect.mjs +3436 -0
  10. package/dist/cli/hs-defer.mjs +8455 -0
  11. package/dist/cli/hs-env.mjs +36845 -0
  12. package/dist/cli/hs-init.mjs +1145 -0
  13. package/dist/cli/hs-install.mjs +1390 -0
  14. package/dist/cli/hs-join.mjs +38553 -0
  15. package/dist/cli/hs-vault-pull.mjs +7782 -0
  16. package/dist/cli/hs-vault.mjs +4166 -0
  17. package/dist/cli/hs-workspace.mjs +2898 -0
  18. package/dist/cli-templates/customer-vault/specs/entity-model.md +1735 -0
  19. package/dist/cli-templates/customer-vault/specs/protocols/canonical-frontmatter-templates.md +531 -0
  20. package/dist/cli-templates/customer-vault/specs/protocols/memory-protocol.md +48 -0
  21. package/dist/cli-templates/customer-vault/specs/protocols/spec-hierarchy.md +548 -0
  22. package/docs/mcp-client-config.md +200 -0
  23. package/package.json +124 -0
  24. package/scripts/check-mcp-freshness.mjs +379 -0
  25. package/scripts/dev-server-status.mjs +722 -0
  26. package/scripts/gateway-skills-lib.mjs +421 -0
  27. package/scripts/lib/console-url-resolver.mjs +152 -0
  28. package/scripts/lib/dev-mode.mjs +369 -0
  29. package/scripts/lib/hs-link.mjs +154 -0
  30. package/scripts/lib/hs-source-commit.mjs +207 -0
  31. package/scripts/lib/hs-source-log.mjs +366 -0
  32. package/scripts/lib/hs-source-preflight.mjs +402 -0
  33. package/scripts/lib/hs-source-push.mjs +307 -0
  34. package/scripts/lib/hs-source-secret-scan.mjs +263 -0
  35. package/scripts/lib/standalone-doctor.mjs +89 -0
  36. package/scripts/vault-id-lib.mjs +173 -0
  37. package/vendor/heyspex-mcp/alignment-gate.mjs +1347 -0
  38. package/vendor/heyspex-mcp/blocker-resolver.mjs +891 -0
  39. package/vendor/heyspex-mcp/breakdown-ladder.mjs +309 -0
  40. package/vendor/heyspex-mcp/breakdown-source.mjs +236 -0
  41. package/vendor/heyspex-mcp/breakdown-writer.mjs +524 -0
  42. package/vendor/heyspex-mcp/brief-observability.mjs +67 -0
  43. package/vendor/heyspex-mcp/canvas-inbox.mjs +130 -0
  44. package/vendor/heyspex-mcp/capture-active-rehydrate.mjs +76 -0
  45. package/vendor/heyspex-mcp/capture-buffer-patches.mjs +147 -0
  46. package/vendor/heyspex-mcp/capture-channel.mjs +577 -0
  47. package/vendor/heyspex-mcp/capture-console-client.mjs +290 -0
  48. package/vendor/heyspex-mcp/capture-focus.mjs +1366 -0
  49. package/vendor/heyspex-mcp/capture-kind-suggest.mjs +121 -0
  50. package/vendor/heyspex-mcp/capture-outbox.mjs +239 -0
  51. package/vendor/heyspex-mcp/capture-stage-gate.mjs +160 -0
  52. package/vendor/heyspex-mcp/cli.mjs +15754 -0
  53. package/vendor/heyspex-mcp/config-settings.mjs +419 -0
  54. package/vendor/heyspex-mcp/console-fetch.mjs +342 -0
  55. package/vendor/heyspex-mcp/defer-spec.mjs +527 -0
  56. package/vendor/heyspex-mcp/deferred-reclaim.mjs +284 -0
  57. package/vendor/heyspex-mcp/design-tools.mjs +449 -0
  58. package/vendor/heyspex-mcp/dev-factory-bridge.mjs +66 -0
  59. package/vendor/heyspex-mcp/diagnose-skeleton.mjs +114 -0
  60. package/vendor/heyspex-mcp/fleet-queue-tools.mjs +241 -0
  61. package/vendor/heyspex-mcp/hooks-engine.mjs +399 -0
  62. package/vendor/heyspex-mcp/login-handoff.mjs +438 -0
  63. package/vendor/heyspex-mcp/mcp-subprocess.mjs +136 -0
  64. package/vendor/heyspex-mcp/mvp-mode.mjs +101 -0
  65. package/vendor/heyspex-mcp/next-resolution.mjs +240 -0
  66. package/vendor/heyspex-mcp/next-review.mjs +548 -0
  67. package/vendor/heyspex-mcp/peer-presence.mjs +430 -0
  68. package/vendor/heyspex-mcp/plan-ground-conflicts.mjs +321 -0
  69. package/vendor/heyspex-mcp/plan-ground.mjs +2001 -0
  70. package/vendor/heyspex-mcp/plan-ingest.mjs +476 -0
  71. package/vendor/heyspex-mcp/plan-push.mjs +1826 -0
  72. package/vendor/heyspex-mcp/plan-review.mjs +1419 -0
  73. package/vendor/heyspex-mcp/preflight-gate.mjs +0 -0
  74. package/vendor/heyspex-mcp/procedure-tools.mjs +970 -0
  75. package/vendor/heyspex-mcp/promote-capture.mjs +1280 -0
  76. package/vendor/heyspex-mcp/promote-diagnose.mjs +187 -0
  77. package/vendor/heyspex-mcp/promote-research-to-diagnose.mjs +338 -0
  78. package/vendor/heyspex-mcp/promote-research.mjs +1553 -0
  79. package/vendor/heyspex-mcp/promotion-helpers.mjs +280 -0
  80. package/vendor/heyspex-mcp/read-path.mjs +138 -0
  81. package/vendor/heyspex-mcp/recipe-tools.mjs +258 -0
  82. package/vendor/heyspex-mcp/regen-tasks.mjs +326 -0
  83. package/vendor/heyspex-mcp/register-spec.mjs +417 -0
  84. package/vendor/heyspex-mcp/research-focus.mjs +476 -0
  85. package/vendor/heyspex-mcp/research-tools.mjs +109 -0
  86. package/vendor/heyspex-mcp/research-type-suggest.mjs +112 -0
  87. package/vendor/heyspex-mcp/resolution-focus.mjs +45 -0
  88. package/vendor/heyspex-mcp/review-focus.mjs +55 -0
  89. package/vendor/heyspex-mcp/review-rollup.mjs +47 -0
  90. package/vendor/heyspex-mcp/run-front.mjs +60 -0
  91. package/vendor/heyspex-mcp/scheduler.mjs +813 -0
  92. package/vendor/heyspex-mcp/seat-identity.mjs +281 -0
  93. package/vendor/heyspex-mcp/settings-tools.mjs +119 -0
  94. package/vendor/heyspex-mcp/spec-id-lib.mjs +790 -0
  95. package/vendor/heyspex-mcp/split-research.mjs +832 -0
  96. package/vendor/heyspex-mcp/submit-for-review.mjs +190 -0
  97. package/vendor/heyspex-mcp/summary-contract.mjs +162 -0
  98. package/vendor/heyspex-mcp/test-helpers.mjs +43 -0
  99. package/vendor/heyspex-mcp/validate-linkage.mjs +574 -0
  100. package/vendor/heyspex-mcp/vault-resolve.mjs +297 -0
  101. package/vendor/heyspex-mcp/vault-settings.mjs +87 -0
  102. package/vendor/heyspex-mcp/verify-reproduction.mjs +314 -0
  103. package/vendor/heyspex-mcp/why-now.mjs +64 -0
  104. 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
+ }