@plures/praxis 1.3.0 → 1.4.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 (36) hide show
  1. package/dist/node/chunk-2IUFZBH3.js +87 -0
  2. package/dist/node/{chunk-WZ6B3LZ6.js → chunk-7CSWBDFL.js} +3 -56
  3. package/dist/node/{chunk-5JQJZADT.js → chunk-7M3HV4XR.js} +3 -3
  4. package/dist/node/{chunk-PTH6MD6P.js → chunk-FWOXU4MM.js} +1 -1
  5. package/dist/node/chunk-PGVSB6NR.js +59 -0
  6. package/dist/node/cli/index.cjs +1078 -211
  7. package/dist/node/cli/index.js +21 -2
  8. package/dist/node/index.cjs +1111 -0
  9. package/dist/node/index.d.cts +499 -1
  10. package/dist/node/index.d.ts +499 -1
  11. package/dist/node/index.js +1092 -78
  12. package/dist/node/integrations/svelte.js +2 -2
  13. package/dist/node/{reverse-W7THPV45.js → reverse-YD3CWIGM.js} +3 -2
  14. package/dist/node/rules-4DAJ4Z4N.js +7 -0
  15. package/dist/node/server-SYZPDULV.js +361 -0
  16. package/dist/node/{validate-EN3M4FUR.js → validate-TQGVIG7G.js} +4 -3
  17. package/package.json +28 -2
  18. package/src/__tests__/expectations.test.ts +364 -0
  19. package/src/__tests__/factory.test.ts +426 -0
  20. package/src/__tests__/mcp-server.test.ts +310 -0
  21. package/src/__tests__/project.test.ts +396 -0
  22. package/src/cli/index.ts +28 -0
  23. package/src/expectations/expectations.ts +471 -0
  24. package/src/expectations/index.ts +29 -0
  25. package/src/expectations/types.ts +95 -0
  26. package/src/factory/factory.ts +634 -0
  27. package/src/factory/index.ts +27 -0
  28. package/src/factory/types.ts +64 -0
  29. package/src/index.ts +57 -0
  30. package/src/mcp/index.ts +33 -0
  31. package/src/mcp/server.ts +485 -0
  32. package/src/mcp/types.ts +161 -0
  33. package/src/project/index.ts +31 -0
  34. package/src/project/project.ts +423 -0
  35. package/src/project/types.ts +87 -0
  36. /package/dist/node/{chunk-R2PSBPKQ.js → chunk-TEMFJOIH.js} +0 -0
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  ReactiveLogicEngine,
3
3
  createReactiveEngine
4
- } from "../chunk-5JQJZADT.js";
5
- import "../chunk-R2PSBPKQ.js";
4
+ } from "../chunk-7M3HV4XR.js";
6
5
  import "../chunk-KMJWAFZV.js";
6
+ import "../chunk-TEMFJOIH.js";
7
7
  import "../chunk-QGM4M3NI.js";
8
8
 
9
9
  // src/integrations/svelte.ts
@@ -2,10 +2,11 @@ import {
2
2
  generateContractFromRule,
3
3
  scanRepository,
4
4
  writeLogicLedgerEntry
5
- } from "./chunk-WZ6B3LZ6.js";
5
+ } from "./chunk-7CSWBDFL.js";
6
+ import "./chunk-PGVSB6NR.js";
6
7
  import {
7
8
  PraxisRegistry
8
- } from "./chunk-R2PSBPKQ.js";
9
+ } from "./chunk-TEMFJOIH.js";
9
10
  import "./chunk-QGM4M3NI.js";
10
11
 
11
12
  // src/cli/commands/reverse.ts
