@crouton-kit/crouter 0.3.3 → 0.3.11
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 +2 -2
- package/dist/builtin-skills/skills/crouter-development/marketplaces/SKILL.md +1 -1
- package/dist/builtin-skills/skills/crouter-development/plugins/SKILL.md +3 -3
- package/dist/cli.js +16 -26
- package/dist/commands/__tests__/skill.test.js +24 -28
- package/dist/commands/agent.d.ts +6 -0
- package/dist/commands/agent.js +585 -0
- package/dist/commands/debug.d.ts +1 -1
- package/dist/commands/debug.js +20 -7
- package/dist/commands/human.js +51 -19
- package/dist/commands/job.d.ts +9 -0
- package/dist/commands/job.js +100 -385
- package/dist/commands/{flow.d.ts → mode.d.ts} +1 -1
- package/dist/commands/mode.js +231 -0
- package/dist/commands/pkg.js +5 -0
- package/dist/commands/plan.d.ts +1 -1
- package/dist/commands/plan.js +24 -11
- package/dist/commands/skill.js +130 -107
- package/dist/commands/spec.d.ts +1 -1
- package/dist/commands/spec.js +24 -11
- package/dist/commands/sys.js +5 -0
- package/dist/core/__tests__/job.test.js +38 -74
- package/dist/core/__tests__/jobs.test.d.ts +1 -0
- package/dist/core/__tests__/jobs.test.js +98 -0
- package/dist/core/__tests__/resolver.test.d.ts +1 -0
- package/dist/core/__tests__/resolver.test.js +181 -0
- package/dist/core/__tests__/spawn.test.d.ts +1 -0
- package/dist/core/__tests__/spawn.test.js +138 -0
- package/dist/core/__tests__/subagents.test.d.ts +1 -0
- package/dist/core/__tests__/subagents.test.js +75 -0
- package/dist/core/__tests__/unknown-path.test.d.ts +1 -0
- package/dist/core/__tests__/unknown-path.test.js +52 -0
- package/dist/core/bootstrap.d.ts +2 -0
- package/dist/core/bootstrap.js +66 -0
- package/dist/core/command.d.ts +58 -2
- package/dist/core/command.js +62 -14
- package/dist/core/config.js +20 -2
- package/dist/core/frontmatter.d.ts +10 -0
- package/dist/core/frontmatter.js +24 -9
- package/dist/core/help.d.ts +39 -8
- package/dist/core/help.js +64 -32
- package/dist/core/jobs.d.ts +33 -13
- package/dist/core/jobs.js +259 -47
- package/dist/core/resolver.d.ts +1 -2
- package/dist/core/resolver.js +111 -47
- package/dist/core/spawn.d.ts +150 -10
- package/dist/core/spawn.js +493 -41
- package/dist/core/subagents.d.ts +18 -0
- package/dist/core/subagents.js +163 -0
- package/dist/prompts/agent.d.ts +12 -3
- package/dist/prompts/agent.js +51 -18
- package/dist/prompts/debug.js +14 -7
- package/dist/prompts/skill.js +16 -16
- package/dist/types.d.ts +22 -1
- package/dist/types.js +5 -2
- package/package.json +2 -2
- package/dist/commands/flow.js +0 -24
package/dist/core/resolver.js
CHANGED
|
@@ -5,6 +5,7 @@ import { listDirs, pathExists, readText, walkFiles, } from './fs-utils.js';
|
|
|
5
5
|
import { readMarketplaceManifest, readPluginManifest } from './manifest.js';
|
|
6
6
|
import { parseFrontmatter } from './frontmatter.js';
|
|
7
7
|
import { ambiguous, notFound, usage } from './errors.js';
|
|
8
|
+
import { InputError } from './io.js';
|
|
8
9
|
import { builtinSkillsRoot, marketplacesDir, pluginsDir, projectScopeRoot, scopeSkillsDir, userScopeRoot, } from './scope.js';
|
|
9
10
|
function getBuiltinPlugin() {
|
|
10
11
|
const root = builtinSkillsRoot();
|
|
@@ -202,33 +203,99 @@ export function resolveSkill(rawName, opts = {}) {
|
|
|
202
203
|
if (parsed.scope && opts.scope && parsed.scope !== opts.scope) {
|
|
203
204
|
throw usage(`scope conflict: identifier "${rawName}" uses scope "${parsed.scope}" but --scope is "${opts.scope}"`);
|
|
204
205
|
}
|
|
205
|
-
if (parsed.plugin && opts.pluginFilter && parsed.plugin !== opts.pluginFilter) {
|
|
206
|
-
throw usage(`plugin conflict: identifier "${rawName}" uses plugin "${parsed.plugin}" but --plugin is "${opts.pluginFilter}"`);
|
|
207
|
-
}
|
|
208
206
|
const effectiveScope = opts.scope ?? parsed.scope;
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
207
|
+
// Lookup-based disambiguation: if segments[0] matches an installed plugin, treat it as plugin.
|
|
208
|
+
// Otherwise the entire segments array is the skill path under the scope-direct plugin.
|
|
209
|
+
let pluginQualifier;
|
|
210
|
+
let skillName;
|
|
211
|
+
if (parsed.segments.length === 0) {
|
|
212
|
+
throw usage(`skill name required`);
|
|
213
|
+
}
|
|
214
|
+
if (opts.pluginFilter !== undefined) {
|
|
215
|
+
// Explicit plugin filter overrides disambiguation.
|
|
216
|
+
pluginQualifier = opts.pluginFilter;
|
|
217
|
+
skillName = parsed.segments.join('/');
|
|
218
|
+
}
|
|
219
|
+
else if (parsed.segments.length > 1) {
|
|
220
|
+
const maybePlugin = parsed.segments[0];
|
|
221
|
+
const pluginMatch = findPluginByName(maybePlugin, effectiveScope) ??
|
|
222
|
+
(effectiveScope === undefined ? null : findPluginByName(maybePlugin));
|
|
223
|
+
if (pluginMatch !== null) {
|
|
224
|
+
pluginQualifier = maybePlugin;
|
|
225
|
+
skillName = parsed.segments.slice(1).join('/');
|
|
224
226
|
}
|
|
227
|
+
else {
|
|
228
|
+
pluginQualifier = undefined;
|
|
229
|
+
skillName = parsed.segments.join('/');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
pluginQualifier = undefined;
|
|
234
|
+
skillName = parsed.segments[0];
|
|
225
235
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
236
|
+
if (pluginQualifier && opts.pluginFilter && pluginQualifier !== opts.pluginFilter) {
|
|
237
|
+
throw usage(`plugin conflict: identifier "${rawName}" uses plugin "${pluginQualifier}" but --plugin is "${opts.pluginFilter}"`);
|
|
238
|
+
}
|
|
239
|
+
const effectivePluginFilter = opts.pluginFilter ?? pluginQualifier;
|
|
240
|
+
const direct = findSkillMatches(skillName, pluginQualifier, effectiveScope, effectivePluginFilter);
|
|
241
|
+
if (direct.length > 0)
|
|
242
|
+
return pickMatch(direct, skillName, pluginQualifier);
|
|
243
|
+
// Leaf-name fallback: the caller supplied only the final path segment
|
|
244
|
+
// (e.g. "cli-design" for "ai/interface/cli-design"). A direct path lookup
|
|
245
|
+
// missed because the skill lives under a nested path. Match by last segment.
|
|
246
|
+
const byLeaf = findSkillsByLeaf(skillName, pluginQualifier, effectiveScope, effectivePluginFilter);
|
|
247
|
+
if (byLeaf.length === 1)
|
|
248
|
+
return byLeaf[0];
|
|
249
|
+
if (byLeaf.length > 1) {
|
|
250
|
+
throw ambiguous(formatLeafAmbiguousMessage(skillName, byLeaf), {
|
|
251
|
+
skill: skillName,
|
|
252
|
+
candidates: byLeaf.map((m) => ({
|
|
253
|
+
id: formatSkillId(m),
|
|
254
|
+
plugin: m.plugin,
|
|
255
|
+
scope: m.scope,
|
|
256
|
+
path: m.path,
|
|
257
|
+
})),
|
|
258
|
+
next: 'Multiple skills share this leaf name. Re-run with one of the full paths in candidates.',
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
throw notFound(formatNotFoundMessage(rawName, skillName, pluginQualifier), {
|
|
262
|
+
skill: skillName,
|
|
263
|
+
plugin: pluginQualifier,
|
|
229
264
|
scope: parsed.scope,
|
|
230
265
|
});
|
|
231
266
|
}
|
|
267
|
+
/** Canonical, unambiguous identifier for a skill. Scope-root skills are
|
|
268
|
+
* qualified by scope; plugin skills by plugin name. */
|
|
269
|
+
function formatSkillId(s) {
|
|
270
|
+
return s.plugin === SCOPE_SKILL_PLUGIN ? `${s.scope}/${s.name}` : `${s.plugin}/${s.name}`;
|
|
271
|
+
}
|
|
272
|
+
/** Match skills whose final path segment equals `leaf`. Only meaningful when
|
|
273
|
+
* `leaf` is a bare segment (no slash) — a slashed query can never equal a
|
|
274
|
+
* single segment, so this returns empty and the caller falls through. */
|
|
275
|
+
function findSkillsByLeaf(leaf, pluginQualifier, scope, pluginFilter) {
|
|
276
|
+
if (leaf.includes('/'))
|
|
277
|
+
return [];
|
|
278
|
+
let all;
|
|
279
|
+
try {
|
|
280
|
+
all = scope ? listAllSkills(scope) : listAllSkills();
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
return [];
|
|
284
|
+
}
|
|
285
|
+
return all.filter((s) => {
|
|
286
|
+
if ((s.name.split('/').pop() ?? s.name) !== leaf)
|
|
287
|
+
return false;
|
|
288
|
+
if (pluginQualifier && s.plugin !== pluginQualifier)
|
|
289
|
+
return false;
|
|
290
|
+
if (pluginFilter && s.plugin !== pluginFilter)
|
|
291
|
+
return false;
|
|
292
|
+
return true;
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
function formatLeafAmbiguousMessage(leaf, matches) {
|
|
296
|
+
const ids = matches.map(formatSkillId).join(', ');
|
|
297
|
+
return `ambiguous skill: ${leaf} matches multiple skills: ${ids}`;
|
|
298
|
+
}
|
|
232
299
|
function findSkillMatches(name, pluginQualifier, scope, pluginFilter) {
|
|
233
300
|
const plugins = scope ? listInstalledPlugins(scope) : listAllPlugins();
|
|
234
301
|
const enabledPlugins = plugins.filter((p) => p.enabled);
|
|
@@ -304,18 +371,18 @@ function pickMatch(matches, name, pluginQualifier) {
|
|
|
304
371
|
})),
|
|
305
372
|
});
|
|
306
373
|
}
|
|
307
|
-
function formatNotFoundMessage(rawName,
|
|
308
|
-
const suggestions = suggestSkills(
|
|
374
|
+
function formatNotFoundMessage(rawName, skillName, pluginQualifier) {
|
|
375
|
+
const suggestions = suggestSkills(skillName, pluginQualifier);
|
|
309
376
|
const lines = [`skill not found: ${rawName}`];
|
|
310
|
-
lines.push(' expected forms: <name>, <plugin
|
|
377
|
+
lines.push(' expected forms: <name>, <plugin>/<name>, <scope>/<name>, <scope>/<plugin>/<name>');
|
|
311
378
|
if (suggestions.length > 0) {
|
|
312
379
|
const formatted = suggestions
|
|
313
|
-
.map((s) => s.plugin === SCOPE_SKILL_PLUGIN ? s.name : `${s.plugin}
|
|
380
|
+
.map((s) => s.plugin === SCOPE_SKILL_PLUGIN ? s.name : `${s.plugin}/${s.name}`)
|
|
314
381
|
.slice(0, 3);
|
|
315
382
|
lines.push(` did you mean: ${formatted.join(', ')}`);
|
|
316
383
|
}
|
|
317
384
|
else {
|
|
318
|
-
lines.push(' run `crtr skill list` or `crtr skill search <query>` to discover skills');
|
|
385
|
+
lines.push(' run `crtr skill find list` or `crtr skill find search <query>` to discover skills');
|
|
319
386
|
}
|
|
320
387
|
return lines.join('\n');
|
|
321
388
|
}
|
|
@@ -386,29 +453,26 @@ function editDistance(a, b) {
|
|
|
386
453
|
const SCOPE_QUALIFIERS = new Set(['user', 'project']);
|
|
387
454
|
// Accepted identifier forms:
|
|
388
455
|
// <name> — bare name; scope-root first, then plugins
|
|
389
|
-
// <plugin
|
|
390
|
-
// <scope
|
|
391
|
-
// <scope
|
|
392
|
-
// Bare `<plugin>/<name>` (no colon) is handled as a fallback inside resolveSkill.
|
|
456
|
+
// <plugin>/<name> — explicit plugin (plugin may contain slashes)
|
|
457
|
+
// <scope>/<name> — scope-root in a specific scope
|
|
458
|
+
// <scope>/<plugin>/<name> — fully qualified; plugin-vs-path disambiguation is lookup-based in resolveSkill
|
|
393
459
|
export function parseSkillQualifier(raw) {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
}
|
|
409
|
-
return { scope, name: after };
|
|
460
|
+
if (raw.includes(':')) {
|
|
461
|
+
const suggested = raw.replace(/:/g, '/');
|
|
462
|
+
throw new InputError({
|
|
463
|
+
error: 'invalid_qualifier',
|
|
464
|
+
message: "mixed separators ':' and '/' no longer supported; use slashes throughout",
|
|
465
|
+
received: raw,
|
|
466
|
+
field: 'name',
|
|
467
|
+
next: `Use ${suggested}.`,
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
const segments = raw.split('/');
|
|
471
|
+
if (SCOPE_QUALIFIERS.has(segments[0])) {
|
|
472
|
+
const scope = segments[0];
|
|
473
|
+
return { scope, segments: segments.slice(1) };
|
|
410
474
|
}
|
|
411
|
-
return {
|
|
475
|
+
return { segments };
|
|
412
476
|
}
|
|
413
477
|
function orderPluginsByResolution(plugins) {
|
|
414
478
|
const score = (p) => {
|
package/dist/core/spawn.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export interface SpawnAgentOptions {
|
|
2
|
-
/** First user message for the new
|
|
2
|
+
/** First user message for the new agent session. */
|
|
3
3
|
prompt: string;
|
|
4
4
|
cwd: string;
|
|
5
5
|
/** crtr job_id injected as CRTR_JOB_ID env var in the pane. */
|
|
@@ -10,6 +10,14 @@ export interface SpawnAgentOptions {
|
|
|
10
10
|
};
|
|
11
11
|
/** Max panes per tmux window before overflowing to a new window. */
|
|
12
12
|
maxPanesPerWindow: number;
|
|
13
|
+
/** Display name passed to the agent's `-n` flag; surfaces in pane title and resume picker. */
|
|
14
|
+
name?: string;
|
|
15
|
+
/** Persona appended via `--append-system-prompt` (subagent body). */
|
|
16
|
+
systemPrompt?: string;
|
|
17
|
+
/** Model pattern/id passed via `--model`. */
|
|
18
|
+
model?: string;
|
|
19
|
+
/** Tool allow-list passed to pi via `--tools`. */
|
|
20
|
+
tools?: string[];
|
|
13
21
|
}
|
|
14
22
|
export interface SpawnAgentResult {
|
|
15
23
|
status: 'spawned' | 'spawn-failed' | 'not-in-tmux';
|
|
@@ -20,10 +28,11 @@ export interface SpawnAgentResult {
|
|
|
20
28
|
message: string;
|
|
21
29
|
}
|
|
22
30
|
export interface DetachOptions {
|
|
23
|
-
/** Inner command to run in the pane. If omitted, build
|
|
31
|
+
/** Inner command to run in the pane. If omitted, build the detected agent's
|
|
32
|
+
* invocation around `<prompt>`. */
|
|
24
33
|
command?: string;
|
|
25
|
-
/** Full first user message for the new
|
|
26
|
-
*
|
|
34
|
+
/** Full first user message for the new agent session (ignored when `command`
|
|
35
|
+
* is set). No custom system prompt. */
|
|
27
36
|
prompt?: string;
|
|
28
37
|
cwd: string;
|
|
29
38
|
/** crtr job_id injected as CRTR_JOB_ID env var in the pane and used by the
|
|
@@ -40,6 +49,9 @@ export interface DetachOptions {
|
|
|
40
49
|
* uses the attached client's currently-focused pane — which drifts if the
|
|
41
50
|
* user switches windows between kickoff and spawn. */
|
|
42
51
|
targetPane?: string;
|
|
52
|
+
/** Display name passed to the agent's `-n` flag; ignored when `command` is set
|
|
53
|
+
* (caller controls the full argv in that mode). */
|
|
54
|
+
name?: string;
|
|
43
55
|
}
|
|
44
56
|
export interface DetachResult {
|
|
45
57
|
status: 'spawned' | 'spawn-failed' | 'not-in-tmux';
|
|
@@ -48,7 +60,126 @@ export interface DetachResult {
|
|
|
48
60
|
}
|
|
49
61
|
export declare function isInTmux(): boolean;
|
|
50
62
|
export declare function shellQuote(s: string): string;
|
|
63
|
+
/** Coding-agent CLIs crtr knows how to spawn as a sibling worker. */
|
|
64
|
+
export type AgentKind = 'claude' | 'pi';
|
|
65
|
+
/**
|
|
66
|
+
* Detect which coding-agent CLI is hosting the current crtr process so spawns
|
|
67
|
+
* launch a matching sibling. pi exports `PI_CODING_AGENT=true` into its tool
|
|
68
|
+
* subprocess environment; Claude Code exports `CLAUDECODE` /
|
|
69
|
+
* `CLAUDE_CODE_SESSION_ID`. Defaults to claude when no signal is present
|
|
70
|
+
* (preserves prior behavior).
|
|
71
|
+
*/
|
|
72
|
+
export declare function detectAgentKind(): AgentKind;
|
|
73
|
+
/**
|
|
74
|
+
* Normalize a `--model` value for the target agent CLI.
|
|
75
|
+
*
|
|
76
|
+
* Subagent frontmatter uses Claude Code's bare aliases (`sonnet`, `opus`,
|
|
77
|
+
* `haiku`, optionally with a `:thinking` suffix). The `claude` CLI resolves
|
|
78
|
+
* those natively, but `pi` maps a bare alias to its default provider —
|
|
79
|
+
* `amazon-bedrock` — which most users have not authenticated, so the spawn
|
|
80
|
+
* dies with "No API key found for amazon-bedrock". These aliases name Anthropic
|
|
81
|
+
* models, so under pi we pin them to the `anthropic/` provider (preserving any
|
|
82
|
+
* `:thinking` suffix). Values that already carry a `provider/` prefix or are
|
|
83
|
+
* concrete model ids are passed through untouched.
|
|
84
|
+
*/
|
|
85
|
+
export declare function normalizeModelForKind(model: string, kind: AgentKind): string;
|
|
86
|
+
export interface AgentCommandOptions {
|
|
87
|
+
/** First user message delivered to the new agent session. */
|
|
88
|
+
prompt: string;
|
|
89
|
+
/** Display name (`-n`); surfaces in the pane title and resume picker. */
|
|
90
|
+
name?: string;
|
|
91
|
+
/** Fork an existing session into a fresh one rather than starting clean. */
|
|
92
|
+
fork?: {
|
|
93
|
+
sessionId: string;
|
|
94
|
+
};
|
|
95
|
+
/** Persona/system prompt appended to the agent's default (`--append-system-prompt`).
|
|
96
|
+
* Used to apply a subagent definition's body. */
|
|
97
|
+
systemPrompt?: string;
|
|
98
|
+
/** Model pattern/id passed via `--model` (both claude and pi). */
|
|
99
|
+
model?: string;
|
|
100
|
+
/** Tool allow-list. Passed to pi via `--tools`; ignored for claude, whose
|
|
101
|
+
* tool names and gating flag differ. */
|
|
102
|
+
tools?: string[];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Build the agent-CLI invocation (no job wrapper) for the given kind.
|
|
106
|
+
*
|
|
107
|
+
* claude: `claude [-n <name>] [--resume <id> --fork-session] \
|
|
108
|
+
* --dangerously-skip-permissions <prompt>`
|
|
109
|
+
* pi: `pi [-n <name>] [--fork <id>] <prompt>`
|
|
110
|
+
*
|
|
111
|
+
* pi has no permission popups, so it needs no skip-permissions flag.
|
|
112
|
+
*/
|
|
113
|
+
export declare function buildAgentCommand(opts: AgentCommandOptions, kind?: AgentKind): string;
|
|
114
|
+
export interface AgentPrintArgv {
|
|
115
|
+
cmd: string;
|
|
116
|
+
args: string[];
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Argv for a non-interactive print-mode run.
|
|
120
|
+
*
|
|
121
|
+
* claude: `claude [-n <name>] [--resume <id> --fork-session] -p \
|
|
122
|
+
* --dangerously-skip-permissions <prompt>`
|
|
123
|
+
* pi: `pi [-n <name>] [--fork <id>] -p <prompt>`
|
|
124
|
+
*
|
|
125
|
+
* Returned as a cmd + args array so callers can spawn without a shell.
|
|
126
|
+
*/
|
|
127
|
+
export declare function buildAgentPrintArgv(opts: AgentCommandOptions, kind?: AgentKind): AgentPrintArgv;
|
|
128
|
+
/** Same as buildAgentPrintArgv but rendered as a single shell-quoted string. */
|
|
129
|
+
export declare function buildAgentPrintCommand(opts: AgentCommandOptions, kind?: AgentKind): string;
|
|
130
|
+
export interface HeadlessRunResult {
|
|
131
|
+
status: 'done' | 'failed';
|
|
132
|
+
/** Captured stdout on success; stdout+stderr (or an error message) on failure. */
|
|
133
|
+
output: string;
|
|
134
|
+
exitCode: number | null;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Run the agent headlessly and resolve once it exits. A blocking caller awaits
|
|
138
|
+
* this. stdout is captured as the result; a non-zero exit yields status
|
|
139
|
+
* 'failed' with the combined output.
|
|
140
|
+
*/
|
|
141
|
+
export declare function runAgentHeadless(opts: {
|
|
142
|
+
prompt: string;
|
|
143
|
+
name?: string;
|
|
144
|
+
cwd: string;
|
|
145
|
+
systemPrompt?: string;
|
|
146
|
+
model?: string;
|
|
147
|
+
tools?: string[];
|
|
148
|
+
}): Promise<HeadlessRunResult>;
|
|
149
|
+
export interface HeadlessDetachResult {
|
|
150
|
+
status: 'spawned' | 'spawn-failed';
|
|
151
|
+
pid?: number;
|
|
152
|
+
message: string;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Launch a headless agent detached (background). Its print output is captured
|
|
156
|
+
* and delivered to the job via `crtr job submit`; a non-zero exit marks the job
|
|
157
|
+
* failed. Returns immediately with the wrapper pid (recorded for crash
|
|
158
|
+
* detection). No tmux required.
|
|
159
|
+
*/
|
|
160
|
+
export declare function spawnHeadlessDetached(opts: {
|
|
161
|
+
prompt: string;
|
|
162
|
+
name?: string;
|
|
163
|
+
cwd: string;
|
|
164
|
+
jobId: string;
|
|
165
|
+
systemPrompt?: string;
|
|
166
|
+
model?: string;
|
|
167
|
+
tools?: string[];
|
|
168
|
+
}): HeadlessDetachResult;
|
|
51
169
|
export declare function countPanesInCurrentWindow(): number;
|
|
170
|
+
/**
|
|
171
|
+
* Find a window in the current tmux session with fewer than `maxPanesPerWindow`
|
|
172
|
+
* panes AND where every existing pane hosts an agent (claude or pi) as its
|
|
173
|
+
* foreground process. Prefers the active window so the spawned pane is visible
|
|
174
|
+
* to the user; otherwise falls back to the first other eligible window. Returns
|
|
175
|
+
* the tmux window id (e.g. `@5`) to pass via `-t`, or null if no window qualifies.
|
|
176
|
+
*
|
|
177
|
+
* Windows holding non-agent panes (dashboards, log tails, idle shells, editors,
|
|
178
|
+
* REPLs, etc.) are skipped so spawning never disrupts those workflows. A pane
|
|
179
|
+
* qualifies as long as an agent comm is among its foreground commands —
|
|
180
|
+
* co-resident helpers like `caffeinate` don't disqualify it.
|
|
181
|
+
*/
|
|
182
|
+
export declare function findWindowWithSpace(maxPanesPerWindow: number): string | null;
|
|
52
183
|
/**
|
|
53
184
|
* Schedule a kill-pane on the *current* tmux pane after `delaySeconds`, detached
|
|
54
185
|
* so the caller can return normally before the pane dies. No-op outside tmux
|
|
@@ -60,18 +191,27 @@ export declare function countPanesInCurrentWindow(): number;
|
|
|
60
191
|
*/
|
|
61
192
|
export declare function scheduleKillCurrentPane(delaySeconds: number): boolean;
|
|
62
193
|
/**
|
|
63
|
-
* Fire-and-forget: launch an interactive
|
|
194
|
+
* Fire-and-forget: launch an interactive agent in a new pane (or window),
|
|
64
195
|
* then schedule the originating pane to be killed after `killAfterSeconds`.
|
|
65
196
|
*
|
|
66
197
|
* No custom system prompt — the task is delivered as the first user message.
|
|
67
|
-
* Returns as soon as the new pane is up; does NOT wait for
|
|
198
|
+
* Returns as soon as the new pane is up; does NOT wait for the agent to finish.
|
|
68
199
|
*/
|
|
69
200
|
export declare function spawnAndDetach(opts: DetachOptions): DetachResult;
|
|
201
|
+
/** Originating pane id + session id of the host (pi/claude) crtr runs under. */
|
|
202
|
+
export declare function originContext(): {
|
|
203
|
+
pane: string;
|
|
204
|
+
sessionId: string;
|
|
205
|
+
} | null;
|
|
206
|
+
/** Deterministic subagent session name for an originating pane id (e.g. `%5`). */
|
|
207
|
+
export declare function subagentSessionName(pane: string): string;
|
|
70
208
|
/**
|
|
71
|
-
* Async sibling spawn. Launches
|
|
72
|
-
*
|
|
73
|
-
*
|
|
209
|
+
* Async sibling spawn. Launches an interactive agent (claude or pi, per
|
|
210
|
+
* detectAgentKind) in the dedicated subagent session, progressively filling
|
|
211
|
+
* windows up to `maxPanesPerWindow` before creating a new window. Returns
|
|
212
|
+
* immediately with the pane id; the parent stays alive. Focus is never
|
|
213
|
+
* switched — the user jumps to the subagent session with Alt-o.
|
|
74
214
|
*
|
|
75
|
-
* If `fork` is set,
|
|
215
|
+
* If `fork` is set, forks the host session into a fresh one.
|
|
76
216
|
*/
|
|
77
217
|
export declare function spawnAgent(opts: SpawnAgentOptions): SpawnAgentResult;
|