@openvulcan/dsh-vulcancode 0.0.0-stage → 0.4.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 (176) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -2
  3. package/assets/openvulcan-icon.svg +33 -0
  4. package/cordis.patch.yml +19 -0
  5. package/node_modules/@openvulcan/dsh-decision-guard/LICENSE +21 -0
  6. package/node_modules/@openvulcan/dsh-decision-guard/README.md +14 -0
  7. package/node_modules/@openvulcan/dsh-decision-guard/assets/decision-guard-icon.png +0 -0
  8. package/node_modules/@openvulcan/dsh-decision-guard/lib/calls.d.ts +40 -0
  9. package/node_modules/@openvulcan/dsh-decision-guard/lib/calls.js +150 -0
  10. package/node_modules/@openvulcan/dsh-decision-guard/lib/client.js +2250 -0
  11. package/node_modules/@openvulcan/dsh-decision-guard/lib/decide.d.ts +90 -0
  12. package/node_modules/@openvulcan/dsh-decision-guard/lib/decide.js +108 -0
  13. package/node_modules/@openvulcan/dsh-decision-guard/lib/index.d.ts +129 -0
  14. package/node_modules/@openvulcan/dsh-decision-guard/lib/index.js +572 -0
  15. package/node_modules/@openvulcan/dsh-decision-guard/lib/intent.d.ts +22 -0
  16. package/node_modules/@openvulcan/dsh-decision-guard/lib/intent.js +77 -0
  17. package/node_modules/@openvulcan/dsh-decision-guard/lib/panel.d.ts +158 -0
  18. package/node_modules/@openvulcan/dsh-decision-guard/lib/panel.js +340 -0
  19. package/node_modules/@openvulcan/dsh-decision-guard/lib/records.d.ts +133 -0
  20. package/node_modules/@openvulcan/dsh-decision-guard/lib/records.js +186 -0
  21. package/node_modules/@openvulcan/dsh-decision-guard/lib/review.d.ts +55 -0
  22. package/node_modules/@openvulcan/dsh-decision-guard/lib/review.js +202 -0
  23. package/node_modules/@openvulcan/dsh-decision-guard/lib/session-state.d.ts +62 -0
  24. package/node_modules/@openvulcan/dsh-decision-guard/lib/session-state.js +155 -0
  25. package/node_modules/@openvulcan/dsh-decision-guard/lib/version.d.ts +2 -0
  26. package/node_modules/@openvulcan/dsh-decision-guard/lib/version.js +4 -0
  27. package/node_modules/@openvulcan/dsh-decision-guard/package.json +68 -0
  28. package/node_modules/@openvulcan/dsh-vulcan-account/LICENSE +21 -0
  29. package/node_modules/@openvulcan/dsh-vulcan-account/assets/account-icon.png +0 -0
  30. package/node_modules/@openvulcan/dsh-vulcan-account/lib/client.js +573 -0
  31. package/node_modules/@openvulcan/dsh-vulcan-account/lib/index.d.ts +92 -0
  32. package/node_modules/@openvulcan/dsh-vulcan-account/lib/index.js +194 -0
  33. package/node_modules/@openvulcan/dsh-vulcan-account/package.json +57 -0
  34. package/node_modules/@openvulcan/dsh-vulcan-answers/LICENSE +21 -0
  35. package/node_modules/@openvulcan/dsh-vulcan-answers/assets/answers-icon.png +0 -0
  36. package/node_modules/@openvulcan/dsh-vulcan-answers/assets/prompts/answer-mode-interactive.md +79 -0
  37. package/node_modules/@openvulcan/dsh-vulcan-answers/assets/prompts/answer-mode-standard.md +1 -0
  38. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/answer-model.d.ts +258 -0
  39. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/answer-model.js +698 -0
  40. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/client.js +671 -0
  41. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/fences.d.ts +29 -0
  42. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/fences.js +70 -0
  43. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/index.d.ts +19 -0
  44. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/index.js +52 -0
  45. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/limits.d.ts +9 -0
  46. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/limits.js +9 -0
  47. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/prompt.d.ts +22 -0
  48. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/prompt.js +39 -0
  49. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/reply-format.d.ts +20 -0
  50. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/reply-format.js +17 -0
  51. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/session-state.d.ts +66 -0
  52. package/node_modules/@openvulcan/dsh-vulcan-answers/lib/session-state.js +118 -0
  53. package/node_modules/@openvulcan/dsh-vulcan-answers/package.json +59 -0
  54. package/node_modules/@openvulcan/dsh-vulcan-luaskills/LICENSE +21 -0
  55. package/node_modules/@openvulcan/dsh-vulcan-luaskills/assets/luaskills-icon.svg +1 -0
  56. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/client.js +949 -0
  57. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/constants.d.ts +46 -0
  58. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/constants.js +51 -0
  59. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/engine.d.ts +55 -0
  60. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/engine.js +149 -0
  61. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/hub.d.ts +49 -0
  62. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/hub.js +116 -0
  63. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/index.d.ts +26 -0
  64. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/index.js +276 -0
  65. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/protocol.d.ts +65 -0
  66. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/protocol.js +2 -0
  67. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/runtime.d.ts +111 -0
  68. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/runtime.js +281 -0
  69. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/sdk-install.d.ts +26 -0
  70. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/sdk-install.js +87 -0
  71. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/settings.d.ts +79 -0
  72. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/settings.js +165 -0
  73. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/skills.d.ts +34 -0
  74. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/skills.js +61 -0
  75. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/tools.d.ts +59 -0
  76. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/tools.js +107 -0
  77. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/worker.d.ts +10 -0
  78. package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/worker.js +140 -0
  79. package/node_modules/@openvulcan/dsh-vulcan-luaskills/package.json +57 -0
  80. package/node_modules/@openvulcan/dsh-vulcan-memory/LICENSE +21 -0
  81. package/node_modules/@openvulcan/dsh-vulcan-memory/assets/markdown-memory-guide.md +21 -0
  82. package/node_modules/@openvulcan/dsh-vulcan-memory/assets/memory-curator/SKILL.md +31 -0
  83. package/node_modules/@openvulcan/dsh-vulcan-memory/assets/memory-icon.png +0 -0
  84. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/client.js +1090 -0
  85. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/index.d.ts +30 -0
  86. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/index.js +240 -0
  87. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/inject.d.ts +30 -0
  88. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/inject.js +46 -0
  89. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/constants.d.ts +23 -0
  90. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/constants.js +25 -0
  91. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/document.d.ts +108 -0
  92. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/document.js +373 -0
  93. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.d.ts +14 -0
  94. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.js +31 -0
  95. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.d.ts +196 -0
  96. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.js +204 -0
  97. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/operations.d.ts +92 -0
  98. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/operations.js +472 -0
  99. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/package.d.ts +126 -0
  100. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/package.js +553 -0
  101. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/registry.d.ts +56 -0
  102. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/registry.js +182 -0
  103. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/scope.d.ts +13 -0
  104. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/scope.js +24 -0
  105. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/search.d.ts +65 -0
  106. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/search.js +227 -0
  107. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/snapshot.d.ts +34 -0
  108. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/snapshot.js +171 -0
  109. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/store.d.ts +72 -0
  110. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/store.js +250 -0
  111. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/timestamp.d.ts +6 -0
  112. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/timestamp.js +12 -0
  113. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/tokens.d.ts +8 -0
  114. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/tokens.js +38 -0
  115. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/page.d.ts +132 -0
  116. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/page.js +235 -0
  117. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/project.d.ts +14 -0
  118. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/project.js +27 -0
  119. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/settings.d.ts +29 -0
  120. package/node_modules/@openvulcan/dsh-vulcan-memory/lib/settings.js +62 -0
  121. package/node_modules/@openvulcan/dsh-vulcan-memory/package.json +63 -0
  122. package/node_modules/@openvulcan/dsh-vulcan-reasoning/LICENSE +21 -0
  123. package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/client.js +509 -0
  124. package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/index.d.ts +6 -0
  125. package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/index.js +8 -0
  126. package/node_modules/@openvulcan/dsh-vulcan-reasoning/package.json +42 -0
  127. package/node_modules/@openvulcan/dsh-vulcan-style/LICENSE +21 -0
  128. package/node_modules/@openvulcan/dsh-vulcan-style/assets/style-icon.png +0 -0
  129. package/node_modules/@openvulcan/dsh-vulcan-style/lib/client.js +1121 -0
  130. package/node_modules/@openvulcan/dsh-vulcan-style/lib/index.d.ts +34 -0
  131. package/node_modules/@openvulcan/dsh-vulcan-style/lib/index.js +42 -0
  132. package/node_modules/@openvulcan/dsh-vulcan-style/package.json +51 -0
  133. package/node_modules/@openvulcan/openvulcan-account/LICENSE +21 -0
  134. package/node_modules/@openvulcan/openvulcan-account/lib/account.d.ts +140 -0
  135. package/node_modules/@openvulcan/openvulcan-account/lib/account.js +347 -0
  136. package/node_modules/@openvulcan/openvulcan-account/lib/index.d.ts +1 -0
  137. package/node_modules/@openvulcan/openvulcan-account/lib/index.js +1 -0
  138. package/node_modules/@openvulcan/openvulcan-account/package.json +29 -0
  139. package/node_modules/@openvulcan/vulcan-decision/LICENSE +21 -0
  140. package/node_modules/@openvulcan/vulcan-decision/README.md +23 -0
  141. package/node_modules/@openvulcan/vulcan-decision/bin/vulcan-decision.js +4 -0
  142. package/node_modules/@openvulcan/vulcan-decision/lib/cli.d.ts +5 -0
  143. package/node_modules/@openvulcan/vulcan-decision/lib/cli.js +267 -0
  144. package/node_modules/@openvulcan/vulcan-decision/lib/cloud/edge.d.ts +211 -0
  145. package/node_modules/@openvulcan/vulcan-decision/lib/cloud/edge.js +594 -0
  146. package/node_modules/@openvulcan/vulcan-decision/lib/download.d.ts +36 -0
  147. package/node_modules/@openvulcan/vulcan-decision/lib/download.js +99 -0
  148. package/node_modules/@openvulcan/vulcan-decision/lib/engine.d.ts +37 -0
  149. package/node_modules/@openvulcan/vulcan-decision/lib/engine.js +39 -0
  150. package/node_modules/@openvulcan/vulcan-decision/lib/hardware.d.ts +105 -0
  151. package/node_modules/@openvulcan/vulcan-decision/lib/hardware.js +207 -0
  152. package/node_modules/@openvulcan/vulcan-decision/lib/home.d.ts +26 -0
  153. package/node_modules/@openvulcan/vulcan-decision/lib/home.js +54 -0
  154. package/node_modules/@openvulcan/vulcan-decision/lib/index.d.ts +17 -0
  155. package/node_modules/@openvulcan/vulcan-decision/lib/index.js +17 -0
  156. package/node_modules/@openvulcan/vulcan-decision/lib/local/engine.d.ts +54 -0
  157. package/node_modules/@openvulcan/vulcan-decision/lib/local/engine.js +157 -0
  158. package/node_modules/@openvulcan/vulcan-decision/lib/local/models.d.ts +126 -0
  159. package/node_modules/@openvulcan/vulcan-decision/lib/local/models.js +261 -0
  160. package/node_modules/@openvulcan/vulcan-decision/lib/local/packs.d.ts +102 -0
  161. package/node_modules/@openvulcan/vulcan-decision/lib/local/packs.js +195 -0
  162. package/node_modules/@openvulcan/vulcan-decision/lib/local/worker.d.ts +61 -0
  163. package/node_modules/@openvulcan/vulcan-decision/lib/local/worker.js +123 -0
  164. package/node_modules/@openvulcan/vulcan-decision/lib/protocol.d.ts +74 -0
  165. package/node_modules/@openvulcan/vulcan-decision/lib/protocol.js +31 -0
  166. package/node_modules/@openvulcan/vulcan-decision/lib/render.d.ts +15 -0
  167. package/node_modules/@openvulcan/vulcan-decision/lib/render.js +33 -0
  168. package/node_modules/@openvulcan/vulcan-decision/lib/version.d.ts +2 -0
  169. package/node_modules/@openvulcan/vulcan-decision/lib/version.js +4 -0
  170. package/node_modules/@openvulcan/vulcan-decision/package.json +40 -0
  171. package/node_modules/@openvulcan/vulcan-decision/python/vulcan_decision_worker.py +363 -0
  172. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/onnx-cpu.json +627 -0
  173. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/onnx-cuda.json +423 -0
  174. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/torch-cpu.json +1047 -0
  175. package/node_modules/@openvulcan/vulcan-decision/runtime-packs/torch-cu128.json +647 -0
  176. package/package.json +55 -4
