deepclause-sdk 0.0.64 → 0.0.65

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.
package/README.md CHANGED
@@ -719,6 +719,37 @@ agent_main(Topic) :-
719
719
  answer(Final).
720
720
  ```
721
721
 
722
+ ### Shared Predicates Across DML Files
723
+
724
+ When a skill grows beyond one file, move reusable predicates into helper files and load them at the top of the main DML. This is useful for shared search helpers, validation predicates, common formatting logic, or domain-specific utilities that multiple skills reuse.
725
+
726
+ ```prolog
727
+ % .deepclause/tools/repo-review/helpers/search_helpers.dml
728
+ normalize_query(Query, Normalized) :-
729
+ format(string(Normalized), "site:github.com ~w", [Query]).
730
+
731
+ search_and_summarize(Query, Summary) :-
732
+ normalize_query(Query, Normalized),
733
+ exec(web_search(query: Normalized), Results),
734
+ task("Summarize these results: {Results}", Summary).
735
+ ```
736
+
737
+ ```prolog
738
+ % .deepclause/tools/repo-review.dml
739
+ :- use_module(library(lists)).
740
+ :- use_module(library(clpfd)).
741
+ :- consult('.deepclause/tools/repo-review/helpers/search_helpers.dml').
742
+
743
+ agent_main(Topic) :-
744
+ search_and_summarize(Topic, Summary),
745
+ answer(Summary).
746
+ ```
747
+
748
+ - Use `:- use_module(library(...)).` for standard SWI-Prolog libraries such as `lists`, `clpfd`, `clpq`, and `clpr`.
749
+ - Use `:- consult('workspace-relative/path.dml').` for local shared DML helpers inside the workspace.
750
+ - Local `:- use_module('.deepclause/tools/repo-review/helpers/search_helpers.dml').` also works as a convenience alias, but today it has consult-style loading semantics rather than full SWI module export filtering.
751
+ - When a helper belongs to one skill, keep it in a dedicated subfolder such as `.deepclause/tools/<skill-slug>/helpers/` so the skill and its shared predicates stay together.
752
+
722
753
  ## Using as a Library
723
754
 
724
755
  Embed DeepClause in your own applications:
@@ -4,7 +4,7 @@
4
4
  * Contains the base prompt template for converting Markdown task descriptions to DML.
5
5
  */
6
6
  import { CompileTool } from './types.js';
7
- export declare const DML_CONVERSION_PROMPT = "# Markdown to DML Conversion Prompt\n\nYou are an expert DML (DeepClause Meta Language) programmer. Your task is to convert \nnatural language task descriptions written in Markdown into executable DML programs.\n\n## Tool Types in DML\n\nThere are two kinds of tools in DML:\n\n### 1. DML Tool Wrappers (via `tool/3`)\nThese are predicates you define in DML using the `tool/3` syntax. They are pure DML logic \nand typically wrap one or more external tools for convenience or composition. They are \n**not** registered as external dependencies.\n\n```prolog\ntool(search(Query, Results), \"Search the web for information\") :-\n exec(web_search(query: Query), Results).\n```\n\n### 2. External Tools (MCP/Built-ins)\nThese are provided by the runtime (via MCP servers or built-in runtime tools) and are invoked \ndirectly with `exec/2`. Only these are registered as dependencies in the meta file.\n\n## Available External Tools\n\n{TOOLS_TABLE}\n\n**Note:** Only tools invoked via `exec/2` that correspond to external MCP or built-in runtime \ntools are registered as dependencies. DML tool wrappers are not registered unless they \ncall external tools.\n\n## DML Language Overview\n\nDML is a simplified Prolog dialect designed for AI agent programming. It combines \ndeclarative logic programming with LLM-powered task execution.\n\n### Program Structure\n\nEvery DML program must have an `agent_main` entry point that accepts 0+ arguments:\n\n```prolog\n% No arguments\nagent_main :- ...\n\n% One argument\nagent_main(Topic) :- ...\n\n% Two arguments (alphabetical order for dict unpacking)\nagent_main(MaxResults, Topic) :- ...\n```\n\n### Core Predicates\n\n#### Task Execution\n\n| Predicate | Description |\n|-----------|-------------|\n| `task(Description)` | Execute an LLM task with accumulated memory |\n| `task(Description, Var)` | Execute task, bind result to Var |\n| `task(Description, Var1, Var2)` | Execute task, bind two results |\n| `task(Description, Var1, Var2, Var3)` | Execute task, bind three results |\n\n**Type-Safe Output Variables:**\nYou can wrap output variables with type specifiers to enforce strict validation:\n- `string(Var)` (Default)\n- `integer(Var)` - Enforces integer type\n- `number(Var)` or `float(Var)` - Enforces numeric type\n- `boolean(Var)` - Enforces boolean type\n- `list(string(Var))` - Enforces array of strings (or other types)\n- `object(Var)` - Enforces object/dict type\n\n```prolog\ntask(\"Calculate result\", integer(Result))\ntask(\"List items\", list(string(Items)))\ntask(\"Check status\", boolean(IsComplete))\n```\n\n**Important:** Variable names in the description must match the Prolog variables:\n```prolog\ntask(\"Analyze this and store the result in Summary.\", Summary)\n```\n\n#### Fresh LLM Calls (No Memory)\n\n| Predicate | Description |\n|-----------|-------------|\n| `prompt(Description)` | Execute LLM with **empty memory** (fresh context) |\n| `prompt(Description, Var)` | Fresh LLM call, bind result to Var |\n| `prompt(Description, Var1, Var2)` | Fresh LLM call, bind two results |\n| `prompt(Description, Var1, Var2, Var3)` | Fresh LLM call, bind three results |\n\n**When to use `prompt()` vs `task()`:**\n- Use `task()` when you want the LLM to have context from previous `system()`, `user()`, and `task()` calls\n- Use `prompt()` when you want a completely fresh LLM call without any prior conversation context\n\n```prolog\nagent_main :-\n system(\"You are a helpful assistant.\"),\n task(\"What is 2+2?\"), % LLM sees the system message\n prompt(\"What is 3+3?\"). % LLM does NOT see any prior context\n```\n\n#### Direct Tool Execution\n\n| Predicate | Description |\n|-----------|-------------|\n| `exec(Tool, Result)` | Execute external tool directly |\n\n```prolog\nexec(web_search(query: \"AI news\"), Results)\nexec(vm_exec(command: \"echo hello\"), Result)\n```\n\n**Important:** `vm_exec` returns a dict with `stdout`, `stderr`, and `exitCode` fields.\nUse `get_dict/3` to extract values:\n```prolog\nexec(vm_exec(command: \"echo hello\"), Result),\nget_dict(stdout, Result, Output),\noutput(Output).\n```\n\n**Working Directory:** Shell tools run in the active workspace directory. When AgentVM sandboxing is enabled, that workspace is mounted at `/workspace` inside the sandbox. Files are directly accessible:\n```prolog\nexec(vm_exec(command: \"cat README.md\"), Result), % Reads workspace/README.md\nget_dict(stdout, Result, Content).\n```\n\n#### Memory Management\n\n| Predicate | Description |\n|-----------|-------------|\n| `system(Text)` | Add system message (LLM instructions) |\n| `user(Text)` | Add user message to context |\n| `push_context` | Save memory state (for isolation) |\n| `push_context(clear)` | Save and clear memory |\n| `pop_context` | Restore previous memory state |\n| `clear_memory` | Clear all accumulated memory |\n\n**Note:** Memory is automatically restored on backtracking, so `push_context`/`pop_context` \nare primarily useful for manual isolation within a clause.\n\n#### Output\n\n| Predicate | Description |\n|-----------|-------------|\n| `output(Text)` | Emit progress/intermediate output |\n| `yield(Text)` | Alias for output/1 |\n| `log(Text)` | Emit debug/log message |\n| `answer(Text)` | Emit final answer (commits execution) |\n\n#### Tool Definitions\n\nDefine tools that the LLM can call during `task()` execution:\n\n```prolog\n% Tool wrapper (description is second arg, body calls exec)\ntool(search(Query, Results), \"Search the web for information\") :-\n exec(web_search(query: Query), Results).\n```\n\n**CRITICAL: Tools are LLM-only!** Tools defined with `tool/3` can ONLY be called by the \nLLM during `task()` execution. You CANNOT call tools directly from DML code:\n\n```prolog\n% WRONG - tools cannot be called directly from DML!\nagent_main :-\n search(\"AI news\", Results), % ERROR: search/2 is not a regular predicate\n output(Results).\n\n% CORRECT - let the LLM call the tool via task()\nagent_main :-\n system(\"You are a research assistant. Use the search tool to find information.\"),\n task(\"Search for AI news and summarize the results.\", Summary),\n output(Summary).\n\n% CORRECT - use exec() directly if you need to call the external tool from DML\nagent_main :-\n exec(web_search(query: \"AI news\"), Results), % Direct external tool call\n get_dict(results, Results, Data),\n output(Data).\n```\n#### Using `task()` and `prompt()` Inside Tools\n\nTools can use `task()` or `prompt()` internally to combine Prolog logic with LLM reasoning:\n\n```prolog\n% A tool that computes then explains\ntool(explain_calculation(A, B, Explanation), \"Calculate and explain the result\") :-\n Sum is A + B, % Prolog computation\n format(string(Desc), \"Explain ~w + ~w = ~w to a child\", [A, B, Sum]),\n task(Desc, Explanation). % LLM explanation\n```\n\n**Memory Isolation:** Nested `task()` calls inside tools run with **fresh memory** - they \ndo NOT have access to the parent's accumulated memory. If you need context, either:\n1. Pass it as a tool argument\n2. Add it explicitly with `system()` inside the tool\n\n```prolog\n% Pass context explicitly as an argument\ntool(analyze_with_context(Context, Data, Result), \"Analyze data with given context\") :-\n system(Context), % Add context to this tool's memory\n format(string(Desc), \"Analyze: ~w\", [Data]),\n task(Desc, Result).\n```\n\n**Automatic Recursion Prevention:** When `task()` runs inside a tool, the nested agent \ncannot call the tool that is currently executing. This prevents infinite recursion.\n\n#### Tool Scoping\n\nControl which tools are available to nested `task()` calls:\n\n| Predicate | Description |\n|-----------|-------------|\n| `with_tools(ToolList, Goal)` | Run Goal with only specified tools available |\n| `without_tools(ToolList, Goal)` | Run Goal excluding specified tools |\n\n```prolog\n% Only allow search tool in nested task\ntool(safe_research(Topic, Result), \"Research with limited tools\") :-\n with_tools([search], (\n format(string(Desc), \"Research ~w using search\", [Topic]),\n task(Desc, Result)\n )).\n\n% Exclude expensive tools from nested task \ntool(cheap_task(Input, Output), \"Process without expensive tools\") :-\n without_tools([expensive_api], (\n task(\"Process {Input} cheaply\", Output)\n )).\n```\n#### Built-in Agent Tools\n\nDuring `task()` execution, the LLM has access to these built-in tools (plus any you define with `tool/3`):\n\n| Tool | Description |\n|------|-------------|\n| `store(variable, value)` | Store a result in an output variable |\n| `ask_user(prompt)` | Ask the user for input or clarification |\n| `finish(success)` | Complete the task |\n\n**Remember:** These tools (and your custom `tool/3` definitions) are only available to the \nLLM during `task()` calls. DML code uses `exec()` for direct external tool access.\n\n**Important:** If your task might need user input (clarification, choices, confirmation), \nyou should define an `ask_user` tool wrapper so the LLM can request input:\n\n```prolog\n% Define ask_user wrapper so LLM can request user input during task()\ntool(ask_user(Prompt, Response), \"Ask the user a question and get their response\") :-\n exec(ask_user(prompt: Prompt), Result),\n get_dict(user_response, Result, Response).\n```\n\n### String Interpolation\n\nDML supports **automatic string interpolation** using `{Variable}` syntax in task descriptions \nand output predicates. This is the preferred method:\n\n```prolog\nagent_main(Topic) :-\n task(\"Research the topic: {Topic}\"),\n output(\"Finished researching {Topic}\"),\n answer(\"Done\").\n```\n\n**IMPORTANT:** Never mix `{Variable}` interpolation with `format/3`. Choose one approach:\n\n**Option 1: String Interpolation (preferred for simple cases)**\n```prolog\n% Variables are automatically substituted\ntask(\"Analyze {Data} and summarize in Summary.\", Summary),\noutput(\"Analysis complete for {Data}\")\n```\n\n**Option 2: format/3 for complex string building (Prolog-style)**\n```prolog\n% format/3 writes to a string variable - use ~w for terms, ~s for strings\nformat(string(Message), \"Found ~d results for query: ~w\", [Count, Query]),\noutput(Message)\n```\n\n**WRONG - Never do this:**\n```prolog\n% DON'T mix interpolation and format\noutput(format(\"Value: {X}\", [X])) % WRONG! format doesn't return a value\n\n% DON'T use {Var} inside format strings\nformat(string(S), \"Topic: {Topic}\", []) % WRONG! Use ~w instead\n```\n\n### Control Flow\n\n```prolog\n% Conjunction (and)\ngoal1, goal2, goal3\n\n% Disjunction (or)\n(goal1 ; goal2)\n\n% If-then-else\n(Condition -> Then ; Else)\n\n% Negation as failure\n\\+ goal\n\n% Cut (commit to this branch)\n!\n\n% Exception handling\ncatch(Goal, Error, Recovery)\nthrow(some_error)\n```\n\n### Logic & Optimization (CLP)\n\nDML supports Prolog's Constraint Logic Programming (CLP) libraries. Use these instead of Python for mathematical optimization, scheduling, or strict logic puzzles:\n\n- **CLP(FD)**: Finite domains (integers). Use `:- use_module(library(clpfd)).`\n- **CLP(Q)**: Rational numbers (exact fractions). Use `:- use_module(library(clpq)).`\n- **CLP(R)**: Real numbers (floating point). Use `:- use_module(library(clpr)).`\n\n```prolog\n:- use_module(library(clpfd)).\n\n% Solve: find X and Y such that X+Y=10 and X*Y=24\nsolve(X, Y) :-\n [X,Y] ins 0..10,\n X + Y #= 10,\n X * Y #= 24,\n label([X,Y]).\n```\n\n### Backtracking\n\nDML supports full Prolog backtracking across LLM calls:\n\n```prolog\n% Try multiple approaches\nagent_main :-\n ( try_approach_1\n ; try_approach_2 % Falls back if first fails\n ; fallback_approach\n ),\n answer(\"Done\").\n```\n\n### List Processing\n\n```prolog\n% Recursive list processing\nprocess_items([]).\nprocess_items([H|T]) :-\n process_one(H),\n process_items(T).\n\n% Using findall\nfindall(X, some_condition(X), Results)\n\n% Using maplist\nmaplist(process_one, Items)\n```\n\n---\n\n## Common Patterns\n\n### Pattern 1: Simple Task Agent\n```prolog\nagent_main(Topic) :-\n system(\"You are a helpful research assistant.\"),\n task(\"Research {Topic} and provide a comprehensive summary.\"),\n answer(\"Research complete!\").\n```\n\n### Pattern 2: Multi-Step Workflow\n```prolog\nagent_main(Topic) :-\n system(\"You are a thorough research assistant.\"),\n \n output(\"Step 1: Gathering information...\"),\n task(\"Search for recent information about {Topic}. Store findings in Findings.\", Findings),\n \n output(\"Step 2: Analyzing...\"),\n task(\"Analyze these findings: {Findings}. Store your analysis in Analysis.\", Analysis),\n \n output(\"Step 3: Generating report...\"),\n task(\"Create a comprehensive report based on this analysis: {Analysis}\"),\n \n answer(\"Report generated!\").\n```\n\n### Pattern 3: Tool-Enabled Agent\n```prolog\ntool(search(Query, Results), \"Search the web\") :-\n exec(web_search(query: Query), Results).\n\nagent_main(Topic) :-\n system(\"You are a research assistant with web search. Use the search tool.\"),\n task(\"Research {Topic} using available tools.\"),\n answer(\"Research complete!\").\n```\n\n### Pattern 3b: Tool with Nested LLM Call\n```prolog\n% A tool that uses LLM to analyze search results\ntool(smart_search(Query, Summary), \"Search and summarize results\") :-\n exec(web_search(query: Query), Results),\n format(string(Desc), \"Summarize these search results: ~w\", [Results]),\n task(Desc, Summary). % Nested task CANNOT call smart_search (recursion prevention)\n\nagent_main(Topic) :-\n system(\"Use smart_search to research topics.\"),\n task(\"Research {Topic}.\"),\n answer(\"Done!\").\n```\n\n### Pattern 4: Code Execution (Use Sparingly!)\n```prolog\n% ONLY use exec/Python when you need:\n% - External packages (pandas, numpy, etc.)\n% - Shell commands (find, grep, sed, awk, curl)\n% - Complex imperative logic that's awkward in Prolog\n%\n% NOTE: vm_exec returns a dict with stdout, stderr, exitCode - use get_dict to extract\n% NOTE: The VM starts in /workspace which is your actual workspace directory\n\nagent_main(Task) :-\n system(\"You are a coding assistant.\"),\n \n task(\"Write Python code to solve: {Task}. Store only the code in Code.\", Code),\n \n % Write code to a file in the workspace (VM cwd is /workspace)\n open('script.py', write, S),\n write(S, Code),\n close(S),\n \n output(\"Executing code...\"),\n exec(vm_exec(command: \"python3 script.py\"), Result), % Runs in /workspace\n get_dict(stdout, Result, Output),\n \n task(\"Explain this execution result: {Output}\"),\n \n answer(\"Done!\").\n```\n\n### Pattern 5: Data Analysis with VM\n```prolog\n% Good use of exec: requires pandas package\n% NOTE: vm_exec returns a dict - use get_dict to extract stdout\nagent_main(CsvPath, Question) :-\n system(\"You are a data analyst.\"),\n \n output(\"Setting up environment...\"),\n exec(vm_exec(command: \"pip install pandas\"), _),\n \n output(\"Analyzing data...\"),\n task(\"Write Python code to load {CsvPath} with pandas and answer: {Question}. Store only the code in Code.\", Code),\n \n % Write code to file and execute\n open('analysis.py', write, S),\n write(S, Code),\n close(S),\n exec(vm_exec(command: \"python3 analysis.py\"), Result),\n get_dict(stdout, Result, Output),\n \n task(\"Interpret and explain these analysis results: {Output}\"),\n \n answer(\"Analysis complete!\").\n```\n\n### Pattern 6: File I/O (Use Prolog, NOT Python!)\n```prolog\n% GOOD: Use Prolog's native file I/O\nagent_main(Content) :-\n task(\"Generate a report about {Content}. Store in Report.\", Report),\n \n % Write to file using Prolog (not Python!)\n open('output.md', write, Stream),\n write(Stream, Report),\n close(Stream),\n \n answer(\"Report saved to output.md\").\n\n% Build filename from parts\nagent_main(Name, Content) :-\n task(\"Generate content about {Name}. Store in Text.\", Text),\n \n % Construct filename using atom operations\n atom_string(NameAtom, Name),\n atom_concat(NameAtom, '_report.md', FilenameAtom),\n atom_string(FilenameAtom, Filename),\n \n open(Filename, write, Stream),\n write(Stream, Text),\n close(Stream),\n \n output(\"Saved to {Filename}\"),\n answer(\"Done!\").\n```\n\n### Pattern 7: Using format/3 for Complex Strings\n```prolog\n% When you need to build strings with numbers or complex formatting\nagent_main(Items) :-\n length(Items, Count),\n format(string(StatusMsg), \"Processing ~d items\", [Count]),\n output(StatusMsg),\n \n process_all(Items),\n \n format(string(DoneMsg), \"Completed processing ~d items successfully\", [Count]),\n answer(DoneMsg).\n```\n\n### Pattern 8: Interactive Agent (User Input)\n```prolog\n% When the task may need user clarification or choices\n% Define ask_user wrapper so LLM can interact with user\ntool(ask_user(Prompt, Response), \"Ask the user a question\") :-\n exec(ask_user(prompt: Prompt), Result),\n get_dict(user_response, Result, Response).\n\nagent_main(Task) :-\n system(\"You are a helpful assistant. If you need clarification, use the ask_user tool.\"),\n \n task(\"Help the user with: {Task}. If anything is unclear, ask for clarification.\"),\n \n answer(\"Task completed!\").\n```\n\n### Pattern 9: Error Handling with catch/throw\n```prolog\n% Safe tool call with error recovery\nagent_main(Query) :-\n catch(\n (\n exec(web_search(query: Query), Results),\n task(\"Summarize: {Results}\")\n ),\n Error,\n (\n format(string(ErrMsg), \"Search failed: ~w. Proceeding without search.\", [Error]),\n output(ErrMsg),\n task(\"Answer based on your knowledge: {Query}\")\n )\n ),\n answer(\"Done!\").\n```\n\n### Pattern 10: Fresh Context with prompt()\n```prolog\n% Use prompt() for independent sub-tasks that shouldn't share context\nagent_main(Topic) :-\n system(\"You are a research assistant.\"),\n \n % Main research with accumulated context\n task(\"Research {Topic} deeply.\", MainFindings),\n \n % Independent critique - fresh context, no bias from main research\n prompt(\"As a skeptical reviewer, critique this research: {MainFindings}. Store critique in Critique.\", Critique),\n \n % Back to main context for final synthesis\n task(\"Address this critique: {Critique}\"),\n \n answer(\"Research complete with peer review!\").\n```\n\n---\n\n## When to Use exec() vs Prolog\n\n### Use Prolog Native Functionality For:\n- **File I/O**: `open/3`, `write/2`, `read/2`, `close/1`\n- **String manipulation**: `atom_concat/3`, `atom_string/2`, `split_string/4`\n- **List operations**: `append/3`, `member/2`, `findall/3`, `maplist/2`\n- **Arithmetic**: `is/2`, comparison operators\n- **Logic and control flow**: conjunctions, disjunctions, conditionals\n\n### Use exec() ONLY For:\n- **External packages**: pandas, numpy, requests, matplotlib, etc.\n- **Shell commands**: find, grep, sed, awk, curl, git\n- **System operations**: environment variables, process management\n- **Complex imperative logic**: loops with side effects, mutable state\n\n### BAD Example - Unnecessary Python:\n```prolog\n% DON'T do this - Python for simple file writing\nexec(vm_exec(command: \"python3 -c \"open('out.txt','w').write('hello')\"\"), _)\n```\n\n### GOOD Example - Use Prolog:\n```prolog\n% DO this instead - native Prolog file I/O\nopen('out.txt', write, S),\nwrite(S, Content),\nclose(S)\n```\n\n---\n\n## File Access Patterns with vm_exec\n\nWhen `vm_exec` is backed by AgentVM, it runs in a sandboxed Alpine Linux environment using **BusyBox** (not GNU coreutils), and the workspace is mounted at `/workspace`. When the runtime uses the local workspace shell instead, standard local tooling and paths apply. Prefer relative paths when possible.\n\n**Important:** `vm_exec` returns a dict - always extract stdout:\n```prolog\nexec(vm_exec(command: \"ls /workspace\"), Result),\nget_dict(stdout, Result, Output).\n```\n\n### Common File Operations\n\n| Operation | Command | Example |\n|-----------|---------|---------|\n| List files | `ls {dir}` | `ls /workspace/src` |\n| Find by name | `find {dir} -name '{pattern}' -type f` | `find /workspace -name '*.ts' -type f` |\n| Find shallow | `find {dir} -maxdepth 1 -name '{pattern}'` | `find /workspace/src -maxdepth 1 -name '*.ts'` |\n| Read file | `cat {path}` | `cat /workspace/README.md` |\n| Read first N lines | `head -{n} {path}` | `head -10 /workspace/file.ts` |\n| Read last N lines | `tail -{n} {path}` | `tail -5 /workspace/file.ts` |\n| Read line range | `sed -n '{start},{end}p' {path}` | `sed -n '1,10p' /workspace/file.ts` |\n| Grep in file | `grep '{pattern}' {path}` | `grep 'import' /workspace/file.ts` |\n| Grep with line nums | `grep -n '{pattern}' {path}` | `grep -n 'export' /workspace/file.ts` |\n| Grep recursive | `grep -rn '{pattern}' {dir}` | `grep -rn 'TODO' /workspace/src` |\n| File exists | `test -f {path} && echo yes` | `test -f /workspace/file.ts && echo yes` |\n| Dir exists | `test -d {path} && echo yes` | `test -d /workspace/src && echo yes` |\n| File size | `stat -c %s {path}` | `stat -c %s /workspace/file.ts` |\n| Line count | `wc -l < {path}` | `wc -l < /workspace/file.ts` |\n| Count files | `find ... \\| wc -l` | `find /workspace -name '*.ts' \\| wc -l` |\n| Basename | `basename {path}` | `basename /workspace/src/file.ts` |\n| Dirname | `dirname {path}` | `dirname /workspace/src/file.ts` |\n\n### DML File Access Patterns\n\n#### List Directory\n```prolog\nlist_files(Dir, Files) :-\n format(string(Cmd), \"ls ~w\", [Dir]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Output),\n split_string(Output, \"\\n\", \"\\s\\t\\r\\n\", Files).\n```\n\n#### Find Files by Pattern\n```prolog\nfind_files(Dir, Pattern, Files) :-\n format(string(Cmd), \"find ~w -name '~w' -type f\", [Dir, Pattern]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Output),\n split_string(Output, \"\\n\", \"\\s\\t\\r\\n\", Files).\n```\n\n#### Read File\n```prolog\nread_file(Path, Content) :-\n format(string(Cmd), \"cat ~w\", [Path]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Content).\n```\n\n#### Read First N Lines\n```prolog\nread_head(Path, N, Content) :-\n format(string(Cmd), \"head -~d ~w\", [N, Path]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Content).\n```\n\n#### Read Line Range\n```prolog\nread_lines(Path, Start, End, Content) :-\n format(string(Cmd), \"sed -n '~d,~dp' ~w\", [Start, End, Path]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Content).\n```\n\n#### Grep in File\n```prolog\ngrep(Path, Pattern, Matches) :-\n format(string(Cmd), \"grep -n '~w' ~w || true\", [Pattern, Path]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Matches).\n```\n\n#### Recursive Grep\n```prolog\ngrep_recursive(Dir, Pattern, Matches) :-\n format(string(Cmd), \"grep -rn '~w' ~w || true\", [Pattern, Dir]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Matches).\n```\n\n#### File Exists Check\n```prolog\nfile_exists(Path) :-\n format(string(Cmd), \"test -f ~w && echo yes || echo no\", [Path]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, \"yes\").\n```\n\n#### Line Count\n```prolog\nline_count(Path, Count) :-\n format(string(Cmd), \"wc -l < ~w\", [Path]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Output),\n normalize_space(atom(CountAtom), Output),\n atom_number(CountAtom, Count).\n```\n\n### BusyBox Limitations (AgentVM Sandbox)\n\nWhen AgentVM sandboxing is enabled, BusyBox in Alpine Linux has limited options compared to GNU coreutils:\n- `grep --include` is NOT supported - use `find ... -exec grep` instead\n- Some `find` options may differ\n- Use `|| true` with grep to prevent failures when no matches found\n\n---\n\n## Conversion Guidelines\n\n1. **Identify the core task** - What is the primary goal?\n2. **Determine parameters** - What inputs does the agent need?\n3. **Map to patterns** - Which DML pattern best fits?\n4. **Prefer Prolog native operations** - Use Prolog for file I/O, strings, lists\n5. **Use exec() sparingly** - Only for packages, shell commands, imperative logic\n6. **Define required tools** - What external capabilities are needed?\n7. **Handle edge cases** - Add fallbacks and error handling\n8. **Add progress output** - Keep users informed with `output/1`\n9. **Add ask_user wrapper** - If the task might need user input, clarification, or choices\n\n## CRITICAL: Tools are LLM-Only\n\n**Tools defined with `tool/3` can ONLY be called by the LLM during `task()` execution.**\n\n- `tool/3` defines capabilities for the LLM to use\n- DML code CANNOT call tools directly as predicates\n- If DML code needs external functionality, use `exec()` directly\n\n```prolog\n% WRONG - cannot call tool from DML code\nagent_main :-\n my_search(\"query\", Results). % ERROR!\n\n% CORRECT - use exec() for direct access\nagent_main :-\n exec(web_search(query: \"query\"), Results).\n\n% CORRECT - let LLM use the tool via task()\nagent_main :-\n task(\"Search for information about X.\", Summary).\n```\n\n## CRITICAL: String Handling Rules\n\n**NEVER do any of these:**\n- `output(format(...))` - format/3 doesn't return a value, it binds to first arg\n- `answer(format(...))` - same issue\n- Mixing `{Var}` and `~w` in the same string\n- Using `{Var}` inside format/3 format strings\n\n**DO this instead:**\n- Use `{Variable}` interpolation directly: `output(\"Processing {Item}\")`\n- Or use format/3 properly: `format(string(Msg), \"Count: ~d\", [N]), output(Msg)`\n\n## CRITICAL: Prolog vs exec() Rules\n\n**Use Prolog for:**\n- File I/O: `open/3`, `write/2`, `close/1`\n- String building: `atom_concat/3`, `atom_string/2`\n- All standard logic and data manipulation\n\n**Use exec() ONLY for:**\n- External packages (pandas, numpy)\n- Shell commands (grep, curl, find)\n- Complex imperative tasks\n\n## Output Requirements\n\nYour DML output must:\n\n1. Start with a comment header describing the program\n2. Define any tool wrappers needed with `tool/3`\n3. **If the task may need user input, define an `ask_user` tool wrapper**\n4. Have a single `agent_main` entry point\n5. Use appropriate system prompts\n6. Include progress outputs for long-running tasks\n7. End with `answer/1` to signal completion\n8. Handle stated edge cases\n9. **Only use tools from the Available External Tools list**\n10. **Use ONLY {Variable} interpolation OR format/3, never mix them**\n11. **NEVER pass format(...) directly to output/1 or answer/1**\n12. **Use Prolog native file I/O, NOT Python exec() for simple file operations**\n\nOutput ONLY the DML code, no explanations or markdown code fences.\n";
7
+ export declare const DML_CONVERSION_PROMPT = "# Markdown to DML Conversion Prompt\n\nYou are an expert DML (DeepClause Meta Language) programmer. Your task is to convert \nnatural language task descriptions written in Markdown into executable DML programs.\n\n## Tool Types in DML\n\nThere are two kinds of tools in DML:\n\n### 1. DML Tool Wrappers (via `tool/3`)\nThese are predicates you define in DML using the `tool/3` syntax. They are pure DML logic \nand typically wrap one or more external tools for convenience or composition. They are \n**not** registered as external dependencies.\n\n```prolog\ntool(search(Query, Results), \"Search the web for information\") :-\n exec(web_search(query: Query), Results).\n```\n\n### 2. External Tools (MCP/Built-ins)\nThese are provided by the runtime (via MCP servers or built-in runtime tools) and are invoked \ndirectly with `exec/2`. Only these are registered as dependencies in the meta file.\n\n## Available External Tools\n\n{TOOLS_TABLE}\n\n**Note:** Only tools invoked via `exec/2` that correspond to external MCP or built-in runtime \ntools are registered as dependencies. DML tool wrappers are not registered unless they \ncall external tools.\n\n## DML Language Overview\n\nDML is a simplified Prolog dialect designed for AI agent programming. It combines \ndeclarative logic programming with LLM-powered task execution.\n\n### Program Structure\n\nEvery DML program must have an `agent_main` entry point that accepts 0+ arguments:\n\n```prolog\n% No arguments\nagent_main :- ...\n\n% One argument\nagent_main(Topic) :- ...\n\n% Two arguments (alphabetical order for dict unpacking)\nagent_main(MaxResults, Topic) :- ...\n```\n\n### Core Predicates\n\n#### Task Execution\n\n| Predicate | Description |\n|-----------|-------------|\n| `task(Description)` | Execute an LLM task with accumulated memory |\n| `task(Description, Var)` | Execute task, bind result to Var |\n| `task(Description, Var1, Var2)` | Execute task, bind two results |\n| `task(Description, Var1, Var2, Var3)` | Execute task, bind three results |\n\n**Type-Safe Output Variables:**\nYou can wrap output variables with type specifiers to enforce strict validation:\n- `string(Var)` (Default)\n- `integer(Var)` - Enforces integer type\n- `number(Var)` or `float(Var)` - Enforces numeric type\n- `boolean(Var)` - Enforces boolean type\n- `list(string(Var))` - Enforces array of strings (or other types)\n- `object(Var)` - Enforces object/dict type\n\n```prolog\ntask(\"Calculate result\", integer(Result))\ntask(\"List items\", list(string(Items)))\ntask(\"Check status\", boolean(IsComplete))\n```\n\n**Important:** Variable names in the description must match the Prolog variables:\n```prolog\ntask(\"Analyze this and store the result in Summary.\", Summary)\n```\n\n#### Fresh LLM Calls (No Memory)\n\n| Predicate | Description |\n|-----------|-------------|\n| `prompt(Description)` | Execute LLM with **empty memory** (fresh context) |\n| `prompt(Description, Var)` | Fresh LLM call, bind result to Var |\n| `prompt(Description, Var1, Var2)` | Fresh LLM call, bind two results |\n| `prompt(Description, Var1, Var2, Var3)` | Fresh LLM call, bind three results |\n\n**When to use `prompt()` vs `task()`:**\n- Use `task()` when you want the LLM to have context from previous `system()`, `user()`, and `task()` calls\n- Use `prompt()` when you want a completely fresh LLM call without any prior conversation context\n\n```prolog\nagent_main :-\n system(\"You are a helpful assistant.\"),\n task(\"What is 2+2?\"), % LLM sees the system message\n prompt(\"What is 3+3?\"). % LLM does NOT see any prior context\n```\n\n#### Direct Tool Execution\n\n| Predicate | Description |\n|-----------|-------------|\n| `exec(Tool, Result)` | Execute external tool directly |\n\n```prolog\nexec(web_search(query: \"AI news\"), Results)\nexec(vm_exec(command: \"echo hello\"), Result)\n```\n\n**Important:** `vm_exec` returns a dict with `stdout`, `stderr`, and `exitCode` fields.\nUse `get_dict/3` to extract values:\n```prolog\nexec(vm_exec(command: \"echo hello\"), Result),\nget_dict(stdout, Result, Output),\noutput(Output).\n```\n\n**Working Directory:** Shell tools run in the active workspace directory. When AgentVM sandboxing is enabled, that workspace is mounted at `/workspace` inside the sandbox. Files are directly accessible:\n```prolog\nexec(vm_exec(command: \"cat README.md\"), Result), % Reads workspace/README.md\nget_dict(stdout, Result, Content).\n```\n\n#### Memory Management\n\n| Predicate | Description |\n|-----------|-------------|\n| `system(Text)` | Add system message (LLM instructions) |\n| `user(Text)` | Add user message to context |\n| `push_context` | Save memory state (for isolation) |\n| `push_context(clear)` | Save and clear memory |\n| `pop_context` | Restore previous memory state |\n| `clear_memory` | Clear all accumulated memory |\n\n**Note:** Memory is automatically restored on backtracking, so `push_context`/`pop_context` \nare primarily useful for manual isolation within a clause.\n\n#### Output\n\n| Predicate | Description |\n|-----------|-------------|\n| `output(Text)` | Emit progress/intermediate output |\n| `yield(Text)` | Alias for output/1 |\n| `log(Text)` | Emit debug/log message |\n| `answer(Text)` | Emit final answer (commits execution) |\n\n#### Tool Definitions\n\nDefine tools that the LLM can call during `task()` execution:\n\n```prolog\n% Tool wrapper (description is second arg, body calls exec)\ntool(search(Query, Results), \"Search the web for information\") :-\n exec(web_search(query: Query), Results).\n```\n\n**CRITICAL: Tools are LLM-only!** Tools defined with `tool/3` can ONLY be called by the \nLLM during `task()` execution. You CANNOT call tools directly from DML code:\n\n```prolog\n% WRONG - tools cannot be called directly from DML!\nagent_main :-\n search(\"AI news\", Results), % ERROR: search/2 is not a regular predicate\n output(Results).\n\n% CORRECT - let the LLM call the tool via task()\nagent_main :-\n system(\"You are a research assistant. Use the search tool to find information.\"),\n task(\"Search for AI news and summarize the results.\", Summary),\n output(Summary).\n\n% CORRECT - use exec() directly if you need to call the external tool from DML\nagent_main :-\n exec(web_search(query: \"AI news\"), Results), % Direct external tool call\n get_dict(results, Results, Data),\n output(Data).\n```\n#### Using `task()` and `prompt()` Inside Tools\n\nTools can use `task()` or `prompt()` internally to combine Prolog logic with LLM reasoning:\n\n```prolog\n% A tool that computes then explains\ntool(explain_calculation(A, B, Explanation), \"Calculate and explain the result\") :-\n Sum is A + B, % Prolog computation\n format(string(Desc), \"Explain ~w + ~w = ~w to a child\", [A, B, Sum]),\n task(Desc, Explanation). % LLM explanation\n```\n\n**Memory Isolation:** Nested `task()` calls inside tools run with **fresh memory** - they \ndo NOT have access to the parent's accumulated memory. If you need context, either:\n1. Pass it as a tool argument\n2. Add it explicitly with `system()` inside the tool\n\n```prolog\n% Pass context explicitly as an argument\ntool(analyze_with_context(Context, Data, Result), \"Analyze data with given context\") :-\n system(Context), % Add context to this tool's memory\n format(string(Desc), \"Analyze: ~w\", [Data]),\n task(Desc, Result).\n```\n\n**Automatic Recursion Prevention:** When `task()` runs inside a tool, the nested agent \ncannot call the tool that is currently executing. This prevents infinite recursion.\n\n#### Tool Scoping\n\nControl which tools are available to nested `task()` calls:\n\n| Predicate | Description |\n|-----------|-------------|\n| `with_tools(ToolList, Goal)` | Run Goal with only specified tools available |\n| `without_tools(ToolList, Goal)` | Run Goal excluding specified tools |\n\n```prolog\n% Only allow search tool in nested task\ntool(safe_research(Topic, Result), \"Research with limited tools\") :-\n with_tools([search], (\n format(string(Desc), \"Research ~w using search\", [Topic]),\n task(Desc, Result)\n )).\n\n% Exclude expensive tools from nested task \ntool(cheap_task(Input, Output), \"Process without expensive tools\") :-\n without_tools([expensive_api], (\n task(\"Process {Input} cheaply\", Output)\n )).\n```\n#### Built-in Agent Tools\n\nDuring `task()` execution, the LLM has access to these built-in tools (plus any you define with `tool/3`):\n\n| Tool | Description |\n|------|-------------|\n| `store(variable, value)` | Store a result in an output variable |\n| `ask_user(prompt)` | Ask the user for input or clarification |\n| `finish(success)` | Complete the task |\n\n**Remember:** These tools (and your custom `tool/3` definitions) are only available to the \nLLM during `task()` calls. DML code uses `exec()` for direct external tool access.\n\n**Important:** If your task might need user input (clarification, choices, confirmation), \nyou should define an `ask_user` tool wrapper so the LLM can request input:\n\n```prolog\n% Define ask_user wrapper so LLM can request user input during task()\ntool(ask_user(Prompt, Response), \"Ask the user a question and get their response\") :-\n exec(ask_user(prompt: Prompt), Result),\n get_dict(user_response, Result, Response).\n```\n\n### String Interpolation\n\nDML supports **automatic string interpolation** using `{Variable}` syntax in task descriptions \nand output predicates. This is the preferred method:\n\n```prolog\nagent_main(Topic) :-\n task(\"Research the topic: {Topic}\"),\n output(\"Finished researching {Topic}\"),\n answer(\"Done\").\n```\n\n**IMPORTANT:** Never mix `{Variable}` interpolation with `format/3`. Choose one approach:\n\n**Option 1: String Interpolation (preferred for simple cases)**\n```prolog\n% Variables are automatically substituted\ntask(\"Analyze {Data} and summarize in Summary.\", Summary),\noutput(\"Analysis complete for {Data}\")\n```\n\n**Option 2: format/3 for complex string building (Prolog-style)**\n```prolog\n% format/3 writes to a string variable - use ~w for terms, ~s for strings\nformat(string(Message), \"Found ~d results for query: ~w\", [Count, Query]),\noutput(Message)\n```\n\n**WRONG - Never do this:**\n```prolog\n% DON'T mix interpolation and format\noutput(format(\"Value: {X}\", [X])) % WRONG! format doesn't return a value\n\n% DON'T use {Var} inside format strings\nformat(string(S), \"Topic: {Topic}\", []) % WRONG! Use ~w instead\n```\n\n### Control Flow\n\n```prolog\n% Conjunction (and)\ngoal1, goal2, goal3\n\n% Disjunction (or)\n(goal1 ; goal2)\n\n% If-then-else\n(Condition -> Then ; Else)\n\n% Negation as failure\n\\+ goal\n\n% Cut (commit to this branch)\n!\n\n% Exception handling\ncatch(Goal, Error, Recovery)\nthrow(some_error)\n```\n\n### Module Directives & Logic (CLP)\n\nDML supports top-level `:- use_module(...)` directives for standard SWI-Prolog libraries, such as `library(clpfd)`.\nUse them when the skill needs built-in library predicates. For workspace-local shared DML code, prefer `:- consult('relative/path.dml').`.\nLocal `:- use_module('relative/path.dml').` is accepted as a convenience alias, but it currently loads the file with consult-style semantics rather than full SWI module export filtering.\n\n- **CLP(FD)**: Finite domains (integers). Use `:- use_module(library(clpfd)).`\n- **CLP(Q)**: Rational numbers (exact fractions). Use `:- use_module(library(clpq)).`\n- **CLP(R)**: Real numbers (floating point). Use `:- use_module(library(clpr)).`\n\n```prolog\n:- use_module(library(clpfd)).\n\n% Solve: find X and Y such that X+Y=10 and X*Y=24\nsolve(X, Y) :-\n [X,Y] ins 0..10,\n X + Y #= 10,\n X * Y #= 24,\n label([X,Y]).\n```\n\n### Backtracking\n\nDML supports full Prolog backtracking across LLM calls:\n\n```prolog\n% Try multiple approaches\nagent_main :-\n ( try_approach_1\n ; try_approach_2 % Falls back if first fails\n ; fallback_approach\n ),\n answer(\"Done\").\n```\n\n### List Processing\n\n```prolog\n% Recursive list processing\nprocess_items([]).\nprocess_items([H|T]) :-\n process_one(H),\n process_items(T).\n\n% Using findall\nfindall(X, some_condition(X), Results)\n\n% Using maplist\nmaplist(process_one, Items)\n```\n\n---\n\n## Common Patterns\n\n### Pattern 1: Simple Task Agent\n```prolog\nagent_main(Topic) :-\n system(\"You are a helpful research assistant.\"),\n task(\"Research {Topic} and provide a comprehensive summary.\"),\n answer(\"Research complete!\").\n```\n\n### Pattern 2: Multi-Step Workflow\n```prolog\nagent_main(Topic) :-\n system(\"You are a thorough research assistant.\"),\n \n output(\"Step 1: Gathering information...\"),\n task(\"Search for recent information about {Topic}. Store findings in Findings.\", Findings),\n \n output(\"Step 2: Analyzing...\"),\n task(\"Analyze these findings: {Findings}. Store your analysis in Analysis.\", Analysis),\n \n output(\"Step 3: Generating report...\"),\n task(\"Create a comprehensive report based on this analysis: {Analysis}\"),\n \n answer(\"Report generated!\").\n```\n\n### Pattern 3: Tool-Enabled Agent\n```prolog\ntool(search(Query, Results), \"Search the web\") :-\n exec(web_search(query: Query), Results).\n\nagent_main(Topic) :-\n system(\"You are a research assistant with web search. Use the search tool.\"),\n task(\"Research {Topic} using available tools.\"),\n answer(\"Research complete!\").\n```\n\n### Pattern 3b: Tool with Nested LLM Call\n```prolog\n% A tool that uses LLM to analyze search results\ntool(smart_search(Query, Summary), \"Search and summarize results\") :-\n exec(web_search(query: Query), Results),\n format(string(Desc), \"Summarize these search results: ~w\", [Results]),\n task(Desc, Summary). % Nested task CANNOT call smart_search (recursion prevention)\n\nagent_main(Topic) :-\n system(\"Use smart_search to research topics.\"),\n task(\"Research {Topic}.\"),\n answer(\"Done!\").\n```\n\n### Pattern 4: Code Execution (Use Sparingly!)\n```prolog\n% ONLY use exec/Python when you need:\n% - External packages (pandas, numpy, etc.)\n% - Shell commands (find, grep, sed, awk, curl)\n% - Complex imperative logic that's awkward in Prolog\n%\n% NOTE: vm_exec returns a dict with stdout, stderr, exitCode - use get_dict to extract\n% NOTE: The VM starts in /workspace which is your actual workspace directory\n\nagent_main(Task) :-\n system(\"You are a coding assistant.\"),\n \n task(\"Write Python code to solve: {Task}. Store only the code in Code.\", Code),\n \n % Write code to a file in the workspace (VM cwd is /workspace)\n open('script.py', write, S),\n write(S, Code),\n close(S),\n \n output(\"Executing code...\"),\n exec(vm_exec(command: \"python3 script.py\"), Result), % Runs in /workspace\n get_dict(stdout, Result, Output),\n \n task(\"Explain this execution result: {Output}\"),\n \n answer(\"Done!\").\n```\n\n### Pattern 5: Data Analysis with VM\n```prolog\n% Good use of exec: requires pandas package\n% NOTE: vm_exec returns a dict - use get_dict to extract stdout\nagent_main(CsvPath, Question) :-\n system(\"You are a data analyst.\"),\n \n output(\"Setting up environment...\"),\n exec(vm_exec(command: \"pip install pandas\"), _),\n \n output(\"Analyzing data...\"),\n task(\"Write Python code to load {CsvPath} with pandas and answer: {Question}. Store only the code in Code.\", Code),\n \n % Write code to file and execute\n open('analysis.py', write, S),\n write(S, Code),\n close(S),\n exec(vm_exec(command: \"python3 analysis.py\"), Result),\n get_dict(stdout, Result, Output),\n \n task(\"Interpret and explain these analysis results: {Output}\"),\n \n answer(\"Analysis complete!\").\n```\n\n### Pattern 6: File I/O (Use Prolog, NOT Python!)\n```prolog\n% GOOD: Use Prolog's native file I/O\nagent_main(Content) :-\n task(\"Generate a report about {Content}. Store in Report.\", Report),\n \n % Write to file using Prolog (not Python!)\n open('output.md', write, Stream),\n write(Stream, Report),\n close(Stream),\n \n answer(\"Report saved to output.md\").\n\n% Build filename from parts\nagent_main(Name, Content) :-\n task(\"Generate content about {Name}. Store in Text.\", Text),\n \n % Construct filename using atom operations\n atom_string(NameAtom, Name),\n atom_concat(NameAtom, '_report.md', FilenameAtom),\n atom_string(FilenameAtom, Filename),\n \n open(Filename, write, Stream),\n write(Stream, Text),\n close(Stream),\n \n output(\"Saved to {Filename}\"),\n answer(\"Done!\").\n```\n\n### Pattern 7: Using format/3 for Complex Strings\n```prolog\n% When you need to build strings with numbers or complex formatting\nagent_main(Items) :-\n length(Items, Count),\n format(string(StatusMsg), \"Processing ~d items\", [Count]),\n output(StatusMsg),\n \n process_all(Items),\n \n format(string(DoneMsg), \"Completed processing ~d items successfully\", [Count]),\n answer(DoneMsg).\n```\n\n### Pattern 8: Interactive Agent (User Input)\n```prolog\n% When the task may need user clarification or choices\n% Define ask_user wrapper so LLM can interact with user\ntool(ask_user(Prompt, Response), \"Ask the user a question\") :-\n exec(ask_user(prompt: Prompt), Result),\n get_dict(user_response, Result, Response).\n\nagent_main(Task) :-\n system(\"You are a helpful assistant. If you need clarification, use the ask_user tool.\"),\n \n task(\"Help the user with: {Task}. If anything is unclear, ask for clarification.\"),\n \n answer(\"Task completed!\").\n```\n\n### Pattern 9: Error Handling with catch/throw\n```prolog\n% Safe tool call with error recovery\nagent_main(Query) :-\n catch(\n (\n exec(web_search(query: Query), Results),\n task(\"Summarize: {Results}\")\n ),\n Error,\n (\n format(string(ErrMsg), \"Search failed: ~w. Proceeding without search.\", [Error]),\n output(ErrMsg),\n task(\"Answer based on your knowledge: {Query}\")\n )\n ),\n answer(\"Done!\").\n```\n\n### Pattern 10: Fresh Context with prompt()\n```prolog\n% Use prompt() for independent sub-tasks that shouldn't share context\nagent_main(Topic) :-\n system(\"You are a research assistant.\"),\n \n % Main research with accumulated context\n task(\"Research {Topic} deeply.\", MainFindings),\n \n % Independent critique - fresh context, no bias from main research\n prompt(\"As a skeptical reviewer, critique this research: {MainFindings}. Store critique in Critique.\", Critique),\n \n % Back to main context for final synthesis\n task(\"Address this critique: {Critique}\"),\n \n answer(\"Research complete with peer review!\").\n```\n\n---\n\n## When to Use exec() vs Prolog\n\n### Use Prolog Native Functionality For:\n- **File I/O**: `open/3`, `write/2`, `read/2`, `close/1`\n- **String manipulation**: `atom_concat/3`, `atom_string/2`, `split_string/4`\n- **List operations**: `append/3`, `member/2`, `findall/3`, `maplist/2`\n- **Arithmetic**: `is/2`, comparison operators\n- **Logic and control flow**: conjunctions, disjunctions, conditionals\n\n### Use exec() ONLY For:\n- **External packages**: pandas, numpy, requests, matplotlib, etc.\n- **Shell commands**: find, grep, sed, awk, curl, git\n- **System operations**: environment variables, process management\n- **Complex imperative logic**: loops with side effects, mutable state\n\n### BAD Example - Unnecessary Python:\n```prolog\n% DON'T do this - Python for simple file writing\nexec(vm_exec(command: \"python3 -c \"open('out.txt','w').write('hello')\"\"), _)\n```\n\n### GOOD Example - Use Prolog:\n```prolog\n% DO this instead - native Prolog file I/O\nopen('out.txt', write, S),\nwrite(S, Content),\nclose(S)\n```\n\n---\n\n## File Access Patterns with vm_exec\n\nWhen `vm_exec` is backed by AgentVM, it runs in a sandboxed Alpine Linux environment using **BusyBox** (not GNU coreutils), and the workspace is mounted at `/workspace`. When the runtime uses the local workspace shell instead, standard local tooling and paths apply. Prefer relative paths when possible.\n\n**Important:** `vm_exec` returns a dict - always extract stdout:\n```prolog\nexec(vm_exec(command: \"ls /workspace\"), Result),\nget_dict(stdout, Result, Output).\n```\n\n### Common File Operations\n\n| Operation | Command | Example |\n|-----------|---------|---------|\n| List files | `ls {dir}` | `ls /workspace/src` |\n| Find by name | `find {dir} -name '{pattern}' -type f` | `find /workspace -name '*.ts' -type f` |\n| Find shallow | `find {dir} -maxdepth 1 -name '{pattern}'` | `find /workspace/src -maxdepth 1 -name '*.ts'` |\n| Read file | `cat {path}` | `cat /workspace/README.md` |\n| Read first N lines | `head -{n} {path}` | `head -10 /workspace/file.ts` |\n| Read last N lines | `tail -{n} {path}` | `tail -5 /workspace/file.ts` |\n| Read line range | `sed -n '{start},{end}p' {path}` | `sed -n '1,10p' /workspace/file.ts` |\n| Grep in file | `grep '{pattern}' {path}` | `grep 'import' /workspace/file.ts` |\n| Grep with line nums | `grep -n '{pattern}' {path}` | `grep -n 'export' /workspace/file.ts` |\n| Grep recursive | `grep -rn '{pattern}' {dir}` | `grep -rn 'TODO' /workspace/src` |\n| File exists | `test -f {path} && echo yes` | `test -f /workspace/file.ts && echo yes` |\n| Dir exists | `test -d {path} && echo yes` | `test -d /workspace/src && echo yes` |\n| File size | `stat -c %s {path}` | `stat -c %s /workspace/file.ts` |\n| Line count | `wc -l < {path}` | `wc -l < /workspace/file.ts` |\n| Count files | `find ... \\| wc -l` | `find /workspace -name '*.ts' \\| wc -l` |\n| Basename | `basename {path}` | `basename /workspace/src/file.ts` |\n| Dirname | `dirname {path}` | `dirname /workspace/src/file.ts` |\n\n### DML File Access Patterns\n\n#### List Directory\n```prolog\nlist_files(Dir, Files) :-\n format(string(Cmd), \"ls ~w\", [Dir]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Output),\n split_string(Output, \"\\n\", \"\\s\\t\\r\\n\", Files).\n```\n\n#### Find Files by Pattern\n```prolog\nfind_files(Dir, Pattern, Files) :-\n format(string(Cmd), \"find ~w -name '~w' -type f\", [Dir, Pattern]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Output),\n split_string(Output, \"\\n\", \"\\s\\t\\r\\n\", Files).\n```\n\n#### Read File\n```prolog\nread_file(Path, Content) :-\n format(string(Cmd), \"cat ~w\", [Path]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Content).\n```\n\n#### Read First N Lines\n```prolog\nread_head(Path, N, Content) :-\n format(string(Cmd), \"head -~d ~w\", [N, Path]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Content).\n```\n\n#### Read Line Range\n```prolog\nread_lines(Path, Start, End, Content) :-\n format(string(Cmd), \"sed -n '~d,~dp' ~w\", [Start, End, Path]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Content).\n```\n\n#### Grep in File\n```prolog\ngrep(Path, Pattern, Matches) :-\n format(string(Cmd), \"grep -n '~w' ~w || true\", [Pattern, Path]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Matches).\n```\n\n#### Recursive Grep\n```prolog\ngrep_recursive(Dir, Pattern, Matches) :-\n format(string(Cmd), \"grep -rn '~w' ~w || true\", [Pattern, Dir]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Matches).\n```\n\n#### File Exists Check\n```prolog\nfile_exists(Path) :-\n format(string(Cmd), \"test -f ~w && echo yes || echo no\", [Path]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, \"yes\").\n```\n\n#### Line Count\n```prolog\nline_count(Path, Count) :-\n format(string(Cmd), \"wc -l < ~w\", [Path]),\n exec(vm_exec(command: Cmd), Result),\n get_dict(stdout, Result, Output),\n normalize_space(atom(CountAtom), Output),\n atom_number(CountAtom, Count).\n```\n\n### BusyBox Limitations (AgentVM Sandbox)\n\nWhen AgentVM sandboxing is enabled, BusyBox in Alpine Linux has limited options compared to GNU coreutils:\n- `grep --include` is NOT supported - use `find ... -exec grep` instead\n- Some `find` options may differ\n- Use `|| true` with grep to prevent failures when no matches found\n\n---\n\n## Conversion Guidelines\n\n1. **Identify the core task** - What is the primary goal?\n2. **Determine parameters** - What inputs does the agent need?\n3. **Map to patterns** - Which DML pattern best fits?\n4. **Prefer Prolog native operations** - Use Prolog for file I/O, strings, lists\n5. **Use exec() sparingly** - Only for packages, shell commands, imperative logic\n6. **Define required tools** - What external capabilities are needed?\n7. **Handle edge cases** - Add fallbacks and error handling\n8. **Add progress output** - Keep users informed with `output/1`\n9. **Add ask_user wrapper** - If the task might need user input, clarification, or choices\n\n## CRITICAL: Tools are LLM-Only\n\n**Tools defined with `tool/3` can ONLY be called by the LLM during `task()` execution.**\n\n- `tool/3` defines capabilities for the LLM to use\n- DML code CANNOT call tools directly as predicates\n- If DML code needs external functionality, use `exec()` directly\n\n```prolog\n% WRONG - cannot call tool from DML code\nagent_main :-\n my_search(\"query\", Results). % ERROR!\n\n% CORRECT - use exec() for direct access\nagent_main :-\n exec(web_search(query: \"query\"), Results).\n\n% CORRECT - let LLM use the tool via task()\nagent_main :-\n task(\"Search for information about X.\", Summary).\n```\n\n## CRITICAL: String Handling Rules\n\n**NEVER do any of these:**\n- `output(format(...))` - format/3 doesn't return a value, it binds to first arg\n- `answer(format(...))` - same issue\n- Mixing `{Var}` and `~w` in the same string\n- Using `{Var}` inside format/3 format strings\n\n**DO this instead:**\n- Use `{Variable}` interpolation directly: `output(\"Processing {Item}\")`\n- Or use format/3 properly: `format(string(Msg), \"Count: ~d\", [N]), output(Msg)`\n\n## CRITICAL: Prolog vs exec() Rules\n\n**Use Prolog for:**\n- File I/O: `open/3`, `write/2`, `close/1`\n- String building: `atom_concat/3`, `atom_string/2`\n- All standard logic and data manipulation\n\n**Use exec() ONLY for:**\n- External packages (pandas, numpy)\n- Shell commands (grep, curl, find)\n- Complex imperative tasks\n\n## Output Requirements\n\nYour DML output must:\n\n1. Start with a comment header describing the program\n2. Define any tool wrappers needed with `tool/3`\n3. **If the task may need user input, define an `ask_user` tool wrapper**\n4. Have a single `agent_main` entry point\n5. Use appropriate system prompts\n6. Include progress outputs for long-running tasks\n7. End with `answer/1` to signal completion\n8. Handle stated edge cases\n9. **Only use tools from the Available External Tools list**\n10. **Use ONLY {Variable} interpolation OR format/3, never mix them**\n11. **NEVER pass format(...) directly to output/1 or answer/1**\n12. **Use Prolog native file I/O, NOT Python exec() for simple file operations**\n\nOutput ONLY the DML code, no explanations or markdown code fences.\n";
8
8
  /**
9
9
  * Build the tools table for the prompt
10
10
  */
@@ -1 +1 @@
1
- {"version":3,"file":"compiler_prompt.d.ts","sourceRoot":"","sources":["../src/compiler_prompt.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAMzC,eAAO,MAAM,qBAAqB,oi1BA6xBjC,CAAC;AAMF;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAsC5D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAGnE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUzD"}
1
+ {"version":3,"file":"compiler_prompt.d.ts","sourceRoot":"","sources":["../src/compiler_prompt.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAMzC,eAAO,MAAM,qBAAqB,i01BA+xBjC,CAAC;AAMF;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAsC5D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAGnE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUzD"}
@@ -321,9 +321,11 @@ catch(Goal, Error, Recovery)
321
321
  throw(some_error)
322
322
  \`\`\`
323
323
 
324
- ### Logic & Optimization (CLP)
324
+ ### Module Directives & Logic (CLP)
325
325
 
326
- DML supports Prolog's Constraint Logic Programming (CLP) libraries. Use these instead of Python for mathematical optimization, scheduling, or strict logic puzzles:
326
+ DML supports top-level \`:- use_module(...)\` directives for standard SWI-Prolog libraries, such as \`library(clpfd)\`.
327
+ Use them when the skill needs built-in library predicates. For workspace-local shared DML code, prefer \`:- consult('relative/path.dml').\`.
328
+ Local \`:- use_module('relative/path.dml').\` is accepted as a convenience alias, but it currently loads the file with consult-style semantics rather than full SWI module export filtering.
327
329
 
328
330
  - **CLP(FD)**: Finite domains (integers). Use \`:- use_module(library(clpfd)).\`
329
331
  - **CLP(Q)**: Rational numbers (exact fractions). Use \`:- use_module(library(clpq)).\`
@@ -1 +1 @@
1
- {"version":3,"file":"compiler_prompt.js","sourceRoot":"","sources":["../src/compiler_prompt.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compiler_prompt.js","sourceRoot":"","sources":["../src/compiler_prompt.ts"],"names":[],"mappings":"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"}
@@ -372,6 +372,33 @@ extract_params([_Arg|Rest], Index, Arity, Inputs, Outputs) :-
372
372
  ; Inputs = [Param|RestInputs], Outputs = RestOutputs
373
373
  ).
374
374
 
375
+ module_reference_string(Module, ModuleStr) :-
376
+ atom(Module),
377
+ !,
378
+ atom_string(Module, ModuleStr).
379
+ module_reference_string(Module, Module) :-
380
+ string(Module).
381
+
382
+ workspace_module_reference(Module) :-
383
+ module_reference_string(Module, ModuleStr),
384
+ ( sub_string(ModuleStr, _, _, _, "/")
385
+ ; sub_string(ModuleStr, _, _, 0, ".dml")
386
+ ; sub_string(ModuleStr, _, _, 0, ".pl")
387
+ ; sub_string(ModuleStr, 0, 1, _, ".")
388
+ ).
389
+
390
+ process_use_module_directive(Module, SessionId) :-
391
+ ( workspace_module_reference(Module)
392
+ -> process_directive(consult(Module), SessionId)
393
+ ; SessionId:use_module(Module)
394
+ ).
395
+
396
+ process_use_module_directive(Module, Exports, SessionId) :-
397
+ ( workspace_module_reference(Module)
398
+ -> process_directive(consult(Module), SessionId)
399
+ ; SessionId:use_module(Module, Exports)
400
+ ).
401
+
375
402
  %% process_directive(+Directive, +SessionId)
376
403
  process_directive(param(Key, Desc), SessionId) :-
377
404
  !,
@@ -379,6 +406,7 @@ process_directive(param(Key, Desc), SessionId) :-
379
406
  process_directive(param(Key, Desc, Default), SessionId) :-
380
407
  !,
381
408
  assertz(SessionId:param_decl(Key, Desc, Default)).
409
+
382
410
  %% Handle consult directive: :- consult(File)
383
411
  process_directive(consult(File), SessionId) :-
384
412
  !,
@@ -390,10 +418,10 @@ process_directive(consult(File), SessionId) :-
390
418
  %% Handle use_module directive
391
419
  process_directive(use_module(Module), SessionId) :-
392
420
  !,
393
- SessionId:use_module(Module).
421
+ process_use_module_directive(Module, SessionId).
394
422
  process_directive(use_module(Module, Exports), SessionId) :-
395
423
  !,
396
- SessionId:use_module(Module, Exports).
424
+ process_use_module_directive(Module, Exports, SessionId).
397
425
  %% Handle list-style consult: :- [File] or :- [File1, File2, ...]
398
426
  process_directive([File|Files], SessionId) :-
399
427
  !,
@@ -1615,6 +1643,16 @@ mi_call(consult(File), StateIn, StateIn) :-
1615
1643
  readutil:read_file_to_string(FullPath, Code, []),
1616
1644
  consult_string(Code, SessionId).
1617
1645
 
1646
+ mi_call(use_module(Module), StateIn, StateIn) :-
1647
+ !,
1648
+ get_session_id(SessionId),
1649
+ process_use_module_directive(Module, SessionId).
1650
+
1651
+ mi_call(use_module(Module, Exports), StateIn, StateIn) :-
1652
+ !,
1653
+ get_session_id(SessionId),
1654
+ process_use_module_directive(Module, Exports, SessionId).
1655
+
1618
1656
  %% mi_call([File], +StateIn, -StateOut)
1619
1657
  %% Alternative consult syntax: [file]
1620
1658
  mi_call([File], StateIn, StateOut) :-
@@ -378,6 +378,23 @@ IMPORTANT: DML skills must NOT install their own dependencies.
378
378
  The skill can assume all dependencies are already available at runtime.
379
379
  </program_structure>
380
380
 
381
+ <module_directives>
382
+ MODULE DIRECTIVES
383
+
384
+ DML supports top-level `:- use_module(...)` directives for standard SWI-Prolog libraries.
385
+ Use them for built-in modules such as:
386
+ :- use_module(library(clpfd)).
387
+ :- use_module(library(clpq)).
388
+ :- use_module(library(clpr)).
389
+ :- use_module(library(lists)).
390
+
391
+ For workspace-local shared DML code, prefer:
392
+ :- consult('relative/path/to/helper.dml').
393
+
394
+ Local `:- use_module('relative/path/to/helper.dml').` is accepted as a convenience alias, but it currently loads the file with consult-style semantics rather than full SWI module export filtering.
395
+ Keep imports at the top of the file and only import modules the skill actually uses.
396
+ </module_directives>
397
+
381
398
  <when_to_use_what>
382
399
  CHOOSING THE RIGHT APPROACH
383
400
 
@@ -393,7 +410,6 @@ CHOOSING THE RIGHT APPROACH
393
410
  | Get user input | exec(ask_user(prompt: P), R) via tool |
394
411
  | Parse JSON | atom_json_dict(Atom, Dict, []) + get_dict/3 |
395
412
 
396
- DO NOT use bash for: fetching web content (curl, wget) - use url_fetch.
397
413
  DO NOT use Python for: simple file writing - use Prolog open/3, write/2, close/1.
398
414
  DO NOT use Prolog for: complex data analysis - use Python via bash.
399
415
  </when_to_use_what>
@@ -430,6 +446,8 @@ GENERAL GUIDELINES:
430
446
  - Install missing packages during skill creation, not inside the final DML skill.
431
447
  - Use runtime tools (`web_search`, `news_search`, `url_fetch`) for web content instead of `curl` or `wget`.
432
448
  - Use the configured workspace for persistent task files.
449
+ - Put helper scripts, templates, prompt fixtures, and other skill-specific runtime artifacts in a dedicated `.deepclause` subfolder, typically `.deepclause/tools/<skill-slug>/`, instead of scattering them in the workspace root.
450
+ - Reference those helper files from DML with workspace-relative paths.
433
451
 
434
452
  PACKAGE INSTALLATION (done by YOU, the skill creator, via bash - NOT inside DML skills):
435
453
  Python: bash("pip install --no-cache-dir pandas numpy matplotlib")
@@ -661,4 +679,5 @@ BEFORE SUBMITTING YOUR DML CODE, VERIFY ALL OF THESE:
661
679
  [ ] No curl/wget in bash - url_fetch for web content
662
680
  [ ] No {word} placeholders in system() - use <word> angle brackets for template text
663
681
  [ ] No package installation in DML code - dependencies are pre-installed by the skill creator
682
+ [ ] Helper scripts and other skill-specific artifacts live in a dedicated `.deepclause` subfolder
664
683
  </output_checklist>
@@ -87,6 +87,7 @@ agent_main(SpecMarkdown) :-
87
87
  format(string(EnvTask),
88
88
  "Set up the execution environment for the skill you are about to build.\n\nBased on your research:\n~w\n\nYour job in this phase:\n1. Use shell() to check what is already installed (python --version, node --version, which <tool>, pip show <pkg>, etc.).\n2. Install ALL missing packages and tools: pip install, apt-get install (always use sudo and -y), npm install -g.\n3. If the skill needs helper scripts (Python, Shell, etc.), create and smoke-test them now with shell().\n4. When you encounter errors during setup, diagnose the root cause (version mismatch, missing system lib, permissions, ...) and resolve it.\n5. Prefer the simplest approach with the fewest new dependencies when the spec does not specify.\n6. Do NOT install anything inside the DML skill itself - the skill must assume everything is pre-installed.\n\nStore a short environment summary (what was installed, what exists) in EnvSummary.",
89
89
  [ResearchSummary]),
90
+
90
91
  task(EnvTask, string(EnvSummary)),
91
92
  output(EnvSummary),
92
93
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepclause-sdk",
3
- "version": "0.0.64",
3
+ "version": "0.0.65",
4
4
  "description": "DeepClause CLI Tool, TUI and Typescript SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -372,6 +372,33 @@ extract_params([_Arg|Rest], Index, Arity, Inputs, Outputs) :-
372
372
  ; Inputs = [Param|RestInputs], Outputs = RestOutputs
373
373
  ).
374
374
 
375
+ module_reference_string(Module, ModuleStr) :-
376
+ atom(Module),
377
+ !,
378
+ atom_string(Module, ModuleStr).
379
+ module_reference_string(Module, Module) :-
380
+ string(Module).
381
+
382
+ workspace_module_reference(Module) :-
383
+ module_reference_string(Module, ModuleStr),
384
+ ( sub_string(ModuleStr, _, _, _, "/")
385
+ ; sub_string(ModuleStr, _, _, 0, ".dml")
386
+ ; sub_string(ModuleStr, _, _, 0, ".pl")
387
+ ; sub_string(ModuleStr, 0, 1, _, ".")
388
+ ).
389
+
390
+ process_use_module_directive(Module, SessionId) :-
391
+ ( workspace_module_reference(Module)
392
+ -> process_directive(consult(Module), SessionId)
393
+ ; SessionId:use_module(Module)
394
+ ).
395
+
396
+ process_use_module_directive(Module, Exports, SessionId) :-
397
+ ( workspace_module_reference(Module)
398
+ -> process_directive(consult(Module), SessionId)
399
+ ; SessionId:use_module(Module, Exports)
400
+ ).
401
+
375
402
  %% process_directive(+Directive, +SessionId)
376
403
  process_directive(param(Key, Desc), SessionId) :-
377
404
  !,
@@ -379,6 +406,7 @@ process_directive(param(Key, Desc), SessionId) :-
379
406
  process_directive(param(Key, Desc, Default), SessionId) :-
380
407
  !,
381
408
  assertz(SessionId:param_decl(Key, Desc, Default)).
409
+
382
410
  %% Handle consult directive: :- consult(File)
383
411
  process_directive(consult(File), SessionId) :-
384
412
  !,
@@ -390,10 +418,10 @@ process_directive(consult(File), SessionId) :-
390
418
  %% Handle use_module directive
391
419
  process_directive(use_module(Module), SessionId) :-
392
420
  !,
393
- SessionId:use_module(Module).
421
+ process_use_module_directive(Module, SessionId).
394
422
  process_directive(use_module(Module, Exports), SessionId) :-
395
423
  !,
396
- SessionId:use_module(Module, Exports).
424
+ process_use_module_directive(Module, Exports, SessionId).
397
425
  %% Handle list-style consult: :- [File] or :- [File1, File2, ...]
398
426
  process_directive([File|Files], SessionId) :-
399
427
  !,
@@ -1615,6 +1643,16 @@ mi_call(consult(File), StateIn, StateIn) :-
1615
1643
  readutil:read_file_to_string(FullPath, Code, []),
1616
1644
  consult_string(Code, SessionId).
1617
1645
 
1646
+ mi_call(use_module(Module), StateIn, StateIn) :-
1647
+ !,
1648
+ get_session_id(SessionId),
1649
+ process_use_module_directive(Module, SessionId).
1650
+
1651
+ mi_call(use_module(Module, Exports), StateIn, StateIn) :-
1652
+ !,
1653
+ get_session_id(SessionId),
1654
+ process_use_module_directive(Module, Exports, SessionId).
1655
+
1618
1656
  %% mi_call([File], +StateIn, -StateOut)
1619
1657
  %% Alternative consult syntax: [file]
1620
1658
  mi_call([File], StateIn, StateOut) :-