@lawrence369/loop-cli 0.1.0 → 0.1.2
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/CHANGELOG.md +8 -0
- package/dist/agent/pty-session.js +14 -7
- package/dist/bin/lclaude.js +0 -0
- package/dist/bin/lcodex.js +0 -0
- package/dist/bin/lgemini.js +0 -0
- package/dist/core/conversation.js +32 -8
- package/dist/index.js +0 -0
- package/dist/ui/interactive.js +6 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.2] - 2026-03-10
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Fix `posix_spawnp failed` crash: `@clack/prompts` placeholder text was leaking as actual CLI arguments
|
|
13
|
+
- Add try-catch around PTY spawn with clear error message when engine CLI is not found
|
|
14
|
+
- Add automatic fallback to non-interactive `engine.run()` when PTY spawn fails
|
|
15
|
+
|
|
8
16
|
## [0.1.0] - 2026-03-10
|
|
9
17
|
|
|
10
18
|
### Added
|
|
@@ -76,13 +76,20 @@ export class PtySession extends EventEmitter {
|
|
|
76
76
|
const rows = opts?.rows ?? process.stdout.rows ?? 24;
|
|
77
77
|
this._engine = opts?.engine;
|
|
78
78
|
this._promptPattern = DEFAULT_PROMPT_PATTERN;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
try {
|
|
80
|
+
this._pty = pty.spawn(command, args, {
|
|
81
|
+
name: "xterm-256color",
|
|
82
|
+
cols,
|
|
83
|
+
rows,
|
|
84
|
+
cwd,
|
|
85
|
+
env: { ...process.env, ...(opts?.env ?? {}) },
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
90
|
+
throw new Error(`Failed to spawn PTY for "${command}": ${msg}\n` +
|
|
91
|
+
`Ensure "${command}" is installed and available in your PATH.`);
|
|
92
|
+
}
|
|
86
93
|
// ── PTY data handler ──────────────────────────────────────────────
|
|
87
94
|
this._pty.onData((data) => {
|
|
88
95
|
if (!this._alive)
|
package/dist/bin/lclaude.js
CHANGED
|
File without changes
|
package/dist/bin/lcodex.js
CHANGED
|
File without changes
|
package/dist/bin/lgemini.js
CHANGED
|
File without changes
|
|
@@ -109,14 +109,38 @@ async function runPtySession(engine, initialPrompt, opts) {
|
|
|
109
109
|
// Set up renderer before spawning the PTY so the first frame is not missed
|
|
110
110
|
const renderer = new PtyRenderer(engine.name, engine.label);
|
|
111
111
|
renderer.start();
|
|
112
|
-
// Create PTY session via engine.interactive()
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
112
|
+
// Create PTY session via engine.interactive(), with fallback to engine.run()
|
|
113
|
+
let session;
|
|
114
|
+
try {
|
|
115
|
+
session = engine.interactive({
|
|
116
|
+
cwd: opts.cwd,
|
|
117
|
+
passthroughArgs: opts.passthroughArgs,
|
|
118
|
+
onData(data) {
|
|
119
|
+
renderer.write(data);
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
// PTY spawn failed — fall back to non-interactive engine.run()
|
|
125
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
126
|
+
renderer.stop({ elapsed: "0.0s", bytes: "0 B" });
|
|
127
|
+
console.log(dim(` PTY spawn failed: ${msg}`));
|
|
128
|
+
console.log(dim(` Falling back to non-interactive mode...\n`));
|
|
129
|
+
const start = Date.now();
|
|
130
|
+
const output = await engine.run(initialPrompt, {
|
|
131
|
+
cwd: opts.cwd,
|
|
132
|
+
verbose: opts.verbose,
|
|
133
|
+
passthroughArgs: opts.passthroughArgs,
|
|
134
|
+
onData(chunk) {
|
|
135
|
+
process.stdout.write(chunk);
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
output,
|
|
140
|
+
bytes: Buffer.byteLength(output),
|
|
141
|
+
durationMs: Date.now() - start,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
120
144
|
return new Promise((resolve, reject) => {
|
|
121
145
|
let done = false;
|
|
122
146
|
let idleTimer = null;
|
package/dist/index.js
CHANGED
|
File without changes
|
package/dist/ui/interactive.js
CHANGED
|
@@ -93,17 +93,20 @@ export async function interactive() {
|
|
|
93
93
|
return null;
|
|
94
94
|
}
|
|
95
95
|
// Native CLI flags (optional)
|
|
96
|
+
const PASS_ARGS_PLACEHOLDER = "e.g., --model claude-sonnet-4-20250514";
|
|
96
97
|
const passArgsInput = await p.text({
|
|
97
98
|
message: "Native CLI flags for executor (optional)",
|
|
98
|
-
placeholder:
|
|
99
|
+
placeholder: PASS_ARGS_PLACEHOLDER,
|
|
99
100
|
defaultValue: "",
|
|
100
101
|
});
|
|
101
102
|
if (p.isCancel(passArgsInput)) {
|
|
102
103
|
p.cancel("Cancelled.");
|
|
103
104
|
return null;
|
|
104
105
|
}
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
// Guard against @clack/prompts returning placeholder text as the value
|
|
107
|
+
const rawPassArgs = typeof passArgsInput === "string" ? passArgsInput : "";
|
|
108
|
+
const passthroughArgs = rawPassArgs.trim() && rawPassArgs.trim() !== PASS_ARGS_PLACEHOLDER
|
|
109
|
+
? rawPassArgs.split(/\s+/).filter(Boolean)
|
|
107
110
|
: [];
|
|
108
111
|
// Task
|
|
109
112
|
const task = await p.text({
|