@@ -0,0 +1,7 @@
1
+ import {
2
+ PraxisRegistry
3
+ } from "./chunk-TEMFJOIH.js";
4
+ import "./chunk-QGM4M3NI.js";
5
+ export {
6
+ PraxisRegistry
7
+ };
@@ -0,0 +1,361 @@
1
+ import {
2
+ auditCompleteness,
3
+ formatReport
4
+ } from "./chunk-2IUFZBH3.js";
5
+ import {
6
+ getContractFromDescriptor
7
+ } from "./chunk-PGVSB6NR.js";
8
+ import {
9
+ LogicEngine,
10
+ RuleResult
11
+ } from "./chunk-KMJWAFZV.js";
12
+ import "./chunk-QGM4M3NI.js";
13
+
14
+ // src/mcp/server.ts
15
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
16
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
17
+ import { z } from "zod";
18
+ function createPraxisMcpServer(options) {
19
+ const {
20
+ name = "@plures/praxis",
21
+ version = "1.0.0",
22
+ initialContext,
23
+ registry,
24
+ initialFacts
25
+ } = options;
26
+ const engine = new LogicEngine({
27
+ initialContext,
28
+ registry,
29
+ initialFacts
30
+ });
31
+ const server = new McpServer({
32
+ name,
33
+ version
34
+ });
35
+ server.tool(
36
+ "praxis.inspect",
37
+ "List all registered rules, constraints, and their contracts",
38
+ {
39
+ filter: z.string().optional().describe("Filter rule/constraint IDs by pattern (substring match)"),
40
+ includeContracts: z.boolean().optional().describe("Include full contract details (default: false)")
41
+ },
42
+ async (params) => {
43
+ const filter = params.filter;
44
+ const includeContracts = params.includeContracts ?? false;
45
+ let rules = registry.getAllRules();
46
+ let constraints = registry.getAllConstraints();
47
+ if (filter) {
48
+ rules = rules.filter((r) => r.id.includes(filter));
49
+ constraints = constraints.filter((c) => c.id.includes(filter));
50
+ }
51
+ const ruleInfos = rules.map((r) => {
52
+ const contract = getContractFromDescriptor(r);
53
+ return {
54
+ id: r.id,
55
+ description: r.description,
56
+ eventTypes: r.eventTypes,
57
+ hasContract: !!contract,
58
+ contract: includeContracts ? contract : void 0,
59
+ meta: r.meta
60
+ };
61
+ });
62
+ const constraintInfos = constraints.map((c) => {
63
+ const contract = getContractFromDescriptor(c);
64
+ return {
65
+ id: c.id,
66
+ description: c.description,
67
+ hasContract: !!contract,
68
+ contract: includeContracts ? contract : void 0,
69
+ meta: c.meta
70
+ };
71
+ });
72
+ const output = {
73
+ rules: ruleInfos,
74
+ constraints: constraintInfos,
75
+ summary: {
76
+ totalRules: ruleInfos.length,
77
+ totalConstraints: constraintInfos.length,
78
+ rulesWithContracts: ruleInfos.filter((r) => r.hasContract).length,
79
+ constraintsWithContracts: constraintInfos.filter((c) => c.hasContract).length
80
+ }
81
+ };
82
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
83
+ }
84
+ );
85
+ server.tool(
86
+ "praxis.evaluate",
87
+ "Run a specific rule against given events and return the result",
88
+ {
89
+ ruleId: z.string().describe("The rule ID to evaluate"),
90
+ events: z.array(z.object({
91
+ tag: z.string(),
92
+ payload: z.unknown()
93
+ })).describe("Events to process through the rule")
94
+ },
95
+ async (params) => {
96
+ const rule = registry.getRule(params.ruleId);
97
+ if (!rule) {
98
+ return {
99
+ content: [{ type: "text", text: JSON.stringify({ error: `Rule "${params.ruleId}" not found` }) }]
100
+ };
101
+ }
102
+ const state = engine.getState();
103
+ const stateWithEvents = { ...state, events: params.events };
104
+ try {
105
+ const rawResult = rule.impl(stateWithEvents, params.events);
106
+ let output;
107
+ if (rawResult instanceof RuleResult) {
108
+ output = {
109
+ ruleId: params.ruleId,
110
+ resultKind: rawResult.kind,
111
+ facts: rawResult.facts,
112
+ retractedTags: rawResult.retractTags,
113
+ reason: rawResult.reason,
114
+ diagnostics: []
115
+ };
116
+ } else if (Array.isArray(rawResult)) {
117
+ output = {
118
+ ruleId: params.ruleId,
119
+ resultKind: "emit",
120
+ facts: rawResult,
121
+ retractedTags: [],
122
+ diagnostics: []
123
+ };
124
+ } else {
125
+ output = {
126
+ ruleId: params.ruleId,
127
+ resultKind: "noop",
128
+ facts: [],
129
+ retractedTags: [],
130
+ diagnostics: []
131
+ };
132
+ }
133
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
134
+ } catch (error) {
135
+ return {
136
+ content: [{
137
+ type: "text",
138
+ text: JSON.stringify({
139
+ error: `Rule evaluation failed: ${error instanceof Error ? error.message : String(error)}`,
140
+ ruleId: params.ruleId
141
+ })
142
+ }]
143
+ };
144
+ }
145
+ }
146
+ );
147
+ server.tool(
148
+ "praxis.audit",
149
+ "Run completeness audit against a manifest and return the report",
150
+ {
151
+ branches: z.array(z.object({
152
+ location: z.string(),
153
+ condition: z.string(),
154
+ kind: z.enum(["domain", "invariant", "ui", "transport", "wiring", "transform"]),
155
+ coveredBy: z.string().nullable(),
156
+ note: z.string().optional()
157
+ })).describe("Logic branches to audit"),
158
+ stateFields: z.array(z.object({
159
+ source: z.string(),
160
+ field: z.string(),
161
+ inContext: z.boolean(),
162
+ usedByRule: z.boolean()
163
+ })).describe("State fields to check context coverage"),
164
+ transitions: z.array(z.object({
165
+ description: z.string(),
166
+ eventTag: z.string().nullable(),
167
+ location: z.string()
168
+ })).describe("State transitions to check event coverage"),
169
+ rulesNeedingContracts: z.array(z.string()).describe("Rule IDs that should have contracts"),
170
+ threshold: z.number().optional().describe("Minimum passing score (default: 90)")
171
+ },
172
+ async (params) => {
173
+ const rulesWithContracts = registry.getAllRules().filter((r) => getContractFromDescriptor(r)).map((r) => r.id);
174
+ const report = auditCompleteness(
175
+ {
176
+ branches: params.branches,
177
+ stateFields: params.stateFields,
178
+ transitions: params.transitions,
179
+ rulesNeedingContracts: params.rulesNeedingContracts
180
+ },
181
+ registry.getRuleIds(),
182
+ registry.getConstraintIds(),
183
+ rulesWithContracts,
184
+ { threshold: params.threshold }
185
+ );
186
+ const output = {
187
+ report,
188
+ formatted: formatReport(report)
189
+ };
190
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
191
+ }
192
+ );
193
+ server.tool(
194
+ "praxis.suggest",
195
+ "Given a gap or description, suggest rules/constraints to add",
196
+ {
197
+ gap: z.string().describe("Description of the gap or failing expectation"),
198
+ context: z.record(z.string(), z.unknown()).optional().describe("Current context for suggestions")
199
+ },
200
+ async (params) => {
201
+ const existingRules = registry.getAllRules();
202
+ const _constraints = registry.getAllConstraints();
203
+ void _constraints;
204
+ const suggestions = [];
205
+ const gapLower = params.gap.toLowerCase();
206
+ const relatedRules = existingRules.filter(
207
+ (r) => r.description.toLowerCase().includes(gapLower) || gapLower.includes(r.id.toLowerCase())
208
+ );
209
+ if (relatedRules.length > 0) {
210
+ for (const rule of relatedRules) {
211
+ suggestions.push({
212
+ type: "constraint",
213
+ id: `${rule.id}/guard`,
214
+ description: `Add a constraint to guard the behavior described by rule "${rule.id}"`,
215
+ rationale: `Rule "${rule.id}" (${rule.description}) exists but may not fully cover: ${params.gap}`
216
+ });
217
+ }
218
+ }
219
+ const invariantTerms = ["must", "never", "always", "require", "valid", "invalid", "prevent"];
220
+ if (invariantTerms.some((t) => gapLower.includes(t))) {
221
+ suggestions.push({
222
+ type: "constraint",
223
+ id: suggestId(params.gap, "constraint"),
224
+ description: `Constraint: ${params.gap}`,
225
+ rationale: "Gap description contains invariant language \u2014 a constraint would encode this guarantee"
226
+ });
227
+ }
228
+ const ruleTerms = ["when", "if", "show", "emit", "trigger", "display", "update"];
229
+ if (ruleTerms.some((t) => gapLower.includes(t))) {
230
+ suggestions.push({
231
+ type: "rule",
232
+ id: suggestId(params.gap, "rule"),
233
+ description: `Rule: ${params.gap}`,
234
+ rationale: "Gap description contains conditional behavior \u2014 a rule would implement this logic"
235
+ });
236
+ }
237
+ const contractGaps = registry.getContractGaps();
238
+ if (contractGaps.length > 0) {
239
+ const relatedGaps = contractGaps.filter(
240
+ (g) => gapLower.includes(g.ruleId.toLowerCase())
241
+ );
242
+ for (const g of relatedGaps) {
243
+ suggestions.push({
244
+ type: "contract",
245
+ id: g.ruleId,
246
+ description: `Add contract for "${g.ruleId}" \u2014 missing: ${g.missing.join(", ")}`,
247
+ rationale: `Related rule "${g.ruleId}" lacks a contract, which could prevent this gap`
248
+ });
249
+ }
250
+ }
251
+ const eventTerms = ["transition", "change", "happen", "occur", "fire", "dispatch"];
252
+ if (eventTerms.some((t) => gapLower.includes(t))) {
253
+ suggestions.push({
254
+ type: "event",
255
+ id: suggestId(params.gap, "event"),
256
+ description: `Event for: ${params.gap}`,
257
+ rationale: "Gap description suggests a state transition \u2014 an event would make it observable"
258
+ });
259
+ }
260
+ if (suggestions.length === 0) {
261
+ suggestions.push({
262
+ type: "rule",
263
+ id: suggestId(params.gap, "rule"),
264
+ description: `Rule: ${params.gap}`,
265
+ rationale: "No existing rules or constraints cover this gap \u2014 a new rule is recommended"
266
+ });
267
+ }
268
+ const output = { suggestions };
269
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
270
+ }
271
+ );
272
+ server.tool(
273
+ "praxis.facts",
274
+ "Get the current fact state of the engine",
275
+ {},
276
+ async () => {
277
+ const facts = engine.getFacts();
278
+ const output = {
279
+ facts,
280
+ count: facts.length
281
+ };
282
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
283
+ }
284
+ );
285
+ server.tool(
286
+ "praxis.step",
287
+ "Step the engine with events and return the new state",
288
+ {
289
+ events: z.array(z.object({
290
+ tag: z.string(),
291
+ payload: z.unknown()
292
+ })).describe("Events to process")
293
+ },
294
+ async (params) => {
295
+ const result = engine.step(params.events);
296
+ const output = {
297
+ facts: result.state.facts,
298
+ diagnostics: result.diagnostics,
299
+ factCount: result.state.facts.length
300
+ };
301
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
302
+ }
303
+ );
304
+ server.tool(
305
+ "praxis.contracts",
306
+ "List all contracts with their coverage status",
307
+ {
308
+ filter: z.string().optional().describe("Filter by rule/constraint ID (substring match)")
309
+ },
310
+ async (params) => {
311
+ const rules = registry.getAllRules();
312
+ const constraints = registry.getAllConstraints();
313
+ let contracts = [
314
+ ...rules.map((r) => ({
315
+ ruleId: r.id,
316
+ hasContract: !!getContractFromDescriptor(r),
317
+ contract: getContractFromDescriptor(r),
318
+ type: "rule"
319
+ })),
320
+ ...constraints.map((c) => ({
321
+ ruleId: c.id,
322
+ hasContract: !!getContractFromDescriptor(c),
323
+ contract: getContractFromDescriptor(c),
324
+ type: "constraint"
325
+ }))
326
+ ];
327
+ if (params.filter) {
328
+ contracts = contracts.filter((c) => c.ruleId.includes(params.filter));
329
+ }
330
+ const total = contracts.length;
331
+ const withContracts = contracts.filter((c) => c.hasContract).length;
332
+ const output = {
333
+ contracts,
334
+ coverage: {
335
+ total,
336
+ withContracts,
337
+ percentage: total > 0 ? Math.round(withContracts / total * 100) : 100
338
+ }
339
+ };
340
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
341
+ }
342
+ );
343
+ return {
344
+ /** The underlying MCP server instance */
345
+ mcpServer: server,
346
+ /** The underlying Praxis engine */
347
+ engine,
348
+ /** Start the server on stdio transport */
349
+ async start() {
350
+ const transport = new StdioServerTransport();
351
+ await server.connect(transport);
352
+ }
353
+ };
354
+ }
355
+ function suggestId(description, type) {
356
+ const slug = description.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 3).join("-");
357
+ return `suggested/${type}/${slug}`;
358
+ }
359
+ export {
360
+ createPraxisMcpServer
361
+ };
@@ -4,13 +4,14 @@ import {
4
4
  formatValidationReportJSON,
5
5
  formatValidationReportSARIF,
6
6
  validateContracts
7
- } from "./chunk-PTH6MD6P.js";
7
+ } from "./chunk-FWOXU4MM.js";
8
8
  import {
9
9
  writeLogicLedgerEntry
10
- } from "./chunk-WZ6B3LZ6.js";
10
+ } from "./chunk-7CSWBDFL.js";
11
+ import "./chunk-PGVSB6NR.js";
11
12
  import {
12
13
  PraxisRegistry
13
- } from "./chunk-R2PSBPKQ.js";
14
+ } from "./chunk-TEMFJOIH.js";
14
15
  import "./chunk-QGM4M3NI.js";
