dsh-logicprobe 0.7.1 → 0.8.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.en-US.md +89 -58
- package/README.md +76 -46
- package/lib/index.js +11 -4
- package/lib/json-value.js +18 -0
- package/lib/types/json-value.d.ts +21 -0
- package/lib/types/uml-tool.d.ts +14 -0
- package/lib/types/uml.d.ts +167 -0
- package/lib/uml-tool.js +104 -0
- package/lib/uml.js +1544 -0
- package/package.json +11 -10
- package/skills/logicprobe/SKILL.md +36 -1
- package/skills/logicprobe/references/logicprobe-engine.py +1586 -2
- package/skills/logicprobe/references/uml-modeling-guide.md +166 -0
- package/src/compose-tool.ts +2 -1
- package/src/concurrency-tool.ts +2 -1
- package/src/data-tool.ts +2 -1
- package/src/export-tool.ts +2 -1
- package/src/index.ts +11 -4
- package/src/json-value.ts +20 -0
- package/src/tool.ts +2 -1
- package/src/uml-tool.ts +106 -0
- package/src/uml.ts +1461 -0
- package/skills/logicprobe/references/__pycache__/logicprobe-engine.cpython-310.pyc +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural mirror of the JSON value union the dsh tool registry accepts.
|
|
3
|
+
*
|
|
4
|
+
* `@deepseek-ai/dsh-tools` exported this type through the 0.1.x line and stopped
|
|
5
|
+
* exporting it in 0.2.x, where the same type now lives in
|
|
6
|
+
* `@deepseek-ai/dsh-util-values`. This package declares peer support for both
|
|
7
|
+
* lines, so it cannot import the type from either one without breaking the other
|
|
8
|
+
* build. Declaring the one-line union here keeps `npm run typecheck` green against
|
|
9
|
+
* both, and it costs nothing at runtime: every use is a type-only cast on a value
|
|
10
|
+
* this package already produced.
|
|
11
|
+
*
|
|
12
|
+
* Keep it structurally identical to the official type. A cast through `unknown` to
|
|
13
|
+
* this alias is only a way to satisfy `defineTool`'s return-type constraint; if the
|
|
14
|
+
* official union ever widens, this alias must widen with it.
|
|
15
|
+
*
|
|
16
|
+
* @module logicprobe-json-value
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural mirror of the JSON value union the dsh tool registry accepts.
|
|
3
|
+
*
|
|
4
|
+
* `@deepseek-ai/dsh-tools` exported this type through the 0.1.x line and stopped
|
|
5
|
+
* exporting it in 0.2.x, where the same type now lives in
|
|
6
|
+
* `@deepseek-ai/dsh-util-values`. This package declares peer support for both
|
|
7
|
+
* lines, so it cannot import the type from either one without breaking the other
|
|
8
|
+
* build. Declaring the one-line union here keeps `npm run typecheck` green against
|
|
9
|
+
* both, and it costs nothing at runtime: every use is a type-only cast on a value
|
|
10
|
+
* this package already produced.
|
|
11
|
+
*
|
|
12
|
+
* Keep it structurally identical to the official type. A cast through `unknown` to
|
|
13
|
+
* this alias is only a way to satisfy `defineTool`'s return-type constraint; if the
|
|
14
|
+
* official union ever widens, this alias must widen with it.
|
|
15
|
+
*
|
|
16
|
+
* @module logicprobe-json-value
|
|
17
|
+
*/
|
|
18
|
+
/** JSON-serializable value: null, boolean, number, string, or an array/object of those. */
|
|
19
|
+
export type JsonValue = null | boolean | number | string | JsonValue[] | {
|
|
20
|
+
[key: string]: JsonValue;
|
|
21
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const LOGICPROBE_UML_TOOL_NAME = "logicprobe_uml";
|
|
2
|
+
/**
|
|
3
|
+
* DSH tool wrapping the UML front end: model a code flow as a UML diagram
|
|
4
|
+
* (`action: "render"`), read a UML diagram back into a LogicModelV1
|
|
5
|
+
* (`action: "parse"`), or review the modelling itself (`action: "review"`).
|
|
6
|
+
*
|
|
7
|
+
* review is the half that makes the feature a check rather than a drawing
|
|
8
|
+
* utility: it reports structural defects the diagram would present as valid
|
|
9
|
+
* flow (unreachable states, dead ends, ambiguous and non-exhaustive branches,
|
|
10
|
+
* self-loops with no exit), documentation gaps (a symbol no reader can map back
|
|
11
|
+
* to code), and — the fidelity check — whether the rendered diagram reads back
|
|
12
|
+
* as the model it was drawn from.
|
|
13
|
+
*/
|
|
14
|
+
export declare const logicProbeUmlTool: import("@deepseek-ai/dsh-tools").ToolDefinition;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UML front end for LogicModelV1 — model a code flow as a UML diagram, then
|
|
3
|
+
* review the modelling itself.
|
|
4
|
+
*
|
|
5
|
+
* Two halves, one data flow:
|
|
6
|
+
*
|
|
7
|
+
* 1. `renderUml` turns a validated LogicModelV1 into Mermaid or PlantUML text
|
|
8
|
+
* (state machine, activity/flow, or sequence trace). The diagram is a
|
|
9
|
+
* *view*: it never invents structure the model does not have, and anything
|
|
10
|
+
* the notation cannot express is reported as a warning instead of being
|
|
11
|
+
* dropped silently.
|
|
12
|
+
* 2. `parseUml` reads that text back into a LogicModelV1, and `reviewUml`
|
|
13
|
+
* compares the two. That comparison is the point of the feature: a UML
|
|
14
|
+
* diagram of a code flow is itself a model, and a model can be wrong —
|
|
15
|
+
* ambiguous branches, dead ends, flows nobody can enter, symbols the
|
|
16
|
+
* source never names. A diagram that does not round-trip to the model it
|
|
17
|
+
* was drawn from is mis-modelled, and the review says so.
|
|
18
|
+
*
|
|
19
|
+
* Why the round trip is the fidelity check: rendering and parsing are inverse
|
|
20
|
+
* only if every construct survives the notation. The generated text carries
|
|
21
|
+
* `logicprobe:` directives (ignored by Mermaid/PlantUML renderers) that pin the
|
|
22
|
+
* initial state, the terminal states and any state id the notation cannot spell
|
|
23
|
+
* verbatim, so an exact comparison is possible rather than a fuzzy one.
|
|
24
|
+
*
|
|
25
|
+
* The review deliberately does NOT replace `logicprobe_verify`: it checks the
|
|
26
|
+
* modelling (structure the diagram claims, documentation coverage, notation
|
|
27
|
+
* fidelity), while S1-S8/A1-A14 check the machine's behaviour (guard
|
|
28
|
+
* exhaustiveness under real valuations, invariant paths, deadlock/liveness in
|
|
29
|
+
* the runtime state space). Findings name the engine check to run next.
|
|
30
|
+
*
|
|
31
|
+
* @module logicprobe-uml
|
|
32
|
+
*/
|
|
33
|
+
import type { GuardNode, LogicModelV1, UpdateSpec } from './engine.js';
|
|
34
|
+
export type UmlNotation = 'mermaid' | 'plantuml';
|
|
35
|
+
export type UmlDiagram = 'state' | 'activity' | 'sequence';
|
|
36
|
+
export declare const UML_NOTATIONS: readonly UmlNotation[];
|
|
37
|
+
export declare const UML_DIAGRAMS: readonly UmlDiagram[];
|
|
38
|
+
export interface UmlRenderResult {
|
|
39
|
+
notation: UmlNotation;
|
|
40
|
+
diagram: UmlDiagram;
|
|
41
|
+
/** The diagram source; hand this to the user or a renderer as-is. */
|
|
42
|
+
primary: string;
|
|
43
|
+
warnings: string[];
|
|
44
|
+
}
|
|
45
|
+
export interface UmlParseResult {
|
|
46
|
+
notation: UmlNotation;
|
|
47
|
+
diagram: UmlDiagram;
|
|
48
|
+
model: LogicModelV1;
|
|
49
|
+
/**
|
|
50
|
+
* Display labels found in the diagram, keyed by state id. They are how a
|
|
51
|
+
* reader learns what a symbol means; a missing entry is an undocumented
|
|
52
|
+
* symbol, which the review reports.
|
|
53
|
+
*/
|
|
54
|
+
labels: Record<string, string>;
|
|
55
|
+
warnings: string[];
|
|
56
|
+
}
|
|
57
|
+
export interface UmlFinding {
|
|
58
|
+
code: string;
|
|
59
|
+
severity: 'error' | 'warning' | 'info';
|
|
60
|
+
message: string;
|
|
61
|
+
states?: string[];
|
|
62
|
+
events?: string[];
|
|
63
|
+
transitions?: Array<{
|
|
64
|
+
from: string;
|
|
65
|
+
event: string;
|
|
66
|
+
to: string;
|
|
67
|
+
}>;
|
|
68
|
+
detail?: string;
|
|
69
|
+
}
|
|
70
|
+
export interface UmlRoundTripReport {
|
|
71
|
+
notation: UmlNotation;
|
|
72
|
+
diagram: UmlDiagram;
|
|
73
|
+
ok: boolean;
|
|
74
|
+
modelHash: string;
|
|
75
|
+
parsedHash: string;
|
|
76
|
+
diffs: string[];
|
|
77
|
+
warnings: string[];
|
|
78
|
+
}
|
|
79
|
+
export interface UmlReviewReport {
|
|
80
|
+
ok: boolean;
|
|
81
|
+
source: 'model' | 'diagram' | 'model+diagram';
|
|
82
|
+
summary: {
|
|
83
|
+
errors: number;
|
|
84
|
+
warnings: number;
|
|
85
|
+
info: number;
|
|
86
|
+
states: number;
|
|
87
|
+
events: number;
|
|
88
|
+
transitions: number;
|
|
89
|
+
terminalStates: number;
|
|
90
|
+
reachableStates: number;
|
|
91
|
+
documentedStates: number;
|
|
92
|
+
};
|
|
93
|
+
findings: UmlFinding[];
|
|
94
|
+
roundTrip: UmlRoundTripReport | null;
|
|
95
|
+
/** Diagram display labels, when a diagram took part in the review. */
|
|
96
|
+
labels?: Record<string, string>;
|
|
97
|
+
/** Model parsed from the diagram, when a diagram was given (feed it to `logicprobe_verify`). */
|
|
98
|
+
model?: LogicModelV1;
|
|
99
|
+
/** Diagram rendered from the model, when only a model was given. */
|
|
100
|
+
primary?: string;
|
|
101
|
+
warnings: string[];
|
|
102
|
+
nextSteps: string[];
|
|
103
|
+
}
|
|
104
|
+
export declare class UmlError extends Error {
|
|
105
|
+
}
|
|
106
|
+
/** Canonical guard text. Rendering wraps every composite node in parentheses, and the parser flattens same-operator chains, so render∘parse is the identity. */
|
|
107
|
+
export declare function guardText(node: GuardNode): string;
|
|
108
|
+
/**
|
|
109
|
+
* Render a LogicModelV1 as UML.
|
|
110
|
+
*
|
|
111
|
+
* PlantUML has no faithful activity view here: its activity syntax is a
|
|
112
|
+
* structured flowchart language, so a graph with merges or cycles needs a
|
|
113
|
+
* while/if reconstruction this module does not perform. Refusing is the honest
|
|
114
|
+
* outcome — quietly emitting a state diagram under an "activity" request would
|
|
115
|
+
* mislabel the model. Mermaid covers all three views.
|
|
116
|
+
*
|
|
117
|
+
* @param input - candidate LogicModelV1.
|
|
118
|
+
* @param notation - `mermaid` (default) or `plantuml`.
|
|
119
|
+
* @param diagram - `state` (default), `activity`, or `sequence`.
|
|
120
|
+
* @param maxSteps - cap on the sequence trace length.
|
|
121
|
+
*/
|
|
122
|
+
export declare function renderUml(input: unknown, notation?: UmlNotation, diagram?: UmlDiagram, maxSteps?: number): UmlRenderResult;
|
|
123
|
+
/** Parse a guard expression such as `(retry < 3 && armed == true)`. */
|
|
124
|
+
export declare function parseGuardText(text: string): GuardNode;
|
|
125
|
+
/** Parse a UML action clause such as `retry := retry + 1, armed := true`. */
|
|
126
|
+
export declare function parseUpdatesText(text: string, warnings: string[]): UpdateSpec[];
|
|
127
|
+
/**
|
|
128
|
+
* Parse a Mermaid or PlantUML diagram back into a LogicModelV1.
|
|
129
|
+
*
|
|
130
|
+
* State and activity diagrams carry the whole machine, so they parse into a
|
|
131
|
+
* complete model. Sequence diagrams do not: a trace shows the paths that were
|
|
132
|
+
* walked, not the branches that were not, so parsing one would silently prune
|
|
133
|
+
* the machine. That case is refused rather than approximated.
|
|
134
|
+
*
|
|
135
|
+
* @param text - diagram source.
|
|
136
|
+
* @param notation - `auto` (default) detects Mermaid vs PlantUML from the text.
|
|
137
|
+
*/
|
|
138
|
+
export declare function parseUml(text: string, notation?: UmlNotation | 'auto'): UmlParseResult;
|
|
139
|
+
/** Compare two machines by structure — the fidelity measure behind the round-trip check. */
|
|
140
|
+
export declare function diffModels(left: LogicModelV1, right: LogicModelV1): string[];
|
|
141
|
+
export interface UmlReviewOptions {
|
|
142
|
+
/** LogicModelV1 to review. Provide it, `diagram`, or both. */
|
|
143
|
+
model?: unknown;
|
|
144
|
+
/** Diagram text: reviewed on its own, or compared against `model` when both are given. */
|
|
145
|
+
diagram?: string;
|
|
146
|
+
notation?: UmlNotation | 'auto';
|
|
147
|
+
diagramKind?: UmlDiagram;
|
|
148
|
+
/** Render-and-reparse fidelity check (default true; only meaningful with a model). */
|
|
149
|
+
roundTrip?: boolean;
|
|
150
|
+
/** Cap on the sequence trace used for the fidelity check. */
|
|
151
|
+
maxSteps?: number;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Review a UML model of a code flow.
|
|
155
|
+
*
|
|
156
|
+
* Three inputs are possible and each answers a different question:
|
|
157
|
+
*
|
|
158
|
+
* - `model` only — "is this machine well-modelled?" The review checks the
|
|
159
|
+
* structure the diagram would draw, then renders and re-parses it to prove the
|
|
160
|
+
* diagram carries the machine faithfully (round trip).
|
|
161
|
+
* - `diagram` only — "what does this diagram actually say?" The diagram is parsed
|
|
162
|
+
* into a model, and that model is reviewed; nothing can be said about fidelity
|
|
163
|
+
* to a machine the caller did not provide.
|
|
164
|
+
* - both — "does this diagram match this model?" Any structural difference is a
|
|
165
|
+
* modelling defect and is reported both as round-trip diffs and as a finding.
|
|
166
|
+
*/
|
|
167
|
+
export declare function reviewUml(options: UmlReviewOptions): UmlReviewReport;
|
package/lib/uml-tool.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
2
|
+
import { renderUml, parseUml, reviewUml, UmlError } from './uml.js';
|
|
3
|
+
export const LOGICPROBE_UML_TOOL_NAME = 'logicprobe_uml';
|
|
4
|
+
/**
|
|
5
|
+
* DSH tool wrapping the UML front end: model a code flow as a UML diagram
|
|
6
|
+
* (`action: "render"`), read a UML diagram back into a LogicModelV1
|
|
7
|
+
* (`action: "parse"`), or review the modelling itself (`action: "review"`).
|
|
8
|
+
*
|
|
9
|
+
* review is the half that makes the feature a check rather than a drawing
|
|
10
|
+
* utility: it reports structural defects the diagram would present as valid
|
|
11
|
+
* flow (unreachable states, dead ends, ambiguous and non-exhaustive branches,
|
|
12
|
+
* self-loops with no exit), documentation gaps (a symbol no reader can map back
|
|
13
|
+
* to code), and — the fidelity check — whether the rendered diagram reads back
|
|
14
|
+
* as the model it was drawn from.
|
|
15
|
+
*/
|
|
16
|
+
export const logicProbeUmlTool = defineTool({
|
|
17
|
+
name: LOGICPROBE_UML_TOOL_NAME,
|
|
18
|
+
description: 'Model a code flow as UML, and review the modelling. action="render" turns a LogicModelV1 (schemaVersion=1) into diagram source: notation mermaid (state | activity flowchart | sequence) or plantuml (state | sequence). action="parse" reads Mermaid/PlantUML state or activity text back into a LogicModelV1 (plus the display labels it found), so a hand-drawn diagram can be verified with logicprobe_verify; a sequence diagram is refused because a trace cannot reconstruct a machine. action="review" audits the modelling: structural defects (UML002 unreachable state, UML003 dead end, UML004 ambiguous branch, UML005 overlapping guard, UML006 inexhaustive branch, UML007 unused event, UML008 self-loop with no exit, UML009 duplicate transition), documentation gaps (UML010 unused variable, UML011 unbounded variable, UML012 no terminal, UML013 no narrative, UML014 undocumented state, UML015 label drift), and the fidelity check — the diagram is rendered and re-parsed and any structural difference is reported as UML017 round-trip mismatch. Give review a model (checks it, renders and re-parses it), a diagram (parses and reviews that), or both (checks the diagram against the model). Rendering never invents structure and never silently drops a construct the notation cannot express: those become warnings. Review does NOT replace logicprobe_verify — it covers the modelling, not behaviour; findings name the engine check to run next.',
|
|
19
|
+
parameters: {
|
|
20
|
+
action: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
required: true,
|
|
23
|
+
enum: ['render', 'parse', 'review'],
|
|
24
|
+
description: 'render = model → UML source; parse = UML source → model; review = audit the modelling (and its fidelity to the model).',
|
|
25
|
+
},
|
|
26
|
+
model: {
|
|
27
|
+
type: 'json',
|
|
28
|
+
description: 'LogicModelV1 machine. Required for render; accepted by review (alone, or next to diagram to check the two against each other).',
|
|
29
|
+
},
|
|
30
|
+
diagram: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
description: 'UML source text. Required for parse; accepted by review.',
|
|
33
|
+
},
|
|
34
|
+
notation: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
enum: ['auto', 'mermaid', 'plantuml'],
|
|
37
|
+
description: 'Diagram language. Default auto: detected from the text for parse/review, mermaid for render.',
|
|
38
|
+
},
|
|
39
|
+
kind: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
enum: ['state', 'activity', 'sequence'],
|
|
42
|
+
description: 'Diagram kind to render. Default state. sequence is one BFS trace, not the whole machine. PlantUML activity is refused (its structured-flowchart syntax cannot faithfully carry a graph with merges or cycles) — use mermaid for that view.',
|
|
43
|
+
},
|
|
44
|
+
roundTrip: {
|
|
45
|
+
type: 'boolean',
|
|
46
|
+
description: 'review only: render and re-parse the model to prove the diagram carries it. Default true.',
|
|
47
|
+
},
|
|
48
|
+
maxSteps: {
|
|
49
|
+
type: 'integer',
|
|
50
|
+
description: 'Cap on the sequence trace length when rendering kind=sequence. Default 60.',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
output: {
|
|
54
|
+
schema: {
|
|
55
|
+
type: 'json',
|
|
56
|
+
description: 'Render result (diagram source), parse result (LogicModelV1 + labels), or the modelling review report with findings, round-trip diff and next steps.',
|
|
57
|
+
},
|
|
58
|
+
render(_args, value) {
|
|
59
|
+
const record = value;
|
|
60
|
+
// A rendered diagram is source text: print it verbatim so it can be copied
|
|
61
|
+
// into a file, then the structured result (warnings, notation, kind).
|
|
62
|
+
if (typeof record.diagram === 'string') {
|
|
63
|
+
return [
|
|
64
|
+
{ type: 'text', text: record.diagram },
|
|
65
|
+
{ type: 'text', text: JSON.stringify(value, null, 2) },
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
return [{ type: 'text', text: JSON.stringify(value, null, 2) }];
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
timeoutMs: 10_000,
|
|
72
|
+
isConcurrencySafe: () => true,
|
|
73
|
+
async execute(args) {
|
|
74
|
+
try {
|
|
75
|
+
if (args.action === 'render') {
|
|
76
|
+
if (args.model === undefined)
|
|
77
|
+
return errorResult('action=render needs `model` (a LogicModelV1 object)');
|
|
78
|
+
const result = renderUml(args.model, (args.notation === undefined || args.notation === 'auto' ? 'mermaid' : args.notation), (args.kind ?? 'state'), args.maxSteps);
|
|
79
|
+
return { ok: true, action: 'render', notation: result.notation, kind: result.diagram, diagram: result.primary, warnings: result.warnings };
|
|
80
|
+
}
|
|
81
|
+
if (args.action === 'parse') {
|
|
82
|
+
if (args.diagram === undefined)
|
|
83
|
+
return errorResult('action=parse needs `diagram` (Mermaid or PlantUML text)');
|
|
84
|
+
const result = parseUml(args.diagram, args.notation ?? 'auto');
|
|
85
|
+
return { ok: true, action: 'parse', notation: result.notation, kind: result.diagram, model: result.model, labels: result.labels, warnings: result.warnings };
|
|
86
|
+
}
|
|
87
|
+
const report = reviewUml({
|
|
88
|
+
model: args.model,
|
|
89
|
+
diagram: args.diagram,
|
|
90
|
+
notation: args.notation ?? 'auto',
|
|
91
|
+
diagramKind: (args.kind ?? 'state'),
|
|
92
|
+
roundTrip: args.roundTrip,
|
|
93
|
+
maxSteps: args.maxSteps,
|
|
94
|
+
});
|
|
95
|
+
return report;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
return errorResult(error instanceof Error ? error.message : String(error), error instanceof UmlError);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
function errorResult(message, isUmlError = false) {
|
|
103
|
+
return { ok: false, ...(isUmlError ? { errorCode: 'UML_INPUT' } : {}), error: message };
|
|
104
|
+
}
|