@plures/praxis 1.2.13 → 1.3.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 (93) hide show
  1. package/README.md +44 -0
  2. package/dist/browser/chunk-MJK3IYTJ.js +384 -0
  3. package/dist/browser/{chunk-K377RW4V.js → chunk-N63K4KWS.js} +1 -1
  4. package/dist/browser/{engine-YJZV4SLD.js → engine-YIEGSX7U.js} +1 -1
  5. package/dist/browser/index.d.ts +104 -2
  6. package/dist/browser/index.js +188 -7
  7. package/dist/browser/integrations/svelte.d.ts +2 -2
  8. package/dist/browser/integrations/svelte.js +2 -2
  9. package/dist/browser/{reactive-engine.svelte-9aS0kTa8.d.ts → reactive-engine.svelte-DjynI82A.d.ts} +139 -5
  10. package/dist/node/{chunk-PRPQO6R5.js → chunk-5JQJZADT.js} +1 -1
  11. package/dist/node/chunk-KMJWAFZV.js +389 -0
  12. package/dist/node/{chunk-5RH7UAQC.js → chunk-PTH6MD6P.js} +1 -0
  13. package/dist/node/cli/index.cjs +1553 -839
  14. package/dist/node/cli/index.js +39 -2
  15. package/dist/node/cloud/index.d.cts +1 -1
  16. package/dist/node/cloud/index.d.ts +1 -1
  17. package/dist/node/components/index.d.cts +2 -2
  18. package/dist/node/components/index.d.ts +2 -2
  19. package/dist/node/conversations-KQBXTP3N.js +596 -0
  20. package/dist/node/{engine-2DQBKBJC.js → engine-FEN5IYZ5.js} +1 -1
  21. package/dist/node/index.cjs +911 -43
  22. package/dist/node/index.d.cts +574 -7
  23. package/dist/node/index.d.ts +574 -7
  24. package/dist/node/index.js +672 -26
  25. package/dist/node/integrations/svelte.cjs +190 -3
  26. package/dist/node/integrations/svelte.d.cts +3 -3
  27. package/dist/node/integrations/svelte.d.ts +3 -3
  28. package/dist/node/integrations/svelte.js +2 -2
  29. package/dist/node/{protocol-Qek7ebBl.d.ts → protocol-DcyGMmWY.d.cts} +8 -1
  30. package/dist/node/{protocol-Qek7ebBl.d.cts → protocol-DcyGMmWY.d.ts} +8 -1
  31. package/dist/node/{reactive-engine.svelte-CRNqHlbv.d.ts → reactive-engine.svelte-Cg0Yc2Hs.d.cts} +145 -6
  32. package/dist/node/{reactive-engine.svelte-BFIZfawz.d.cts → reactive-engine.svelte-DekxqFu0.d.ts} +145 -6
  33. package/dist/node/{terminal-adapter-B-UK_Vdz.d.ts → terminal-adapter-CvIvgTo4.d.ts} +1 -1
  34. package/dist/node/{terminal-adapter-BQSIF5bf.d.cts → terminal-adapter-Db-snPJ3.d.cts} +1 -1
  35. package/dist/node/{validate-CNHUULQE.js → validate-EN3M4FUR.js} +1 -1
  36. package/dist/node/{verify-KLJRXVJS.js → verify-7VZRP2WS.js} +2 -2
  37. package/docs/BOT_UPDATE_POLICY.md +125 -0
  38. package/docs/DOGFOODING_CHECKLIST.md +254 -0
  39. package/docs/DOGFOODING_INDEX.md +169 -0
  40. package/docs/DOGFOODING_QUICK_START.md +140 -0
  41. package/docs/KNO_ENG_EXTRACTION_PLAN.md +577 -0
  42. package/docs/PLURES_TOOLS_INVENTORY.md +170 -0
  43. package/docs/README.md +12 -0
  44. package/docs/TESTING_BOT_WORKFLOWS.md +154 -0
  45. package/docs/conversations/INTEGRATION_POINTS.md +719 -0
  46. package/docs/conversations/README.md +168 -0
  47. package/docs/core/extending-praxis-core.md +604 -0
  48. package/docs/core/praxis-core-api.md +385 -0
  49. package/docs/decision-ledger/contract-index.json +2 -2
  50. package/docs/decision-ledger/decisions/2026-02-01-monorepo-organization.md +130 -0
  51. package/docs/examples/DOGFOODING_WORKFLOW_EXAMPLE.md +295 -0
  52. package/docs/examples/README.md +41 -0
  53. package/docs/workflows/pr-overlap-guard.md +50 -0
  54. package/package.json +8 -3
  55. package/src/__tests__/chronicle.test.ts +512 -0
  56. package/src/__tests__/conversations.test.ts +312 -0
  57. package/src/__tests__/edge-cases.test.ts +1 -1
  58. package/src/__tests__/engine-dx.test.ts +355 -0
  59. package/src/__tests__/engine-v2.test.ts +532 -0
  60. package/src/cli/commands/conversations.ts +252 -0
  61. package/src/cli/index.ts +73 -0
  62. package/src/conversations/README.md +230 -0
  63. package/src/conversations/candidate.schema.json +123 -0
  64. package/src/conversations/candidates.ts +114 -0
  65. package/src/conversations/capture.ts +56 -0
  66. package/src/conversations/classify.ts +110 -0
  67. package/src/conversations/conversation.schema.json +106 -0
  68. package/src/conversations/emitters/fs.ts +65 -0
  69. package/src/conversations/emitters/github.ts +115 -0
  70. package/src/conversations/gate.ts +102 -0
  71. package/src/conversations/index.ts +28 -0
  72. package/src/conversations/normalize.ts +51 -0
  73. package/src/conversations/redact.ts +57 -0
  74. package/src/conversations/types.ts +96 -0
  75. package/src/core/chronicle/chronicle.ts +227 -0
  76. package/src/core/chronicle/context.ts +80 -0
  77. package/src/core/chronicle/index.ts +53 -0
  78. package/src/core/chronicle/mcp.ts +135 -0
  79. package/src/core/chronicle/types.ts +61 -0
  80. package/src/core/completeness.ts +274 -0
  81. package/src/core/engine.ts +143 -3
  82. package/src/core/pluresdb/index.ts +22 -0
  83. package/src/core/pluresdb/store.ts +171 -8
  84. package/src/core/protocol.ts +7 -0
  85. package/src/core/rule-result.ts +130 -0
  86. package/src/core/rules.ts +24 -5
  87. package/src/core/ui-rules.ts +340 -0
  88. package/src/dsl/index.ts +6 -0
  89. package/src/index.ts +45 -0
  90. package/src/integrations/pluresdb.ts +22 -0
  91. package/src/vite/completeness-plugin.ts +72 -0
  92. package/dist/browser/chunk-VOMLVI6V.js +0 -197
  93. package/dist/node/chunk-VOMLVI6V.js +0 -197
