@bubblebrain-ai/bubble 0.0.1

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 (248) hide show
  1. package/README.md +70 -0
  2. package/dist/agent/evidence-tracker.d.ts +15 -0
  3. package/dist/agent/evidence-tracker.js +93 -0
  4. package/dist/agent/execution-governor.d.ts +30 -0
  5. package/dist/agent/execution-governor.js +169 -0
  6. package/dist/agent/subtask-policy.d.ts +14 -0
  7. package/dist/agent/subtask-policy.js +60 -0
  8. package/dist/agent/task-classifier.d.ts +3 -0
  9. package/dist/agent/task-classifier.js +36 -0
  10. package/dist/agent/tool-arbiter.d.ts +7 -0
  11. package/dist/agent/tool-arbiter.js +33 -0
  12. package/dist/agent/tool-intent.d.ts +20 -0
  13. package/dist/agent/tool-intent.js +176 -0
  14. package/dist/agent.d.ts +95 -0
  15. package/dist/agent.js +672 -0
  16. package/dist/approval/controller.d.ts +48 -0
  17. package/dist/approval/controller.js +78 -0
  18. package/dist/approval/danger.d.ts +13 -0
  19. package/dist/approval/danger.js +55 -0
  20. package/dist/approval/diff-hunks.d.ts +12 -0
  21. package/dist/approval/diff-hunks.js +32 -0
  22. package/dist/approval/session-cache.d.ts +35 -0
  23. package/dist/approval/session-cache.js +68 -0
  24. package/dist/approval/tool-helper.d.ts +14 -0
  25. package/dist/approval/tool-helper.js +32 -0
  26. package/dist/approval/types.d.ts +56 -0
  27. package/dist/approval/types.js +8 -0
  28. package/dist/bubble-home.d.ts +8 -0
  29. package/dist/bubble-home.js +19 -0
  30. package/dist/cli.d.ts +19 -0
  31. package/dist/cli.js +82 -0
  32. package/dist/config.d.ts +41 -0
  33. package/dist/config.js +144 -0
  34. package/dist/context/budget.d.ts +21 -0
  35. package/dist/context/budget.js +72 -0
  36. package/dist/context/compact-llm.d.ts +16 -0
  37. package/dist/context/compact-llm.js +132 -0
  38. package/dist/context/compact.d.ts +15 -0
  39. package/dist/context/compact.js +251 -0
  40. package/dist/context/overflow.d.ts +9 -0
  41. package/dist/context/overflow.js +46 -0
  42. package/dist/context/projector.d.ts +26 -0
  43. package/dist/context/projector.js +150 -0
  44. package/dist/context/prune.d.ts +9 -0
  45. package/dist/context/prune.js +111 -0
  46. package/dist/lsp/config.d.ts +18 -0
  47. package/dist/lsp/config.js +58 -0
  48. package/dist/lsp/diagnostics.d.ts +24 -0
  49. package/dist/lsp/diagnostics.js +103 -0
  50. package/dist/lsp/index.d.ts +3 -0
  51. package/dist/lsp/index.js +3 -0
  52. package/dist/lsp/service.d.ts +85 -0
  53. package/dist/lsp/service.js +695 -0
  54. package/dist/main.d.ts +5 -0
  55. package/dist/main.js +352 -0
  56. package/dist/mcp/client.d.ts +68 -0
  57. package/dist/mcp/client.js +163 -0
  58. package/dist/mcp/config.d.ts +26 -0
  59. package/dist/mcp/config.js +127 -0
  60. package/dist/mcp/manager.d.ts +55 -0
  61. package/dist/mcp/manager.js +296 -0
  62. package/dist/mcp/name.d.ts +26 -0
  63. package/dist/mcp/name.js +40 -0
  64. package/dist/mcp/transports.d.ts +53 -0
  65. package/dist/mcp/transports.js +248 -0
  66. package/dist/mcp/types.d.ts +111 -0
  67. package/dist/mcp/types.js +14 -0
  68. package/dist/memory/db.d.ts +62 -0
  69. package/dist/memory/db.js +313 -0
  70. package/dist/memory/index.d.ts +9 -0
  71. package/dist/memory/index.js +9 -0
  72. package/dist/memory/paths.d.ts +18 -0
  73. package/dist/memory/paths.js +38 -0
  74. package/dist/memory/phase1.d.ts +23 -0
  75. package/dist/memory/phase1.js +172 -0
  76. package/dist/memory/phase2.d.ts +19 -0
  77. package/dist/memory/phase2.js +100 -0
  78. package/dist/memory/prompts.d.ts +19 -0
  79. package/dist/memory/prompts.js +99 -0
  80. package/dist/memory/reset.d.ts +1 -0
  81. package/dist/memory/reset.js +13 -0
  82. package/dist/memory/start.d.ts +24 -0
  83. package/dist/memory/start.js +50 -0
  84. package/dist/memory/storage.d.ts +10 -0
  85. package/dist/memory/storage.js +82 -0
  86. package/dist/memory/store.d.ts +43 -0
  87. package/dist/memory/store.js +193 -0
  88. package/dist/memory/usage.d.ts +1 -0
  89. package/dist/memory/usage.js +38 -0
  90. package/dist/model-catalog.d.ts +20 -0
  91. package/dist/model-catalog.js +99 -0
  92. package/dist/model-config.d.ts +32 -0
  93. package/dist/model-config.js +59 -0
  94. package/dist/model-pricing.d.ts +23 -0
  95. package/dist/model-pricing.js +46 -0
  96. package/dist/oauth/index.d.ts +3 -0
  97. package/dist/oauth/index.js +2 -0
  98. package/dist/oauth/openai-codex.d.ts +9 -0
  99. package/dist/oauth/openai-codex.js +173 -0
  100. package/dist/oauth/storage.d.ts +18 -0
  101. package/dist/oauth/storage.js +60 -0
  102. package/dist/oauth/types.d.ts +15 -0
  103. package/dist/oauth/types.js +1 -0
  104. package/dist/orchestrator/default-hooks.d.ts +2 -0
  105. package/dist/orchestrator/default-hooks.js +96 -0
  106. package/dist/orchestrator/hooks.d.ts +78 -0
  107. package/dist/orchestrator/hooks.js +52 -0
  108. package/dist/orchestrator/workflow.d.ts +10 -0
  109. package/dist/orchestrator/workflow.js +22 -0
  110. package/dist/permission/mode.d.ts +23 -0
  111. package/dist/permission/mode.js +20 -0
  112. package/dist/permissions/rule.d.ts +39 -0
  113. package/dist/permissions/rule.js +234 -0
  114. package/dist/permissions/settings.d.ts +71 -0
  115. package/dist/permissions/settings.js +202 -0
  116. package/dist/permissions/types.d.ts +61 -0
  117. package/dist/permissions/types.js +14 -0
  118. package/dist/prompt/compose.d.ts +12 -0
  119. package/dist/prompt/compose.js +67 -0
  120. package/dist/prompt/environment.d.ts +12 -0
  121. package/dist/prompt/environment.js +38 -0
  122. package/dist/prompt/provider-prompts/anthropic.d.ts +1 -0
  123. package/dist/prompt/provider-prompts/anthropic.js +5 -0
  124. package/dist/prompt/provider-prompts/codex.d.ts +1 -0
  125. package/dist/prompt/provider-prompts/codex.js +5 -0
  126. package/dist/prompt/provider-prompts/default.d.ts +1 -0
  127. package/dist/prompt/provider-prompts/default.js +6 -0
  128. package/dist/prompt/provider-prompts/gemini.d.ts +1 -0
  129. package/dist/prompt/provider-prompts/gemini.js +5 -0
  130. package/dist/prompt/provider-prompts/gpt.d.ts +1 -0
  131. package/dist/prompt/provider-prompts/gpt.js +5 -0
  132. package/dist/prompt/reminders.d.ts +30 -0
  133. package/dist/prompt/reminders.js +164 -0
  134. package/dist/prompt/runtime.d.ts +12 -0
  135. package/dist/prompt/runtime.js +31 -0
  136. package/dist/prompt/skills.d.ts +2 -0
  137. package/dist/prompt/skills.js +4 -0
  138. package/dist/provider-openai-codex.d.ts +14 -0
  139. package/dist/provider-openai-codex.js +409 -0
  140. package/dist/provider-registry.d.ts +56 -0
  141. package/dist/provider-registry.js +244 -0
  142. package/dist/provider-transform.d.ts +10 -0
  143. package/dist/provider-transform.js +69 -0
  144. package/dist/provider.d.ts +31 -0
  145. package/dist/provider.js +269 -0
  146. package/dist/question/controller.d.ts +22 -0
  147. package/dist/question/controller.js +97 -0
  148. package/dist/question/index.d.ts +2 -0
  149. package/dist/question/index.js +2 -0
  150. package/dist/question/types.d.ts +42 -0
  151. package/dist/question/types.js +6 -0
  152. package/dist/session-log.d.ts +16 -0
  153. package/dist/session-log.js +267 -0
  154. package/dist/session-types.d.ts +55 -0
  155. package/dist/session-types.js +1 -0
  156. package/dist/session.d.ts +32 -0
  157. package/dist/session.js +135 -0
  158. package/dist/skills/discovery.d.ts +12 -0
  159. package/dist/skills/discovery.js +148 -0
  160. package/dist/skills/format.d.ts +2 -0
  161. package/dist/skills/format.js +47 -0
  162. package/dist/skills/frontmatter.d.ts +5 -0
  163. package/dist/skills/frontmatter.js +60 -0
  164. package/dist/skills/invocation.d.ts +8 -0
  165. package/dist/skills/invocation.js +51 -0
  166. package/dist/skills/registry.d.ts +17 -0
  167. package/dist/skills/registry.js +42 -0
  168. package/dist/skills/types.d.ts +32 -0
  169. package/dist/skills/types.js +1 -0
  170. package/dist/slash-commands/commands.d.ts +7 -0
  171. package/dist/slash-commands/commands.js +779 -0
  172. package/dist/slash-commands/index.d.ts +4 -0
  173. package/dist/slash-commands/index.js +8 -0
  174. package/dist/slash-commands/registry.d.ts +31 -0
  175. package/dist/slash-commands/registry.js +70 -0
  176. package/dist/slash-commands/types.d.ts +44 -0
  177. package/dist/slash-commands/types.js +1 -0
  178. package/dist/slash-commands/unified.d.ts +38 -0
  179. package/dist/slash-commands/unified.js +38 -0
  180. package/dist/system-prompt.d.ts +34 -0
  181. package/dist/system-prompt.js +7 -0
  182. package/dist/tools/bash.d.ts +6 -0
  183. package/dist/tools/bash.js +135 -0
  184. package/dist/tools/edit.d.ts +16 -0
  185. package/dist/tools/edit.js +95 -0
  186. package/dist/tools/exa-mcp.d.ts +3 -0
  187. package/dist/tools/exa-mcp.js +74 -0
  188. package/dist/tools/exit-plan-mode.d.ts +17 -0
  189. package/dist/tools/exit-plan-mode.js +68 -0
  190. package/dist/tools/glob.d.ts +5 -0
  191. package/dist/tools/glob.js +129 -0
  192. package/dist/tools/grep.d.ts +5 -0
  193. package/dist/tools/grep.js +111 -0
  194. package/dist/tools/index.d.ts +36 -0
  195. package/dist/tools/index.js +59 -0
  196. package/dist/tools/lsp.d.ts +4 -0
  197. package/dist/tools/lsp.js +92 -0
  198. package/dist/tools/memory.d.ts +3 -0
  199. package/dist/tools/memory.js +90 -0
  200. package/dist/tools/question.d.ts +3 -0
  201. package/dist/tools/question.js +174 -0
  202. package/dist/tools/read.d.ts +7 -0
  203. package/dist/tools/read.js +83 -0
  204. package/dist/tools/sensitive-paths.d.ts +3 -0
  205. package/dist/tools/sensitive-paths.js +24 -0
  206. package/dist/tools/skill.d.ts +5 -0
  207. package/dist/tools/skill.js +51 -0
  208. package/dist/tools/task.d.ts +2 -0
  209. package/dist/tools/task.js +57 -0
  210. package/dist/tools/todo.d.ts +12 -0
  211. package/dist/tools/todo.js +151 -0
  212. package/dist/tools/tool-search.d.ts +23 -0
  213. package/dist/tools/tool-search.js +124 -0
  214. package/dist/tools/web-fetch.d.ts +6 -0
  215. package/dist/tools/web-fetch.js +75 -0
  216. package/dist/tools/web-search.d.ts +5 -0
  217. package/dist/tools/web-search.js +49 -0
  218. package/dist/tools/write.d.ts +11 -0
  219. package/dist/tools/write.js +77 -0
  220. package/dist/tui/display-history.d.ts +35 -0
  221. package/dist/tui/display-history.js +243 -0
  222. package/dist/tui/file-mentions.d.ts +29 -0
  223. package/dist/tui/file-mentions.js +174 -0
  224. package/dist/tui/image-paste.d.ts +54 -0
  225. package/dist/tui/image-paste.js +288 -0
  226. package/dist/tui/markdown-theme-rules.d.ts +23 -0
  227. package/dist/tui/markdown-theme-rules.js +164 -0
  228. package/dist/tui/markdown-theme.d.ts +5 -0
  229. package/dist/tui/markdown-theme.js +27 -0
  230. package/dist/tui/opencode-spinner.d.ts +21 -0
  231. package/dist/tui/opencode-spinner.js +216 -0
  232. package/dist/tui/prompt-keybindings.d.ts +41 -0
  233. package/dist/tui/prompt-keybindings.js +28 -0
  234. package/dist/tui/recent-activity.d.ts +8 -0
  235. package/dist/tui/recent-activity.js +71 -0
  236. package/dist/tui/run.d.ts +39 -0
  237. package/dist/tui/run.js +5696 -0
  238. package/dist/tui/sidebar-mcp.d.ts +31 -0
  239. package/dist/tui/sidebar-mcp.js +62 -0
  240. package/dist/tui/sidebar-state.d.ts +12 -0
  241. package/dist/tui/sidebar-state.js +69 -0
  242. package/dist/types.d.ts +219 -0
  243. package/dist/types.js +4 -0
  244. package/dist/variant/thinking-level.d.ts +5 -0
  245. package/dist/variant/thinking-level.js +25 -0
  246. package/dist/variant/variant-resolver.d.ts +4 -0
  247. package/dist/variant/variant-resolver.js +12 -0
  248. package/package.json +47 -0
