linersh 0.5.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 (95) hide show
  1. package/README.md +168 -0
  2. package/bin/liner.js +126 -0
  3. package/dist/App.js +195 -0
  4. package/dist/App.js.map +1 -0
  5. package/dist/agents/detect.js +102 -0
  6. package/dist/agents/detect.js.map +1 -0
  7. package/dist/agents/events.js +8 -0
  8. package/dist/agents/events.js.map +1 -0
  9. package/dist/agents/jtbd-clarify.js +144 -0
  10. package/dist/agents/jtbd-clarify.js.map +1 -0
  11. package/dist/agents/parsers.js +316 -0
  12. package/dist/agents/parsers.js.map +1 -0
  13. package/dist/agents/prompts.js +301 -0
  14. package/dist/agents/prompts.js.map +1 -0
  15. package/dist/agents/run-log.js +71 -0
  16. package/dist/agents/run-log.js.map +1 -0
  17. package/dist/agents/runner.js +180 -0
  18. package/dist/agents/runner.js.map +1 -0
  19. package/dist/agents/types.js +2 -0
  20. package/dist/agents/types.js.map +1 -0
  21. package/dist/bin-resolver.js +92 -0
  22. package/dist/bin-resolver.js.map +1 -0
  23. package/dist/browser-support.js +62 -0
  24. package/dist/browser-support.js.map +1 -0
  25. package/dist/colors.js +105 -0
  26. package/dist/colors.js.map +1 -0
  27. package/dist/components/Header.js +17 -0
  28. package/dist/components/Header.js.map +1 -0
  29. package/dist/components/KeyHints.js +10 -0
  30. package/dist/components/KeyHints.js.map +1 -0
  31. package/dist/components/LabeledBox.js +53 -0
  32. package/dist/components/LabeledBox.js.map +1 -0
  33. package/dist/components/NextSteps.js +18 -0
  34. package/dist/components/NextSteps.js.map +1 -0
  35. package/dist/config.js +88 -0
  36. package/dist/config.js.map +1 -0
  37. package/dist/editor.js +40 -0
  38. package/dist/editor.js.map +1 -0
  39. package/dist/gates.js +121 -0
  40. package/dist/gates.js.map +1 -0
  41. package/dist/index.js +17 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/ipc.js +136 -0
  44. package/dist/ipc.js.map +1 -0
  45. package/dist/phases.js +348 -0
  46. package/dist/phases.js.map +1 -0
  47. package/dist/progress.js +185 -0
  48. package/dist/progress.js.map +1 -0
  49. package/dist/screens/AgentSetup.js +217 -0
  50. package/dist/screens/AgentSetup.js.map +1 -0
  51. package/dist/screens/BootSplash.js +159 -0
  52. package/dist/screens/BootSplash.js.map +1 -0
  53. package/dist/screens/BrowserSetup.js +181 -0
  54. package/dist/screens/BrowserSetup.js.map +1 -0
  55. package/dist/screens/CompileView.js +297 -0
  56. package/dist/screens/CompileView.js.map +1 -0
  57. package/dist/screens/FramingConfirmReview.js +193 -0
  58. package/dist/screens/FramingConfirmReview.js.map +1 -0
  59. package/dist/screens/Gate1Review.js +143 -0
  60. package/dist/screens/Gate1Review.js.map +1 -0
  61. package/dist/screens/Gate2Review.js +158 -0
  62. package/dist/screens/Gate2Review.js.map +1 -0
  63. package/dist/screens/ImportPrompt.js +65 -0
  64. package/dist/screens/ImportPrompt.js.map +1 -0
  65. package/dist/screens/LocalFilePicker.js +139 -0
  66. package/dist/screens/LocalFilePicker.js.map +1 -0
  67. package/dist/screens/NewMixtapeWizard.js +662 -0
  68. package/dist/screens/NewMixtapeWizard.js.map +1 -0
  69. package/dist/screens/NewProjectPrompt.js +66 -0
  70. package/dist/screens/NewProjectPrompt.js.map +1 -0
  71. package/dist/screens/PhaseRunner.js +1119 -0
  72. package/dist/screens/PhaseRunner.js.map +1 -0
  73. package/dist/screens/ProcessManifest.js +158 -0
  74. package/dist/screens/ProcessManifest.js.map +1 -0
  75. package/dist/screens/ProjectHub.js +546 -0
  76. package/dist/screens/ProjectHub.js.map +1 -0
  77. package/dist/screens/Settings.js +72 -0
  78. package/dist/screens/Settings.js.map +1 -0
  79. package/dist/screens/SourceModal.js +269 -0
  80. package/dist/screens/SourceModal.js.map +1 -0
  81. package/dist/screens/TapeBrowser.js +277 -0
  82. package/dist/screens/TapeBrowser.js.map +1 -0
  83. package/dist/screens/TapeDraftReview.js +440 -0
  84. package/dist/screens/TapeDraftReview.js.map +1 -0
  85. package/dist/screens/TapeEditor.js +310 -0
  86. package/dist/screens/TapeEditor.js.map +1 -0
  87. package/dist/screens/cassette-art.js +72 -0
  88. package/dist/screens/cassette-art.js.map +1 -0
  89. package/dist/store.js +10 -0
  90. package/dist/store.js.map +1 -0
  91. package/dist/types.js +3 -0
  92. package/dist/types.js.map +1 -0
  93. package/dist/yaml-io.js +345 -0
  94. package/dist/yaml-io.js.map +1 -0
  95. package/package.json +54 -0
