deepclause-pi 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/dist/diagram/extract.d.ts +5 -0
- package/dist/diagram/extract.js +701 -0
- package/dist/diagram/grade.d.ts +41 -0
- package/dist/diagram/grade.js +70 -0
- package/dist/diagram/validate.d.ts +36 -0
- package/dist/diagram/validate.js +148 -0
- package/dist/diagram/viewer.d.ts +30 -0
- package/dist/diagram/viewer.js +94 -0
- package/dist/diagram/workspace.d.ts +24 -0
- package/dist/diagram/workspace.js +106 -0
- package/dist/index.js +142 -2
- package/dist/model.d.ts +16 -0
- package/dist/model.js +28 -0
- package/docs/BLOG_POST_PI_EXTENSION.md +253 -0
- package/docs/DIAGRAM_INTEGRATION_PROPOSAL.md +154 -0
- package/package.json +6 -2
- package/skills/handbook-dml/SKILL.md +265 -0
- package/src/assets/AGENTS.md +16 -0
- package/src/assets/vendor/mermaid.min.js +3636 -0
- package/src/assets/viewer.template.html +319 -0
- package/src/diagram/extract.ts +721 -0
- package/src/diagram/grade.ts +104 -0
- package/src/diagram/validate.ts +188 -0
- package/src/diagram/viewer.ts +133 -0
- package/src/diagram/workspace.ts +109 -0
- package/src/index.ts +158 -2
- package/src/model.ts +46 -0
package/dist/index.js
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
|
-
import { readdir } from "node:fs/promises";
|
|
1
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
4
5
|
import { Type } from "typebox";
|
|
5
6
|
import { buildInitialMessages } from "./context.js";
|
|
6
7
|
import { loadConfig, setModelToolEnabled } from "./config.js";
|
|
8
|
+
import { renderDml, renderSequence } from "./diagram/extract.js";
|
|
9
|
+
import { polishDiagram, resolveGrade } from "./diagram/grade.js";
|
|
10
|
+
import { findChrome, validateMermaid } from "./diagram/validate.js";
|
|
11
|
+
import { buildViewer, openViewerInBrowser, writeSidecar, } from "./diagram/viewer.js";
|
|
12
|
+
import { collectDiagramTargets, diagramNameFor, displayPath, ensureDiagramDir, resolveDiagramSource, } from "./diagram/workspace.js";
|
|
13
|
+
import { completeTextWithPiModel } from "./model.js";
|
|
7
14
|
import { executeDml } from "./runtime.js";
|
|
8
15
|
import { getPaths, initializeWorkspace, resolveDmlPath } from "./workspace.js";
|
|
9
16
|
import { assemblePlanDml, buildPlanningPrompt, DC_PLAN_COMMIT_TOOL, isContextualPlan, PI_AGENT_STEP_TOOL, readPlanRequiredTools, validateGeneratedPlan, validatePlanSpec, writePlanNonDestructively, } from "./planner.js";
|
|
10
17
|
const DC_RUN_TOOL = "dc_run";
|
|
11
|
-
const
|
|
18
|
+
const DC_DIAGRAM_TOOL = "dc_diagram";
|
|
19
|
+
const AUTHORING_INSTRUCTION = `DeepClause programs live in .pi/deepclause/skills/ and executable generated plans live in .pi/deepclause/plans/. You may create and edit DML skills directly after consulting .pi/deepclause/AGENTS.md and DML_REFERENCE.md. Use /dc-plan when the user asks pi to design a contextual executable plan; finish that planning turn with dc_plan_commit. When the user asks for a diagram, flowchart, or visual of a .dml file, call the dc_diagram tool with the exact path and the requested grade (presentation or specification); it writes the viewer under .pi/deepclause/diagrams/ and opens it, so do not hand-write Mermaid. DeepClause compilation is unavailable, so generated content must already be valid DML. Users execute programs through /dc-run. If the opt-in dc_run tool is active, you may execute an ordinary skill with it, but contextual plans requiring pi_agent_step must be started by the user. Never invoke a compiler or create .deepclause/.`;
|
|
12
20
|
const STATUS_KEY = "deepclause";
|
|
13
21
|
const WIDGET_KEY = "deepclause-stream";
|
|
14
22
|
export function splitArguments(input) {
|
|
@@ -170,6 +178,12 @@ async function listDmlFiles(directory, prefix = "") {
|
|
|
170
178
|
function modelLabel(ctx) {
|
|
171
179
|
return ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "none selected";
|
|
172
180
|
}
|
|
181
|
+
async function bundledViewerTemplate() {
|
|
182
|
+
return readFile(fileURLToPath(new URL("./assets/viewer.template.html", import.meta.url)), "utf8");
|
|
183
|
+
}
|
|
184
|
+
function viewerVendorAssetPath() {
|
|
185
|
+
return fileURLToPath(new URL("./assets/vendor/mermaid.min.js", import.meta.url));
|
|
186
|
+
}
|
|
173
187
|
function publishResult(pi, content, details) {
|
|
174
188
|
pi.sendMessage({ customType: "deepclause-result", content, display: true, details });
|
|
175
189
|
}
|
|
@@ -177,6 +191,7 @@ export default function deepClauseExtension(pi) {
|
|
|
177
191
|
let activeController;
|
|
178
192
|
let activeDescription;
|
|
179
193
|
let modelToolRegistered = false;
|
|
194
|
+
let diagramToolRegistered = false;
|
|
180
195
|
let planCommitRegistered = false;
|
|
181
196
|
let planningTransaction;
|
|
182
197
|
let pendingAgentStep;
|
|
@@ -468,9 +483,131 @@ export default function deepClauseExtension(pi) {
|
|
|
468
483
|
: activeTools.filter((name) => name !== DC_RUN_TOOL));
|
|
469
484
|
}
|
|
470
485
|
};
|
|
486
|
+
const setDiagramToolActive = () => {
|
|
487
|
+
if (!diagramToolRegistered) {
|
|
488
|
+
pi.registerTool({
|
|
489
|
+
name: DC_DIAGRAM_TOOL,
|
|
490
|
+
label: "Create DeepClause Diagram",
|
|
491
|
+
description: "Create a presentation-grade or specification-grade Mermaid diagram from any .dml file, write a self-contained offline viewer under .pi/deepclause/diagrams/, and open it.",
|
|
492
|
+
promptSnippet: "Create a presentation- or specification-grade diagram from a DML file",
|
|
493
|
+
promptGuidelines: [
|
|
494
|
+
"Use dc_diagram whenever the user asks for a diagram, flowchart, or visual of a .dml file; pass the exact path the user named.",
|
|
495
|
+
"Choose grade=presentation for slides and overviews and grade=specification for engineering detail; use grade=both only when the user asks for both.",
|
|
496
|
+
"Do not hand-write Mermaid or run diagram tools yourself; call dc_diagram and report the viewer result.",
|
|
497
|
+
],
|
|
498
|
+
parameters: Type.Object({
|
|
499
|
+
dml: Type.String({ description: "Path to a .dml file, relative to the workspace or absolute. A leading @ is ignored." }),
|
|
500
|
+
grade: Type.Optional(Type.String({ description: "presentation (default), specification, or both. Synonyms such as detailed or technical map to specification." })),
|
|
501
|
+
view: Type.Optional(StringEnum(["flow", "sequence"], { description: "Base layout used to seed the grade; default flow." })),
|
|
502
|
+
}),
|
|
503
|
+
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
504
|
+
if (activeController) {
|
|
505
|
+
return {
|
|
506
|
+
content: [{ type: "text", text: "Another DeepClause operation is already active; wait for it to finish." }],
|
|
507
|
+
details: { success: false, error: "execution_already_active" },
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
const requested = resolveGrade(String(params.grade ?? "")) ?? "presentation";
|
|
511
|
+
const grades = requested === "both" ? ["presentation", "specification"] : [requested];
|
|
512
|
+
const view = params.view === "sequence" ? "sequence" : "flow";
|
|
513
|
+
let sourcePath;
|
|
514
|
+
try {
|
|
515
|
+
sourcePath = await resolveDiagramSource(ctx.cwd, params.dml);
|
|
516
|
+
}
|
|
517
|
+
catch (error) {
|
|
518
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
519
|
+
return { content: [{ type: "text", text: `dc_diagram failed: ${message}` }], details: { success: false, error: message } };
|
|
520
|
+
}
|
|
521
|
+
if (!ctx.model) {
|
|
522
|
+
return {
|
|
523
|
+
content: [{ type: "text", text: "dc_diagram requires an active pi model. Select one and try again." }],
|
|
524
|
+
details: { success: false, error: "no_model" },
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
const config = await loadConfig(getPaths(ctx.cwd).config);
|
|
528
|
+
const source = await readFile(sourcePath, "utf8");
|
|
529
|
+
const display = displayPath(ctx.cwd, sourcePath);
|
|
530
|
+
const seed = view === "sequence"
|
|
531
|
+
? renderSequence(display, source)
|
|
532
|
+
: renderDml(display, source, { hideOutput: true });
|
|
533
|
+
const targets = await collectDiagramTargets(ctx.cwd, [sourcePath]);
|
|
534
|
+
const name = diagramNameFor(sourcePath, targets, ctx.cwd);
|
|
535
|
+
const { diagrams, vendor } = await ensureDiagramDir(ctx.cwd, viewerVendorAssetPath());
|
|
536
|
+
const templateText = await bundledViewerTemplate();
|
|
537
|
+
const controller = new AbortController();
|
|
538
|
+
const cancel = () => controller.abort(signal?.reason ?? new Error("dc_diagram cancelled"));
|
|
539
|
+
if (signal?.aborted)
|
|
540
|
+
cancel();
|
|
541
|
+
else
|
|
542
|
+
signal?.addEventListener("abort", cancel, { once: true });
|
|
543
|
+
activeController = controller;
|
|
544
|
+
activeDescription = `diagram ${name} (${grades.join("+")})`;
|
|
545
|
+
const run = (command, args, options) => pi.exec(command, args, options);
|
|
546
|
+
let chrome;
|
|
547
|
+
let chromeResolved = false;
|
|
548
|
+
try {
|
|
549
|
+
for (const grade of grades) {
|
|
550
|
+
const result = await polishDiagram({
|
|
551
|
+
grade,
|
|
552
|
+
view,
|
|
553
|
+
source,
|
|
554
|
+
seed,
|
|
555
|
+
maxTokens: config.maxTokens,
|
|
556
|
+
signal: controller.signal,
|
|
557
|
+
complete: (options) => completeTextWithPiModel(ctx, options),
|
|
558
|
+
validate: async (code) => {
|
|
559
|
+
if (!chromeResolved) {
|
|
560
|
+
chrome = await findChrome(run);
|
|
561
|
+
chromeResolved = true;
|
|
562
|
+
}
|
|
563
|
+
const outcome = await validateMermaid(code, view, { run, vendorDir: vendor, chrome: chrome ?? null });
|
|
564
|
+
return outcome.result;
|
|
565
|
+
},
|
|
566
|
+
onProgress: (message) => onUpdate?.({
|
|
567
|
+
content: [{ type: "text", text: message }],
|
|
568
|
+
details: { dml: display, grades, name },
|
|
569
|
+
}),
|
|
570
|
+
});
|
|
571
|
+
await writeSidecar(diagrams, name, grade, result.code);
|
|
572
|
+
}
|
|
573
|
+
const build = await buildViewer({
|
|
574
|
+
cwd: ctx.cwd,
|
|
575
|
+
templateText,
|
|
576
|
+
vendorAssetPath: viewerVendorAssetPath(),
|
|
577
|
+
extraPaths: [sourcePath],
|
|
578
|
+
});
|
|
579
|
+
const opened = ctx.hasUI
|
|
580
|
+
? await openViewerInBrowser(pi, build.viewerPath, name, grades[0] ?? "presentation")
|
|
581
|
+
: false;
|
|
582
|
+
const viewer = displayPath(ctx.cwd, build.viewerPath);
|
|
583
|
+
const text = `Created ${grades.join(" + ")}-grade diagram for ${display}. Viewer: ${viewer}${opened ? " (opened in your browser)" : ""}`;
|
|
584
|
+
return {
|
|
585
|
+
content: [{ type: "text", text }],
|
|
586
|
+
details: { success: true, dml: display, grades, name, viewer, opened, chrome: Boolean(chrome) },
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
catch (error) {
|
|
590
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
591
|
+
return { content: [{ type: "text", text: `dc_diagram failed: ${message}` }], details: { success: false, error: message } };
|
|
592
|
+
}
|
|
593
|
+
finally {
|
|
594
|
+
signal?.removeEventListener("abort", cancel);
|
|
595
|
+
activeController = undefined;
|
|
596
|
+
activeDescription = undefined;
|
|
597
|
+
}
|
|
598
|
+
},
|
|
599
|
+
});
|
|
600
|
+
diagramToolRegistered = true;
|
|
601
|
+
}
|
|
602
|
+
const activeTools = pi.getActiveTools();
|
|
603
|
+
if (!activeTools.includes(DC_DIAGRAM_TOOL)) {
|
|
604
|
+
pi.setActiveTools([...activeTools, DC_DIAGRAM_TOOL]);
|
|
605
|
+
}
|
|
606
|
+
};
|
|
471
607
|
pi.on("session_start", async (_event, ctx) => {
|
|
472
608
|
const config = await loadConfig(getPaths(ctx.cwd).config);
|
|
473
609
|
setModelToolActive(config.modelToolEnabled);
|
|
610
|
+
setDiagramToolActive();
|
|
474
611
|
});
|
|
475
612
|
pi.on("tool_execution_start", (event) => {
|
|
476
613
|
if (pendingAgentStep && !pendingAgentStep.toolsUsed.includes(event.toolName)) {
|
|
@@ -547,6 +684,9 @@ export default function deepClauseExtension(pi) {
|
|
|
547
684
|
`Plans: ${path.relative(ctx.cwd, paths.plans)}`,
|
|
548
685
|
`Context: ${config.contextMode} (verbose default: ${config.verbose})`,
|
|
549
686
|
`Model tool (${DC_RUN_TOOL}): ${config.modelToolEnabled && pi.getActiveTools().includes(DC_RUN_TOOL) ? "enabled" : "disabled"}`,
|
|
687
|
+
`Model tool (${DC_DIAGRAM_TOOL}): ${pi.getActiveTools().includes(DC_DIAGRAM_TOOL) ? "enabled" : "disabled"}`,
|
|
688
|
+
"Ask pi for a presentation-grade or specification-grade diagram of any .dml file;",
|
|
689
|
+
"it writes the viewer under .pi/deepclause/diagrams/ and opens it.",
|
|
550
690
|
"Commands:",
|
|
551
691
|
" /dc-list",
|
|
552
692
|
" /dc-plan <request> [--name=slug] create an executable contextual DML plan",
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { Usage } from "@earendil-works/pi-ai";
|
|
3
|
+
export interface TextCompletion {
|
|
4
|
+
text: string;
|
|
5
|
+
usage?: Usage;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* One-shot text completion through pi's active model and credentials. Used by
|
|
9
|
+
* diagram grading; it never requests API keys or mutates provider state.
|
|
10
|
+
*/
|
|
11
|
+
export declare function completeTextWithPiModel(ctx: ExtensionContext, options: {
|
|
12
|
+
systemPrompt?: string;
|
|
13
|
+
prompt: string;
|
|
14
|
+
maxTokens: number;
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
}): Promise<TextCompletion>;
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-shot text completion through pi's active model and credentials. Used by
|
|
3
|
+
* diagram grading; it never requests API keys or mutates provider state.
|
|
4
|
+
*/
|
|
5
|
+
export async function completeTextWithPiModel(ctx, options) {
|
|
6
|
+
const model = ctx.model;
|
|
7
|
+
if (!model)
|
|
8
|
+
throw new Error("Select a pi model before generating a diagram grade");
|
|
9
|
+
if (!ctx.modelRegistry.hasConfiguredAuth(model)) {
|
|
10
|
+
throw new Error(`Pi has no configured authentication for ${model.provider}/${model.id}`);
|
|
11
|
+
}
|
|
12
|
+
const response = await ctx.modelRegistry.complete(model, {
|
|
13
|
+
systemPrompt: options.systemPrompt,
|
|
14
|
+
messages: [{ role: "user", content: options.prompt, timestamp: Date.now() }],
|
|
15
|
+
}, {
|
|
16
|
+
signal: options.signal,
|
|
17
|
+
maxTokens: options.maxTokens,
|
|
18
|
+
cacheRetention: "none",
|
|
19
|
+
});
|
|
20
|
+
if (response.stopReason === "error" || response.stopReason === "aborted") {
|
|
21
|
+
throw new Error(response.errorMessage || `Pi model request ${response.stopReason}`);
|
|
22
|
+
}
|
|
23
|
+
const text = response.content
|
|
24
|
+
.filter((content) => content.type === "text")
|
|
25
|
+
.map((content) => content.text)
|
|
26
|
+
.join("");
|
|
27
|
+
return { text, usage: response.usage };
|
|
28
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# DeepClause meets pi: executable agent plans inside your coding session
|
|
2
|
+
|
|
3
|
+
### DML orchestration with pi’s models, tools and context
|
|
4
|
+
|
|
5
|
+
`tldr;` I built a [pi](https://github.com/badlogic/pi-mono) extension for [DeepClause](https://github.com/deepclause/deepclause-sdk). It runs DML programs with pi’s active model, credentials and session context. It can also turn a normal planning request into an executable DML plan. The plan can delegate individual steps back to pi, with a fixed set of tools for each step.
|
|
6
|
+
|
|
7
|
+
The extension is available here: [https://github.com/deepclause/deepclause-pi](https://github.com/deepclause/deepclause-pi)
|
|
8
|
+
|
|
9
|
+
[The following is mostly written by a real person ;-]
|
|
10
|
+
|
|
11
|
+
I have spent quite a bit of time using DeepClause as a standalone agent runtime. That works well for benchmarks and self-contained workflows, but it leaves out something useful: the environment of a coding agent that is already running.
|
|
12
|
+
|
|
13
|
+
A coding agent such as pi already has:
|
|
14
|
+
|
|
15
|
+
- a selected model and working authentication
|
|
16
|
+
- the current conversation and compacted session history
|
|
17
|
+
- repository instructions and loaded skills
|
|
18
|
+
- file, shell and extension tools
|
|
19
|
+
- a terminal UI, cancellation and usage accounting
|
|
20
|
+
|
|
21
|
+
Reimplementing all of that in DeepClause would make little sense. The more useful option is to let pi remain the host and use DeepClause for the part it is good at: explicit orchestration.
|
|
22
|
+
|
|
23
|
+
This is what the new extension does.
|
|
24
|
+
|
|
25
|
+
[Image: `/dc` status and command overview]
|
|
26
|
+
|
|
27
|
+
### Running DML inside pi
|
|
28
|
+
|
|
29
|
+
The basic case is simple. DML programs live in `.pi/deepclause/skills/` and can be executed with a slash command:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
/dc-run example --debug
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The extension uses the model currently selected in pi. It does not ask for another API key or try to guess the provider. Model calls, cancellation, input prompts and usage stay connected to the current pi session.
|
|
36
|
+
|
|
37
|
+
A minimal skill still looks like ordinary DML:
|
|
38
|
+
|
|
39
|
+
```prolog
|
|
40
|
+
agent_main(Topic) :-
|
|
41
|
+
system("You are a concise technical analyst."),
|
|
42
|
+
format(string(Request),
|
|
43
|
+
"Explain ~w. Store the final explanation in Summary.",
|
|
44
|
+
[Topic]),
|
|
45
|
+
task(Request, string(Summary)),
|
|
46
|
+
answer(Summary).
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Run it as follows:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
/dc-run skills/explain.dml "constraint logic programming"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
There are three context modes:
|
|
56
|
+
|
|
57
|
+
- `turn` imports the current request and its immediate context
|
|
58
|
+
- `branch` imports a bounded part of the active session branch
|
|
59
|
+
- `isolated` starts without pi conversation history
|
|
60
|
+
|
|
61
|
+
For example:
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
/dc-run skills/explain.dml "constraint logic programming" --context=isolated
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Pi remains the session owner. DeepClause does not create another chat history next to it.
|
|
68
|
+
|
|
69
|
+
### Tools are deliberately boring
|
|
70
|
+
|
|
71
|
+
Ordinary DML programs do not receive pi’s complete tool registry. They start with two narrow host operations:
|
|
72
|
+
|
|
73
|
+
- `pi_workspace_list` lists one directory level inside the workspace
|
|
74
|
+
- `pi_bash` runs an approved command inside the workspace
|
|
75
|
+
|
|
76
|
+
Every `pi_bash` call requires user confirmation. Paths are checked against the active workspace, including resolved symlinks.
|
|
77
|
+
|
|
78
|
+
A DML program can wrap these operations in a more useful tool predicate:
|
|
79
|
+
|
|
80
|
+
```prolog
|
|
81
|
+
tool(bing_search(Query, Results),
|
|
82
|
+
"Search Bing RSS and return the response body") :-
|
|
83
|
+
format(string(QueryArg), "q=~w", [Query]),
|
|
84
|
+
exec(pi_bash("curl", [
|
|
85
|
+
"--fail", "--silent", "--show-error", "--location", "--get",
|
|
86
|
+
"--data-urlencode", QueryArg,
|
|
87
|
+
"https://www.bing.com/search?format=rss&count=8"
|
|
88
|
+
]), Result),
|
|
89
|
+
get_dict(stdout, Result, Results).
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The repository includes a small deep-research example built on this pattern. It asks the user to review a search plan through pi’s input UI, runs approved `curl` requests and produces a cited report.
|
|
93
|
+
|
|
94
|
+
I intentionally did not expose all coding tools directly through `exec/2`. Pi extensions can describe their tools, but there is no public generic API for another extension to invoke any tool by name. More importantly, copying tool execution into DeepClause would bypass behavior owned by pi or another extension, such as approval dialogs and policy checks.
|
|
95
|
+
|
|
96
|
+
This becomes relevant for planning.
|
|
97
|
+
|
|
98
|
+
### DML is the plan
|
|
99
|
+
|
|
100
|
+
The extension adds this command:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
/dc-plan <request> [--name=slug]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
For example:
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
/dc-plan build a small Three.js Space Invaders game and verify it --name=threejs-space-invaders
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
This starts a normal pi turn. The planner can inspect the repository, read its instructions, see loaded skills and consider the currently active tools. It does not directly return a Markdown checklist and it does not generate arbitrary DML source in one shot.
|
|
113
|
+
|
|
114
|
+
Instead, the planning turn finishes by calling a temporary tool named `dc_plan_commit`. The tool accepts a typed plan specification with:
|
|
115
|
+
|
|
116
|
+
- a title and objective
|
|
117
|
+
- ordered steps
|
|
118
|
+
- an executor for each step
|
|
119
|
+
- exact required tool names
|
|
120
|
+
- relevant pi skills
|
|
121
|
+
- an expected result for every step
|
|
122
|
+
- a fallback message
|
|
123
|
+
|
|
124
|
+
The extension validates this object and shows a preview. After user confirmation, it deterministically assembles the DML file, parses the generated program and writes it under `.pi/deepclause/plans/` without overwriting an existing file.
|
|
125
|
+
|
|
126
|
+
This follows the same general idea as the planner used in my DeepPlanning experiments: ask the model for a constrained intermediate representation and let normal code produce the executable DML. This is simpler and more reliable than asking the model to get every comma, variable and fallback clause right.
|
|
127
|
+
|
|
128
|
+
A generated plan looks like this:
|
|
129
|
+
|
|
130
|
+
```prolog
|
|
131
|
+
agent_main :-
|
|
132
|
+
output("Step 1/3: Inspect the workspace"),
|
|
133
|
+
exec(pi_agent_step(
|
|
134
|
+
instruction: "Inspect repository instructions and identify the target app.",
|
|
135
|
+
tools: ["bash", "read"],
|
|
136
|
+
expected: "A target directory and concrete implementation baseline.",
|
|
137
|
+
skills: []
|
|
138
|
+
), Step1Summary),
|
|
139
|
+
Step1Summary \= "",
|
|
140
|
+
|
|
141
|
+
output("Step 2/3: Implement the application"),
|
|
142
|
+
exec(pi_agent_step(
|
|
143
|
+
instruction: "Implement the agreed application and keep changes scoped.",
|
|
144
|
+
tools: ["read", "write", "edit"],
|
|
145
|
+
expected: "A runnable implementation with a concise change summary.",
|
|
146
|
+
skills: []
|
|
147
|
+
), Step2Summary),
|
|
148
|
+
Step2Summary \= "",
|
|
149
|
+
|
|
150
|
+
output("Step 3/3: Validate the result"),
|
|
151
|
+
exec(pi_agent_step(
|
|
152
|
+
instruction: "Run the relevant checks and fix implementation failures.",
|
|
153
|
+
tools: ["bash", "read", "write", "edit"],
|
|
154
|
+
expected: "Passing checks or a precise account of remaining failures.",
|
|
155
|
+
skills: []
|
|
156
|
+
), Step3Summary),
|
|
157
|
+
Step3Summary \= "",
|
|
158
|
+
|
|
159
|
+
answer([Step1Summary, Step2Summary, Step3Summary]).
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The full file is executable. There is no Markdown-to-DML compilation step in the pi integration.
|
|
163
|
+
|
|
164
|
+
[Image: generated DML plan in the editor]
|
|
165
|
+
|
|
166
|
+
### What `pi_agent_step` does
|
|
167
|
+
|
|
168
|
+
`pi_agent_step` is the bridge between DML orchestration and a normal pi coding turn.
|
|
169
|
+
|
|
170
|
+
When the DML runtime reaches one of these calls, the extension:
|
|
171
|
+
|
|
172
|
+
1. checks that every named tool is installed and currently active
|
|
173
|
+
2. rejects DeepClause control tools to prevent recursive planning or execution
|
|
174
|
+
3. saves pi’s current active-tool set
|
|
175
|
+
4. temporarily activates only the tools named by the step
|
|
176
|
+
5. sends the bounded instruction through a normal pi turn
|
|
177
|
+
6. captures the final textual summary and tool failures
|
|
178
|
+
7. restores the previous active-tool set on success, failure or cancellation
|
|
179
|
+
|
|
180
|
+
The important point is that pi still executes the turn. A third-party extension tool therefore keeps its own UI, approvals and policy. DeepClause only controls which tools are available to that particular step and what should happen next.
|
|
181
|
+
|
|
182
|
+
The user must start such a plan explicitly:
|
|
183
|
+
|
|
184
|
+
```text
|
|
185
|
+
/dc-run plans/threejs_space_invaders.dml
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Before execution, the extension shows the required tools and asks for confirmation.
|
|
189
|
+
|
|
190
|
+
The optional model-callable `dc_run` tool cannot execute contextual plans. Allowing an agent turn to start a plan that starts more agent turns would make recursion and session ownership needlessly difficult. For this first version, the boundary is simple: reusable self-contained skills may be model-called after explicit enablement; contextual plans are user-called.
|
|
191
|
+
|
|
192
|
+
### A concrete example
|
|
193
|
+
|
|
194
|
+
I used `/dc-plan` to create a five-step plan for a small Three.js Space Invaders game. The generated DML fixes the execution order:
|
|
195
|
+
|
|
196
|
+
1. inspect the workspace and choose the integration boundary
|
|
197
|
+
2. create the browser application foundation
|
|
198
|
+
3. implement deterministic game state and collision logic
|
|
199
|
+
4. connect the state to rendering, controls and UI
|
|
200
|
+
5. run tests and build the production bundle
|
|
201
|
+
|
|
202
|
+
Each step has a different tool set. Inspection gets `bash` and `read`. Pure implementation steps get `read`, `write` and `edit`. Validation gets all four. The plan records these requirements before any work starts.
|
|
203
|
+
|
|
204
|
+
This does not guarantee a correct game. The model can still write bad code, misunderstand an API or produce an incomplete summary. What it does guarantee is a more explicit execution structure. The agent cannot quietly skip from initial inspection to a confident final answer without the intervening DML goals succeeding.
|
|
205
|
+
|
|
206
|
+
This is the same reason I find DML useful with smaller models. Long context alone does not make long-running execution reliable. An explicit program can carry the sequence, checks, retries and fallback while the model deals with the parts that actually require judgment.
|
|
207
|
+
|
|
208
|
+
### Installation
|
|
209
|
+
|
|
210
|
+
The current release is `0.1.2` and depends on `deepclause-sdk` `0.0.87`.
|
|
211
|
+
|
|
212
|
+
Install directly from GitHub:
|
|
213
|
+
|
|
214
|
+
```sh
|
|
215
|
+
pi install git:github.com/deepclause/deepclause-pi
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Or install it for one project:
|
|
219
|
+
|
|
220
|
+
```sh
|
|
221
|
+
pi install git:github.com/deepclause/deepclause-pi -l
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
For a project-local installation, start pi in the directory containing `.pi/settings.json`. The startup screen should list the DeepClause extension. If it does not, `/dc-run` will be treated as an ordinary message and sent to the model.
|
|
225
|
+
|
|
226
|
+
Useful commands:
|
|
227
|
+
|
|
228
|
+
```text
|
|
229
|
+
/dc
|
|
230
|
+
/dc-list
|
|
231
|
+
/dc-plan inspect this repository and propose a safe migration --name=migration
|
|
232
|
+
/dc-run plans/migration.dml
|
|
233
|
+
/dc-tool enable
|
|
234
|
+
/dc-cancel
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
`dc_run` is disabled by default. `/dc-tool enable` makes it available to the model for that workspace. This does not grant ordinary DML programs access to all pi tools.
|
|
238
|
+
|
|
239
|
+
### Some more thoughts and notes
|
|
240
|
+
|
|
241
|
+
1. This is an early integration. The main execution path works and has tests for plan creation, tool preflight, contextual delegation, cancellation and tool restoration. There are still plenty of rough edges to find in real repositories.
|
|
242
|
+
|
|
243
|
+
2. `task/N` supports stream events, but the current pi model adapter receives a completed model response and forwards it as one chunk. Tool activity, DML progress, input requests and usage are already live.
|
|
244
|
+
|
|
245
|
+
3. Generated plans currently record tool names, but not stable hashes of their schemas. A tool can change between planning and execution. The runtime catches missing or inactive tools, while deeper schema-drift checks can be added later.
|
|
246
|
+
|
|
247
|
+
4. DML backtracking does not undo external effects. If a plan writes a file and later fails, Prolog can try another clause, but the file is still there. Generated coding plans therefore use ordered steps and explicit summaries rather than pretending that side effects are transactional.
|
|
248
|
+
|
|
249
|
+
5. Does this produce better coding results than an unconstrained pi turn? I have some good examples, but no benchmark yet. The useful claim for now is narrower: it gives us an executable, inspectable plan with a clear boundary around context and tools. Please try it and report what breaks.
|
|
250
|
+
|
|
251
|
+
Repository: [https://github.com/deepclause/deepclause-pi](https://github.com/deepclause/deepclause-pi)
|
|
252
|
+
|
|
253
|
+
DeepClause SDK: [https://github.com/deepclause/deepclause-sdk](https://github.com/deepclause/deepclause-sdk)
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# DML diagrams in `deepclause-pi` — minimal design
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
> The user asks pi to turn a DML file into a diagram — **presentation grade** or
|
|
6
|
+
> **specification grade**. When it's done, a viewer opens.
|
|
7
|
+
|
|
8
|
+
That's the whole feature. No command suite, no manual build/edit/check/clean
|
|
9
|
+
ritual. Pi does the work; the user just asks.
|
|
10
|
+
|
|
11
|
+
## How the user experiences it
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
User: “Make me a presentation-grade diagram of
|
|
15
|
+
skills/deep_research.dml”
|
|
16
|
+
|
|
17
|
+
pi: (calls the dc_diagram tool)
|
|
18
|
+
Creating a presentation-grade diagram for deep_research.dml…
|
|
19
|
+
(viewer opens in the browser at the Presentation view)
|
|
20
|
+
|
|
21
|
+
User: “Now a specification-grade one for the same file”
|
|
22
|
+
|
|
23
|
+
pi: (calls dc_diagram with grade=specification)
|
|
24
|
+
(viewer opens at the Specification view)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The DML file can live **anywhere** — a relative path, an absolute path, or a
|
|
28
|
+
path outside `.pi/deepclause/`. Pi points at it; the tool reads it.
|
|
29
|
+
|
|
30
|
+
Grade is inferred from the request, or passed explicitly:
|
|
31
|
+
`presentation` / `presentation-grade` → Presentation; `specification` /
|
|
32
|
+
`spec` / `detailed` / `technical` → Specification. If the user says "both",
|
|
33
|
+
produce both views.
|
|
34
|
+
|
|
35
|
+
## What the extension exposes
|
|
36
|
+
|
|
37
|
+
Exactly **one model-callable tool**:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
dc_diagram
|
|
41
|
+
dml : string // path to any .dml file (relative or absolute)
|
|
42
|
+
grade : "presentation" | "specification" | "both" // default: presentation
|
|
43
|
+
view : "flow" | "sequence" // optional, default: flow
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- Registered on startup (it is a safe, read-and-generate operation — it does not
|
|
47
|
+
execute the DML, unlike `dc_run`, so it does not need the `/dc-tool` opt-in).
|
|
48
|
+
- `promptGuidelines` tell pi to call it whenever the user asks for a diagram /
|
|
49
|
+
flowchart / visual of a DML file, to infer the grade from the wording, and not
|
|
50
|
+
to hand-write Mermaid itself.
|
|
51
|
+
- A short line in `AUTHORING_INSTRUCTION` / `.pi/deepclause/AGENTS.md` mentions
|
|
52
|
+
the tool and where diagrams live.
|
|
53
|
+
|
|
54
|
+
Optional, only if we want manual triggering: a single `/dc-diagram <path>
|
|
55
|
+
[--grade=...]` command that does exactly the same thing. Not required.
|
|
56
|
+
|
|
57
|
+
## What the tool does (one call, end to end)
|
|
58
|
+
|
|
59
|
+
1. Resolve the input path: relative to the workspace or absolute; require an
|
|
60
|
+
existing `.dml` file (`realpath` + extension check). No `.pi/deepclause/`
|
|
61
|
+
restriction on input.
|
|
62
|
+
2. Read the DML and compute a deterministic Mermaid **seed** in-process
|
|
63
|
+
(`renderDml` / `renderSequence`, ported from `dml_flow.mjs`). Always valid.
|
|
64
|
+
3. If a grade is requested, ask the active pi model to rewrite the seed in that
|
|
65
|
+
grade (reusing pi's model backend — no `pi_bash`, no API keys), then validate
|
|
66
|
+
and retry up to ~3 rounds:
|
|
67
|
+
- **Presentation**: ~8–12 nodes, plain language, headline numbers, 1–2
|
|
68
|
+
callouts, no function names or framework jargon.
|
|
69
|
+
- **Specification**: keep function names, `task`/`tool` roles,
|
|
70
|
+
post-conditions, seed data; precise for an engineer.
|
|
71
|
+
- Validation: structural checks always; real Mermaid parser via headless
|
|
72
|
+
Chrome only if Chrome is available. A broken result never reaches the
|
|
73
|
+
viewer.
|
|
74
|
+
4. Write the sidecar(s) under the active workspace's
|
|
75
|
+
`.pi/deepclause/diagrams/`: `<name>.presentation.mmd` and/or
|
|
76
|
+
`<name>.specification.mmd`.
|
|
77
|
+
5. (Re)build `viewer.html` (embedded manifest + vendored Mermaid).
|
|
78
|
+
6. **Open the viewer** at that diagram and view:
|
|
79
|
+
`xdg-open .pi/deepclause/diagrams/viewer.html?view=presentation#<name>`
|
|
80
|
+
(fixed argv via `pi.exec`, TUI/interactive only; otherwise just return the
|
|
81
|
+
path).
|
|
82
|
+
7. Return a one-line result: file, grade, and viewer path.
|
|
83
|
+
|
|
84
|
+
Progress and cancellation reuse the existing `/dc-run` UI plumbing
|
|
85
|
+
(`setWidget`/`setStatus`, a local `AbortController` wired to the active
|
|
86
|
+
execution so `/dc-cancel` still works).
|
|
87
|
+
|
|
88
|
+
## Names and collisions
|
|
89
|
+
|
|
90
|
+
The viewer is keyed by the DML **file name** (without `.dml`). If two DML files
|
|
91
|
+
with the same base name are diagrammed, disambiguate with a short path hash
|
|
92
|
+
(e.g. `deep_research-3f9a`) so sidecars and viewer entries never collide. The
|
|
93
|
+
full source path is always shown in the viewer.
|
|
94
|
+
|
|
95
|
+
## Artifacts
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
.pi/deepclause/diagrams/ # in the active workspace, regardless
|
|
99
|
+
├── viewer.html # of where the DML source lives
|
|
100
|
+
├── index.json # manifest (also handy for tests/scripts)
|
|
101
|
+
├── <name>.md # Mermaid fences for editors
|
|
102
|
+
├── <name>.presentation.mmd # presentation-grade sidecar
|
|
103
|
+
└── <name>.specification.mmd # specification-grade sidecar
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- Created lazily on first use; `initializeWorkspace()` stays untouched.
|
|
107
|
+
- Output always stays under `.pi/deepclause/`; no `.deepclause/`, no `docs/`.
|
|
108
|
+
- Regenerating a grade overwrites only that sidecar (the user asked for it);
|
|
109
|
+
the other grade and other diagrams are preserved.
|
|
110
|
+
|
|
111
|
+
## Viewer
|
|
112
|
+
|
|
113
|
+
Reuse the handbook `viewer.template.html` nearly verbatim, with the sidebar
|
|
114
|
+
list, theme picker, split pane (editable Mermaid + read-only DML), SVG/PNG
|
|
115
|
+
export and hash deep-links. The view dropdown becomes:
|
|
116
|
+
|
|
117
|
+
`Presentation → Specification → Flow → Sequence`
|
|
118
|
+
|
|
119
|
+
Fallback order for the default view: Presentation → Specification → Flow.
|
|
120
|
+
|
|
121
|
+
## Code changes
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
src/diagram/extract.ts # TS port of dml_flow.mjs (renderDml, renderSequence)
|
|
125
|
+
src/diagram/grade.ts # model rewrite + validation/retry loop
|
|
126
|
+
src/diagram/viewer.ts # write sidecars, build viewer.html, open browser
|
|
127
|
+
src/assets/viewer.template.html # adapted; Presentation/Specification labels
|
|
128
|
+
src/assets/vendor/mermaid.min.js # vendored, written on first build
|
|
129
|
+
src/runtime.ts # export a small completeWithPiModel() helper
|
|
130
|
+
src/index.ts # register dc_diagram + authoring note
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Policy
|
|
134
|
+
|
|
135
|
+
- No compiler, no `.deepclause/`, no DML execution.
|
|
136
|
+
- Input may be any readable `.dml` path; **output** is confined to the active
|
|
137
|
+
workspace's `.pi/deepclause/diagrams/`.
|
|
138
|
+
- Does not register runtime tools and does not widen `pi_bash`/approval scope.
|
|
139
|
+
- Browser opening uses a fixed command, not a shell string.
|
|
140
|
+
|
|
141
|
+
## Validation
|
|
142
|
+
|
|
143
|
+
- Asking pi for a diagram in natural language results in a `dc_diagram` call and
|
|
144
|
+
an opened viewer.
|
|
145
|
+
- A DML file outside `.pi/deepclause/` (including an absolute path) works.
|
|
146
|
+
- Presentation vs specification wording selects the right grade; "both"
|
|
147
|
+
produces both.
|
|
148
|
+
- Chrome present → real-parser gate; Chrome absent → structural gate, viewer
|
|
149
|
+
still opens.
|
|
150
|
+
- Invalid/partial model output is retried and never written as a final sidecar.
|
|
151
|
+
- Missing file / non-`.dml` input is rejected.
|
|
152
|
+
- Same-name DML files do not collide in the viewer.
|
|
153
|
+
- No approval prompts are raised.
|
|
154
|
+
- No files appear outside `.pi/deepclause/diagrams/`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepclause-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Pi-hosted runtime for DeepClause DML programs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,11 +25,15 @@
|
|
|
25
25
|
"files": [
|
|
26
26
|
"dist",
|
|
27
27
|
"docs",
|
|
28
|
-
"src"
|
|
28
|
+
"src",
|
|
29
|
+
"skills"
|
|
29
30
|
],
|
|
30
31
|
"pi": {
|
|
31
32
|
"extensions": [
|
|
32
33
|
"./src/index.ts"
|
|
34
|
+
],
|
|
35
|
+
"skills": [
|
|
36
|
+
"./skills"
|
|
33
37
|
]
|
|
34
38
|
},
|
|
35
39
|
"scripts": {
|