dirac-lang 0.1.24 → 0.1.26

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 (221) hide show
  1. package/dist/cli.js +13 -1
  2. package/lib/index.di +9 -0
  3. package/{examples/lib → lib}/math.di +3 -3
  4. package/package.json +13 -1
  5. package/.env.example +0 -8
  6. package/COMMUNITY.md +0 -465
  7. package/CONDITIONAL-TAGS.md +0 -172
  8. package/EXCEPTION-HANDLING.md +0 -156
  9. package/LIBRARIES.md +0 -172
  10. package/LLM-VALIDATION.md +0 -128
  11. package/NAMESPACES.md +0 -366
  12. package/PROMOTION.md +0 -257
  13. package/QUICKSTART-LIBRARY.md +0 -93
  14. package/TEST-COVERAGE.md +0 -113
  15. package/TESTING.md +0 -162
  16. package/config.test.yml +0 -5
  17. package/dirac-http/examples/demo.di +0 -9
  18. package/dirac-http/lib/index.di +0 -12
  19. package/examples/add-demo.di +0 -74
  20. package/examples/add.bk +0 -11
  21. package/examples/advanced-math-demo.di +0 -53
  22. package/examples/calculator.di +0 -32
  23. package/examples/compact-test.di +0 -6
  24. package/examples/comprehensive.bk +0 -29
  25. package/examples/defvar-variable-demo.di +0 -18
  26. package/examples/direct-call.di +0 -17
  27. package/examples/disk-analysis.di +0 -16
  28. package/examples/exception-demo.di +0 -82
  29. package/examples/executable-hello.di +0 -7
  30. package/examples/execute-demo.di +0 -38
  31. package/examples/file-manager.di +0 -77
  32. package/examples/file-stats.di +0 -18
  33. package/examples/hello.bk +0 -1
  34. package/examples/hello.di +0 -5
  35. package/examples/if-comparison.di +0 -91
  36. package/examples/if-cstyle-test.di +0 -149
  37. package/examples/if-vs-testif.di +0 -56
  38. package/examples/import-demo.di +0 -31
  39. package/examples/inline-test.bk +0 -7
  40. package/examples/llm-agent.di +0 -32
  41. package/examples/llm-basic.di +0 -12
  42. package/examples/llm-command-more.di +0 -6
  43. package/examples/llm-command-no-exec.di +0 -13
  44. package/examples/llm-command.di +0 -6
  45. package/examples/llm-complex.di +0 -141
  46. package/examples/llm-feedback-debug.di +0 -30
  47. package/examples/llm-feedback-demo.di +0 -19
  48. package/examples/llm-feedback-math.di +0 -22
  49. package/examples/llm-feedback-simple.di +0 -16
  50. package/examples/llm-feedback-sub.di +0 -22
  51. package/examples/llm-no-feedback.di +0 -10
  52. package/examples/llm-recursive.di +0 -31
  53. package/examples/llm-reflection-test.di +0 -19
  54. package/examples/llm-simple-test.di +0 -12
  55. package/examples/llm-subs.di +0 -132
  56. package/examples/llm-use-subs.di +0 -6
  57. package/examples/llm-validate-test.di +0 -18
  58. package/examples/loop.di +0 -12
  59. package/examples/math-test.di +0 -22
  60. package/examples/minimal-test.di +0 -13
  61. package/examples/mongodb-context-test.di +0 -27
  62. package/examples/mongodb-count-events.di +0 -8
  63. package/examples/mongodb-import-demo.di +0 -25
  64. package/examples/mongodb-simple-test.di +0 -18
  65. package/examples/nl-agent.di +0 -47
  66. package/examples/parameters-demo.di +0 -68
  67. package/examples/params-meta-test.di +0 -17
  68. package/examples/params-test.di +0 -10
  69. package/examples/recipe-chain.di +0 -38
  70. package/examples/recursive-llm.di +0 -44
  71. package/examples/sample-library/README.md +0 -152
  72. package/examples/sample-library/examples/demo.di +0 -34
  73. package/examples/sample-library/lib/index.di +0 -65
  74. package/examples/sample-library/package.json +0 -31
  75. package/examples/scope-test-nested.di +0 -60
  76. package/examples/scope-test.di +0 -55
  77. package/examples/seamless.di +0 -45
  78. package/examples/shell-test.bk +0 -10
  79. package/examples/simple-import.di +0 -13
  80. package/examples/simple-recursive.di +0 -26
  81. package/examples/story-builder.di +0 -45
  82. package/examples/subroutine.di +0 -23
  83. package/examples/system-llm.di +0 -21
  84. package/examples/system-simple.di +0 -3
  85. package/examples/system-test.di +0 -8
  86. package/examples/tag-check-test.di +0 -139
  87. package/examples/task-assistant.di +0 -27
  88. package/examples/test-if-demo.di +0 -110
  89. package/examples/test-parameters.di +0 -50
  90. package/examples/try-catch-test.di +0 -118
  91. package/examples/two-styles.di +0 -28
  92. package/examples/var-debug.di +0 -6
  93. package/examples/var-inline.di +0 -4
  94. package/examples/var-test2.di +0 -6
  95. package/examples/variable-replace.di +0 -25
  96. package/examples/variable-simple.di +0 -16
  97. package/examples/variable-test.di +0 -22
  98. package/examples/whitespace-test.di +0 -24
  99. package/filePath +0 -1
  100. package/greeting.txt +0 -1
  101. package/src/cli.ts +0 -140
  102. package/src/index.ts +0 -33
  103. package/src/llm/ollama.ts +0 -58
  104. package/src/runtime/braket-parser.ts +0 -234
  105. package/src/runtime/interpreter.ts +0 -203
  106. package/src/runtime/parser.ts +0 -155
  107. package/src/runtime/session.ts +0 -325
  108. package/src/tags/assign.ts +0 -37
  109. package/src/tags/attr.ts +0 -64
  110. package/src/tags/available-subroutines.ts +0 -70
  111. package/src/tags/call.ts +0 -259
  112. package/src/tags/catch.ts +0 -24
  113. package/src/tags/defvar.ts +0 -115
  114. package/src/tags/environment.ts +0 -21
  115. package/src/tags/eval.ts +0 -71
  116. package/src/tags/exception.ts +0 -20
  117. package/src/tags/execute.ts +0 -52
  118. package/src/tags/expr.ts +0 -128
  119. package/src/tags/foreach.ts +0 -170
  120. package/src/tags/if.ts +0 -191
  121. package/src/tags/import.ts +0 -135
  122. package/src/tags/index.ts +0 -41
  123. package/src/tags/input.ts +0 -182
  124. package/src/tags/llm.ts +0 -415
  125. package/src/tags/loop.ts +0 -43
  126. package/src/tags/mongodb.ts +0 -70
  127. package/src/tags/output.ts +0 -53
  128. package/src/tags/parameters.ts +0 -81
  129. package/src/tags/require_module.ts +0 -19
  130. package/src/tags/subroutine.ts +0 -75
  131. package/src/tags/system.ts +0 -93
  132. package/src/tags/tag-check.ts +0 -176
  133. package/src/tags/test-if.ts +0 -112
  134. package/src/tags/throw.ts +0 -26
  135. package/src/tags/try.ts +0 -19
  136. package/src/tags/variable.ts +0 -25
  137. package/src/test-runner.ts +0 -300
  138. package/src/types/index.ts +0 -128
  139. package/src/utils/llm-adapter.ts +0 -113
  140. package/src/utils/tag-validator.ts +0 -231
  141. package/test-available-extends.di +0 -28
  142. package/test-available-simple.di +0 -12
  143. package/test-available-subroutines.di +0 -16
  144. package/test-call.di +0 -9
  145. package/test-extend-basic.di +0 -17
  146. package/test-extend-chain.di +0 -26
  147. package/test-extend-debug.di +0 -17
  148. package/test-extend-debug2.di +0 -15
  149. package/test-extend-person.di +0 -17
  150. package/test-extend-simple.di +0 -11
  151. package/test-extend-zhi.di +0 -23
  152. package/test-extend.di +0 -19
  153. package/test-extend2.di +0 -26
  154. package/test-extend3-fixed.di +0 -23
  155. package/test-extend3-trouble.di +0 -23
  156. package/test-extend3.di +0 -21
  157. package/test-factorial.di +0 -19
  158. package/test-input-debug.di +0 -19
  159. package/test-nested-debug.di +0 -7
  160. package/test-nested-debug2.di +0 -8
  161. package/test-no-extend.di +0 -16
  162. package/test-simple-call.di +0 -7
  163. package/test-simple.di +0 -6
  164. package/tests/README.md +0 -69
  165. package/tests/assign-basic.test.di +0 -7
  166. package/tests/assign-from-eval.test.di +0 -7
  167. package/tests/available-subroutines-foreach.test.di +0 -32
  168. package/tests/basic-output.test.di +0 -5
  169. package/tests/call-basic.test.di +0 -11
  170. package/tests/call-with-output.test.di +0 -10
  171. package/tests/comments-before-content.test.di +0 -6
  172. package/tests/defvar-multiple.test.di +0 -8
  173. package/tests/environment-basic.test.di +0 -22
  174. package/tests/environment-nonexistent.test.di +0 -5
  175. package/tests/environment-with-defvar.test.di +0 -15
  176. package/tests/eval-basic.test.di +0 -6
  177. package/tests/eval-with-variables.test.di +0 -8
  178. package/tests/exception-basic.test.di +0 -10
  179. package/tests/expr-basic.test.di +0 -11
  180. package/tests/extend-basic.test.di +0 -19
  181. package/tests/extend-inheritance-chain.test.di +0 -26
  182. package/tests/extend-multiple-nested.test.di +0 -24
  183. package/tests/extend-self.test.di +0 -19
  184. package/tests/extend-with-parameters.test.di +0 -14
  185. package/tests/generate-dirac.test.di +0 -22
  186. package/tests/if-conditional.test.di +0 -17
  187. package/tests/if-else.test.di +0 -11
  188. package/tests/if-with-variables.test.di +0 -8
  189. package/tests/import-basic.test.di +0 -6
  190. package/tests/input-file-all.test.di +0 -5
  191. package/tests/input-file-line.test.di +0 -15
  192. package/tests/input-file-loop.test.di +0 -12
  193. package/tests/input-stdin-all.test.di +0 -8
  194. package/tests/input-stdin-all.txt +0 -1
  195. package/tests/llm-validate.test.di +0 -17
  196. package/tests/loop-basic.test.di +0 -9
  197. package/tests/loop-nested.test.di +0 -10
  198. package/tests/loop-with-eval.test.di +0 -8
  199. package/tests/no-root-element.test.di +0 -6
  200. package/tests/output-file.test.di +0 -19
  201. package/tests/output-multiple.test.di +0 -9
  202. package/tests/parameters-basic.test.di +0 -6
  203. package/tests/require-module-basic.test.di +0 -7
  204. package/tests/subroutine-basic.test.di +0 -9
  205. package/tests/subroutine-multiple-params.test.di +0 -10
  206. package/tests/subroutine-nested-calls.test.di +0 -32
  207. package/tests/subroutine-sequential-calls.test.di +0 -36
  208. package/tests/system-background.test.di +0 -39
  209. package/tests/system-basic.test.di +0 -5
  210. package/tests/tag-check.test.di +0 -27
  211. package/tests/test-if-basic.test.di +0 -8
  212. package/tests/test-input.txt +0 -3
  213. package/tests/try-catch-basic.test.di +0 -10
  214. package/tests/try-catch-eval-error.test.di +0 -10
  215. package/tests/variable-basic.test.di +0 -6
  216. package/tests/variable-interpolation.test.di +0 -7
  217. package/tools/create-library.sh +0 -175
  218. package/tsconfig.json +0 -19
  219. /package/{examples/lib → lib}/advanced-math.di +0 -0
  220. /package/{examples/lib → lib}/fileops.di +0 -0
  221. /package/{examples/lib → lib}/mongodb.di +0 -0
