@spekn/cli 1.0.1 → 1.0.3

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 (79) hide show
  1. package/dist/main.js +34856 -29545
  2. package/dist/prompts/governance-analysis.prompt.md +109 -0
  3. package/dist/resources/prompts/repo-analysis.prompt.md +28 -136
  4. package/dist/resources/prompts/repo-sync-analysis.prompt.md +31 -68
  5. package/dist/tui/app.d.ts +7 -0
  6. package/dist/tui/app.js +122 -0
  7. package/dist/tui/args.d.ts +8 -0
  8. package/dist/tui/args.js +57 -0
  9. package/dist/tui/capabilities/policy.d.ts +7 -0
  10. package/dist/tui/capabilities/policy.js +64 -0
  11. package/dist/tui/chunk-4WEASLXY.mjs +3444 -0
  12. package/dist/tui/chunk-755CADEG.mjs +3401 -0
  13. package/dist/tui/chunk-BUJQVTY5.mjs +3409 -0
  14. package/dist/tui/chunk-BZKKMGFB.mjs +1959 -0
  15. package/dist/tui/chunk-DJYOBCNM.mjs +3159 -0
  16. package/dist/tui/chunk-GTFTFDY4.mjs +3417 -0
  17. package/dist/tui/chunk-IMEBD2KA.mjs +3444 -0
  18. package/dist/tui/chunk-IX6DR5SW.mjs +3433 -0
  19. package/dist/tui/chunk-JKFOY4IF.mjs +2003 -0
  20. package/dist/tui/chunk-OXXZ3O5L.mjs +3378 -0
  21. package/dist/tui/chunk-SHJNIAAJ.mjs +1697 -0
  22. package/dist/tui/chunk-V4SNDRUS.mjs +1666 -0
  23. package/dist/tui/chunk-VXVHNZST.mjs +1666 -0
  24. package/dist/tui/chunk-WCTSFKTA.mjs +3459 -0
  25. package/dist/tui/chunk-X2XP5ACW.mjs +3443 -0
  26. package/dist/tui/chunk-YUYJ7VBG.mjs +2029 -0
  27. package/dist/tui/chunk-ZM3EI5IA.mjs +3384 -0
  28. package/dist/tui/chunk-ZYOX64HP.mjs +1653 -0
  29. package/dist/tui/components/frame.d.ts +8 -0
  30. package/dist/tui/components/frame.js +8 -0
  31. package/dist/tui/components/status-bar.d.ts +8 -0
  32. package/dist/tui/components/status-bar.js +8 -0
  33. package/dist/tui/index.d.ts +2 -0
  34. package/dist/tui/index.js +23 -0
  35. package/dist/tui/index.mjs +6999 -6938
  36. package/dist/tui/keymap/use-global-keymap.d.ts +19 -0
  37. package/dist/tui/keymap/use-global-keymap.js +82 -0
  38. package/dist/tui/navigation/nav-items.d.ts +3 -0
  39. package/dist/tui/navigation/nav-items.js +18 -0
  40. package/dist/tui/prompts/spec-creation-system.prompt.md +47 -0
  41. package/dist/tui/prompts/spec-refinement-system.prompt.md +81 -0
  42. package/dist/tui/screens/bridge.d.ts +8 -0
  43. package/dist/tui/screens/bridge.js +19 -0
  44. package/dist/tui/screens/decisions.d.ts +5 -0
  45. package/dist/tui/screens/decisions.js +28 -0
  46. package/dist/tui/screens/export.d.ts +5 -0
  47. package/dist/tui/screens/export.js +16 -0
  48. package/dist/tui/screens/home.d.ts +5 -0
  49. package/dist/tui/screens/home.js +33 -0
  50. package/dist/tui/screens/locked.d.ts +5 -0
  51. package/dist/tui/screens/locked.js +9 -0
  52. package/dist/tui/screens/specs.d.ts +5 -0
  53. package/dist/tui/screens/specs.js +31 -0
  54. package/dist/tui/services/client.d.ts +1 -0
  55. package/dist/tui/services/client.js +18 -0
  56. package/dist/tui/services/context-service.d.ts +19 -0
  57. package/dist/tui/services/context-service.js +246 -0
  58. package/dist/tui/shared-enums.d.ts +16 -0
  59. package/dist/tui/shared-enums.js +19 -0
  60. package/dist/tui/state/use-app-state.d.ts +35 -0
  61. package/dist/tui/state/use-app-state.js +177 -0
  62. package/dist/tui/types.d.ts +77 -0
  63. package/dist/tui/types.js +2 -0
  64. package/dist/tui/use-session-store-63YUGUFA.mjs +8 -0
  65. package/dist/tui/use-session-store-ACO2SMJC.mjs +8 -0
  66. package/dist/tui/use-session-store-BVFDAWOB.mjs +8 -0
  67. package/dist/tui/use-session-store-DJIZ3FQZ.mjs +9 -0
  68. package/dist/tui/use-session-store-EAIQA4UG.mjs +9 -0
  69. package/dist/tui/use-session-store-EFBAXC3G.mjs +8 -0
  70. package/dist/tui/use-session-store-FJOR4KTG.mjs +8 -0
  71. package/dist/tui/use-session-store-IJE5KVOC.mjs +8 -0
  72. package/dist/tui/use-session-store-KGAFXCKI.mjs +8 -0
  73. package/dist/tui/use-session-store-KS4DPNDY.mjs +8 -0
  74. package/dist/tui/use-session-store-MMHJENNL.mjs +8 -0
  75. package/dist/tui/use-session-store-OZ6HC4I2.mjs +9 -0
  76. package/dist/tui/use-session-store-PTMWISNJ.mjs +8 -0
  77. package/dist/tui/use-session-store-VCDECQMW.mjs +8 -0
  78. package/dist/tui/use-session-store-VOK5ML5J.mjs +9 -0
  79. package/package.json +6 -3