@@ -0,0 +1,92 @@
1
+ // Locate the `liner` binary to spawn.
2
+ //
3
+ // Resolution order:
4
+ // 1. LINER_BIN env var (explicit override)
5
+ // 2. Bundled platform-specific binary from optionalDependencies
6
+ // (e.g. linersh-darwin-arm64/liner) — the production path
7
+ // 3. Repo-local `.venv/bin/liner` discovered by walking up from this file
8
+ // (dev convenience — when you're hacking on the Python source in a
9
+ // checkout, the local venv should always win over any global install
10
+ // so your edits actually run.)
11
+ // 4. `liner` on $PATH (e.g. `pipx install linersh` or an activated venv)
12
+ //
13
+ // Branches (1) and (2) are the only paths that matter for the published npm
14
+ // package — neither dev path applies there (no `.venv` to walk up to). (3)
15
+ // before (4) is the dev affordance: previously PATH beat .venv, which meant
16
+ // a developer with a pipx-installed `liner` would silently run the global
17
+ // version instead of their local Python edits.
18
+ import { existsSync } from "node:fs";
19
+ import { spawnSync } from "node:child_process";
20
+ import { createRequire } from "node:module";
21
+ import { dirname, join } from "node:path";
22
+ import { fileURLToPath } from "node:url";
23
+ const require = createRequire(import.meta.url);
24
+ const __filename = fileURLToPath(import.meta.url);
25
+ export function resolveBinary() {
26
+ // 1. Explicit env override
27
+ const envBin = process.env["LINER_BIN"];
28
+ if (envBin && existsSync(envBin)) {
29
+ return { command: envBin, args: [], source: "env" };
30
+ }
31
+ // 2. Bundled binary via optionalDependencies (production path)
32
+ const target = `linersh-${process.platform}-${process.arch}`;
33
+ try {
34
+ const pkgJson = require.resolve(`${target}/package.json`);
35
+ const baseDir = pkgJson.replace(/[\\/]package\.json$/, "");
36
+ const bundled = join(baseDir, exeName());
37
+ if (existsSync(bundled)) {
38
+ return { command: bundled, args: [], source: "bundled" };
39
+ }
40
+ }
41
+ catch {
42
+ // fall through
43
+ }
44
+ // 3. Repo-local venv (dev convenience). Walk up from this file until we
45
+ // hit a `.venv/bin/liner` or the filesystem root. Prefer this over a
46
+ // global pipx install so dev edits to the Python source take effect
47
+ // immediately without juggling `LINER_BIN`.
48
+ const repoVenvBin = findRepoVenvBinary();
49
+ if (repoVenvBin) {
50
+ return { command: repoVenvBin, args: [], source: "repo-venv" };
51
+ }
52
+ // 4. PATH lookup (works when user has pipx-installed liner globally or
53
+ // activated an unrelated venv).
54
+ const probe = spawnSync(process.platform === "win32" ? "where" : "which", ["liner"]);
55
+ if (probe.status === 0) {
56
+ return { command: "liner", args: [], source: "path" };
57
+ }
58
+ throw new BinaryNotFoundError(target);
59
+ }
60
+ function exeName() {
61
+ return process.platform === "win32" ? "liner.exe" : "liner";
62
+ }
63
+ function findRepoVenvBinary() {
64
+ const venvBinDir = process.platform === "win32" ? "Scripts" : "bin";
65
+ let dir = dirname(__filename);
66
+ // Cap the walk at ~10 levels so we never blow up on weird filesystems.
67
+ for (let i = 0; i < 10; i += 1) {
68
+ const candidate = join(dir, ".venv", venvBinDir, exeName());
69
+ if (existsSync(candidate))
70
+ return candidate;
71
+ const parent = dirname(dir);
72
+ if (parent === dir)
73
+ break; // hit filesystem root
74
+ dir = parent;
75
+ }
76
+ return null;
77
+ }
78
+ export class BinaryNotFoundError extends Error {
79
+ constructor(target) {
80
+ super(`Could not find the liner binary.\n` +
81
+ ` • No LINER_BIN env var set.\n` +
82
+ ` • No bundled binary for ${target} (optionalDependency missing).\n` +
83
+ ` • \`liner\` not on PATH.\n` +
84
+ ` • No repo-local \`.venv/bin/liner\` found by walking up from this file.\n\n` +
85
+ `Fix one of:\n` +
86
+ ` • Install the Python core globally: \`pipx install linersh\`\n` +
87
+ ` • Activate the repo's venv: \`source .venv/bin/activate\` (then re-run)\n` +
88
+ ` • Point at a specific binary: \`LINER_BIN=/path/to/liner npm run dev\``);
89
+ this.name = "BinaryNotFoundError";
90
+ }
91
+ }
92
+ //# sourceMappingURL=bin-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin-resolver.js","sourceRoot":"","sources":["../src/bin-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import { readdirSync, rmSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ export function getBrowserSupportStatus() {
5
+ for (const cacheDir of candidateBrowserCacheDirs()) {
6
+ if (hasChromiumBrowser(cacheDir))
7
+ return { ready: true, cacheDir };
8
+ }
9
+ return { ready: false, cacheDir: candidateBrowserCacheDirs()[0] ?? null };
10
+ }
11
+ export function isBrowserSupportReady() {
12
+ return getBrowserSupportStatus().ready;
13
+ }
14
+ export function removeBrowserSupport() {
15
+ const removed = [];
16
+ for (const cacheDir of candidateBrowserCacheDirs()) {
17
+ for (const path of removableBrowserPaths(cacheDir)) {
18
+ rmSync(path, { recursive: true, force: true });
19
+ removed.push(path);
20
+ }
21
+ }
22
+ return removed;
23
+ }
24
+ function candidateBrowserCacheDirs() {
25
+ const override = process.env["PLAYWRIGHT_BROWSERS_PATH"];
26
+ if (override && override !== "0")
27
+ return [override];
28
+ if (process.platform === "darwin") {
29
+ return [join(homedir(), "Library", "Caches", "ms-playwright")];
30
+ }
31
+ if (process.platform === "win32") {
32
+ const localAppData = process.env["LOCALAPPDATA"];
33
+ if (localAppData)
34
+ return [join(localAppData, "ms-playwright")];
35
+ }
36
+ const xdgCache = process.env["XDG_CACHE_HOME"] || join(homedir(), ".cache");
37
+ return [join(xdgCache, "ms-playwright")];
38
+ }
39
+ function hasChromiumBrowser(cacheDir) {
40
+ try {
41
+ return readdirSync(cacheDir, { withFileTypes: true }).some((entry) => entry.isDirectory() &&
42
+ (entry.name.startsWith("chromium-") ||
43
+ entry.name.startsWith("chromium_headless_shell-")));
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ }
49
+ function removableBrowserPaths(cacheDir) {
50
+ try {
51
+ return readdirSync(cacheDir, { withFileTypes: true })
52
+ .filter((entry) => entry.isDirectory() &&
53
+ (entry.name.startsWith("chromium-") ||
54
+ entry.name.startsWith("chromium_headless_shell-") ||
55
+ entry.name.startsWith("ffmpeg-")))
56
+ .map((entry) => join(cacheDir, entry.name));
57
+ }
58
+ catch {
59
+ return [];
60
+ }
61
+ }
62
+ //# sourceMappingURL=browser-support.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-support.js","sourceRoot":"","sources":["../src/browser-support.ts"],"names":[],"mappings":"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"}
package/dist/colors.js ADDED
@@ -0,0 +1,105 @@
1
+ // Liner color palette — exactly five colors, no more.
2
+ //
3
+ // We use ONLY these hex values across the TUI. If you find yourself reaching
4
+ // for "cyan", "red", "white", "black", or any other named color, stop and
5
+ // pick one of the five below. The constraint is intentional — five colors,
6
+ // consistently applied, gives the product a recognizable signature and
7
+ // keeps the visual language tight.
8
+ //
9
+ // Palette
10
+ // =======
11
+ //
12
+ // #2bd3b9 teal — structural, neutral-positive
13
+ // #facd2f yellow — attention, "look here now"
14
+ // #7b1fc2 purple — heading / brand accent
15
+ // #ea2790 magenta-pink — alarm / error
16
+ // #daf1fd pale blue — light-on-dark text (chip labels, highlights)
17
+ //
18
+ // Roles
19
+ // =====
20
+ //
21
+ // STRUCTURE (teal) — borders, chip backgrounds, hotkey labels, "this
22
+ // is interactive" affordances. The structural
23
+ // skeleton of every screen.
24
+ //
25
+ // CURRENT (yellow) — "you are here." The currently-selected menu
26
+ // item, the chip on the focus card when its
27
+ // phase is in_progress, the active selection in
28
+ // every list. NEVER use for borders.
29
+ //
30
+ // HEADING (purple) — content section headers. Markdown `**Heading:**`
31
+ // runs, wizard step questions, the "Get started"
32
+ // label on the splash, "Review:" before the
33
+ // confirmation table.
34
+ //
35
+ // SUCCESS (teal) — "done." ✓ glyphs, completed phases, accepted
36
+ // gates, a fully-succeeded compile. Doubles up
37
+ // with STRUCTURE; in practice success borders
38
+ // and structural borders are rarely adjacent,
39
+ // and context disambiguates.
40
+ //
41
+ // WARNING (yellow) — partial / draft-pending / "in flight, look at
42
+ // this." Doubles up with CURRENT — both are
43
+ // attention signals; the only difference is
44
+ // whether the user caused it (CURRENT) or the
45
+ // system did (WARNING).
46
+ //
47
+ // ERROR (magenta-pink) — "failed." ✗ glyphs, failed compiles, error
48
+ // messages. Loud and unambiguous against the
49
+ // other four colors.
50
+ //
51
+ // HIGHLIGHT (pale blue) — light text on dark chip backgrounds (replaces
52
+ // the legacy "black on cyan" pattern), and any
53
+ // place a foreground text accent should pop
54
+ // against the default body color.
55
+ //
56
+ // MUTED (use `dimColor` prop) — secondary content. Ink dims whatever
57
+ // terminal color is configured; no specific hex
58
+ // needed (and dim text shouldn't grab attention
59
+ // the way a brand color would).
60
+ //
61
+ // Default (no color) — primary body text. Most content lands here so
62
+ // the colored accents have somewhere to pop.
63
+ //
64
+ // Box color rule (the only rule)
65
+ // ==============================
66
+ //
67
+ // **Border and chip share the same color**, always. The chip is a solid
68
+ // section of the top border line; visually they're one element, so they
69
+ // can never disagree.
70
+ //
71
+ // default state → STRUCTURE (teal)
72
+ // current / "you are here" → CURRENT (yellow)
73
+ // running / in-flight → CURRENT (yellow) — same as current
74
+ // done / accepted → SUCCESS (teal)
75
+ // partial / warn / draft → WARNING (yellow)
76
+ // failed → ERROR (magenta-pink)
77
+ //
78
+ // Pass exactly ONE color to `LabeledBox` via the `color` prop and the chip
79
+ // background follows automatically. Don't override `labelBg` separately
80
+ // unless you have a specific reason (e.g. the Header brand chip).
81
+ // Raw palette values. Prefer the role aliases below — these are exported
82
+ // for the cassette logo and any other place that wants the full palette
83
+ // (e.g. a random pick per character).
84
+ export const COLOR_TEAL = "#2bd3b9";
85
+ export const COLOR_YELLOW = "#facd2f";
86
+ export const COLOR_PURPLE = "#7b1fc2";
87
+ export const COLOR_MAGENTA = "#ea2790";
88
+ export const COLOR_PALE = "#daf1fd";
89
+ /** The full palette as an array — useful for randomized rendering. */
90
+ export const PALETTE = [
91
+ COLOR_TEAL,
92
+ COLOR_YELLOW,
93
+ COLOR_PURPLE,
94
+ COLOR_MAGENTA,
95
+ COLOR_PALE,
96
+ ];
97
+ // Role aliases — what every screen should import.
98
+ export const STRUCTURE = COLOR_TEAL;
99
+ export const CURRENT = COLOR_YELLOW;
100
+ export const HEADING = COLOR_PURPLE;
101
+ export const SUCCESS = COLOR_TEAL;
102
+ export const WARNING = COLOR_YELLOW;
103
+ export const ERROR = COLOR_MAGENTA;
104
+ export const HIGHLIGHT = COLOR_PALE;
105
+ //# sourceMappingURL=colors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.js","sourceRoot":"","sources":["../src/colors.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,EAAE;AACF,6EAA6E;AAC7E,0EAA0E;AAC1E,2EAA2E;AAC3E,uEAAuE;AACvE,mCAAmC;AACnC,EAAE;AACF,UAAU;AACV,UAAU;AACV,EAAE;AACF,2DAA2D;AAC3D,yDAAyD;AACzD,qDAAqD;AACrD,4CAA4C;AAC5C,2EAA2E;AAC3E,EAAE;AACF,QAAQ;AACR,QAAQ;AACR,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,yDAAyD;AACzD,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,6EAA6E;AAC7E,kEAAkE;AAClE,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,yEAAyE;AACzE,mDAAmD;AACnD,EAAE;AACF,2EAA2E;AAC3E,4EAA4E;AAC5E,2EAA2E;AAC3E,2EAA2E;AAC3E,0DAA0D;AAC1D,EAAE;AACF,4EAA4E;AAC5E,yEAAyE;AACzE,yEAAyE;AACzE,2EAA2E;AAC3E,qDAAqD;AACrD,EAAE;AACF,yEAAyE;AACzE,0EAA0E;AAC1E,kDAAkD;AAClD,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,yEAAyE;AACzE,+DAA+D;AAC/D,EAAE;AACF,0EAA0E;AAC1E,6EAA6E;AAC7E,6EAA6E;AAC7E,6DAA6D;AAC7D,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,EAAE;AACF,iCAAiC;AACjC,iCAAiC;AACjC,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,sBAAsB;AACtB,EAAE;AACF,mDAAmD;AACnD,qDAAqD;AACrD,uEAAuE;AACvE,mDAAmD;AACnD,qDAAqD;AACrD,2DAA2D;AAC3D,EAAE;AACF,2EAA2E;AAC3E,wEAAwE;AACxE,kEAAkE;AAElE,yEAAyE;AACzE,wEAAwE;AACxE,sCAAsC;AACtC,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC;AACpC,MAAM,CAAC,MAAM,YAAY,GAAG,SAAS,CAAC;AACtC,MAAM,CAAC,MAAM,YAAY,GAAG,SAAS,CAAC;AACtC,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC;AACvC,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC;AAEpC,sEAAsE;AACtE,MAAM,CAAC,MAAM,OAAO,GAAsB;IACxC,UAAU;IACV,YAAY;IACZ,YAAY;IACZ,aAAa;IACb,UAAU;CACX,CAAC;AAEF,kDAAkD;AAClD,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAC;AACpC,MAAM,CAAC,MAAM,OAAO,GAAG,YAAY,CAAC;AACpC,MAAM,CAAC,MAAM,OAAO,GAAG,YAAY,CAAC;AACpC,MAAM,CAAC,MAAM,OAAO,GAAG,UAAU,CAAC;AAClC,MAAM,CAAC,MAAM,OAAO,GAAG,YAAY,CAAC;AACpC,MAAM,CAAC,MAAM,KAAK,GAAG,aAAa,CAAC;AACnC,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAC"}
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ import { HIGHLIGHT, STRUCTURE } from "../colors.js";
4
+ // Single source of truth for the brand chip. The version is read from the
5
+ // build at compile time — see scripts/inject-version or fall back to a static.
6
+ const VERSION = "0.5.0";
7
+ export function Header({ title, subtitle, }) {
8
+ return (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
9
+ React.createElement(Box, null,
10
+ React.createElement(Text, { backgroundColor: STRUCTURE, color: HIGHLIGHT, bold: true }, " ▶ liner "),
11
+ React.createElement(Text, null, " "),
12
+ React.createElement(Text, { bold: true, color: STRUCTURE }, title),
13
+ React.createElement(Text, { dimColor: true }, " · v" + VERSION)),
14
+ subtitle ? (React.createElement(Box, { marginTop: 0 },
15
+ React.createElement(Text, { dimColor: true }, subtitle))) : null));
16
+ }
17
+ //# sourceMappingURL=Header.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Header.js","sourceRoot":"","sources":["../../src/components/Header.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpD,0EAA0E;AAC1E,+EAA+E;AAC/E,MAAM,OAAO,GAAG,OAAO,CAAC;AAExB,MAAM,UAAU,MAAM,CAAC,EACrB,KAAK,EACL,QAAQ,GAIT;IACC,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,YAAY,EAAE,CAAC;QACzC,oBAAC,GAAG;YACF,oBAAC,IAAI,IAAC,eAAe,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,UACrD,WAAW,CACP;YACP,oBAAC,IAAI,QAAE,IAAI,CAAQ;YACnB,oBAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAE,SAAS,IACxB,KAAK,CACD;YACP,oBAAC,IAAI,IAAC,QAAQ,UAAE,QAAQ,GAAG,OAAO,CAAQ,CACtC;QACL,QAAQ,CAAC,CAAC,CAAC,CACV,oBAAC,GAAG,IAAC,SAAS,EAAE,CAAC;YACf,oBAAC,IAAI,IAAC,QAAQ,UAAE,QAAQ,CAAQ,CAC5B,CACP,CAAC,CAAC,CAAC,IAAI,CACJ,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ import { STRUCTURE } from "../colors.js";
4
+ export function KeyHints({ hints }) {
5
+ return (React.createElement(Box, { flexWrap: "wrap", columnGap: 2 }, hints.map((h) => (React.createElement(Text, { key: h.key, dimColor: true },
6
+ React.createElement(Text, { color: STRUCTURE }, h.key),
7
+ " ",
8
+ h.label)))));
9
+ }
10
+ //# sourceMappingURL=KeyHints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyHints.js","sourceRoot":"","sources":["../../src/components/KeyHints.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzC,MAAM,UAAU,QAAQ,CAAC,EAAE,KAAK,EAAqB;IACnD,OAAO,CACL,oBAAC,GAAG,IAAC,QAAQ,EAAC,MAAM,EAAC,SAAS,EAAE,CAAC,IAC9B,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAChB,oBAAC,IAAI,IAAC,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ;QACxB,oBAAC,IAAI,IAAC,KAAK,EAAE,SAAS,IAAG,CAAC,CAAC,GAAG,CAAQ;;QAAE,CAAC,CAAC,KAAK,CAC1C,CACR,CAAC,CACE,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,53 @@
1
+ import React from "react";
2
+ import { Box, Text, useStdout } from "ink";
3
+ import { STRUCTURE } from "../colors.js";
4
+ // Charm/Lipgloss-style card: a bordered box with a chip on the top border line.
5
+ // Inspired by the Daytona TUI screenshots — the chip provides a clear card label
6
+ // that doesn't waste a content line.
7
+ //
8
+ // Implementation: Ink 5 lets us toggle each side of a border independently
9
+ // (borderTop={false}). We render the top border ourselves as a manual row of
10
+ // characters with the chip embedded, then let Ink draw the side + bottom
11
+ // borders normally. The corner glyphs must match between the manual top row
12
+ // and Ink's border style — both use the same "round" or "single" set.
13
+ const ROUND = {
14
+ topLeft: "╭",
15
+ topRight: "╮",
16
+ h: "─",
17
+ };
18
+ const SINGLE = {
19
+ topLeft: "┌",
20
+ topRight: "┐",
21
+ h: "─",
22
+ };
23
+ const DOUBLE = {
24
+ topLeft: "╔",
25
+ topRight: "╗",
26
+ h: "═",
27
+ };
28
+ export function LabeledBox({ label, color = STRUCTURE, labelBg, labelColor = "black", borderStyle = "round", width, paddingX = 2, paddingY = 1, children, }) {
29
+ // Default chip bg = border color, so the chip looks like a solid section
30
+ // of the top border. Callers can opt out by passing a different bg.
31
+ const resolvedLabelBg = labelBg ?? color;
32
+ const { stdout } = useStdout();
33
+ const cols = stdout?.columns ?? 80;
34
+ // Default width: terminal minus a 2-cell right margin so the box doesn't
35
+ // butt against the edge of the pane. Floor at 40 so things don't collapse
36
+ // in very narrow terminals.
37
+ const w = Math.max(40, width ?? cols - 2);
38
+ const chars = borderStyle === "round" ? ROUND : borderStyle === "double" ? DOUBLE : SINGLE;
39
+ // Truncate the label if it would overflow the top row.
40
+ // Budget: 2 corners + 2 leading dashes + " label " + at least 1 filler dash.
41
+ const maxLabelLen = Math.max(4, w - 2 - 2 - 2 - 1);
42
+ const safeLabel = label.length > maxLabelLen ? label.slice(0, maxLabelLen - 1) + "…" : label;
43
+ const labelText = ` ${safeLabel} `;
44
+ const leadDashes = 2;
45
+ const filler = Math.max(1, w - 2 - leadDashes - labelText.length);
46
+ return (React.createElement(Box, { flexDirection: "column", width: w },
47
+ React.createElement(Box, { width: w },
48
+ React.createElement(Text, { color: color }, chars.topLeft + chars.h.repeat(leadDashes)),
49
+ React.createElement(Text, { backgroundColor: resolvedLabelBg, color: labelColor, bold: true }, labelText),
50
+ React.createElement(Text, { color: color }, chars.h.repeat(filler) + chars.topRight)),
51
+ React.createElement(Box, { borderStyle: borderStyle, borderColor: color, borderTop: false, flexDirection: "column", width: w, paddingX: paddingX, paddingY: paddingY }, children)));
52
+ }
53
+ //# sourceMappingURL=LabeledBox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LabeledBox.js","sourceRoot":"","sources":["../../src/components/LabeledBox.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ import { STRUCTURE } from "../colors.js";
4
+ /**
5
+ * Bordered callout listing what to do next. Modeled on `clack`'s `p.note()`.
6
+ * Use after a wizard completes, a compile succeeds, or any other moment
7
+ * where the user just finished a discrete unit of work.
8
+ */
9
+ export function NextSteps({ title = "Next steps", items, color = STRUCTURE, }) {
10
+ return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: color, paddingX: 2, paddingY: 0, marginBottom: 1 },
11
+ React.createElement(Box, { marginBottom: 1 },
12
+ React.createElement(Text, { color: color, bold: true }, title)),
13
+ items.map((item, i) => (React.createElement(Box, { key: i },
14
+ React.createElement(Text, { color: color }, `${i + 1}. `),
15
+ React.createElement(Text, null, item.label),
16
+ item.hint ? (React.createElement(Text, { dimColor: true }, " " + item.hint)) : null)))));
17
+ }
18
+ //# sourceMappingURL=NextSteps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NextSteps.js","sourceRoot":"","sources":["../../src/components/NextSteps.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AASzC;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,EACxB,KAAK,GAAG,YAAY,EACpB,KAAK,EACL,KAAK,GAAG,SAAS,GAKlB;IACC,OAAO,CACL,oBAAC,GAAG,IACF,aAAa,EAAC,QAAQ,EACtB,WAAW,EAAC,OAAO,EACnB,WAAW,EAAE,KAAK,EAClB,QAAQ,EAAE,CAAC,EACX,QAAQ,EAAE,CAAC,EACX,YAAY,EAAE,CAAC;QAEf,oBAAC,GAAG,IAAC,YAAY,EAAE,CAAC;YAClB,oBAAC,IAAI,IAAC,KAAK,EAAE,KAAK,EAAE,IAAI,UACrB,KAAK,CACD,CACH;QACL,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CACtB,oBAAC,GAAG,IAAC,GAAG,EAAE,CAAC;YACT,oBAAC,IAAI,IAAC,KAAK,EAAE,KAAK,IAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAQ;YACzC,oBAAC,IAAI,QAAE,IAAI,CAAC,KAAK,CAAQ;YACxB,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CACX,oBAAC,IAAI,IAAC,QAAQ,UAAE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAQ,CACzC,CAAC,CAAC,CAAC,IAAI,CACJ,CACP,CAAC,CACE,CACP,CAAC;AACJ,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,88 @@
1
+ // User-level config persisted to ~/.liner/config.yaml.
2
+ //
3
+ // Today this is just the agent preference. The shape stays an object so the
4
+ // model picker (deferred) and any other settings can extend it without a
5
+ // migration. Anything missing from the file resolves to null on read; the
6
+ // caller's defaults take over from there.
7
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
8
+ import { homedir } from "node:os";
9
+ import { join } from "node:path";
10
+ import * as YAML from "yaml";
11
+ const CONFIG_DIR = join(homedir(), ".liner");
12
+ const CONFIG_PATH = join(CONFIG_DIR, "config.yaml");
13
+ export function configPath() {
14
+ return CONFIG_PATH;
15
+ }
16
+ export function configExists() {
17
+ return existsSync(CONFIG_PATH);
18
+ }
19
+ /**
20
+ * Read the user-level config. Returns a fully-populated config with `null`
21
+ * defaults for any field that's missing or malformed — callers don't have
22
+ * to undefined-check. Never throws; a corrupt config file degrades to "no
23
+ * preference set" rather than crashing the TUI.
24
+ */
25
+ export function readConfig() {
26
+ if (!existsSync(CONFIG_PATH)) {
27
+ return { agent: null };
28
+ }
29
+ try {
30
+ const raw = YAML.parse(readFileSync(CONFIG_PATH, "utf8"));
31
+ if (!raw || typeof raw !== "object")
32
+ return { agent: null };
33
+ const agent = raw["agent"];
34
+ const validAgent = agent === "claude" || agent === "codex" ? agent : null;
35
+ return { agent: validAgent };
36
+ }
37
+ catch {
38
+ return { agent: null };
39
+ }
40
+ }
41
+ export function writeConfig(config) {
42
+ if (!existsSync(CONFIG_DIR)) {
43
+ mkdirSync(CONFIG_DIR, { recursive: true });
44
+ }
45
+ // Stable, hand-editable output. Comment at the top so curators who poke at
46
+ // the file know what's there and how to extend it.
47
+ const body = [
48
+ "# Liner user config — generated by the TUI agent picker.",
49
+ "# Hand-edit if you know what you want; the TUI rewrites this on settings",
50
+ "# changes but preserves unknown keys (round-trip via YAML).",
51
+ "",
52
+ YAML.stringify(config),
53
+ ].join("\n");
54
+ writeFileSync(CONFIG_PATH, body, "utf8");
55
+ }
56
+ /**
57
+ * Pick which agent to use. Returns null when no agents are available at all.
58
+ * Priority order:
59
+ * 1. LINER_AGENT env var (escape hatch for CI / power users)
60
+ * 2. The configured agent in ~/.liner/config.yaml, IF it's installed
61
+ * 3. The single installed agent (when only one is available)
62
+ * 4. null — caller should show a picker
63
+ */
64
+ export function resolveConfiguredAgent(installed, config = readConfig()) {
65
+ if (installed.length === 0)
66
+ return null;
67
+ const envPin = (process.env["LINER_AGENT"] || "").toLowerCase();
68
+ if (envPin) {
69
+ const pinned = installed.find((a) => a.id === envPin);
70
+ if (pinned)
71
+ return pinned;
72
+ // Env names an agent that isn't installed — fall through to the
73
+ // configured / auto-pick path rather than crashing.
74
+ }
75
+ if (config.agent) {
76
+ const configured = installed.find((a) => a.id === config.agent);
77
+ if (configured)
78
+ return configured;
79
+ // The configured agent isn't installed anymore (user uninstalled it).
80
+ // Fall through; the caller will either pick the remaining one or
81
+ // re-prompt setup.
82
+ }
83
+ if (installed.length === 1) {
84
+ return installed[0];
85
+ }
86
+ return null;
87
+ }
88
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/editor.js ADDED
@@ -0,0 +1,40 @@
1
+ // Centralized $EDITOR resolution.
2
+ //
3
+ // The default fallback used to be `vi`, which is the classic Unix tradition
4
+ // but a hostile UX for anyone who hasn't learned vim. Nano is on every macOS
5
+ // install, is on most Linux distros, and crucially shows its keybindings at
6
+ // the bottom of the screen — a user who has never seen it before can still
7
+ // figure out save (^O) and exit (^X).
8
+ //
9
+ // $VISUAL beats $EDITOR per the de-facto convention; the user explicitly set
10
+ // either one if they prefer it.
11
+ import { existsSync } from "node:fs";
12
+ const FALLBACK_CANDIDATES = ["nano", "pico", "vi"];
13
+ /**
14
+ * Resolve which editor to spawn for in-tui $EDITOR launches. Honors the
15
+ * user's $VISUAL / $EDITOR if set; otherwise probes for newbie-friendly
16
+ * editors before falling back to vi.
17
+ */
18
+ export function resolveEditor() {
19
+ const explicit = (process.env["VISUAL"] || process.env["EDITOR"] || "").trim();
20
+ if (explicit)
21
+ return explicit;
22
+ for (const candidate of FALLBACK_CANDIDATES) {
23
+ if (binaryOnPath(candidate))
24
+ return candidate;
25
+ }
26
+ return "vi";
27
+ }
28
+ /** True when a binary lookups in the user's PATH. */
29
+ function binaryOnPath(name) {
30
+ const pathEnv = process.env["PATH"] || "";
31
+ const sep = process.platform === "win32" ? ";" : ":";
32
+ for (const dir of pathEnv.split(sep)) {
33
+ if (!dir)
34
+ continue;
35
+ if (existsSync(`${dir}/${name}`))
36
+ return true;
37
+ }
38
+ return false;
39
+ }
40
+ //# sourceMappingURL=editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor.js","sourceRoot":"","sources":["../src/editor.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,4EAA4E;AAC5E,6EAA6E;AAC7E,4EAA4E;AAC5E,2EAA2E;AAC3E,sCAAsC;AACtC,EAAE;AACF,6EAA6E;AAC7E,gCAAgC;AAEhC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAU,CAAC;AAE5D;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/E,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,KAAK,MAAM,SAAS,IAAI,mBAAmB,EAAE,CAAC;QAC5C,IAAI,YAAY,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;IAChD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,qDAAqD;AACrD,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC1C,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACrD,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,IAAI,UAAU,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC;IAChD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}