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.
- package/README.md +168 -0
- package/bin/liner.js +126 -0
- package/dist/App.js +195 -0
- package/dist/App.js.map +1 -0
- package/dist/agents/detect.js +102 -0
- package/dist/agents/detect.js.map +1 -0
- package/dist/agents/events.js +8 -0
- package/dist/agents/events.js.map +1 -0
- package/dist/agents/jtbd-clarify.js +144 -0
- package/dist/agents/jtbd-clarify.js.map +1 -0
- package/dist/agents/parsers.js +316 -0
- package/dist/agents/parsers.js.map +1 -0
- package/dist/agents/prompts.js +301 -0
- package/dist/agents/prompts.js.map +1 -0
- package/dist/agents/run-log.js +71 -0
- package/dist/agents/run-log.js.map +1 -0
- package/dist/agents/runner.js +180 -0
- package/dist/agents/runner.js.map +1 -0
- package/dist/agents/types.js +2 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/bin-resolver.js +92 -0
- package/dist/bin-resolver.js.map +1 -0
- package/dist/browser-support.js +62 -0
- package/dist/browser-support.js.map +1 -0
- package/dist/colors.js +105 -0
- package/dist/colors.js.map +1 -0
- package/dist/components/Header.js +17 -0
- package/dist/components/Header.js.map +1 -0
- package/dist/components/KeyHints.js +10 -0
- package/dist/components/KeyHints.js.map +1 -0
- package/dist/components/LabeledBox.js +53 -0
- package/dist/components/LabeledBox.js.map +1 -0
- package/dist/components/NextSteps.js +18 -0
- package/dist/components/NextSteps.js.map +1 -0
- package/dist/config.js +88 -0
- package/dist/config.js.map +1 -0
- package/dist/editor.js +40 -0
- package/dist/editor.js.map +1 -0
- package/dist/gates.js +121 -0
- package/dist/gates.js.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/ipc.js +136 -0
- package/dist/ipc.js.map +1 -0
- package/dist/phases.js +348 -0
- package/dist/phases.js.map +1 -0
- package/dist/progress.js +185 -0
- package/dist/progress.js.map +1 -0
- package/dist/screens/AgentSetup.js +217 -0
- package/dist/screens/AgentSetup.js.map +1 -0
- package/dist/screens/BootSplash.js +159 -0
- package/dist/screens/BootSplash.js.map +1 -0
- package/dist/screens/BrowserSetup.js +181 -0
- package/dist/screens/BrowserSetup.js.map +1 -0
- package/dist/screens/CompileView.js +297 -0
- package/dist/screens/CompileView.js.map +1 -0
- package/dist/screens/FramingConfirmReview.js +193 -0
- package/dist/screens/FramingConfirmReview.js.map +1 -0
- package/dist/screens/Gate1Review.js +143 -0
- package/dist/screens/Gate1Review.js.map +1 -0
- package/dist/screens/Gate2Review.js +158 -0
- package/dist/screens/Gate2Review.js.map +1 -0
- package/dist/screens/ImportPrompt.js +65 -0
- package/dist/screens/ImportPrompt.js.map +1 -0
- package/dist/screens/LocalFilePicker.js +139 -0
- package/dist/screens/LocalFilePicker.js.map +1 -0
- package/dist/screens/NewMixtapeWizard.js +662 -0
- package/dist/screens/NewMixtapeWizard.js.map +1 -0
- package/dist/screens/NewProjectPrompt.js +66 -0
- package/dist/screens/NewProjectPrompt.js.map +1 -0
- package/dist/screens/PhaseRunner.js +1119 -0
- package/dist/screens/PhaseRunner.js.map +1 -0
- package/dist/screens/ProcessManifest.js +158 -0
- package/dist/screens/ProcessManifest.js.map +1 -0
- package/dist/screens/ProjectHub.js +546 -0
- package/dist/screens/ProjectHub.js.map +1 -0
- package/dist/screens/Settings.js +72 -0
- package/dist/screens/Settings.js.map +1 -0
- package/dist/screens/SourceModal.js +269 -0
- package/dist/screens/SourceModal.js.map +1 -0
- package/dist/screens/TapeBrowser.js +277 -0
- package/dist/screens/TapeBrowser.js.map +1 -0
- package/dist/screens/TapeDraftReview.js +440 -0
- package/dist/screens/TapeDraftReview.js.map +1 -0
- package/dist/screens/TapeEditor.js +310 -0
- package/dist/screens/TapeEditor.js.map +1 -0
- package/dist/screens/cassette-art.js +72 -0
- package/dist/screens/cassette-art.js.map +1 -0
- package/dist/store.js +10 -0
- package/dist/store.js.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/yaml-io.js +345 -0
- package/dist/yaml-io.js.map +1 -0
- 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"}
|