@agent-loom/loom 1.0.1 → 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.
Files changed (145) hide show
  1. package/README.md +69 -0
  2. package/dist/acp/client.d.ts +182 -0
  3. package/dist/acp/client.d.ts.map +1 -0
  4. package/dist/acp/client.js +432 -0
  5. package/dist/acp/client.js.map +1 -0
  6. package/dist/acp/index.d.ts +5 -0
  7. package/dist/acp/index.d.ts.map +1 -0
  8. package/dist/acp/index.js +3 -0
  9. package/dist/acp/index.js.map +1 -0
  10. package/dist/acp/run.d.ts +41 -0
  11. package/dist/acp/run.d.ts.map +1 -0
  12. package/dist/acp/run.js +32 -0
  13. package/dist/acp/run.js.map +1 -0
  14. package/dist/apply.d.ts +17 -6
  15. package/dist/apply.d.ts.map +1 -1
  16. package/dist/apply.js +85 -47
  17. package/dist/apply.js.map +1 -1
  18. package/dist/chat/chat.d.ts +108 -0
  19. package/dist/chat/chat.d.ts.map +1 -0
  20. package/dist/chat/chat.js +221 -0
  21. package/dist/chat/chat.js.map +1 -0
  22. package/dist/chat/discovery.d.ts +30 -0
  23. package/dist/chat/discovery.d.ts.map +1 -0
  24. package/dist/chat/discovery.js +68 -0
  25. package/dist/chat/discovery.js.map +1 -0
  26. package/dist/chat/frontmatter.d.ts +12 -0
  27. package/dist/chat/frontmatter.d.ts.map +1 -0
  28. package/dist/chat/frontmatter.js +11 -0
  29. package/dist/chat/frontmatter.js.map +1 -0
  30. package/dist/chat/index.d.ts +16 -0
  31. package/dist/chat/index.d.ts.map +1 -0
  32. package/dist/chat/index.js +11 -0
  33. package/dist/chat/index.js.map +1 -0
  34. package/dist/chat/registry.d.ts +73 -0
  35. package/dist/chat/registry.d.ts.map +1 -0
  36. package/dist/chat/registry.js +118 -0
  37. package/dist/chat/registry.js.map +1 -0
  38. package/dist/chat/resolve-agent.d.ts +39 -0
  39. package/dist/chat/resolve-agent.d.ts.map +1 -0
  40. package/dist/chat/resolve-agent.js +36 -0
  41. package/dist/chat/resolve-agent.js.map +1 -0
  42. package/dist/chat/suggest.d.ts +20 -0
  43. package/dist/chat/suggest.d.ts.map +1 -0
  44. package/dist/chat/suggest.js +55 -0
  45. package/dist/chat/suggest.js.map +1 -0
  46. package/dist/cli.js +628 -75
  47. package/dist/cli.js.map +1 -1
  48. package/dist/clone.d.ts +21 -3
  49. package/dist/clone.d.ts.map +1 -1
  50. package/dist/clone.js +240 -12
  51. package/dist/clone.js.map +1 -1
  52. package/dist/copilot/mcp.d.ts +48 -0
  53. package/dist/copilot/mcp.d.ts.map +1 -0
  54. package/dist/copilot/mcp.js +146 -0
  55. package/dist/copilot/mcp.js.map +1 -0
  56. package/dist/copilot/resolve.d.ts +33 -0
  57. package/dist/copilot/resolve.d.ts.map +1 -0
  58. package/dist/copilot/resolve.js +96 -0
  59. package/dist/copilot/resolve.js.map +1 -0
  60. package/dist/copilot/spawn.d.ts +51 -0
  61. package/dist/copilot/spawn.d.ts.map +1 -0
  62. package/dist/copilot/spawn.js +132 -0
  63. package/dist/copilot/spawn.js.map +1 -0
  64. package/dist/index.d.ts +19 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +15 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/launch/index.d.ts +10 -0
  69. package/dist/launch/index.d.ts.map +1 -0
  70. package/dist/launch/index.js +9 -0
  71. package/dist/launch/index.js.map +1 -0
  72. package/dist/launch/stage.d.ts +62 -0
  73. package/dist/launch/stage.d.ts.map +1 -0
  74. package/dist/launch/stage.js +108 -0
  75. package/dist/launch/stage.js.map +1 -0
  76. package/dist/manifest.d.ts +165 -18
  77. package/dist/manifest.d.ts.map +1 -1
  78. package/dist/manifest.js +980 -225
  79. package/dist/manifest.js.map +1 -1
  80. package/dist/renderers/claude.d.ts +5 -0
  81. package/dist/renderers/claude.d.ts.map +1 -1
  82. package/dist/renderers/claude.js +17 -3
  83. package/dist/renderers/claude.js.map +1 -1
  84. package/dist/renderers/copilot.d.ts +1 -1
  85. package/dist/renderers/copilot.d.ts.map +1 -1
  86. package/dist/renderers/copilot.js +205 -22
  87. package/dist/renderers/copilot.js.map +1 -1
  88. package/dist/repo-clone.js +17 -11
  89. package/dist/repo-clone.js.map +1 -1
  90. package/dist/resolve-template.d.ts +12 -4
  91. package/dist/resolve-template.d.ts.map +1 -1
  92. package/dist/resolve-template.js +39 -8
  93. package/dist/resolve-template.js.map +1 -1
  94. package/dist/run/index.d.ts +4 -0
  95. package/dist/run/index.d.ts.map +1 -0
  96. package/dist/run/index.js +2 -0
  97. package/dist/run/index.js.map +1 -0
  98. package/dist/run/run.d.ts +143 -0
  99. package/dist/run/run.d.ts.map +1 -0
  100. package/dist/run/run.js +406 -0
  101. package/dist/run/run.js.map +1 -0
  102. package/dist/search-registry.d.ts +10 -3
  103. package/dist/search-registry.d.ts.map +1 -1
  104. package/dist/search-registry.js +16 -16
  105. package/dist/search-registry.js.map +1 -1
  106. package/dist/sessions/index.d.ts +16 -0
  107. package/dist/sessions/index.d.ts.map +1 -0
  108. package/dist/sessions/index.js +15 -0
  109. package/dist/sessions/index.js.map +1 -0
  110. package/dist/sessions/store.d.ts +56 -0
  111. package/dist/sessions/store.d.ts.map +1 -0
  112. package/dist/sessions/store.js +220 -0
  113. package/dist/sessions/store.js.map +1 -0
  114. package/dist/sessions/types.d.ts +62 -0
  115. package/dist/sessions/types.d.ts.map +1 -0
  116. package/dist/sessions/types.js +5 -0
  117. package/dist/sessions/types.js.map +1 -0
  118. package/dist/skill-fetcher.d.ts.map +1 -1
  119. package/dist/skill-fetcher.js +5 -6
  120. package/dist/skill-fetcher.js.map +1 -1
  121. package/dist/types.d.ts +123 -41
  122. package/dist/types.d.ts.map +1 -1
  123. package/dist/types.js +12 -0
  124. package/dist/types.js.map +1 -1
  125. package/dist/util/binary-cache.d.ts +53 -0
  126. package/dist/util/binary-cache.d.ts.map +1 -0
  127. package/dist/util/binary-cache.js +211 -0
  128. package/dist/util/binary-cache.js.map +1 -0
  129. package/dist/util/frontmatter.d.ts +53 -0
  130. package/dist/util/frontmatter.d.ts.map +1 -0
  131. package/dist/util/frontmatter.js +85 -0
  132. package/dist/util/frontmatter.js.map +1 -0
  133. package/dist/util/loom-home.d.ts +19 -0
  134. package/dist/util/loom-home.d.ts.map +1 -0
  135. package/dist/util/loom-home.js +37 -0
  136. package/dist/util/loom-home.js.map +1 -0
  137. package/dist/util/workspace-folder.d.ts +29 -0
  138. package/dist/util/workspace-folder.d.ts.map +1 -0
  139. package/dist/util/workspace-folder.js +43 -0
  140. package/dist/util/workspace-folder.js.map +1 -0
  141. package/dist/validate.d.ts +7 -1
  142. package/dist/validate.d.ts.map +1 -1
  143. package/dist/validate.js +90 -17
  144. package/dist/validate.js.map +1 -1
  145. package/package.json +31 -2
