@lapis-notes/ai 0.1.0

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 (266) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE.md +8 -0
  3. package/README.md +20 -0
  4. package/dist/ai-plugin.d.ts +88 -0
  5. package/dist/ai-plugin.js +742 -0
  6. package/dist/bootstrap/build.d.ts +15 -0
  7. package/dist/bootstrap/build.js +28 -0
  8. package/dist/bootstrap/folder-instructions.d.ts +4 -0
  9. package/dist/bootstrap/folder-instructions.js +49 -0
  10. package/dist/bootstrap/format.d.ts +2 -0
  11. package/dist/bootstrap/format.js +46 -0
  12. package/dist/bootstrap/instructions.d.ts +1 -0
  13. package/dist/bootstrap/instructions.js +8 -0
  14. package/dist/bootstrap/types.d.ts +36 -0
  15. package/dist/bootstrap/types.js +3 -0
  16. package/dist/catalog/ai-catalog-panel.svelte +742 -0
  17. package/dist/catalog/ai-catalog-panel.svelte.d.ts +13 -0
  18. package/dist/catalog/ai-catalog-view-type.d.ts +1 -0
  19. package/dist/catalog/ai-catalog-view-type.js +1 -0
  20. package/dist/catalog/ai-catalog-view.d.ts +22 -0
  21. package/dist/catalog/ai-catalog-view.js +48 -0
  22. package/dist/catalog/filter.d.ts +3 -0
  23. package/dist/catalog/filter.js +24 -0
  24. package/dist/catalog/inventory.d.ts +21 -0
  25. package/dist/catalog/inventory.js +255 -0
  26. package/dist/catalog/tree.d.ts +12 -0
  27. package/dist/catalog/tree.js +60 -0
  28. package/dist/catalog/types.d.ts +44 -0
  29. package/dist/catalog/types.js +1 -0
  30. package/dist/chat/ai-approval-card.svelte +54 -0
  31. package/dist/chat/ai-approval-card.svelte.d.ts +9 -0
  32. package/dist/chat/ai-chat-panel.svelte +1288 -0
  33. package/dist/chat/ai-chat-panel.svelte.d.ts +50 -0
  34. package/dist/chat/ai-inventory-result.css +55 -0
  35. package/dist/chat/ai-inventory-result.svelte +63 -0
  36. package/dist/chat/ai-inventory-result.svelte.d.ts +5 -0
  37. package/dist/chat/ai-question-card.svelte +132 -0
  38. package/dist/chat/ai-question-card.svelte.d.ts +9 -0
  39. package/dist/chat/ai-view-bootstrap.d.ts +13 -0
  40. package/dist/chat/ai-view-bootstrap.js +86 -0
  41. package/dist/chat/ai-view-panel.svelte +89 -0
  42. package/dist/chat/ai-view-panel.svelte.d.ts +13 -0
  43. package/dist/chat/ai-view-type.d.ts +1 -0
  44. package/dist/chat/ai-view-type.js +1 -0
  45. package/dist/chat/ai-view.d.ts +60 -0
  46. package/dist/chat/ai-view.js +121 -0
  47. package/dist/chat/chat-controller.svelte.d.ts +82 -0
  48. package/dist/chat/chat-controller.svelte.js +1926 -0
  49. package/dist/chat/chat-items.d.ts +88 -0
  50. package/dist/chat/chat-items.js +9 -0
  51. package/dist/chat/chat-mentions.d.ts +15 -0
  52. package/dist/chat/chat-mentions.js +77 -0
  53. package/dist/chat/chat-result-views.d.ts +5 -0
  54. package/dist/chat/chat-result-views.js +15 -0
  55. package/dist/chat/chat-session.d.ts +28 -0
  56. package/dist/chat/chat-session.js +50 -0
  57. package/dist/chat/chat-status.d.ts +1 -0
  58. package/dist/chat/chat-status.js +3 -0
  59. package/dist/chat/chat-time.d.ts +18 -0
  60. package/dist/chat/chat-time.js +90 -0
  61. package/dist/chat/chat-tool-display.d.ts +17 -0
  62. package/dist/chat/chat-tool-display.js +213 -0
  63. package/dist/chat/chat-tool-identity.d.ts +4 -0
  64. package/dist/chat/chat-tool-identity.js +42 -0
  65. package/dist/chat/chat-trace.d.ts +6 -0
  66. package/dist/chat/chat-trace.js +256 -0
  67. package/dist/chat/live-runtime-unavailable.d.ts +1 -0
  68. package/dist/chat/live-runtime-unavailable.js +1 -0
  69. package/dist/commands/agent.d.ts +8 -0
  70. package/dist/commands/agent.js +38 -0
  71. package/dist/commands/catalog.d.ts +18 -0
  72. package/dist/commands/catalog.js +115 -0
  73. package/dist/commands/discovery.d.ts +36 -0
  74. package/dist/commands/discovery.js +151 -0
  75. package/dist/commands/groups.d.ts +16 -0
  76. package/dist/commands/groups.js +121 -0
  77. package/dist/commands/inspect.d.ts +18 -0
  78. package/dist/commands/inspect.js +29 -0
  79. package/dist/commands/markdown.d.ts +21 -0
  80. package/dist/commands/markdown.js +117 -0
  81. package/dist/commands/parser.d.ts +4 -0
  82. package/dist/commands/parser.js +24 -0
  83. package/dist/commands/reserved.d.ts +4 -0
  84. package/dist/commands/reserved.js +107 -0
  85. package/dist/commands/router.d.ts +17 -0
  86. package/dist/commands/router.js +170 -0
  87. package/dist/commands/seed.d.ts +4 -0
  88. package/dist/commands/seed.js +8 -0
  89. package/dist/commands/types.d.ts +54 -0
  90. package/dist/commands/types.js +1 -0
  91. package/dist/commands/user-agents-node.d.ts +3 -0
  92. package/dist/commands/user-agents-node.js +52 -0
  93. package/dist/commands/user-agents.d.ts +22 -0
  94. package/dist/commands/user-agents.js +40 -0
  95. package/dist/conversations/approval-grants.d.ts +20 -0
  96. package/dist/conversations/approval-grants.js +75 -0
  97. package/dist/conversations/binding-state.d.ts +6 -0
  98. package/dist/conversations/binding-state.js +35 -0
  99. package/dist/conversations/context-handoff.d.ts +25 -0
  100. package/dist/conversations/context-handoff.js +207 -0
  101. package/dist/conversations/conversation-index-coordinator.d.ts +18 -0
  102. package/dist/conversations/conversation-index-coordinator.js +81 -0
  103. package/dist/conversations/conversation-index.d.ts +16 -0
  104. package/dist/conversations/conversation-index.js +125 -0
  105. package/dist/conversations/conversation-locator.d.ts +2 -0
  106. package/dist/conversations/conversation-locator.js +20 -0
  107. package/dist/conversations/conversation-palette.d.ts +18 -0
  108. package/dist/conversations/conversation-palette.js +29 -0
  109. package/dist/conversations/conversation-repository.d.ts +39 -0
  110. package/dist/conversations/conversation-repository.js +213 -0
  111. package/dist/conversations/handoff-summary.d.ts +48 -0
  112. package/dist/conversations/handoff-summary.js +269 -0
  113. package/dist/conversations/hashes.d.ts +5 -0
  114. package/dist/conversations/hashes.js +40 -0
  115. package/dist/conversations/history-creation-scope.d.ts +12 -0
  116. package/dist/conversations/history-creation-scope.js +44 -0
  117. package/dist/conversations/jsonl.d.ts +10 -0
  118. package/dist/conversations/jsonl.js +41 -0
  119. package/dist/conversations/memory-transcript-store.d.ts +15 -0
  120. package/dist/conversations/memory-transcript-store.js +119 -0
  121. package/dist/conversations/paths.d.ts +14 -0
  122. package/dist/conversations/paths.js +92 -0
  123. package/dist/conversations/redaction.d.ts +13 -0
  124. package/dist/conversations/redaction.js +94 -0
  125. package/dist/conversations/reveal-scope.d.ts +3 -0
  126. package/dist/conversations/reveal-scope.js +8 -0
  127. package/dist/conversations/scope-resolver.d.ts +11 -0
  128. package/dist/conversations/scope-resolver.js +18 -0
  129. package/dist/conversations/scope-tree.d.ts +10 -0
  130. package/dist/conversations/scope-tree.js +71 -0
  131. package/dist/conversations/transcript-projection.d.ts +9 -0
  132. package/dist/conversations/transcript-projection.js +409 -0
  133. package/dist/conversations/transcript-store.d.ts +24 -0
  134. package/dist/conversations/transcript-store.js +27 -0
  135. package/dist/conversations/types.d.ts +224 -0
  136. package/dist/conversations/types.js +12 -0
  137. package/dist/conversations/validation.d.ts +4 -0
  138. package/dist/conversations/validation.js +383 -0
  139. package/dist/conversations/vault-transcript-store.d.ts +22 -0
  140. package/dist/conversations/vault-transcript-store.js +219 -0
  141. package/dist/core/event-queue.d.ts +6 -0
  142. package/dist/core/event-queue.js +39 -0
  143. package/dist/core/types.d.ts +241 -0
  144. package/dist/core/types.js +50 -0
  145. package/dist/history/ai-history-panel.svelte +937 -0
  146. package/dist/history/ai-history-panel.svelte.d.ts +17 -0
  147. package/dist/history/ai-history-view-type.d.ts +1 -0
  148. package/dist/history/ai-history-view-type.js +1 -0
  149. package/dist/history/ai-history-view.d.ts +26 -0
  150. package/dist/history/ai-history-view.js +51 -0
  151. package/dist/history/conversation-history-tree.d.ts +15 -0
  152. package/dist/history/conversation-history-tree.js +71 -0
  153. package/dist/host/agent-workspace.d.ts +6 -0
  154. package/dist/host/agent-workspace.js +16 -0
  155. package/dist/host/create-host-runtimes.d.ts +2 -0
  156. package/dist/host/create-host-runtimes.js +14 -0
  157. package/dist/host/desktop-process-host.d.ts +6 -0
  158. package/dist/host/desktop-process-host.js +74 -0
  159. package/dist/host/host-runtime-availability.d.ts +1 -0
  160. package/dist/host/host-runtime-availability.js +3 -0
  161. package/dist/host/process-host.d.ts +31 -0
  162. package/dist/host/process-host.js +6 -0
  163. package/dist/index.d.ts +69 -0
  164. package/dist/index.js +49 -0
  165. package/dist/jsonl/ai-jsonl-preview.d.ts +27 -0
  166. package/dist/jsonl/ai-jsonl-preview.js +68 -0
  167. package/dist/jsonl/ai-jsonl-view-type.d.ts +1 -0
  168. package/dist/jsonl/ai-jsonl-view-type.js +1 -0
  169. package/dist/jsonl/ai-jsonl-view.d.ts +16 -0
  170. package/dist/jsonl/ai-jsonl-view.js +57 -0
  171. package/dist/jsonl/ai-jsonl-view.svelte +260 -0
  172. package/dist/jsonl/ai-jsonl-view.svelte.d.ts +9 -0
  173. package/dist/memory/candidate-extractor.d.ts +25 -0
  174. package/dist/memory/candidate-extractor.js +191 -0
  175. package/dist/memory/forget-confirmation.d.ts +3 -0
  176. package/dist/memory/forget-confirmation.js +45 -0
  177. package/dist/memory/hashes.d.ts +5 -0
  178. package/dist/memory/hashes.js +37 -0
  179. package/dist/memory/memory-ingestion-coordinator.d.ts +19 -0
  180. package/dist/memory/memory-ingestion-coordinator.js +108 -0
  181. package/dist/memory/memory-record-store.d.ts +62 -0
  182. package/dist/memory/memory-record-store.js +369 -0
  183. package/dist/memory/memory-scheduler.d.ts +21 -0
  184. package/dist/memory/memory-scheduler.js +143 -0
  185. package/dist/memory/memory-tools.d.ts +61 -0
  186. package/dist/memory/memory-tools.js +99 -0
  187. package/dist/memory/native-memory-service.d.ts +72 -0
  188. package/dist/memory/native-memory-service.js +1256 -0
  189. package/dist/memory/paths.d.ts +13 -0
  190. package/dist/memory/paths.js +60 -0
  191. package/dist/memory/runtime-consolidation-provider.d.ts +18 -0
  192. package/dist/memory/runtime-consolidation-provider.js +154 -0
  193. package/dist/memory/types.d.ts +139 -0
  194. package/dist/memory/types.js +1 -0
  195. package/dist/providers/acp-model-provider.d.ts +11 -0
  196. package/dist/providers/acp-model-provider.js +99 -0
  197. package/dist/providers/codex-model-catalog.d.ts +7 -0
  198. package/dist/providers/codex-model-catalog.js +179 -0
  199. package/dist/providers/codex-model-provider.d.ts +12 -0
  200. package/dist/providers/codex-model-provider.js +42 -0
  201. package/dist/providers/model-provider.d.ts +26 -0
  202. package/dist/providers/model-provider.js +26 -0
  203. package/dist/public-components.d.ts +3 -0
  204. package/dist/public-components.js +3 -0
  205. package/dist/registry/runtime-registry.d.ts +11 -0
  206. package/dist/registry/runtime-registry.js +71 -0
  207. package/dist/registry/select-runtime.d.ts +9 -0
  208. package/dist/registry/select-runtime.js +31 -0
  209. package/dist/runtime-adapters.d.ts +16 -0
  210. package/dist/runtime-adapters.js +9 -0
  211. package/dist/runtimes/acp/acp-event-mapper.d.ts +56 -0
  212. package/dist/runtimes/acp/acp-event-mapper.js +237 -0
  213. package/dist/runtimes/acp/acp-runtime.d.ts +46 -0
  214. package/dist/runtimes/acp/acp-runtime.js +165 -0
  215. package/dist/runtimes/acp/desktop-acp-backend.d.ts +16 -0
  216. package/dist/runtimes/acp/desktop-acp-backend.js +243 -0
  217. package/dist/runtimes/codex/app-server-protocol.d.ts +28 -0
  218. package/dist/runtimes/codex/app-server-protocol.js +274 -0
  219. package/dist/runtimes/codex/codex-runtime.d.ts +24 -0
  220. package/dist/runtimes/codex/codex-runtime.js +318 -0
  221. package/dist/runtimes/fake/fake-runtime.d.ts +51 -0
  222. package/dist/runtimes/fake/fake-runtime.js +257 -0
  223. package/dist/sessions/plugin-data.d.ts +8 -0
  224. package/dist/sessions/plugin-data.js +29 -0
  225. package/dist/sessions/session-store.d.ts +47 -0
  226. package/dist/sessions/session-store.js +117 -0
  227. package/dist/settings/acp-agents.d.ts +8 -0
  228. package/dist/settings/acp-agents.js +11 -0
  229. package/dist/settings/ai-settings-tab.d.ts +8 -0
  230. package/dist/settings/ai-settings-tab.js +249 -0
  231. package/dist/settings/ai-settings.d.ts +43 -0
  232. package/dist/settings/ai-settings.js +147 -0
  233. package/dist/settings/app-tool-setting-rows.d.ts +16 -0
  234. package/dist/settings/app-tool-setting-rows.js +30 -0
  235. package/dist/settings/register-ai-settings.d.ts +3 -0
  236. package/dist/settings/register-ai-settings.js +393 -0
  237. package/dist/skills/activation.d.ts +2 -0
  238. package/dist/skills/activation.js +20 -0
  239. package/dist/skills/bundled/lapis-notes.d.ts +3 -0
  240. package/dist/skills/bundled/lapis-notes.js +52 -0
  241. package/dist/skills/bundled/research.d.ts +6 -0
  242. package/dist/skills/bundled/research.js +29 -0
  243. package/dist/skills/discovery.d.ts +21 -0
  244. package/dist/skills/discovery.js +227 -0
  245. package/dist/skills/manifest.d.ts +3 -0
  246. package/dist/skills/manifest.js +39 -0
  247. package/dist/skills/parser.d.ts +12 -0
  248. package/dist/skills/parser.js +124 -0
  249. package/dist/skills/registry.d.ts +21 -0
  250. package/dist/skills/registry.js +89 -0
  251. package/dist/skills/seed.d.ts +5 -0
  252. package/dist/skills/seed.js +18 -0
  253. package/dist/skills/skill-tools.d.ts +8 -0
  254. package/dist/skills/skill-tools.js +113 -0
  255. package/dist/skills/types.d.ts +67 -0
  256. package/dist/skills/types.js +1 -0
  257. package/dist/styles.css +514 -0
  258. package/dist/tools/app-tool-host.d.ts +47 -0
  259. package/dist/tools/app-tool-host.js +419 -0
  260. package/dist/tools/desktop-app-tool-bridge.d.ts +23 -0
  261. package/dist/tools/desktop-app-tool-bridge.js +223 -0
  262. package/dist/tools/mcp-server-registry.d.ts +7 -0
  263. package/dist/tools/mcp-server-registry.js +30 -0
  264. package/manifest.json +34 -0
  265. package/package.json +83 -0
  266. package/styles.css +1 -0
