@fnclaude/cli 0.7.2 → 0.7.3
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 +215 -27
- package/package.json +1 -1
- package/src/argParser.ts +19 -32
- package/src/args/preserve.ts +2 -2
- package/src/args.ts +200 -23
- package/src/argv.ts +18 -19
- package/src/config.ts +85 -52
- package/src/help.ts +1 -1
- package/src/hostAliases.ts +40 -15
- package/src/index.ts +11 -5
- package/src/main.ts +209 -88
- package/src/mcp/client.ts +12 -9
- package/src/mcp/protocol.ts +66 -26
- package/src/mcp/socketListener.ts +20 -10
- package/src/passthrough.ts +36 -0
- package/src/paths.ts +31 -0
- package/src/prompts.ts +5 -12
- package/src/pty/unix.ts +250 -107
- package/src/pty.ts +20 -13
- package/src/repoSettings.ts +35 -11
- package/src/silentRelaunch.ts +3 -3
- package/src/spawn.ts +2 -28
- package/src/warnings.ts +15 -31
- package/src/worktree.ts +57 -43
package/src/argv.ts
CHANGED
|
@@ -16,22 +16,22 @@
|
|
|
16
16
|
// 4. System-prompt fragment injection: --append-system-prompt <merged-text>
|
|
17
17
|
// composed by selectFragments + withAppendedSystemPrompts.
|
|
18
18
|
|
|
19
|
-
import { existsSync
|
|
19
|
+
import { existsSync } from 'node:fs';
|
|
20
20
|
import { isAbsolute, join } from 'node:path';
|
|
21
|
-
import
|
|
22
|
-
import type { Args } from './args.js';
|
|
21
|
+
import type { InterceptedArgs } from './args.js';
|
|
23
22
|
import {
|
|
24
23
|
nameInPassthrough as _nameInPassthrough,
|
|
25
24
|
settingSourcesInPassthrough,
|
|
26
25
|
tokenInPassthrough,
|
|
27
|
-
} from './
|
|
26
|
+
} from './passthrough.js';
|
|
28
27
|
import type { Config } from './config.js';
|
|
28
|
+
import { resolveSelfPath } from './paths.js';
|
|
29
29
|
import { isInteractiveSession, selectFragments, type PromptSet } from './prompts.js';
|
|
30
30
|
|
|
31
31
|
// Re-export the passthrough inspection helpers from their canonical home so
|
|
32
32
|
// callers can reach them via "./argv.js" — mirrors the Go reference where
|
|
33
33
|
// they sit next to buildArgv. The single-source-of-truth implementation
|
|
34
|
-
// stays in
|
|
34
|
+
// stays in passthrough.ts.
|
|
35
35
|
export { settingSourcesInPassthrough, tokenInPassthrough };
|
|
36
36
|
|
|
37
37
|
// ── MCP self-injection ─────────────────────────────────────────────────────
|
|
@@ -58,14 +58,7 @@ interface McpConfigEntry {
|
|
|
58
58
|
* → repo/bin/fnclaude symlink resolves to the real binary path).
|
|
59
59
|
*/
|
|
60
60
|
export function buildFnclaudeMCPConfigJSON(noop: boolean): string | null {
|
|
61
|
-
const
|
|
62
|
-
let exe = argv1 !== undefined && argv1 !== '' ? argv1 : process.execPath;
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
exe = realpathSync(exe);
|
|
66
|
-
} catch {
|
|
67
|
-
// Fall back to unresolved path; symlink resolution failure isn't fatal.
|
|
68
|
-
}
|
|
61
|
+
const exe = resolveSelfPath();
|
|
69
62
|
|
|
70
63
|
const args = ['mcp'];
|
|
71
64
|
if (noop) args.push('--noop');
|
|
@@ -123,17 +116,23 @@ export function withAppendedSystemPrompts(
|
|
|
123
116
|
// ── buildArgv ──────────────────────────────────────────────────────────────
|
|
124
117
|
|
|
125
118
|
/**
|
|
126
|
-
* buildArgv constructs the argv slice to exec claude with, given the
|
|
127
|
-
* fnclaude args
|
|
128
|
-
*
|
|
129
|
-
* the
|
|
119
|
+
* buildArgv constructs the argv slice to exec claude with, given the
|
|
120
|
+
* fnclaude args at their final pipeline stage (`InterceptedArgs` — the
|
|
121
|
+
* intercept must have run so `worktreeMatched` is meaningful), the user's
|
|
122
|
+
* shell cwd (used to resolve relative extra-dir paths), the loaded config,
|
|
123
|
+
* and the set of prompt fragments loaded from the install dir.
|
|
124
|
+
*
|
|
125
|
+
* Accepting `InterceptedArgs` makes the ordering invariant a compile-time
|
|
126
|
+
* check: passing a `ParsedArgs` or `ResolvedArgs` is a type error,
|
|
127
|
+
* preventing the auto-tmux gate from reading a stale `worktreeMatched`
|
|
128
|
+
* value the parse stage couldn't know.
|
|
130
129
|
*
|
|
131
130
|
* `shellCWD` is the process working directory at fnclaude startup —
|
|
132
131
|
* normally `process.cwd()`. It's threaded through (rather than reached for
|
|
133
132
|
* directly) so tests can pin it without `chdir`-ing.
|
|
134
133
|
*/
|
|
135
134
|
export function buildArgv(
|
|
136
|
-
a:
|
|
135
|
+
a: InterceptedArgs,
|
|
137
136
|
shellCWD: string,
|
|
138
137
|
cfg: Config,
|
|
139
138
|
prompts: PromptSet,
|
|
@@ -200,5 +199,5 @@ export function buildArgv(
|
|
|
200
199
|
}
|
|
201
200
|
|
|
202
201
|
// Re-export so callers that already import nameInPassthrough from argv.ts
|
|
203
|
-
// (parity with the Go file layout) don't have to reach into
|
|
202
|
+
// (parity with the Go file layout) don't have to reach into passthrough.
|
|
204
203
|
export const nameInPassthrough = _nameInPassthrough;
|
package/src/config.ts
CHANGED
|
@@ -22,7 +22,14 @@ function home(): string {
|
|
|
22
22
|
// ── public types ───────────────────────────────────────────────────────────
|
|
23
23
|
|
|
24
24
|
export type TmuxMode = 'never' | 'worktree';
|
|
25
|
-
|
|
25
|
+
/**
|
|
26
|
+
* `'never'`, `'ask'`, or a non-negative integer-as-string (e.g. `'5'`).
|
|
27
|
+
*
|
|
28
|
+
* The template-literal `${number}` variant narrows correctly: a bare
|
|
29
|
+
* `string` would collapse the union, so runtime validation gates env-var
|
|
30
|
+
* and config-file inputs into this type via `normalizeHandoffMode`.
|
|
31
|
+
*/
|
|
32
|
+
export type HandoffMode = 'never' | 'ask' | `${number}`;
|
|
26
33
|
|
|
27
34
|
export interface NameConfig {
|
|
28
35
|
/** Model used for the noop name session. */
|
|
@@ -121,34 +128,48 @@ export function parseBoolEnv(v: string): boolean {
|
|
|
121
128
|
}
|
|
122
129
|
}
|
|
123
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Result of a normalize-mode call: the validated value plus an optional
|
|
133
|
+
* warning describing any fallback that was applied. Callers thread the
|
|
134
|
+
* warning into their own returned warnings list rather than mutating a
|
|
135
|
+
* module-global sink.
|
|
136
|
+
*/
|
|
137
|
+
export interface NormalizeResult<T> {
|
|
138
|
+
value: T;
|
|
139
|
+
warning: string | null;
|
|
140
|
+
}
|
|
141
|
+
|
|
124
142
|
/**
|
|
125
143
|
* normalizeTmuxMode validates against the supported set and falls back to
|
|
126
|
-
* "never" for anything else,
|
|
127
|
-
* empty-string case
|
|
144
|
+
* "never" for anything else, returning the fallback value and an optional
|
|
145
|
+
* warning describing what was rejected (the empty-string case is the
|
|
146
|
+
* absent-value default path and produces no warning).
|
|
128
147
|
*/
|
|
129
|
-
export function normalizeTmuxMode(v: string): TmuxMode {
|
|
130
|
-
if (v === 'never' || v === 'worktree') return v;
|
|
131
|
-
if (v === '') return 'never';
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
148
|
+
export function normalizeTmuxMode(v: string): NormalizeResult<TmuxMode> {
|
|
149
|
+
if (v === 'never' || v === 'worktree') return { value: v, warning: null };
|
|
150
|
+
if (v === '') return { value: 'never', warning: null };
|
|
151
|
+
return {
|
|
152
|
+
value: 'never',
|
|
153
|
+
warning: `fnclaude: auto.tmux=${JSON.stringify(v)} is not a valid mode (use "never" or "worktree"), falling back to "never"`,
|
|
154
|
+
};
|
|
136
155
|
}
|
|
137
156
|
|
|
138
157
|
/**
|
|
139
158
|
* normalizeHandoffMode validates against the supported set and falls back
|
|
140
|
-
* to "ask" for anything else (with
|
|
141
|
-
* Valid: "never", "ask", or a non-negative integer (as a string).
|
|
159
|
+
* to "ask" for anything else (with an optional warning, except empty
|
|
160
|
+
* string). Valid: "never", "ask", or a non-negative integer (as a string).
|
|
142
161
|
*/
|
|
143
|
-
export function normalizeHandoffMode(v: string): HandoffMode {
|
|
144
|
-
if (v === 'never' || v === 'ask') return v;
|
|
145
|
-
if (v === '') return 'ask';
|
|
146
|
-
// Non-negative integer (no decimal, no unit).
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
162
|
+
export function normalizeHandoffMode(v: string): NormalizeResult<HandoffMode> {
|
|
163
|
+
if (v === 'never' || v === 'ask') return { value: v, warning: null };
|
|
164
|
+
if (v === '') return { value: 'ask', warning: null };
|
|
165
|
+
// Non-negative integer (no decimal, no unit). The regex guarantees the
|
|
166
|
+
// template-literal shape, which TS's type narrowing can't infer from a
|
|
167
|
+
// .test() call alone — so assert it explicitly once.
|
|
168
|
+
if (/^\d+$/.test(v)) return { value: v as `${number}`, warning: null };
|
|
169
|
+
return {
|
|
170
|
+
value: 'ask',
|
|
171
|
+
warning: `fnclaude: auto.handoff=${JSON.stringify(v)} is not a valid mode (use "never", "ask", or a non-negative integer), falling back to "ask"`,
|
|
172
|
+
};
|
|
152
173
|
}
|
|
153
174
|
|
|
154
175
|
/**
|
|
@@ -188,23 +209,6 @@ export function parseDuration(s: string): number | null {
|
|
|
188
209
|
return total;
|
|
189
210
|
}
|
|
190
211
|
|
|
191
|
-
// Deferred stderr warnings — fnclaude collects these during config load
|
|
192
|
-
// and flushes them via the shared warnings sink at a sensible time (after
|
|
193
|
-
// claude exits, in run()). The local `deferredWarnings` export is kept
|
|
194
|
-
// for backward compatibility with callers that imported it; it shadows
|
|
195
|
-
// the shared sink's view of config-emitted warnings only.
|
|
196
|
-
export const deferredWarnings: string[] = [];
|
|
197
|
-
|
|
198
|
-
// Defer-import the shared warnings module so config remains import-cycle-
|
|
199
|
-
// safe and any test that loads config in isolation still works without
|
|
200
|
-
// the warnings module having been initialized.
|
|
201
|
-
import { warn as globalWarn } from './warnings.js';
|
|
202
|
-
|
|
203
|
-
function warn(msg: string): void {
|
|
204
|
-
deferredWarnings.push(msg);
|
|
205
|
-
globalWarn(msg);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
212
|
// ── raw TOML shape (mirrors the Go rawConfig) ──────────────────────────────
|
|
209
213
|
|
|
210
214
|
interface RawConfig {
|
|
@@ -228,26 +232,46 @@ interface RawConfig {
|
|
|
228
232
|
|
|
229
233
|
// ── loadConfig ─────────────────────────────────────────────────────────────
|
|
230
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Result of `loadConfig` — the merged Config plus any non-fatal warnings
|
|
237
|
+
* raised during the load (malformed file, invalid mode value, bogus
|
|
238
|
+
* duration, etc.). The caller threads warnings into the deferred-flush
|
|
239
|
+
* mechanism in `main.ts`; this module owns no global mutable state.
|
|
240
|
+
*/
|
|
241
|
+
export interface LoadConfigResult {
|
|
242
|
+
config: Config;
|
|
243
|
+
warnings: readonly string[];
|
|
244
|
+
}
|
|
245
|
+
|
|
231
246
|
/**
|
|
232
247
|
* loadConfig loads the configuration from the config file and environment
|
|
233
248
|
* variables, merging over built-in defaults. Order of precedence:
|
|
234
249
|
*
|
|
235
250
|
* env var > config file > built-in default
|
|
236
251
|
*
|
|
237
|
-
* A missing config file is not an error. A malformed config file
|
|
238
|
-
* warning and falls back to defaults.
|
|
252
|
+
* A missing config file is not an error. A malformed config file produces
|
|
253
|
+
* a warning and falls back to defaults.
|
|
239
254
|
*/
|
|
240
|
-
export function loadConfig():
|
|
255
|
+
export function loadConfig(): LoadConfigResult {
|
|
241
256
|
const cfg = defaultConfig();
|
|
257
|
+
const warnings: string[] = [];
|
|
242
258
|
const path = configFilePath();
|
|
243
259
|
|
|
260
|
+
const recordNormalize = <T>(
|
|
261
|
+
r: NormalizeResult<T>,
|
|
262
|
+
set: (v: T) => void,
|
|
263
|
+
): void => {
|
|
264
|
+
set(r.value);
|
|
265
|
+
if (r.warning !== null) warnings.push(r.warning);
|
|
266
|
+
};
|
|
267
|
+
|
|
244
268
|
if (existsSync(path)) {
|
|
245
269
|
let raw: RawConfig | null = null;
|
|
246
270
|
try {
|
|
247
271
|
const body = readFileSync(path, 'utf8');
|
|
248
272
|
raw = Bun.TOML.parse(body) as RawConfig;
|
|
249
273
|
} catch (err) {
|
|
250
|
-
|
|
274
|
+
warnings.push(
|
|
251
275
|
`fnclaude: config file ${path} is malformed, using defaults: ${(err as Error).message}`,
|
|
252
276
|
);
|
|
253
277
|
raw = null;
|
|
@@ -259,7 +283,7 @@ export function loadConfig(): Config {
|
|
|
259
283
|
if (d !== null) {
|
|
260
284
|
cfg.name.timeout = d;
|
|
261
285
|
} else {
|
|
262
|
-
|
|
286
|
+
warnings.push(
|
|
263
287
|
`fnclaude: invalid timeout ${JSON.stringify(raw.name.timeout)} in config, using default`,
|
|
264
288
|
);
|
|
265
289
|
}
|
|
@@ -268,10 +292,14 @@ export function loadConfig(): Config {
|
|
|
268
292
|
cfg.name.quietMissingAPIKey = raw.name.quiet_missing_api_key;
|
|
269
293
|
}
|
|
270
294
|
if (typeof raw.auto?.tmux === 'string' && raw.auto.tmux !== '') {
|
|
271
|
-
|
|
295
|
+
recordNormalize(normalizeTmuxMode(raw.auto.tmux), (v) => {
|
|
296
|
+
cfg.auto.tmux = v;
|
|
297
|
+
});
|
|
272
298
|
}
|
|
273
299
|
if (typeof raw.auto?.handoff === 'string' && raw.auto.handoff !== '') {
|
|
274
|
-
|
|
300
|
+
recordNormalize(normalizeHandoffMode(raw.auto.handoff), (v) => {
|
|
301
|
+
cfg.auto.handoff = v;
|
|
302
|
+
});
|
|
275
303
|
}
|
|
276
304
|
if (
|
|
277
305
|
typeof raw.auto?.spawn_command === 'string' &&
|
|
@@ -293,7 +321,7 @@ export function loadConfig(): Config {
|
|
|
293
321
|
if (d !== null) {
|
|
294
322
|
cfg.name.timeout = d;
|
|
295
323
|
} else {
|
|
296
|
-
|
|
324
|
+
warnings.push(
|
|
297
325
|
`fnclaude: invalid FNCLAUDE_NAME_TIMEOUT ${JSON.stringify(e.FNCLAUDE_NAME_TIMEOUT)}, using current value`,
|
|
298
326
|
);
|
|
299
327
|
}
|
|
@@ -301,14 +329,19 @@ export function loadConfig(): Config {
|
|
|
301
329
|
if (e.FNCLAUDE_QUIET_MISSING_API_KEY) {
|
|
302
330
|
cfg.name.quietMissingAPIKey = parseBoolEnv(e.FNCLAUDE_QUIET_MISSING_API_KEY);
|
|
303
331
|
}
|
|
304
|
-
if (e.FNCLAUDE_TMUX)
|
|
305
|
-
|
|
332
|
+
if (e.FNCLAUDE_TMUX) {
|
|
333
|
+
recordNormalize(normalizeTmuxMode(e.FNCLAUDE_TMUX), (v) => {
|
|
334
|
+
cfg.auto.tmux = v;
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
if (e.FNCLAUDE_HANDOFF) {
|
|
338
|
+
recordNormalize(normalizeHandoffMode(e.FNCLAUDE_HANDOFF), (v) => {
|
|
339
|
+
cfg.auto.handoff = v;
|
|
340
|
+
});
|
|
341
|
+
}
|
|
306
342
|
if (e.FNCLAUDE_SPAWN_COMMAND) cfg.auto.spawnCommand = e.FNCLAUDE_SPAWN_COMMAND;
|
|
307
343
|
|
|
308
|
-
cfg
|
|
309
|
-
cfg.auto.handoff = normalizeHandoffMode(cfg.auto.handoff);
|
|
310
|
-
|
|
311
|
-
return cfg;
|
|
344
|
+
return { config: cfg, warnings };
|
|
312
345
|
}
|
|
313
346
|
|
|
314
347
|
// ── envFromConfig ─────────────────────────────────────────────────────────
|
package/src/help.ts
CHANGED
package/src/hostAliases.ts
CHANGED
|
@@ -27,11 +27,22 @@ export function hostAliasesUserPath(home: string): string {
|
|
|
27
27
|
return join(home, '.local', 'share', 'fnrhombus', 'host-aliases.json');
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Result of a host-aliases load: the merged alias map plus any non-fatal
|
|
32
|
+
* warnings (e.g. malformed files that were skipped). Mirrors
|
|
33
|
+
* `LoadConfigResult` so the caller can thread warnings into the deferred
|
|
34
|
+
* flush.
|
|
35
|
+
*/
|
|
36
|
+
export interface LoadHostAliasesResult {
|
|
37
|
+
aliases: Record<string, string>;
|
|
38
|
+
warnings: readonly string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
30
41
|
/**
|
|
31
42
|
* Read both files (if present) and merge them, user-level winning per
|
|
32
43
|
* key. Either or both missing returns whatever is available (or empty).
|
|
33
44
|
*/
|
|
34
|
-
export function loadHostAliases(home: string):
|
|
45
|
+
export function loadHostAliases(home: string): LoadHostAliasesResult {
|
|
35
46
|
return mergeHostAliases([HOST_ALIASES_SYSTEM_PATH, hostAliasesUserPath(home)]);
|
|
36
47
|
}
|
|
37
48
|
|
|
@@ -40,43 +51,57 @@ export function loadHostAliases(home: string): Record<string, string> {
|
|
|
40
51
|
* winning over earlier ones. Callers order system-first, user-second so
|
|
41
52
|
* user wins on conflict.
|
|
42
53
|
*/
|
|
43
|
-
export function mergeHostAliases(paths: string[]):
|
|
54
|
+
export function mergeHostAliases(paths: string[]): LoadHostAliasesResult {
|
|
44
55
|
const merged: Record<string, string> = {};
|
|
56
|
+
const warnings: string[] = [];
|
|
45
57
|
for (const p of paths) {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
const { aliases, warning } = readHostAliasesFile(p);
|
|
59
|
+
if (warning !== null) warnings.push(warning);
|
|
60
|
+
for (const k of Object.keys(aliases)) {
|
|
61
|
+
merged[k] = aliases[k] as string;
|
|
49
62
|
}
|
|
50
63
|
}
|
|
51
|
-
return merged;
|
|
64
|
+
return { aliases: merged, warnings };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface ReadHostAliasesFileResult {
|
|
68
|
+
aliases: Record<string, string>;
|
|
69
|
+
warning: string | null;
|
|
52
70
|
}
|
|
53
71
|
|
|
54
72
|
/**
|
|
55
|
-
* Parse one alias file. Missing file
|
|
56
|
-
*
|
|
57
|
-
*
|
|
73
|
+
* Parse one alias file. Missing file is the common path and stays
|
|
74
|
+
* silent. Malformed JSON or non-object roots produce a warning so the
|
|
75
|
+
* user can fix the file rather than wondering why their aliases don't
|
|
76
|
+
* apply. Non-string values are silently dropped (mirrors the JS plugin).
|
|
58
77
|
*/
|
|
59
|
-
export function readHostAliasesFile(path: string):
|
|
78
|
+
export function readHostAliasesFile(path: string): ReadHostAliasesFileResult {
|
|
60
79
|
let data: string;
|
|
61
80
|
try {
|
|
62
81
|
data = readFileSync(path, 'utf8');
|
|
63
82
|
} catch {
|
|
64
|
-
return {};
|
|
83
|
+
return { aliases: {}, warning: null };
|
|
65
84
|
}
|
|
66
85
|
let raw: unknown;
|
|
67
86
|
try {
|
|
68
87
|
raw = JSON.parse(data);
|
|
69
|
-
} catch {
|
|
70
|
-
return {
|
|
88
|
+
} catch (err) {
|
|
89
|
+
return {
|
|
90
|
+
aliases: {},
|
|
91
|
+
warning: `fnclaude: host-aliases file ${path} is malformed, skipping: ${(err as Error).message}`,
|
|
92
|
+
};
|
|
71
93
|
}
|
|
72
94
|
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
73
|
-
return {
|
|
95
|
+
return {
|
|
96
|
+
aliases: {},
|
|
97
|
+
warning: `fnclaude: host-aliases file ${path} has a non-object root, skipping`,
|
|
98
|
+
};
|
|
74
99
|
}
|
|
75
100
|
const out: Record<string, string> = {};
|
|
76
101
|
for (const [k, v] of Object.entries(raw as Record<string, unknown>)) {
|
|
77
102
|
if (typeof v === 'string') out[k] = v;
|
|
78
103
|
}
|
|
79
|
-
return out;
|
|
104
|
+
return { aliases: out, warning: null };
|
|
80
105
|
}
|
|
81
106
|
|
|
82
107
|
/**
|
package/src/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ export {
|
|
|
9
9
|
parseRepoRef,
|
|
10
10
|
type RepoRef,
|
|
11
11
|
} from './repoRef.js';
|
|
12
|
-
export { expandTildePath } from './paths.js';
|
|
12
|
+
export { expandTildePath, resolveSelfPath } from './paths.js';
|
|
13
13
|
export {
|
|
14
14
|
findPromptsDir,
|
|
15
15
|
isInteractiveSession,
|
|
@@ -55,9 +55,14 @@ export {
|
|
|
55
55
|
readRequest,
|
|
56
56
|
readResponse,
|
|
57
57
|
type Action,
|
|
58
|
+
type CopyRequest,
|
|
58
59
|
type Op,
|
|
59
60
|
type Request,
|
|
61
|
+
type RequestOverrides,
|
|
60
62
|
type Response,
|
|
63
|
+
type RestartRequest,
|
|
64
|
+
type SpawnRequest,
|
|
65
|
+
type SwitchRequest,
|
|
61
66
|
} from './mcp/protocol.js';
|
|
62
67
|
export {
|
|
63
68
|
SocketListener,
|
|
@@ -83,9 +88,10 @@ export {
|
|
|
83
88
|
// embedding hosts that want to render their own help.
|
|
84
89
|
export { helpText, setVersion, version, wantsHelp, wantsVersion } from './help.js';
|
|
85
90
|
|
|
86
|
-
// Warning
|
|
87
|
-
//
|
|
88
|
-
|
|
91
|
+
// Warning flush. Loaders (loadConfig / loadRepoSettings / loadHostAliases
|
|
92
|
+
// / loadPrompts) return their warnings; flushWarnings drains a provided
|
|
93
|
+
// list to stderr.
|
|
94
|
+
export { flushWarnings } from './warnings.js';
|
|
89
95
|
|
|
90
96
|
// noop dir seeding (noop fallback when fnclaude is invoked with no path).
|
|
91
97
|
export { NOOP_HANDOFF_TEMPLATE, defaultNoopDir, seedNoop } from './noop.js';
|
|
@@ -94,7 +100,7 @@ export { NOOP_HANDOFF_TEMPLATE, defaultNoopDir, seedNoop } from './noop.js';
|
|
|
94
100
|
export { silentRelaunch, silentRelaunchHandoff, spawnAndExit } from './silentRelaunch.js';
|
|
95
101
|
|
|
96
102
|
// Top-level run loop + entry point.
|
|
97
|
-
export { main, run, type RunDeps } from './main.js';
|
|
103
|
+
export { main, run, type RunConfig, type RunDeps, type RunIO } from './main.js';
|
|
98
104
|
|
|
99
105
|
// PTY runner + shared helpers (ring buffer, cross-cwd detection,
|
|
100
106
|
// reconstructArgv, ensureCWD).
|