@@ -0,0 +1,340 @@
1
+ /**
2
+ * Praxis UI Rules
3
+ *
4
+ * Lightweight, predefined UI-specific rules and constraints.
5
+ * These govern UI behavior without muddying business logic rules.
6
+ *
7
+ * UI rules are separated from domain rules by convention:
8
+ * - Domain rules: business decisions, data invariants, workflow logic
9
+ * - UI rules: visibility, loading states, error display, navigation guards
10
+ *
11
+ * Ship predefined rules that apps can opt into. Every `if` in the UI
12
+ * can be governed by Praxis — business rules stay clean, UI rules stay separate.
13
+ */
14
+
15
+ import type { PraxisEvent } from './protocol.js';
16
+ import type { PraxisModule, RuleDescriptor, ConstraintDescriptor } from './rules.js';
17
+ import { RuleResult, fact } from './rule-result.js';
18
+
19
+ // ─── UI Rule Tag Prefix ─────────────────────────────────────────────────────
20
+ // All UI facts are prefixed with 'ui.' to distinguish from domain facts.
21
+
22
+ /**
23
+ * Standard UI state fields that UI rules can read from context.
24
+ * Apps extend their context with these fields to enable UI rules.
25
+ */
26
+ export interface UIContext {
27
+ /** Whether the app is currently loading data */
28
+ loading?: boolean;
29
+ /** Current error message, if any */
30
+ error?: string | null;
31
+ /** Whether the app is in offline mode */
32
+ offline?: boolean;
33
+ /** Whether there are unsaved changes */
34
+ dirty?: boolean;
35
+ /** Current route/view name */
36
+ route?: string;
37
+ /** Whether the app has completed initialization */
38
+ initialized?: boolean;
39
+ /** Screen width category: 'mobile' | 'tablet' | 'desktop' */
40
+ viewport?: 'mobile' | 'tablet' | 'desktop';
41
+ /** Whether a modal/dialog is currently open */
42
+ modalOpen?: boolean;
43
+ /** Active panel/tab name */
44
+ activePanel?: string | null;
45
+ }
46
+
47
+ // ─── Predefined UI Rules ────────────────────────────────────────────────────
48
+
49
+ /**
50
+ * Loading gate: emits ui.loading-gate when data is loading.
51
+ * UI components can subscribe to this fact to show loading indicators.
52
+ */
53
+ export const loadingGateRule: RuleDescriptor<UIContext> = {
54
+ id: 'ui/loading-gate',
55
+ description: 'Signals when the app is in a loading state',
56
+ eventTypes: ['ui.state-change', 'app.init'],
57
+ impl: (state) => {
58
+ const ctx = state.context;
59
+ if (ctx.loading) {
60
+ return RuleResult.emit([fact('ui.loading-gate', { active: true })]);
61
+ }
62
+ return RuleResult.retract(['ui.loading-gate'], 'Not loading');
63
+ },
64
+ contract: {
65
+ ruleId: 'RULE_ID_PLACEHOLDER',
66
+ behavior: 'Emits ui.loading-gate when context.loading is true, retracts when false',
67
+ examples: [
68
+ { given: 'loading is true', when: 'ui state changes', then: 'ui.loading-gate emitted' },
69
+ { given: 'loading is false', when: 'ui state changes', then: 'ui.loading-gate retracted' },
70
+ ],
71
+ invariants: ['Loading gate must reflect context.loading exactly'],
72
+ },
73
+ };
74
+
75
+ /**
76
+ * Error display: emits ui.error-display with the error message.
77
+ * Retracts when error clears.
78
+ */
79
+ export const errorDisplayRule: RuleDescriptor<UIContext> = {
80
+ id: 'ui/error-display',
81
+ description: 'Signals when an error should be displayed to the user',
82
+ eventTypes: ['ui.state-change', 'app.error'],
83
+ impl: (state) => {
84
+ const ctx = state.context;
85
+ if (ctx.error) {
86
+ return RuleResult.emit([fact('ui.error-display', { message: ctx.error, severity: 'error' })]);
87
+ }
88
+ return RuleResult.retract(['ui.error-display'], 'Error cleared');
89
+ },
90
+ contract: {
91
+ ruleId: 'RULE_ID_PLACEHOLDER',
92
+ behavior: 'Emits ui.error-display when context.error is non-null, retracts when cleared',
93
+ examples: [
94
+ { given: 'error is set', when: 'ui state changes', then: 'ui.error-display emitted with message' },
95
+ { given: 'error is null', when: 'ui state changes', then: 'ui.error-display retracted' },
96
+ ],
97
+ invariants: ['Error display must clear when error is null'],
98
+ },
99
+ };
100
+
101
+ /**
102
+ * Offline indicator: emits ui.offline-indicator when the app detects offline state.
103
+ */
104
+ export const offlineIndicatorRule: RuleDescriptor<UIContext> = {
105
+ id: 'ui/offline-indicator',
106
+ description: 'Signals when the app is offline',
107
+ eventTypes: ['ui.state-change', 'network.change'],
108
+ impl: (state) => {
109
+ if (state.context.offline) {
110
+ return RuleResult.emit([fact('ui.offline', { message: 'You are offline. Changes will sync when reconnected.' })]);
111
+ }
112
+ return RuleResult.retract(['ui.offline'], 'Back online');
113
+ },
114
+ contract: {
115
+ ruleId: 'RULE_ID_PLACEHOLDER',
116
+ behavior: 'Emits ui.offline when context.offline is true, retracts when back online',
117
+ examples: [
118
+ { given: 'offline is true', when: 'network changes', then: 'ui.offline emitted' },
119
+ { given: 'offline is false', when: 'network changes', then: 'ui.offline retracted' },
120
+ ],
121
+ invariants: ['Offline indicator must match actual connectivity'],
122
+ },
123
+ };
124
+
125
+ /**
126
+ * Dirty guard: emits ui.unsaved-warning when there are unsaved changes.
127
+ * Can be used to block navigation or show save prompts.
128
+ */
129
+ export const dirtyGuardRule: RuleDescriptor<UIContext> = {
130
+ id: 'ui/dirty-guard',
131
+ description: 'Warns when there are unsaved changes',
132
+ eventTypes: ['ui.state-change', 'navigation.request'],
133
+ impl: (state) => {
134
+ if (state.context.dirty) {
135
+ return RuleResult.emit([fact('ui.unsaved-warning', {
136
+ message: 'You have unsaved changes',
137
+ blocking: true,
138
+ })]);
139
+ }
140
+ return RuleResult.retract(['ui.unsaved-warning'], 'No unsaved changes');
141
+ },
142
+ contract: {
143
+ ruleId: 'RULE_ID_PLACEHOLDER',
144
+ behavior: 'Emits ui.unsaved-warning when context.dirty is true, retracts when saved',
145
+ examples: [
146
+ { given: 'dirty is true', when: 'ui state changes', then: 'ui.unsaved-warning emitted with blocking=true' },
147
+ { given: 'dirty is false', when: 'ui state changes', then: 'ui.unsaved-warning retracted' },
148
+ ],
149
+ invariants: ['Dirty guard must clear after save'],
150
+ },
151
+ };
152
+
153
+ /**
154
+ * Init gate: blocks UI interactions until app is initialized.
155
+ */
156
+ export const initGateRule: RuleDescriptor<UIContext> = {
157
+ id: 'ui/init-gate',
158
+ description: 'Signals whether the app has completed initialization',
159
+ eventTypes: ['ui.state-change', 'app.init'],
160
+ impl: (state) => {
161
+ if (!state.context.initialized) {
162
+ return RuleResult.emit([fact('ui.init-pending', {
163
+ message: 'App is initializing...',
164
+ })]);
165
+ }
166
+ return RuleResult.retract(['ui.init-pending'], 'App initialized');
167
+ },
168
+ contract: {
169
+ ruleId: 'RULE_ID_PLACEHOLDER',
170
+ behavior: 'Emits ui.init-pending until context.initialized is true',
171
+ examples: [
172
+ { given: 'initialized is false', when: 'app starts', then: 'ui.init-pending emitted' },
173
+ { given: 'initialized is true', when: 'init completes', then: 'ui.init-pending retracted' },
174
+ ],
175
+ invariants: ['Init gate must clear exactly once, when initialization completes'],
176
+ },
177
+ };
178
+
179
+ /**
180
+ * Viewport-responsive: emits ui.viewport-class based on screen size.
181
+ */
182
+ export const viewportRule: RuleDescriptor<UIContext> = {
183
+ id: 'ui/viewport-class',
184
+ description: 'Classifies viewport size for responsive layout decisions',
185
+ eventTypes: ['ui.state-change', 'ui.resize'],
186
+ impl: (state) => {
187
+ const vp = state.context.viewport;
188
+ if (!vp) return RuleResult.skip('No viewport data');
189
+ return RuleResult.emit([fact('ui.viewport-class', {
190
+ viewport: vp,
191
+ compact: vp === 'mobile',
192
+ showSidebar: vp !== 'mobile',
193
+ })]);
194
+ },
195
+ contract: {
196
+ ruleId: 'RULE_ID_PLACEHOLDER',
197
+ behavior: 'Classifies viewport into responsive layout hints',
198
+ examples: [
199
+ { given: 'viewport is mobile', when: 'resize event', then: 'compact=true, showSidebar=false' },
200
+ { given: 'viewport is desktop', when: 'resize event', then: 'compact=false, showSidebar=true' },
201
+ ],
202
+ invariants: ['Viewport class must update on every resize event'],
203
+ },
204
+ };
205
+
206
+ // ─── UI Constraints ─────────────────────────────────────────────────────────
207
+
208
+ /**
209
+ * No interaction while loading: constraint that fails if actions are taken during loading.
210
+ */
211
+ export const noInteractionWhileLoadingConstraint: ConstraintDescriptor<UIContext> = {
212
+ id: 'ui/no-interaction-while-loading',
213
+ description: 'Prevents data mutations while a load operation is in progress',
214
+ impl: (state) => {
215
+ // This constraint is advisory — apps can use checkConstraints() before mutations
216
+ if (state.context.loading) {
217
+ return 'Cannot perform action while data is loading';
218
+ }
219
+ return true;
220
+ },
221
+ contract: {
222
+ ruleId: 'RULE_ID_PLACEHOLDER',
223
+ behavior: 'Fails when context.loading is true',
224
+ examples: [
225
+ { given: 'loading is true', when: 'action attempted', then: 'violation' },
226
+ { given: 'loading is false', when: 'action attempted', then: 'pass' },
227
+ ],
228
+ invariants: ['Must always fail during loading'],
229
+ },
230
+ };
231
+
232
+ /**
233
+ * Must be initialized: constraint that fails if app hasn't completed init.
234
+ */
235
+ export const mustBeInitializedConstraint: ConstraintDescriptor<UIContext> = {
236
+ id: 'ui/must-be-initialized',
237
+ description: 'Requires app initialization before user interactions',
238
+ impl: (state) => {
239
+ if (!state.context.initialized) {
240
+ return 'App must be initialized before performing this action';
241
+ }
242
+ return true;
243
+ },
244
+ contract: {
245
+ ruleId: 'RULE_ID_PLACEHOLDER',
246
+ behavior: 'Fails when context.initialized is false',
247
+ examples: [
248
+ { given: 'initialized is false', when: 'action attempted', then: 'violation' },
249
+ { given: 'initialized is true', when: 'action attempted', then: 'pass' },
250
+ ],
251
+ invariants: ['Must always fail before init completes'],
252
+ },
253
+ };
254
+
255
+ // ─── Module Bundle ──────────────────────────────────────────────────────────
256
+
257
+ /**
258
+ * The complete UI rules module.
259
+ * Register this to get all predefined UI rules and constraints.
260
+ *
261
+ * @example
262
+ * import { uiModule } from '@plures/praxis';
263
+ * registry.registerModule(uiModule);
264
+ */
265
+ export const uiModule: PraxisModule<UIContext> = {
266
+ rules: [
267
+ loadingGateRule,
268
+ errorDisplayRule,
269
+ offlineIndicatorRule,
270
+ dirtyGuardRule,
271
+ initGateRule,
272
+ viewportRule,
273
+ ],
274
+ constraints: [
275
+ noInteractionWhileLoadingConstraint,
276
+ mustBeInitializedConstraint,
277
+ ],
278
+ meta: {
279
+ name: 'praxis-ui',
280
+ version: '1.0.0',
281
+ description: 'Predefined UI rules and constraints — separate from business logic',
282
+ },
283
+ };
284
+
285
+ /**
286
+ * Create a customized UI module with only the rules you need.
287
+ *
288
+ * @example
289
+ * const myUI = createUIModule({
290
+ * rules: ['ui/loading-gate', 'ui/dirty-guard'],
291
+ * constraints: ['ui/must-be-initialized'],
292
+ * });
293
+ * registry.registerModule(myUI);
294
+ */
295
+ export function createUIModule<TContext extends UIContext>(options: {
296
+ rules?: string[];
297
+ constraints?: string[];
298
+ extraRules?: RuleDescriptor<TContext>[];
299
+ extraConstraints?: ConstraintDescriptor<TContext>[];
300
+ }): PraxisModule<TContext> {
301
+ const allRules = uiModule.rules as RuleDescriptor<TContext>[];
302
+ const allConstraints = uiModule.constraints as ConstraintDescriptor<TContext>[];
303
+
304
+ const selectedRules = options.rules
305
+ ? allRules.filter(r => options.rules!.includes(r.id))
306
+ : allRules;
307
+
308
+ const selectedConstraints = options.constraints
309
+ ? allConstraints.filter(c => options.constraints!.includes(c.id))
310
+ : allConstraints;
311
+
312
+ return {
313
+ rules: [...selectedRules, ...(options.extraRules ?? [])],
314
+ constraints: [...selectedConstraints, ...(options.extraConstraints ?? [])],
315
+ meta: { ...uiModule.meta, customized: true },
316
+ };
317
+ }
318
+
319
+ // ─── UI Event Helpers ───────────────────────────────────────────────────────
320
+
321
+ /**
322
+ * Create a UI state change event. Fire this when UIContext fields change.
323
+ */
324
+ export function uiStateChanged(changes?: Record<string, unknown>): PraxisEvent {
325
+ return { tag: 'ui.state-change', payload: changes ?? {} };
326
+ }
327
+
328
+ /**
329
+ * Create a navigation request event. Used with dirty guard.
330
+ */
331
+ export function navigationRequest(to: string): PraxisEvent {
332
+ return { tag: 'navigation.request', payload: { to } };
333
+ }
334
+
335
+ /**
336
+ * Create a resize event. Used with viewport rule.
337
+ */
338
+ export function resizeEvent(width: number, height: number): PraxisEvent {
339
+ return { tag: 'ui.resize', payload: { width, height } };
340
+ }
package/src/dsl/index.ts CHANGED
@@ -85,6 +85,11 @@ export interface DefineRuleOptions<TContext = unknown> {
85
85
  id: string;
86
86
  description: string;
87
87
  impl: RuleFn<TContext>;
88
+ /**
89
+ * Optional event type filter — only evaluate this rule when at least one
90
+ * event in the batch has a matching `tag`. Accepts a single tag or array.
91
+ */
92
+ eventTypes?: string | string[];
88
93
  contract?: Contract;
89
94
  meta?: Record<string, unknown>;
90
95
  }