@@ -0,0 +1,68 @@
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 { existsSync, readdirSync, statSync } from 'node:fs';
11
+ import { dirname, join, resolve } from 'node:path';
12
+ import { parseAgentFrontmatter } from './frontmatter.js';
13
+ /**
14
+ * Walk up from `cwd` looking for a directory that contains `.github/agents/`.
15
+ * Returns the absolute path of that directory, or null if no such ancestor
16
+ * exists (including the filesystem root).
17
+ */
18
+ export function findWorkspaceRoot(cwd) {
19
+ let current = resolve(cwd);
20
+ // Guard against infinite loops on edge cases where dirname() is a no-op.
21
+ let safety = 64;
22
+ while (safety-- > 0) {
23
+ const candidate = join(current, '.github', 'agents');
24
+ if (existsSync(candidate)) {
25
+ try {
26
+ if (statSync(candidate).isDirectory())
27
+ return current;
28
+ }
29
+ catch { /* ignore and keep walking */ }
30
+ }
31
+ const parent = dirname(current);
32
+ if (parent === current)
33
+ return null;
34
+ current = parent;
35
+ }
36
+ return null;
37
+ }
38
+ /**
39
+ * List all agents in a workspace's `.github/agents/` directory.
40
+ * Returns an empty array if the directory doesn't exist.
41
+ */
42
+ export function listWorkspaceAgents(workspaceDir) {
43
+ const agentDir = join(workspaceDir, '.github', 'agents');
44
+ if (!existsSync(agentDir))
45
+ return [];
46
+ let entries;
47
+ try {
48
+ entries = readdirSync(agentDir);
49
+ }
50
+ catch {
51
+ return [];
52
+ }
53
+ const agents = [];
54
+ for (const entry of entries) {
55
+ if (!entry.endsWith('.agent.md'))
56
+ continue;
57
+ const agentId = entry.slice(0, -'.agent.md'.length);
58
+ const agentFilePath = join(agentDir, entry);
59
+ agents.push({
60
+ agentId,
61
+ agentFilePath,
62
+ metadata: parseAgentFrontmatter(agentFilePath),
63
+ });
64
+ }
65
+ agents.sort((a, b) => a.agentId.localeCompare(b.agentId));
66
+ return agents;
67
+ }
68
+ //# sourceMappingURL=discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/chat/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAYzD;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,IAAI,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3B,yEAAyE;IACzE,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,OAAO,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QACrD,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC;gBACH,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE;oBAAE,OAAO,OAAO,CAAC;YACxD,CAAC;YAAC,MAAM,CAAC,CAAC,6BAA6B,CAAC,CAAC;QAC3C,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,MAAM,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC;QACpC,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAAoB;IACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IACzD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IAErC,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC;YAAE,SAAS;QAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC;YACV,OAAO;YACP,aAAa;YACb,QAAQ,EAAE,qBAAqB,CAAC,aAAa,CAAC;SAC/C,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1D,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Backwards-compatible re-exports of the frontmatter parser.
3
+ *
4
+ * The implementation lives under `src/util/frontmatter.ts` because every
5
+ * layer (chat, run, renderers, manifest) needs it — having it here forced
6
+ * renderers and util code to depend on a chat module. Existing imports
7
+ * from `./chat/frontmatter.js` still work via these re-exports, but new
8
+ * code should import from `../util/frontmatter.js`.
9
+ */
10
+ export { parseFrontmatterRaw, stripFrontmatter, parseAgentFrontmatter, } from '../util/frontmatter.js';
11
+ export type { AgentMetadata } from '../util/frontmatter.js';
12
+ //# sourceMappingURL=frontmatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/chat/frontmatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Backwards-compatible re-exports of the frontmatter parser.
3
+ *
4
+ * The implementation lives under `src/util/frontmatter.ts` because every
5
+ * layer (chat, run, renderers, manifest) needs it — having it here forced
6
+ * renderers and util code to depend on a chat module. Existing imports
7
+ * from `./chat/frontmatter.js` still work via these re-exports, but new
8
+ * code should import from `../util/frontmatter.js`.
9
+ */
10
+ export { parseFrontmatterRaw, stripFrontmatter, parseAgentFrontmatter, } from '../util/frontmatter.js';
11
+ //# sourceMappingURL=frontmatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/chat/frontmatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Public exports for the chat module.
3
+ * See `chat.ts` for behavior.
4
+ */
5
+ export { chat, buildChatCommand, defaultPreChatBanner } from './chat.js';
6
+ export type { ChatOptions, ChatResult, LaunchRuntimeKind, PreLaunchInfo, } from './chat.js';
7
+ export { resolveAgentInWorkspace } from './resolve-agent.js';
8
+ export type { ResolvedAgentRef, ResolveAgentOptions } from './resolve-agent.js';
9
+ export { findWorkspaceRoot, listWorkspaceAgents } from './discovery.js';
10
+ export type { WorkspaceAgentInfo } from './discovery.js';
11
+ export { parseAgentFrontmatter } from './frontmatter.js';
12
+ export type { AgentMetadata } from './frontmatter.js';
13
+ export { suggestClosest, levenshtein } from './suggest.js';
14
+ export { resolveAgentFromRegistry, listRegistryAgentIds, listRegistryAgents, } from './registry.js';
15
+ export type { RegistryAgentHit, RegistryAgentListing, ResolveAgentFromRegistryOptions, } from './registry.js';
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACzE,YAAY,EACV,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,aAAa,GACd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,YAAY,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACxE,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,+BAA+B,GAChC,MAAM,eAAe,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Public exports for the chat module.
3
+ * See `chat.ts` for behavior.
4
+ */
5
+ export { chat, buildChatCommand, defaultPreChatBanner } from './chat.js';
6
+ export { resolveAgentInWorkspace } from './resolve-agent.js';
7
+ export { findWorkspaceRoot, listWorkspaceAgents } from './discovery.js';
8
+ export { parseAgentFrontmatter } from './frontmatter.js';
9
+ export { suggestClosest, levenshtein } from './suggest.js';
10
+ export { resolveAgentFromRegistry, listRegistryAgentIds, listRegistryAgents, } from './registry.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAQzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAExE,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Registry resolution for `loom <agent>`.
3
+ *
4
+ * When an agent id doesn't match an in-workspace `.github/agents/<id>.agent.md`
5
+ * file, we fall back to configured Loom registries. The registry index lists
6
+ * templates and their nested agents; we search it for an exact agent-id hit
7
+ * and return enough information for the launch code to stage the template
8
+ * into a session workspace via `applyTemplate`.
9
+ *
10
+ * This is deliberately *resolution only* — no staging, no filesystem writes
11
+ * beyond what `resolveRegistryIndexPaths` already does when it auto-clones a
12
+ * registry.
13
+ */
14
+ import type { AgentRegistryEntry, RegistryTemplateEntry } from '../types.js';
15
+ export interface RegistryAgentHit {
16
+ /** Registry name (as configured in ~/.loom/config.yaml). */
17
+ registryName: string;
18
+ /** Absolute path to the registry root (the dir containing index.yaml). */
19
+ registryRoot: string;
20
+ /** Id of the template that contains the matched agent. */
21
+ templateId: string;
22
+ /** Absolute path to the template directory (contains manifest.yaml). */
23
+ templatePath: string;
24
+ /** The matched agent id (== requested id). */
25
+ agentId: string;
26
+ /** Frontmatter/index metadata for the matched agent, when available. */
27
+ agent: AgentRegistryEntry;
28
+ /** Template metadata, for UX. */
29
+ template: RegistryTemplateEntry;
30
+ }
31
+ export interface ResolveAgentFromRegistryOptions {
32
+ /** The agent id to resolve (exact match against index entries). */
33
+ agentId: string;
34
+ /** Override `~/.loom` config dir (tests, power users). */
35
+ configDir?: string;
36
+ /** Override workspace dir used for registry cache. */
37
+ workspaceDir?: string;
38
+ /** Inject an exec fn to suppress real git clones during tests. */
39
+ execFn?: (cmd: string) => void;
40
+ /** Limit resolution to a single named registry. */
41
+ registryName?: string;
42
+ }
43
+ /**
44
+ * Walk all configured registries searching for a template whose nested
45
+ * agents include an exact match for `agentId`.
46
+ *
47
+ * Returns the first hit (deterministic: registries are iterated in config
48
+ * order). Returns an object describing the match or null if none found.
49
+ *
50
+ * Side effects: `resolveRegistryIndexPaths` may auto-clone registries that
51
+ * aren't yet cached. Callers that want a pure lookup must pre-populate the
52
+ * cache.
53
+ */
54
+ export declare function resolveAgentFromRegistry(options: ResolveAgentFromRegistryOptions): Promise<RegistryAgentHit | null>;
55
+ /**
56
+ * Discover all agent ids offered across configured registries — used for
57
+ * fuzzy suggestions when an exact match fails. Registry errors are swallowed.
58
+ */
59
+ export declare function listRegistryAgentIds(options?: Omit<ResolveAgentFromRegistryOptions, 'agentId'>): Promise<string[]>;
60
+ export interface RegistryAgentListing {
61
+ agentId: string;
62
+ agentName?: string;
63
+ description?: string;
64
+ registryName: string;
65
+ templateId: string;
66
+ tags?: string[];
67
+ }
68
+ /**
69
+ * Discover all agents across configured registries with enough metadata to
70
+ * render a "what could I launch?" listing. Registry errors are swallowed.
71
+ */
72
+ export declare function listRegistryAgents(options?: Omit<ResolveAgentFromRegistryOptions, 'agentId'>): Promise<RegistryAgentListing[]>;
73
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/chat/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,OAAO,KAAK,EAAiB,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE5F,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,YAAY,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,YAAY,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,KAAK,EAAE,kBAAkB,CAAC;IAC1B,iCAAiC;IACjC,QAAQ,EAAE,qBAAqB,CAAC;CACjC;AAED,MAAM,WAAW,+BAA+B;IAC9C,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAuClC;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,GAAE,IAAI,CAAC,+BAA+B,EAAE,SAAS,CAAM,GAC7D,OAAO,CAAC,MAAM,EAAE,CAAC,CAGnB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,GAAE,IAAI,CAAC,+BAA+B,EAAE,SAAS,CAAM,GAC7D,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAiCjC"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Registry resolution for `loom <agent>`.
3
+ *
4
+ * When an agent id doesn't match an in-workspace `.github/agents/<id>.agent.md`
5
+ * file, we fall back to configured Loom registries. The registry index lists
6
+ * templates and their nested agents; we search it for an exact agent-id hit
7
+ * and return enough information for the launch code to stage the template
8
+ * into a session workspace via `applyTemplate`.
9
+ *
10
+ * This is deliberately *resolution only* — no staging, no filesystem writes
11
+ * beyond what `resolveRegistryIndexPaths` already does when it auto-clones a
12
+ * registry.
13
+ */
14
+ import { readFile } from 'node:fs/promises';
15
+ import { dirname } from 'node:path';
16
+ import yaml from 'js-yaml';
17
+ import { resolveRegistryIndexPaths } from '../search-registry.js';
18
+ import { resolveTemplatePath } from '../clone.js';
19
+ /**
20
+ * Walk all configured registries searching for a template whose nested
21
+ * agents include an exact match for `agentId`.
22
+ *
23
+ * Returns the first hit (deterministic: registries are iterated in config
24
+ * order). Returns an object describing the match or null if none found.
25
+ *
26
+ * Side effects: `resolveRegistryIndexPaths` may auto-clone registries that
27
+ * aren't yet cached. Callers that want a pure lookup must pre-populate the
28
+ * cache.
29
+ */
30
+ export async function resolveAgentFromRegistry(options) {
31
+ const indexPaths = await resolveRegistryIndexPaths({
32
+ registryName: options.registryName,
33
+ workspaceDir: options.workspaceDir,
34
+ configDir: options.configDir,
35
+ execFn: options.execFn,
36
+ });
37
+ for (const { name, path: indexPath } of indexPaths) {
38
+ let index;
39
+ try {
40
+ const raw = await readFile(indexPath, 'utf-8');
41
+ index = yaml.load(raw);
42
+ }
43
+ catch {
44
+ continue;
45
+ }
46
+ if (!index || !Array.isArray(index.templates))
47
+ continue;
48
+ for (const tmpl of index.templates) {
49
+ if (!tmpl.agents)
50
+ continue;
51
+ const match = tmpl.agents.find((a) => a.id === options.agentId);
52
+ if (!match)
53
+ continue;
54
+ // registryRoot is the directory containing index.yaml.
55
+ const registryRoot = dirname(indexPath);
56
+ const templatePath = resolveTemplatePath(registryRoot, tmpl.id);
57
+ return {
58
+ registryName: name,
59
+ registryRoot,
60
+ templateId: tmpl.id,
61
+ templatePath,
62
+ agentId: options.agentId,
63
+ agent: match,
64
+ template: tmpl,
65
+ };
66
+ }
67
+ }
68
+ return null;
69
+ }
70
+ /**
71
+ * Discover all agent ids offered across configured registries — used for
72
+ * fuzzy suggestions when an exact match fails. Registry errors are swallowed.
73
+ */
74
+ export async function listRegistryAgentIds(options = {}) {
75
+ const listings = await listRegistryAgents(options);
76
+ return Array.from(new Set(listings.map((l) => l.agentId))).sort();
77
+ }
78
+ /**
79
+ * Discover all agents across configured registries with enough metadata to
80
+ * render a "what could I launch?" listing. Registry errors are swallowed.
81
+ */
82
+ export async function listRegistryAgents(options = {}) {
83
+ const indexPaths = await resolveRegistryIndexPaths({
84
+ registryName: options.registryName,
85
+ workspaceDir: options.workspaceDir,
86
+ configDir: options.configDir,
87
+ execFn: options.execFn,
88
+ }).catch(() => []);
89
+ const out = [];
90
+ for (const { name, path: indexPath } of indexPaths) {
91
+ try {
92
+ const raw = await readFile(indexPath, 'utf-8');
93
+ const index = yaml.load(raw);
94
+ if (!index || !Array.isArray(index.templates))
95
+ continue;
96
+ for (const tmpl of index.templates) {
97
+ if (!tmpl.agents)
98
+ continue;
99
+ for (const a of tmpl.agents) {
100
+ out.push({
101
+ agentId: a.id,
102
+ agentName: a.name,
103
+ description: a.description,
104
+ registryName: name,
105
+ templateId: tmpl.id,
106
+ tags: a.tags,
107
+ });
108
+ }
109
+ }
110
+ }
111
+ catch {
112
+ // skip unreadable index
113
+ }
114
+ }
115
+ out.sort((x, y) => x.agentId.localeCompare(y.agentId));
116
+ return out;
117
+ }
118
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/chat/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Agent Resolution for `loom <agent>`
3
+ *
4
+ * MVP: in-workspace resolution only.
5
+ * Walks up from `cwd` to find the nearest workspace (directory containing
6
+ * `.github/agents/`) and looks for `<id>.agent.md` there.
7
+ *
8
+ * Future layers (not yet implemented):
9
+ * - Installed shortname lookup via ~/.loom/workspaces.yaml
10
+ * - Registry-reference resolution (requires agent-definitions adapter)
11
+ */
12
+ export interface ResolvedAgentRef {
13
+ /** Agent id (slug from filename) */
14
+ agentId: string;
15
+ /** Absolute path to the `.agent.md` file */
16
+ agentFilePath: string;
17
+ /** Absolute path to the workspace root (dir containing `.github/`) */
18
+ workspaceDir: string;
19
+ /** Resolution layer that produced the match */
20
+ source: 'workspace';
21
+ }
22
+ export interface ResolveAgentOptions {
23
+ /** Agent id to resolve (filename stem before `.agent.md`) */
24
+ agentId: string;
25
+ /** Directory to search from (typically process.cwd()) */
26
+ cwd: string;
27
+ /**
28
+ * If true, only look in `cwd` itself (no walk-up). Used for tests and
29
+ * for callers that need strict resolution semantics.
30
+ */
31
+ strictCwd?: boolean;
32
+ }
33
+ /**
34
+ * Resolve an agent id against the current workspace.
35
+ * Returns `null` if not found (caller decides whether to fall through
36
+ * to installed-shortname or registry layers in future iterations).
37
+ */
38
+ export declare function resolveAgentInWorkspace(options: ResolveAgentOptions): ResolvedAgentRef | null;
39
+ //# sourceMappingURL=resolve-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-agent.d.ts","sourceRoot":"","sources":["../../src/chat/resolve-agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,mBAAmB,GAC3B,gBAAgB,GAAG,IAAI,CAkBzB"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Agent Resolution for `loom <agent>`
3
+ *
4
+ * MVP: in-workspace resolution only.
5
+ * Walks up from `cwd` to find the nearest workspace (directory containing
6
+ * `.github/agents/`) and looks for `<id>.agent.md` there.
7
+ *
8
+ * Future layers (not yet implemented):
9
+ * - Installed shortname lookup via ~/.loom/workspaces.yaml
10
+ * - Registry-reference resolution (requires agent-definitions adapter)
11
+ */
12
+ import { existsSync } from 'node:fs';
13
+ import { join, resolve } from 'node:path';
14
+ import { findWorkspaceRoot } from './discovery.js';
15
+ /**
16
+ * Resolve an agent id against the current workspace.
17
+ * Returns `null` if not found (caller decides whether to fall through
18
+ * to installed-shortname or registry layers in future iterations).
19
+ */
20
+ export function resolveAgentInWorkspace(options) {
21
+ const { agentId, cwd, strictCwd = false } = options;
22
+ if (!agentId || !/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(agentId)) {
23
+ throw new Error(`Invalid agent id: "${agentId}". Must match [a-zA-Z0-9][a-zA-Z0-9._-]*`);
24
+ }
25
+ const workspaceDir = strictCwd ? resolve(cwd) : (findWorkspaceRoot(cwd) ?? resolve(cwd));
26
+ const agentFilePath = join(workspaceDir, '.github', 'agents', `${agentId}.agent.md`);
27
+ if (!existsSync(agentFilePath))
28
+ return null;
29
+ return {
30
+ agentId,
31
+ agentFilePath,
32
+ workspaceDir,
33
+ source: 'workspace',
34
+ };
35
+ }
36
+ //# sourceMappingURL=resolve-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-agent.js","sourceRoot":"","sources":["../../src/chat/resolve-agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAyBnD;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CACrC,OAA4B;IAE5B,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IACpD,IAAI,CAAC,OAAO,IAAI,CAAC,8BAA8B,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,KAAK,CACb,sBAAsB,OAAO,0CAA0C,CACxE,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IACzF,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,OAAO,WAAW,CAAC,CAAC;IACrF,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;QAAE,OAAO,IAAI,CAAC;IAE5C,OAAO;QACL,OAAO;QACP,aAAa;QACb,YAAY;QACZ,MAAM,EAAE,WAAW;KACpB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Fuzzy suggestion helper for "agent not found" errors.
3
+ * Uses classic Levenshtein distance — small, fast, dependency-free,
4
+ * and adequate for the small candidate sets we deal with (dozens of
5
+ * agents per workspace at most).
6
+ */
7
+ /**
8
+ * Compute the Levenshtein edit distance between two strings.
9
+ * Case-insensitive comparison.
10
+ */
11
+ export declare function levenshtein(a: string, b: string): number;
12
+ /**
13
+ * Return up to `limit` candidates from `options` that are closest to `query`,
14
+ * ordered by distance ascending. Candidates with distance greater than
15
+ * `maxDistance` are excluded. When `maxDistance` is unset, defaults to
16
+ * max(2, ceil(query.length / 2)) — tolerates roughly half the characters
17
+ * wrong but at least two edits.
18
+ */
19
+ export declare function suggestClosest(query: string, options: readonly string[], limit?: number, maxDistance?: number): string[];
20
+ //# sourceMappingURL=suggest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suggest.d.ts","sourceRoot":"","sources":["../../src/chat/suggest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CA0BxD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,KAAK,SAAI,EACT,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,EAAE,CASV"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Fuzzy suggestion helper for "agent not found" errors.
3
+ * Uses classic Levenshtein distance — small, fast, dependency-free,
4
+ * and adequate for the small candidate sets we deal with (dozens of
5
+ * agents per workspace at most).
6
+ */
7
+ /**
8
+ * Compute the Levenshtein edit distance between two strings.
9
+ * Case-insensitive comparison.
10
+ */
11
+ export function levenshtein(a, b) {
12
+ const s1 = a.toLowerCase();
13
+ const s2 = b.toLowerCase();
14
+ const m = s1.length;
15
+ const n = s2.length;
16
+ if (m === 0)
17
+ return n;
18
+ if (n === 0)
19
+ return m;
20
+ // Two-row DP to keep memory to O(min(m,n)).
21
+ let prev = new Array(n + 1);
22
+ let curr = new Array(n + 1);
23
+ for (let j = 0; j <= n; j++)
24
+ prev[j] = j;
25
+ for (let i = 1; i <= m; i++) {
26
+ curr[0] = i;
27
+ for (let j = 1; j <= n; j++) {
28
+ const cost = s1.charCodeAt(i - 1) === s2.charCodeAt(j - 1) ? 0 : 1;
29
+ curr[j] = Math.min(curr[j - 1] + 1, // insert
30
+ prev[j] + 1, // delete
31
+ prev[j - 1] + cost);
32
+ }
33
+ [prev, curr] = [curr, prev];
34
+ }
35
+ return prev[n];
36
+ }
37
+ /**
38
+ * Return up to `limit` candidates from `options` that are closest to `query`,
39
+ * ordered by distance ascending. Candidates with distance greater than
40
+ * `maxDistance` are excluded. When `maxDistance` is unset, defaults to
41
+ * max(2, ceil(query.length / 2)) — tolerates roughly half the characters
42
+ * wrong but at least two edits.
43
+ */
44
+ export function suggestClosest(query, options, limit = 3, maxDistance) {
45
+ const threshold = maxDistance ?? Math.max(2, Math.ceil(query.length / 2));
46
+ const scored = [];
47
+ for (const option of options) {
48
+ const d = levenshtein(query, option);
49
+ if (d <= threshold)
50
+ scored.push({ option, d });
51
+ }
52
+ scored.sort((a, b) => a.d - b.d || a.option.localeCompare(b.option));
53
+ return scored.slice(0, limit).map((s) => s.option);
54
+ }
55
+ //# sourceMappingURL=suggest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suggest.js","sourceRoot":"","sources":["../../src/chat/suggest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,CAAS,EAAE,CAAS;IAC9C,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3B,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3B,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IACpB,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IACpB,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACtB,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAEtB,4CAA4C;IAC5C,IAAI,IAAI,GAAG,IAAI,KAAK,CAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,IAAI,IAAI,GAAG,IAAI,KAAK,CAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;QAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAEzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACnE,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAChB,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAS,SAAS;YACjC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,EAAa,SAAS;YACjC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CACnB,CAAC;QACJ,CAAC;QACD,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAa,EACb,OAA0B,EAC1B,KAAK,GAAG,CAAC,EACT,WAAoB;IAEpB,MAAM,SAAS,GAAG,WAAW,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IAC1E,MAAM,MAAM,GAAyC,EAAE,CAAC;IACxD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACrE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AACrD,CAAC"}