@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
@@ -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));