@entelligentsia/forgecli 0.8.4 → 0.9.1

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 (170) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +165 -2
  3. package/dist/bin/argv.d.ts +2 -2
  4. package/dist/bin/argv.js +17 -0
  5. package/dist/bin/argv.js.map +1 -1
  6. package/dist/bin/config.d.ts +69 -0
  7. package/dist/bin/config.js +315 -0
  8. package/dist/bin/config.js.map +1 -0
  9. package/dist/bin/doctor.d.ts +1 -0
  10. package/dist/bin/doctor.js +12 -0
  11. package/dist/bin/doctor.js.map +1 -1
  12. package/dist/bin/forge.js +7 -0
  13. package/dist/bin/forge.js.map +1 -1
  14. package/dist/extensions/forgecli/config-command.d.ts +8 -0
  15. package/dist/extensions/forgecli/config-command.js +66 -0
  16. package/dist/extensions/forgecli/config-command.js.map +1 -0
  17. package/dist/extensions/forgecli/config-layer.d.ts +38 -0
  18. package/dist/extensions/forgecli/config-layer.js +68 -0
  19. package/dist/extensions/forgecli/config-layer.js.map +1 -0
  20. package/dist/extensions/forgecli/config-tui/component.d.ts +35 -0
  21. package/dist/extensions/forgecli/config-tui/component.js +236 -0
  22. package/dist/extensions/forgecli/config-tui/component.js.map +1 -0
  23. package/dist/extensions/forgecli/config-tui/handler.d.ts +40 -0
  24. package/dist/extensions/forgecli/config-tui/handler.js +240 -0
  25. package/dist/extensions/forgecli/config-tui/handler.js.map +1 -0
  26. package/dist/extensions/forgecli/config-tui/index.d.ts +5 -0
  27. package/dist/extensions/forgecli/config-tui/index.js +5 -0
  28. package/dist/extensions/forgecli/config-tui/index.js.map +1 -0
  29. package/dist/extensions/forgecli/config-tui/keys.d.ts +26 -0
  30. package/dist/extensions/forgecli/config-tui/keys.js +33 -0
  31. package/dist/extensions/forgecli/config-tui/keys.js.map +1 -0
  32. package/dist/extensions/forgecli/config-tui/plugin-config-reader.d.ts +23 -0
  33. package/dist/extensions/forgecli/config-tui/plugin-config-reader.js +58 -0
  34. package/dist/extensions/forgecli/config-tui/plugin-config-reader.js.map +1 -0
  35. package/dist/extensions/forgecli/config-tui/screens/advanced-menu.d.ts +7 -0
  36. package/dist/extensions/forgecli/config-tui/screens/advanced-menu.js +83 -0
  37. package/dist/extensions/forgecli/config-tui/screens/advanced-menu.js.map +1 -0
  38. package/dist/extensions/forgecli/config-tui/screens/confirm-quit.d.ts +11 -0
  39. package/dist/extensions/forgecli/config-tui/screens/confirm-quit.js +54 -0
  40. package/dist/extensions/forgecli/config-tui/screens/confirm-quit.js.map +1 -0
  41. package/dist/extensions/forgecli/config-tui/screens/override-editor.d.ts +11 -0
  42. package/dist/extensions/forgecli/config-tui/screens/override-editor.js +233 -0
  43. package/dist/extensions/forgecli/config-tui/screens/override-editor.js.map +1 -0
  44. package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.d.ts +7 -0
  45. package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.js +91 -0
  46. package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.js.map +1 -0
  47. package/dist/extensions/forgecli/config-tui/screens/overrides-list.d.ts +7 -0
  48. package/dist/extensions/forgecli/config-tui/screens/overrides-list.js +71 -0
  49. package/dist/extensions/forgecli/config-tui/screens/overrides-list.js.map +1 -0
  50. package/dist/extensions/forgecli/config-tui/screens/persona-editor.d.ts +10 -0
  51. package/dist/extensions/forgecli/config-tui/screens/persona-editor.js +182 -0
  52. package/dist/extensions/forgecli/config-tui/screens/persona-editor.js.map +1 -0
  53. package/dist/extensions/forgecli/config-tui/screens/persona-picker.d.ts +7 -0
  54. package/dist/extensions/forgecli/config-tui/screens/persona-picker.js +76 -0
  55. package/dist/extensions/forgecli/config-tui/screens/persona-picker.js.map +1 -0
  56. package/dist/extensions/forgecli/config-tui/screens/personas-list.d.ts +7 -0
  57. package/dist/extensions/forgecli/config-tui/screens/personas-list.js +98 -0
  58. package/dist/extensions/forgecli/config-tui/screens/personas-list.js.map +1 -0
  59. package/dist/extensions/forgecli/config-tui/screens/shared.d.ts +29 -0
  60. package/dist/extensions/forgecli/config-tui/screens/shared.js +100 -0
  61. package/dist/extensions/forgecli/config-tui/screens/shared.js.map +1 -0
  62. package/dist/extensions/forgecli/config-tui/screens/show-resolved.d.ts +23 -0
  63. package/dist/extensions/forgecli/config-tui/screens/show-resolved.js +128 -0
  64. package/dist/extensions/forgecli/config-tui/screens/show-resolved.js.map +1 -0
  65. package/dist/extensions/forgecli/config-tui/screens/tier-menu.d.ts +7 -0
  66. package/dist/extensions/forgecli/config-tui/screens/tier-menu.js +135 -0
  67. package/dist/extensions/forgecli/config-tui/screens/tier-menu.js.map +1 -0
  68. package/dist/extensions/forgecli/config-tui/screens/tier-picker.d.ts +9 -0
  69. package/dist/extensions/forgecli/config-tui/screens/tier-picker.js +122 -0
  70. package/dist/extensions/forgecli/config-tui/screens/tier-picker.js.map +1 -0
  71. package/dist/extensions/forgecli/config-tui/screens/types.d.ts +24 -0
  72. package/dist/extensions/forgecli/config-tui/screens/types.js +5 -0
  73. package/dist/extensions/forgecli/config-tui/screens/types.js.map +1 -0
  74. package/dist/extensions/forgecli/config-tui/screens.d.ts +24 -0
  75. package/dist/extensions/forgecli/config-tui/screens.js +78 -0
  76. package/dist/extensions/forgecli/config-tui/screens.js.map +1 -0
  77. package/dist/extensions/forgecli/config-tui/state/buffer.d.ts +11 -0
  78. package/dist/extensions/forgecli/config-tui/state/buffer.js +91 -0
  79. package/dist/extensions/forgecli/config-tui/state/buffer.js.map +1 -0
  80. package/dist/extensions/forgecli/config-tui/state/constants.d.ts +4 -0
  81. package/dist/extensions/forgecli/config-tui/state/constants.js +14 -0
  82. package/dist/extensions/forgecli/config-tui/state/constants.js.map +1 -0
  83. package/dist/extensions/forgecli/config-tui/state/index.d.ts +6 -0
  84. package/dist/extensions/forgecli/config-tui/state/index.js +9 -0
  85. package/dist/extensions/forgecli/config-tui/state/index.js.map +1 -0
  86. package/dist/extensions/forgecli/config-tui/state/init.d.ts +2 -0
  87. package/dist/extensions/forgecli/config-tui/state/init.js +30 -0
  88. package/dist/extensions/forgecli/config-tui/state/init.js.map +1 -0
  89. package/dist/extensions/forgecli/config-tui/state/model.d.ts +192 -0
  90. package/dist/extensions/forgecli/config-tui/state/model.js +4 -0
  91. package/dist/extensions/forgecli/config-tui/state/model.js.map +1 -0
  92. package/dist/extensions/forgecli/config-tui/state/reducer.d.ts +2 -0
  93. package/dist/extensions/forgecli/config-tui/state/reducer.js +212 -0
  94. package/dist/extensions/forgecli/config-tui/state/reducer.js.map +1 -0
  95. package/dist/extensions/forgecli/config-tui/state/selectors.d.ts +91 -0
  96. package/dist/extensions/forgecli/config-tui/state/selectors.js +231 -0
  97. package/dist/extensions/forgecli/config-tui/state/selectors.js.map +1 -0
  98. package/dist/extensions/forgecli/config-tui/state.d.ts +6 -0
  99. package/dist/extensions/forgecli/config-tui/state.js +11 -0
  100. package/dist/extensions/forgecli/config-tui/state.js.map +1 -0
  101. package/dist/extensions/forgecli/config-tui/theme.d.ts +37 -0
  102. package/dist/extensions/forgecli/config-tui/theme.js +88 -0
  103. package/dist/extensions/forgecli/config-tui/theme.js.map +1 -0
  104. package/dist/extensions/forgecli/config-tui/tier-meta.d.ts +28 -0
  105. package/dist/extensions/forgecli/config-tui/tier-meta.js +69 -0
  106. package/dist/extensions/forgecli/config-tui/tier-meta.js.map +1 -0
  107. package/dist/extensions/forgecli/config-writer.d.ts +16 -0
  108. package/dist/extensions/forgecli/config-writer.js +63 -0
  109. package/dist/extensions/forgecli/config-writer.js.map +1 -0
  110. package/dist/extensions/forgecli/fix-bug.js +85 -1
  111. package/dist/extensions/forgecli/fix-bug.js.map +1 -1
  112. package/dist/extensions/forgecli/forge-cli-schema.json +54 -0
  113. package/dist/extensions/forgecli/forge-commands.js +3 -8
  114. package/dist/extensions/forgecli/forge-commands.js.map +1 -1
  115. package/dist/extensions/forgecli/forge-subagent.d.ts +13 -0
  116. package/dist/extensions/forgecli/forge-subagent.js +19 -0
  117. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  118. package/dist/extensions/forgecli/index.js +16 -0
  119. package/dist/extensions/forgecli/index.js.map +1 -1
  120. package/dist/extensions/forgecli/input-router.d.ts +33 -0
  121. package/dist/extensions/forgecli/input-router.js +133 -0
  122. package/dist/extensions/forgecli/input-router.js.map +1 -0
  123. package/dist/extensions/forgecli/model-resolver.d.ts +32 -0
  124. package/dist/extensions/forgecli/model-resolver.js +65 -0
  125. package/dist/extensions/forgecli/model-resolver.js.map +1 -0
  126. package/dist/extensions/forgecli/model-validator.d.ts +29 -0
  127. package/dist/extensions/forgecli/model-validator.js +107 -0
  128. package/dist/extensions/forgecli/model-validator.js.map +1 -0
  129. package/dist/extensions/forgecli/run-sprint.js +59 -0
  130. package/dist/extensions/forgecli/run-sprint.js.map +1 -1
  131. package/dist/extensions/forgecli/run-task.js +93 -1
  132. package/dist/extensions/forgecli/run-task.js.map +1 -1
  133. package/dist/extensions/forgecli/thread-switcher.js +5 -2
  134. package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
  135. package/dist/extensions/forgecli/whats-new-widget.js +5 -2
  136. package/dist/extensions/forgecli/whats-new-widget.js.map +1 -1
  137. package/package.json +11 -3
  138. package/dist/extensions/forgecli/review-command.d.ts +0 -2
  139. package/dist/extensions/forgecli/review-command.js +0 -184
  140. package/dist/extensions/forgecli/review-command.js.map +0 -1
  141. package/dist/forge-payload/.tools/banners.cjs +0 -435
  142. package/dist/forge-payload/.tools/build-context-pack.cjs +0 -290
  143. package/dist/forge-payload/.tools/build-init-context.cjs +0 -322
  144. package/dist/forge-payload/.tools/build-overlay.cjs +0 -326
  145. package/dist/forge-payload/.tools/build-persona-pack.cjs +0 -226
  146. package/dist/forge-payload/.tools/collate.cjs +0 -1041
  147. package/dist/forge-payload/.tools/generation-manifest.cjs +0 -311
  148. package/dist/forge-payload/.tools/lib/forge-root.cjs +0 -59
  149. package/dist/forge-payload/.tools/lib/paths.cjs +0 -29
  150. package/dist/forge-payload/.tools/lib/pricing.cjs +0 -165
  151. package/dist/forge-payload/.tools/lib/project-root.cjs +0 -32
  152. package/dist/forge-payload/.tools/lib/result.js +0 -40
  153. package/dist/forge-payload/.tools/lib/store-facade.cjs +0 -162
  154. package/dist/forge-payload/.tools/lib/store-nlp.cjs +0 -250
  155. package/dist/forge-payload/.tools/lib/store-query-exec.cjs +0 -272
  156. package/dist/forge-payload/.tools/lib/validate.js +0 -141
  157. package/dist/forge-payload/.tools/manage-config.cjs +0 -340
  158. package/dist/forge-payload/.tools/manage-versions.cjs +0 -365
  159. package/dist/forge-payload/.tools/package.json +0 -3
  160. package/dist/forge-payload/.tools/parse-gates.cjs +0 -151
  161. package/dist/forge-payload/.tools/parse-verdict.cjs +0 -67
  162. package/dist/forge-payload/.tools/preflight-gate.cjs +0 -350
  163. package/dist/forge-payload/.tools/prompts/sprint-plan-prompt.md +0 -70
  164. package/dist/forge-payload/.tools/schemas/task-list.schema.json +0 -53
  165. package/dist/forge-payload/.tools/seed-store.cjs +0 -237
  166. package/dist/forge-payload/.tools/store-cli.cjs +0 -1226
  167. package/dist/forge-payload/.tools/store-query.cjs +0 -319
  168. package/dist/forge-payload/.tools/store.cjs +0 -315
  169. package/dist/forge-payload/.tools/substitute-placeholders.cjs +0 -625
  170. package/dist/forge-payload/.tools/validate-store.cjs +0 -593
