@agent-loom/loom 1.0.2 → 1.0.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 +69 -0
- package/dist/acp/client.d.ts +182 -0
- package/dist/acp/client.d.ts.map +1 -0
- package/dist/acp/client.js +432 -0
- package/dist/acp/client.js.map +1 -0
- package/dist/acp/index.d.ts +5 -0
- package/dist/acp/index.d.ts.map +1 -0
- package/dist/acp/index.js +3 -0
- package/dist/acp/index.js.map +1 -0
- package/dist/acp/run.d.ts +41 -0
- package/dist/acp/run.d.ts.map +1 -0
- package/dist/acp/run.js +32 -0
- package/dist/acp/run.js.map +1 -0
- package/dist/apply.d.ts +15 -6
- package/dist/apply.d.ts.map +1 -1
- package/dist/apply.js +78 -49
- package/dist/apply.js.map +1 -1
- package/dist/chat/chat.d.ts +108 -0
- package/dist/chat/chat.d.ts.map +1 -0
- package/dist/chat/chat.js +221 -0
- package/dist/chat/chat.js.map +1 -0
- package/dist/chat/discovery.d.ts +30 -0
- package/dist/chat/discovery.d.ts.map +1 -0
- package/dist/chat/discovery.js +68 -0
- package/dist/chat/discovery.js.map +1 -0
- package/dist/chat/frontmatter.d.ts +12 -0
- package/dist/chat/frontmatter.d.ts.map +1 -0
- package/dist/chat/frontmatter.js +11 -0
- package/dist/chat/frontmatter.js.map +1 -0
- package/dist/chat/index.d.ts +16 -0
- package/dist/chat/index.d.ts.map +1 -0
- package/dist/chat/index.js +11 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/chat/registry.d.ts +73 -0
- package/dist/chat/registry.d.ts.map +1 -0
- package/dist/chat/registry.js +118 -0
- package/dist/chat/registry.js.map +1 -0
- package/dist/chat/resolve-agent.d.ts +39 -0
- package/dist/chat/resolve-agent.d.ts.map +1 -0
- package/dist/chat/resolve-agent.js +36 -0
- package/dist/chat/resolve-agent.js.map +1 -0
- package/dist/chat/suggest.d.ts +20 -0
- package/dist/chat/suggest.d.ts.map +1 -0
- package/dist/chat/suggest.js +55 -0
- package/dist/chat/suggest.js.map +1 -0
- package/dist/cli.js +627 -75
- package/dist/cli.js.map +1 -1
- package/dist/clone.d.ts +21 -3
- package/dist/clone.d.ts.map +1 -1
- package/dist/clone.js +240 -12
- package/dist/clone.js.map +1 -1
- package/dist/copilot/mcp.d.ts +48 -0
- package/dist/copilot/mcp.d.ts.map +1 -0
- package/dist/copilot/mcp.js +146 -0
- package/dist/copilot/mcp.js.map +1 -0
- package/dist/copilot/resolve.d.ts +33 -0
- package/dist/copilot/resolve.d.ts.map +1 -0
- package/dist/copilot/resolve.js +96 -0
- package/dist/copilot/resolve.js.map +1 -0
- package/dist/copilot/spawn.d.ts +51 -0
- package/dist/copilot/spawn.d.ts.map +1 -0
- package/dist/copilot/spawn.js +132 -0
- package/dist/copilot/spawn.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/launch/index.d.ts +10 -0
- package/dist/launch/index.d.ts.map +1 -0
- package/dist/launch/index.js +9 -0
- package/dist/launch/index.js.map +1 -0
- package/dist/launch/stage.d.ts +62 -0
- package/dist/launch/stage.d.ts.map +1 -0
- package/dist/launch/stage.js +108 -0
- package/dist/launch/stage.js.map +1 -0
- package/dist/manifest.d.ts +165 -18
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +980 -225
- package/dist/manifest.js.map +1 -1
- package/dist/renderers/claude.d.ts +5 -0
- package/dist/renderers/claude.d.ts.map +1 -1
- package/dist/renderers/claude.js +17 -3
- package/dist/renderers/claude.js.map +1 -1
- package/dist/renderers/copilot.d.ts +1 -1
- package/dist/renderers/copilot.d.ts.map +1 -1
- package/dist/renderers/copilot.js +205 -22
- package/dist/renderers/copilot.js.map +1 -1
- package/dist/repo-clone.js +17 -11
- package/dist/repo-clone.js.map +1 -1
- package/dist/resolve-template.d.ts +12 -4
- package/dist/resolve-template.d.ts.map +1 -1
- package/dist/resolve-template.js +39 -8
- package/dist/resolve-template.js.map +1 -1
- package/dist/run/index.d.ts +4 -0
- package/dist/run/index.d.ts.map +1 -0
- package/dist/run/index.js +2 -0
- package/dist/run/index.js.map +1 -0
- package/dist/run/run.d.ts +143 -0
- package/dist/run/run.d.ts.map +1 -0
- package/dist/run/run.js +406 -0
- package/dist/run/run.js.map +1 -0
- package/dist/search-registry.d.ts +10 -3
- package/dist/search-registry.d.ts.map +1 -1
- package/dist/search-registry.js +16 -16
- package/dist/search-registry.js.map +1 -1
- package/dist/sessions/index.d.ts +16 -0
- package/dist/sessions/index.d.ts.map +1 -0
- package/dist/sessions/index.js +15 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/sessions/store.d.ts +56 -0
- package/dist/sessions/store.d.ts.map +1 -0
- package/dist/sessions/store.js +220 -0
- package/dist/sessions/store.js.map +1 -0
- package/dist/sessions/types.d.ts +62 -0
- package/dist/sessions/types.d.ts.map +1 -0
- package/dist/sessions/types.js +5 -0
- package/dist/sessions/types.js.map +1 -0
- package/dist/skill-fetcher.d.ts.map +1 -1
- package/dist/skill-fetcher.js +5 -6
- package/dist/skill-fetcher.js.map +1 -1
- package/dist/types.d.ts +123 -41
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -1
- package/dist/util/binary-cache.d.ts +53 -0
- package/dist/util/binary-cache.d.ts.map +1 -0
- package/dist/util/binary-cache.js +211 -0
- package/dist/util/binary-cache.js.map +1 -0
- package/dist/util/frontmatter.d.ts +53 -0
- package/dist/util/frontmatter.d.ts.map +1 -0
- package/dist/util/frontmatter.js +85 -0
- package/dist/util/frontmatter.js.map +1 -0
- package/dist/util/loom-home.d.ts +19 -0
- package/dist/util/loom-home.d.ts.map +1 -0
- package/dist/util/loom-home.js +37 -0
- package/dist/util/loom-home.js.map +1 -0
- package/dist/util/workspace-folder.d.ts +29 -0
- package/dist/util/workspace-folder.d.ts.map +1 -0
- package/dist/util/workspace-folder.js +43 -0
- package/dist/util/workspace-folder.js.map +1 -0
- package/dist/validate.d.ts +7 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +90 -17
- package/dist/validate.js.map +1 -1
- package/package.json +31 -2
package/dist/apply.js
CHANGED
|
@@ -1,59 +1,85 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Apply
|
|
2
|
+
* Apply -- resolves a template from a registry and renders it to a target workspace.
|
|
3
|
+
*
|
|
4
|
+
* Per U6 the manifest pipeline is monolithic: parseManifest +
|
|
5
|
+
* resolveManifest + skillFetcher + workspace-folder expansion run
|
|
6
|
+
* inline here. The legacy ManifestAdapter dispatcher and dobby/loom
|
|
7
|
+
* adapters are gone -- parseManifest natively probes for both
|
|
8
|
+
* manifest.yaml and dobby.yaml and normalizes flat-layout dobby-style
|
|
9
|
+
* input into the canonical contents:-wrapped shape.
|
|
3
10
|
*/
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import { parseManifest, resolveManifest, parseAgentManifest, discoverNestedAgents, mergeTeamAndAgentManifests } from './manifest.js';
|
|
6
11
|
import { renderCopilot } from './renderers/copilot.js';
|
|
7
12
|
import { renderClaude } from './renderers/claude.js';
|
|
8
13
|
import { resolveRegistrySkills } from './skill-fetcher.js';
|
|
9
|
-
import { cloneManifestRepos } from './repo-clone.js';
|
|
14
|
+
import { cloneManifestRepos, deriveRepoDir } from './repo-clone.js';
|
|
15
|
+
import { parseManifest, resolveManifest } from './manifest.js';
|
|
16
|
+
import { buildRepoNameMap, expandWorkspaceFolder } from './util/workspace-folder.js';
|
|
10
17
|
/**
|
|
11
18
|
* Apply a template to a workspace directory.
|
|
12
19
|
*
|
|
13
20
|
* Steps:
|
|
14
|
-
* 1. Parse manifest.yaml
|
|
15
|
-
* 2. Resolve all $ref / file references
|
|
16
|
-
* 3.
|
|
17
|
-
* 4.
|
|
18
|
-
* 5.
|
|
21
|
+
* 1. Parse the manifest from templateDir (probes manifest.yaml then dobby.yaml).
|
|
22
|
+
* 2. Resolve all $ref / file / discover references.
|
|
23
|
+
* 3. Filter agents (when --agent was passed).
|
|
24
|
+
* 4. Fetch registry skills if a skillFetcher is provided.
|
|
25
|
+
* 5. Expand ${workspaceFolder:X} placeholders in MCP args/cwd.
|
|
26
|
+
* 6. Render output files using the appropriate renderer.
|
|
27
|
+
* 7. Return list of registry skills that need external install (if not fetched).
|
|
19
28
|
*/
|
|
20
29
|
export async function applyTemplate(options) {
|
|
21
30
|
const { templateDir, registryRoot, outputDir, target, skillFetcher, cloneRepos, cloneMode, execFn, promptFn, linkFn, agents: selectedAgents, onProgress } = options;
|
|
22
31
|
const log = onProgress ?? (() => { });
|
|
32
|
+
// 1. Parse + 2. Resolve. parseManifest natively probes for
|
|
33
|
+
// manifest.yaml or dobby.yaml and normalizes flat-layout aliases.
|
|
23
34
|
log('Parsing manifest...');
|
|
24
|
-
const
|
|
25
|
-
const teamManifest = await parseManifest(manifestPath);
|
|
26
|
-
// Detect nested agents
|
|
27
|
-
const nestedAgentIds = await discoverNestedAgents(templateDir);
|
|
28
|
-
let manifest = teamManifest;
|
|
29
|
-
if (nestedAgentIds.length > 0) {
|
|
30
|
-
// Nested template — determine which agents to include
|
|
31
|
-
const agentIds = selectedAgents && selectedAgents.length > 0
|
|
32
|
-
? selectedAgents.filter((id) => nestedAgentIds.includes(id))
|
|
33
|
-
: nestedAgentIds; // All agents if none specified
|
|
34
|
-
log(`Resolving ${agentIds.length} agent(s): ${agentIds.join(', ')}...`);
|
|
35
|
-
// Parse agent manifests
|
|
36
|
-
const agentManifests = [];
|
|
37
|
-
for (const agentId of agentIds) {
|
|
38
|
-
const agentManifestPath = join(templateDir, 'agents', agentId, 'manifest.yaml');
|
|
39
|
-
const am = await parseAgentManifest(agentManifestPath);
|
|
40
|
-
agentManifests.push(am);
|
|
41
|
-
}
|
|
42
|
-
// Merge team + agent manifests
|
|
43
|
-
manifest = mergeTeamAndAgentManifests(teamManifest, agentManifests, agentIds);
|
|
44
|
-
}
|
|
35
|
+
const manifest = await parseManifest(templateDir);
|
|
45
36
|
log('Resolving references...');
|
|
46
|
-
|
|
47
|
-
//
|
|
37
|
+
let resolved = await resolveManifest(manifest, templateDir, registryRoot, { agents: selectedAgents });
|
|
38
|
+
// 3. --agent filtering: when callers ask for a subset, drop the rest
|
|
39
|
+
// from the resolved list. resolveManifest already populates each
|
|
40
|
+
// ResolvedAgent.mcp from the agent file's frontmatter `mcp-servers:`
|
|
41
|
+
// block (loom#127 / U4.5), so no further per-agent MCP plumbing is
|
|
42
|
+
// needed here.
|
|
43
|
+
if (selectedAgents?.length) {
|
|
44
|
+
const wanted = new Set(selectedAgents);
|
|
45
|
+
resolved = {
|
|
46
|
+
...resolved,
|
|
47
|
+
agents: resolved.agents.filter((a) => wanted.has(a.agentId)),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// 4. Fetch registry skills (when a fetcher is provided)
|
|
48
51
|
if (skillFetcher && resolved.skills.length > 0) {
|
|
49
52
|
log(`Fetching ${resolved.skills.length} skill(s)...`);
|
|
53
|
+
const skills = await resolveRegistrySkills(resolved.skills, skillFetcher);
|
|
54
|
+
resolved = { ...resolved, skills };
|
|
50
55
|
}
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
// 5. Expand ${workspaceFolder:X} in MCP args/cwd. Without this, repo-
|
|
57
|
+
// backed MCP servers (e.g. dotnet projects, scripts referenced from
|
|
58
|
+
// a checked-out path) would emit literal placeholder strings into
|
|
59
|
+
// .mcp.json / .vscode/mcp.json, which the MCP runtime cannot
|
|
60
|
+
// interpret. dirNameFor mirrors what cloneManifestRepos does:
|
|
61
|
+
// deriveRepoDir returns id when set, else the URL basename. Repos
|
|
62
|
+
// are cloned (or junctioned) into <workspace>/<dirName>, so the
|
|
63
|
+
// placeholder resolves to the same path regardless of how the repo
|
|
64
|
+
// was materialized.
|
|
65
|
+
const workDir = outputDir;
|
|
66
|
+
const repoNameMap = buildRepoNameMap(resolved.repos, (r) => deriveRepoDir(r.url ?? '', r.id));
|
|
67
|
+
const expandVar = (s) => expandWorkspaceFolder(s, repoNameMap, workDir);
|
|
68
|
+
const expandMcp = (m) => ({
|
|
69
|
+
...m,
|
|
70
|
+
args: m.args?.map(expandVar),
|
|
71
|
+
cwd: m.cwd ? expandVar(m.cwd) : undefined,
|
|
72
|
+
});
|
|
73
|
+
// Expand placeholders in BOTH team-level (resolved.mcp) and
|
|
74
|
+
// per-agent (agent.mcp) MCPs -- the placeholder semantics are
|
|
75
|
+
// the same for either scope (loom#127).
|
|
76
|
+
resolved = {
|
|
53
77
|
...resolved,
|
|
54
|
-
|
|
78
|
+
mcp: resolved.mcp.map(expandMcp),
|
|
79
|
+
agents: resolved.agents.map((a) => a.mcp ? { ...a, mcp: a.mcp.map(expandMcp) } : a),
|
|
55
80
|
};
|
|
56
|
-
|
|
81
|
+
const manifestWithLocalSkills = resolved;
|
|
82
|
+
// 6. Render
|
|
57
83
|
log(`Rendering ${target} workspace files...`);
|
|
58
84
|
switch (target) {
|
|
59
85
|
case 'copilot':
|
|
@@ -63,7 +89,7 @@ export async function applyTemplate(options) {
|
|
|
63
89
|
await renderClaude(manifestWithLocalSkills, outputDir);
|
|
64
90
|
break;
|
|
65
91
|
case 'cursor':
|
|
66
|
-
// Cursor renderer not yet implemented
|
|
92
|
+
// Cursor renderer not yet implemented -- fall back to copilot
|
|
67
93
|
await renderCopilot(manifestWithLocalSkills, outputDir);
|
|
68
94
|
break;
|
|
69
95
|
default:
|
|
@@ -71,12 +97,12 @@ export async function applyTemplate(options) {
|
|
|
71
97
|
}
|
|
72
98
|
// Clone manifest repos if requested
|
|
73
99
|
let repoCloneResult;
|
|
74
|
-
if (cloneRepos &&
|
|
75
|
-
log(`Cloning ${
|
|
100
|
+
if (cloneRepos && manifestWithLocalSkills.repos.length > 0) {
|
|
101
|
+
log(`Cloning ${manifestWithLocalSkills.repos.length} repo(s)...`);
|
|
76
102
|
// Apply --clone-mode partial: override repos without explicit cloneStrategy
|
|
77
103
|
const repos = cloneMode === 'partial'
|
|
78
|
-
?
|
|
79
|
-
:
|
|
104
|
+
? manifestWithLocalSkills.repos.map((r) => (!r.cloneStrategy && !r.sparse) ? { ...r, cloneStrategy: 'partial' } : r)
|
|
105
|
+
: manifestWithLocalSkills.repos;
|
|
80
106
|
repoCloneResult = await cloneManifestRepos({
|
|
81
107
|
repos,
|
|
82
108
|
workspaceDir: outputDir === '.' ? process.cwd() : outputDir,
|
|
@@ -86,17 +112,20 @@ export async function applyTemplate(options) {
|
|
|
86
112
|
linkFn,
|
|
87
113
|
});
|
|
88
114
|
}
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
115
|
+
// Skills with registry coordinates fall into two buckets:
|
|
116
|
+
// - registrySkills: registry-rooted, content NOT fetched (caller must install)
|
|
117
|
+
// - localizedSkills: registry-rooted, content fetched (rendered locally)
|
|
118
|
+
// A skill gets `content` only when a SkillFetcher resolved it, so
|
|
119
|
+
// presence of `content` is the discriminator.
|
|
120
|
+
const registrySkills = manifestWithLocalSkills.skills
|
|
121
|
+
.filter((s) => s.registry && s.ref && s.content === undefined)
|
|
92
122
|
.map((s) => ({ registry: s.registry, ref: s.ref }));
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
.
|
|
96
|
-
.map((s) => ({ name: s.name, registry: s.originalRegistry, ref: s.originalRef }));
|
|
123
|
+
const localizedSkills = manifestWithLocalSkills.skills
|
|
124
|
+
.filter((s) => s.registry && s.ref && s.content !== undefined)
|
|
125
|
+
.map((s) => ({ name: s.name ?? s.ref, registry: s.registry, ref: s.ref }));
|
|
97
126
|
return {
|
|
98
127
|
target,
|
|
99
|
-
template:
|
|
128
|
+
template: manifestWithLocalSkills.template.id,
|
|
100
129
|
filesGenerated: true,
|
|
101
130
|
registrySkills,
|
|
102
131
|
localizedSkills,
|
package/dist/apply.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../src/apply.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../src/apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chat()` -- single-agent interactive session (powers verb-less `loom <agent>`).
|
|
3
|
+
*
|
|
4
|
+
* Composition note: shares the MCP-discovery + bin-resolution + spawn
|
|
5
|
+
* substrate with `src/run/run.ts` via the shared helpers in `src/copilot/`.
|
|
6
|
+
* The key difference from `run` is stdio: `chat` inherits the parent tty so
|
|
7
|
+
* the user talks to Copilot directly; `run` pipes stdio to capture output.
|
|
8
|
+
*/
|
|
9
|
+
import type { ResolvedAgentRef } from './resolve-agent.js';
|
|
10
|
+
import type { AgentMetadata } from './frontmatter.js';
|
|
11
|
+
import { type Session } from '../sessions/index.js';
|
|
12
|
+
export type LaunchRuntimeKind = 'copilot' | 'claude';
|
|
13
|
+
export interface ChatOptions {
|
|
14
|
+
/** Agent id to resolve. */
|
|
15
|
+
agentId: string;
|
|
16
|
+
/** Working directory (where to resolve the agent from and launch in). */
|
|
17
|
+
cwd: string;
|
|
18
|
+
/** One-shot prompt. If set with interactive=false, runs non-interactively. */
|
|
19
|
+
prompt?: string;
|
|
20
|
+
/**
|
|
21
|
+
* When true, runs non-interactively (suitable for `--prompt` one-shot or scripting).
|
|
22
|
+
* When false (default), starts interactive mode with stdio inherited.
|
|
23
|
+
*/
|
|
24
|
+
interactive?: boolean;
|
|
25
|
+
/** Runtime to launch. Default 'copilot'. */
|
|
26
|
+
runtime?: LaunchRuntimeKind;
|
|
27
|
+
/** Extra args appended to the runtime command line. */
|
|
28
|
+
extraArgs?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Pre-resolved agent. If provided, skips resolution. Used for tests and
|
|
31
|
+
* for callers that want to resolve via a different strategy.
|
|
32
|
+
*/
|
|
33
|
+
resolved?: ResolvedAgentRef;
|
|
34
|
+
/**
|
|
35
|
+
* Optional callback invoked once we have the agent + its metadata, but
|
|
36
|
+
* before spawning the runtime. Defaults to printing a banner to stderr.
|
|
37
|
+
* Pass `() => {}` to suppress.
|
|
38
|
+
*/
|
|
39
|
+
onPreLaunch?: (info: PreLaunchInfo) => void;
|
|
40
|
+
/**
|
|
41
|
+
* When false, skip session-dir creation. Default true.
|
|
42
|
+
* Useful for tests and for scripted one-shots that don't want to litter
|
|
43
|
+
* `~/.loom/sessions/` with short-lived records.
|
|
44
|
+
*/
|
|
45
|
+
recordSession?: boolean;
|
|
46
|
+
/** Optional friendly label written to session meta.yaml. */
|
|
47
|
+
sessionName?: string;
|
|
48
|
+
/** Origin hint written to the session meta. Default 'workspace'. */
|
|
49
|
+
sessionOrigin?: 'workspace' | 'staged';
|
|
50
|
+
/**
|
|
51
|
+
* Pre-existing session to use instead of creating one. When set, the
|
|
52
|
+
* session is still finalized on exit. Implies `recordSession` was handled
|
|
53
|
+
* by the caller (e.g., registry staging).
|
|
54
|
+
*/
|
|
55
|
+
session?: Session;
|
|
56
|
+
}
|
|
57
|
+
export interface PreLaunchInfo {
|
|
58
|
+
agent: ResolvedAgentRef;
|
|
59
|
+
metadata: AgentMetadata;
|
|
60
|
+
runtime: LaunchRuntimeKind;
|
|
61
|
+
interactive: boolean;
|
|
62
|
+
cwd: string;
|
|
63
|
+
/** Session handle if a session was created; null when recordSession=false. */
|
|
64
|
+
session: Session | null;
|
|
65
|
+
}
|
|
66
|
+
export interface ChatResult {
|
|
67
|
+
/** Resolved agent reference used for this launch. */
|
|
68
|
+
agent: ResolvedAgentRef;
|
|
69
|
+
/** Exit code from the runtime process. */
|
|
70
|
+
exitCode: number | null;
|
|
71
|
+
/** Total duration in milliseconds. */
|
|
72
|
+
durationMs: number;
|
|
73
|
+
/** Session record, if one was created. */
|
|
74
|
+
session: Session | null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Build the command line for a given runtime. Exported for testability.
|
|
78
|
+
*/
|
|
79
|
+
export declare function buildChatCommand(params: {
|
|
80
|
+
runtime: LaunchRuntimeKind;
|
|
81
|
+
prompt?: string;
|
|
82
|
+
interactive: boolean;
|
|
83
|
+
agent: ResolvedAgentRef;
|
|
84
|
+
/**
|
|
85
|
+
* The value to pass to copilot's `--agent` flag. Copilot identifies
|
|
86
|
+
* agents by their frontmatter `name:` (e.g. "Live Site"), not by the
|
|
87
|
+
* filename slug (e.g. "live-site"). Callers should pass
|
|
88
|
+
* `metadata.name ?? agent.agentId` so the spawn finds the agent.
|
|
89
|
+
*/
|
|
90
|
+
agentDisplayName?: string;
|
|
91
|
+
mcpConfigPath?: string | null;
|
|
92
|
+
extraArgs?: string[];
|
|
93
|
+
}): {
|
|
94
|
+
bin: string;
|
|
95
|
+
args: string[];
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Default banner printer. Emits a 3-line summary to stderr before the
|
|
99
|
+
* runtime takes over the terminal. Kept minimal so it doesn't compete with
|
|
100
|
+
* Copilot CLI's own startup output.
|
|
101
|
+
*/
|
|
102
|
+
export declare function defaultPreChatBanner(info: PreLaunchInfo): void;
|
|
103
|
+
/**
|
|
104
|
+
* Launch an agent interactively. Binds stdio to the parent tty so the
|
|
105
|
+
* user talks to Copilot directly. Returns when the runtime process exits.
|
|
106
|
+
*/
|
|
107
|
+
export declare function chat(options: ChatOptions): Promise<ChatResult>;
|
|
108
|
+
//# sourceMappingURL=chat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/chat/chat.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAG3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAItD,OAAO,EAGL,KAAK,OAAO,EACb,MAAM,sBAAsB,CAAC;AAE9B,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,QAAQ,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAC5C;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,aAAa,CAAC,EAAE,WAAW,GAAG,QAAQ,CAAC;IACvC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,WAAW,EAAE,OAAO,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,qDAAqD;IACrD,KAAK,EAAE,gBAAgB,CAAC;IACxB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACzB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,KAAK,EAAE,gBAAgB,CAAC;IACxB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAwClC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CA6B9D;AAgCD;;;GAGG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAqGpE"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chat()` -- single-agent interactive session (powers verb-less `loom <agent>`).
|
|
3
|
+
*
|
|
4
|
+
* Composition note: shares the MCP-discovery + bin-resolution + spawn
|
|
5
|
+
* substrate with `src/run/run.ts` via the shared helpers in `src/copilot/`.
|
|
6
|
+
* The key difference from `run` is stdio: `chat` inherits the parent tty so
|
|
7
|
+
* the user talks to Copilot directly; `run` pipes stdio to capture output.
|
|
8
|
+
*/
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { resolveAgentInWorkspace } from './resolve-agent.js';
|
|
12
|
+
import { parseAgentFrontmatter } from './frontmatter.js';
|
|
13
|
+
import { resolveCopilotBin } from '../copilot/resolve.js';
|
|
14
|
+
import { buildCopilotMcpConfigJson, materializeTempMcpConfig } from '../copilot/mcp.js';
|
|
15
|
+
import { spawnInteractive } from '../copilot/spawn.js';
|
|
16
|
+
import { createSession, finalizeSession, } from '../sessions/index.js';
|
|
17
|
+
/**
|
|
18
|
+
* Build the command line for a given runtime. Exported for testability.
|
|
19
|
+
*/
|
|
20
|
+
export function buildChatCommand(params) {
|
|
21
|
+
const { runtime, prompt, interactive, agent, agentDisplayName, mcpConfigPath, extraArgs = [] } = params;
|
|
22
|
+
switch (runtime) {
|
|
23
|
+
case 'copilot': {
|
|
24
|
+
const bin = resolveCopilotBin();
|
|
25
|
+
const args = [];
|
|
26
|
+
if (prompt) {
|
|
27
|
+
// -p runs non-interactive and exits; -i <prompt> runs the kickoff
|
|
28
|
+
// then keeps the interactive session open. Interactive with no
|
|
29
|
+
// prompt: don't emit -i (it takes a required value and would
|
|
30
|
+
// swallow the next flag as the prompt).
|
|
31
|
+
args.push(interactive ? '-i' : '-p', prompt);
|
|
32
|
+
}
|
|
33
|
+
args.push('--allow-all-tools');
|
|
34
|
+
args.push('--add-dir', agent.workspaceDir);
|
|
35
|
+
// Activate the agent persona (otherwise Copilot just loads the file
|
|
36
|
+
// as a tool but talks back as plain Copilot, with no visible identity).
|
|
37
|
+
// --agent wants the frontmatter name (e.g. "Live Site"), not the
|
|
38
|
+
// filename slug (e.g. "live-site"); fall back to slug if metadata
|
|
39
|
+
// wasn't passed (the spawn will then surface copilot's own
|
|
40
|
+
// "No such agent" error).
|
|
41
|
+
args.push('--agent', agentDisplayName ?? agent.agentId);
|
|
42
|
+
if (mcpConfigPath) {
|
|
43
|
+
// Copilot expects JSON or a file path prefixed with '@'.
|
|
44
|
+
args.push('--additional-mcp-config', `@${mcpConfigPath}`);
|
|
45
|
+
}
|
|
46
|
+
args.push(...extraArgs);
|
|
47
|
+
return { bin, args };
|
|
48
|
+
}
|
|
49
|
+
case 'claude': {
|
|
50
|
+
const bin = 'claude';
|
|
51
|
+
const args = ['--dangerously-skip-permissions'];
|
|
52
|
+
if (prompt)
|
|
53
|
+
args.push('--prompt', prompt);
|
|
54
|
+
args.push(...extraArgs);
|
|
55
|
+
return { bin, args };
|
|
56
|
+
}
|
|
57
|
+
default:
|
|
58
|
+
throw new Error(`Unknown runtime: ${String(runtime)}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Default banner printer. Emits a 3-line summary to stderr before the
|
|
63
|
+
* runtime takes over the terminal. Kept minimal so it doesn't compete with
|
|
64
|
+
* Copilot CLI's own startup output.
|
|
65
|
+
*/
|
|
66
|
+
export function defaultPreChatBanner(info) {
|
|
67
|
+
const out = process.stderr;
|
|
68
|
+
const agentId = info.agent.agentId;
|
|
69
|
+
const displayName = info.metadata.name ?? agentId;
|
|
70
|
+
const mode = info.interactive ? 'interactive' : 'one-shot';
|
|
71
|
+
const runtimeLabel = `${info.runtime} \u00b7 ${mode}`;
|
|
72
|
+
// Top rule + agent badge
|
|
73
|
+
out.write('\n');
|
|
74
|
+
out.write(` ${chalk.cyan.bold(agentId)} ${chalk.dim(runtimeLabel)}\n`);
|
|
75
|
+
if (displayName !== agentId) {
|
|
76
|
+
out.write(` ${chalk.dim(displayName)}\n`);
|
|
77
|
+
}
|
|
78
|
+
// Description (wrapped to ~80 cols)
|
|
79
|
+
if (info.metadata.description) {
|
|
80
|
+
out.write('\n');
|
|
81
|
+
for (const line of wrapText(info.metadata.description, 76)) {
|
|
82
|
+
out.write(` ${line}\n`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Footer facts
|
|
86
|
+
out.write('\n');
|
|
87
|
+
out.write(` ${chalk.dim('workspace')} ${tildify(info.agent.workspaceDir)}\n`);
|
|
88
|
+
if (info.session) {
|
|
89
|
+
out.write(` ${chalk.dim('session')} ${info.session.meta.id}\n`);
|
|
90
|
+
}
|
|
91
|
+
out.write('\n');
|
|
92
|
+
}
|
|
93
|
+
function tildify(p) {
|
|
94
|
+
const home = homedir();
|
|
95
|
+
if (p === home)
|
|
96
|
+
return '~';
|
|
97
|
+
if (p.toLowerCase().startsWith(home.toLowerCase() + '\\')) {
|
|
98
|
+
return '~\\' + p.slice(home.length + 1);
|
|
99
|
+
}
|
|
100
|
+
if (p.toLowerCase().startsWith(home.toLowerCase() + '/')) {
|
|
101
|
+
return '~/' + p.slice(home.length + 1);
|
|
102
|
+
}
|
|
103
|
+
return p;
|
|
104
|
+
}
|
|
105
|
+
function wrapText(text, width) {
|
|
106
|
+
const words = text.replace(/\s+/g, ' ').trim().split(' ');
|
|
107
|
+
const lines = [];
|
|
108
|
+
let current = '';
|
|
109
|
+
for (const word of words) {
|
|
110
|
+
if (!current) {
|
|
111
|
+
current = word;
|
|
112
|
+
}
|
|
113
|
+
else if (current.length + 1 + word.length <= width) {
|
|
114
|
+
current += ' ' + word;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
lines.push(current);
|
|
118
|
+
current = word;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (current)
|
|
122
|
+
lines.push(current);
|
|
123
|
+
return lines;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Launch an agent interactively. Binds stdio to the parent tty so the
|
|
127
|
+
* user talks to Copilot directly. Returns when the runtime process exits.
|
|
128
|
+
*/
|
|
129
|
+
export async function chat(options) {
|
|
130
|
+
const startTime = Date.now();
|
|
131
|
+
const runtime = options.runtime ?? 'copilot';
|
|
132
|
+
const interactive = options.interactive ?? true;
|
|
133
|
+
const agent = options.resolved ?? resolveAgentInWorkspace({
|
|
134
|
+
agentId: options.agentId,
|
|
135
|
+
cwd: options.cwd,
|
|
136
|
+
});
|
|
137
|
+
if (!agent) {
|
|
138
|
+
throw new Error(`Agent "${options.agentId}" not found in workspace.\n` +
|
|
139
|
+
` Looked for: ${options.cwd}/.github/agents/${options.agentId}.agent.md\n` +
|
|
140
|
+
` (Installed-shortname and registry resolution layers are not yet implemented.)`);
|
|
141
|
+
}
|
|
142
|
+
const metadata = parseAgentFrontmatter(agent.agentFilePath);
|
|
143
|
+
// Record the session (unless opted out or supplied by caller) before we
|
|
144
|
+
// hand off to the runtime. We want this row to exist even if the spawn
|
|
145
|
+
// itself explodes, so `loom sessions list` can see failed launches too.
|
|
146
|
+
let session = options.session ?? null;
|
|
147
|
+
if (!session && options.recordSession !== false) {
|
|
148
|
+
session = createSession({
|
|
149
|
+
agentId: agent.agentId,
|
|
150
|
+
agentName: metadata.name,
|
|
151
|
+
runtime,
|
|
152
|
+
workspaceDir: agent.workspaceDir,
|
|
153
|
+
origin: options.sessionOrigin ?? 'workspace',
|
|
154
|
+
name: options.sessionName,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
const preLaunch = options.onPreLaunch ?? defaultPreChatBanner;
|
|
158
|
+
preLaunch({ agent, metadata, runtime, interactive, cwd: options.cwd, session });
|
|
159
|
+
// Persist MCP config to a temp file so --additional-mcp-config can reference it.
|
|
160
|
+
// Returns null when the discovered config has zero usable servers; the temp
|
|
161
|
+
// dir is cleaned up in the finally block.
|
|
162
|
+
let tempMcp = null;
|
|
163
|
+
if (runtime === 'copilot') {
|
|
164
|
+
tempMcp = await materializeTempMcpConfig({
|
|
165
|
+
baseConfig: buildCopilotMcpConfigJson(agent.workspaceDir),
|
|
166
|
+
dirPrefix: 'loom-chat-',
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const { bin, args } = buildChatCommand({
|
|
171
|
+
runtime,
|
|
172
|
+
prompt: options.prompt,
|
|
173
|
+
interactive,
|
|
174
|
+
agent,
|
|
175
|
+
agentDisplayName: metadata.name ?? agent.agentId,
|
|
176
|
+
mcpConfigPath: tempMcp?.path ?? null,
|
|
177
|
+
extraArgs: options.extraArgs,
|
|
178
|
+
});
|
|
179
|
+
// Set the terminal tab title so the user can always see which agent
|
|
180
|
+
// they are in, even after Copilot's alt-screen TUI clears our banner.
|
|
181
|
+
// OSC 0 is supported by Windows Terminal, iTerm2, GNOME Terminal, etc.
|
|
182
|
+
// We can't read the previous title back, so on exit we restore to a
|
|
183
|
+
// neutral "loom" so it's obvious the agent has ended.
|
|
184
|
+
const titleTag = `loom: ${agent.agentId}`;
|
|
185
|
+
const setTitle = (s) => {
|
|
186
|
+
if (process.stderr.isTTY)
|
|
187
|
+
process.stderr.write(`\x1b]0;${s}\x07`);
|
|
188
|
+
};
|
|
189
|
+
setTitle(titleTag);
|
|
190
|
+
const restoreTitle = () => setTitle('loom');
|
|
191
|
+
let exitCode = null;
|
|
192
|
+
let errored = false;
|
|
193
|
+
try {
|
|
194
|
+
const result = await spawnInteractive({ bin, args, cwd: agent.workspaceDir });
|
|
195
|
+
exitCode = result.exitCode;
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
errored = true;
|
|
199
|
+
restoreTitle();
|
|
200
|
+
if (session) {
|
|
201
|
+
finalizeSession(session, {
|
|
202
|
+
exitCode: null,
|
|
203
|
+
durationMs: Date.now() - startTime,
|
|
204
|
+
status: 'errored',
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
throw err;
|
|
208
|
+
}
|
|
209
|
+
restoreTitle();
|
|
210
|
+
const durationMs = Date.now() - startTime;
|
|
211
|
+
if (session && !errored) {
|
|
212
|
+
session = finalizeSession(session, { exitCode, durationMs });
|
|
213
|
+
}
|
|
214
|
+
return { agent, exitCode, durationMs, session };
|
|
215
|
+
}
|
|
216
|
+
finally {
|
|
217
|
+
// Best-effort cleanup of the per-launch MCP-config temp dir.
|
|
218
|
+
tempMcp?.cleanup();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=chat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/chat/chat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace + agent discovery for `loom <agent>` and `loom agents`.
|
|
3
|
+
*
|
|
4
|
+
* - `findWorkspaceRoot(cwd)`: walk up the directory tree looking for a
|
|
5
|
+
* `.github/agents/` directory. Matches git's "find-root" ergonomics so
|
|
6
|
+
* users don't have to be at the exact workspace root.
|
|
7
|
+
* - `listWorkspaceAgents(workspaceDir)`: enumerate `.agent.md` files under
|
|
8
|
+
* `.github/agents/` with their parsed frontmatter metadata.
|
|
9
|
+
*/
|
|
10
|
+
import type { AgentMetadata } from './frontmatter.js';
|
|
11
|
+
export interface WorkspaceAgentInfo {
|
|
12
|
+
/** Agent id (filename stem before `.agent.md`) */
|
|
13
|
+
agentId: string;
|
|
14
|
+
/** Absolute path to the agent file */
|
|
15
|
+
agentFilePath: string;
|
|
16
|
+
/** Frontmatter metadata */
|
|
17
|
+
metadata: AgentMetadata;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Walk up from `cwd` looking for a directory that contains `.github/agents/`.
|
|
21
|
+
* Returns the absolute path of that directory, or null if no such ancestor
|
|
22
|
+
* exists (including the filesystem root).
|
|
23
|
+
*/
|
|
24
|
+
export declare function findWorkspaceRoot(cwd: string): string | null;
|
|
25
|
+
/**
|
|
26
|
+
* List all agents in a workspace's `.github/agents/` directory.
|
|
27
|
+
* Returns an empty array if the directory doesn't exist.
|
|
28
|
+
*/
|
|
29
|
+
export declare function listWorkspaceAgents(workspaceDir: string): WorkspaceAgentInfo[];
|
|
30
|
+
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/chat/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B;IAC3B,QAAQ,EAAE,aAAa,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB5D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAyB9E"}
|