dirac-lang 0.1.44 → 0.1.46

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  integrate
3
- } from "./chunk-4ROK7OUQ.js";
3
+ } from "./chunk-TXL3RV74.js";
4
4
  import {
5
5
  DiracParser
6
6
  } from "./chunk-HRHAMPOB.js";
@@ -460,12 +460,12 @@ async function executeIf(session, element) {
460
460
  const condition = await evaluatePredicate(session, conditionElement);
461
461
  if (condition) {
462
462
  if (thenElement) {
463
- const { integrateChildren: integrateChildren2 } = await import("./interpreter-Y2BOWVF4.js");
463
+ const { integrateChildren: integrateChildren2 } = await import("./interpreter-XH3RFKVP.js");
464
464
  await integrateChildren2(session, thenElement);
465
465
  }
466
466
  } else {
467
467
  if (elseElement) {
468
- const { integrateChildren: integrateChildren2 } = await import("./interpreter-Y2BOWVF4.js");
468
+ const { integrateChildren: integrateChildren2 } = await import("./interpreter-XH3RFKVP.js");
469
469
  await integrateChildren2(session, elseElement);
470
470
  }
471
471
  }
@@ -478,7 +478,7 @@ async function evaluatePredicate(session, predicateElement) {
478
478
  return await evaluateCondition(session, predicateElement);
479
479
  }
480
480
  const outputLengthBefore = session.output.length;
481
- const { integrate: integrate2 } = await import("./interpreter-Y2BOWVF4.js");
481
+ const { integrate: integrate2 } = await import("./interpreter-XH3RFKVP.js");
482
482
  await integrate2(session, predicateElement);
483
483
  const newOutputChunks = session.output.slice(outputLengthBefore);
484
484
  const result = newOutputChunks.join("").trim();
@@ -501,11 +501,11 @@ async function evaluateCondition(session, condElement) {
501
501
  }
502
502
  const outputLengthBefore = session.output.length;
503
503
  const args = [];
504
- const { integrate: integrate2 } = await import("./interpreter-Y2BOWVF4.js");
504
+ const { integrate: integrate2 } = await import("./interpreter-XH3RFKVP.js");
505
505
  for (const child of condElement.children) {
506
506
  if (child.tag.toLowerCase() === "arg") {
507
507
  const argOutputStart = session.output.length;
508
- const { integrateChildren: integrateChildren2 } = await import("./interpreter-Y2BOWVF4.js");
508
+ const { integrateChildren: integrateChildren2 } = await import("./interpreter-XH3RFKVP.js");
509
509
  await integrateChildren2(session, child);
510
510
  const newChunks = session.output.slice(argOutputStart);
511
511
  const argValue = newChunks.join("");
@@ -564,10 +564,113 @@ function evaluateConditionType(evalType, args) {
564
564
  }
565
565
 
566
566
  // src/tags/llm.ts
567
+ import * as fs2 from "fs";
568
+ import * as path2 from "path";
569
+ import * as os from "os";
570
+ function dumpGeneratedSubroutines(session, diracCode, userPrompt) {
571
+ try {
572
+ let findSubroutines2 = function(element) {
573
+ if (element.tag === "subroutine") {
574
+ subroutines.push(element);
575
+ }
576
+ if (element.children) {
577
+ for (const child of element.children) {
578
+ findSubroutines2(child);
579
+ }
580
+ }
581
+ };
582
+ var findSubroutines = findSubroutines2;
583
+ const parser = new DiracParser();
584
+ const ast = parser.parse(diracCode);
585
+ const subroutines = [];
586
+ findSubroutines2(ast);
587
+ if (subroutines.length === 0) {
588
+ return;
589
+ }
590
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, -5);
591
+ const dumpDir = path2.join(os.homedir(), ".dirac", "lib", timestamp);
592
+ if (!fs2.existsSync(dumpDir)) {
593
+ fs2.mkdirSync(dumpDir, { recursive: true });
594
+ }
595
+ for (const sub of subroutines) {
596
+ const subName = sub.attributes.name || "unnamed";
597
+ const filePath = path2.join(dumpDir, `${subName}.di`);
598
+ const xml = serializeElement(sub, userPrompt);
599
+ fs2.writeFileSync(filePath, xml, "utf-8");
600
+ if (session.debug) {
601
+ console.error(`[LLM] Dumped subroutine '${subName}' to: ${filePath}`);
602
+ }
603
+ }
604
+ if (session.debug) {
605
+ console.error(`[LLM] Dumped ${subroutines.length} subroutine(s) to: ${dumpDir}`);
606
+ }
607
+ } catch (error) {
608
+ if (session.debug) {
609
+ console.error(`[LLM] Failed to dump subroutines: ${error}`);
610
+ }
611
+ }
612
+ }
613
+ function serializeElement(element, prompt) {
614
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
615
+ const lines = [];
616
+ lines.push("<!--");
617
+ lines.push(` Created: ${timestamp}`);
618
+ lines.push(` Generated by: LLM`);
619
+ lines.push(` Prompt: ${prompt.replace(/-->/g, "--&gt;")}`);
620
+ lines.push("-->");
621
+ lines.push("");
622
+ function serialize(el, indent = "") {
623
+ if (el.text && !el.tag) {
624
+ lines.push(indent + el.text);
625
+ return;
626
+ }
627
+ if (!el.tag) return;
628
+ let tag = `${indent}<${el.tag}`;
629
+ if (el.attributes) {
630
+ for (const [key, value] of Object.entries(el.attributes)) {
631
+ tag += ` ${key}="${value.replace(/"/g, "&quot;")}"`;
632
+ }
633
+ }
634
+ if (!el.children || el.children.length === 0) {
635
+ if (el.text) {
636
+ lines.push(tag + ">");
637
+ lines.push(indent + " " + el.text);
638
+ lines.push(`${indent}</${el.tag}>`);
639
+ } else {
640
+ lines.push(tag + " />");
641
+ }
642
+ } else {
643
+ lines.push(tag + ">");
644
+ for (const child of el.children) {
645
+ serialize(child, indent + " ");
646
+ }
647
+ lines.push(`${indent}</${el.tag}>`);
648
+ }
649
+ }
650
+ serialize(element);
651
+ return lines.join("\n");
652
+ }
567
653
  async function executeLLM(session, element) {
568
654
  if (!session.llmClient) {
569
655
  throw new Error("<llm> tag requires LLM configuration. Set LLM_PROVIDER (ollama/anthropic/openai/custom) and appropriate API keys in environment or config.yml");
570
656
  }
657
+ const callAnthropic = async (client, model2, maxTokens2, temperature2, messages) => {
658
+ const systemMessages = messages.filter((m) => m.role === "system");
659
+ const userAssistantMessages = messages.filter((m) => m.role !== "system");
660
+ const systemContent = systemMessages.map((m) => m.content).join("\n\n");
661
+ const anthropicParams = {
662
+ model: model2,
663
+ max_tokens: maxTokens2,
664
+ temperature: temperature2,
665
+ messages: userAssistantMessages
666
+ };
667
+ if (systemContent) {
668
+ anthropicParams.system = systemContent;
669
+ }
670
+ const response = await client.messages.create(anthropicParams);
671
+ const content = response.content[0];
672
+ return content.type === "text" ? content.text : "";
673
+ };
571
674
  if (session.limits.currentLLMCalls >= session.limits.maxLLMCalls) {
572
675
  throw new Error("Maximum LLM calls exceeded");
573
676
  }
@@ -580,6 +683,7 @@ async function executeLLM(session, element) {
580
683
  const model = element.attributes.model || process.env.DEFAULT_MODEL || defaultModel;
581
684
  const outputVar = element.attributes.output;
582
685
  const contextVar = element.attributes.context;
686
+ const saveDialog = element.attributes["save-dialog"] === "true";
583
687
  const executeMode = element.attributes.execute === "true";
584
688
  const temperature = parseFloat(element.attributes.temperature || "1.0");
585
689
  const maxTokens = parseInt(element.attributes.maxTokens || "4096", 10);
@@ -598,23 +702,29 @@ async function executeLLM(session, element) {
598
702
  throw new Error("<LLM> requires prompt content");
599
703
  }
600
704
  let dialogHistory = [];
601
- if (contextVar) {
602
- const existing = getVariable(session, contextVar);
705
+ let hasExistingDialog = false;
706
+ if (contextVar || saveDialog) {
707
+ const varName = contextVar || "__llm_dialog__";
708
+ const existing = getVariable(session, varName);
709
+ if (session.debug) {
710
+ console.error(`[LLM] Checking for dialog context in variable: ${varName}`);
711
+ console.error(`[LLM] Existing value type: ${Array.isArray(existing) ? "array" : typeof existing}`);
712
+ if (Array.isArray(existing)) {
713
+ console.error(`[LLM] Existing dialog length: ${existing.length} messages`);
714
+ }
715
+ }
603
716
  if (Array.isArray(existing)) {
604
717
  dialogHistory = [...existing];
718
+ hasExistingDialog = dialogHistory.length > 0;
605
719
  } else if (existing) {
606
720
  dialogHistory = [{ role: "system", content: String(existing) }];
721
+ hasExistingDialog = true;
607
722
  }
608
723
  }
609
724
  const noExtra = element.attributes.noextra === "true";
610
- let prompt;
611
725
  let systemPrompt = "";
612
- if (noExtra) {
613
- prompt = userPrompt;
614
- if (session.debug || process.env.DIRAC_LOG_PROMPT === "1") {
615
- console.error("[LLM] Full prompt sent to LLM (noextra):\n" + prompt + "\n");
616
- }
617
- } else {
726
+ let currentUserPrompt = userPrompt;
727
+ if (!noExtra) {
618
728
  const { getAvailableSubroutines: getAvailableSubroutines2 } = await import("./session-UBATJEND.js");
619
729
  const subroutines = getAvailableSubroutines2(session);
620
730
  if (session.debug) {
@@ -623,48 +733,85 @@ async function executeLLM(session, element) {
623
733
  subroutines.map((s) => ({ name: s.name, description: s.description, parameters: s.parameters }))
624
734
  );
625
735
  }
626
- systemPrompt = `Dirac is a XML based language, you define the subroutine like
736
+ if (hasExistingDialog && (contextVar || saveDialog)) {
737
+ systemPrompt = "Updated available Dirac XML tags:";
738
+ for (const sub of subroutines) {
739
+ systemPrompt += `
740
+ - ${sub.name} : ${sub.description || ""}`;
741
+ systemPrompt += `
742
+ Ex: <${sub.name}`;
743
+ if (sub.parameters && sub.parameters.length > 0) {
744
+ for (const p of sub.parameters) {
745
+ systemPrompt += ` ${p.name}="${p.example || "string"}"`;
746
+ }
747
+ }
748
+ let example = sub.meta?.body?.example || "";
749
+ example = example.replace(/&quot;/g, '"').replace(/&#58;/g, ":");
750
+ systemPrompt += ">" + example + "</" + sub.name + ">";
751
+ }
752
+ currentUserPrompt = systemPrompt + "\n\nUser request: " + userPrompt;
753
+ if (session.debug || process.env.DIRAC_LOG_PROMPT === "1") {
754
+ console.error("[LLM] Continuing dialog with updated subroutines\n");
755
+ }
756
+ } else {
757
+ systemPrompt = `Dirac is a XML-based language. To define a subroutine with parameters:
758
+
627
759
  \`\`\`xml
628
- <subroutine name=background param-color="string">
629
- <paint_the_color_somewhere />
760
+ <subroutine name="greet" param-name="string">
761
+ <!-- param-name defines a parameter called "name" -->
762
+ <!-- Access it inside using: <variable name="name"/> -->
763
+ <output>Hello, <variable name="name"/>!</output>
630
764
  </subroutine>
631
765
  \`\`\`
632
- then you call it like
766
+
767
+ To call it:
633
768
  \`\`\`xml
634
- <background color="blue" />
769
+ <greet name="Alice" />
770
+ <!-- Use just the parameter name (name), NOT param-name -->
635
771
  \`\`\`
772
+
773
+ CRITICAL: When defining parameters:
774
+ - Use param-NAME="type" format where NAME is the parameter's name
775
+ - Example: param-username="string" means parameter is called "username"
776
+ - Inside the subroutine, access with: <variable name="username"/>
777
+ - When calling: <mytag username="John" /> (use parameter name directly)
636
778
  `;
637
- systemPrompt += "Now, You are an expert Dirac XML code generator.\nAllowed Dirac XML tags (use ONLY these tags):";
638
- for (const sub of subroutines) {
639
- systemPrompt += `
779
+ systemPrompt += "Now, You are an expert Dirac XML code generator.\nAllowed Dirac XML tags (use ONLY these tags):";
780
+ for (const sub of subroutines) {
781
+ systemPrompt += `
640
782
  - ${sub.name} : ${sub.description || ""}`;
641
- systemPrompt += `
783
+ systemPrompt += `
642
784
  Ex: <${sub.name}`;
643
- if (sub.parameters && sub.parameters.length > 0) {
644
- for (const p of sub.parameters) {
645
- systemPrompt += ` ${p.name}="${p.example || "string"}"`;
785
+ if (sub.parameters && sub.parameters.length > 0) {
786
+ for (const p of sub.parameters) {
787
+ systemPrompt += ` ${p.name}="${p.example || "string"}"`;
788
+ }
646
789
  }
790
+ let example = sub.meta?.body?.example || "";
791
+ example = example.replace(/&quot;/g, '"').replace(/&#58;/g, ":");
792
+ systemPrompt += ">" + example + "</" + sub.name + ">";
793
+ }
794
+ systemPrompt += "\n\nIMPORTANT INSTRUCTIONS:";
795
+ systemPrompt += "\n1. Output ONLY valid XML tags from the list above";
796
+ systemPrompt += "\n2. Do NOT include any explanations, descriptions, or extra text";
797
+ systemPrompt += "\n3. Do NOT use bullet points or formatting - just pure XML";
798
+ systemPrompt += "\n4. Do NOT invent tags - only use tags from the list above";
799
+ systemPrompt += "\n5. Start your response directly with the XML tag (e.g., <add ...>)";
800
+ systemPrompt += "\n\nDouble-check: Does your response contain ONLY XML tags? If not, remove all non-XML text.";
801
+ if (dialogHistory.length === 0) {
802
+ dialogHistory.push({ role: "system", content: systemPrompt });
803
+ }
804
+ currentUserPrompt = userPrompt;
805
+ if (session.debug || process.env.DIRAC_LOG_PROMPT === "1") {
806
+ console.error("[LLM] First call - sending full system prompt\n");
647
807
  }
648
- let example = sub.meta?.body?.example || "";
649
- example = example.replace(/&quot;/g, '"').replace(/&#58;/g, ":");
650
- systemPrompt += ">" + example + "</" + sub.name + ">";
651
- }
652
- systemPrompt += "\n\nIMPORTANT INSTRUCTIONS:";
653
- systemPrompt += "\n1. Output ONLY valid XML tags from the list above";
654
- systemPrompt += "\n2. Do NOT include any explanations, descriptions, or extra text";
655
- systemPrompt += "\n3. Do NOT use bullet points or formatting - just pure XML";
656
- systemPrompt += "\n4. Do NOT invent tags - only use tags from the list above";
657
- systemPrompt += "\n5. Start your response directly with the XML tag (e.g., <add ...>)";
658
- systemPrompt += "\n\nDouble-check: Does your response contain ONLY XML tags? If not, remove all non-XML text.";
659
- prompt = systemPrompt + "\nUser: " + userPrompt + "\nOutput:";
660
- if (session.debug || process.env.DIRAC_LOG_PROMPT === "1") {
661
- console.error("[LLM] Full prompt sent to LLM:\n" + prompt + "\n");
662
- }
663
- }
664
- dialogHistory.push({ role: "user", content: noExtra ? userPrompt : prompt });
808
+ }
809
+ }
810
+ dialogHistory.push({ role: "user", content: currentUserPrompt });
665
811
  if (session.debug) {
666
- console.error(`[LLM] Calling ${model} with prompt length: ${prompt.length}`);
812
+ console.error(`[LLM] Calling ${model}`);
667
813
  console.error(`[LLM] Dialog history length: ${dialogHistory.length} messages`);
814
+ console.error(`[LLM] Has existing dialog: ${hasExistingDialog}`);
668
815
  }
669
816
  try {
670
817
  let result;
@@ -692,21 +839,21 @@ then you call it like
692
839
  messages: dialogHistory
693
840
  });
694
841
  } else {
695
- const response = await session.llmClient.messages.create({
696
- model,
697
- max_tokens: maxTokens,
698
- temperature,
699
- messages: dialogHistory
700
- });
701
- const content = response.content[0];
702
- result = content.type === "text" ? content.text : "";
842
+ result = await callAnthropic(session.llmClient, model, maxTokens, temperature, dialogHistory);
703
843
  }
704
844
  if (session.debug) {
705
845
  console.error(`[LLM] Response length: ${result.length}`);
846
+ console.error(`[LLM] Generated code:
847
+ ${result}
848
+ `);
706
849
  }
707
- if (contextVar) {
708
- dialogHistory.push({ role: "assistant", content: result });
709
- setVariable(session, contextVar, dialogHistory, true);
850
+ dialogHistory.push({ role: "assistant", content: result });
851
+ const varName = contextVar || (saveDialog ? "__llm_dialog__" : null);
852
+ if (varName) {
853
+ if (session.debug) {
854
+ console.error(`[LLM] Saving dialog history (${dialogHistory.length} messages) to: ${varName}`);
855
+ }
856
+ setVariable(session, varName, dialogHistory, true);
710
857
  }
711
858
  if (outputVar) {
712
859
  setVariable(session, outputVar, result, false);
@@ -788,18 +935,13 @@ Please fix these errors and generate valid Dirac XML again. Remember to only use
788
935
  messages: dialogHistory
789
936
  });
790
937
  } else {
791
- const response = await session.llmClient.messages.create({
792
- model,
793
- max_tokens: maxTokens,
794
- temperature,
795
- messages: dialogHistory
796
- });
797
- const content = response.content[0];
798
- result = content.type === "text" ? content.text : "";
938
+ result = await callAnthropic(session.llmClient, model, maxTokens, temperature, dialogHistory);
799
939
  }
800
940
  dialogHistory.push({ role: "assistant", content: result });
801
941
  if (contextVar) {
802
942
  setVariable(session, contextVar, dialogHistory, true);
943
+ } else if (saveDialog) {
944
+ setVariable(session, "__llm_dialog__", dialogHistory, true);
803
945
  }
804
946
  if (session.debug) {
805
947
  console.error(`[LLM] Retry ${retryCount} response:
@@ -832,6 +974,9 @@ ${validation.errorMessages.join("\n")}`);
832
974
  }
833
975
  }
834
976
  await integrate(session, dynamicAST);
977
+ if (iteration === 1) {
978
+ dumpGeneratedSubroutines(session, diracCode, userPrompt);
979
+ }
835
980
  if (feedbackMode) {
836
981
  const outputAfter = session.output.slice();
837
982
  const executionOutput = outputAfter.slice(outputBefore.length).join("");
@@ -876,18 +1021,13 @@ ${feedbackPrompt}
876
1021
  messages: dialogHistory
877
1022
  });
878
1023
  } else {
879
- const response = await session.llmClient.messages.create({
880
- model,
881
- max_tokens: maxTokens,
882
- temperature,
883
- messages: dialogHistory
884
- });
885
- const content = response.content[0];
886
- result = content.type === "text" ? content.text : "";
1024
+ result = await callAnthropic(session.llmClient, model, maxTokens, temperature, dialogHistory);
887
1025
  }
888
1026
  dialogHistory.push({ role: "assistant", content: result });
889
1027
  if (contextVar) {
890
1028
  setVariable(session, contextVar, dialogHistory, true);
1029
+ } else if (saveDialog) {
1030
+ setVariable(session, "__llm_dialog__", dialogHistory, true);
891
1031
  }
892
1032
  if (session.debug) {
893
1033
  console.error(`[LLM] Feedback response:
@@ -945,11 +1085,11 @@ ${expr}
945
1085
  for (const v of session.variables) {
946
1086
  context[v.name] = v.value;
947
1087
  }
948
- const { default: fs5 } = await import("fs");
949
- const { default: path3 } = await import("path");
1088
+ const { default: fs6 } = await import("fs");
1089
+ const { default: path4 } = await import("path");
950
1090
  const { fileURLToPath } = await import("url");
951
- context.fs = fs5;
952
- context.path = path3;
1091
+ context.fs = fs6;
1092
+ context.path = path4;
953
1093
  context.__dirname = process.cwd();
954
1094
  context.getParams = () => {
955
1095
  const params = session.parameterStack[session.parameterStack.length - 1];
@@ -1003,8 +1143,8 @@ ${diracCode}
1003
1143
  }
1004
1144
 
1005
1145
  // src/tags/import.ts
1006
- import { readFileSync, existsSync as existsSync2 } from "fs";
1007
- import { resolve, dirname as dirname2, join } from "path";
1146
+ import { readFileSync, existsSync as existsSync3 } from "fs";
1147
+ import { resolve, dirname as dirname2, join as join2 } from "path";
1008
1148
  function resolveImportPath(src, currentDir) {
1009
1149
  if (src.startsWith("./") || src.startsWith("../") || src.startsWith("/")) {
1010
1150
  const resolved2 = resolve(currentDir, src);
@@ -1012,26 +1152,26 @@ function resolveImportPath(src, currentDir) {
1012
1152
  }
1013
1153
  let searchDir = currentDir;
1014
1154
  while (true) {
1015
- const nodeModulesPath = join(searchDir, "node_modules", src);
1016
- if (existsSync2(nodeModulesPath)) {
1017
- const packageJsonPath = join(nodeModulesPath, "package.json");
1018
- if (existsSync2(packageJsonPath)) {
1155
+ const nodeModulesPath = join2(searchDir, "node_modules", src);
1156
+ if (existsSync3(nodeModulesPath)) {
1157
+ const packageJsonPath = join2(nodeModulesPath, "package.json");
1158
+ if (existsSync3(packageJsonPath)) {
1019
1159
  try {
1020
1160
  const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
1021
1161
  const mainFile = packageJson.main || "lib/index.di";
1022
- const entryPath = join(nodeModulesPath, mainFile);
1023
- if (existsSync2(entryPath)) {
1162
+ const entryPath = join2(nodeModulesPath, mainFile);
1163
+ if (existsSync3(entryPath)) {
1024
1164
  return entryPath;
1025
1165
  }
1026
1166
  } catch (err) {
1027
1167
  }
1028
1168
  }
1029
1169
  const fallbacks = [
1030
- join(nodeModulesPath, "lib", "index.di"),
1031
- join(nodeModulesPath, "index.di")
1170
+ join2(nodeModulesPath, "lib", "index.di"),
1171
+ join2(nodeModulesPath, "index.di")
1032
1172
  ];
1033
1173
  for (const fallback of fallbacks) {
1034
- if (existsSync2(fallback)) {
1174
+ if (existsSync3(fallback)) {
1035
1175
  return fallback;
1036
1176
  }
1037
1177
  }
@@ -1252,13 +1392,13 @@ async function executeRequireModule(session, element) {
1252
1392
  }
1253
1393
 
1254
1394
  // src/tags/tag-check.ts
1255
- import fs2 from "fs";
1395
+ import fs3 from "fs";
1256
1396
  import yaml from "js-yaml";
1257
1397
  var SIMILARITY_CUTOFF = 0.75;
1258
1398
  function getEmbeddingServerConfig() {
1259
1399
  try {
1260
1400
  const configPath = process.env.DIRAC_CONFIG || "config.yml";
1261
- const config = yaml.load(fs2.readFileSync(configPath, "utf8"));
1401
+ const config = yaml.load(fs3.readFileSync(configPath, "utf8"));
1262
1402
  const host = config.embeddingServer?.host || "localhost";
1263
1403
  const port = config.embeddingServer?.port || 11434;
1264
1404
  const model = config.embeddingServer?.model || "embeddinggemma";
@@ -1394,7 +1534,7 @@ async function executeTagCheck(session, element) {
1394
1534
  const executeTag = correctedTag || tagName;
1395
1535
  console.error(`[tag-check] Executing <${executeTag}/> as all checks passed and execute=true.`);
1396
1536
  const elementToExecute = correctedTag ? { ...child, tag: correctedTag } : child;
1397
- const { integrate: integrate2 } = await import("./interpreter-Y2BOWVF4.js");
1537
+ const { integrate: integrate2 } = await import("./interpreter-XH3RFKVP.js");
1398
1538
  await integrate2(session, elementToExecute);
1399
1539
  }
1400
1540
  }
@@ -1403,7 +1543,7 @@ async function executeTagCheck(session, element) {
1403
1543
  // src/tags/throw.ts
1404
1544
  async function executeThrow(session, element) {
1405
1545
  const exceptionName = element.attributes?.name || "exception";
1406
- const { integrateChildren: integrateChildren2 } = await import("./interpreter-Y2BOWVF4.js");
1546
+ const { integrateChildren: integrateChildren2 } = await import("./interpreter-XH3RFKVP.js");
1407
1547
  const exceptionDom = {
1408
1548
  tag: "exception-content",
1409
1549
  attributes: { name: exceptionName },
@@ -1416,7 +1556,7 @@ async function executeThrow(session, element) {
1416
1556
  // src/tags/try.ts
1417
1557
  async function executeTry(session, element) {
1418
1558
  setExceptionBoundary(session);
1419
- const { integrateChildren: integrateChildren2 } = await import("./interpreter-Y2BOWVF4.js");
1559
+ const { integrateChildren: integrateChildren2 } = await import("./interpreter-XH3RFKVP.js");
1420
1560
  await integrateChildren2(session, element);
1421
1561
  unsetExceptionBoundary(session);
1422
1562
  }
@@ -1426,7 +1566,7 @@ async function executeCatch(session, element) {
1426
1566
  const exceptionName = element.attributes?.name || "exception";
1427
1567
  const caughtCount = lookupException(session, exceptionName);
1428
1568
  if (caughtCount > 0) {
1429
- const { integrateChildren: integrateChildren2 } = await import("./interpreter-Y2BOWVF4.js");
1569
+ const { integrateChildren: integrateChildren2 } = await import("./interpreter-XH3RFKVP.js");
1430
1570
  await integrateChildren2(session, element);
1431
1571
  }
1432
1572
  flushCurrentException(session);
@@ -1435,7 +1575,7 @@ async function executeCatch(session, element) {
1435
1575
  // src/tags/exception.ts
1436
1576
  async function executeException(session, element) {
1437
1577
  const exceptions = getCurrentExceptions(session);
1438
- const { integrateChildren: integrateChildren2 } = await import("./interpreter-Y2BOWVF4.js");
1578
+ const { integrateChildren: integrateChildren2 } = await import("./interpreter-XH3RFKVP.js");
1439
1579
  for (const exceptionDom of exceptions) {
1440
1580
  await integrateChildren2(session, exceptionDom);
1441
1581
  }
@@ -1630,23 +1770,23 @@ function formatAsText(subroutines) {
1630
1770
  }
1631
1771
 
1632
1772
  // src/runtime/subroutine-registry.ts
1633
- import * as fs3 from "fs";
1634
- import * as path2 from "path";
1635
- import * as os from "os";
1773
+ import * as fs4 from "fs";
1774
+ import * as path3 from "path";
1775
+ import * as os2 from "os";
1636
1776
  var SubroutineRegistry = class {
1637
1777
  indexPath;
1638
1778
  index;
1639
1779
  constructor(indexPath) {
1640
- this.indexPath = indexPath || path2.join(os.homedir(), ".dirac", "subroutine-index.json");
1780
+ this.indexPath = indexPath || path3.join(os2.homedir(), ".dirac", "subroutine-index.json");
1641
1781
  this.index = this.loadIndex();
1642
1782
  }
1643
1783
  /**
1644
1784
  * Load index from disk
1645
1785
  */
1646
1786
  loadIndex() {
1647
- if (fs3.existsSync(this.indexPath)) {
1787
+ if (fs4.existsSync(this.indexPath)) {
1648
1788
  try {
1649
- const data = fs3.readFileSync(this.indexPath, "utf-8");
1789
+ const data = fs4.readFileSync(this.indexPath, "utf-8");
1650
1790
  return JSON.parse(data);
1651
1791
  } catch (err) {
1652
1792
  console.error("Error loading subroutine index:", err);
@@ -1661,12 +1801,12 @@ var SubroutineRegistry = class {
1661
1801
  * Save index to disk
1662
1802
  */
1663
1803
  saveIndex() {
1664
- const dir = path2.dirname(this.indexPath);
1665
- if (!fs3.existsSync(dir)) {
1666
- fs3.mkdirSync(dir, { recursive: true });
1804
+ const dir = path3.dirname(this.indexPath);
1805
+ if (!fs4.existsSync(dir)) {
1806
+ fs4.mkdirSync(dir, { recursive: true });
1667
1807
  }
1668
1808
  this.index.lastUpdated = Date.now();
1669
- fs3.writeFileSync(this.indexPath, JSON.stringify(this.index, null, 2), "utf-8");
1809
+ fs4.writeFileSync(this.indexPath, JSON.stringify(this.index, null, 2), "utf-8");
1670
1810
  }
1671
1811
  /**
1672
1812
  * Scan a directory for .di files and index all subroutines
@@ -1674,9 +1814,9 @@ var SubroutineRegistry = class {
1674
1814
  async indexDirectory(dirPath) {
1675
1815
  let count = 0;
1676
1816
  const scanDir = (dir) => {
1677
- const entries = fs3.readdirSync(dir, { withFileTypes: true });
1817
+ const entries = fs4.readdirSync(dir, { withFileTypes: true });
1678
1818
  for (const entry of entries) {
1679
- const fullPath = path2.join(dir, entry.name);
1819
+ const fullPath = path3.join(dir, entry.name);
1680
1820
  if (entry.isDirectory()) {
1681
1821
  scanDir(fullPath);
1682
1822
  } else if (entry.isFile() && entry.name.endsWith(".di")) {
@@ -1693,7 +1833,7 @@ var SubroutineRegistry = class {
1693
1833
  */
1694
1834
  indexFile(filePath) {
1695
1835
  try {
1696
- const content = fs3.readFileSync(filePath, "utf-8");
1836
+ const content = fs4.readFileSync(filePath, "utf-8");
1697
1837
  const parser = new DiracParser();
1698
1838
  const ast = parser.parse(content);
1699
1839
  this.index.subroutines = this.index.subroutines.filter((s) => s.filePath !== filePath);
@@ -1898,7 +2038,7 @@ async function executeLoadContext(session, element) {
1898
2038
  query = element.text.trim();
1899
2039
  }
1900
2040
  if (!query && element.children.length > 0) {
1901
- const { integrate: integrate2 } = await import("./interpreter-Y2BOWVF4.js");
2041
+ const { integrate: integrate2 } = await import("./interpreter-XH3RFKVP.js");
1902
2042
  const beforeOutput = session.output.length;
1903
2043
  for (const child of element.children) {
1904
2044
  await integrate2(session, child);
@@ -1973,18 +2113,17 @@ async function executeLoadContext(session, element) {
1973
2113
  }
1974
2114
 
1975
2115
  // src/tags/save-subroutine.ts
1976
- import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
1977
- import { resolve as resolve2, dirname as dirname4 } from "path";
2116
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
2117
+ import { resolve as resolve2, dirname as dirname4, join as join4 } from "path";
2118
+ import { homedir as homedir3 } from "os";
1978
2119
  async function executeSaveSubroutine(session, element) {
1979
2120
  const name = element.attributes.name;
1980
2121
  const file = element.attributes.file;
2122
+ const pathAttr = element.attributes.path;
1981
2123
  const format = element.attributes.format || "xml";
1982
2124
  if (!name) {
1983
2125
  throw new Error("<save-subroutine> requires name attribute");
1984
2126
  }
1985
- if (!file) {
1986
- throw new Error("<save-subroutine> requires file attribute");
1987
- }
1988
2127
  let subroutine = void 0;
1989
2128
  for (let i = session.subroutines.length - 1; i >= 0; i--) {
1990
2129
  if (session.subroutines[i].name === name) {
@@ -2001,12 +2140,27 @@ async function executeSaveSubroutine(session, element) {
2001
2140
  } else {
2002
2141
  content = generateXMLNotation(subroutine);
2003
2142
  }
2004
- const filePath = resolve2(process.cwd(), file);
2143
+ let filePath;
2144
+ if (file) {
2145
+ filePath = resolve2(process.cwd(), file);
2146
+ } else if (pathAttr) {
2147
+ const targetDir = join4(homedir3(), ".dirac", "lib", pathAttr);
2148
+ filePath = join4(targetDir, `${name}.di`);
2149
+ } else {
2150
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, -5);
2151
+ const defaultDir = join4(homedir3(), ".dirac", "lib", timestamp);
2152
+ filePath = join4(defaultDir, `${name}.di`);
2153
+ }
2005
2154
  const dir = dirname4(filePath);
2006
- mkdirSync3(dir, { recursive: true });
2007
- writeFileSync2(filePath, content, "utf-8");
2155
+ if (!existsSync5(dir)) {
2156
+ mkdirSync4(dir, { recursive: true });
2157
+ }
2158
+ writeFileSync3(filePath, content, "utf-8");
2008
2159
  emit(session, `Subroutine '${name}' saved to: ${filePath}
2009
2160
  `);
2161
+ if (session.debug) {
2162
+ console.error(`[save-subroutine] Saved '${name}' to: ${filePath}`);
2163
+ }
2010
2164
  }
2011
2165
  function generateXMLNotation(subroutine) {
2012
2166
  let xml = "<!-- Exported subroutine -->\n\n";
@@ -2137,7 +2291,7 @@ async function executeForeach(session, element) {
2137
2291
  const parser2 = new DiracParser2();
2138
2292
  try {
2139
2293
  const fromElement = parser2.parse(fromAttr);
2140
- const { integrate: integrate2 } = await import("./interpreter-Y2BOWVF4.js");
2294
+ const { integrate: integrate2 } = await import("./interpreter-XH3RFKVP.js");
2141
2295
  await integrate2(session, fromElement);
2142
2296
  } catch (e) {
2143
2297
  session.output = savedOutput;
@@ -2285,7 +2439,7 @@ async function executeEnvironment(session, element) {
2285
2439
  }
2286
2440
 
2287
2441
  // src/tags/input.ts
2288
- import * as fs4 from "fs";
2442
+ import * as fs5 from "fs";
2289
2443
  import * as readline from "readline";
2290
2444
  var fileReaders = /* @__PURE__ */ new Map();
2291
2445
  var fileIterators = /* @__PURE__ */ new Map();
@@ -2309,11 +2463,11 @@ async function executeInput(session, element) {
2309
2463
  throw new Error(`<input> invalid mode: ${mode}. Use 'all' or 'line'`);
2310
2464
  }
2311
2465
  } else if (source === "file") {
2312
- const path3 = substituteAttribute(session, pathAttr);
2466
+ const path4 = substituteAttribute(session, pathAttr);
2313
2467
  if (mode === "all") {
2314
- value = fs4.readFileSync(path3, "utf-8");
2468
+ value = fs5.readFileSync(path4, "utf-8");
2315
2469
  } else if (mode === "line") {
2316
- value = await readLineFromFile(path3);
2470
+ value = await readLineFromFile(path4);
2317
2471
  } else {
2318
2472
  throw new Error(`<input> invalid mode: ${mode}. Use 'all' or 'line'`);
2319
2473
  }
@@ -2370,23 +2524,23 @@ async function readLineStdin() {
2370
2524
  }
2371
2525
  return result.value;
2372
2526
  }
2373
- async function readLineFromFile(path3) {
2374
- if (!fileReaders.has(path3)) {
2375
- const fileStream = fs4.createReadStream(path3);
2527
+ async function readLineFromFile(path4) {
2528
+ if (!fileReaders.has(path4)) {
2529
+ const fileStream = fs5.createReadStream(path4);
2376
2530
  const rl = readline.createInterface({
2377
2531
  input: fileStream,
2378
2532
  crlfDelay: Infinity
2379
2533
  });
2380
- fileReaders.set(path3, rl);
2381
- fileIterators.set(path3, rl[Symbol.asyncIterator]());
2534
+ fileReaders.set(path4, rl);
2535
+ fileIterators.set(path4, rl[Symbol.asyncIterator]());
2382
2536
  }
2383
- const iterator = fileIterators.get(path3);
2537
+ const iterator = fileIterators.get(path4);
2384
2538
  const result = await iterator.next();
2385
2539
  if (result.done) {
2386
- const reader = fileReaders.get(path3);
2540
+ const reader = fileReaders.get(path4);
2387
2541
  reader.close();
2388
- fileReaders.delete(path3);
2389
- fileIterators.delete(path3);
2542
+ fileReaders.delete(path4);
2543
+ fileIterators.delete(path4);
2390
2544
  return "";
2391
2545
  }
2392
2546
  return result.value;
package/dist/cli.js CHANGED
@@ -4,8 +4,8 @@ import {
4
4
  } from "./chunk-UEFKQRYN.js";
5
5
  import {
6
6
  execute
7
- } from "./chunk-2SNN7MBU.js";
8
- import "./chunk-4ROK7OUQ.js";
7
+ } from "./chunk-TISVDOGD.js";
8
+ import "./chunk-TXL3RV74.js";
9
9
  import "./chunk-HRHAMPOB.js";
10
10
  import "./chunk-4QLTSCDG.js";
11
11
  import "./chunk-GLXVY235.js";
@@ -16,7 +16,7 @@ import "dotenv/config";
16
16
  // package.json
17
17
  var package_default = {
18
18
  name: "dirac-lang",
19
- version: "0.1.44",
19
+ version: "0.1.46",
20
20
  description: "LLM-Augmented Declarative Execution",
21
21
  type: "module",
22
22
  main: "dist/index.js",
@@ -96,7 +96,7 @@ async function main() {
96
96
  process.exit(0);
97
97
  }
98
98
  if (args[0] === "shell") {
99
- const { DiracShell } = await import("./shell-3ZZUMKP3.js");
99
+ const { DiracShell } = await import("./shell-LGNPK4GO.js");
100
100
  const shellConfig = { debug: false };
101
101
  for (let i = 1; i < args.length; i++) {
102
102
  const arg = args[i];
@@ -142,6 +142,9 @@ async function main() {
142
142
  let filePath;
143
143
  let emitXml = false;
144
144
  let configFile;
145
+ if (process.env.DEBUG === "1") {
146
+ config.debug = true;
147
+ }
145
148
  for (let i = 0; i < args.length; i++) {
146
149
  const arg = args[i];
147
150
  if (arg === "--debug") {
package/dist/index.js CHANGED
@@ -2,10 +2,10 @@ import {
2
2
  createLLMAdapter,
3
3
  execute,
4
4
  executeUserCommand
5
- } from "./chunk-2SNN7MBU.js";
5
+ } from "./chunk-TISVDOGD.js";
6
6
  import {
7
7
  integrate
8
- } from "./chunk-4ROK7OUQ.js";
8
+ } from "./chunk-TXL3RV74.js";
9
9
  import {
10
10
  DiracParser
11
11
  } from "./chunk-HRHAMPOB.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  integrate,
3
3
  integrateChildren
4
- } from "./chunk-4ROK7OUQ.js";
4
+ } from "./chunk-TXL3RV74.js";
5
5
  import "./chunk-HRHAMPOB.js";
6
6
  import "./chunk-4QLTSCDG.js";
7
7
  import "./chunk-GLXVY235.js";
@@ -3,7 +3,7 @@ import {
3
3
  listScheduledTasks,
4
4
  stopAllScheduledTasks,
5
5
  stopScheduledTask
6
- } from "./chunk-4ROK7OUQ.js";
6
+ } from "./chunk-TXL3RV74.js";
7
7
  import "./chunk-HRHAMPOB.js";
8
8
  import "./chunk-4QLTSCDG.js";
9
9
  import "./chunk-GLXVY235.js";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-UEFKQRYN.js";
5
5
  import {
6
6
  integrate
7
- } from "./chunk-4ROK7OUQ.js";
7
+ } from "./chunk-TXL3RV74.js";
8
8
  import {
9
9
  DiracParser
10
10
  } from "./chunk-HRHAMPOB.js";
@@ -66,7 +66,7 @@ var DiracShell = class {
66
66
  });
67
67
  this.rl.on("close", () => {
68
68
  this.saveHistory();
69
- import("./schedule-DZ2SPHSX.js").then(({ stopAllScheduledTasks }) => {
69
+ import("./schedule-5HZCREEP.js").then(({ stopAllScheduledTasks }) => {
70
70
  stopAllScheduledTasks();
71
71
  console.log("\nGoodbye!");
72
72
  process.exit(0);
@@ -195,7 +195,7 @@ Commands:
195
195
  :index <path> Index subroutines from directory
196
196
  :search <query> Search indexed subroutines
197
197
  :load <query> Load context (search and import subroutines)
198
- :save <name> <file> Save subroutine to file
198
+ :save <name> [file] Save subroutine (default: ~/.dirac/lib/TIMESTAMP/name.di)
199
199
  :stats Show registry statistics
200
200
  :tasks List all scheduled tasks
201
201
  :stop <name> Stop a scheduled task
@@ -335,13 +335,27 @@ Examples:
335
335
  }
336
336
  break;
337
337
  case "save":
338
- if (args.length < 2) {
339
- console.log("Usage: :save <subroutine-name> <file>");
338
+ if (args.length === 0) {
339
+ console.log("Usage: :save <subroutine-name> [file]");
340
+ console.log(" If file is omitted, saves to ~/.dirac/lib/TIMESTAMP/NAME.di");
341
+ console.log("Examples:");
342
+ console.log(" :save greet # saves to ~/.dirac/lib/TIMESTAMP/greet.di");
343
+ console.log(" :save greet ./my-lib/greet.di # saves to specific file");
344
+ console.log(" :save greet utils # saves to ~/.dirac/lib/utils/greet.di");
340
345
  } else {
341
346
  const subName = args[0];
342
- const fileName = args[1];
347
+ const fileName = args.length > 1 ? args[1] : void 0;
343
348
  try {
344
- const xml = `<save-subroutine name="${subName}" file="${fileName}" format="xml" />`;
349
+ let xml;
350
+ if (fileName) {
351
+ if (fileName.includes("/") || fileName.includes(".di")) {
352
+ xml = `<save-subroutine name="${subName}" file="${fileName}" format="xml" />`;
353
+ } else {
354
+ xml = `<save-subroutine name="${subName}" path="${fileName}" format="xml" />`;
355
+ }
356
+ } else {
357
+ xml = `<save-subroutine name="${subName}" format="xml" />`;
358
+ }
345
359
  const ast = this.xmlParser.parse(xml);
346
360
  await integrate(this.session, ast);
347
361
  if (this.session.output.length > 0) {
@@ -366,7 +380,7 @@ Examples:
366
380
  break;
367
381
  case "tasks":
368
382
  try {
369
- const { listScheduledTasks } = await import("./schedule-DZ2SPHSX.js");
383
+ const { listScheduledTasks } = await import("./schedule-5HZCREEP.js");
370
384
  const tasks = listScheduledTasks();
371
385
  if (tasks.length === 0) {
372
386
  console.log("No scheduled tasks running.");
@@ -385,7 +399,7 @@ Examples:
385
399
  console.log("Usage: :stop <task-name>");
386
400
  } else {
387
401
  try {
388
- const { stopScheduledTask } = await import("./schedule-DZ2SPHSX.js");
402
+ const { stopScheduledTask } = await import("./schedule-5HZCREEP.js");
389
403
  const taskName = args[0];
390
404
  const stopped = stopScheduledTask(taskName);
391
405
  if (stopped) {
@@ -400,7 +414,7 @@ Examples:
400
414
  break;
401
415
  case "stopall":
402
416
  try {
403
- const { stopAllScheduledTasks } = await import("./schedule-DZ2SPHSX.js");
417
+ const { stopAllScheduledTasks } = await import("./schedule-5HZCREEP.js");
404
418
  stopAllScheduledTasks();
405
419
  console.log("All scheduled tasks stopped.");
406
420
  } catch (error) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  integrate
3
- } from "./chunk-4ROK7OUQ.js";
3
+ } from "./chunk-TXL3RV74.js";
4
4
  import {
5
5
  DiracParser
6
6
  } from "./chunk-HRHAMPOB.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dirac-lang",
3
- "version": "0.1.44",
3
+ "version": "0.1.46",
4
4
  "description": "LLM-Augmented Declarative Execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",