@@ -0,0 +1,29 @@
1
+ import type { ConfigTuiState } from "../state/model.js";
2
+ import type { Theme } from "@earendil-works/pi-coding-agent";
3
+ /** Themed horizontal rule spanning the full width. */
4
+ export declare function rule(width: number, theme: Theme): string;
5
+ /**
6
+ * Compute a windowed slice of `items` around `cursor` so that long lists fit
7
+ * inside `maxRows`. Returns the visible items, their absolute start index,
8
+ * and scroll indicator counts. Default window: 10 rows.
9
+ */
10
+ export declare function windowList<T>(items: T[], cursor: number, maxRows?: number): {
11
+ visible: T[];
12
+ start: number;
13
+ aboveCount: number;
14
+ belowCount: number;
15
+ };
16
+ export declare function authBadgeFor(state: ConfigTuiState, provider: string, theme: Theme): string;
17
+ export declare function authStatusLine(state: ConfigTuiState, theme: Theme): string;
18
+ export declare function resolvedSummary(state: ConfigTuiState, theme: Theme): string;
19
+ export declare function formatOverride(override: string | {
20
+ provider: string;
21
+ model: string;
22
+ } | undefined): string;
23
+ /** Render the four-rule cascade footer in plain English.
24
+ * This is the "How models get picked" section from Screen 3. */
25
+ export declare function cascadeFooter(width: number, theme: Theme): string[];
26
+ /** Render a tier badge like "Heavy", "Standard", "Light" in themed color. */
27
+ export declare function tierBadge(tier: "heavy" | "standard" | "light", theme: Theme): string;
28
+ /** Width-safe final guard: truncate every line to the given terminal width. */
29
+ export declare function safeLines(lines: string[], width: number): string[];
@@ -0,0 +1,100 @@
1
+ // Shared screen rendering utilities — windowList, formatOverride, and themed helpers.
2
+ // Split from screens.ts (Phase 1). Phase 3: theming applied to all visible strings.
3
+ import { authBadge, muted, rule as themedRule, error as themedError, truncateLines } from "../theme.js";
4
+ /** Themed horizontal rule spanning the full width. */
5
+ export function rule(width, theme) {
6
+ return themedRule(width, theme);
7
+ }
8
+ /**
9
+ * Compute a windowed slice of `items` around `cursor` so that long lists fit
10
+ * inside `maxRows`. Returns the visible items, their absolute start index,
11
+ * and scroll indicator counts. Default window: 10 rows.
12
+ */
13
+ export function windowList(items, cursor, maxRows = 10) {
14
+ if (items.length <= maxRows) {
15
+ return { visible: items, start: 0, aboveCount: 0, belowCount: 0 };
16
+ }
17
+ // Keep cursor in the middle when possible.
18
+ const half = Math.floor(maxRows / 2);
19
+ let start = Math.max(0, cursor - half);
20
+ if (start + maxRows > items.length)
21
+ start = items.length - maxRows;
22
+ return {
23
+ visible: items.slice(start, start + maxRows),
24
+ start,
25
+ aboveCount: start,
26
+ belowCount: items.length - start - maxRows,
27
+ };
28
+ }
29
+ export function authBadgeFor(state, provider, theme) {
30
+ return authBadge(state.authenticatedProviders.includes(provider), theme);
31
+ }
32
+ export function authStatusLine(state, theme) {
33
+ // If auth discovery failed entirely, surface the error.
34
+ if (state.authError) {
35
+ return themedError(` Auth error ${state.authError}`, theme);
36
+ }
37
+ // Stable ordering: alphabetical by provider name.
38
+ const providers = [...new Set([
39
+ ...state.authenticatedProviders,
40
+ ...state.availableModels.map((m) => m.provider),
41
+ ])].sort();
42
+ if (providers.length === 0)
43
+ return muted(" Auth status (no providers detected)", theme);
44
+ const cells = providers.map((p) => `${p} ${authBadgeFor(state, p, theme)}`);
45
+ return muted(` Auth status ${cells.join(" ")}`, theme);
46
+ }
47
+ export function resolvedSummary(state, theme) {
48
+ const personas = listResolvedPersonasInner(state);
49
+ if (personas.length === 0) {
50
+ return muted(" Resolved all personas inherit pi current model", theme);
51
+ }
52
+ const routable = personas.filter((p) => state.availableModels.some((m) => m.provider === p.provider && m.id === p.model)).length;
53
+ const total = personas.length;
54
+ return muted(` Resolved ${total} persona-model assignment${total === 1 ? "" : "s"} · ${routable} routable`, theme);
55
+ }
56
+ function listResolvedPersonasInner(state) {
57
+ const out = new Map();
58
+ const projectMap = state.buffer.project["persona-models"] ?? {};
59
+ const globalMap = state.buffer.global["persona-models"] ?? {};
60
+ for (const [persona, entry] of Object.entries(globalMap)) {
61
+ out.set(persona, { persona, provider: entry.provider, model: entry.model, source: "L1" });
62
+ }
63
+ for (const [persona, entry] of Object.entries(projectMap)) {
64
+ out.set(persona, { persona, provider: entry.provider, model: entry.model, source: "L2" });
65
+ }
66
+ return [...out.values()].sort((a, b) => a.persona.localeCompare(b.persona));
67
+ }
68
+ export function formatOverride(override) {
69
+ if (override === undefined)
70
+ return "(none)";
71
+ if (typeof override === "string")
72
+ return `"${override}"`;
73
+ return `{${override.provider}:${override.model}}`;
74
+ }
75
+ /** Render the four-rule cascade footer in plain English.
76
+ * This is the "How models get picked" section from Screen 3. */
77
+ export function cascadeFooter(width, theme) {
78
+ const m = (text) => theme.fg("muted", text);
79
+ const a = (text) => theme.fg("accent", text);
80
+ return [
81
+ "",
82
+ a("How models get picked"),
83
+ theme.fg("border", "─".repeat(Math.max(1, width))),
84
+ m("Each step's persona looks for an override at four levels (most specific wins):"),
85
+ m(" 1. A model set just for this step ") + a("(Step override)"),
86
+ m(" 2. A model set just for this persona ") + a("(Per-persona override)"),
87
+ m(" 3. The tier baseline you set above ") + a("(Heavy / Standard / Light)"),
88
+ m(" 4. Whatever model pi is currently running on ") + m("(falls back automatically)"),
89
+ ];
90
+ }
91
+ /** Render a tier badge like "Heavy", "Standard", "Light" in themed color. */
92
+ export function tierBadge(tier, theme) {
93
+ const labels = { heavy: "Heavy", standard: "Standard", light: "Light" };
94
+ return theme.fg("accent", labels[tier] ?? tier);
95
+ }
96
+ /** Width-safe final guard: truncate every line to the given terminal width. */
97
+ export function safeLines(lines, width) {
98
+ return truncateLines(lines, width);
99
+ }
100
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../../../src/extensions/forgecli/config-tui/screens/shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { ConfigTuiState } from "../state/model.js";
2
+ import type { Theme } from "@earendil-works/pi-coding-agent";
3
+ import { InputResult, Screen } from "./types.js";
4
+ export interface ResolvedRow {
5
+ index: number;
6
+ role: string;
7
+ persona: string;
8
+ resolved: string;
9
+ source: string;
10
+ available: boolean;
11
+ }
12
+ /**
13
+ * Legacy compute helper — kept for backward compatibility in barrel re-export.
14
+ * Prefer getPhaseTable() for new code.
15
+ */
16
+ export declare function computeResolvedRows(state: ConfigTuiState): Array<{
17
+ pipeline: string;
18
+ rows: ResolvedRow[];
19
+ }>;
20
+ export declare class ShowResolvedScreen implements Screen {
21
+ render(state: ConfigTuiState, width: number, theme: Theme): string[];
22
+ handleInput(data: string, state: ConfigTuiState): InputResult;
23
+ }
@@ -0,0 +1,128 @@
1
+ // Show-resolved screen — "Show what runs at each step" (Screen 3).
2
+ // Phase C: rewritten to the phase-table format with emoji + persona names +
3
+ // plain-English tier-source labels + cascade footer.
4
+ // No more L1/L2 badges, no more "cascade" jargon.
5
+ import { Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
6
+ import { getActiveView, getPhaseTable } from "../state/selectors.js";
7
+ import { PHASE_PERSONA_TABLE } from "../tier-meta.js";
8
+ import { resolveModelForPhase } from "../../model-resolver.js";
9
+ import { rule, windowList, safeLines, cascadeFooter } from "./shared.js";
10
+ import { padRight, accentBold, muted, cursor } from "../theme.js";
11
+ /**
12
+ * Legacy compute helper — kept for backward compatibility in barrel re-export.
13
+ * Prefer getPhaseTable() for new code.
14
+ */
15
+ export function computeResolvedRows(state) {
16
+ const pipelineNames = state.pipelineCatalogue
17
+ ? state.pipelineCatalogue
18
+ : ["default"];
19
+ return pipelineNames.map((pipeline) => ({
20
+ pipeline,
21
+ rows: PHASE_PERSONA_TABLE.map((phase, i) => {
22
+ const result = resolveModelForPhase(pipeline, phase.role, phase.personaNoun, {
23
+ ...state.buffer.global,
24
+ ...state.buffer.project,
25
+ _global: state.buffer.global,
26
+ _project: state.buffer.project,
27
+ });
28
+ const resolved = result.model
29
+ ? `${result.model.provider}:${result.model.model}`
30
+ : "(inherit pi current)";
31
+ const available = result.model
32
+ ? state.availableModels.some((m) => m.provider === result.model.provider && m.id === result.model.model)
33
+ : true;
34
+ return {
35
+ index: i,
36
+ role: phase.role,
37
+ persona: phase.personaNoun,
38
+ resolved,
39
+ source: result.source,
40
+ available,
41
+ };
42
+ }),
43
+ }));
44
+ }
45
+ /**
46
+ * Build a single table row with ANSI-safe column truncation.
47
+ * Each column value is padded or truncated to its allocated visible width.
48
+ * Truncated values have their trailing ANSI resets stripped (truncateToWidth
49
+ * appends \\x1b[0m to close styles; since we control the content, this is
50
+ * safe to strip).
51
+ */
52
+ function buildRow(parts) {
53
+ return parts.map(({ text, width }) => {
54
+ const vis = visibleWidth(text);
55
+ if (vis > width) {
56
+ // Truncate and strip trailing ANSI reset that truncateToWidth adds
57
+ const raw = truncateToWidth(text, width, "");
58
+ return raw.replace(/\x1b\[0m$/, "");
59
+ }
60
+ // Pad with spaces to hit the target visible width
61
+ return text + " ".repeat(width - vis);
62
+ }).join(" ");
63
+ }
64
+ export class ShowResolvedScreen {
65
+ render(state, width, theme) {
66
+ const view = getActiveView(state);
67
+ if (view.kind !== "show-resolved") {
68
+ return ["(show-resolved called with wrong active view)"];
69
+ }
70
+ const lines = [];
71
+ // ── Column sizing ──────────────────────────────────────────────────
72
+ // Reserve enough room for source labels. Longest: "Falls back to pi current" = 24.
73
+ const minSource = 24;
74
+ const leading = 4; // " {cursor} "
75
+ const gap = 3; // 3 single-space gaps between 4 columns
76
+ // stepCol: longest step is "review-plan"/"review-code" = 11 vis
77
+ // Use 11 so model gets more room, and step names fit exactly.
78
+ const stepCol = 11;
79
+ // personaCol: emoji(2 vis) + space + name; names up to ~12 chars
80
+ // padOrTruncate handles overflow gracefully.
81
+ const personaCol = 14;
82
+ // modelCol: flexible; long models get truncated with visible ellipsis
83
+ const modelCol = Math.max(16, width - leading - gap - stepCol - personaCol - minSource);
84
+ // ── Title ───────────────────────────────────────────────────────────
85
+ lines.push(accentBold("forge config › current setup", theme));
86
+ lines.push(rule(width, theme));
87
+ // ── Column headers ─────────────────────────────────────────────────
88
+ lines.push(` ${muted(padRight("Step", stepCol), theme)} ${muted(padRight("Persona", personaCol), theme)} ${muted(padRight("Model", modelCol), theme)} ${muted("Source", theme)}`);
89
+ // ── Phase rows ─────────────────────────────────────────────────────
90
+ const rows = getPhaseTable(state);
91
+ const win = windowList(rows, view.cursor, 10);
92
+ if (win.aboveCount > 0)
93
+ lines.push(muted(` ↑ ${win.aboveCount} row(s) above`, theme));
94
+ for (let i = 0; i < win.visible.length; i++) {
95
+ const row = win.visible[i];
96
+ const absoluteIdx = win.start + i;
97
+ const cur = cursor(absoluteIdx === view.cursor, theme);
98
+ const rowStr = buildRow([
99
+ { text: row.role, width: stepCol },
100
+ { text: `${row.personaEmoji} ${row.persona}`, width: personaCol },
101
+ { text: row.resolved, width: modelCol },
102
+ // Source column is the last column — no fixed width needed
103
+ ]);
104
+ lines.push(` ${cur} ${rowStr} ${row.sourceLabel}`);
105
+ }
106
+ if (win.belowCount > 0)
107
+ lines.push(muted(` ↓ ${win.belowCount} row(s) below`, theme));
108
+ // ── Cascade footer ─────────────────────────────────────────────────
109
+ lines.push(...cascadeFooter(width, theme));
110
+ // ── Navigation ─────────────────────────────────────────────────────
111
+ lines.push("");
112
+ lines.push(muted(` ↑/↓ scroll esc back q quit`, theme));
113
+ return safeLines(lines, width);
114
+ }
115
+ handleInput(data, state) {
116
+ const view = getActiveView(state);
117
+ if (view.kind !== "show-resolved")
118
+ return { kind: "no-op" };
119
+ if (matchesKey(data, Key.up) || matchesKey(data, "k")) {
120
+ return { kind: "dispatch", action: { kind: "cursor-move", delta: -1 } };
121
+ }
122
+ if (matchesKey(data, Key.down) || matchesKey(data, "j")) {
123
+ return { kind: "dispatch", action: { kind: "cursor-move", delta: 1 } };
124
+ }
125
+ return { kind: "no-op" };
126
+ }
127
+ }
128
+ //# sourceMappingURL=show-resolved.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"show-resolved.js","sourceRoot":"","sources":["../../../../../src/extensions/forgecli/config-tui/screens/show-resolved.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ConfigTuiState } from "../state/model.js";
2
+ import type { Theme } from "@earendil-works/pi-coding-agent";
3
+ import { InputResult, Screen } from "./types.js";
4
+ export declare class TierMenuScreen implements Screen {
5
+ render(state: ConfigTuiState, width: number, theme: Theme): string[];
6
+ handleInput(data: string, state: ConfigTuiState): InputResult;
7
+ }
@@ -0,0 +1,135 @@
1
+ // Tier-menu screen — the new landing view for forge:config.
2
+ // Phase A: replaces top-menu as the default entry point.
3
+ // Shows three workload tiers (Heavy / Standard / Light) as primary CTAs,
4
+ // a scope toggle, and secondary navigation to show-resolved / advanced.
5
+ import { Key, matchesKey } from "@earendil-works/pi-tui";
6
+ import { getActiveView, getAllScopedTierAssignments } from "../state/selectors.js";
7
+ import { TIERS, TIER_LABELS, TIER_SUB_LABELS } from "../tier-meta.js";
8
+ import { rule, safeLines } from "./shared.js";
9
+ import { padRight, cursor, accentBold, accent, muted, warning, dirtyMarker } from "../theme.js";
10
+ /** Interactive row count: 3 tiers + show-resolved + advanced = 5. */
11
+ const ITEM_COUNT = 5;
12
+ export class TierMenuScreen {
13
+ render(state, width, theme) {
14
+ const view = getActiveView(state);
15
+ if (view.kind !== "tier-menu") {
16
+ return ["(tier-menu called with wrong active view)"];
17
+ }
18
+ const cursorIdx = view.cursor;
19
+ const lines = [];
20
+ // ── Header ─────────────────────────────────────────────────────────
21
+ lines.push(accentBold("forge config", theme));
22
+ lines.push(rule(width, theme));
23
+ // ── "Active right now" summary ─────────────────────────────────────
24
+ lines.push(muted("Active right now", theme));
25
+ const scopedAssignments = getAllScopedTierAssignments(state, state.scope);
26
+ for (const { tier, assignment } of scopedAssignments) {
27
+ const label = padRight(TIER_LABELS[tier], 10);
28
+ if (assignment.status === "set") {
29
+ const modelStr = `${assignment.provider}:${assignment.model}`;
30
+ const layerLabel = `(${assignment.layer})`;
31
+ lines.push(` ${label} ${modelStr} ${muted(layerLabel, theme)}`);
32
+ }
33
+ else if (assignment.status === "set-in-other-layer") {
34
+ const otherLabel = assignment.otherLayer === "global" ? "global" : "project";
35
+ const modelStr = `${assignment.provider}:${assignment.model}`;
36
+ lines.push(` ${label} ${muted(modelStr + " (" + otherLabel + ")", theme)}`);
37
+ }
38
+ else if (assignment.status === "mixed") {
39
+ lines.push(` ${label} ${warning("mixed — personas differ", theme)}`);
40
+ }
41
+ else {
42
+ lines.push(` ${label} ${muted("not set — falls back to pi current", theme)}`);
43
+ }
44
+ }
45
+ // Scope row
46
+ const scopeLabel = state.scope === "global" ? "global" : `project ( ${state.cwd} )`;
47
+ const altLabel = state.scope === "global" ? "project" : "global";
48
+ lines.push(` ${padRight("Scope", 10)} ${accent(scopeLabel, theme)} ${muted(altLabel, theme)}`);
49
+ lines.push("");
50
+ // ── "Choose models for your AI workflow" ────────────────────────────
51
+ lines.push(muted("Choose models for your AI workflow", theme));
52
+ lines.push(rule(width, theme));
53
+ // Tier rows (items 0–2)
54
+ for (let i = 0; i < TIERS.length; i++) {
55
+ const tier = TIERS[i];
56
+ const cur = cursor(i === cursorIdx, theme);
57
+ const label = padRight(TIER_LABELS[tier], 10);
58
+ const subLabel = muted(`(${TIER_SUB_LABELS[tier]})`, theme);
59
+ const tierScoped = scopedAssignments.find((a) => a.tier === tier).assignment;
60
+ const valueStr = tierScoped.status === "set"
61
+ ? `${tierScoped.provider}:${tierScoped.model}`
62
+ : tierScoped.status === "set-in-other-layer"
63
+ ? muted(`(${tierScoped.otherLayer})`, theme)
64
+ : tierScoped.status === "mixed"
65
+ ? warning("mixed", theme)
66
+ : muted("not set", theme);
67
+ lines.push(` ${cur} ${label} ${subLabel} ${valueStr}`);
68
+ }
69
+ // "Show what runs at each step" (item 3)
70
+ {
71
+ const cur = cursor(3 === cursorIdx, theme);
72
+ lines.push(` ${cur} ${muted("Show what runs at each step", theme)}`);
73
+ }
74
+ // "Advanced — per-persona / per-step overrides" (item 4)
75
+ {
76
+ const cur = cursor(4 === cursorIdx, theme);
77
+ lines.push(` ${cur} ${muted("Advanced — per-persona / per-step overrides", theme)}`);
78
+ }
79
+ lines.push("");
80
+ // ── Footer ──────────────────────────────────────────────────────────
81
+ const tierWord = cursorIdx < 3 ? "enter pick model" : "enter open";
82
+ lines.push(muted(` ${tierWord} tab toggle scope q quit`, theme));
83
+ if (state.dirty)
84
+ lines.push(` ${dirtyMarker(theme)}`);
85
+ return safeLines(lines, width);
86
+ }
87
+ handleInput(data, state) {
88
+ const view = getActiveView(state);
89
+ if (view.kind !== "tier-menu")
90
+ return { kind: "no-op" };
91
+ // ── Navigation ──────────────────────────────────────────────────────
92
+ if (matchesKey(data, Key.up) || matchesKey(data, "k")) {
93
+ return { kind: "dispatch", action: { kind: "cursor-move", delta: -1 } };
94
+ }
95
+ if (matchesKey(data, Key.down) || matchesKey(data, "j")) {
96
+ if (view.cursor < ITEM_COUNT - 1) {
97
+ return { kind: "dispatch", action: { kind: "cursor-move", delta: 1 } };
98
+ }
99
+ return { kind: "consumed" };
100
+ }
101
+ // ── Tab: toggle scope ───────────────────────────────────────────────
102
+ if (matchesKey(data, Key.tab)) {
103
+ return { kind: "dispatch", action: { kind: "toggle-scope" } };
104
+ }
105
+ // ── Enter: act on current cursor position ───────────────────────────
106
+ if (matchesKey(data, Key.enter)) {
107
+ if (view.cursor < 3) {
108
+ // Tier row → open tier picker
109
+ const tier = TIERS[view.cursor];
110
+ return { kind: "dispatch", action: { kind: "select-tier", tier } };
111
+ }
112
+ if (view.cursor === 3) {
113
+ // "Show what runs at each step"
114
+ return { kind: "dispatch", action: { kind: "push-view", view: { kind: "show-resolved", cursor: 0 } } };
115
+ }
116
+ if (view.cursor === 4) {
117
+ // "Advanced" → advanced-menu (Phase D)
118
+ return { kind: "dispatch", action: { kind: "push-view", view: { kind: "advanced-menu", cursor: 0 } } };
119
+ }
120
+ }
121
+ // ── Shortcuts: 1/2/3 for tiers, s for show, a for advanced ──────────
122
+ if (matchesKey(data, "1"))
123
+ return { kind: "dispatch", action: { kind: "select-tier", tier: "heavy" } };
124
+ if (matchesKey(data, "2"))
125
+ return { kind: "dispatch", action: { kind: "select-tier", tier: "standard" } };
126
+ if (matchesKey(data, "3"))
127
+ return { kind: "dispatch", action: { kind: "select-tier", tier: "light" } };
128
+ if (matchesKey(data, "s"))
129
+ return { kind: "dispatch", action: { kind: "push-view", view: { kind: "show-resolved", cursor: 0 } } };
130
+ if (matchesKey(data, "a"))
131
+ return { kind: "dispatch", action: { kind: "push-view", view: { kind: "advanced-menu", cursor: 0 } } };
132
+ return { kind: "no-op" };
133
+ }
134
+ }
135
+ //# sourceMappingURL=tier-menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tier-menu.js","sourceRoot":"","sources":["../../../../../src/extensions/forgecli/config-tui/screens/tier-menu.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ConfigTuiState } from "../state/model.js";
2
+ import type { Theme } from "@earendil-works/pi-coding-agent";
3
+ import { InputResult, Screen } from "./types.js";
4
+ export declare class TierPickerScreen implements Screen {
5
+ render(state: ConfigTuiState, width: number, theme: Theme): string[];
6
+ handleInput(data: string, state: ConfigTuiState): InputResult;
7
+ private handlePickProviderInput;
8
+ private handlePickModelInput;
9
+ }
@@ -0,0 +1,122 @@
1
+ // Tier-picker screen — provider + model picker for a workload tier.
2
+ // Phase A: minimal placeholder. Phase B adds full provider/model picking flow.
3
+ // Reuses the same UX patterns as persona-editor but scoped to a tier.
4
+ import { Key, matchesKey } from "@earendil-works/pi-tui";
5
+ import { getActiveView, uniqueProviders } from "../state/selectors.js";
6
+ import { TIER_LABELS, tierPersonaLine } from "../tier-meta.js";
7
+ import { rule, authBadgeFor, windowList, safeLines } from "./shared.js";
8
+ import { padRight, cursor, accentBold, muted, dirtyMarker } from "../theme.js";
9
+ export class TierPickerScreen {
10
+ render(state, width, theme) {
11
+ const view = getActiveView(state);
12
+ if (view.kind !== "tier-picker") {
13
+ return ["(tier-picker called with wrong active view)"];
14
+ }
15
+ const tierLabel = TIER_LABELS[view.tier];
16
+ const lines = [];
17
+ if (view.step === "pick-provider") {
18
+ lines.push(accentBold(`forge config › ${tierLabel} › pick provider`, theme));
19
+ lines.push(rule(width, theme));
20
+ lines.push(muted(` This will run for: ${tierPersonaLine(view.tier)}`, theme));
21
+ lines.push("");
22
+ const providers = uniqueProviders(state);
23
+ const win = windowList(providers, view.cursor);
24
+ if (win.aboveCount > 0)
25
+ lines.push(muted(` ↑ ${win.aboveCount} more above`, theme));
26
+ win.visible.forEach((p, i) => {
27
+ const absoluteIdx = win.start + i;
28
+ const cur = cursor(absoluteIdx === view.cursor, theme);
29
+ const auth = authBadgeFor(state, p, theme);
30
+ const modelCount = state.availableModels.filter((m) => m.provider === p).length;
31
+ const countLabel = `${modelCount} model${modelCount === 1 ? "" : "s"}`;
32
+ lines.push(` ${cur} ${padRight(p, 18)} ${auth} ${muted(countLabel, theme)}`);
33
+ });
34
+ if (win.belowCount > 0)
35
+ lines.push(muted(` ↓ ${win.belowCount} more below`, theme));
36
+ lines.push("");
37
+ lines.push(muted(" ↑↓ select enter advance esc back", theme));
38
+ }
39
+ else if (view.step === "pick-model") {
40
+ lines.push(accentBold(`forge config › ${tierLabel} › pick model`, theme));
41
+ lines.push(rule(width, theme));
42
+ lines.push(muted(` provider: ${view.provider ?? "(unknown)"}`, theme));
43
+ lines.push(muted(` This will run for: ${tierPersonaLine(view.tier)}`, theme));
44
+ lines.push("");
45
+ const models = state.availableModels.filter((m) => m.provider === view.provider);
46
+ if (models.length === 0) {
47
+ lines.push(muted(" No models available for this provider.", theme));
48
+ lines.push(muted(` (Run \`pi /login ${view.provider}\` then return.)`, theme));
49
+ }
50
+ else {
51
+ const win = windowList(models, view.cursor);
52
+ if (win.aboveCount > 0)
53
+ lines.push(muted(` ↑ ${win.aboveCount} more above`, theme));
54
+ win.visible.forEach((m, i) => {
55
+ const absoluteIdx = win.start + i;
56
+ const cur = cursor(absoluteIdx === view.cursor, theme);
57
+ lines.push(` ${cur} ${m.id}`);
58
+ });
59
+ if (win.belowCount > 0)
60
+ lines.push(muted(` ↓ ${win.belowCount} more below`, theme));
61
+ }
62
+ lines.push("");
63
+ lines.push(muted(" ↑↓ select enter save esc back", theme));
64
+ }
65
+ if (state.dirty)
66
+ lines.push(` ${dirtyMarker(theme)}`);
67
+ return safeLines(lines, width);
68
+ }
69
+ handleInput(data, state) {
70
+ const view = getActiveView(state);
71
+ if (view.kind !== "tier-picker")
72
+ return { kind: "no-op" };
73
+ if (view.step === "pick-provider") {
74
+ return this.handlePickProviderInput(data, state, view);
75
+ }
76
+ if (view.step === "pick-model") {
77
+ return this.handlePickModelInput(data, state, view);
78
+ }
79
+ return { kind: "no-op" };
80
+ }
81
+ handlePickProviderInput(data, state, view) {
82
+ const providers = uniqueProviders(state);
83
+ if (matchesKey(data, Key.up) || matchesKey(data, "k")) {
84
+ return { kind: "dispatch", action: { kind: "cursor-move", delta: -1 } };
85
+ }
86
+ if (matchesKey(data, Key.down) || matchesKey(data, "j")) {
87
+ if (view.cursor < providers.length - 1) {
88
+ return { kind: "dispatch", action: { kind: "cursor-move", delta: 1 } };
89
+ }
90
+ return { kind: "consumed" };
91
+ }
92
+ if (matchesKey(data, Key.enter)) {
93
+ const provider = providers[view.cursor];
94
+ if (provider) {
95
+ return { kind: "dispatch", action: { kind: "set-tier-provider", tier: view.tier, provider } };
96
+ }
97
+ return { kind: "consumed" };
98
+ }
99
+ return { kind: "no-op" };
100
+ }
101
+ handlePickModelInput(data, state, view) {
102
+ const models = state.availableModels.filter((m) => m.provider === view.provider);
103
+ if (matchesKey(data, Key.up) || matchesKey(data, "k")) {
104
+ return { kind: "dispatch", action: { kind: "cursor-move", delta: -1 } };
105
+ }
106
+ if (matchesKey(data, Key.down) || matchesKey(data, "j")) {
107
+ if (view.cursor < models.length - 1) {
108
+ return { kind: "dispatch", action: { kind: "cursor-move", delta: 1 } };
109
+ }
110
+ return { kind: "consumed" };
111
+ }
112
+ if (matchesKey(data, Key.enter)) {
113
+ const target = models[view.cursor];
114
+ if (target && view.provider) {
115
+ return { kind: "dispatch", action: { kind: "commit-tier-model", tier: view.tier, provider: view.provider, model: target.id, layer: state.scope } };
116
+ }
117
+ return { kind: "consumed" };
118
+ }
119
+ return { kind: "no-op" };
120
+ }
121
+ }
122
+ //# sourceMappingURL=tier-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tier-picker.js","sourceRoot":"","sources":["../../../../../src/extensions/forgecli/config-tui/screens/tier-picker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { ConfigTuiState, ConfigTuiAction } from "../state/model.js";
2
+ import type { Theme } from "@earendil-works/pi-coding-agent";
3
+ export type InputResult = {
4
+ kind: "consumed";
5
+ } | {
6
+ kind: "no-op";
7
+ } | {
8
+ kind: "dispatch";
9
+ action: ConfigTuiAction;
10
+ } | {
11
+ kind: "dispatch-seq";
12
+ actions: ConfigTuiAction[];
13
+ } | {
14
+ kind: "pop";
15
+ } | {
16
+ kind: "quit";
17
+ } | {
18
+ kind: "error";
19
+ message: string;
20
+ };
21
+ export interface Screen {
22
+ render(state: ConfigTuiState, width: number, theme: Theme): string[];
23
+ handleInput(data: string, state: ConfigTuiState): InputResult;
24
+ }