15
16
 
16
17
  // src/cli/commands/validate.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plures/praxis",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "The Full Plures Application Framework - declarative schemas, logic engine, component generation, and local-first data",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@9.15.1",
@@ -62,6 +62,30 @@
62
62
  "import": "./dist/node/components/index.js",
63
63
  "require": "./dist/node/components/index.cjs",
64
64
  "default": "./dist/node/components/index.js"
65
+ },
66
+ "./mcp": {
67
+ "types": "./dist/node/mcp/index.d.ts",
68
+ "import": "./dist/node/mcp/index.js",
69
+ "require": "./dist/node/mcp/index.cjs",
70
+ "default": "./dist/node/mcp/index.js"
71
+ },
72
+ "./expectations": {
73
+ "types": "./dist/node/expectations/index.d.ts",
74
+ "import": "./dist/node/expectations/index.js",
75
+ "require": "./dist/node/expectations/index.cjs",
76
+ "default": "./dist/node/expectations/index.js"
77
+ },
78
+ "./factory": {
79
+ "types": "./dist/node/factory/index.d.ts",
80
+ "import": "./dist/node/factory/index.js",
81
+ "require": "./dist/node/factory/index.cjs",
82
+ "default": "./dist/node/factory/index.js"
83
+ },
84
+ "./project": {
85
+ "types": "./dist/node/project/index.d.ts",
86
+ "import": "./dist/node/project/index.js",
87
+ "require": "./dist/node/project/index.cjs",
88
+ "default": "./dist/node/project/index.js"
65
89
  }
66
90
  },
67
91
  "files": [
@@ -123,9 +147,11 @@
123
147
  "vitest": "^4.0.15"
124
148
  },
125
149
  "dependencies": {
150
+ "@modelcontextprotocol/sdk": "^1.27.1",
151
+ "@plures/pluresdb": "^2.9.7",
126
152
  "commander": "^14.0.2",
127
153
  "js-yaml": "^4.1.1",
128
- "@plures/pluresdb": "^2.9.7"
154
+ "zod": "^4.3.6"
129
155
  },
130
156
  "peerDependencies": {
131
157
  "svelte": "^5.46.0"