@@ -0,0 +1,16 @@
1
+ import type { EffectiveSlashCommand, SlashCommandSource } from "./types";
2
+ export type ComposerSlashItem = {
3
+ id: string;
4
+ label: string;
5
+ value: string;
6
+ description: string;
7
+ source: SlashCommandSource;
8
+ submitOnSelect: boolean;
9
+ };
10
+ export type SlashCommandGroup = "app" | "actions" | "skills" | "agent";
11
+ export declare function slashCommandGroup(command: EffectiveSlashCommand): SlashCommandGroup;
12
+ export declare function groupLabel(group: SlashCommandGroup, agentLabel?: string): string;
13
+ export declare function groupSlashCommands(commands: readonly EffectiveSlashCommand[]): Record<SlashCommandGroup, EffectiveSlashCommand[]>;
14
+ export declare function formatSlashHelp(commands: readonly EffectiveSlashCommand[], agentLabel?: string): string;
15
+ export declare function composerSlashItems(commands: readonly EffectiveSlashCommand[], agentLabel?: string): ComposerSlashItem[];
16
+ export declare function filterComposerSlashItems(items: readonly ComposerSlashItem[], query: string): ComposerSlashItem[];
@@ -0,0 +1,121 @@
1
+ import { fuzzySearch } from "@lapis-notes/ui";
2
+ const SLASH_SEARCH_KEYS = [
3
+ { name: "label", weight: 2 },
4
+ { name: "id", weight: 2 },
5
+ ];
6
+ function normalizeSlashQuery(query) {
7
+ return query.trim().replace(/^\/+/u, "");
8
+ }
9
+ function commandNameFields(item) {
10
+ const label = item.label.replace(/^\/+/u, "");
11
+ const idTail = item.id.includes(":")
12
+ ? item.id.slice(item.id.lastIndexOf(":") + 1)
13
+ : item.id;
14
+ return [item.id, idTail, label, item.value.replace(/^\/+/u, "")];
15
+ }
16
+ function matchesCommandName(item, query) {
17
+ const needle = query.toLowerCase();
18
+ return commandNameFields(item).some((field) => field.toLowerCase().includes(needle));
19
+ }
20
+ const GROUP_LABEL = {
21
+ app: "App",
22
+ actions: "Actions",
23
+ skills: "Skills",
24
+ agent: "Current Agent",
25
+ };
26
+ const GROUP_ORDER = [
27
+ "app",
28
+ "actions",
29
+ "skills",
30
+ "agent",
31
+ ];
32
+ export function slashCommandGroup(command) {
33
+ if (command.source === "extension" ||
34
+ command.source === "folder" ||
35
+ command.source === "vault" ||
36
+ command.source === "user") {
37
+ return "actions";
38
+ }
39
+ if (command.source === "skill")
40
+ return "skills";
41
+ if (command.source === "native-agent")
42
+ return "agent";
43
+ return "app";
44
+ }
45
+ export function groupLabel(group, agentLabel) {
46
+ if (group === "agent" && agentLabel)
47
+ return `Current Agent · ${agentLabel}`;
48
+ return GROUP_LABEL[group];
49
+ }
50
+ export function groupSlashCommands(commands) {
51
+ const groups = {
52
+ app: [],
53
+ actions: [],
54
+ skills: [],
55
+ agent: [],
56
+ };
57
+ for (const command of commands) {
58
+ groups[slashCommandGroup(command)].push(command);
59
+ }
60
+ return groups;
61
+ }
62
+ export function formatSlashHelp(commands, agentLabel) {
63
+ const grouped = groupSlashCommands(commands);
64
+ const sections = [];
65
+ for (const group of GROUP_ORDER) {
66
+ const items = grouped[group];
67
+ if (items.length === 0)
68
+ continue;
69
+ const lines = [groupLabel(group, agentLabel)];
70
+ for (const command of items) {
71
+ const name = command.source === "native-agent"
72
+ ? `/native ${command.name}`
73
+ : `/${command.name}`;
74
+ const hint = command.argumentHint ? ` ${command.argumentHint}` : "";
75
+ lines.push(` ${name}${hint} — ${command.description}`);
76
+ }
77
+ sections.push(lines.join("\n"));
78
+ }
79
+ return sections.join("\n\n") || "No commands are available.";
80
+ }
81
+ export function composerSlashItems(commands, agentLabel) {
82
+ const grouped = groupSlashCommands(commands);
83
+ const items = [];
84
+ for (const group of GROUP_ORDER) {
85
+ const heading = groupLabel(group, agentLabel);
86
+ for (const command of grouped[group]) {
87
+ const native = command.source === "native-agent";
88
+ const label = native ? `/native ${command.name}` : `/${command.name}`;
89
+ items.push({
90
+ id: native ? `native:${command.name}` : command.name,
91
+ label,
92
+ value: label,
93
+ description: `${heading} · ${command.description}`,
94
+ source: command.source,
95
+ submitOnSelect: !command.argumentHint,
96
+ });
97
+ }
98
+ }
99
+ return items;
100
+ }
101
+ export function filterComposerSlashItems(items, query) {
102
+ const needle = normalizeSlashQuery(query);
103
+ if (!needle)
104
+ return [...items];
105
+ const options = {
106
+ keys: [...SLASH_SEARCH_KEYS, { name: "description", weight: 0.4 }],
107
+ minMatchCharLength: 1,
108
+ };
109
+ const matches = fuzzySearch([...items], needle, options);
110
+ const nameHits = [];
111
+ const descriptionHits = [];
112
+ for (const { item } of matches) {
113
+ if (matchesCommandName(item, needle)) {
114
+ nameHits.push(item);
115
+ }
116
+ else {
117
+ descriptionHits.push(item);
118
+ }
119
+ }
120
+ return [...nameHits, ...descriptionHits];
121
+ }
@@ -0,0 +1,18 @@
1
+ export declare function formatScopeNotice(input: {
2
+ scopeDir: string;
3
+ launchNotePath?: string;
4
+ workspace?: string;
5
+ source?: "explicit" | "active-file" | "vault-root" | "conversation" | "folder";
6
+ }): string;
7
+ export declare function formatContextNotice(input: {
8
+ conversationId?: string;
9
+ scopeDir: string;
10
+ launchNotePath?: string;
11
+ workspace?: string;
12
+ agent: string;
13
+ model?: string;
14
+ tools: readonly string[];
15
+ skills: readonly string[];
16
+ folderInstructionPaths?: readonly string[];
17
+ truncated?: boolean;
18
+ }): string;
@@ -0,0 +1,29 @@
1
+ export function formatScopeNotice(input) {
2
+ const scope = input.scopeDir.trim() || "(vault root)";
3
+ const source = input.source ?? (input.scopeDir.trim() ? "folder" : "vault-root");
4
+ return [
5
+ `Scope: ${scope}`,
6
+ `Source: ${source}`,
7
+ `Started from: ${input.launchNotePath?.trim() || "(none)"}`,
8
+ `Workspace: ${input.workspace?.trim() || "(none)"}`,
9
+ ].join("\n");
10
+ }
11
+ export function formatContextNotice(input) {
12
+ const tools = input.tools.length > 0 ? input.tools.join(", ") : "(none)";
13
+ const skills = input.skills.length > 0 ? input.skills.join(", ") : "(none)";
14
+ const folders = input.folderInstructionPaths && input.folderInstructionPaths.length > 0
15
+ ? input.folderInstructionPaths.join(", ")
16
+ : "(none)";
17
+ return [
18
+ `Conversation: ${input.conversationId ?? "(none)"}`,
19
+ `Scope: ${input.scopeDir.trim() || "(vault root)"}`,
20
+ `Started from: ${input.launchNotePath?.trim() || "(none)"}`,
21
+ `Workspace: ${input.workspace?.trim() || "(none)"}`,
22
+ `Agent: ${input.agent}`,
23
+ `Model: ${input.model?.trim() || "(none)"}`,
24
+ `Available app tools: ${tools}`,
25
+ `Available skills: ${skills}`,
26
+ `Folder instructions: ${folders}`,
27
+ `Context status: ${input.truncated ? "Bootstrap truncated" : "No bootstrap truncation"}`,
28
+ ].join("\n");
29
+ }
@@ -0,0 +1,21 @@
1
+ import type { AppSlashCommandDispatch } from "@lapis-notes/api/agent-skills";
2
+ export declare class CommandParseError extends Error {
3
+ readonly path: string;
4
+ constructor(path: string, message: string);
5
+ }
6
+ export type CommandFileKind = "prompt" | "host" | "tool" | "skill";
7
+ export interface ParsedCommandFile {
8
+ name: string;
9
+ description: string;
10
+ argumentHint?: string;
11
+ aliases?: string[];
12
+ kind: CommandFileKind;
13
+ tool?: string;
14
+ skill?: string;
15
+ template: string;
16
+ }
17
+ export declare function commandNameFromFilename(filename: string): string | undefined;
18
+ export declare function parseCommandMarkdown(content: string, path: string, name: string): ParsedCommandFile;
19
+ export declare function commandFileDispatch(parsed: ParsedCommandFile): AppSlashCommandDispatch;
20
+ export declare function parseCommandArguments(raw: string): string[];
21
+ export declare function interpolateCommandTemplate(template: string, rawArguments: string): string;
@@ -0,0 +1,117 @@
1
+ import { parse as parseYaml } from "yaml";
2
+ import { SLASH_COMMAND_NAME_PATTERN } from "@lapis-notes/api/agent-skills";
3
+ const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/u;
4
+ const POSITIONAL = /\$([1-9]\d*)/gu;
5
+ export class CommandParseError extends Error {
6
+ path;
7
+ constructor(path, message) {
8
+ super(message);
9
+ this.path = path;
10
+ this.name = "CommandParseError";
11
+ }
12
+ }
13
+ function isRecord(value) {
14
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
15
+ }
16
+ function readString(value) {
17
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
18
+ }
19
+ function readStringList(value) {
20
+ if (typeof value === "string") {
21
+ return readStringList(value.split(","));
22
+ }
23
+ if (!Array.isArray(value))
24
+ return undefined;
25
+ const items = value
26
+ .filter((item) => typeof item === "string")
27
+ .map((item) => item.trim())
28
+ .filter(Boolean);
29
+ return items.length > 0 ? items : undefined;
30
+ }
31
+ export function commandNameFromFilename(filename) {
32
+ const name = filename.replace(/\.md$/u, "").trim();
33
+ return SLASH_COMMAND_NAME_PATTERN.test(name) ? name : undefined;
34
+ }
35
+ export function parseCommandMarkdown(content, path, name) {
36
+ if (!SLASH_COMMAND_NAME_PATTERN.test(name)) {
37
+ throw new CommandParseError(path, `Invalid command name: ${name}`);
38
+ }
39
+ const match = FRONTMATTER.exec(content);
40
+ const data = match ? parseYaml(match[1] ?? "") : {};
41
+ if (match && !isRecord(data)) {
42
+ throw new CommandParseError(path, "Command front-matter must be a mapping.");
43
+ }
44
+ const record = isRecord(data) ? data : {};
45
+ const kind = (readString(record.kind) ?? "prompt");
46
+ if (kind !== "prompt" &&
47
+ kind !== "host" &&
48
+ kind !== "tool" &&
49
+ kind !== "skill") {
50
+ throw new CommandParseError(path, `Unsupported command kind: ${kind}`);
51
+ }
52
+ const tool = readString(record.tool);
53
+ const skill = readString(record.skill);
54
+ if (kind === "tool" && !tool) {
55
+ throw new CommandParseError(path, "kind: tool requires tool");
56
+ }
57
+ if (kind === "skill" && !skill) {
58
+ throw new CommandParseError(path, "kind: skill requires skill");
59
+ }
60
+ const body = (match ? match[2] ?? "" : content).replace(/^\s+|\s+$/gu, "");
61
+ return {
62
+ name,
63
+ description: readString(record.description) ?? name,
64
+ argumentHint: readString(record.argumentHint) ?? readString(record["argument-hint"]),
65
+ aliases: readStringList(record.aliases),
66
+ kind,
67
+ tool,
68
+ skill,
69
+ template: body,
70
+ };
71
+ }
72
+ export function commandFileDispatch(parsed) {
73
+ if (parsed.kind === "tool" && parsed.tool) {
74
+ return { kind: "tool", tool: parsed.tool };
75
+ }
76
+ if (parsed.kind === "skill" && parsed.skill) {
77
+ return { kind: "skill", skill: parsed.skill };
78
+ }
79
+ if (parsed.kind === "host") {
80
+ return { kind: "host", execute: () => undefined };
81
+ }
82
+ return { kind: "prompt", template: parsed.template };
83
+ }
84
+ export function parseCommandArguments(raw) {
85
+ const items = [];
86
+ const pattern = /"([^"]*)"|'([^']*)'|(\S+)/gu;
87
+ let match = pattern.exec(raw);
88
+ while (match) {
89
+ items.push(match[1] ?? match[2] ?? match[3] ?? "");
90
+ match = pattern.exec(raw);
91
+ }
92
+ return items;
93
+ }
94
+ export function interpolateCommandTemplate(template, rawArguments) {
95
+ const body = template.replace(/^\s+|\s+$/gu, "");
96
+ const args = rawArguments.trim();
97
+ const hasNamed = body.includes("$ARGUMENTS") || body.includes("{{args}}");
98
+ const positionals = [...body.matchAll(POSITIONAL)].map((item) => Number(item[1]));
99
+ const hasPositional = positionals.length > 0;
100
+ let result = body
101
+ .replaceAll("$ARGUMENTS", args)
102
+ .replaceAll("{{args}}", args);
103
+ if (hasPositional) {
104
+ const parsed = parseCommandArguments(rawArguments);
105
+ const max = Math.max(...positionals);
106
+ result = result.replace(POSITIONAL, (_match, digits) => {
107
+ const index = Number(digits);
108
+ if (index === max)
109
+ return parsed.slice(index - 1).join(" ");
110
+ return parsed[index - 1] ?? "";
111
+ });
112
+ }
113
+ if (!hasNamed && !hasPositional && args) {
114
+ return `${result}\n\n${args}`;
115
+ }
116
+ return result;
117
+ }
@@ -0,0 +1,4 @@
1
+ import type { ParsedSlashCommand } from "./types";
2
+ export declare function parseSlashCommand(input: string): ParsedSlashCommand | undefined;
3
+ export declare function isLiteralSlashText(input: string): boolean;
4
+ export declare function unescapeLiteralSlash(input: string): string;
@@ -0,0 +1,24 @@
1
+ const COMMAND_PATTERN = /^\/(?<name>[A-Za-z][A-Za-z0-9_-]{0,63})(?<sep>[:\s]|$)(?<args>.*)$/u;
2
+ export function parseSlashCommand(input) {
3
+ const text = input.trimStart();
4
+ if (text.startsWith("//"))
5
+ return undefined;
6
+ if (!text.startsWith("/"))
7
+ return undefined;
8
+ const match = text.match(COMMAND_PATTERN);
9
+ if (!match?.groups?.name)
10
+ return undefined;
11
+ return {
12
+ name: match.groups.name.toLowerCase(),
13
+ rawArguments: (match.groups.args ?? "").trim(),
14
+ original: input,
15
+ };
16
+ }
17
+ export function isLiteralSlashText(input) {
18
+ return input.trimStart().startsWith("//");
19
+ }
20
+ export function unescapeLiteralSlash(input) {
21
+ const trimmedStart = input.match(/^\s*/u)?.[0] ?? "";
22
+ const rest = input.slice(trimmedStart.length);
23
+ return rest.startsWith("//") ? `${trimmedStart}${rest.slice(1)}` : input;
24
+ }
@@ -0,0 +1,4 @@
1
+ import type { EffectiveSlashCommand } from "./types";
2
+ export declare const RESERVED_SLASH_COMMANDS: EffectiveSlashCommand[];
3
+ export declare function reservedSlashCommandNames(): Set<string>;
4
+ export declare function reservedCommandMarkdown(command: EffectiveSlashCommand): string;
@@ -0,0 +1,107 @@
1
+ export const RESERVED_SLASH_COMMANDS = [
2
+ {
3
+ name: "help",
4
+ description: "Show available commands grouped by App, Actions, Skills, and Current Agent.",
5
+ aliases: ["commands"],
6
+ source: "app",
7
+ dispatch: { kind: "host", execute: () => undefined },
8
+ },
9
+ {
10
+ name: "new",
11
+ description: "Start a new chat in the current scope.",
12
+ source: "app",
13
+ dispatch: { kind: "host", execute: () => undefined },
14
+ },
15
+ {
16
+ name: "agent",
17
+ description: "Show or switch the current agent.",
18
+ argumentHint: "[codex|cursor|native|fake]",
19
+ source: "app",
20
+ dispatch: { kind: "host", execute: () => undefined },
21
+ },
22
+ {
23
+ name: "model",
24
+ description: "Reserved. Change the model from the composer Model menu.",
25
+ argumentHint: "[name]",
26
+ source: "app",
27
+ dispatch: { kind: "host", execute: () => undefined },
28
+ },
29
+ {
30
+ name: "status",
31
+ description: "Show conversation, agent, model, scope, and executor context.",
32
+ source: "app",
33
+ dispatch: { kind: "host", execute: () => undefined },
34
+ },
35
+ {
36
+ name: "scope",
37
+ description: "Show the current folder scope, or start a new chat in a folder.",
38
+ argumentHint: "[folder]",
39
+ source: "app",
40
+ dispatch: { kind: "host", execute: () => undefined },
41
+ },
42
+ {
43
+ name: "context",
44
+ description: "Show the context the app is making available to the agent.",
45
+ source: "app",
46
+ dispatch: { kind: "host", execute: () => undefined },
47
+ },
48
+ {
49
+ name: "skills",
50
+ description: "List effective application skills.",
51
+ source: "app",
52
+ dispatch: { kind: "host", execute: () => undefined },
53
+ },
54
+ {
55
+ name: "tools",
56
+ description: "List effective application tools.",
57
+ source: "app",
58
+ dispatch: { kind: "host", execute: () => undefined },
59
+ },
60
+ {
61
+ name: "skill",
62
+ description: "Activate a skill by name.",
63
+ argumentHint: "<name> [arguments]",
64
+ source: "app",
65
+ dispatch: { kind: "host", execute: () => undefined },
66
+ },
67
+ {
68
+ name: "native",
69
+ description: "Forward a command to the current agent.",
70
+ argumentHint: "<command> [arguments]",
71
+ source: "app",
72
+ dispatch: { kind: "host", execute: () => undefined },
73
+ },
74
+ {
75
+ name: "cancel",
76
+ description: "Reserved. Use Stop in the composer to cancel the active run.",
77
+ source: "app",
78
+ dispatch: { kind: "host", execute: () => undefined },
79
+ },
80
+ {
81
+ name: "refresh",
82
+ description: "Refresh agent skills on a replacement binding.",
83
+ source: "app",
84
+ dispatch: { kind: "host", execute: () => undefined },
85
+ },
86
+ ];
87
+ export function reservedSlashCommandNames() {
88
+ return new Set(RESERVED_SLASH_COMMANDS.flatMap((command) => [
89
+ command.name,
90
+ ...(command.aliases ?? []),
91
+ ]));
92
+ }
93
+ export function reservedCommandMarkdown(command) {
94
+ const lines = [
95
+ "---",
96
+ `description: ${JSON.stringify(command.description)}`,
97
+ "kind: host",
98
+ ];
99
+ if (command.argumentHint) {
100
+ lines.push(`argumentHint: ${JSON.stringify(command.argumentHint)}`);
101
+ }
102
+ if (command.aliases?.length) {
103
+ lines.push(`aliases: [${command.aliases.map((alias) => JSON.stringify(alias)).join(", ")}]`);
104
+ }
105
+ lines.push("---", "", `Reserved Lapis command \`/${command.name}\`. This file documents the host handler and is not sent as a prompt.`, "");
106
+ return lines.join("\n");
107
+ }
@@ -0,0 +1,17 @@
1
+ import type { SkillRegistry } from "../skills/registry";
2
+ import type { LoadedAppSkill, SkillDiscoveryContext } from "../skills/types";
3
+ import { SlashCommandCatalog } from "./catalog";
4
+ import type { CommandExecutionResult, CommandResolution, ParsedSlashCommand } from "./types";
5
+ export declare class SlashCommandRouter {
6
+ #private;
7
+ readonly catalog: SlashCommandCatalog;
8
+ private readonly skills?;
9
+ constructor(catalog: SlashCommandCatalog, skills?: SkillRegistry | undefined);
10
+ parse(input: string): ParsedSlashCommand | undefined;
11
+ resolve(input: string, agentBindingId?: string): CommandResolution | undefined;
12
+ execute(resolution: CommandResolution, context: {
13
+ agentBindingId?: string;
14
+ discovery: SkillDiscoveryContext;
15
+ loadSkill?: (name: string) => LoadedAppSkill | undefined;
16
+ }): Promise<CommandExecutionResult>;
17
+ }
@@ -0,0 +1,170 @@
1
+ import { SlashCommandCatalog } from "./catalog";
2
+ import { interpolateCommandTemplate } from "./markdown";
3
+ import { isLiteralSlashText, parseSlashCommand, unescapeLiteralSlash, } from "./parser";
4
+ export class SlashCommandRouter {
5
+ catalog;
6
+ skills;
7
+ constructor(catalog, skills) {
8
+ this.catalog = catalog;
9
+ this.skills = skills;
10
+ }
11
+ parse(input) {
12
+ return parseSlashCommand(input);
13
+ }
14
+ resolve(input, agentBindingId) {
15
+ if (isLiteralSlashText(input)) {
16
+ return { kind: "literal", text: unescapeLiteralSlash(input) };
17
+ }
18
+ const parsed = parseSlashCommand(input);
19
+ if (!parsed)
20
+ return undefined;
21
+ const command = this.catalog.get(parsed.name, agentBindingId);
22
+ if (!command) {
23
+ const suggestions = this.catalog
24
+ .list(agentBindingId)
25
+ .map((item) => item.name)
26
+ .filter((name) => name.startsWith(parsed.name.slice(0, 2)))
27
+ .slice(0, 5);
28
+ return { kind: "unknown", parsed, suggestions };
29
+ }
30
+ return { kind: "command", parsed, command };
31
+ }
32
+ async execute(resolution, context) {
33
+ if (resolution.kind === "literal") {
34
+ return { kind: "prompt", prompt: resolution.text };
35
+ }
36
+ if (resolution.kind === "unknown") {
37
+ const hint = resolution.suggestions.length > 0
38
+ ? ` Did you mean /${resolution.suggestions.join(", /")}?`
39
+ : "";
40
+ return {
41
+ kind: "error",
42
+ message: `Unknown command: /${resolution.parsed.name}.${hint}`,
43
+ };
44
+ }
45
+ const { parsed, command } = resolution;
46
+ return this.#dispatch(command, parsed, context);
47
+ }
48
+ async #dispatch(command, parsed, context) {
49
+ if (command.name === "skill") {
50
+ const [skillName, ...rest] = parsed.rawArguments.split(/\s+/u);
51
+ if (!skillName) {
52
+ return { kind: "error", message: "Usage: /skill <name> [arguments]" };
53
+ }
54
+ return this.#activateSkill(skillName, rest.join(" "), context);
55
+ }
56
+ if (command.name === "native") {
57
+ const [nativeName, ...rest] = parsed.rawArguments.split(/\s+/u);
58
+ if (!nativeName) {
59
+ return { kind: "error", message: "Usage: /native <command> [arguments]" };
60
+ }
61
+ const native = this.catalog.native(context.agentBindingId, nativeName);
62
+ if (!native) {
63
+ return {
64
+ kind: "error",
65
+ message: `Native command is unavailable: ${nativeName}`,
66
+ };
67
+ }
68
+ return {
69
+ kind: "native",
70
+ name: native.name,
71
+ arguments: rest.join(" "),
72
+ };
73
+ }
74
+ if (command.name === "new")
75
+ return { kind: "local", notice: "new" };
76
+ if (command.name === "help" || command.name === "commands") {
77
+ return { kind: "local", notice: "help" };
78
+ }
79
+ if (command.name === "skills")
80
+ return { kind: "local", notice: "skills" };
81
+ if (command.name === "tools")
82
+ return { kind: "local", notice: "tools" };
83
+ if (command.name === "refresh")
84
+ return { kind: "local", notice: "refresh" };
85
+ if (command.name === "scope") {
86
+ return {
87
+ kind: "local",
88
+ notice: "scope",
89
+ arguments: parsed.rawArguments,
90
+ };
91
+ }
92
+ if (command.name === "context" || command.name === "status") {
93
+ return { kind: "local", notice: "context" };
94
+ }
95
+ if (command.name === "model")
96
+ return { kind: "local", notice: "model" };
97
+ if (command.name === "cancel")
98
+ return { kind: "local", notice: "cancel" };
99
+ if (command.name === "agent") {
100
+ return {
101
+ kind: "local",
102
+ notice: "agent",
103
+ arguments: parsed.rawArguments,
104
+ };
105
+ }
106
+ const dispatch = command.dispatch;
107
+ if (dispatch.kind === "host") {
108
+ await dispatch.execute({
109
+ arguments: parsed.rawArguments,
110
+ });
111
+ return { kind: "local", notice: command.name };
112
+ }
113
+ if (dispatch.kind === "tool") {
114
+ return toolDispatch(dispatch.tool, parsed.rawArguments, command.name);
115
+ }
116
+ if (dispatch.kind === "skill") {
117
+ return this.#activateSkill(dispatch.skill, parsed.rawArguments, context);
118
+ }
119
+ if (dispatch.kind === "prompt") {
120
+ return {
121
+ kind: "prompt",
122
+ prompt: interpolateCommandTemplate(dispatch.template, parsed.rawArguments),
123
+ };
124
+ }
125
+ if (dispatch.kind === "native-agent") {
126
+ return {
127
+ kind: "native",
128
+ name: dispatch.nativeName,
129
+ arguments: parsed.rawArguments,
130
+ };
131
+ }
132
+ return { kind: "error", message: `Unsupported command: /${command.name}` };
133
+ }
134
+ async #activateSkill(name, args, context) {
135
+ const loaded = this.skills
136
+ ? await this.skills.resolve(name, context.discovery)
137
+ : undefined;
138
+ if (!loaded) {
139
+ return { kind: "error", message: `Unknown skill: ${name}` };
140
+ }
141
+ if (loaded.command.kind === "tool") {
142
+ return toolDispatch(loaded.command.tool, args, name);
143
+ }
144
+ const activation = this.skills
145
+ ? await this.skills.activate(name, context.discovery, "user", args)
146
+ : undefined;
147
+ if (!activation) {
148
+ return { kind: "error", message: `Unknown skill: ${name}` };
149
+ }
150
+ return { kind: "skill", activation };
151
+ }
152
+ }
153
+ function toolDispatch(tool, args, commandName) {
154
+ if (tool === "notes_search") {
155
+ const query = args.trim();
156
+ if (!query) {
157
+ return { kind: "error", message: `Usage: /${commandName} <query>` };
158
+ }
159
+ return { kind: "tool", tool, input: { query } };
160
+ }
161
+ return {
162
+ kind: "tool",
163
+ tool,
164
+ input: {
165
+ command: args,
166
+ commandName,
167
+ skillName: commandName,
168
+ },
169
+ };
170
+ }
@@ -0,0 +1,4 @@
1
+ import type { UserAgentsCommandStore } from "./user-agents";
2
+ export declare function seedReservedCommands(store: UserAgentsCommandStore, options?: {
3
+ overwrite?: boolean;
4
+ }): Promise<void>;
@@ -0,0 +1,8 @@
1
+ import { reservedCommandMarkdown, RESERVED_SLASH_COMMANDS, } from "./reserved";
2
+ export async function seedReservedCommands(store, options = {}) {
3
+ for (const command of RESERVED_SLASH_COMMANDS) {
4
+ if (!options.overwrite && (await store.exists(command.name)))
5
+ continue;
6
+ await store.write(command.name, reservedCommandMarkdown(command));
7
+ }
8
+ }