@plures/praxis 1.2.41 → 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.
- package/dist/browser/{chunk-BBP2F7TT.js → chunk-MJK3IYTJ.js} +123 -5
- package/dist/browser/{chunk-FCEH7WMH.js → chunk-N63K4KWS.js} +1 -1
- package/dist/browser/{engine-65QDGCAN.js → engine-YIEGSX7U.js} +1 -1
- package/dist/browser/index.d.ts +2 -2
- package/dist/browser/index.js +10 -5
- package/dist/browser/integrations/svelte.d.ts +2 -2
- package/dist/browser/integrations/svelte.js +2 -2
- package/dist/browser/{reactive-engine.svelte-Cqd8Mod2.d.ts → reactive-engine.svelte-DjynI82A.d.ts} +83 -4
- package/dist/node/chunk-2IUFZBH3.js +87 -0
- package/dist/node/{chunk-WZ6B3LZ6.js → chunk-7CSWBDFL.js} +3 -56
- package/dist/node/{chunk-32YFEEML.js → chunk-7M3HV4XR.js} +4 -4
- package/dist/node/{chunk-PTH6MD6P.js → chunk-FWOXU4MM.js} +1 -1
- package/dist/node/{chunk-BBP2F7TT.js → chunk-KMJWAFZV.js} +128 -5
- package/dist/node/chunk-PGVSB6NR.js +59 -0
- package/dist/node/cli/index.cjs +1078 -211
- package/dist/node/cli/index.js +21 -2
- package/dist/node/cloud/index.d.cts +1 -1
- package/dist/node/cloud/index.d.ts +1 -1
- package/dist/node/{engine-7CXQV6RC.js → engine-FEN5IYZ5.js} +1 -1
- package/dist/node/index.cjs +1633 -59
- package/dist/node/index.d.cts +769 -5
- package/dist/node/index.d.ts +769 -5
- package/dist/node/index.js +1375 -45
- package/dist/node/integrations/svelte.cjs +123 -5
- package/dist/node/integrations/svelte.d.cts +3 -3
- package/dist/node/integrations/svelte.d.ts +3 -3
- package/dist/node/integrations/svelte.js +3 -3
- package/dist/node/{protocol-BocKczNv.d.ts → protocol-DcyGMmWY.d.cts} +7 -0
- package/dist/node/{protocol-BocKczNv.d.cts → protocol-DcyGMmWY.d.ts} +7 -0
- package/dist/node/{reactive-engine.svelte-CGe8SpVE.d.cts → reactive-engine.svelte-Cg0Yc2Hs.d.cts} +90 -6
- package/dist/node/{reactive-engine.svelte-D-xTDxT5.d.ts → reactive-engine.svelte-DekxqFu0.d.ts} +90 -6
- package/dist/node/{reverse-W7THPV45.js → reverse-YD3CWIGM.js} +3 -2
- package/dist/node/rules-4DAJ4Z4N.js +7 -0
- package/dist/node/server-SYZPDULV.js +361 -0
- package/dist/node/{validate-EN3M4FUR.js → validate-TQGVIG7G.js} +4 -3
- package/package.json +29 -3
- package/src/__tests__/engine-v2.test.ts +532 -0
- package/src/__tests__/expectations.test.ts +364 -0
- package/src/__tests__/factory.test.ts +426 -0
- package/src/__tests__/mcp-server.test.ts +310 -0
- package/src/__tests__/project.test.ts +396 -0
- package/src/cli/index.ts +28 -0
- package/src/core/completeness.ts +274 -0
- package/src/core/engine.ts +47 -5
- package/src/core/pluresdb/store.ts +9 -3
- package/src/core/protocol.ts +7 -0
- package/src/core/rule-result.ts +130 -0
- package/src/core/rules.ts +12 -5
- package/src/core/ui-rules.ts +340 -0
- package/src/expectations/expectations.ts +471 -0
- package/src/expectations/index.ts +29 -0
- package/src/expectations/types.ts +95 -0
- package/src/factory/factory.ts +634 -0
- package/src/factory/index.ts +27 -0
- package/src/factory/types.ts +64 -0
- package/src/index.ts +84 -0
- package/src/mcp/index.ts +33 -0
- package/src/mcp/server.ts +485 -0
- package/src/mcp/types.ts +161 -0
- package/src/project/index.ts +31 -0
- package/src/project/project.ts +423 -0
- package/src/project/types.ts +87 -0
- package/src/vite/completeness-plugin.ts +72 -0
- /package/dist/node/{chunk-R2PSBPKQ.js → chunk-TEMFJOIH.js} +0 -0
package/dist/node/cli/index.cjs
CHANGED
|
@@ -3517,6 +3517,1065 @@ var init_cloud = __esm({
|
|
|
3517
3517
|
}
|
|
3518
3518
|
});
|
|
3519
3519
|
|
|
3520
|
+
// src/core/protocol.ts
|
|
3521
|
+
var PRAXIS_PROTOCOL_VERSION;
|
|
3522
|
+
var init_protocol = __esm({
|
|
3523
|
+
"src/core/protocol.ts"() {
|
|
3524
|
+
"use strict";
|
|
3525
|
+
PRAXIS_PROTOCOL_VERSION = "1.0.0";
|
|
3526
|
+
}
|
|
3527
|
+
});
|
|
3528
|
+
|
|
3529
|
+
// src/core/rule-result.ts
|
|
3530
|
+
var RuleResult;
|
|
3531
|
+
var init_rule_result = __esm({
|
|
3532
|
+
"src/core/rule-result.ts"() {
|
|
3533
|
+
"use strict";
|
|
3534
|
+
RuleResult = class _RuleResult {
|
|
3535
|
+
/** The kind of result */
|
|
3536
|
+
kind;
|
|
3537
|
+
/** Facts produced (only for 'emit') */
|
|
3538
|
+
facts;
|
|
3539
|
+
/** Fact tags to retract (only for 'retract') */
|
|
3540
|
+
retractTags;
|
|
3541
|
+
/** Optional reason (for noop/skip/retract — useful for debugging) */
|
|
3542
|
+
reason;
|
|
3543
|
+
/** The rule ID that produced this result (set by engine) */
|
|
3544
|
+
ruleId;
|
|
3545
|
+
constructor(kind, facts, retractTags, reason) {
|
|
3546
|
+
this.kind = kind;
|
|
3547
|
+
this.facts = facts;
|
|
3548
|
+
this.retractTags = retractTags;
|
|
3549
|
+
this.reason = reason;
|
|
3550
|
+
}
|
|
3551
|
+
/**
|
|
3552
|
+
* Rule produced facts.
|
|
3553
|
+
*
|
|
3554
|
+
* @example
|
|
3555
|
+
* return RuleResult.emit([
|
|
3556
|
+
* { tag: 'sprint.behind', payload: { deficit: 5 } }
|
|
3557
|
+
* ]);
|
|
3558
|
+
*/
|
|
3559
|
+
static emit(facts) {
|
|
3560
|
+
if (facts.length === 0) {
|
|
3561
|
+
throw new Error(
|
|
3562
|
+
"RuleResult.emit() requires at least one fact. Use RuleResult.noop() or RuleResult.skip() when a rule has nothing to say."
|
|
3563
|
+
);
|
|
3564
|
+
}
|
|
3565
|
+
return new _RuleResult("emit", facts, []);
|
|
3566
|
+
}
|
|
3567
|
+
/**
|
|
3568
|
+
* Rule evaluated but had nothing to report.
|
|
3569
|
+
* Unlike returning [], this is explicit and traceable.
|
|
3570
|
+
*
|
|
3571
|
+
* @example
|
|
3572
|
+
* if (ctx.completedHours >= expectedHours) {
|
|
3573
|
+
* return RuleResult.noop('Sprint is on pace');
|
|
3574
|
+
* }
|
|
3575
|
+
*/
|
|
3576
|
+
static noop(reason) {
|
|
3577
|
+
return new _RuleResult("noop", [], [], reason);
|
|
3578
|
+
}
|
|
3579
|
+
/**
|
|
3580
|
+
* Rule decided to skip because preconditions were not met.
|
|
3581
|
+
* Distinct from noop: skip means "I can't evaluate", noop means "I evaluated and found nothing".
|
|
3582
|
+
*
|
|
3583
|
+
* @example
|
|
3584
|
+
* if (!ctx.sprintName) {
|
|
3585
|
+
* return RuleResult.skip('No active sprint');
|
|
3586
|
+
* }
|
|
3587
|
+
*/
|
|
3588
|
+
static skip(reason) {
|
|
3589
|
+
return new _RuleResult("skip", [], [], reason);
|
|
3590
|
+
}
|
|
3591
|
+
/**
|
|
3592
|
+
* Rule retracts previously emitted facts by tag.
|
|
3593
|
+
* Used when a condition that previously produced facts is no longer true.
|
|
3594
|
+
*
|
|
3595
|
+
* @example
|
|
3596
|
+
* // Sprint was behind, but caught up
|
|
3597
|
+
* if (ctx.completedHours >= expectedHours) {
|
|
3598
|
+
* return RuleResult.retract(['sprint.behind'], 'Sprint caught up');
|
|
3599
|
+
* }
|
|
3600
|
+
*/
|
|
3601
|
+
static retract(tags, reason) {
|
|
3602
|
+
if (tags.length === 0) {
|
|
3603
|
+
throw new Error("RuleResult.retract() requires at least one tag.");
|
|
3604
|
+
}
|
|
3605
|
+
return new _RuleResult("retract", [], tags, reason);
|
|
3606
|
+
}
|
|
3607
|
+
/** Whether this result produced facts */
|
|
3608
|
+
get hasFacts() {
|
|
3609
|
+
return this.facts.length > 0;
|
|
3610
|
+
}
|
|
3611
|
+
/** Whether this result retracts facts */
|
|
3612
|
+
get hasRetractions() {
|
|
3613
|
+
return this.retractTags.length > 0;
|
|
3614
|
+
}
|
|
3615
|
+
};
|
|
3616
|
+
}
|
|
3617
|
+
});
|
|
3618
|
+
|
|
3619
|
+
// src/core/engine.ts
|
|
3620
|
+
function safeClone(value) {
|
|
3621
|
+
if (value === null || typeof value !== "object") {
|
|
3622
|
+
return value;
|
|
3623
|
+
}
|
|
3624
|
+
if (typeof globalThis.structuredClone === "function") {
|
|
3625
|
+
try {
|
|
3626
|
+
return globalThis.structuredClone(value);
|
|
3627
|
+
} catch {
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3630
|
+
if (Array.isArray(value)) {
|
|
3631
|
+
return [...value];
|
|
3632
|
+
}
|
|
3633
|
+
return { ...value };
|
|
3634
|
+
}
|
|
3635
|
+
var LogicEngine;
|
|
3636
|
+
var init_engine = __esm({
|
|
3637
|
+
"src/core/engine.ts"() {
|
|
3638
|
+
"use strict";
|
|
3639
|
+
init_protocol();
|
|
3640
|
+
init_rule_result();
|
|
3641
|
+
LogicEngine = class {
|
|
3642
|
+
state;
|
|
3643
|
+
registry;
|
|
3644
|
+
factDedup;
|
|
3645
|
+
maxFacts;
|
|
3646
|
+
constructor(options) {
|
|
3647
|
+
this.registry = options.registry;
|
|
3648
|
+
this.factDedup = options.factDedup ?? "last-write-wins";
|
|
3649
|
+
this.maxFacts = options.maxFacts ?? 1e3;
|
|
3650
|
+
this.state = {
|
|
3651
|
+
context: options.initialContext,
|
|
3652
|
+
facts: options.initialFacts ?? [],
|
|
3653
|
+
meta: options.initialMeta ?? {},
|
|
3654
|
+
protocolVersion: PRAXIS_PROTOCOL_VERSION
|
|
3655
|
+
};
|
|
3656
|
+
}
|
|
3657
|
+
/**
|
|
3658
|
+
* Get the current state (immutable copy)
|
|
3659
|
+
*/
|
|
3660
|
+
getState() {
|
|
3661
|
+
return {
|
|
3662
|
+
context: safeClone(this.state.context),
|
|
3663
|
+
facts: [...this.state.facts],
|
|
3664
|
+
meta: this.state.meta ? safeClone(this.state.meta) : void 0,
|
|
3665
|
+
protocolVersion: this.state.protocolVersion
|
|
3666
|
+
};
|
|
3667
|
+
}
|
|
3668
|
+
/**
|
|
3669
|
+
* Get the current context
|
|
3670
|
+
*/
|
|
3671
|
+
getContext() {
|
|
3672
|
+
return safeClone(this.state.context);
|
|
3673
|
+
}
|
|
3674
|
+
/**
|
|
3675
|
+
* Get current facts
|
|
3676
|
+
*/
|
|
3677
|
+
getFacts() {
|
|
3678
|
+
return [...this.state.facts];
|
|
3679
|
+
}
|
|
3680
|
+
/**
|
|
3681
|
+
* Process events through the engine.
|
|
3682
|
+
* Applies all registered rules and checks all registered constraints.
|
|
3683
|
+
*
|
|
3684
|
+
* @param events Events to process
|
|
3685
|
+
* @returns Result with new state and diagnostics
|
|
3686
|
+
*/
|
|
3687
|
+
step(events) {
|
|
3688
|
+
const config = {
|
|
3689
|
+
ruleIds: this.registry.getRuleIds(),
|
|
3690
|
+
constraintIds: this.registry.getConstraintIds()
|
|
3691
|
+
};
|
|
3692
|
+
return this.stepWithConfig(events, config);
|
|
3693
|
+
}
|
|
3694
|
+
/**
|
|
3695
|
+
* Process events with specific rule and constraint configuration.
|
|
3696
|
+
*
|
|
3697
|
+
* @param events Events to process
|
|
3698
|
+
* @param config Step configuration
|
|
3699
|
+
* @returns Result with new state and diagnostics
|
|
3700
|
+
*/
|
|
3701
|
+
stepWithConfig(events, config) {
|
|
3702
|
+
const diagnostics = [];
|
|
3703
|
+
let newState = { ...this.state };
|
|
3704
|
+
const stateWithEvents = {
|
|
3705
|
+
...newState,
|
|
3706
|
+
events
|
|
3707
|
+
// current batch — rules can read state.events
|
|
3708
|
+
};
|
|
3709
|
+
const newFacts = [];
|
|
3710
|
+
const retractions = [];
|
|
3711
|
+
const eventTags = new Set(events.map((e) => e.tag));
|
|
3712
|
+
for (const ruleId of config.ruleIds) {
|
|
3713
|
+
const rule = this.registry.getRule(ruleId);
|
|
3714
|
+
if (!rule) {
|
|
3715
|
+
diagnostics.push({
|
|
3716
|
+
kind: "rule-error",
|
|
3717
|
+
message: `Rule "${ruleId}" not found in registry`,
|
|
3718
|
+
data: { ruleId }
|
|
3719
|
+
});
|
|
3720
|
+
continue;
|
|
3721
|
+
}
|
|
3722
|
+
if (rule.eventTypes) {
|
|
3723
|
+
const filterTags = Array.isArray(rule.eventTypes) ? rule.eventTypes : [rule.eventTypes];
|
|
3724
|
+
if (!filterTags.some((t) => eventTags.has(t))) {
|
|
3725
|
+
continue;
|
|
3726
|
+
}
|
|
3727
|
+
}
|
|
3728
|
+
try {
|
|
3729
|
+
const rawResult = rule.impl(stateWithEvents, events);
|
|
3730
|
+
if (rawResult instanceof RuleResult) {
|
|
3731
|
+
rawResult.ruleId = ruleId;
|
|
3732
|
+
switch (rawResult.kind) {
|
|
3733
|
+
case "emit":
|
|
3734
|
+
newFacts.push(...rawResult.facts);
|
|
3735
|
+
break;
|
|
3736
|
+
case "retract":
|
|
3737
|
+
retractions.push(...rawResult.retractTags);
|
|
3738
|
+
break;
|
|
3739
|
+
case "noop":
|
|
3740
|
+
case "skip":
|
|
3741
|
+
if (rawResult.reason) {
|
|
3742
|
+
diagnostics.push({
|
|
3743
|
+
kind: "rule-error",
|
|
3744
|
+
// reused kind — could add 'rule-trace' in protocol v2
|
|
3745
|
+
message: `[${rawResult.kind}] ${ruleId}: ${rawResult.reason}`,
|
|
3746
|
+
data: { ruleId, resultKind: rawResult.kind, reason: rawResult.reason }
|
|
3747
|
+
});
|
|
3748
|
+
}
|
|
3749
|
+
break;
|
|
3750
|
+
}
|
|
3751
|
+
} else if (Array.isArray(rawResult)) {
|
|
3752
|
+
newFacts.push(...rawResult);
|
|
3753
|
+
}
|
|
3754
|
+
} catch (error) {
|
|
3755
|
+
diagnostics.push({
|
|
3756
|
+
kind: "rule-error",
|
|
3757
|
+
message: `Error executing rule "${ruleId}": ${error instanceof Error ? error.message : String(error)}`,
|
|
3758
|
+
data: { ruleId, error }
|
|
3759
|
+
});
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3762
|
+
let existingFacts = newState.facts;
|
|
3763
|
+
if (retractions.length > 0) {
|
|
3764
|
+
const retractSet = new Set(retractions);
|
|
3765
|
+
existingFacts = existingFacts.filter((f) => !retractSet.has(f.tag));
|
|
3766
|
+
}
|
|
3767
|
+
let mergedFacts;
|
|
3768
|
+
switch (this.factDedup) {
|
|
3769
|
+
case "last-write-wins": {
|
|
3770
|
+
const factMap = /* @__PURE__ */ new Map();
|
|
3771
|
+
for (const f of existingFacts) factMap.set(f.tag, f);
|
|
3772
|
+
for (const f of newFacts) factMap.set(f.tag, f);
|
|
3773
|
+
mergedFacts = Array.from(factMap.values());
|
|
3774
|
+
break;
|
|
3775
|
+
}
|
|
3776
|
+
case "append":
|
|
3777
|
+
mergedFacts = [...existingFacts, ...newFacts];
|
|
3778
|
+
break;
|
|
3779
|
+
case "none":
|
|
3780
|
+
default:
|
|
3781
|
+
mergedFacts = [...existingFacts, ...newFacts];
|
|
3782
|
+
break;
|
|
3783
|
+
}
|
|
3784
|
+
if (this.maxFacts > 0 && mergedFacts.length > this.maxFacts) {
|
|
3785
|
+
mergedFacts = mergedFacts.slice(mergedFacts.length - this.maxFacts);
|
|
3786
|
+
}
|
|
3787
|
+
newState = {
|
|
3788
|
+
...newState,
|
|
3789
|
+
facts: mergedFacts
|
|
3790
|
+
};
|
|
3791
|
+
for (const constraintId of config.constraintIds) {
|
|
3792
|
+
const constraint = this.registry.getConstraint(constraintId);
|
|
3793
|
+
if (!constraint) {
|
|
3794
|
+
diagnostics.push({
|
|
3795
|
+
kind: "constraint-violation",
|
|
3796
|
+
message: `Constraint "${constraintId}" not found in registry`,
|
|
3797
|
+
data: { constraintId }
|
|
3798
|
+
});
|
|
3799
|
+
continue;
|
|
3800
|
+
}
|
|
3801
|
+
try {
|
|
3802
|
+
const result = constraint.impl(newState);
|
|
3803
|
+
if (result === false) {
|
|
3804
|
+
diagnostics.push({
|
|
3805
|
+
kind: "constraint-violation",
|
|
3806
|
+
message: `Constraint "${constraintId}" violated`,
|
|
3807
|
+
data: { constraintId, description: constraint.description }
|
|
3808
|
+
});
|
|
3809
|
+
} else if (typeof result === "string") {
|
|
3810
|
+
diagnostics.push({
|
|
3811
|
+
kind: "constraint-violation",
|
|
3812
|
+
message: result,
|
|
3813
|
+
data: { constraintId, description: constraint.description }
|
|
3814
|
+
});
|
|
3815
|
+
}
|
|
3816
|
+
} catch (error) {
|
|
3817
|
+
diagnostics.push({
|
|
3818
|
+
kind: "constraint-violation",
|
|
3819
|
+
message: `Error checking constraint "${constraintId}": ${error instanceof Error ? error.message : String(error)}`,
|
|
3820
|
+
data: { constraintId, error }
|
|
3821
|
+
});
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
this.state = newState;
|
|
3825
|
+
return {
|
|
3826
|
+
state: newState,
|
|
3827
|
+
diagnostics
|
|
3828
|
+
};
|
|
3829
|
+
}
|
|
3830
|
+
/**
|
|
3831
|
+
* Update the context directly (for exceptional cases).
|
|
3832
|
+
* Generally, context should be updated through rules.
|
|
3833
|
+
*
|
|
3834
|
+
* @param updater Function that produces new context from old context
|
|
3835
|
+
*/
|
|
3836
|
+
updateContext(updater) {
|
|
3837
|
+
this.state = {
|
|
3838
|
+
...this.state,
|
|
3839
|
+
context: updater(this.state.context)
|
|
3840
|
+
};
|
|
3841
|
+
}
|
|
3842
|
+
/**
|
|
3843
|
+
* Atomically update context AND process events in a single call.
|
|
3844
|
+
*
|
|
3845
|
+
* This avoids the fragile pattern of calling updateContext() then step()
|
|
3846
|
+
* separately, where rules could see stale context if the ordering is wrong.
|
|
3847
|
+
*
|
|
3848
|
+
* @param updater Function that produces new context from old context
|
|
3849
|
+
* @param events Events to process after context is updated
|
|
3850
|
+
* @returns Result with new state and diagnostics
|
|
3851
|
+
*
|
|
3852
|
+
* @example
|
|
3853
|
+
* engine.stepWithContext(
|
|
3854
|
+
* ctx => ({ ...ctx, sprintName: sprint.name, items: sprint.items }),
|
|
3855
|
+
* [{ tag: 'sprint.update', payload: { name: sprint.name } }]
|
|
3856
|
+
* );
|
|
3857
|
+
*/
|
|
3858
|
+
stepWithContext(updater, events) {
|
|
3859
|
+
this.state = {
|
|
3860
|
+
...this.state,
|
|
3861
|
+
context: updater(this.state.context)
|
|
3862
|
+
};
|
|
3863
|
+
return this.step(events);
|
|
3864
|
+
}
|
|
3865
|
+
/**
|
|
3866
|
+
* Add facts directly (for exceptional cases).
|
|
3867
|
+
* Generally, facts should be added through rules.
|
|
3868
|
+
*
|
|
3869
|
+
* @param facts Facts to add
|
|
3870
|
+
*/
|
|
3871
|
+
addFacts(facts) {
|
|
3872
|
+
this.state = {
|
|
3873
|
+
...this.state,
|
|
3874
|
+
facts: [...this.state.facts, ...facts]
|
|
3875
|
+
};
|
|
3876
|
+
}
|
|
3877
|
+
/**
|
|
3878
|
+
* Check all constraints without processing any events.
|
|
3879
|
+
*
|
|
3880
|
+
* Useful for validation-only scenarios (e.g., form validation,
|
|
3881
|
+
* pre-save checks) where you want constraint diagnostics without
|
|
3882
|
+
* triggering any rules.
|
|
3883
|
+
*
|
|
3884
|
+
* @returns Array of constraint violation diagnostics (empty = all passing)
|
|
3885
|
+
*/
|
|
3886
|
+
checkConstraints() {
|
|
3887
|
+
return this.stepWithConfig([], {
|
|
3888
|
+
ruleIds: [],
|
|
3889
|
+
constraintIds: this.registry.getConstraintIds()
|
|
3890
|
+
}).diagnostics;
|
|
3891
|
+
}
|
|
3892
|
+
/**
|
|
3893
|
+
* Clear all facts
|
|
3894
|
+
*/
|
|
3895
|
+
clearFacts() {
|
|
3896
|
+
this.state = {
|
|
3897
|
+
...this.state,
|
|
3898
|
+
facts: []
|
|
3899
|
+
};
|
|
3900
|
+
}
|
|
3901
|
+
/**
|
|
3902
|
+
* Reset the engine to initial state
|
|
3903
|
+
*/
|
|
3904
|
+
reset(options) {
|
|
3905
|
+
this.state = {
|
|
3906
|
+
context: options.initialContext,
|
|
3907
|
+
facts: options.initialFacts ?? [],
|
|
3908
|
+
meta: options.initialMeta ?? {},
|
|
3909
|
+
protocolVersion: PRAXIS_PROTOCOL_VERSION
|
|
3910
|
+
};
|
|
3911
|
+
}
|
|
3912
|
+
};
|
|
3913
|
+
}
|
|
3914
|
+
});
|
|
3915
|
+
|
|
3916
|
+
// src/core/completeness.ts
|
|
3917
|
+
function auditCompleteness(manifest, registryRuleIds, registryConstraintIds, rulesWithContracts, config) {
|
|
3918
|
+
const threshold = config?.threshold ?? 90;
|
|
3919
|
+
const domainBranches = manifest.branches.filter((b) => b.kind === "domain");
|
|
3920
|
+
const coveredDomain = domainBranches.filter((b) => b.coveredBy && registryRuleIds.includes(b.coveredBy));
|
|
3921
|
+
const uncoveredDomain = domainBranches.filter((b) => !b.coveredBy || !registryRuleIds.includes(b.coveredBy));
|
|
3922
|
+
const invariantBranches = manifest.branches.filter((b) => b.kind === "invariant");
|
|
3923
|
+
const coveredInvariants = invariantBranches.filter((b) => b.coveredBy && registryConstraintIds.includes(b.coveredBy));
|
|
3924
|
+
const uncoveredInvariants = invariantBranches.filter((b) => !b.coveredBy || !registryConstraintIds.includes(b.coveredBy));
|
|
3925
|
+
const needContracts = manifest.rulesNeedingContracts;
|
|
3926
|
+
const haveContracts = needContracts.filter((id) => rulesWithContracts.includes(id));
|
|
3927
|
+
const missingContracts = needContracts.filter((id) => !rulesWithContracts.includes(id));
|
|
3928
|
+
const neededFields = manifest.stateFields.filter((f) => f.usedByRule);
|
|
3929
|
+
const coveredFields = neededFields.filter((f) => f.inContext);
|
|
3930
|
+
const missingFields = neededFields.filter((f) => !f.inContext);
|
|
3931
|
+
const coveredTransitions = manifest.transitions.filter((t) => t.eventTag);
|
|
3932
|
+
const missingTransitions = manifest.transitions.filter((t) => !t.eventTag);
|
|
3933
|
+
const ruleScore = domainBranches.length > 0 ? coveredDomain.length / domainBranches.length * 40 : 40;
|
|
3934
|
+
const constraintScore = invariantBranches.length > 0 ? coveredInvariants.length / invariantBranches.length * 20 : 20;
|
|
3935
|
+
const contractScore = needContracts.length > 0 ? haveContracts.length / needContracts.length * 15 : 15;
|
|
3936
|
+
const contextScore = neededFields.length > 0 ? coveredFields.length / neededFields.length * 15 : 15;
|
|
3937
|
+
const eventScore = manifest.transitions.length > 0 ? coveredTransitions.length / manifest.transitions.length * 10 : 10;
|
|
3938
|
+
const score = Math.round(ruleScore + constraintScore + contractScore + contextScore + eventScore);
|
|
3939
|
+
const rating = score >= 90 ? "complete" : score >= 70 ? "good" : score >= 50 ? "partial" : "incomplete";
|
|
3940
|
+
const report = {
|
|
3941
|
+
score,
|
|
3942
|
+
rating,
|
|
3943
|
+
rules: { total: domainBranches.length, covered: coveredDomain.length, uncovered: uncoveredDomain },
|
|
3944
|
+
constraints: { total: invariantBranches.length, covered: coveredInvariants.length, uncovered: uncoveredInvariants },
|
|
3945
|
+
contracts: { total: needContracts.length, withContracts: haveContracts.length, missing: missingContracts },
|
|
3946
|
+
context: { total: neededFields.length, covered: coveredFields.length, missing: missingFields },
|
|
3947
|
+
events: { total: manifest.transitions.length, covered: coveredTransitions.length, missing: missingTransitions }
|
|
3948
|
+
};
|
|
3949
|
+
if (config?.strict && score < threshold) {
|
|
3950
|
+
throw new Error(`Praxis completeness ${score}/100 (${rating}) \u2014 below threshold ${threshold}. ${uncoveredDomain.length} uncovered rules, ${uncoveredInvariants.length} uncovered invariants, ${missingContracts.length} missing contracts.`);
|
|
3951
|
+
}
|
|
3952
|
+
return report;
|
|
3953
|
+
}
|
|
3954
|
+
function formatReport(report) {
|
|
3955
|
+
const lines = [];
|
|
3956
|
+
const icon = report.rating === "complete" ? "\u2705" : report.rating === "good" ? "\u{1F7E2}" : report.rating === "partial" ? "\u{1F7E1}" : "\u{1F534}";
|
|
3957
|
+
lines.push(`${icon} Praxis Completeness: ${report.score}/100 (${report.rating})`);
|
|
3958
|
+
lines.push("");
|
|
3959
|
+
lines.push(`Rules: ${report.rules.covered}/${report.rules.total} domain branches covered (${pct(report.rules.covered, report.rules.total)})`);
|
|
3960
|
+
lines.push(`Constraints: ${report.constraints.covered}/${report.constraints.total} invariants covered (${pct(report.constraints.covered, report.constraints.total)})`);
|
|
3961
|
+
lines.push(`Contracts: ${report.contracts.withContracts}/${report.contracts.total} rules have contracts (${pct(report.contracts.withContracts, report.contracts.total)})`);
|
|
3962
|
+
lines.push(`Context: ${report.context.covered}/${report.context.total} state fields in context (${pct(report.context.covered, report.context.total)})`);
|
|
3963
|
+
lines.push(`Events: ${report.events.covered}/${report.events.total} transitions have events (${pct(report.events.covered, report.events.total)})`);
|
|
3964
|
+
if (report.rules.uncovered.length > 0) {
|
|
3965
|
+
lines.push("");
|
|
3966
|
+
lines.push("Uncovered domain logic:");
|
|
3967
|
+
for (const b of report.rules.uncovered) {
|
|
3968
|
+
lines.push(` \u274C ${b.location}: ${b.condition}${b.note ? ` \u2014 ${b.note}` : ""}`);
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
if (report.constraints.uncovered.length > 0) {
|
|
3972
|
+
lines.push("");
|
|
3973
|
+
lines.push("Uncovered invariants:");
|
|
3974
|
+
for (const b of report.constraints.uncovered) {
|
|
3975
|
+
lines.push(` \u274C ${b.location}: ${b.condition}${b.note ? ` \u2014 ${b.note}` : ""}`);
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
if (report.contracts.missing.length > 0) {
|
|
3979
|
+
lines.push("");
|
|
3980
|
+
lines.push("Rules missing contracts:");
|
|
3981
|
+
for (const id of report.contracts.missing) {
|
|
3982
|
+
lines.push(` \u{1F4DD} ${id}`);
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
if (report.events.missing.length > 0) {
|
|
3986
|
+
lines.push("");
|
|
3987
|
+
lines.push("State transitions without events:");
|
|
3988
|
+
for (const t of report.events.missing) {
|
|
3989
|
+
lines.push(` \u26A1 ${t.location}: ${t.description}`);
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
return lines.join("\n");
|
|
3993
|
+
}
|
|
3994
|
+
function pct(a, b) {
|
|
3995
|
+
if (b === 0) return "100%";
|
|
3996
|
+
return Math.round(a / b * 100) + "%";
|
|
3997
|
+
}
|
|
3998
|
+
var init_completeness = __esm({
|
|
3999
|
+
"src/core/completeness.ts"() {
|
|
4000
|
+
"use strict";
|
|
4001
|
+
}
|
|
4002
|
+
});
|
|
4003
|
+
|
|
4004
|
+
// src/decision-ledger/types.ts
|
|
4005
|
+
function isContract(obj) {
|
|
4006
|
+
if (typeof obj !== "object" || obj === null) {
|
|
4007
|
+
return false;
|
|
4008
|
+
}
|
|
4009
|
+
const contract = obj;
|
|
4010
|
+
return typeof contract.ruleId === "string" && typeof contract.behavior === "string" && Array.isArray(contract.examples) && contract.examples.length > 0 && contract.examples.every(
|
|
4011
|
+
(ex) => typeof ex === "object" && ex !== null && typeof ex.given === "string" && typeof ex.when === "string" && typeof ex.then === "string"
|
|
4012
|
+
) && Array.isArray(contract.invariants) && contract.invariants.every((inv) => typeof inv === "string");
|
|
4013
|
+
}
|
|
4014
|
+
function defineContract(options) {
|
|
4015
|
+
if (options.examples.length === 0) {
|
|
4016
|
+
throw new Error("Contract must have at least one example");
|
|
4017
|
+
}
|
|
4018
|
+
if (options.assumptions) {
|
|
4019
|
+
for (const assumption of options.assumptions) {
|
|
4020
|
+
if (assumption.confidence < 0 || assumption.confidence > 1) {
|
|
4021
|
+
throw new Error(
|
|
4022
|
+
`Assumption '${assumption.id}' has invalid confidence value ${assumption.confidence}. Must be between 0.0 and 1.0`
|
|
4023
|
+
);
|
|
4024
|
+
}
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
return {
|
|
4028
|
+
ruleId: options.ruleId,
|
|
4029
|
+
behavior: options.behavior,
|
|
4030
|
+
examples: options.examples,
|
|
4031
|
+
invariants: options.invariants,
|
|
4032
|
+
assumptions: options.assumptions,
|
|
4033
|
+
references: options.references,
|
|
4034
|
+
version: options.version || "1.0.0",
|
|
4035
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4036
|
+
};
|
|
4037
|
+
}
|
|
4038
|
+
function getContract(meta) {
|
|
4039
|
+
if (!meta || !meta.contract) {
|
|
4040
|
+
return void 0;
|
|
4041
|
+
}
|
|
4042
|
+
if (isContract(meta.contract)) {
|
|
4043
|
+
return meta.contract;
|
|
4044
|
+
}
|
|
4045
|
+
return void 0;
|
|
4046
|
+
}
|
|
4047
|
+
function getContractFromDescriptor(descriptor) {
|
|
4048
|
+
if (!descriptor) {
|
|
4049
|
+
return void 0;
|
|
4050
|
+
}
|
|
4051
|
+
if (descriptor.contract && isContract(descriptor.contract)) {
|
|
4052
|
+
return descriptor.contract;
|
|
4053
|
+
}
|
|
4054
|
+
return getContract(descriptor.meta);
|
|
4055
|
+
}
|
|
4056
|
+
var init_types2 = __esm({
|
|
4057
|
+
"src/decision-ledger/types.ts"() {
|
|
4058
|
+
"use strict";
|
|
4059
|
+
}
|
|
4060
|
+
});
|
|
4061
|
+
|
|
4062
|
+
// src/mcp/server.ts
|
|
4063
|
+
var server_exports = {};
|
|
4064
|
+
__export(server_exports, {
|
|
4065
|
+
createPraxisMcpServer: () => createPraxisMcpServer
|
|
4066
|
+
});
|
|
4067
|
+
function createPraxisMcpServer(options) {
|
|
4068
|
+
const {
|
|
4069
|
+
name = "@plures/praxis",
|
|
4070
|
+
version = "1.0.0",
|
|
4071
|
+
initialContext,
|
|
4072
|
+
registry,
|
|
4073
|
+
initialFacts
|
|
4074
|
+
} = options;
|
|
4075
|
+
const engine = new LogicEngine({
|
|
4076
|
+
initialContext,
|
|
4077
|
+
registry,
|
|
4078
|
+
initialFacts
|
|
4079
|
+
});
|
|
4080
|
+
const server2 = new import_mcp.McpServer({
|
|
4081
|
+
name,
|
|
4082
|
+
version
|
|
4083
|
+
});
|
|
4084
|
+
server2.tool(
|
|
4085
|
+
"praxis.inspect",
|
|
4086
|
+
"List all registered rules, constraints, and their contracts",
|
|
4087
|
+
{
|
|
4088
|
+
filter: import_zod.z.string().optional().describe("Filter rule/constraint IDs by pattern (substring match)"),
|
|
4089
|
+
includeContracts: import_zod.z.boolean().optional().describe("Include full contract details (default: false)")
|
|
4090
|
+
},
|
|
4091
|
+
async (params) => {
|
|
4092
|
+
const filter = params.filter;
|
|
4093
|
+
const includeContracts = params.includeContracts ?? false;
|
|
4094
|
+
let rules = registry.getAllRules();
|
|
4095
|
+
let constraints = registry.getAllConstraints();
|
|
4096
|
+
if (filter) {
|
|
4097
|
+
rules = rules.filter((r) => r.id.includes(filter));
|
|
4098
|
+
constraints = constraints.filter((c) => c.id.includes(filter));
|
|
4099
|
+
}
|
|
4100
|
+
const ruleInfos = rules.map((r) => {
|
|
4101
|
+
const contract = getContractFromDescriptor(r);
|
|
4102
|
+
return {
|
|
4103
|
+
id: r.id,
|
|
4104
|
+
description: r.description,
|
|
4105
|
+
eventTypes: r.eventTypes,
|
|
4106
|
+
hasContract: !!contract,
|
|
4107
|
+
contract: includeContracts ? contract : void 0,
|
|
4108
|
+
meta: r.meta
|
|
4109
|
+
};
|
|
4110
|
+
});
|
|
4111
|
+
const constraintInfos = constraints.map((c) => {
|
|
4112
|
+
const contract = getContractFromDescriptor(c);
|
|
4113
|
+
return {
|
|
4114
|
+
id: c.id,
|
|
4115
|
+
description: c.description,
|
|
4116
|
+
hasContract: !!contract,
|
|
4117
|
+
contract: includeContracts ? contract : void 0,
|
|
4118
|
+
meta: c.meta
|
|
4119
|
+
};
|
|
4120
|
+
});
|
|
4121
|
+
const output = {
|
|
4122
|
+
rules: ruleInfos,
|
|
4123
|
+
constraints: constraintInfos,
|
|
4124
|
+
summary: {
|
|
4125
|
+
totalRules: ruleInfos.length,
|
|
4126
|
+
totalConstraints: constraintInfos.length,
|
|
4127
|
+
rulesWithContracts: ruleInfos.filter((r) => r.hasContract).length,
|
|
4128
|
+
constraintsWithContracts: constraintInfos.filter((c) => c.hasContract).length
|
|
4129
|
+
}
|
|
4130
|
+
};
|
|
4131
|
+
return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
|
|
4132
|
+
}
|
|
4133
|
+
);
|
|
4134
|
+
server2.tool(
|
|
4135
|
+
"praxis.evaluate",
|
|
4136
|
+
"Run a specific rule against given events and return the result",
|
|
4137
|
+
{
|
|
4138
|
+
ruleId: import_zod.z.string().describe("The rule ID to evaluate"),
|
|
4139
|
+
events: import_zod.z.array(import_zod.z.object({
|
|
4140
|
+
tag: import_zod.z.string(),
|
|
4141
|
+
payload: import_zod.z.unknown()
|
|
4142
|
+
})).describe("Events to process through the rule")
|
|
4143
|
+
},
|
|
4144
|
+
async (params) => {
|
|
4145
|
+
const rule = registry.getRule(params.ruleId);
|
|
4146
|
+
if (!rule) {
|
|
4147
|
+
return {
|
|
4148
|
+
content: [{ type: "text", text: JSON.stringify({ error: `Rule "${params.ruleId}" not found` }) }]
|
|
4149
|
+
};
|
|
4150
|
+
}
|
|
4151
|
+
const state = engine.getState();
|
|
4152
|
+
const stateWithEvents = { ...state, events: params.events };
|
|
4153
|
+
try {
|
|
4154
|
+
const rawResult = rule.impl(stateWithEvents, params.events);
|
|
4155
|
+
let output;
|
|
4156
|
+
if (rawResult instanceof RuleResult) {
|
|
4157
|
+
output = {
|
|
4158
|
+
ruleId: params.ruleId,
|
|
4159
|
+
resultKind: rawResult.kind,
|
|
4160
|
+
facts: rawResult.facts,
|
|
4161
|
+
retractedTags: rawResult.retractTags,
|
|
4162
|
+
reason: rawResult.reason,
|
|
4163
|
+
diagnostics: []
|
|
4164
|
+
};
|
|
4165
|
+
} else if (Array.isArray(rawResult)) {
|
|
4166
|
+
output = {
|
|
4167
|
+
ruleId: params.ruleId,
|
|
4168
|
+
resultKind: "emit",
|
|
4169
|
+
facts: rawResult,
|
|
4170
|
+
retractedTags: [],
|
|
4171
|
+
diagnostics: []
|
|
4172
|
+
};
|
|
4173
|
+
} else {
|
|
4174
|
+
output = {
|
|
4175
|
+
ruleId: params.ruleId,
|
|
4176
|
+
resultKind: "noop",
|
|
4177
|
+
facts: [],
|
|
4178
|
+
retractedTags: [],
|
|
4179
|
+
diagnostics: []
|
|
4180
|
+
};
|
|
4181
|
+
}
|
|
4182
|
+
return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
|
|
4183
|
+
} catch (error) {
|
|
4184
|
+
return {
|
|
4185
|
+
content: [{
|
|
4186
|
+
type: "text",
|
|
4187
|
+
text: JSON.stringify({
|
|
4188
|
+
error: `Rule evaluation failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
4189
|
+
ruleId: params.ruleId
|
|
4190
|
+
})
|
|
4191
|
+
}]
|
|
4192
|
+
};
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
);
|
|
4196
|
+
server2.tool(
|
|
4197
|
+
"praxis.audit",
|
|
4198
|
+
"Run completeness audit against a manifest and return the report",
|
|
4199
|
+
{
|
|
4200
|
+
branches: import_zod.z.array(import_zod.z.object({
|
|
4201
|
+
location: import_zod.z.string(),
|
|
4202
|
+
condition: import_zod.z.string(),
|
|
4203
|
+
kind: import_zod.z.enum(["domain", "invariant", "ui", "transport", "wiring", "transform"]),
|
|
4204
|
+
coveredBy: import_zod.z.string().nullable(),
|
|
4205
|
+
note: import_zod.z.string().optional()
|
|
4206
|
+
})).describe("Logic branches to audit"),
|
|
4207
|
+
stateFields: import_zod.z.array(import_zod.z.object({
|
|
4208
|
+
source: import_zod.z.string(),
|
|
4209
|
+
field: import_zod.z.string(),
|
|
4210
|
+
inContext: import_zod.z.boolean(),
|
|
4211
|
+
usedByRule: import_zod.z.boolean()
|
|
4212
|
+
})).describe("State fields to check context coverage"),
|
|
4213
|
+
transitions: import_zod.z.array(import_zod.z.object({
|
|
4214
|
+
description: import_zod.z.string(),
|
|
4215
|
+
eventTag: import_zod.z.string().nullable(),
|
|
4216
|
+
location: import_zod.z.string()
|
|
4217
|
+
})).describe("State transitions to check event coverage"),
|
|
4218
|
+
rulesNeedingContracts: import_zod.z.array(import_zod.z.string()).describe("Rule IDs that should have contracts"),
|
|
4219
|
+
threshold: import_zod.z.number().optional().describe("Minimum passing score (default: 90)")
|
|
4220
|
+
},
|
|
4221
|
+
async (params) => {
|
|
4222
|
+
const rulesWithContracts = registry.getAllRules().filter((r) => getContractFromDescriptor(r)).map((r) => r.id);
|
|
4223
|
+
const report = auditCompleteness(
|
|
4224
|
+
{
|
|
4225
|
+
branches: params.branches,
|
|
4226
|
+
stateFields: params.stateFields,
|
|
4227
|
+
transitions: params.transitions,
|
|
4228
|
+
rulesNeedingContracts: params.rulesNeedingContracts
|
|
4229
|
+
},
|
|
4230
|
+
registry.getRuleIds(),
|
|
4231
|
+
registry.getConstraintIds(),
|
|
4232
|
+
rulesWithContracts,
|
|
4233
|
+
{ threshold: params.threshold }
|
|
4234
|
+
);
|
|
4235
|
+
const output = {
|
|
4236
|
+
report,
|
|
4237
|
+
formatted: formatReport(report)
|
|
4238
|
+
};
|
|
4239
|
+
return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
|
|
4240
|
+
}
|
|
4241
|
+
);
|
|
4242
|
+
server2.tool(
|
|
4243
|
+
"praxis.suggest",
|
|
4244
|
+
"Given a gap or description, suggest rules/constraints to add",
|
|
4245
|
+
{
|
|
4246
|
+
gap: import_zod.z.string().describe("Description of the gap or failing expectation"),
|
|
4247
|
+
context: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional().describe("Current context for suggestions")
|
|
4248
|
+
},
|
|
4249
|
+
async (params) => {
|
|
4250
|
+
const existingRules = registry.getAllRules();
|
|
4251
|
+
const _constraints = registry.getAllConstraints();
|
|
4252
|
+
void _constraints;
|
|
4253
|
+
const suggestions = [];
|
|
4254
|
+
const gapLower = params.gap.toLowerCase();
|
|
4255
|
+
const relatedRules = existingRules.filter(
|
|
4256
|
+
(r) => r.description.toLowerCase().includes(gapLower) || gapLower.includes(r.id.toLowerCase())
|
|
4257
|
+
);
|
|
4258
|
+
if (relatedRules.length > 0) {
|
|
4259
|
+
for (const rule of relatedRules) {
|
|
4260
|
+
suggestions.push({
|
|
4261
|
+
type: "constraint",
|
|
4262
|
+
id: `${rule.id}/guard`,
|
|
4263
|
+
description: `Add a constraint to guard the behavior described by rule "${rule.id}"`,
|
|
4264
|
+
rationale: `Rule "${rule.id}" (${rule.description}) exists but may not fully cover: ${params.gap}`
|
|
4265
|
+
});
|
|
4266
|
+
}
|
|
4267
|
+
}
|
|
4268
|
+
const invariantTerms = ["must", "never", "always", "require", "valid", "invalid", "prevent"];
|
|
4269
|
+
if (invariantTerms.some((t) => gapLower.includes(t))) {
|
|
4270
|
+
suggestions.push({
|
|
4271
|
+
type: "constraint",
|
|
4272
|
+
id: suggestId(params.gap, "constraint"),
|
|
4273
|
+
description: `Constraint: ${params.gap}`,
|
|
4274
|
+
rationale: "Gap description contains invariant language \u2014 a constraint would encode this guarantee"
|
|
4275
|
+
});
|
|
4276
|
+
}
|
|
4277
|
+
const ruleTerms = ["when", "if", "show", "emit", "trigger", "display", "update"];
|
|
4278
|
+
if (ruleTerms.some((t) => gapLower.includes(t))) {
|
|
4279
|
+
suggestions.push({
|
|
4280
|
+
type: "rule",
|
|
4281
|
+
id: suggestId(params.gap, "rule"),
|
|
4282
|
+
description: `Rule: ${params.gap}`,
|
|
4283
|
+
rationale: "Gap description contains conditional behavior \u2014 a rule would implement this logic"
|
|
4284
|
+
});
|
|
4285
|
+
}
|
|
4286
|
+
const contractGaps = registry.getContractGaps();
|
|
4287
|
+
if (contractGaps.length > 0) {
|
|
4288
|
+
const relatedGaps = contractGaps.filter(
|
|
4289
|
+
(g) => gapLower.includes(g.ruleId.toLowerCase())
|
|
4290
|
+
);
|
|
4291
|
+
for (const g of relatedGaps) {
|
|
4292
|
+
suggestions.push({
|
|
4293
|
+
type: "contract",
|
|
4294
|
+
id: g.ruleId,
|
|
4295
|
+
description: `Add contract for "${g.ruleId}" \u2014 missing: ${g.missing.join(", ")}`,
|
|
4296
|
+
rationale: `Related rule "${g.ruleId}" lacks a contract, which could prevent this gap`
|
|
4297
|
+
});
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
const eventTerms = ["transition", "change", "happen", "occur", "fire", "dispatch"];
|
|
4301
|
+
if (eventTerms.some((t) => gapLower.includes(t))) {
|
|
4302
|
+
suggestions.push({
|
|
4303
|
+
type: "event",
|
|
4304
|
+
id: suggestId(params.gap, "event"),
|
|
4305
|
+
description: `Event for: ${params.gap}`,
|
|
4306
|
+
rationale: "Gap description suggests a state transition \u2014 an event would make it observable"
|
|
4307
|
+
});
|
|
4308
|
+
}
|
|
4309
|
+
if (suggestions.length === 0) {
|
|
4310
|
+
suggestions.push({
|
|
4311
|
+
type: "rule",
|
|
4312
|
+
id: suggestId(params.gap, "rule"),
|
|
4313
|
+
description: `Rule: ${params.gap}`,
|
|
4314
|
+
rationale: "No existing rules or constraints cover this gap \u2014 a new rule is recommended"
|
|
4315
|
+
});
|
|
4316
|
+
}
|
|
4317
|
+
const output = { suggestions };
|
|
4318
|
+
return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
|
|
4319
|
+
}
|
|
4320
|
+
);
|
|
4321
|
+
server2.tool(
|
|
4322
|
+
"praxis.facts",
|
|
4323
|
+
"Get the current fact state of the engine",
|
|
4324
|
+
{},
|
|
4325
|
+
async () => {
|
|
4326
|
+
const facts = engine.getFacts();
|
|
4327
|
+
const output = {
|
|
4328
|
+
facts,
|
|
4329
|
+
count: facts.length
|
|
4330
|
+
};
|
|
4331
|
+
return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
|
|
4332
|
+
}
|
|
4333
|
+
);
|
|
4334
|
+
server2.tool(
|
|
4335
|
+
"praxis.step",
|
|
4336
|
+
"Step the engine with events and return the new state",
|
|
4337
|
+
{
|
|
4338
|
+
events: import_zod.z.array(import_zod.z.object({
|
|
4339
|
+
tag: import_zod.z.string(),
|
|
4340
|
+
payload: import_zod.z.unknown()
|
|
4341
|
+
})).describe("Events to process")
|
|
4342
|
+
},
|
|
4343
|
+
async (params) => {
|
|
4344
|
+
const result = engine.step(params.events);
|
|
4345
|
+
const output = {
|
|
4346
|
+
facts: result.state.facts,
|
|
4347
|
+
diagnostics: result.diagnostics,
|
|
4348
|
+
factCount: result.state.facts.length
|
|
4349
|
+
};
|
|
4350
|
+
return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
|
|
4351
|
+
}
|
|
4352
|
+
);
|
|
4353
|
+
server2.tool(
|
|
4354
|
+
"praxis.contracts",
|
|
4355
|
+
"List all contracts with their coverage status",
|
|
4356
|
+
{
|
|
4357
|
+
filter: import_zod.z.string().optional().describe("Filter by rule/constraint ID (substring match)")
|
|
4358
|
+
},
|
|
4359
|
+
async (params) => {
|
|
4360
|
+
const rules = registry.getAllRules();
|
|
4361
|
+
const constraints = registry.getAllConstraints();
|
|
4362
|
+
let contracts = [
|
|
4363
|
+
...rules.map((r) => ({
|
|
4364
|
+
ruleId: r.id,
|
|
4365
|
+
hasContract: !!getContractFromDescriptor(r),
|
|
4366
|
+
contract: getContractFromDescriptor(r),
|
|
4367
|
+
type: "rule"
|
|
4368
|
+
})),
|
|
4369
|
+
...constraints.map((c) => ({
|
|
4370
|
+
ruleId: c.id,
|
|
4371
|
+
hasContract: !!getContractFromDescriptor(c),
|
|
4372
|
+
contract: getContractFromDescriptor(c),
|
|
4373
|
+
type: "constraint"
|
|
4374
|
+
}))
|
|
4375
|
+
];
|
|
4376
|
+
if (params.filter) {
|
|
4377
|
+
contracts = contracts.filter((c) => c.ruleId.includes(params.filter));
|
|
4378
|
+
}
|
|
4379
|
+
const total = contracts.length;
|
|
4380
|
+
const withContracts = contracts.filter((c) => c.hasContract).length;
|
|
4381
|
+
const output = {
|
|
4382
|
+
contracts,
|
|
4383
|
+
coverage: {
|
|
4384
|
+
total,
|
|
4385
|
+
withContracts,
|
|
4386
|
+
percentage: total > 0 ? Math.round(withContracts / total * 100) : 100
|
|
4387
|
+
}
|
|
4388
|
+
};
|
|
4389
|
+
return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
|
|
4390
|
+
}
|
|
4391
|
+
);
|
|
4392
|
+
return {
|
|
4393
|
+
/** The underlying MCP server instance */
|
|
4394
|
+
mcpServer: server2,
|
|
4395
|
+
/** The underlying Praxis engine */
|
|
4396
|
+
engine,
|
|
4397
|
+
/** Start the server on stdio transport */
|
|
4398
|
+
async start() {
|
|
4399
|
+
const transport = new import_stdio.StdioServerTransport();
|
|
4400
|
+
await server2.connect(transport);
|
|
4401
|
+
}
|
|
4402
|
+
};
|
|
4403
|
+
}
|
|
4404
|
+
function suggestId(description, type) {
|
|
4405
|
+
const slug = description.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().split(/\s+/).slice(0, 3).join("-");
|
|
4406
|
+
return `suggested/${type}/${slug}`;
|
|
4407
|
+
}
|
|
4408
|
+
var import_mcp, import_stdio, import_zod;
|
|
4409
|
+
var init_server = __esm({
|
|
4410
|
+
"src/mcp/server.ts"() {
|
|
4411
|
+
"use strict";
|
|
4412
|
+
import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
4413
|
+
import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
4414
|
+
import_zod = require("zod");
|
|
4415
|
+
init_engine();
|
|
4416
|
+
init_rule_result();
|
|
4417
|
+
init_completeness();
|
|
4418
|
+
init_types2();
|
|
4419
|
+
}
|
|
4420
|
+
});
|
|
4421
|
+
|
|
4422
|
+
// src/core/rules.ts
|
|
4423
|
+
var rules_exports = {};
|
|
4424
|
+
__export(rules_exports, {
|
|
4425
|
+
PraxisRegistry: () => PraxisRegistry
|
|
4426
|
+
});
|
|
4427
|
+
var PraxisRegistry;
|
|
4428
|
+
var init_rules = __esm({
|
|
4429
|
+
"src/core/rules.ts"() {
|
|
4430
|
+
"use strict";
|
|
4431
|
+
PraxisRegistry = class {
|
|
4432
|
+
rules = /* @__PURE__ */ new Map();
|
|
4433
|
+
constraints = /* @__PURE__ */ new Map();
|
|
4434
|
+
compliance;
|
|
4435
|
+
contractGaps = [];
|
|
4436
|
+
constructor(options = {}) {
|
|
4437
|
+
const defaultEnabled = typeof process !== "undefined" ? process.env?.NODE_ENV !== "production" : false;
|
|
4438
|
+
this.compliance = {
|
|
4439
|
+
enabled: defaultEnabled,
|
|
4440
|
+
requiredFields: ["behavior", "examples", "invariants"],
|
|
4441
|
+
missingSeverity: "warning",
|
|
4442
|
+
...options.compliance
|
|
4443
|
+
};
|
|
4444
|
+
}
|
|
4445
|
+
/**
|
|
4446
|
+
* Register a rule
|
|
4447
|
+
*/
|
|
4448
|
+
registerRule(descriptor) {
|
|
4449
|
+
if (this.rules.has(descriptor.id)) {
|
|
4450
|
+
throw new Error(`Rule with id "${descriptor.id}" already registered`);
|
|
4451
|
+
}
|
|
4452
|
+
this.rules.set(descriptor.id, descriptor);
|
|
4453
|
+
this.trackContractCompliance(descriptor.id, descriptor);
|
|
4454
|
+
}
|
|
4455
|
+
/**
|
|
4456
|
+
* Register a constraint
|
|
4457
|
+
*/
|
|
4458
|
+
registerConstraint(descriptor) {
|
|
4459
|
+
if (this.constraints.has(descriptor.id)) {
|
|
4460
|
+
throw new Error(`Constraint with id "${descriptor.id}" already registered`);
|
|
4461
|
+
}
|
|
4462
|
+
this.constraints.set(descriptor.id, descriptor);
|
|
4463
|
+
this.trackContractCompliance(descriptor.id, descriptor);
|
|
4464
|
+
}
|
|
4465
|
+
/**
|
|
4466
|
+
* Register a module (all its rules and constraints)
|
|
4467
|
+
*/
|
|
4468
|
+
registerModule(module2) {
|
|
4469
|
+
for (const rule of module2.rules) {
|
|
4470
|
+
this.registerRule(rule);
|
|
4471
|
+
}
|
|
4472
|
+
for (const constraint of module2.constraints) {
|
|
4473
|
+
this.registerConstraint(constraint);
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
/**
|
|
4477
|
+
* Get a rule by ID
|
|
4478
|
+
*/
|
|
4479
|
+
getRule(id) {
|
|
4480
|
+
return this.rules.get(id);
|
|
4481
|
+
}
|
|
4482
|
+
/**
|
|
4483
|
+
* Get a constraint by ID
|
|
4484
|
+
*/
|
|
4485
|
+
getConstraint(id) {
|
|
4486
|
+
return this.constraints.get(id);
|
|
4487
|
+
}
|
|
4488
|
+
/**
|
|
4489
|
+
* Get all registered rule IDs
|
|
4490
|
+
*/
|
|
4491
|
+
getRuleIds() {
|
|
4492
|
+
return Array.from(this.rules.keys());
|
|
4493
|
+
}
|
|
4494
|
+
/**
|
|
4495
|
+
* Get all registered constraint IDs
|
|
4496
|
+
*/
|
|
4497
|
+
getConstraintIds() {
|
|
4498
|
+
return Array.from(this.constraints.keys());
|
|
4499
|
+
}
|
|
4500
|
+
/**
|
|
4501
|
+
* Get all rules
|
|
4502
|
+
*/
|
|
4503
|
+
getAllRules() {
|
|
4504
|
+
return Array.from(this.rules.values());
|
|
4505
|
+
}
|
|
4506
|
+
/**
|
|
4507
|
+
* Get all constraints
|
|
4508
|
+
*/
|
|
4509
|
+
getAllConstraints() {
|
|
4510
|
+
return Array.from(this.constraints.values());
|
|
4511
|
+
}
|
|
4512
|
+
/**
|
|
4513
|
+
* Get collected contract gaps from registration-time validation.
|
|
4514
|
+
*/
|
|
4515
|
+
getContractGaps() {
|
|
4516
|
+
return [...this.contractGaps];
|
|
4517
|
+
}
|
|
4518
|
+
/**
|
|
4519
|
+
* Clear collected contract gaps.
|
|
4520
|
+
*/
|
|
4521
|
+
clearContractGaps() {
|
|
4522
|
+
this.contractGaps = [];
|
|
4523
|
+
}
|
|
4524
|
+
trackContractCompliance(id, descriptor) {
|
|
4525
|
+
if (!this.compliance.enabled) {
|
|
4526
|
+
return;
|
|
4527
|
+
}
|
|
4528
|
+
const gaps = this.validateDescriptorContract(id, descriptor);
|
|
4529
|
+
for (const gap of gaps) {
|
|
4530
|
+
this.contractGaps.push(gap);
|
|
4531
|
+
if (this.compliance.onGap) {
|
|
4532
|
+
this.compliance.onGap(gap);
|
|
4533
|
+
} else {
|
|
4534
|
+
const label = gap.severity === "error" ? "ERROR" : gap.severity === "warning" ? "WARN" : "INFO";
|
|
4535
|
+
console.warn(`[Praxis][${label}] Contract gap for "${gap.ruleId}": missing ${gap.missing.join(", ")}`);
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
}
|
|
4539
|
+
validateDescriptorContract(id, descriptor) {
|
|
4540
|
+
const requiredFields = this.compliance.requiredFields ?? ["behavior", "examples", "invariants"];
|
|
4541
|
+
const missingSeverity = this.compliance.missingSeverity ?? "warning";
|
|
4542
|
+
const contract = descriptor.contract ?? (descriptor.meta?.contract && typeof descriptor.meta.contract === "object" ? descriptor.meta.contract : void 0);
|
|
4543
|
+
if (!contract) {
|
|
4544
|
+
return [
|
|
4545
|
+
{
|
|
4546
|
+
ruleId: id,
|
|
4547
|
+
missing: ["contract"],
|
|
4548
|
+
severity: missingSeverity,
|
|
4549
|
+
message: `Contract missing for "${id}"`
|
|
4550
|
+
}
|
|
4551
|
+
];
|
|
4552
|
+
}
|
|
4553
|
+
const missing = [];
|
|
4554
|
+
if (requiredFields.includes("behavior") && (!contract.behavior || contract.behavior.trim() === "")) {
|
|
4555
|
+
missing.push("behavior");
|
|
4556
|
+
}
|
|
4557
|
+
if (requiredFields.includes("examples") && (!contract.examples || contract.examples.length === 0)) {
|
|
4558
|
+
missing.push("examples");
|
|
4559
|
+
}
|
|
4560
|
+
if (requiredFields.includes("invariants") && (!contract.invariants || contract.invariants.length === 0)) {
|
|
4561
|
+
missing.push("invariants");
|
|
4562
|
+
}
|
|
4563
|
+
if (missing.length === 0) {
|
|
4564
|
+
return [];
|
|
4565
|
+
}
|
|
4566
|
+
return [
|
|
4567
|
+
{
|
|
4568
|
+
ruleId: id,
|
|
4569
|
+
missing,
|
|
4570
|
+
severity: "warning",
|
|
4571
|
+
message: `Contract for "${id}" is incomplete: missing ${missing.join(", ")}`
|
|
4572
|
+
}
|
|
4573
|
+
];
|
|
4574
|
+
}
|
|
4575
|
+
};
|
|
4576
|
+
}
|
|
4577
|
+
});
|
|
4578
|
+
|
|
3520
4579
|
// node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/typescript.js
|
|
3521
4580
|
var require_typescript = __commonJS({
|
|
3522
4581
|
"node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/typescript.js"(exports2, module2) {
|
|
@@ -214475,217 +215534,6 @@ var init_verify = __esm({
|
|
|
214475
215534
|
}
|
|
214476
215535
|
});
|
|
214477
215536
|
|
|
214478
|
-
// src/core/rules.ts
|
|
214479
|
-
var PraxisRegistry;
|
|
214480
|
-
var init_rules = __esm({
|
|
214481
|
-
"src/core/rules.ts"() {
|
|
214482
|
-
"use strict";
|
|
214483
|
-
PraxisRegistry = class {
|
|
214484
|
-
rules = /* @__PURE__ */ new Map();
|
|
214485
|
-
constraints = /* @__PURE__ */ new Map();
|
|
214486
|
-
compliance;
|
|
214487
|
-
contractGaps = [];
|
|
214488
|
-
constructor(options = {}) {
|
|
214489
|
-
const defaultEnabled = typeof process !== "undefined" ? process.env?.NODE_ENV !== "production" : false;
|
|
214490
|
-
this.compliance = {
|
|
214491
|
-
enabled: defaultEnabled,
|
|
214492
|
-
requiredFields: ["behavior", "examples", "invariants"],
|
|
214493
|
-
missingSeverity: "warning",
|
|
214494
|
-
...options.compliance
|
|
214495
|
-
};
|
|
214496
|
-
}
|
|
214497
|
-
/**
|
|
214498
|
-
* Register a rule
|
|
214499
|
-
*/
|
|
214500
|
-
registerRule(descriptor) {
|
|
214501
|
-
if (this.rules.has(descriptor.id)) {
|
|
214502
|
-
throw new Error(`Rule with id "${descriptor.id}" already registered`);
|
|
214503
|
-
}
|
|
214504
|
-
this.rules.set(descriptor.id, descriptor);
|
|
214505
|
-
this.trackContractCompliance(descriptor.id, descriptor);
|
|
214506
|
-
}
|
|
214507
|
-
/**
|
|
214508
|
-
* Register a constraint
|
|
214509
|
-
*/
|
|
214510
|
-
registerConstraint(descriptor) {
|
|
214511
|
-
if (this.constraints.has(descriptor.id)) {
|
|
214512
|
-
throw new Error(`Constraint with id "${descriptor.id}" already registered`);
|
|
214513
|
-
}
|
|
214514
|
-
this.constraints.set(descriptor.id, descriptor);
|
|
214515
|
-
this.trackContractCompliance(descriptor.id, descriptor);
|
|
214516
|
-
}
|
|
214517
|
-
/**
|
|
214518
|
-
* Register a module (all its rules and constraints)
|
|
214519
|
-
*/
|
|
214520
|
-
registerModule(module2) {
|
|
214521
|
-
for (const rule of module2.rules) {
|
|
214522
|
-
this.registerRule(rule);
|
|
214523
|
-
}
|
|
214524
|
-
for (const constraint of module2.constraints) {
|
|
214525
|
-
this.registerConstraint(constraint);
|
|
214526
|
-
}
|
|
214527
|
-
}
|
|
214528
|
-
/**
|
|
214529
|
-
* Get a rule by ID
|
|
214530
|
-
*/
|
|
214531
|
-
getRule(id) {
|
|
214532
|
-
return this.rules.get(id);
|
|
214533
|
-
}
|
|
214534
|
-
/**
|
|
214535
|
-
* Get a constraint by ID
|
|
214536
|
-
*/
|
|
214537
|
-
getConstraint(id) {
|
|
214538
|
-
return this.constraints.get(id);
|
|
214539
|
-
}
|
|
214540
|
-
/**
|
|
214541
|
-
* Get all registered rule IDs
|
|
214542
|
-
*/
|
|
214543
|
-
getRuleIds() {
|
|
214544
|
-
return Array.from(this.rules.keys());
|
|
214545
|
-
}
|
|
214546
|
-
/**
|
|
214547
|
-
* Get all registered constraint IDs
|
|
214548
|
-
*/
|
|
214549
|
-
getConstraintIds() {
|
|
214550
|
-
return Array.from(this.constraints.keys());
|
|
214551
|
-
}
|
|
214552
|
-
/**
|
|
214553
|
-
* Get all rules
|
|
214554
|
-
*/
|
|
214555
|
-
getAllRules() {
|
|
214556
|
-
return Array.from(this.rules.values());
|
|
214557
|
-
}
|
|
214558
|
-
/**
|
|
214559
|
-
* Get all constraints
|
|
214560
|
-
*/
|
|
214561
|
-
getAllConstraints() {
|
|
214562
|
-
return Array.from(this.constraints.values());
|
|
214563
|
-
}
|
|
214564
|
-
/**
|
|
214565
|
-
* Get collected contract gaps from registration-time validation.
|
|
214566
|
-
*/
|
|
214567
|
-
getContractGaps() {
|
|
214568
|
-
return [...this.contractGaps];
|
|
214569
|
-
}
|
|
214570
|
-
/**
|
|
214571
|
-
* Clear collected contract gaps.
|
|
214572
|
-
*/
|
|
214573
|
-
clearContractGaps() {
|
|
214574
|
-
this.contractGaps = [];
|
|
214575
|
-
}
|
|
214576
|
-
trackContractCompliance(id, descriptor) {
|
|
214577
|
-
if (!this.compliance.enabled) {
|
|
214578
|
-
return;
|
|
214579
|
-
}
|
|
214580
|
-
const gaps = this.validateDescriptorContract(id, descriptor);
|
|
214581
|
-
for (const gap of gaps) {
|
|
214582
|
-
this.contractGaps.push(gap);
|
|
214583
|
-
if (this.compliance.onGap) {
|
|
214584
|
-
this.compliance.onGap(gap);
|
|
214585
|
-
} else {
|
|
214586
|
-
const label = gap.severity === "error" ? "ERROR" : gap.severity === "warning" ? "WARN" : "INFO";
|
|
214587
|
-
console.warn(`[Praxis][${label}] Contract gap for "${gap.ruleId}": missing ${gap.missing.join(", ")}`);
|
|
214588
|
-
}
|
|
214589
|
-
}
|
|
214590
|
-
}
|
|
214591
|
-
validateDescriptorContract(id, descriptor) {
|
|
214592
|
-
const requiredFields = this.compliance.requiredFields ?? ["behavior", "examples", "invariants"];
|
|
214593
|
-
const missingSeverity = this.compliance.missingSeverity ?? "warning";
|
|
214594
|
-
const contract = descriptor.contract ?? (descriptor.meta?.contract && typeof descriptor.meta.contract === "object" ? descriptor.meta.contract : void 0);
|
|
214595
|
-
if (!contract) {
|
|
214596
|
-
return [
|
|
214597
|
-
{
|
|
214598
|
-
ruleId: id,
|
|
214599
|
-
missing: ["contract"],
|
|
214600
|
-
severity: missingSeverity,
|
|
214601
|
-
message: `Contract missing for "${id}"`
|
|
214602
|
-
}
|
|
214603
|
-
];
|
|
214604
|
-
}
|
|
214605
|
-
const missing = [];
|
|
214606
|
-
if (requiredFields.includes("behavior") && (!contract.behavior || contract.behavior.trim() === "")) {
|
|
214607
|
-
missing.push("behavior");
|
|
214608
|
-
}
|
|
214609
|
-
if (requiredFields.includes("examples") && (!contract.examples || contract.examples.length === 0)) {
|
|
214610
|
-
missing.push("examples");
|
|
214611
|
-
}
|
|
214612
|
-
if (requiredFields.includes("invariants") && (!contract.invariants || contract.invariants.length === 0)) {
|
|
214613
|
-
missing.push("invariants");
|
|
214614
|
-
}
|
|
214615
|
-
if (missing.length === 0) {
|
|
214616
|
-
return [];
|
|
214617
|
-
}
|
|
214618
|
-
return [
|
|
214619
|
-
{
|
|
214620
|
-
ruleId: id,
|
|
214621
|
-
missing,
|
|
214622
|
-
severity: "warning",
|
|
214623
|
-
message: `Contract for "${id}" is incomplete: missing ${missing.join(", ")}`
|
|
214624
|
-
}
|
|
214625
|
-
];
|
|
214626
|
-
}
|
|
214627
|
-
};
|
|
214628
|
-
}
|
|
214629
|
-
});
|
|
214630
|
-
|
|
214631
|
-
// src/decision-ledger/types.ts
|
|
214632
|
-
function isContract(obj) {
|
|
214633
|
-
if (typeof obj !== "object" || obj === null) {
|
|
214634
|
-
return false;
|
|
214635
|
-
}
|
|
214636
|
-
const contract = obj;
|
|
214637
|
-
return typeof contract.ruleId === "string" && typeof contract.behavior === "string" && Array.isArray(contract.examples) && contract.examples.length > 0 && contract.examples.every(
|
|
214638
|
-
(ex) => typeof ex === "object" && ex !== null && typeof ex.given === "string" && typeof ex.when === "string" && typeof ex.then === "string"
|
|
214639
|
-
) && Array.isArray(contract.invariants) && contract.invariants.every((inv) => typeof inv === "string");
|
|
214640
|
-
}
|
|
214641
|
-
function defineContract(options) {
|
|
214642
|
-
if (options.examples.length === 0) {
|
|
214643
|
-
throw new Error("Contract must have at least one example");
|
|
214644
|
-
}
|
|
214645
|
-
if (options.assumptions) {
|
|
214646
|
-
for (const assumption of options.assumptions) {
|
|
214647
|
-
if (assumption.confidence < 0 || assumption.confidence > 1) {
|
|
214648
|
-
throw new Error(
|
|
214649
|
-
`Assumption '${assumption.id}' has invalid confidence value ${assumption.confidence}. Must be between 0.0 and 1.0`
|
|
214650
|
-
);
|
|
214651
|
-
}
|
|
214652
|
-
}
|
|
214653
|
-
}
|
|
214654
|
-
return {
|
|
214655
|
-
ruleId: options.ruleId,
|
|
214656
|
-
behavior: options.behavior,
|
|
214657
|
-
examples: options.examples,
|
|
214658
|
-
invariants: options.invariants,
|
|
214659
|
-
assumptions: options.assumptions,
|
|
214660
|
-
references: options.references,
|
|
214661
|
-
version: options.version || "1.0.0",
|
|
214662
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
214663
|
-
};
|
|
214664
|
-
}
|
|
214665
|
-
function getContract(meta) {
|
|
214666
|
-
if (!meta || !meta.contract) {
|
|
214667
|
-
return void 0;
|
|
214668
|
-
}
|
|
214669
|
-
if (isContract(meta.contract)) {
|
|
214670
|
-
return meta.contract;
|
|
214671
|
-
}
|
|
214672
|
-
return void 0;
|
|
214673
|
-
}
|
|
214674
|
-
function getContractFromDescriptor(descriptor) {
|
|
214675
|
-
if (!descriptor) {
|
|
214676
|
-
return void 0;
|
|
214677
|
-
}
|
|
214678
|
-
if (descriptor.contract && isContract(descriptor.contract)) {
|
|
214679
|
-
return descriptor.contract;
|
|
214680
|
-
}
|
|
214681
|
-
return getContract(descriptor.meta);
|
|
214682
|
-
}
|
|
214683
|
-
var init_types2 = __esm({
|
|
214684
|
-
"src/decision-ledger/types.ts"() {
|
|
214685
|
-
"use strict";
|
|
214686
|
-
}
|
|
214687
|
-
});
|
|
214688
|
-
|
|
214689
215537
|
// src/dsl/index.ts
|
|
214690
215538
|
function defineFact(tag) {
|
|
214691
215539
|
return {
|
|
@@ -217810,6 +218658,25 @@ cloudCmd.command("usage").description("View cloud usage metrics").action(async (
|
|
|
217810
218658
|
process.exit(1);
|
|
217811
218659
|
}
|
|
217812
218660
|
});
|
|
218661
|
+
program.command("mcp").description("Start the Praxis MCP server (Model Context Protocol) for AI assistant integration").option("--name <name>", "Server name", "@plures/praxis").option("--version <version>", "Server version", "1.0.0").action(async (options) => {
|
|
218662
|
+
try {
|
|
218663
|
+
const { createPraxisMcpServer: createPraxisMcpServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
218664
|
+
const { PraxisRegistry: PraxisRegistry2 } = await Promise.resolve().then(() => (init_rules(), rules_exports));
|
|
218665
|
+
const registry = new PraxisRegistry2({
|
|
218666
|
+
compliance: { enabled: false }
|
|
218667
|
+
});
|
|
218668
|
+
const server2 = createPraxisMcpServer2({
|
|
218669
|
+
name: options.name,
|
|
218670
|
+
version: options.version,
|
|
218671
|
+
initialContext: {},
|
|
218672
|
+
registry
|
|
218673
|
+
});
|
|
218674
|
+
await server2.start();
|
|
218675
|
+
} catch (error) {
|
|
218676
|
+
console.error("Error starting MCP server:", error);
|
|
218677
|
+
process.exit(1);
|
|
218678
|
+
}
|
|
218679
|
+
});
|
|
217813
218680
|
program.command("verify <type>").description("Verify project implementation (e.g., implementation)").option("-d, --detailed", "Show detailed analysis").action(async (type, options) => {
|
|
217814
218681
|
try {
|
|
217815
218682
|
const { verify: verify2 } = await Promise.resolve().then(() => (init_verify(), verify_exports));
|