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
package/README.md ADDED
@@ -0,0 +1,168 @@
1
+ # Liner TUI
2
+
3
+ Interactive authoring UI for [Liner](../..) — create, edit, compile, and share mixtape **project folders** from the keyboard.
4
+
5
+ A *tape file* (`tape.yaml`) is the recipe. A *mixtape* is the project folder Liner builds from it. The TUI is for authoring: it scaffolds folders, edits tapes, surfaces the synthesis status, then runs `liner compile` to produce the consumable `MIXTAPE.md` + `sources/` artifacts.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ # One-shot:
11
+ npx linersh
12
+
13
+ # Persistent:
14
+ npm install -g linersh # adds `liner` to PATH
15
+ liner
16
+ ```
17
+
18
+ The npm package includes the TUI and resolves the Python core from a per-platform optional package (`linersh-<platform>-<arch>`). With no arguments, `liner` opens the TUI. CLI subcommands such as `liner setup-js`, `liner compile`, `liner share`, and `liner status` pass through to the bundled core.
19
+
20
+ For JavaScript-rendered pages, run the setup command from the same npm install:
21
+
22
+ ```sh
23
+ liner setup-js
24
+ ```
25
+
26
+ That command is idempotent and downloads the Playwright browser used for `render: js` and automatic JS-wall fallback. For debugging or local development, `LINER_BIN=/path/to/liner liner` overrides the bundled binary.
27
+
28
+ ## Run
29
+
30
+ ```sh
31
+ cd ~/projects/my-thing
32
+ liner
33
+ ```
34
+
35
+ The TUI looks for (and creates) a `./mixtapes/` folder in your current directory. Each subdirectory inside it is a mixtape project folder (must contain a `tape.yaml`).
36
+
37
+ Override the workspace with `LINER_DIR`:
38
+
39
+ ```sh
40
+ LINER_DIR=~/mixtapes liner
41
+ ```
42
+
43
+ ## Keys
44
+
45
+ ### Browser
46
+ | Key | Action |
47
+ |---|---|
48
+ | `↑` `↓` or `j` `k` | move selection |
49
+ | `enter` or `e` | open in editor |
50
+ | `n` | new mixtape (prompts for slug) |
51
+ | `c` | compile selected |
52
+ | `s` | share selected (zip to `.mixtape`) |
53
+ | `i` | import a `.mixtape` archive |
54
+ | `d` | delete selected folder (with confirm) |
55
+ | `r` | refresh |
56
+ | `q` | quit |
57
+
58
+ ### Editor
59
+ | Key | Action |
60
+ |---|---|
61
+ | `t` `d` `u` | edit title / description / curator |
62
+ | `m` | toggle mode (`quick` ⇄ `methodology`) |
63
+ | `g` | edit JTBD (job-to-be-done) |
64
+ | `E` | open `synthesis.md` in `$EDITOR` |
65
+ | `↑` `↓` | select source |
66
+ | `a` `e` `x` | add / edit / remove source |
67
+ | `K` `J` | reorder selected source up/down |
68
+ | `s` | save `tape.yaml` |
69
+ | `c` | save + compile (blocked if synthesis is empty or still a placeholder) |
70
+ | `esc` | back (warns on unsaved changes) |
71
+
72
+ A soft warning appears when any source is missing a curator note. Notes are load-bearing — see [CURATION.md](https://liner.sh) §6.
73
+
74
+ #### Local sources
75
+
76
+ `local_file` sources reference content you've placed under `<project>/personal/` — book chapter PDFs, Reader-Mode-saved articles, exported HIG pages. Use them for material that isn't on the public web or that lives behind a paywall you have legitimate access to.
77
+
78
+ Workflow:
79
+
80
+ 1. Save the file into `<project>/personal/`. The TUI creates the folder lazily — the file picker offers a "new file (paste path)" option that you can use after saving the file manually with your OS file manager.
81
+ 2. Add a source in the editor; press `T` in the source modal to switch the type to `local_file`.
82
+ 3. Pick the file via the picker; supply a `citation` (author, title, publication, date — the AI uses this to weight the source).
83
+ 4. Compile. The CLI extracts the content (PDFs via `pdfplumber`, HTML via the same readability pipeline as web sources, `.md`/`.txt` as-is).
84
+
85
+ `liner share` includes `personal/` by default. Pass `--no-personal` (or use the corresponding TUI flag) to exclude it — required for any tape intended as a library submission.
86
+
87
+ The synthesis indicator shows one of:
88
+ - `✗ missing` — `liner compile` will hard-fail
89
+ - `⚠ placeholder` — file exists but still has the boilerplate from `liner init`
90
+ - `⚠ empty` — file exists but is empty
91
+ - `✓ N chars` — ready to compile
92
+
93
+ ### Source modal
94
+ | Key | Action |
95
+ |---|---|
96
+ | `↑` `↓` | move between fields |
97
+ | `enter` | edit the focused field (or open the file picker on the path field for `local_file` sources) |
98
+ | `tab` | next field while editing |
99
+ | `T` | toggle source type (`web` ⇄ `local_file`) |
100
+ | `R` | toggle `render: server` ⇄ `render: js` (web sources only) |
101
+ | `p` | toggle required/optional |
102
+ | `s` | save source |
103
+ | `esc` | cancel |
104
+
105
+ For `web` sources the URL is the load-bearing field; the type is auto-detected as `youtube` or `web`. For `local_file` sources the modal shows `path`, `citation`, `note`, `section`, `priority` — pick the path via the file picker (lists files under `personal/`, with a "paste path" affordance for new files).
106
+
107
+ ### Compile
108
+ | Key | Action |
109
+ |---|---|
110
+ | `esc` | cancel running compile |
111
+ | `y` | copy compiled `MIXTAPE.md` to clipboard (`pbcopy` / `xclip`) |
112
+ | `s` | share the compiled mixtape (zip to `.mixtape`) |
113
+ | `r` | re-run compile |
114
+ | `b` | back to editor |
115
+
116
+ The compile writes `MIXTAPE.md` and `sources/NN-<slug>.md` files directly into the project folder. There's nothing to "save" afterwards — the artifacts are already on disk.
117
+
118
+ ## Uninstalling
119
+
120
+ The TUI ships in the same `linersh` package as the CLI. The cleanest path is the repo's uninstaller:
121
+
122
+ ```sh
123
+ ./scripts/uninstall.sh # interactive
124
+ ./scripts/uninstall.sh --yes # no prompts
125
+ ```
126
+
127
+ It removes the optional Playwright browser, the local cache, any pipx-installed CLI, and any globally installed TUI (`npm install -g linersh`). Your mixtape project folders are left alone unless you pass `--purge-workspace`. See the root [README — Uninstalling](../../README.md#uninstalling) for the manual recipe.
128
+
129
+ ## Develop
130
+
131
+ ```sh
132
+ cd packages/tui
133
+ npm install
134
+ npm run dev # tsx (no watch — Ctrl+C and re-run after code changes)
135
+ npm run dev:built # builds, then runs the compiled output (closer to ship)
136
+ npm run typecheck
137
+ npm test # vitest
138
+ npm run build # emits dist/
139
+ ```
140
+
141
+ **Why no `tsx watch`?** `tsx watch` keeps the TTY in raw mode for its own restart UI (Enter to restart, "rs" to force, Esc to clear). Ink TUIs also want raw stdin, and the two collide — typed characters get eaten, the app gets surprise-restarted, screens flicker. For interactive UIs, restart manually after code changes.
142
+
143
+ In dev, the bin-resolver tries `LINER_BIN`, the bundled optional package, the repo-local `.venv/bin/liner`, then `liner` on `$PATH`. To point at a specific binary:
144
+
145
+ ```sh
146
+ LINER_BIN=/path/to/liner npm run dev
147
+ ```
148
+
149
+ ## Architecture
150
+
151
+ ```
152
+ TapeBrowser ────► NewProjectPrompt ─┐
153
+ │ │
154
+ │ ImportPrompt ─────┤
155
+ │ │
156
+ ├────► TapeEditor ⇄ SourceModal
157
+ │ │
158
+ │ ▼
159
+ │ CompileView (streams NDJSON from `liner compile --emit-events`)
160
+ │
161
+ └────► (share/delete inline)
162
+ ```
163
+
164
+ State is global via `AppContext`. YAML is parsed and re-written in Node via the `yaml` package's `Document` API so user-written block comments survive edits. All folder operations (`init`, `compile`, `share`, `import`, `list`) go through the Python CLI as subprocesses — the TUI never reimplements them.
165
+
166
+ The CLI's `compile --emit-events` streams NDJSON progress events to stdout; the TUI parses each line and updates per-source state live. The final `result` event reports the produced folder, `MIXTAPE.md` path, and per-source file paths.
167
+
168
+ The same NDJSON protocol will power the planned MCP server, which calls into the same Python core directly without going through the CLI.
package/bin/liner.js ADDED
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+ // Entry shim for `npx linersh` / `liner`.
3
+ //
4
+ // No args: launch the Ink TUI from dist/.
5
+ // CLI args: forward to the bundled Python core (or LINER_BIN/dev fallback).
6
+ //
7
+ // This lets the npm package expose one binary name while still supporting
8
+ // mechanical commands like `liner setup-js`, `liner compile`, and `liner status`.
9
+ import { fileURLToPath } from "node:url";
10
+ import { createRequire } from "node:module";
11
+ import { spawn, spawnSync } from "node:child_process";
12
+ import { existsSync } from "node:fs";
13
+ import { dirname, join } from "node:path";
14
+
15
+ const here = dirname(fileURLToPath(import.meta.url));
16
+ const packageRoot = join(here, "..");
17
+ const argv = process.argv.slice(2);
18
+
19
+ const CLI_COMMANDS = new Set([
20
+ "init",
21
+ "replay",
22
+ "compile",
23
+ "share",
24
+ "import",
25
+ "clone",
26
+ "setup-js",
27
+ "list",
28
+ "cache",
29
+ "manifest",
30
+ "status",
31
+ ]);
32
+
33
+ function wantsCli(args) {
34
+ if (args.length === 0) return false;
35
+ const first = args[0];
36
+ return first === "--help" || first === "--version" || first === "-h" || CLI_COMMANDS.has(first);
37
+ }
38
+
39
+ if (wantsCli(argv)) {
40
+ const resolved = resolveCliBinary();
41
+ const child = spawn(resolved.command, [...resolved.args, ...argv], {
42
+ stdio: "inherit",
43
+ env: { ...process.env, LINER_TUI_SHIM: "1" },
44
+ });
45
+ child.on("exit", (code, signal) => {
46
+ if (signal) {
47
+ process.kill(process.pid, signal);
48
+ return;
49
+ }
50
+ process.exit(code ?? 1);
51
+ });
52
+ } else {
53
+ await import(join(packageRoot, "dist", "index.js"));
54
+ }
55
+
56
+ function resolveCliBinary() {
57
+ const envBin = process.env.LINER_BIN;
58
+ if (envBin && existsSync(envBin)) {
59
+ return { command: envBin, args: [] };
60
+ }
61
+
62
+ const bundled = findBundledBinary();
63
+ if (bundled) return { command: bundled, args: [] };
64
+
65
+ const repoVenv = findRepoVenvBinary();
66
+ if (repoVenv) return { command: repoVenv, args: [] };
67
+
68
+ const pathHit = findPathBinary();
69
+ if (pathHit) return { command: pathHit, args: [] };
70
+
71
+ console.error(
72
+ [
73
+ "Could not find the Liner CLI binary.",
74
+ "",
75
+ "Expected one of:",
76
+ " - LINER_BIN=/path/to/liner",
77
+ " - bundled platform package (linersh-<platform>-<arch>)",
78
+ " - repo-local .venv/bin/liner",
79
+ " - a separate liner on PATH",
80
+ ].join("\n"),
81
+ );
82
+ process.exit(1);
83
+ }
84
+
85
+ function findBundledBinary() {
86
+ const require = createRequire(import.meta.url);
87
+ const target = `linersh-${process.platform}-${process.arch}`;
88
+ try {
89
+ const pkgJson = require.resolve(`${target}/package.json`);
90
+ const baseDir = pkgJson.replace(/[\\/]package\.json$/, "");
91
+ const candidate = join(baseDir, exeName());
92
+ return existsSync(candidate) ? candidate : null;
93
+ } catch {
94
+ return null;
95
+ }
96
+ }
97
+
98
+ function findRepoVenvBinary() {
99
+ const venvBinDir = process.platform === "win32" ? "Scripts" : "bin";
100
+ let dir = packageRoot;
101
+ for (let i = 0; i < 12; i += 1) {
102
+ const candidate = join(dir, ".venv", venvBinDir, exeName());
103
+ if (existsSync(candidate)) return candidate;
104
+ const parent = dirname(dir);
105
+ if (parent === dir) break;
106
+ dir = parent;
107
+ }
108
+ return null;
109
+ }
110
+
111
+ function findPathBinary() {
112
+ const probe = spawnSync(process.platform === "win32" ? "where" : "which", ["liner"], {
113
+ encoding: "utf8",
114
+ });
115
+ if (probe.status !== 0) return null;
116
+ const hits = probe.stdout
117
+ .split(/\r?\n/)
118
+ .map((s) => s.trim())
119
+ .filter(Boolean);
120
+ const thisShim = fileURLToPath(import.meta.url);
121
+ return hits.find((hit) => hit !== thisShim) ?? null;
122
+ }
123
+
124
+ function exeName() {
125
+ return process.platform === "win32" ? "liner.exe" : "liner";
126
+ }
package/dist/App.js ADDED
@@ -0,0 +1,195 @@
1
+ import React, { useCallback, useEffect, useRef, useState } from "react";
2
+ import { Box, Text } from "ink";
3
+ import { AppContext } from "./store.js";
4
+ import { ERROR } from "./colors.js";
5
+ import { TapeBrowser } from "./screens/TapeBrowser.js";
6
+ import { TapeEditor } from "./screens/TapeEditor.js";
7
+ import { SourceModal } from "./screens/SourceModal.js";
8
+ import { CompileView } from "./screens/CompileView.js";
9
+ import { NewMixtapeWizard } from "./screens/NewMixtapeWizard.js";
10
+ import { ProjectHub } from "./screens/ProjectHub.js";
11
+ import { PhaseRunner } from "./screens/PhaseRunner.js";
12
+ import { FramingConfirmReview } from "./screens/FramingConfirmReview.js";
13
+ import { Gate1Review } from "./screens/Gate1Review.js";
14
+ import { Gate2Review } from "./screens/Gate2Review.js";
15
+ import { TapeDraftReview } from "./screens/TapeDraftReview.js";
16
+ import { ImportPrompt } from "./screens/ImportPrompt.js";
17
+ import { AgentSetup } from "./screens/AgentSetup.js";
18
+ import { ProcessManifest } from "./screens/ProcessManifest.js";
19
+ import { BootSplash, shouldShowSplash } from "./screens/BootSplash.js";
20
+ import { configExists } from "./config.js";
21
+ import { detectAgents } from "./agents/detect.js";
22
+ import { listProjects } from "./ipc.js";
23
+ import { existsSync, mkdirSync } from "node:fs";
24
+ import { join } from "node:path";
25
+ const NOTIFICATION_MS = 3500;
26
+ /** Cap on the back stack — deeper history isn't useful and just costs memory. */
27
+ const HISTORY_LIMIT = 10;
28
+ export function App() {
29
+ const baseDir = useRef(resolveBaseDir()).current;
30
+ // Decide what the user sees first. Three states, in priority order:
31
+ //
32
+ // 1. Agent setup — first launch on a machine with multiple agents
33
+ // installed AND no config file yet. Force a choice before the splash
34
+ // so the methodology phases don't hit the mid-phase picker.
35
+ // 2. Splash — the normal greeting (cassette art + launch menu).
36
+ // 3. Browser — fallback when stdout isn't a TTY or LINER_NO_SPLASH is
37
+ // set (CI runs, narrow terminals).
38
+ //
39
+ // Single-agent installs skip step 1: there's nothing to pick. Repeat runs
40
+ // skip it too because config.yaml exists.
41
+ const initialScreen = resolveInitialScreen();
42
+ const [screen, setScreen] = useState(initialScreen);
43
+ const [history, setHistory] = useState([]);
44
+ const [projects, setProjects] = useState([]);
45
+ const [notification, setNotification] = useState(null);
46
+ const [bootError, setBootError] = useState(null);
47
+ // navigate(next) pushes the current screen onto a back stack and replaces
48
+ // it with `next`. Side effect: clears the terminal scrollback when the
49
+ // user moves between meaningfully different surfaces (any kind change, or
50
+ // a phaseRunner re-entry with a different phaseId). Clearing happens
51
+ // before setScreen so Ink's next render lands on a fresh canvas — vs.
52
+ // clearing after, which would briefly show then erase the new screen.
53
+ const navigate = useCallback((next) => {
54
+ setScreen((current) => {
55
+ setHistory((h) => [...h, current].slice(-HISTORY_LIMIT));
56
+ if (process.stdout.isTTY && shouldClearOnNavigate(current, next)) {
57
+ process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
58
+ }
59
+ return next;
60
+ });
61
+ }, []);
62
+ // back() pops the history stack and navigates to the prior screen. When
63
+ // the stack is empty (root) it falls back to the browser. We pop INSIDE
64
+ // setScreen so the two updates batch and Ink renders once.
65
+ const back = useCallback(() => {
66
+ setHistory((h) => {
67
+ if (h.length === 0) {
68
+ if (process.stdout.isTTY)
69
+ process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
70
+ setScreen({ kind: "browser" });
71
+ return h;
72
+ }
73
+ const prev = h[h.length - 1];
74
+ if (process.stdout.isTTY)
75
+ process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
76
+ setScreen(prev);
77
+ return h.slice(0, -1);
78
+ });
79
+ }, []);
80
+ const refreshProjects = useCallback(async () => {
81
+ try {
82
+ const result = await listProjects(baseDir);
83
+ setProjects(result);
84
+ }
85
+ catch (e) {
86
+ setBootError(e.message);
87
+ }
88
+ }, [baseDir]);
89
+ useEffect(() => {
90
+ void refreshProjects();
91
+ }, [refreshProjects]);
92
+ useEffect(() => {
93
+ if (!notification)
94
+ return;
95
+ const timer = setTimeout(() => setNotification(null), NOTIFICATION_MS);
96
+ return () => clearTimeout(timer);
97
+ }, [notification]);
98
+ if (bootError) {
99
+ return (React.createElement(Box, { flexDirection: "column" },
100
+ React.createElement(Text, { color: ERROR }, bootError),
101
+ React.createElement(Text, { dimColor: true }, "Press Ctrl+C to exit.")));
102
+ }
103
+ const ctx = {
104
+ screen,
105
+ baseDir,
106
+ projects,
107
+ notification,
108
+ navigate,
109
+ back,
110
+ refreshProjects,
111
+ setNotification,
112
+ };
113
+ return (React.createElement(AppContext.Provider, { value: ctx },
114
+ React.createElement(ScreenRoot, { screen: screen })));
115
+ }
116
+ function ScreenRoot({ screen }) {
117
+ switch (screen.kind) {
118
+ case "splash":
119
+ return React.createElement(BootSplash, null);
120
+ case "agentSetup":
121
+ return React.createElement(AgentSetup, null);
122
+ case "browser":
123
+ return React.createElement(TapeBrowser, null);
124
+ case "newProject":
125
+ return React.createElement(NewMixtapeWizard, null);
126
+ case "hub":
127
+ return React.createElement(ProjectHub, { tape: screen.tape, folder: screen.folder });
128
+ case "phaseRunner":
129
+ // `key` forces React to unmount and remount when phaseId changes —
130
+ // PhaseRunner's auto-launch effect has empty deps and only fires on
131
+ // initial mount, so without the key, navigating Phase 2 → Phase 4 (both
132
+ // "phaseRunner" kind) leaves the component dormant: same DOM, no
133
+ // agent launch. The remount is also what resets the tool list,
134
+ // finalText, and stats from the previous phase.
135
+ return (React.createElement(PhaseRunner, { key: screen.phaseId, tape: screen.tape, folder: screen.folder, phaseId: screen.phaseId }));
136
+ case "gate0":
137
+ return React.createElement(FramingConfirmReview, { tape: screen.tape, folder: screen.folder });
138
+ case "gate1":
139
+ return React.createElement(Gate1Review, { tape: screen.tape, folder: screen.folder });
140
+ case "gate2":
141
+ return React.createElement(Gate2Review, { tape: screen.tape, folder: screen.folder });
142
+ case "tapeDraft":
143
+ return React.createElement(TapeDraftReview, { tape: screen.tape, folder: screen.folder });
144
+ case "import":
145
+ return React.createElement(ImportPrompt, null);
146
+ case "editor":
147
+ return React.createElement(TapeEditor, { tape: screen.tape, folder: screen.folder });
148
+ case "sourceModal":
149
+ return (React.createElement(SourceModal, { tape: screen.tape, folder: screen.folder, editingIndex: screen.editingIndex }));
150
+ case "compile":
151
+ return React.createElement(CompileView, { tape: screen.tape, folder: screen.folder });
152
+ case "manifest":
153
+ return React.createElement(ProcessManifest, { tape: screen.tape, folder: screen.folder });
154
+ }
155
+ }
156
+ /**
157
+ * Pick the screen the user lands on at startup. See the inline doc in
158
+ * `App()` for the priority order.
159
+ */
160
+ function resolveInitialScreen() {
161
+ // If no splash (non-TTY / narrow / opted-out), don't show setup either —
162
+ // the user is probably in CI or scripting and just wants the browser.
163
+ if (!shouldShowSplash())
164
+ return { kind: "browser" };
165
+ // First-run setup only when we have a real choice to surface. One agent
166
+ // (or zero) → just go to splash; setup would be a no-op.
167
+ if (!configExists()) {
168
+ const installed = detectAgents();
169
+ if (installed.length >= 2)
170
+ return { kind: "agentSetup" };
171
+ }
172
+ return { kind: "splash" };
173
+ }
174
+ /**
175
+ * Decide whether navigation `prev → next` should clear the terminal. We clear
176
+ * on any kind change, and on phaseRunner re-entry with a different phaseId so
177
+ * "entering Phase 3" feels like starting fresh, not stacking on top of Phase 2.
178
+ */
179
+ function shouldClearOnNavigate(prev, next) {
180
+ if (prev.kind !== next.kind)
181
+ return true;
182
+ if (prev.kind === "phaseRunner" && next.kind === "phaseRunner") {
183
+ return prev.phaseId !== next.phaseId;
184
+ }
185
+ return false;
186
+ }
187
+ function resolveBaseDir() {
188
+ const override = process.env["LINER_DIR"];
189
+ const dir = override || join(process.cwd(), "mixtapes");
190
+ if (!existsSync(dir)) {
191
+ mkdirSync(dir, { recursive: true });
192
+ }
193
+ return dir;
194
+ }
195
+ //# sourceMappingURL=App.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App.js","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ /**
5
+ * Resolve an agent binary path. Resolution order:
6
+ *
7
+ * 1. Explicit env override (`LINER_CLAUDE_BIN` / `LINER_CODEX_BIN`).
8
+ * Honored even if the path is missing — we surface the path the user
9
+ * named so they can see exactly what was tried.
10
+ * 2. `which <name>` against the inherited PATH. Fast and correct for the
11
+ * common case (user launched the TUI from their normal shell).
12
+ * 3. `bash -lc 'command -v <name>'` against the *login* shell. Costs ~50ms
13
+ * but sources the user's shell init (.bashrc / .zshrc / fnm / nvm /
14
+ * asdf), catching version-manager shims that aren't in the TUI's
15
+ * inherited PATH — e.g. cmux-launched sessions where fnm hasn't run.
16
+ *
17
+ * Returning null means "definitely not installed" — both shells failed and
18
+ * no env override was set.
19
+ */
20
+ function resolveBin(name, envVar) {
21
+ const override = process.env[envVar];
22
+ if (override && override.trim())
23
+ return override.trim();
24
+ const direct = spawnSync("which", [name], { encoding: "utf8" });
25
+ if (direct.status === 0) {
26
+ const out = direct.stdout.trim();
27
+ if (out)
28
+ return out;
29
+ }
30
+ // Fall back to a login shell so shims (fnm / nvm / asdf / pyenv / etc.)
31
+ // get a chance to register. `command -v` is POSIX-portable; `-l` makes
32
+ // bash source the login profile that puts those shims on PATH.
33
+ const viaLoginShell = spawnSync("bash", ["-lc", `command -v ${name}`], {
34
+ encoding: "utf8",
35
+ });
36
+ if (viaLoginShell.status === 0) {
37
+ const out = viaLoginShell.stdout.trim();
38
+ if (out)
39
+ return out;
40
+ }
41
+ return null;
42
+ }
43
+ /**
44
+ * Names the env var that overrides binary detection for an agent. Mirrors
45
+ * the LINER_BIN pattern from bin-resolver — explicit > inferred.
46
+ */
47
+ export function envVarForAgent(id) {
48
+ return id === "claude" ? "LINER_CLAUDE_BIN" : "LINER_CODEX_BIN";
49
+ }
50
+ /** Catalog of agents the TUI knows how to drive. Single source of truth so
51
+ * `detectAgents` and the "not detected" UI in AgentSetup stay in sync. */
52
+ export const KNOWN_AGENTS = [
53
+ { id: "claude", name: "Claude Code", envVar: "LINER_CLAUDE_BIN" },
54
+ { id: "codex", name: "OpenAI Codex", envVar: "LINER_CODEX_BIN" },
55
+ ];
56
+ export function detectAgents() {
57
+ const found = [];
58
+ for (const meta of KNOWN_AGENTS) {
59
+ const bin = resolveBin(meta.id, meta.envVar);
60
+ if (bin)
61
+ found.push({ id: meta.id, name: meta.name, bin });
62
+ }
63
+ return found;
64
+ }
65
+ /**
66
+ * Find the curating-mixtapes skill bundle on disk.
67
+ *
68
+ * Resolution order:
69
+ * 1. `LINER_SKILL_PATH` env var (must contain SKILL.md).
70
+ * 2. Walk up from cwd looking for `cli-update-docs/SKILL.md` or
71
+ * `skills/curating-mixtapes/SKILL.md`.
72
+ * 3. Walk up from the TUI binary location (covers the dev/global-install case).
73
+ */
74
+ export function resolveSkillPath() {
75
+ const env = process.env["LINER_SKILL_PATH"];
76
+ if (env && existsSync(join(env, "SKILL.md")))
77
+ return env;
78
+ const candidates = ["cli-update-docs", "skills/curating-mixtapes"];
79
+ const searchFromRoots = [process.cwd(), dirname(process.argv[1] ?? "")].filter(Boolean);
80
+ for (const root of searchFromRoots) {
81
+ const hit = walkUp(root, candidates);
82
+ if (hit)
83
+ return hit;
84
+ }
85
+ return null;
86
+ }
87
+ function walkUp(start, candidates) {
88
+ let dir = start;
89
+ for (let i = 0; i < 12; i++) {
90
+ for (const rel of candidates) {
91
+ const guess = join(dir, rel);
92
+ if (existsSync(join(guess, "SKILL.md")))
93
+ return guess;
94
+ }
95
+ const parent = dirname(dir);
96
+ if (parent === dir)
97
+ return null;
98
+ dir = parent;
99
+ }
100
+ return null;
101
+ }
102
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/agents/detect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ // Normalized event stream used by the TUI to render agent progress.
2
+ //
3
+ // Both Claude (`--output-format stream-json --verbose`) and Codex (`--json`)
4
+ // emit newline-delimited JSON. Their schemas differ; per-CLI parsers in this
5
+ // directory convert each line into one of the AgentEvent variants below so
6
+ // the PhaseRunner UI is agent-agnostic.
7
+ export {};
8
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/agents/events.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,2EAA2E;AAC3E,wCAAwC"}