dsh-logicprobe 0.9.0 → 1.1.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.
Files changed (63) hide show
  1. package/README.en-US.md +128 -66
  2. package/README.md +128 -66
  3. package/lib/baseline.js +142 -0
  4. package/lib/client.js +56 -1
  5. package/lib/compose-tool.js +1 -1
  6. package/lib/concurrency.js +14 -0
  7. package/lib/data-engine.js +42 -8
  8. package/lib/engine.js +3151 -2801
  9. package/lib/export-tool.js +27 -2
  10. package/lib/granularity.js +237 -0
  11. package/lib/index.js +63 -41
  12. package/lib/report-diff-tool.js +39 -0
  13. package/lib/structure-tool.js +64 -0
  14. package/lib/structure.js +648 -0
  15. package/lib/tool.js +3 -3
  16. package/lib/types/baseline.d.ts +69 -0
  17. package/lib/types/concurrency.d.ts +10 -0
  18. package/lib/types/data-engine.d.ts +13 -1
  19. package/lib/types/engine.d.ts +419 -277
  20. package/lib/types/granularity.d.ts +106 -0
  21. package/lib/types/index.d.ts +9 -8
  22. package/lib/types/report-diff-tool.d.ts +8 -0
  23. package/lib/types/structure-tool.d.ts +10 -0
  24. package/lib/types/structure.d.ts +156 -0
  25. package/lib/types/tool.d.ts +2 -2
  26. package/lib/types/uml.d.ts +89 -0
  27. package/lib/uml-tool.js +146 -104
  28. package/lib/uml.js +1748 -1544
  29. package/locale/en.json +6 -0
  30. package/locale/zh.json +6 -0
  31. package/package.json +16 -14
  32. package/skills/logicprobe/SKILL.md +84 -58
  33. package/skills/logicprobe/references/dsh-model-schema.md +102 -10
  34. package/skills/logicprobe/references/gap-routing-guide.md +37 -36
  35. package/skills/logicprobe/references/hash-spec.md +153 -0
  36. package/skills/logicprobe/references/logic-verification-guide.md +27 -250
  37. package/skills/logicprobe-concurrency/SKILL.md +43 -0
  38. package/skills/{logicprobe → logicprobe-concurrency}/references/concurrency-risk-guide.md +1 -1
  39. package/skills/logicprobe-datamodel/SKILL.md +5 -4
  40. package/skills/logicprobe-datamodel/references/data-model-guide.md +1 -1
  41. package/skills/logicprobe-datamodel/references/data-model-schema.md +1 -1
  42. package/skills/logicprobe-structure/SKILL.md +95 -0
  43. package/skills/logicprobe-structure/references/structure-review-guide.md +177 -0
  44. package/skills/logicprobe-uml/SKILL.md +57 -0
  45. package/skills/{logicprobe → logicprobe-uml}/references/uml-modeling-guide.md +121 -8
  46. package/src/baseline.ts +180 -0
  47. package/src/client.js +56 -1
  48. package/src/compose-tool.ts +47 -47
  49. package/src/concurrency.ts +23 -0
  50. package/src/data-engine.ts +51 -9
  51. package/src/engine.ts +503 -72
  52. package/src/export-tool.ts +73 -48
  53. package/src/granularity.ts +312 -0
  54. package/src/index.ts +63 -41
  55. package/src/report-diff-tool.ts +43 -0
  56. package/src/structure-tool.ts +68 -0
  57. package/src/structure.ts +724 -0
  58. package/src/tool.ts +3 -3
  59. package/src/uml-tool.ts +53 -10
  60. package/src/uml.ts +288 -24
  61. package/skills/logicprobe/references/logicprobe-engine.py +0 -4943
  62. package/skills/logicprobe/references/verification-harness.py +0 -959
  63. package/skills/logicprobe-datamodel/references/data-model-harness.py +0 -528