@@ -0,0 +1,61 @@
1
+ /**
2
+ * What is installed in the ROOT and USER skill layers, read from the runtime's folders: the
3
+ * skill folders themselves, their `skill.yaml` name and version, the install record and whether
4
+ * the skill is disabled. Reading changes nothing.
5
+ */
6
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
7
+ import path from 'node:path';
8
+ /** The layers this plugin manages; PROJECT skills are left to the project. */
9
+ export const LAYERS = [
10
+ { root: 'ROOT', folder: 'root_skills' },
11
+ { root: 'USER', folder: 'user_skills' },
12
+ ];
13
+ /** Top-level `key: value` scalars of a small YAML document; nested blocks are skipped. */
14
+ export function topLevelScalars(text) {
15
+ const values = new Map();
16
+ for (const line of text.split(/\r?\n/)) {
17
+ const match = /^([A-Za-z_][\w-]*):[ \t]*(.*?)[ \t]*$/.exec(line);
18
+ if (match === null || match[2] === '')
19
+ continue;
20
+ values.set(match[1], match[2].replace(/^(['"])(.*)\1$/, '$2'));
21
+ }
22
+ return values;
23
+ }
24
+ /** `source_type` and `locator` from the `source:` block of an install record. */
25
+ export function installSource(text) {
26
+ const block = /^source:\r?\n((?:[ \t]+.*(?:\r?\n|$))+)/m.exec(text)?.[1];
27
+ if (block === undefined)
28
+ return undefined;
29
+ const fields = topLevelScalars(block.split(/\r?\n/).map(line => line.trim()).join('\n'));
30
+ const type = fields.get('source_type');
31
+ const locator = fields.get('locator');
32
+ return type === undefined || locator === undefined ? undefined : { type, locator };
33
+ }
34
+ export function listInstalled(runtimeRoot) {
35
+ const state = path.join(runtimeRoot, 'state');
36
+ const skills = [];
37
+ for (const layer of LAYERS) {
38
+ const folder = path.join(runtimeRoot, layer.folder);
39
+ if (!existsSync(folder))
40
+ continue;
41
+ for (const entry of readdirSync(folder, { withFileTypes: true })) {
42
+ if (!entry.isDirectory())
43
+ continue;
44
+ const manifest = path.join(folder, entry.name, 'skill.yaml');
45
+ if (!existsSync(manifest))
46
+ continue;
47
+ const fields = topLevelScalars(readFileSync(manifest, 'utf8'));
48
+ const record = path.join(state, 'installs', `${entry.name}.yaml`);
49
+ const source = existsSync(record) ? installSource(readFileSync(record, 'utf8')) : undefined;
50
+ skills.push({
51
+ skillId: entry.name,
52
+ root: layer.root,
53
+ name: fields.get('name') ?? entry.name,
54
+ version: fields.get('version') ?? '',
55
+ enabled: !existsSync(path.join(state, 'skills', 'disabled', `${entry.name}.json`)),
56
+ ...source === undefined ? {} : { source },
57
+ });
58
+ }
59
+ }
60
+ return skills;
61
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * LuaSkills entries as dsh tools, named and filled in the way VulcanCode does: the canonical
3
+ * name lowercased with every other character turned into `_`, at most 64 characters, and `ls_` in
4
+ * front when dsh already has a tool of that name. The session id and working directory are filled
5
+ * in by the Host and hidden from the model.
6
+ */
7
+ import type { ToolDefinition } from '@deepseek-ai/dsh-tools';
8
+ import type { EntryDescriptor, InvocationResult } from './protocol.js';
9
+ /** Arguments the Host fills in; the model never sees them. */
10
+ export declare const SESSION_FIELD = "LUASKILL_SID";
11
+ export declare const DIRECTORY_FIELD = "PWD";
12
+ export declare function toolName(canonical: string, taken: (name: string) => boolean): string;
13
+ /** The entry's schema without the Host-filled fields, and which of them it declares. */
14
+ export declare function modelSchema(schema: unknown): {
15
+ parameters: Record<string, unknown>;
16
+ session: boolean;
17
+ directory: boolean;
18
+ };
19
+ /** The invocation context LuaSkills budgets and attributes a call by. */
20
+ export declare function invocationContext(tool: string, sessionId: string | undefined, dshVersion: string): {
21
+ request_context: {
22
+ session_id?: string;
23
+ request_id: string;
24
+ client_name: string;
25
+ client_info: {
26
+ kind: string;
27
+ name: string;
28
+ version: string;
29
+ };
30
+ };
31
+ client_budget: {
32
+ client_name: string;
33
+ tool_name: string;
34
+ matched_client_pattern: string;
35
+ tool_result: {
36
+ bytes: number;
37
+ lines: number;
38
+ };
39
+ file_read: {
40
+ bytes: number;
41
+ lines: number;
42
+ };
43
+ tool_config: {};
44
+ };
45
+ };
46
+ /** What a skill call needs from the Host. */
47
+ export interface ToolHost {
48
+ readonly dshVersion: string;
49
+ /** The session's working directory. */
50
+ workingDirectory(exec: Parameters<ToolDefinition['execute']>[1]): Promise<string>;
51
+ call(canonical: string, args: Record<string, unknown>, context: unknown): Promise<InvocationResult>;
52
+ }
53
+ /** The canonical value every LuaSkills tool returns. */
54
+ export interface SkillValue {
55
+ readonly content: string;
56
+ }
57
+ export declare function skillTool(name: string, entry: EntryDescriptor, host: ToolHost): ToolDefinition;
58
+ /** `luaskill_help`: a skill's main help or one of its topics, as listed in the skill catalog. */
59
+ export declare function helpTool(name: string, render: (skillId: string, flow: string | undefined) => Promise<string>): ToolDefinition;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * LuaSkills entries as dsh tools, named and filled in the way VulcanCode does: the canonical
3
+ * name lowercased with every other character turned into `_`, at most 64 characters, and `ls_` in
4
+ * front when dsh already has a tool of that name. The session id and working directory are filled
5
+ * in by the Host and hidden from the model.
6
+ */
7
+ import { RESULT_BUDGET_BYTES } from './constants.js';
8
+ /** dsh and DeepSeek function names: at most 64 characters. */
9
+ const MAX_TOOL_NAME = 64;
10
+ /** Arguments the Host fills in; the model never sees them. */
11
+ export const SESSION_FIELD = 'LUASKILL_SID';
12
+ export const DIRECTORY_FIELD = 'PWD';
13
+ const HIDDEN_FIELDS = [SESSION_FIELD, DIRECTORY_FIELD];
14
+ export function toolName(canonical, taken) {
15
+ const base = canonical.toLowerCase().replace(/[^a-z0-9]/g, '_').slice(0, MAX_TOOL_NAME);
16
+ return taken(base) ? `ls_${base}`.slice(0, MAX_TOOL_NAME) : base;
17
+ }
18
+ /** The entry's schema without the Host-filled fields, and which of them it declares. */
19
+ export function modelSchema(schema) {
20
+ const source = isRecord(schema) ? schema : {};
21
+ const properties = isRecord(source['properties']) ? { ...source['properties'] } : {};
22
+ const session = SESSION_FIELD in properties;
23
+ const directory = DIRECTORY_FIELD in properties;
24
+ for (const field of HIDDEN_FIELDS)
25
+ delete properties[field];
26
+ const required = Array.isArray(source['required'])
27
+ ? source['required'].filter(name => typeof name === 'string' && !HIDDEN_FIELDS.includes(name))
28
+ : undefined;
29
+ return {
30
+ parameters: { ...source, type: 'object', properties, ...required === undefined ? {} : { required } },
31
+ session,
32
+ directory,
33
+ };
34
+ }
35
+ /** The invocation context LuaSkills budgets and attributes a call by. */
36
+ export function invocationContext(tool, sessionId, dshVersion) {
37
+ return {
38
+ request_context: {
39
+ request_id: `luaskills-tool:${tool}`,
40
+ client_name: 'dsh',
41
+ client_info: { kind: 'cli', name: 'dsh', version: dshVersion },
42
+ ...sessionId === undefined ? {} : { session_id: sessionId },
43
+ },
44
+ client_budget: {
45
+ client_name: 'dsh',
46
+ tool_name: tool,
47
+ matched_client_pattern: 'dsh',
48
+ tool_result: { bytes: RESULT_BUDGET_BYTES, lines: -1 },
49
+ file_read: { bytes: RESULT_BUDGET_BYTES, lines: -1 },
50
+ tool_config: {},
51
+ },
52
+ };
53
+ }
54
+ const OUTPUT = {
55
+ schema: { type: 'object', properties: { content: { type: 'string' } }, required: ['content'], additionalProperties: false },
56
+ render: (_args, value) => [{ type: 'text', text: value.content }],
57
+ };
58
+ export function skillTool(name, entry, host) {
59
+ const { parameters, session, directory } = modelSchema(entry.input_schema);
60
+ return {
61
+ name,
62
+ description: entry.description,
63
+ parameters,
64
+ output: OUTPUT,
65
+ async execute(args, exec) {
66
+ const input = isRecord(args) ? { ...args } : {};
67
+ const sessionId = exec.agent?.id;
68
+ if (session)
69
+ input[SESSION_FIELD] = `LUASKILLS-SID-${sessionId ?? 'none'}`;
70
+ if (directory)
71
+ input[DIRECTORY_FIELD] = await host.workingDirectory(exec);
72
+ const result = await host.call(entry.canonical_name, input, invocationContext(entry.canonical_name, sessionId, host.dshVersion));
73
+ return { content: result.content };
74
+ },
75
+ };
76
+ }
77
+ /** `luaskill_help`: a skill's main help or one of its topics, as listed in the skill catalog. */
78
+ export function helpTool(name, render) {
79
+ return {
80
+ name,
81
+ description: 'Read the help of one LuaSkills skill: its main guide, or one named topic of it. '
82
+ + 'Call before first using a skill\'s tools when the skill catalog lists topics.',
83
+ parameters: {
84
+ type: 'object',
85
+ properties: {
86
+ skill_id: { type: 'string', description: 'Skill id, as the skill catalog lists it.' },
87
+ flow: { type: 'string', description: 'Topic name; omit for the main guide.' },
88
+ },
89
+ required: ['skill_id'],
90
+ additionalProperties: false,
91
+ },
92
+ output: OUTPUT,
93
+ async execute(args) {
94
+ const input = isRecord(args) ? args : {};
95
+ const skillId = input['skill_id'];
96
+ const flow = input['flow'];
97
+ if (typeof skillId !== 'string' || skillId === '')
98
+ throw new Error('skill_id must be a non-empty string');
99
+ if (flow !== undefined && typeof flow !== 'string')
100
+ throw new Error('flow must be a string');
101
+ return { content: await render(skillId, flow === '' ? undefined : flow) };
102
+ },
103
+ };
104
+ }
105
+ function isRecord(value) {
106
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
107
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The LuaSkills worker process. The SDK calls LuaSkills synchronously through FFI, so it lives
3
+ * here instead of on dsh's event loop, and in its own process so ending it unloads the core
4
+ * library (see `engine.ts`). The SDK is imported from the runtime directory it was installed
5
+ * into, named by the first argument as a `file:` URL; nothing in dsh's dependency tree provides it.
6
+ *
7
+ * Messages in: `{ id, method, params }`. Messages out: `{ id, ok, value | error }` answers and
8
+ * `{ event: 'download', role, received, total }` while runtime archives download.
9
+ */
10
+ export {};
@@ -0,0 +1,140 @@
1
+ /**
2
+ * The LuaSkills worker process. The SDK calls LuaSkills synchronously through FFI, so it lives
3
+ * here instead of on dsh's event loop, and in its own process so ending it unloads the core
4
+ * library (see `engine.ts`). The SDK is imported from the runtime directory it was installed
5
+ * into, named by the first argument as a `file:` URL; nothing in dsh's dependency tree provides it.
6
+ *
7
+ * Messages in: `{ id, method, params }`. Messages out: `{ id, ok, value | error }` answers and
8
+ * `{ event: 'download', role, received, total }` while runtime archives download.
9
+ */
10
+ if (process.send === undefined)
11
+ throw new Error('the LuaSkills worker must be started by the plugin');
12
+ // Without dsh there is nobody to answer; never linger as an orphan.
13
+ process.on('disconnect', () => { process.exit(0); });
14
+ const sdk = await import(process.argv[2]);
15
+ let client;
16
+ let roots = [];
17
+ const post = (message) => { process.send(message); };
18
+ /** Report download bytes of the runtime archives, which the SDK fetches with the global `fetch`. */
19
+ function countDownloads(assets) {
20
+ const original = globalThis.fetch;
21
+ globalThis.fetch = async (input, init) => {
22
+ const response = await original(input, init);
23
+ const url = input instanceof Request ? input.url : String(input);
24
+ const role = assets.get(url);
25
+ if (role === undefined || response.body === null)
26
+ return response;
27
+ const header = Number(response.headers.get('content-length'));
28
+ const total = Number.isSafeInteger(header) && header > 0 ? header : null;
29
+ let received = 0;
30
+ let reported = 0;
31
+ post({ event: 'download', role, received, total });
32
+ const counted = response.body.pipeThrough(new TransformStream({
33
+ transform(chunk, controller) {
34
+ received += chunk.byteLength;
35
+ // About every 256 KiB, so the page moves without flooding the Host.
36
+ if (received - reported >= 262_144) {
37
+ reported = received;
38
+ post({ event: 'download', role, received, total });
39
+ }
40
+ controller.enqueue(chunk);
41
+ },
42
+ flush() {
43
+ post({ event: 'download', role, received, total });
44
+ },
45
+ }));
46
+ return new Response(counted, { status: response.status, statusText: response.statusText, headers: response.headers });
47
+ };
48
+ return () => { globalThis.fetch = original; };
49
+ }
50
+ function engine() {
51
+ if (client === undefined)
52
+ throw new Error('the LuaSkills engine is not started');
53
+ return client;
54
+ }
55
+ function root(name) {
56
+ const found = roots.find(item => item.name === name);
57
+ if (found === undefined)
58
+ throw new Error(`no ${name} skill root`);
59
+ return found;
60
+ }
61
+ const SYSTEM = 'system';
62
+ const handlers = {
63
+ async installRuntime(params) {
64
+ const options = {
65
+ runtimeRoot: params.runtimeRoot,
66
+ database: 'none',
67
+ luaskillsVersion: params.luaskillsVersion,
68
+ luaRuntimeVersion: params.luaRuntimeVersion,
69
+ };
70
+ const plan = sdk.buildRuntimeInstallManifest(options);
71
+ const assets = new Map(plan.assets.map((asset) => [asset.download_url, asset.role]));
72
+ const restore = countDownloads(assets);
73
+ try {
74
+ await sdk.installRuntimeAssets(options);
75
+ }
76
+ finally {
77
+ restore();
78
+ }
79
+ return null;
80
+ },
81
+ start(params) {
82
+ client?.close();
83
+ client = undefined;
84
+ roots = sdk.RuntimeRoots.standard(params.runtimeRoot);
85
+ const created = sdk.LuaSkillsClient.create({ runtimeRoot: params.runtimeRoot, poolConfig: params.poolConfig, hostOptions: params.hostOptions });
86
+ try {
87
+ created.loadFromRoots(roots);
88
+ }
89
+ catch (error) {
90
+ created.close();
91
+ throw error;
92
+ }
93
+ client = created;
94
+ return null;
95
+ },
96
+ entries: () => engine().listEntries(SYSTEM),
97
+ help: () => engine().listSkillHelp(SYSTEM),
98
+ helpDetail: (params) => engine().renderSkillHelpDetail(params.skillId, params.flow, { authority: SYSTEM }),
99
+ call: (params) => engine().callSkill(params.name, params.args, params.context),
100
+ install(params) {
101
+ const result = engine().system(SYSTEM).install(roots, params.request, { targetRoot: root(params.root), authority: SYSTEM });
102
+ engine().reloadFromRoots(roots);
103
+ return result;
104
+ },
105
+ update(params) {
106
+ const result = engine().system(SYSTEM).update(roots, params.request, { targetRoot: root(params.root), authority: SYSTEM });
107
+ engine().reloadFromRoots(roots);
108
+ return result;
109
+ },
110
+ uninstall(params) {
111
+ const result = engine().system(SYSTEM).uninstall(roots, params.skillId, {}, { targetRoot: root(params.root), authority: SYSTEM });
112
+ engine().reloadFromRoots(roots);
113
+ return result;
114
+ },
115
+ setEnabled(params) {
116
+ const system = engine().system(SYSTEM);
117
+ return params.enabled ? system.enable(roots, params.skillId) : system.disable(roots, params.skillId, 'disabled in dsh');
118
+ },
119
+ stop() {
120
+ client?.close();
121
+ client = undefined;
122
+ return null;
123
+ },
124
+ };
125
+ process.on('message', (request) => {
126
+ void (async () => {
127
+ try {
128
+ const handler = handlers[request.method];
129
+ if (handler === undefined)
130
+ throw new Error(`unknown worker method ${request.method}`);
131
+ const value = await handler(request.params);
132
+ post({ id: request.id, ok: true, value: value ?? null });
133
+ }
134
+ catch (error) {
135
+ post({ id: request.id, ok: false, error: error instanceof Error ? error.message : String(error) });
136
+ }
137
+ })();
138
+ });
139
+ post({ event: 'ready' });
140
+ export {};
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@openvulcan/dsh-vulcan-luaskills",
3
+ "version": "0.4.3",
4
+ "description": "LuaSkills for DeepSeek Harness: skill tools for the model, the codekit default skill, VM settings and the LuaSkills Hub. The runtime is installed on demand.",
5
+ "license": "MIT",
6
+ "icon": "./assets/luaskills-icon.svg",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/OpenVulcan/dsh-vulcancode.git",
10
+ "directory": "packages/dsh-vulcan-luaskills"
11
+ },
12
+ "type": "module",
13
+ "main": "lib/index.js",
14
+ "types": "lib/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./lib/index.d.ts",
18
+ "default": "./lib/index.js"
19
+ },
20
+ "./client": "./lib/client.js",
21
+ "./package.json": "./package.json"
22
+ },
23
+ "files": [
24
+ "lib",
25
+ "!lib/**/*.test.*",
26
+ "assets"
27
+ ],
28
+ "engines": {
29
+ "dsh": ">=0.2.1-rc.2 <0.3.0"
30
+ },
31
+ "dsh": {
32
+ "client": {
33
+ "platform": "web",
34
+ "inject": [
35
+ "@deepseek-ai/dsh-client-locale",
36
+ "@deepseek-ai/dsh-client-ui-layout",
37
+ "@deepseek-ai/dsh-client-ui-settings",
38
+ "@deepseek-ai/dsh-client-ui-settings-general",
39
+ "@deepseek-ai/dsh-client-ui-sidebar"
40
+ ]
41
+ }
42
+ },
43
+ "peerDependencies": {
44
+ "@deepseek-ai/cordis": "^4.0.5",
45
+ "@deepseek-ai/dsh-agent": "^0.2.1-rc.2",
46
+ "@deepseek-ai/dsh-app-boot": "^0.2.1-rc.2",
47
+ "@deepseek-ai/dsh-client-connection": "^0.2.1-rc.2",
48
+ "@deepseek-ai/dsh-home-paths": "^0.2.1-rc.2",
49
+ "@deepseek-ai/dsh-sandbox-policy": "^0.2.1-rc.2",
50
+ "@deepseek-ai/dsh-skill": "^0.2.1-rc.2",
51
+ "@deepseek-ai/dsh-tools": "^0.2.1-rc.2",
52
+ "@deepseek-ai/dsh-working-directory": "^0.2.1-rc.2"
53
+ },
54
+ "publishConfig": {
55
+ "access": "public"
56
+ }
57
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenVulcan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ You keep long-term memory with `memory_outline`, `memory_search` and `memory_write`. It outlives this conversation and is shared by every conversation of the user; `user` and `space` memory also reach every project.
2
+
3
+ When to write, during the work and without asking first:
4
+ - The user asks you to remember something.
5
+ - The user corrects your approach or states a preference.
6
+ - A decision, constraint or pitfall emerges that the repository itself does not show.
7
+ Do not record what code, git history or docs already state, one-off task details, or secrets.
8
+
9
+ Where it belongs:
10
+ 1. Would it still hold in another project? No: `project`.
11
+ 2. Yes: about the person (role, expertise, language, how they like to work with you): `user`; about how to work (rules, tools, environment): `space`.
12
+ - The `base` sections of space and project are injected in full into every conversation. Keep them to a few critical, stable rules that would cause mistakes if unknown. Everything else goes into other sections, which are injected as titles only.
13
+ - Work in progress belongs in the project `in-progress` section; delete it when the work is done.
14
+
15
+ How to write:
16
+ - Search first. Update or append to an existing node instead of adding a near duplicate, and merge duplicates that a receipt reports.
17
+ - Make each title state the fact itself ("Verify with targeted tests only"), not a topic ("Testing"), so catalog lines carry meaning.
18
+ - One fact per node; keep bodies short and put details in child nodes (down to H4).
19
+ - Correct or delete nodes that turn out to be wrong or outdated.
20
+
21
+ Memory can be stale. Before relying on a remembered file, function, flag or command, check that it still exists. The user can also edit these files directly; their edits are authoritative.
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: memory-curator
3
+ description: Use when asked to tidy, clean up, reorganize or review long-term memory, when a memory_write receipt warns that a base section is nearly full or over budget, or when memory_outline shows duplicates, vague titles or misplaced nodes. Requires the memory_outline, memory_search and memory_write tools (Markdown memory mode).
4
+ ---
5
+
6
+ # Memory Curator
7
+
8
+ Long-term memory is three Markdown files of heading nodes: `user` (who the user is), `space` (rules and environment for every project) and `project` (this project only). The `base` sections of space and project are injected in full into every conversation; every other section is injected as titles only. Curation keeps memory accurate, small and easy to scan.
9
+
10
+ ## Workflow
11
+
12
+ 1. Run `memory_outline` for all scopes. Note the budget lines from the last write receipt if you have one.
13
+ 2. Read candidate nodes with `memory_search` (by `ids`, or by patterns for suspected duplicates) before changing anything. Every change needs the node's current `expectedRevision`.
14
+ 3. Plan the changes, then apply them with as few `memory_write` calls as practical; one call applies up to 16 operations all-or-nothing.
15
+ 4. Report what changed: merged, moved, renamed, deleted, and anything you left for the user to decide.
16
+
17
+ ## What to fix
18
+
19
+ - **Duplicates.** Merge nodes that state the same fact: keep the clearer node, `append` or `replace` the missing details into it, then `delete` the other. Check `space` against `project` too.
20
+ - **Stale facts.** For project nodes that name files, functions, flags or commands, verify them with the workspace tools. Correct or delete what no longer holds. When unsure, keep the node and say so in the report.
21
+ - **Wrong scope.** Ask: would it still hold in another project? No: `project`. Yes: about the person: `user`; about how to work: `space`. `move` keeps the node id.
22
+ - **Heavy base.** Base sections should hold only a few critical, stable rules whose absence causes mistakes. Move the rest to another section (`move` with `section`), most specific first. Finished work in `in-progress` should be deleted or turned into a lasting fact elsewhere.
23
+ - **Vague titles.** Rename topic titles ("Testing") to statements of the fact ("Verify with targeted tests only") so catalog lines carry meaning. Titles stay single-line plain text.
24
+ - **Oversized nodes.** Split a node that mixes several facts into one node per fact, using child nodes for details (down to H4).
25
+
26
+ ## Limits
27
+
28
+ - Never delete a node only because it is old; age alone does not make a fact wrong.
29
+ - Never rewrite text the user wrote by hand (`source: user`) beyond moving it, unless the user asks; suggest changes instead.
30
+ - Do not invent facts while merging. Keep the user's wording where possible.
31
+ - The `base` section itself cannot be moved or deleted; change the nodes inside it.