@@ -115,6 +120,7 @@ export function defineRule<TContext = unknown>(
115
120
  id: options.id,
116
121
  description: options.description,
117
122
  impl: options.impl,
123
+ eventTypes: options.eventTypes,
118
124
  contract,
119
125
  meta,
120
126
  };
package/src/index.ts CHANGED
@@ -208,6 +208,18 @@ export type {
208
208
  GeneratedPluresDBFile,
209
209
  PluresDBAdapter,
210
210
  PluresDBAdapterOptions,
211
+ // Chronicle
212
+ TraceDirection,
213
+ EdgeType,
214
+ ChronicleEvent,
215
+ ChronicleNode,
216
+ ChronicleEdge,
217
+ Chronicle,
218
+ ChronicleSpan,
219
+ ChronosTraceParams,
220
+ ChronosSearchParams,
221
+ McpToolResult,
222
+ ChronosMcpTools,
211
223
  } from './integrations/pluresdb.js';
212
224
  export {
213
225
  InMemoryPraxisDB,
@@ -229,6 +241,12 @@ export {
229
241
  createPluresDBGenerator,
230
242
  createPluresDBAdapter,
231
243
  attachToEngine,
244
+ // Chronicle
245
+ ChronicleContext,
246
+ PluresDbChronicle,
247
+ createChronicle,
248
+ CHRONICLE_PATHS,
249
+ createChronosMcpTools,
232
250
  } from './integrations/pluresdb.js';
233
251
 
234
252
  // Unum Integration (Identity & Channels)
@@ -308,3 +326,30 @@ export {
308
326
  // Unified Integration Helpers
309
327
  export type { UnifiedAppConfig, UnifiedApp } from './integrations/unified.js';
310
328
  export { createUnifiedApp, attachAllIntegrations } from './integrations/unified.js';
329
+
330
+ // ── Rule Result (typed rule returns — no empty arrays) ──────────────────────
331
+ export { RuleResult, fact } from './core/rule-result.js';
332
+ export type { TypedRuleFn } from './core/rule-result.js';
333
+
334
+ // ── UI Rules (predefined, lightweight, separate from business logic) ────────
335
+ export {
336
+ uiModule,
337
+ createUIModule,
338
+ loadingGateRule,
339
+ errorDisplayRule,
340
+ offlineIndicatorRule,
341
+ dirtyGuardRule,
342
+ initGateRule,
343
+ viewportRule,
344
+ noInteractionWhileLoadingConstraint,
345
+ mustBeInitializedConstraint,
346
+ uiStateChanged,
347
+ navigationRequest,
348
+ resizeEvent,
349
+ } from './core/ui-rules.js';
350
+ export type { UIContext } from './core/ui-rules.js';
351
+
352
+ // ── Completeness Analysis ───────────────────────────────────────────────────
353
+ export { auditCompleteness, formatReport } from './core/completeness.js';
354
+ export type { LogicBranch, StateField, StateTransition, CompletenessReport, CompletenessConfig } from './core/completeness.js';
355
+
@@ -57,6 +57,28 @@ export type {
57
57
  GeneratedPluresDBFile,
58
58
  } from '../core/pluresdb/generator.js';
59
59
 
60
+ // Chronicle - Causal graph tracking for Praxis state transitions
61
+ export type {
62
+ TraceDirection,
63
+ EdgeType,
64
+ ChronicleEvent,
65
+ ChronicleNode,
66
+ ChronicleEdge,
67
+ Chronicle,
68
+ ChronicleSpan,
69
+ ChronosTraceParams,
70
+ ChronosSearchParams,
71
+ McpToolResult,
72
+ ChronosMcpTools,
73
+ } from '../core/chronicle/index.js';
74
+ export {
75
+ ChronicleContext,
76
+ PluresDbChronicle,
77
+ createChronicle,
78
+ CHRONICLE_PATHS,
79
+ createChronosMcpTools,
80
+ } from '../core/chronicle/index.js';
81
+
60
82
  /**
61
83
  * PluresDB adapter interface for engine integration
62
84
  *
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Vite Plugin: Praxis Completeness Report
3
+ *
4
+ * Automatically outputs completeness score in build output.
5
+ * Silent by default in production builds unless `verbose: true`.
6
+ * Always shows in dev mode unless explicitly silenced.
7
+ *
8
+ * Usage in vite.config.ts:
9
+ * ```ts
10
+ * import { praxisCompletenessPlugin } from '@plures/praxis/vite';
11
+ *
12
+ * export default defineConfig({
13
+ * plugins: [
14
+ * praxisCompletenessPlugin({
15
+ * manifestPath: './src/lib/completeness-manifest.ts',
16
+ * threshold: 90, // fail build if below (CI mode)
17
+ * strict: false, // set to true for CI
18
+ * silent: false, // set to true to suppress output
19
+ * }),
20
+ * ],
21
+ * });
22
+ * ```
23
+ */
24
+
25
+ export interface PraxisCompletenessPluginOptions {
26
+ /**
27
+ * Path to the completeness manifest module.
28
+ * Must default-export or named-export { branches, stateFields, transitions, rulesNeedingContracts }.
29
+ */
30
+ manifestPath?: string;
31
+ /**
32
+ * Minimum score to pass (default: 90). Below this is a warning; in strict mode, an error.
33
+ */
34
+ threshold?: number;
35
+ /**
36
+ * If true, build fails when below threshold. Use for CI.
37
+ */
38
+ strict?: boolean;
39
+ /**
40
+ * If true, suppresses all completeness output.
41
+ */
42
+ silent?: boolean;
43
+ }
44
+
45
+ /**
46
+ * Creates a Vite plugin that reports Praxis completeness in build output.
47
+ *
48
+ * The plugin loads the manifest at build time, evaluates coverage, and
49
+ * prints a summary. In strict mode, it fails the build if below threshold.
50
+ */
51
+ export function praxisCompletenessPlugin(_options?: PraxisCompletenessPluginOptions) {
52
+ const options = _options ?? {};
53
+ const threshold = options.threshold ?? 90;
54
+ const silent = options.silent ?? false;
55
+
56
+ return {
57
+ name: 'vite-plugin-praxis-completeness',
58
+ enforce: 'post' as const,
59
+
60
+ // We hook into buildEnd so the report appears at the end of the build
61
+ buildEnd() {
62
+ if (silent) return;
63
+
64
+ // This plugin works as a hook point — the actual manifest loading
65
+ // happens at the app level since manifests are app-specific.
66
+ // The plugin provides the infrastructure; apps wire their manifest.
67
+ console.log(`\n⟐ Praxis Completeness: threshold=${threshold}, strict=${options.strict ?? false}`);
68
+ console.log(' Import your manifest and call auditCompleteness() in a build script or test.');
69
+ console.log(' See @plures/praxis docs for setup.\n');
70
+ },
71
+ };
72
+ }