@@ -0,0 +1,19 @@
1
+ import type { NavItemPolicy, TuiScreenId } from '../types';
2
+ interface KeymapOptions {
3
+ screen: TuiScreenId;
4
+ navPolicy: NavItemPolicy[];
5
+ commandMode: boolean;
6
+ showHelp: boolean;
7
+ onNavigate: (screen: TuiScreenId) => void;
8
+ onHelpToggle: () => void;
9
+ onSearchToggle: () => void;
10
+ onSearchClear: () => void;
11
+ onCommandModeToggle: (value: boolean) => void;
12
+ onRefresh: () => void;
13
+ onExportPreview: () => void;
14
+ onExportGenerate: () => void;
15
+ onBridgeStart: () => void;
16
+ onBridgeStop: () => void;
17
+ }
18
+ export declare function useGlobalKeymap(options: KeymapOptions): void;
19
+ export {};
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useGlobalKeymap = useGlobalKeymap;
4
+ const ink_1 = require("ink");
5
+ const nav_items_1 = require("../navigation/nav-items");
6
+ function useGlobalKeymap(options) {
7
+ (0, ink_1.useInput)((input, key) => {
8
+ // In command mode, TextInput handles all keystrokes — only handle escape
9
+ if (options.commandMode) {
10
+ if (key.escape) {
11
+ options.onCommandModeToggle(false);
12
+ }
13
+ return;
14
+ }
15
+ if (key.ctrl && input === 'c') {
16
+ process.exit(0);
17
+ return;
18
+ }
19
+ if (input === '?') {
20
+ options.onHelpToggle();
21
+ return;
22
+ }
23
+ if (input === ':') {
24
+ options.onCommandModeToggle(true);
25
+ return;
26
+ }
27
+ if (input === '/') {
28
+ options.onSearchToggle();
29
+ return;
30
+ }
31
+ if (key.escape) {
32
+ options.onSearchClear();
33
+ return;
34
+ }
35
+ if (input === 'j' || key.downArrow) {
36
+ options.onNavigate((0, nav_items_1.nextScreen)(options.screen, options.navPolicy));
37
+ return;
38
+ }
39
+ if (input === 'k' || key.upArrow) {
40
+ options.onNavigate((0, nav_items_1.previousScreen)(options.screen, options.navPolicy));
41
+ return;
42
+ }
43
+ if (input === 'h' || key.leftArrow) {
44
+ options.onNavigate((0, nav_items_1.previousScreen)(options.screen, options.navPolicy));
45
+ return;
46
+ }
47
+ if (input === 'l' || key.rightArrow) {
48
+ options.onNavigate((0, nav_items_1.nextScreen)(options.screen, options.navPolicy));
49
+ return;
50
+ }
51
+ if (input === 'r') {
52
+ options.onRefresh();
53
+ return;
54
+ }
55
+ if (options.screen === 'export' && input === 'p') {
56
+ options.onExportPreview();
57
+ return;
58
+ }
59
+ if (options.screen === 'export' && input === 'g') {
60
+ options.onExportGenerate();
61
+ return;
62
+ }
63
+ if (options.screen === 'bridge' && input === 's') {
64
+ options.onBridgeStart();
65
+ return;
66
+ }
67
+ if (options.screen === 'bridge' && input === 'x') {
68
+ options.onBridgeStop();
69
+ return;
70
+ }
71
+ if (input === '1')
72
+ options.onNavigate('home');
73
+ if (input === '2')
74
+ options.onNavigate('specs');
75
+ if (input === '3')
76
+ options.onNavigate('export');
77
+ if (input === '4')
78
+ options.onNavigate('decisions');
79
+ if (input === '5')
80
+ options.onNavigate('bridge');
81
+ });
82
+ }
@@ -0,0 +1,3 @@
1
+ import type { NavItemPolicy, TuiScreenId } from '../types';
2
+ export declare function nextScreen(current: TuiScreenId, items: NavItemPolicy[]): TuiScreenId;
3
+ export declare function previousScreen(current: TuiScreenId, items: NavItemPolicy[]): TuiScreenId;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.nextScreen = nextScreen;
4
+ exports.previousScreen = previousScreen;
5
+ function nextScreen(current, items) {
6
+ const index = items.findIndex((item) => item.id === current);
7
+ if (index === -1 || items.length === 0)
8
+ return 'home';
9
+ const target = items[(index + 1) % items.length];
10
+ return target?.id ?? 'home';
11
+ }
12
+ function previousScreen(current, items) {
13
+ const index = items.findIndex((item) => item.id === current);
14
+ if (index === -1 || items.length === 0)
15
+ return 'home';
16
+ const target = items[(index - 1 + items.length) % items.length];
17
+ return target?.id ?? 'home';
18
+ }
@@ -0,0 +1,47 @@
1
+ You are a specification creation assistant for Spekn (Spec-Driven Development).
2
+
3
+ ## Communication Style
4
+
5
+ You are running inside a terminal UI (TUI). You do NOT have WebMCP or browser tools.
6
+ Communicate with the user through:
7
+ - **Interactive prompts** (the bridge will send interaction_request events for selections and questions)
8
+ - **Plain text responses** that stream to the terminal
9
+
10
+ Keep responses concise — the terminal has limited width.
11
+
12
+ ## Available Context
13
+
14
+ Read these MCP resources for spec state:
15
+ - `spec://content` — Current specification content
16
+ - `spec://frontmatter` — Parsed YAML frontmatter
17
+ - `spec://health` — Quick health check (has frontmatter, anchors, hints, relationships)
18
+ - `spec://quality` — Quality score, grade, and issues
19
+ - `spec://anchors` — Anchor paths in the spec
20
+
21
+ Call these MCP prompts for detailed guidance:
22
+ - `spec-creation-workflow` — Step-by-step creation workflow
23
+ - `spec-rules` — Frontmatter schema, body structure, quality scoring rules
24
+ - `available-actions` — Available tools and when to use them
25
+
26
+ ## High-Level Workflow
27
+
28
+ 1. Analyze the user's request (1-2 sentences)
29
+ 2. Confirm spec type (capability, architectural, workflow, operational, decision, intent)
30
+ 3. Confirm spec depth (memo, standard, full)
31
+ 4. Gather requirements — batch related questions (2-4 per interaction). Allow multiple selections where appropriate. For standard/full depth, ask about related specs (dependsOn, compatibleWith).
32
+ 5. Generate the specification with proper YAML frontmatter and markdown body. For **capability** specs, include a `capability` block with `module`, `feature`, and an `anchors` record matching body anchor headings (each with at least `status: "draft"`). **Populate relationship fields** (`dependsOn`, `compatibleWith`, `extends`, `conflictsWith`) based on user answers or inferred from context.
33
+ 6. Present the full specification content as your final response
34
+
35
+ Execute ALL workflow steps in a SINGLE turn when possible. After each interaction response, immediately proceed to the next step. The user should not need to type "continue".
36
+
37
+ The final specification MUST include:
38
+ - YAML frontmatter with title, type, status: draft, generation: 1, and relevant hints/relationships
39
+ - Structured markdown body with proper heading hierarchy and anchor paths (### #anchor.path)
40
+ - Acceptance criteria, constraints, and technical context sections as appropriate
41
+
42
+ Spec type hint: {{SPEC_TYPE}}
43
+ Spec depth hint: {{SPEC_DEPTH}}
44
+
45
+ ## User Request
46
+
47
+ {{USER_MESSAGE}}
@@ -0,0 +1,81 @@
1
+ You are a specification refinement assistant for Spekn (Spec-Driven Development).
2
+ Your goal is to help the user reach a quality score of 70+ (grade B or higher).
3
+
4
+ ## Communication Style
5
+
6
+ You are running inside a terminal UI (TUI). You do NOT have WebMCP or browser tools.
7
+ Communicate with the user through:
8
+ - **Interactive prompts** (the bridge will send interaction_request events for selections and questions)
9
+ - **Plain text responses** that stream to the terminal
10
+
11
+ Keep responses concise — the terminal has limited width.
12
+
13
+ ## Available Context
14
+
15
+ Read these MCP resources for spec state:
16
+ - `spec://content` — Current specification content (always read before proposing changes)
17
+ - `spec://frontmatter` — Parsed YAML frontmatter
18
+ - `spec://health` — Quick health check
19
+ - `spec://quality` — Quality score, grade, breakdown, and issues
20
+ - `spec://anchors` — Anchor paths in the spec
21
+
22
+ Call these MCP prompts for detailed guidance:
23
+ - `spec-refinement-workflow` — Step-by-step refinement workflow
24
+ - `spec-rules` — Frontmatter schema, body structure, quality scoring rules
25
+ - `available-actions` (flow=refinement) — Available tools and when to use them
26
+
27
+ ## Frontmatter Enrichment
28
+
29
+ If the spec's frontmatter is missing `hints` (constraints, requirements, technical, guidance) or `aiContext` (priorityAnchors), propose changes to populate them. Extract:
30
+ - `hints.constraints` from constraint sections and protected sections
31
+ - `hints.requirements` from acceptance criteria
32
+ - `hints.technical` from technical context sections
33
+ - `aiContext.priorityAnchors` from the most critical anchor paths (max 5)
34
+
35
+ ## Relationship Enrichment
36
+
37
+ If the spec's frontmatter is missing relationships (`dependsOn`, `extends`, `compatibleWith`, `conflictsWith`):
38
+ - Use `spekn_spec_list` or `spekn_spec_search` to find related specs in the same project
39
+ - Propose `dependsOn` for specs this one requires or references
40
+ - Propose `compatibleWith` for specs in the same domain/module (matching first anchor segment)
41
+ - Propose `conflictsWith` if overlapping or contradictory requirements exist
42
+ - Relationships are worth +4 points in the frontmatter scoring category
43
+
44
+ ## Capability Block Enrichment
45
+
46
+ For **capability** specs, check whether the `capability` frontmatter block is present and complete:
47
+
48
+ 1. **Missing `capability` block**: If the spec type is `capability` but has no `capability` block, propose one with:
49
+ - `module`: inferred from the first segment of anchor paths (e.g., `auth` from `auth.login.validation`)
50
+ - `feature`: inferred from the second segment (e.g., `login`)
51
+ - `anchors`: record mapping each body anchor path to `{ status: "draft" }`
52
+
53
+ 2. **Populate rich metadata**: For each anchor in `capability.anchors`, propose adding:
54
+ - `hint`: one-sentence summary of the anchor's purpose
55
+ - `complexity`: trivial | low | medium | high | critical
56
+ - `status`: draft | ready | implemented | tested
57
+
58
+ 3. **Anchor drift**: Detect mismatches between body anchor headings, `frontmatter.anchors`, and `capability.anchors` keys. Propose fixes to bring all three in sync.
59
+
60
+ ## Critical Rules
61
+
62
+ - Start by analyzing the current spec quality and presenting key issues
63
+ - Lead with critical issues (most score impact)
64
+ - Provide EXACT before/after diffs when proposing changes
65
+ - Do NOT re-propose fixes the user has skipped
66
+ - Present the updated specification as your final response when refinement is complete
67
+
68
+ ## Current Specification
69
+
70
+ Title: {{SPEC_TITLE}}
71
+ {{QUALITY_SCORE_LINE}}
72
+
73
+ ### Issues Found:
74
+ {{ISSUES_SUMMARY}}
75
+
76
+ ### Spec Content:
77
+ ```markdown
78
+ {{SPEC_CONTENT}}
79
+ ```
80
+
81
+ Begin by analyzing the specification and presenting the key quality issues to address.
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import type { TuiState } from '../state/use-app-state';
3
+ interface BridgeScreenProps {
4
+ state: TuiState;
5
+ onBridgeStop?: () => void;
6
+ }
7
+ export declare function BridgeScreen({ state, onBridgeStop }: BridgeScreenProps): React.JSX.Element;
8
+ export {};
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BridgeScreen = BridgeScreen;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const ink_1 = require("ink");
7
+ const ui_1 = require("@inkjs/ui");
8
+ function BridgeScreen({ state, onBridgeStop }) {
9
+ const [confirmingStop, setConfirmingStop] = (0, react_1.useState)(false);
10
+ if (state.loading) {
11
+ return (0, jsx_runtime_1.jsx)(ui_1.Spinner, { label: "Checking bridge health..." });
12
+ }
13
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "Local Bridge" }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Feature flag: ", state.bridge?.featureEnabled ? 'enabled' : 'disabled'] }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Cloud connected devices: ", state.bridge?.connectedDevices ?? 0] }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Cloud auth failures: ", state.bridge?.authFailures ?? 0] }), (0, jsx_runtime_1.jsxs)(ink_1.Box, { marginTop: 1, flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "Local Runtime" }), state.localBridge?.running ? ((0, jsx_runtime_1.jsxs)(ui_1.StatusMessage, { variant: "success", children: ["Running on port ", state.localBridge.port] })) : ((0, jsx_runtime_1.jsx)(ui_1.StatusMessage, { variant: "error", children: "Stopped" })), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Paired: ", state.localBridge?.paired ? 'yes' : 'no'] }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Uptime: ", state.localBridge?.uptimeSec ?? 0, "s"] })] }), (0, jsx_runtime_1.jsxs)(ink_1.Box, { marginTop: 1, flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "Actions" }), confirmingStop ? ((0, jsx_runtime_1.jsxs)(ink_1.Box, { gap: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { children: "Stop the bridge?" }), (0, jsx_runtime_1.jsx)(ui_1.ConfirmInput, { onConfirm: () => {
14
+ setConfirmingStop(false);
15
+ onBridgeStop?.();
16
+ }, onCancel: () => {
17
+ setConfirmingStop(false);
18
+ } })] })) : ((0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: "s=start (detached), x=stop signal, r=refresh" }))] }), (0, jsx_runtime_1.jsxs)(ink_1.Box, { marginTop: 1, flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "Registered Devices" }), (state.bridge?.devices ?? []).slice(0, 7).map((device) => ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["- ", device.name, " ", (0, jsx_runtime_1.jsx)(ui_1.Badge, { color: device.status === 'online' ? 'green' : device.status === 'error' ? 'red' : 'yellow', children: device.status }), " ", device.isDefault ? '(default)' : ''] }, device.id)))] })] }));
19
+ }
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import type { TuiState } from '../state/use-app-state';
3
+ export declare function DecisionsScreen({ state }: {
4
+ state: TuiState;
5
+ }): React.JSX.Element;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DecisionsScreen = DecisionsScreen;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ink_1 = require("ink");
6
+ const ui_1 = require("@inkjs/ui");
7
+ function statusBadgeColor(status) {
8
+ if (status === 'approved')
9
+ return 'green';
10
+ if (status === 'pending')
11
+ return 'yellow';
12
+ if (status === 'rejected')
13
+ return 'red';
14
+ return 'blue';
15
+ }
16
+ function filterItems(items, query) {
17
+ if (!query.trim())
18
+ return items;
19
+ const normalized = query.trim().toLowerCase();
20
+ return items.filter((item) => item.title.toLowerCase().includes(normalized));
21
+ }
22
+ function DecisionsScreen({ state }) {
23
+ if (state.loading) {
24
+ return (0, jsx_runtime_1.jsx)(ui_1.Spinner, { label: "Loading decisions..." });
25
+ }
26
+ const decisions = filterItems(state.decisions, state.searchQuery).slice(0, 7);
27
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "Decision Log" }), decisions.length === 0 ? ((0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: "No decisions in current view." })) : ((0, jsx_runtime_1.jsx)(ui_1.UnorderedList, { children: decisions.map((decision, index) => ((0, jsx_runtime_1.jsx)(ui_1.UnorderedList.Item, { children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: index === 0 ? 'green' : undefined, children: [decision.title, " ", (0, jsx_runtime_1.jsx)(ui_1.Badge, { color: statusBadgeColor(decision.status), children: decision.status }), " (", decision.decisionType, ") ", decision.specAnchor ? `@${decision.specAnchor}` : ''] }) }, decision.id))) })), (0, jsx_runtime_1.jsxs)(ink_1.Box, { marginTop: 1, flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "Role-aware Actions" }), state.boot?.role === 'viewer' ? ((0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", children: "Viewer role: read-only. Approvals disabled." })) : ((0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: "Use command palette: :approve DECISION_ID or :reject DECISION_ID" }))] })] }));
28
+ }
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import type { TuiState } from '../state/use-app-state';
3
+ export declare function ExportScreen({ state }: {
4
+ state: TuiState;
5
+ }): React.JSX.Element;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExportScreen = ExportScreen;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ink_1 = require("ink");
6
+ const ui_1 = require("@inkjs/ui");
7
+ function ExportScreen({ state }) {
8
+ if (state.loading) {
9
+ return (0, jsx_runtime_1.jsx)(ui_1.Spinner, { label: "Loading export..." });
10
+ }
11
+ const preview = state.exportPreview;
12
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "Context Export" }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Format: ", state.exportFormat] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: "Commands: p=preview, g=generate, :=run command palette" }), (0, jsx_runtime_1.jsxs)(ink_1.Box, { marginTop: 1, flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "Summary" }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Anchors: ", preview?.anchorCount ?? 0] }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Spec version: ", preview?.specVersion ?? 'n/a'] }), preview?.warnings?.length ? ((0, jsx_runtime_1.jsxs)(ui_1.StatusMessage, { variant: "warning", children: ["Warnings: ", preview.warnings.join('; ')] })) : ((0, jsx_runtime_1.jsx)(ink_1.Text, { children: "Warnings: none" }))] }), state.statusLine.includes('generated') || state.statusLine.includes('Export generated') ? ((0, jsx_runtime_1.jsx)(ink_1.Box, { marginTop: 1, children: (0, jsx_runtime_1.jsx)(ui_1.StatusMessage, { variant: "success", children: "Export generated successfully" }) })) : state.statusLine.includes('failed') ? ((0, jsx_runtime_1.jsx)(ink_1.Box, { marginTop: 1, children: (0, jsx_runtime_1.jsx)(ui_1.StatusMessage, { variant: "error", children: "Export generation failed" }) })) : null, (0, jsx_runtime_1.jsxs)(ink_1.Box, { marginTop: 1, flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "Preview (first 12 lines)" }), preview ? (preview.content
13
+ .split('\n')
14
+ .slice(0, 12)
15
+ .map((line, index) => ((0, jsx_runtime_1.jsx)(ink_1.Text, { children: line }, `preview-${index}`)))) : ((0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: "No preview yet." }))] })] }));
16
+ }
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import type { TuiState } from '../state/use-app-state';
3
+ export declare function HomeScreen({ state }: {
4
+ state: TuiState;
5
+ }): React.JSX.Element;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HomeScreen = HomeScreen;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ink_1 = require("ink");
6
+ const ui_1 = require("@inkjs/ui");
7
+ function nextBestAction(state) {
8
+ if (state.error)
9
+ return 'Fix authentication/context errors first';
10
+ if (!state.exportPreview)
11
+ return 'Generate context export (press 3, then g)';
12
+ if (state.workflow.blockedCount > 0)
13
+ return 'Review blocked workflow states';
14
+ if (!state.workflow.hasPlanningArtifacts)
15
+ return 'Create planning artifacts in PLAN phase';
16
+ if (!state.workflow.hasVerificationEvidence)
17
+ return 'Capture verification evidence';
18
+ return 'Review Decision Log and continue implementation';
19
+ }
20
+ function phaseVariant(state) {
21
+ if (state.workflow.blockedCount > 0)
22
+ return 'warning';
23
+ if (state.workflow.currentPhase)
24
+ return 'info';
25
+ return 'success';
26
+ }
27
+ function HomeScreen({ state }) {
28
+ if (state.loading) {
29
+ return (0, jsx_runtime_1.jsx)(ui_1.Spinner, { label: "Loading dashboard..." });
30
+ }
31
+ const action = nextBestAction(state);
32
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "green", children: "Next Best Action" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: action }), (0, jsx_runtime_1.jsxs)(ink_1.Box, { marginTop: 1, flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "Workflow" }), (0, jsx_runtime_1.jsxs)(ui_1.StatusMessage, { variant: phaseVariant(state), children: ["Phase: ", state.workflow.currentPhase ?? 'n/a', " | Blocked: ", state.workflow.blockedCount] })] }), (0, jsx_runtime_1.jsxs)(ink_1.Box, { marginTop: 1, flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "Unfinished Work" }), (0, jsx_runtime_1.jsxs)(ui_1.UnorderedList, { children: [(0, jsx_runtime_1.jsx)(ui_1.UnorderedList.Item, { children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Specs: ", state.specs.length] }) }), (0, jsx_runtime_1.jsx)(ui_1.UnorderedList.Item, { children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Decisions: ", state.decisions.length] }) }), (0, jsx_runtime_1.jsx)(ui_1.UnorderedList.Item, { children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Export ready: ", state.exportPreview ? 'yes' : 'no'] }) })] })] }), (0, jsx_runtime_1.jsx)(ink_1.Box, { marginTop: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: "Quick actions: [2] Specs [3] Export [4] Decisions [5] Bridge" }) })] }));
33
+ }
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import type { NavItemPolicy } from '../types';
3
+ export declare function LockedScreen({ item }: {
4
+ item?: NavItemPolicy;
5
+ }): React.JSX.Element;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LockedScreen = LockedScreen;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ink_1 = require("ink");
6
+ const ui_1 = require("@inkjs/ui");
7
+ function LockedScreen({ item }) {
8
+ return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", children: (0, jsx_runtime_1.jsxs)(ui_1.Alert, { variant: "warning", children: [item?.label ?? 'Feature', " is locked \u2014 ", item?.reason ?? 'Upgrade required', ". ", item?.description ?? 'Higher-tier feature preview', ". This feature is visible for discoverability but unavailable on your current plan."] }) }));
9
+ }
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import type { TuiState } from '../state/use-app-state';
3
+ export declare function SpecsScreen({ state }: {
4
+ state: TuiState;
5
+ }): React.JSX.Element;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SpecsScreen = SpecsScreen;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ink_1 = require("ink");
6
+ const ui_1 = require("@inkjs/ui");
7
+ function statusBadgeColor(status) {
8
+ if (status === 'locked')
9
+ return 'green';
10
+ if (status === 'draft')
11
+ return 'yellow';
12
+ if (status === 'review')
13
+ return 'blue';
14
+ if (status === 'archived')
15
+ return 'red';
16
+ return 'yellow';
17
+ }
18
+ function filterItems(items, query) {
19
+ if (!query.trim())
20
+ return items;
21
+ const normalized = query.trim().toLowerCase();
22
+ return items.filter((item) => item.title.toLowerCase().includes(normalized));
23
+ }
24
+ function SpecsScreen({ state }) {
25
+ if (state.loading) {
26
+ return (0, jsx_runtime_1.jsx)(ui_1.Spinner, { label: "Loading specifications..." });
27
+ }
28
+ const specs = filterItems(state.specs, state.searchQuery).slice(0, 7);
29
+ const selected = specs[0];
30
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsxs)(ink_1.Text, { bold: true, children: ["Specifications ", state.searchQuery ? `(filter: ${state.searchQuery})` : ''] }), specs.length === 0 ? ((0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: "No specs in current view." })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ui_1.UnorderedList, { children: specs.map((spec, index) => ((0, jsx_runtime_1.jsx)(ui_1.UnorderedList.Item, { children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: index === 0 ? 'green' : undefined, children: [spec.title, " ", (0, jsx_runtime_1.jsx)(ui_1.Badge, { color: statusBadgeColor(spec.status), children: spec.status }), " v", spec.version] }) }, spec.id))) }), state.specs.length > 7 ? (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: "...showing first 7 specs (Miller's law limit)" }) : null] })), (0, jsx_runtime_1.jsxs)(ink_1.Box, { marginTop: 1, flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "Anchor Detail" }), selected ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["ID: ", selected.id] }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Status: ", selected.status] }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Type: ", selected.type ?? 'n/a'] }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Updated: ", selected.updatedAt ?? 'n/a'] })] })) : ((0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: "Select a specification to view details." }))] })] }));
31
+ }
@@ -0,0 +1 @@
1
+ export declare function createApiClient(apiUrl: string, token: string, organizationId: string): any;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createApiClient = createApiClient;
4
+ const client_1 = require("@trpc/client");
5
+ const trpc_url_1 = require("../../utils/trpc-url");
6
+ function createApiClient(apiUrl, token, organizationId) {
7
+ return (0, client_1.createTRPCProxyClient)({
8
+ links: [
9
+ (0, client_1.httpBatchLink)({
10
+ url: (0, trpc_url_1.normalizeTrpcUrl)(apiUrl),
11
+ headers: {
12
+ authorization: token ? `Bearer ${token}` : '',
13
+ 'x-organization-id': organizationId,
14
+ },
15
+ }),
16
+ ],
17
+ });
18
+ }
@@ -0,0 +1,19 @@
1
+ import type { BridgeSummary, DecisionListItem, ExportFormat, ExportPreview, LocalBridgeSummary, SpecListItem, TuiBootContext, WorkflowSummary } from '../types';
2
+ export declare class TuiContextService {
3
+ private readonly apiUrl;
4
+ private readonly credentialsStore;
5
+ constructor(apiUrl: string);
6
+ bootstrap(projectIdArg?: string): Promise<{
7
+ boot: TuiBootContext;
8
+ client: any;
9
+ }>;
10
+ loadSpecs(client: any, projectId: string): Promise<SpecListItem[]>;
11
+ loadDecisions(client: any, projectId: string): Promise<DecisionListItem[]>;
12
+ loadWorkflowSummary(client: any, projectId: string): Promise<WorkflowSummary>;
13
+ previewExport(client: any, projectId: string, format: ExportFormat): Promise<ExportPreview>;
14
+ generateExport(client: any, projectId: string, format: ExportFormat): Promise<ExportPreview>;
15
+ loadBridgeSummary(client: any): Promise<BridgeSummary>;
16
+ loadLocalBridgeSummary(): Promise<LocalBridgeSummary>;
17
+ startLocalBridgeDetached(): void;
18
+ stopLocalBridge(configPort?: number): Promise<void>;
19
+ }