@@ -0,0 +1,148 @@
1
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { basename, join } from "node:path";
3
+ import { parseFrontmatter } from "./frontmatter.js";
4
+ export function discoverSkills(options) {
5
+ const diagnostics = [];
6
+ const skills = [];
7
+ const seenNames = new Map();
8
+ for (const root of options.roots) {
9
+ if (!existsSync(root.path))
10
+ continue;
11
+ let entries = [];
12
+ try {
13
+ entries = readdirSync(root.path);
14
+ }
15
+ catch (error) {
16
+ diagnostics.push({
17
+ level: "warning",
18
+ filePath: root.path,
19
+ message: `Failed to read skill root: ${error.message || String(error)}`,
20
+ });
21
+ continue;
22
+ }
23
+ for (const entry of entries) {
24
+ const entryPath = join(root.path, entry);
25
+ let stat;
26
+ try {
27
+ stat = statSync(entryPath);
28
+ }
29
+ catch {
30
+ continue;
31
+ }
32
+ if (!stat.isDirectory())
33
+ continue;
34
+ const skillFile = join(entryPath, "SKILL.md");
35
+ if (!existsSync(skillFile))
36
+ continue;
37
+ const parsed = loadSkillRecord(skillFile, root.source);
38
+ diagnostics.push(...parsed.diagnostics);
39
+ if (!parsed.skill)
40
+ continue;
41
+ const existing = seenNames.get(parsed.skill.meta.name);
42
+ if (existing) {
43
+ diagnostics.push({
44
+ level: "error",
45
+ skillName: parsed.skill.meta.name,
46
+ filePath: skillFile,
47
+ message: `Duplicate skill name "${parsed.skill.meta.name}" already defined at ${existing}`,
48
+ });
49
+ continue;
50
+ }
51
+ seenNames.set(parsed.skill.meta.name, skillFile);
52
+ skills.push(parsed.skill);
53
+ }
54
+ }
55
+ skills.sort((a, b) => a.meta.name.localeCompare(b.meta.name));
56
+ return { skills, diagnostics };
57
+ }
58
+ function loadSkillRecord(skillFile, source) {
59
+ const diagnostics = [];
60
+ let raw = "";
61
+ try {
62
+ raw = readFileSync(skillFile, "utf8");
63
+ }
64
+ catch (error) {
65
+ diagnostics.push({
66
+ level: "error",
67
+ filePath: skillFile,
68
+ message: `Failed to read skill file: ${error.message || String(error)}`,
69
+ });
70
+ return { diagnostics };
71
+ }
72
+ const { attributes, body } = parseFrontmatter(raw);
73
+ const rootDir = skillFile.slice(0, -"/SKILL.md".length);
74
+ const fallbackName = basename(rootDir);
75
+ const meta = normalizeMeta(attributes, fallbackName, skillFile, diagnostics);
76
+ if (!meta) {
77
+ return { diagnostics };
78
+ }
79
+ return {
80
+ skill: {
81
+ meta,
82
+ rootDir,
83
+ skillFile,
84
+ content: body.trim(),
85
+ resources: indexResources(rootDir),
86
+ source,
87
+ },
88
+ diagnostics,
89
+ };
90
+ }
91
+ function normalizeMeta(attributes, fallbackName, filePath, diagnostics) {
92
+ const name = typeof attributes.name === "string" && attributes.name.trim() ? attributes.name.trim() : fallbackName;
93
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name)) {
94
+ diagnostics.push({
95
+ level: "error",
96
+ skillName: name,
97
+ filePath,
98
+ message: `Invalid skill name "${name}"`,
99
+ });
100
+ return undefined;
101
+ }
102
+ const description = typeof attributes.description === "string" ? attributes.description.trim() : "";
103
+ if (!description) {
104
+ diagnostics.push({
105
+ level: "error",
106
+ skillName: name,
107
+ filePath,
108
+ message: "Skill description is required",
109
+ });
110
+ return undefined;
111
+ }
112
+ const tags = Array.isArray(attributes.tags)
113
+ ? attributes.tags.filter((item) => typeof item === "string" && !!item.trim()).map((item) => item.trim())
114
+ : undefined;
115
+ return {
116
+ name,
117
+ description,
118
+ disableModelInvocation: attributes["disable-model-invocation"] === true,
119
+ version: typeof attributes.version === "number" ? attributes.version : undefined,
120
+ tags: tags && tags.length > 0 ? tags : undefined,
121
+ };
122
+ }
123
+ function indexResources(rootDir) {
124
+ return {
125
+ references: listRelativeFiles(join(rootDir, "references"), rootDir),
126
+ scripts: listRelativeFiles(join(rootDir, "scripts"), rootDir),
127
+ assets: listRelativeFiles(join(rootDir, "assets"), rootDir),
128
+ };
129
+ }
130
+ function listRelativeFiles(targetDir, rootDir) {
131
+ if (!existsSync(targetDir))
132
+ return [];
133
+ const results = [];
134
+ walkDir(targetDir, rootDir, results);
135
+ results.sort((a, b) => a.localeCompare(b));
136
+ return results;
137
+ }
138
+ function walkDir(targetDir, rootDir, results) {
139
+ for (const entry of readdirSync(targetDir)) {
140
+ const fullPath = join(targetDir, entry);
141
+ const stat = statSync(fullPath);
142
+ if (stat.isDirectory()) {
143
+ walkDir(fullPath, rootDir, results);
144
+ continue;
145
+ }
146
+ results.push(fullPath.slice(rootDir.length + 1).replace(/\\/g, "/"));
147
+ }
148
+ }
@@ -0,0 +1,2 @@
1
+ import type { SkillSummary } from "./types.js";
2
+ export declare function formatSkillsPrompt(skills: SkillSummary[]): string;
@@ -0,0 +1,47 @@
1
+ const MAX_SKILL_DESC_CHARS = 200;
2
+ const SKILLS_BUDGET_CHARS = 6000;
3
+ const SOURCE_PRIORITY = {
4
+ project: 0,
5
+ configured: 1,
6
+ user: 2,
7
+ };
8
+ export function formatSkillsPrompt(skills) {
9
+ if (skills.length === 0)
10
+ return "";
11
+ const sorted = [...skills].sort((a, b) => {
12
+ const ap = SOURCE_PRIORITY[a.source ?? "user"] ?? 3;
13
+ const bp = SOURCE_PRIORITY[b.source ?? "user"] ?? 3;
14
+ if (ap !== bp)
15
+ return ap - bp;
16
+ return a.name.localeCompare(b.name);
17
+ });
18
+ const header = [
19
+ "Skills provide specialized instructions and workflows for specific tasks.",
20
+ "Use the skill system when a task clearly matches a skill description.",
21
+ "Available skills:",
22
+ ];
23
+ const lines = [];
24
+ let used = 0;
25
+ let dropped = 0;
26
+ for (const skill of sorted) {
27
+ const line = formatSkillLine(skill);
28
+ if (used + line.length + 1 > SKILLS_BUDGET_CHARS) {
29
+ dropped = sorted.length - lines.length;
30
+ break;
31
+ }
32
+ lines.push(line);
33
+ used += line.length + 1;
34
+ }
35
+ if (dropped > 0) {
36
+ lines.push(`- ... and ${dropped} more skills (use /skills to list all)`);
37
+ }
38
+ return [...header, ...lines].join("\n");
39
+ }
40
+ function formatSkillLine(skill) {
41
+ const tagSuffix = skill.tags && skill.tags.length > 0 ? ` [tags: ${skill.tags.join(", ")}]` : "";
42
+ const rawDesc = skill.description ?? "";
43
+ const desc = rawDesc.length > MAX_SKILL_DESC_CHARS
44
+ ? rawDesc.slice(0, MAX_SKILL_DESC_CHARS - 1) + "…"
45
+ : rawDesc;
46
+ return `- ${skill.name}: ${desc}${tagSuffix}`;
47
+ }
@@ -0,0 +1,5 @@
1
+ export interface ParsedFrontmatter {
2
+ attributes: Record<string, unknown>;
3
+ body: string;
4
+ }
5
+ export declare function parseFrontmatter(raw: string): ParsedFrontmatter;
@@ -0,0 +1,60 @@
1
+ export function parseFrontmatter(raw) {
2
+ if (!raw.startsWith("---\n") && !raw.startsWith("---\r\n")) {
3
+ return { attributes: {}, body: raw };
4
+ }
5
+ const normalized = raw.replace(/\r\n/g, "\n");
6
+ const end = normalized.indexOf("\n---\n", 4);
7
+ if (end === -1) {
8
+ return { attributes: {}, body: raw };
9
+ }
10
+ const frontmatter = normalized.slice(4, end);
11
+ const body = normalized.slice(end + 5);
12
+ return {
13
+ attributes: parseFrontmatterBlock(frontmatter),
14
+ body,
15
+ };
16
+ }
17
+ function parseFrontmatterBlock(block) {
18
+ const attributes = {};
19
+ const lines = block.split("\n");
20
+ let currentListKey = null;
21
+ for (const line of lines) {
22
+ if (!line.trim())
23
+ continue;
24
+ const listMatch = line.match(/^\s*-\s+(.*)$/);
25
+ if (listMatch && currentListKey) {
26
+ const current = attributes[currentListKey];
27
+ if (Array.isArray(current)) {
28
+ current.push(parseScalar(listMatch[1].trim()));
29
+ }
30
+ else {
31
+ attributes[currentListKey] = [parseScalar(listMatch[1].trim())];
32
+ }
33
+ continue;
34
+ }
35
+ const keyValueMatch = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
36
+ if (!keyValueMatch) {
37
+ currentListKey = null;
38
+ continue;
39
+ }
40
+ const [, key, rawValue] = keyValueMatch;
41
+ if (!rawValue.trim()) {
42
+ attributes[key] = [];
43
+ currentListKey = key;
44
+ continue;
45
+ }
46
+ attributes[key] = parseScalar(rawValue.trim());
47
+ currentListKey = null;
48
+ }
49
+ return attributes;
50
+ }
51
+ function parseScalar(value) {
52
+ const unquoted = value.replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
53
+ if (unquoted === "true")
54
+ return true;
55
+ if (unquoted === "false")
56
+ return false;
57
+ if (/^-?\d+$/.test(unquoted))
58
+ return Number.parseInt(unquoted, 10);
59
+ return unquoted;
60
+ }
@@ -0,0 +1,8 @@
1
+ import type { SkillRegistry } from "./registry.js";
2
+ import type { SkillRecord } from "./types.js";
3
+ export interface SkillInvocation {
4
+ skill: SkillRecord;
5
+ task: string;
6
+ actualPrompt: string;
7
+ }
8
+ export declare function parseSkillInvocation(input: string, registry: SkillRegistry): SkillInvocation | undefined;
@@ -0,0 +1,51 @@
1
+ export function parseSkillInvocation(input, registry) {
2
+ const trimmed = input.trim();
3
+ if (!trimmed.startsWith("/"))
4
+ return undefined;
5
+ const withoutSlash = trimmed.slice(1).trim();
6
+ if (!withoutSlash)
7
+ return undefined;
8
+ if (withoutSlash.startsWith("skill ")) {
9
+ const rest = withoutSlash.slice("skill ".length).trim();
10
+ const firstSpace = rest.indexOf(" ");
11
+ if (firstSpace === -1)
12
+ return undefined;
13
+ const skillName = rest.slice(0, firstSpace).trim();
14
+ const task = rest.slice(firstSpace + 1).trim();
15
+ if (!skillName || !task)
16
+ return undefined;
17
+ const skill = registry.get(skillName);
18
+ if (!skill)
19
+ return undefined;
20
+ return {
21
+ skill,
22
+ task,
23
+ actualPrompt: buildSkillExecutionPrompt(skill, task),
24
+ };
25
+ }
26
+ const firstSpace = withoutSlash.indexOf(" ");
27
+ if (firstSpace === -1)
28
+ return undefined;
29
+ const skillName = withoutSlash.slice(0, firstSpace).trim();
30
+ const task = withoutSlash.slice(firstSpace + 1).trim();
31
+ if (!skillName || !task)
32
+ return undefined;
33
+ const skill = registry.get(skillName);
34
+ if (!skill)
35
+ return undefined;
36
+ return {
37
+ skill,
38
+ task,
39
+ actualPrompt: buildSkillExecutionPrompt(skill, task),
40
+ };
41
+ }
42
+ function buildSkillExecutionPrompt(skill, task) {
43
+ return [
44
+ `Use the skill tool to load the "${skill.meta.name}" skill before responding.`,
45
+ `The task clearly matches that skill.`,
46
+ `Do not simply restate the skill contents; use the loaded instructions to complete the task.`,
47
+ "",
48
+ "User request:",
49
+ task,
50
+ ].join("\n");
51
+ }
@@ -0,0 +1,17 @@
1
+ import type { SkillDiagnostic, SkillRecord, SkillSummary } from "./types.js";
2
+ export interface SkillRegistryOptions {
3
+ cwd?: string;
4
+ bubbleHome?: string;
5
+ agentsHome?: string;
6
+ skillPaths?: string[];
7
+ }
8
+ export declare class SkillRegistry {
9
+ private readonly skills;
10
+ private readonly diagnostics;
11
+ constructor(options?: SkillRegistryOptions);
12
+ all(): SkillRecord[];
13
+ promptVisible(): SkillRecord[];
14
+ summaries(): SkillSummary[];
15
+ get(name: string): SkillRecord | undefined;
16
+ getDiagnostics(): SkillDiagnostic[];
17
+ }
@@ -0,0 +1,42 @@
1
+ import { join } from "node:path";
2
+ import { homedir } from "node:os";
3
+ import { getBubbleHome } from "../bubble-home.js";
4
+ import { discoverSkills } from "./discovery.js";
5
+ export class SkillRegistry {
6
+ skills;
7
+ diagnostics;
8
+ constructor(options = {}) {
9
+ const cwd = options.cwd ?? process.cwd();
10
+ const bubbleHome = options.bubbleHome ?? getBubbleHome();
11
+ const agentsHome = options.agentsHome ?? join(homedir(), ".agents");
12
+ const roots = [
13
+ { path: join(bubbleHome, "skills"), source: "user" },
14
+ { path: join(agentsHome, "skills"), source: "user" },
15
+ { path: join(cwd, ".bubble", "skills"), source: "project" },
16
+ ...(options.skillPaths ?? []).map((path) => ({ path, source: "configured" })),
17
+ ];
18
+ const result = discoverSkills({ roots });
19
+ this.skills = result.skills;
20
+ this.diagnostics = result.diagnostics;
21
+ }
22
+ all() {
23
+ return this.skills.slice();
24
+ }
25
+ promptVisible() {
26
+ return this.skills.filter((skill) => !skill.meta.disableModelInvocation);
27
+ }
28
+ summaries() {
29
+ return this.promptVisible().map((skill) => ({
30
+ name: skill.meta.name,
31
+ description: skill.meta.description,
32
+ tags: skill.meta.tags,
33
+ source: skill.source,
34
+ }));
35
+ }
36
+ get(name) {
37
+ return this.skills.find((skill) => skill.meta.name === name);
38
+ }
39
+ getDiagnostics() {
40
+ return this.diagnostics.slice();
41
+ }
42
+ }
@@ -0,0 +1,32 @@
1
+ export interface SkillMeta {
2
+ name: string;
3
+ description: string;
4
+ disableModelInvocation: boolean;
5
+ version?: number;
6
+ tags?: string[];
7
+ }
8
+ export interface SkillResourceIndex {
9
+ references: string[];
10
+ scripts: string[];
11
+ assets: string[];
12
+ }
13
+ export interface SkillRecord {
14
+ meta: SkillMeta;
15
+ rootDir: string;
16
+ skillFile: string;
17
+ content: string;
18
+ resources: SkillResourceIndex;
19
+ source: "user" | "project" | "configured";
20
+ }
21
+ export interface SkillSummary {
22
+ name: string;
23
+ description: string;
24
+ tags?: string[];
25
+ source?: "user" | "project" | "configured";
26
+ }
27
+ export interface SkillDiagnostic {
28
+ level: "warning" | "error";
29
+ skillName?: string;
30
+ filePath?: string;
31
+ message: string;
32
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { UnifiedCommand } from "./unified.js";
2
+ /**
3
+ * Public export — built-in commands tagged with `source: "builtin"` so the
4
+ * registry and TUI can group them uniformly alongside MCP-derived commands.
5
+ * Kept as a mapped projection to avoid adding the field to every object literal.
6
+ */
7
+ export declare const builtinSlashCommands: UnifiedCommand[];