@@ -1,70 +0,0 @@
1
- import { TagHandler, TagHandlerContext } from '../types';
2
-
3
- console.log('[mongodb tag] mongodb.ts loaded');
4
- import { MongoClient, Db, Collection } from 'mongodb';
5
-
6
- // Utility to expand variables/child tags as Dirac does
7
- async function expandContent(ctx: TagHandlerContext): Promise<any> {
8
- if (ctx.body) {
9
- // If body is present, expand it (could be JSON, array, or object)
10
- const expanded = await ctx.evaluate(ctx.body);
11
- try {
12
- return JSON.parse(expanded);
13
- } catch {
14
- return expanded;
15
- }
16
- }
17
- return undefined;
18
- }
19
-
20
- export const mongodb: TagHandler = async (ctx: TagHandlerContext) => {
21
- const uri = process.env.MONGODB_URI || 'mongodb://localhost:27017';
22
- const database = ctx.getAttr('database');
23
- const collection = ctx.getAttr('collection');
24
- const action = ctx.getAttr('action');
25
- const client = new MongoClient(uri);
26
- let result;
27
- console.log('[mongodb tag] Connecting to:', uri);
28
- console.log('[mongodb tag] Database:', database, 'Collection:', collection, 'Action:', action);
29
- try {
30
- await client.connect();
31
- let db: Db | undefined = database ? client.db(database) : undefined;
32
- let col: Collection | undefined = db && collection ? db.collection(collection) : undefined;
33
- const content = await expandContent(ctx);
34
- console.log('[mongodb tag] Query content:', content);
35
- switch (action) {
36
- case 'find':
37
- if (!col) throw new Error('Collection required for find');
38
- result = await col.find(content || {}).toArray();
39
- console.log('[mongodb tag] Find result:', result);
40
- break;
41
- case 'aggregate':
42
- if (!col) throw new Error('Collection required for aggregate');
43
- result = await col.aggregate(content).toArray();
44
- console.log('[mongodb tag] Aggregate result:', result);
45
- break;
46
- case 'insert':
47
- if (!col) throw new Error('Collection required for insert');
48
- result = await col.insertOne(content);
49
- console.log('[mongodb tag] Insert result:', result);
50
- break;
51
- case 'createDb':
52
- if (!db) throw new Error('Database required for createDb');
53
- result = { db: database, created: true };
54
- console.log('[mongodb tag] CreateDb result:', result);
55
- break;
56
- case 'createCollection':
57
- if (!db || !collection) throw new Error('Database and collection required');
58
- result = await db.createCollection(collection);
59
- console.log('[mongodb tag] CreateCollection result:', result);
60
- break;
61
- default:
62
- throw new Error('Unknown action for <mongodb>');
63
- }
64
- } finally {
65
- await client.close();
66
- }
67
- return result;
68
- };
69
-
70
- export default mongodb;
@@ -1,53 +0,0 @@
1
- /**
2
- * <output> tag - emit content
3
- * Maps to mask_tag_output in MASK
4
- */
5
-
6
- import type { DiracSession, DiracElement } from '../types/index.js';
7
- import { emit, substituteVariables, substituteAttribute } from '../runtime/session.js';
8
- import { integrateChildren } from '../runtime/interpreter.js';
9
- import * as fs from 'fs';
10
- import * as path from 'path';
11
-
12
- export async function executeOutput(session: DiracSession, element: DiracElement): Promise<void> {
13
- const fileAttr = element.attributes?.file;
14
- const filePath = fileAttr ? substituteAttribute(session, fileAttr) : null;
15
-
16
- // If writing to a file, collect content first
17
- if (filePath) {
18
- let content = '';
19
-
20
- if (element.children && element.children.length > 0) {
21
- // Capture output from children
22
- const prevOutput = session.output;
23
- session.output = [];
24
- await integrateChildren(session, element);
25
- content = session.output.join('');
26
- session.output = prevOutput;
27
- } else if (element.text) {
28
- content = substituteVariables(session, element.text);
29
- }
30
-
31
- // Ensure directory exists
32
- const dir = path.dirname(filePath);
33
- if (dir !== '.' && !fs.existsSync(dir)) {
34
- fs.mkdirSync(dir, { recursive: true });
35
- }
36
-
37
- // Append to file
38
- fs.appendFileSync(filePath, content + '\n', 'utf8');
39
- return;
40
- }
41
-
42
- // Normal output to stdout
43
- if (element.children && element.children.length > 0) {
44
- await integrateChildren(session, element);
45
- return;
46
- }
47
-
48
- if (element.text) {
49
- const content = substituteVariables(session, element.text);
50
- emit(session, content);
51
- return;
52
- }
53
- }
@@ -1,81 +0,0 @@
1
- /**
2
- * <parameters> tag - Access parameters passed to subroutine
3
- * Maps to MASK parameter selection syntax
4
- */
5
-
6
- import type { DiracSession, DiracElement } from '../types/index.js';
7
- import { getCurrentParameters, emit, setVariable } from '../runtime/session.js';
8
- import { integrate } from '../runtime/interpreter.js';
9
-
10
- export async function executeParameters(session: DiracSession, element: DiracElement): Promise<string | void> {
11
- const select = element.attributes.select;
12
-
13
- if (!select) {
14
- throw new Error('<parameters> requires select attribute');
15
- }
16
-
17
- // Get parameters from current call context
18
- const params = getCurrentParameters(session);
19
-
20
- if (!params || params.length === 0) {
21
- if (session.debug) {
22
- console.error(`[PARAMETERS] No parameters available`);
23
- }
24
- return;
25
- }
26
-
27
- // The caller element is params[0] (the calling tag itself)
28
- const caller = params[0];
29
-
30
- if (select === '*') {
31
- // Select all child elements - execute them and RETURN output
32
- if (session.debug) {
33
- console.error(`[PARAMETERS] Selecting all children (${caller.children.length} elements)`);
34
- }
35
- // Save current output buffer
36
- const prevOutput = session.output;
37
- session.output = [];
38
- for (const child of caller.children) {
39
- await integrate(session, child);
40
- }
41
- const captured = session.output.join('');
42
- session.output = prevOutput;
43
- return captured;
44
- } else if (select.startsWith('@')) {
45
- // Select attribute(s)
46
- const attrName = select.slice(1); // Remove '@'
47
-
48
- if (attrName === '*') {
49
- // Select all attributes
50
- if (session.debug) {
51
- console.error(`[PARAMETERS] Selecting all attributes`);
52
- }
53
-
54
- const attrs = Object.entries(caller.attributes)
55
- .map(([key, val]) => `${key}="${val}"`)
56
- .join(' ');
57
- emit(session, attrs);
58
-
59
- } else {
60
- // Select specific attribute - automatically create variable with that name
61
- const value = caller.attributes[attrName];
62
-
63
- if (session.debug) {
64
- console.error(`[PARAMETERS] Setting variable '${attrName}' = '${value}'`);
65
- }
66
- if (value !== undefined) {
67
- // Only set variable if not already set in current boundary
68
- const alreadySet = session.variables.slice(session.varBoundary).some(v => v.name === attrName);
69
- if (!alreadySet) {
70
- setVariable(session, attrName, value, false);
71
- }
72
- }
73
- for (const child of element.children) {
74
- await integrate(session, child);
75
- }
76
- }
77
-
78
- } else {
79
- throw new Error(`<parameters> invalid select: ${select}`);
80
- }
81
- }
@@ -1,19 +0,0 @@
1
- import type { DiracSession, DiracElement } from '../types/index.js';
2
- import { setVariable } from '../runtime/session.js';
3
-
4
- /**
5
- * <require_module name="mongodb" var="mongo" />
6
- * Loads a Node.js module and stores it in a session variable.
7
- */
8
- export async function executeRequireModule(session: DiracSession, element: DiracElement): Promise<void> {
9
- const name = element.attributes.name;
10
- const varName = element.attributes.var || name;
11
- if (!name) throw new Error('<require_module> missing name attribute');
12
- try {
13
- // Dynamically import the module (ESM compatible)
14
- const mod = await import(name);
15
- setVariable(session, varName, mod, true); // visible: true
16
- } catch (err) {
17
- throw new Error(`<require_module> failed to load module '${name}': ${err}`);
18
- }
19
- }
@@ -1,75 +0,0 @@
1
- /**
2
- * <subroutine> tag - define reusable code block
3
- * Maps to mask_tag_subroutine in MASK
4
- */
5
-
6
- import type { DiracSession, DiracElement, ParameterMetadata } from '../types/index.js';
7
- import { registerSubroutine } from '../runtime/session.js';
8
-
9
- export function executeSubroutine(session: DiracSession, element: DiracElement): void {
10
- // Skip registration if we're in extend mode (nested subroutines already registered)
11
- if (session.skipSubroutineRegistration) {
12
- return;
13
- }
14
-
15
- const name = element.attributes.name;
16
-
17
- if (!name) {
18
- throw new Error('<subroutine> requires name attribute');
19
- }
20
-
21
- // Extract metadata from attributes (no structural changes!)
22
- const description = element.attributes.description;
23
- const parameters: ParameterMetadata[] = [];
24
- const meta: Record<string, any> = {};
25
-
26
- // Parse param- prefixed attributes for metadata
27
- function parseMetaField(metaString: string) {
28
- const parts = metaString.split(':');
29
- return {
30
- type: parts[0] || 'string',
31
- required: parts[1] === 'required',
32
- description: parts[2] || undefined,
33
- example: parts[3] || undefined
34
- };
35
- }
36
- for (const [attrName, attrValue] of Object.entries(element.attributes)) {
37
- if (attrName.startsWith('param-')) {
38
- const paramName = attrName.substring(6);
39
- const parts = attrValue.split(':');
40
- const paramMeta: ParameterMetadata = {
41
- name: paramName,
42
- type: parts[0] || 'string',
43
- required: parts[1] === 'required',
44
- description: parts[2] || undefined,
45
- };
46
- if (parts.length > 3 && parts[3]) {
47
- paramMeta.enum = parts[3].split('|');
48
- }
49
- if (parts.length > 4 && parts[4]) {
50
- paramMeta.example = parts[4];
51
- }
52
- parameters.push(paramMeta);
53
- } else if (attrName.startsWith('meta-')) {
54
- const metaName = attrName.substring(5);
55
- meta[metaName] = parseMetaField(attrValue);
56
- }
57
- }
58
-
59
- // Store subroutine exactly as before (preserves nesting and extends)
60
- const subroutine: DiracElement = {
61
- tag: 'subroutine',
62
- attributes: { ...element.attributes },
63
- children: element.children,
64
- };
65
-
66
- // Pass meta as a field in the subroutine registry, not on the element
67
- registerSubroutine(
68
- session,
69
- name,
70
- subroutine,
71
- description,
72
- parameters.length > 0 ? parameters : undefined,
73
- Object.keys(meta).length > 0 ? meta : undefined
74
- );
75
- }
@@ -1,93 +0,0 @@
1
- /**
2
- * <system> tag - Execute shell commands
3
- * Spawns a Unix process and runs the command
4
- */
5
-
6
- import type { DiracSession, DiracElement } from '../types/index.js';
7
- import { substituteVariables, emit } from '../runtime/session.js';
8
- import { exec, spawn } from 'child_process';
9
- import { promisify } from 'util';
10
- import { integrate } from '../runtime/interpreter.js';
11
-
12
- const execAsync = promisify(exec);
13
-
14
- export async function executeSystem(session: DiracSession, element: DiracElement): Promise<void> {
15
- // Build command from text content or children
16
- let command: string;
17
-
18
- // Check if has non-text children (actual element children)
19
- const hasElementChildren = element.children.some(child => child.tag !== '');
20
-
21
- if (hasElementChildren) {
22
- // Execute children to build command dynamically
23
- const beforeOutput = session.output.length;
24
-
25
- for (const child of element.children) {
26
- await integrate(session, child);
27
- }
28
-
29
- // Collect output from children as the command
30
- const childOutput = session.output.slice(beforeOutput);
31
- command = childOutput.join('');
32
-
33
- // Remove child output from main output
34
- session.output = session.output.slice(0, beforeOutput);
35
- } else if (element.text) {
36
- // Simple text command with variable substitution
37
- command = substituteVariables(session, element.text);
38
- } else {
39
- throw new Error('<system> requires command content');
40
- }
41
-
42
- if (!command.trim()) {
43
- return;
44
- }
45
-
46
- // Check for background attribute
47
- const backgroundAttr = element.attributes?.background;
48
- const isBackground = backgroundAttr === 'true';
49
-
50
- if (session.debug) {
51
- console.error(`[SYSTEM] Executing${isBackground ? ' (background)' : ''}: ${command}`);
52
- }
53
-
54
- // Background mode - spawn and don't wait
55
- if (isBackground) {
56
- const child = spawn(command, {
57
- detached: true,
58
- stdio: 'ignore',
59
- shell: true,
60
- });
61
-
62
- // Unref so parent can exit without waiting
63
- child.unref();
64
-
65
- if (session.debug) {
66
- console.error(`[SYSTEM] Background process started with PID: ${child.pid}`);
67
- }
68
-
69
- return;
70
- }
71
-
72
- // Foreground mode - wait for completion (original behavior)
73
- try {
74
- const { stdout, stderr } = await execAsync(command, {
75
- encoding: 'utf-8',
76
- maxBuffer: 10 * 1024 * 1024, // 10MB buffer
77
- });
78
-
79
- // Emit stdout
80
- if (stdout) {
81
- emit(session, stdout);
82
- }
83
-
84
- // Emit stderr if debug mode
85
- if (stderr && session.debug) {
86
- console.error(`[SYSTEM STDERR] ${stderr}`);
87
- }
88
-
89
- } catch (error: any) {
90
- const errorMsg = error.message || String(error);
91
- throw new Error(`System command failed: ${errorMsg}`);
92
- }
93
- }
@@ -1,176 +0,0 @@
1
- /**
2
- * <tag-check> tag - validates child tags against allowed subroutines
3
- * If a tag is not found, uses embedding similarity to suggest the closest match.
4
- */
5
-
6
- import type { DiracSession, DiracElement } from '../types/index.js';
7
- import { emit } from '../runtime/session.js';
8
- // Use global fetch (Node.js 18+)
9
- import fs from 'fs';
10
- import yaml from 'js-yaml';
11
-
12
- // Configurable similarity cutoff
13
- const SIMILARITY_CUTOFF = 0.75;
14
-
15
-
16
- // Helper: get embedding server config from config.yml
17
- function getEmbeddingServerConfig() {
18
- try {
19
- const configPath = process.env.DIRAC_CONFIG || 'config.yml';
20
- const config = yaml.load(fs.readFileSync(configPath, 'utf8')) as any;
21
- const host = config.embeddingServer?.host || 'localhost';
22
- const port = config.embeddingServer?.port || 11434;
23
- const model = config.embeddingServer?.model || 'embeddinggemma';
24
- return { host, port, model };
25
- } catch (e) {
26
- // Fallback to defaults if config file not found
27
- return { host: 'localhost', port: 11434, model: 'embeddinggemma' };
28
- }
29
- }
30
-
31
- // Helper: call Ollama embedding API directly
32
- async function getEmbeddings(tags: string[]): Promise<number[][]> {
33
- const { host, port, model } = getEmbeddingServerConfig();
34
- return await Promise.all(tags.map(async tag => {
35
- const response = await fetch(`http://${host}:${port}/api/embeddings`, {
36
- method: 'POST',
37
- headers: { 'Content-Type': 'application/json' },
38
- body: JSON.stringify({ model, prompt: tag })
39
- });
40
- const data = await response.json();
41
- return data.embedding;
42
- }));
43
- }
44
-
45
- function cosine(a: number[], b: number[]): number {
46
- const dot = a.reduce((sum, v, i) => sum + v * b[i], 0);
47
- const normA = Math.sqrt(a.reduce((sum, v) => sum + v * v, 0));
48
- const normB = Math.sqrt(b.reduce((sum, v) => sum + v * v, 0));
49
- return dot / (normA * normB);
50
- }
51
-
52
- async function getBestTagMatch(candidate: string, allowed: string[]): Promise<{tag: string, score: number}> {
53
- const tags = [candidate, ...allowed];
54
- const embeddings = await getEmbeddings(tags);
55
- const candidateVec = embeddings[0];
56
- const allowedVecs = embeddings.slice(1);
57
- let bestIdx = 0, bestScore = -1;
58
- allowedVecs.forEach((vec, i) => {
59
- const score = cosine(candidateVec, vec);
60
- if (score > bestScore) { bestScore = score; bestIdx = i; }
61
- });
62
- return { tag: allowed[bestIdx], score: bestScore };
63
- }
64
-
65
- export async function executeTagCheck(session: DiracSession, element: DiracElement): Promise<void> {
66
- // Get allowed subroutine names
67
- const { getAvailableSubroutines } = await import('../runtime/session.js');
68
- const subroutines = getAvailableSubroutines(session);
69
- const allowed = new Set(subroutines.map(s => s.name));
70
- console.error('[tag-check] Allowed subroutines:', Array.from(allowed));
71
-
72
- const autocorrect = element.attributes?.autocorrect === "true";
73
- const shouldExecute = element.attributes?.execute === "true";
74
-
75
- for (const child of element.children) {
76
- const tagName = child.tag;
77
-
78
- // Skip text nodes (empty tag names) and whitespace
79
- if (!tagName || tagName.trim() === '') {
80
- continue;
81
- }
82
-
83
- console.error(`[tag-check] Checking tag: <${tagName}>`);
84
- let allValid = false;
85
- let correctedTag: string | null = null;
86
-
87
- if (allowed.has(tagName)) {
88
- // Tag name is valid, now check required and unknown parameters
89
- const sub = subroutines.find(s => s.name === tagName);
90
- let missing: string[] = [];
91
- let unknown: string[] = [];
92
- let paramNames: string[] = [];
93
- if (sub && Array.isArray(sub.parameters)) {
94
- paramNames = sub.parameters.map(p => p.name);
95
- for (const param of sub.parameters) {
96
- if (param.required && !(param.name in child.attributes)) {
97
- missing.push(param.name);
98
- }
99
- }
100
- for (const attr in child.attributes) {
101
- if (!paramNames.includes(attr)) {
102
- unknown.push(attr);
103
- }
104
- }
105
- }
106
- if (missing.length > 0) {
107
- emit(session, `<${tagName}/> is missing required parameter(s): ${missing.join(', ')}`);
108
- console.error(`[tag-check] <${tagName}/> missing required: ${missing.join(', ')}`);
109
- }
110
- if (unknown.length > 0) {
111
- emit(session, `<${tagName}/> has unknown attribute(s): ${unknown.join(', ')}`);
112
- console.error(`[tag-check] <${tagName}/> unknown attributes: ${unknown.join(', ')}`);
113
- }
114
- if (missing.length === 0 && unknown.length === 0) {
115
- emit(session, `<${tagName}/> is valid.`);
116
- console.error(`[tag-check] <${tagName}/> is valid.`);
117
- allValid = true;
118
- }
119
- } else {
120
- // Find best semantic match
121
- console.error(`[tag-check] <${tagName}/> not found, searching for best match...`);
122
- const best = await getBestTagMatch(tagName, Array.from(allowed));
123
- console.error(`[tag-check] Best match: <${best.tag}> (score: ${best.score})`);
124
- if (best.score >= SIMILARITY_CUTOFF) {
125
- if (autocorrect) {
126
- emit(session, `The tag <${tagName}> was auto-corrected to <${best.tag}> (similarity: ${best.score.toFixed(2)})`);
127
- console.error(`[tag-check] Auto-correcting <${tagName}/> to <${best.tag}>`);
128
- correctedTag = best.tag;
129
- // Validate parameters for corrected tag
130
- const sub = subroutines.find(s => s.name === correctedTag);
131
- let missing: string[] = [];
132
- let unknown: string[] = [];
133
- let paramNames: string[] = [];
134
- if (sub && Array.isArray(sub.parameters)) {
135
- paramNames = sub.parameters.map(p => p.name);
136
- for (const param of sub.parameters) {
137
- if (param.required && !(param.name in child.attributes)) {
138
- missing.push(param.name);
139
- }
140
- }
141
- for (const attr in child.attributes) {
142
- if (!paramNames.includes(attr)) {
143
- unknown.push(attr);
144
- }
145
- }
146
- }
147
- if (missing.length > 0) {
148
- emit(session, `<${correctedTag}/> is missing required parameter(s): ${missing.join(', ')}`);
149
- console.error(`[tag-check] <${correctedTag}/> missing required: ${missing.join(', ')}`);
150
- }
151
- if (unknown.length > 0) {
152
- emit(session, `<${correctedTag}/> has unknown attribute(s): ${unknown.join(', ')}`);
153
- console.error(`[tag-check] <${correctedTag}/> unknown attributes: ${unknown.join(', ')}`);
154
- }
155
- if (missing.length === 0 && unknown.length === 0) {
156
- allValid = true;
157
- }
158
- } else {
159
- emit(session, `The tag <${tagName}> does not exist. Did you mean <${best.tag}>? (similarity: ${best.score.toFixed(2)})`);
160
- }
161
- } else {
162
- emit(session, `The tag <${tagName}> does not exist and no similar tag was found.`);
163
- }
164
- }
165
- // If all checks pass and execute="true" is present, execute the child tag
166
- if (allValid && shouldExecute) {
167
- const executeTag = correctedTag || tagName;
168
- console.error(`[tag-check] Executing <${executeTag}/> as all checks passed and execute=true.`);
169
- // Create a corrected element if tag was auto-corrected
170
- const elementToExecute = correctedTag ? { ...child, tag: correctedTag } : child;
171
- // Dynamically import interpreter and execute the child tag
172
- const { integrate } = await import('../runtime/interpreter.js');
173
- await integrate(session, elementToExecute);
174
- }
175
- }
176
- }
@@ -1,112 +0,0 @@
1
- /**
2
- * <test-if> tag - conditional execution with test attribute
3
- * This is the attribute-based conditional (original Dirac style)
4
- *
5
- * Usage: <test-if test="$var == value">...</test-if>
6
- */
7
-
8
- import type { DiracSession, DiracElement } from '../types/index.js';
9
- import { substituteAttribute } from '../runtime/session.js';
10
- import { integrateChildren } from '../runtime/interpreter.js';
11
-
12
- export async function executeTestIf(session: DiracSession, element: DiracElement): Promise<void> {
13
- const test = element.attributes.test;
14
-
15
- if (!test) {
16
- throw new Error('<test-if> requires test attribute');
17
- }
18
-
19
- // Get variable value
20
- const value = substituteAttribute(session, test);
21
-
22
- // Check comparison attributes
23
- const eq = element.attributes.eq;
24
- const ne = element.attributes.ne;
25
- const lt = element.attributes.lt;
26
- const gt = element.attributes.gt;
27
- const le = element.attributes.le;
28
- const ge = element.attributes.ge;
29
-
30
- let condition = false;
31
-
32
- if (eq !== undefined) {
33
- const compareValue = substituteAttribute(session, eq);
34
- condition = value === compareValue;
35
- } else if (ne !== undefined) {
36
- const compareValue = substituteAttribute(session, ne);
37
- condition = value !== compareValue;
38
- } else if (lt !== undefined) {
39
- const compareValue = substituteAttribute(session, lt);
40
- const valueNum = parseFloat(value);
41
- const compareNum = parseFloat(compareValue);
42
- condition = !isNaN(valueNum) && !isNaN(compareNum) && valueNum < compareNum;
43
- } else if (gt !== undefined) {
44
- const compareValue = substituteAttribute(session, gt);
45
- const valueNum = parseFloat(value);
46
- const compareNum = parseFloat(compareValue);
47
- condition = !isNaN(valueNum) && !isNaN(compareNum) && valueNum > compareNum;
48
- } else if (le !== undefined) {
49
- const compareValue = substituteAttribute(session, le);
50
- const valueNum = parseFloat(value);
51
- const compareNum = parseFloat(compareValue);
52
- condition = !isNaN(valueNum) && !isNaN(compareNum) && valueNum <= compareNum;
53
- } else if (ge !== undefined) {
54
- const compareValue = substituteAttribute(session, ge);
55
- const valueNum = parseFloat(value);
56
- const compareNum = parseFloat(compareValue);
57
- condition = !isNaN(valueNum) && !isNaN(compareNum) && valueNum >= compareNum;
58
- } else {
59
- // No comparison attribute, evaluate test value as boolean
60
- condition = evaluateCondition(session, test);
61
- }
62
-
63
- if (condition) {
64
- await integrateChildren(session, element);
65
- }
66
- }
67
-
68
- function evaluateCondition(session: DiracSession, test: string): boolean {
69
- // Substitute variables first
70
- // Use attribute-style substitution for test condition
71
- const substituted = substituteAttribute(session, test);
72
-
73
- // Simple condition evaluation
74
- // Supports: ==, !=, <, >, <=, >=
75
-
76
- const operators = ['==', '!=', '<=', '>=', '<', '>'];
77
-
78
- for (const op of operators) {
79
- const parts = substituted.split(op);
80
- if (parts.length === 2) {
81
- let left = parts[0].trim();
82
- let right = parts[1].trim();
83
- const leftNum = parseFloat(left);
84
- const rightNum = parseFloat(right);
85
- const bothNumbers = !isNaN(leftNum) && !isNaN(rightNum);
86
-
87
- switch (op) {
88
- case '==':
89
- return bothNumbers ? leftNum === rightNum : left === right;
90
- case '!=':
91
- return bothNumbers ? leftNum !== rightNum : left !== right;
92
- case '<':
93
- return leftNum < rightNum;
94
- case '>':
95
- return leftNum > rightNum;
96
- case '<=':
97
- return leftNum <= rightNum;
98
- case '>=':
99
- return leftNum >= rightNum;
100
- }
101
- }
102
- }
103
-
104
- // If no operator found, treat as boolean
105
- // Empty, "0", or "false" is false
106
- // Everything else is true
107
- if (substituted === '' || substituted === '0' || substituted === 'false') {
108
- return false;
109
- }
110
-
111
- return true;
112
- }