package/lib/uml-tool.js CHANGED
@@ -1,104 +1,146 @@
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
- }
1
+ import { defineTool } from '@deepseek-ai/dsh-tools';
2
+ import { createHash } from 'node:crypto';
3
+ import { REPORT_SCHEMAS, DEFAULT_HASH_SPEC, modelHash, refusalVerdict, verdictOf, verdictOfFindings } from './engine.js';
4
+ import { renderUml, parseUml, parseFindings, reviewUml, explainLabels, UmlError } from './uml.js';
5
+ export const LOGICPROBE_UML_TOOL_NAME = 'logicprobe_uml';
6
+ /**
7
+ * DSH tool wrapping the UML front end: model a code flow as a UML diagram
8
+ * (`action: "render"`), read a UML diagram back into a LogicModelV1
9
+ * (`action: "parse"`), or review the modelling itself (`action: "review"`).
10
+ *
11
+ * review is the half that makes the feature a check rather than a drawing
12
+ * utility: it reports structural defects the diagram would present as valid
13
+ * flow (unreachable states, dead ends, ambiguous and non-exhaustive branches,
14
+ * self-loops with no exit), documentation gaps (a symbol no reader can map back
15
+ * to code), and — the fidelity check — whether the rendered diagram reads back
16
+ * as the model it was drawn from.
17
+ */
18
+ export const logicProbeUmlTool = defineTool({
19
+ name: LOGICPROBE_UML_TOOL_NAME,
20
+ description: 'Model a code flow as UML, and audit the modelling. action="render" turns a LogicModelV1 into diagram source: mermaid (state, activity, sequence) or plantuml (state, sequence). action="parse" reads Mermaid or PlantUML state or activity text back into a LogicModelV1, so a hand-drawn diagram can be verified. A sequence diagram is refused: a trace cannot reconstruct a machine. So is any other diagram family (class, ER, gantt, mindmap). That refusal carries errorCode "UML_NOT_A_STATE_DIAGRAM". A PlantUML component, package or deployment diagram declares no kind, so the parser detects those constructs itself. It returns a by-product model and reports the error UML_NOT_A_STATE_DIAGRAM, with `discardedConstructs` and `discardedEdges`. Never treat that model as a model of the file. action="review" audits the modelling: structural defects (UML002-UML009), documentation gaps (UML010-UML015), and a fidelity check that re-parses the rendered diagram (UML017). Give it a model, a diagram, or both. action="explain-labels" prints the label and comment conventions the parser accepts, without needing a diagram. Rendering never invents structure. A construct the notation cannot express becomes a warning. For dependency questions on a component or class diagram, use `logicprobe_structure_verify` instead. Read `verdict`, not `ok`: error findings mean a failed review.',
21
+ parameters: {
22
+ action: {
23
+ type: 'string',
24
+ required: true,
25
+ enum: ['render', 'parse', 'review', 'explain-labels'],
26
+ description: 'render = model → UML source; parse = UML source → model; review = audit the modelling (and its fidelity to the model); explain-labels = print the label and comment conventions this parser accepts.',
27
+ },
28
+ model: {
29
+ type: 'json',
30
+ description: 'LogicModelV1 machine. Required for render; accepted by review (alone, or next to diagram to check the two against each other).',
31
+ },
32
+ diagram: {
33
+ type: 'string',
34
+ description: 'UML source text. Required for parse; accepted by review.',
35
+ },
36
+ notation: {
37
+ type: 'string',
38
+ enum: ['auto', 'mermaid', 'plantuml'],
39
+ description: 'Diagram language. Default auto: detected from the text for parse/review, mermaid for render.',
40
+ },
41
+ kind: {
42
+ type: 'string',
43
+ enum: ['state', 'activity', 'sequence'],
44
+ 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.',
45
+ },
46
+ roundTrip: {
47
+ type: 'boolean',
48
+ description: 'review only: render and re-parse the model to prove the diagram carries it. Default true.',
49
+ },
50
+ maxSteps: {
51
+ type: 'integer',
52
+ description: 'Cap on the sequence trace length when rendering kind=sequence. Default 60.',
53
+ },
54
+ },
55
+ output: {
56
+ schema: {
57
+ type: 'json',
58
+ description: 'Render result (diagram source), parse result (LogicModelV1 + labels), or the modelling review report with findings, round-trip diff and next steps.',
59
+ },
60
+ render(_args, value) {
61
+ const record = value;
62
+ // A rendered diagram is source text: print it verbatim so it can be copied
63
+ // into a file, then the structured result (warnings, notation, kind).
64
+ if (typeof record.diagram === 'string') {
65
+ return [
66
+ { type: 'text', text: record.diagram },
67
+ { type: 'text', text: JSON.stringify(value, null, 2) },
68
+ ];
69
+ }
70
+ return [{ type: 'text', text: JSON.stringify(value, null, 2) }];
71
+ },
72
+ },
73
+ timeoutMs: 10_000,
74
+ isConcurrencySafe: () => true,
75
+ async execute(args) {
76
+ try {
77
+ if (args.action === 'render') {
78
+ if (args.model === undefined)
79
+ return errorResult('action=render needs `model` (a LogicModelV1 object)');
80
+ const result = renderUml(args.model, (args.notation === undefined || args.notation === 'auto' ? 'mermaid' : args.notation), (args.kind ?? 'state'), args.maxSteps);
81
+ return { ok: true, ran: true, ...verdictOf(0, result.warnings.length), schema: REPORT_SCHEMAS.umlRender, action: 'render', notation: result.notation, kind: result.diagram, diagram: result.primary, hashes: { diagram: createHash('sha256').update(result.primary).digest('hex') }, warnings: result.warnings };
82
+ }
83
+ if (args.action === 'parse') {
84
+ if (args.diagram === undefined)
85
+ return errorResult('action=parse needs `diagram` (Mermaid or PlantUML text)');
86
+ const result = parseUml(args.diagram, args.notation ?? 'auto');
87
+ // A diagram from another family still parses into *something*. `parseFindings`
88
+ // says so, and the verdict is what stops that something from being trusted:
89
+ // `ok: true` only means the parser ran.
90
+ const findings = parseFindings(result);
91
+ return {
92
+ ok: true,
93
+ ran: true,
94
+ ...parseVerdict(result, findings),
95
+ schema: REPORT_SCHEMAS.umlParse,
96
+ action: 'parse',
97
+ notation: result.notation,
98
+ kind: result.diagram,
99
+ model: result.model,
100
+ labels: result.labels,
101
+ findings,
102
+ discardedConstructs: result.discardedConstructs,
103
+ discardedEdges: result.discardedEdges,
104
+ hashes: { hashSpec: DEFAULT_HASH_SPEC, modelHash: modelHash(result.model), diagram: createHash('sha256').update(args.diagram).digest('hex') },
105
+ warnings: result.warnings,
106
+ };
107
+ }
108
+ // R9: the label contract was documented in the library and in `explainLabels()`, but no
109
+ // tool exposed it, so it was reachable only by reading the source or the docs.
110
+ if (args.action === 'explain-labels') {
111
+ const notation = (args.notation === undefined || args.notation === 'auto' ? 'mermaid' : args.notation);
112
+ return {
113
+ ok: true,
114
+ ran: true,
115
+ ...verdictOf(0, 0),
116
+ schema: REPORT_SCHEMAS.umlExplainLabels,
117
+ action: 'explain-labels',
118
+ ...explainLabels(notation),
119
+ };
120
+ }
121
+ const report = reviewUml({
122
+ model: args.model,
123
+ diagram: args.diagram,
124
+ notation: args.notation ?? 'auto',
125
+ diagramKind: (args.kind ?? 'state'),
126
+ roundTrip: args.roundTrip,
127
+ maxSteps: args.maxSteps,
128
+ });
129
+ return report;
130
+ }
131
+ catch (error) {
132
+ const message = error instanceof Error ? error.message : String(error);
133
+ return errorResult(message, error instanceof UmlError ? error.code : undefined);
134
+ }
135
+ },
136
+ });
137
+ /** The parse verdict counts parser notes as findings: an ignored line is information the model does not carry. */
138
+ function parseVerdict(result, findings) {
139
+ const errors = findings.filter((finding) => finding.severity === 'error').length;
140
+ const warnings = findings.filter((finding) => finding.severity === 'warning').length + result.warnings.length;
141
+ const firstCode = findings.find((finding) => finding.severity === 'error')?.code ?? findings.find((finding) => finding.severity === 'warning')?.code;
142
+ return verdictOf(errors, warnings, firstCode);
143
+ }
144
+ function errorResult(message, errorCode = 'UML_INPUT') {
145
+ return { ok: false, ran: false, ...refusalVerdict(message), errorCode, error: message };
146
+ }