omegon 0.6.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 (160) hide show
  1. package/.gitattributes +3 -0
  2. package/AGENTS.md +16 -0
  3. package/LICENSE +15 -0
  4. package/README.md +289 -0
  5. package/bin/pi.mjs +30 -0
  6. package/extensions/00-secrets/index.ts +1126 -0
  7. package/extensions/01-auth/auth.ts +401 -0
  8. package/extensions/01-auth/index.ts +289 -0
  9. package/extensions/auto-compact.ts +42 -0
  10. package/extensions/bootstrap/deps.ts +291 -0
  11. package/extensions/bootstrap/index.ts +811 -0
  12. package/extensions/chronos/chronos.sh +487 -0
  13. package/extensions/chronos/index.ts +148 -0
  14. package/extensions/cleave/assessment.ts +754 -0
  15. package/extensions/cleave/bridge.ts +31 -0
  16. package/extensions/cleave/conflicts.ts +250 -0
  17. package/extensions/cleave/dispatcher.ts +808 -0
  18. package/extensions/cleave/guardrails.ts +426 -0
  19. package/extensions/cleave/index.ts +3121 -0
  20. package/extensions/cleave/lifecycle-emitter.ts +20 -0
  21. package/extensions/cleave/openspec.ts +811 -0
  22. package/extensions/cleave/planner.ts +260 -0
  23. package/extensions/cleave/review.ts +579 -0
  24. package/extensions/cleave/skills.ts +355 -0
  25. package/extensions/cleave/types.ts +261 -0
  26. package/extensions/cleave/workspace.ts +861 -0
  27. package/extensions/cleave/worktree.ts +243 -0
  28. package/extensions/core-renderers.ts +253 -0
  29. package/extensions/dashboard/context-gauge.ts +58 -0
  30. package/extensions/dashboard/file-watch.ts +14 -0
  31. package/extensions/dashboard/footer.ts +1145 -0
  32. package/extensions/dashboard/git.ts +185 -0
  33. package/extensions/dashboard/index.ts +478 -0
  34. package/extensions/dashboard/memory-audit.ts +34 -0
  35. package/extensions/dashboard/overlay-data.ts +705 -0
  36. package/extensions/dashboard/overlay.ts +365 -0
  37. package/extensions/dashboard/render-utils.ts +54 -0
  38. package/extensions/dashboard/types.ts +191 -0
  39. package/extensions/dashboard/uri-helper.ts +45 -0
  40. package/extensions/debug.ts +69 -0
  41. package/extensions/defaults.ts +282 -0
  42. package/extensions/design-tree/dashboard-state.ts +161 -0
  43. package/extensions/design-tree/design-card.ts +362 -0
  44. package/extensions/design-tree/index.ts +2130 -0
  45. package/extensions/design-tree/lifecycle-emitter.ts +41 -0
  46. package/extensions/design-tree/tree.ts +1607 -0
  47. package/extensions/design-tree/types.ts +163 -0
  48. package/extensions/distill.ts +127 -0
  49. package/extensions/effort/index.ts +395 -0
  50. package/extensions/effort/tiers.ts +146 -0
  51. package/extensions/effort/types.ts +105 -0
  52. package/extensions/lib/git-state.ts +227 -0
  53. package/extensions/lib/local-models.ts +157 -0
  54. package/extensions/lib/model-preferences.ts +51 -0
  55. package/extensions/lib/model-routing.ts +720 -0
  56. package/extensions/lib/operator-fallback.ts +205 -0
  57. package/extensions/lib/operator-profile.ts +360 -0
  58. package/extensions/lib/slash-command-bridge.ts +253 -0
  59. package/extensions/lib/typebox-helpers.ts +16 -0
  60. package/extensions/local-inference/index.ts +727 -0
  61. package/extensions/mcp-bridge/README.md +220 -0
  62. package/extensions/mcp-bridge/index.ts +951 -0
  63. package/extensions/mcp-bridge/lib.ts +365 -0
  64. package/extensions/mcp-bridge/mcp.json +3 -0
  65. package/extensions/mcp-bridge/package.json +11 -0
  66. package/extensions/model-budget.ts +752 -0
  67. package/extensions/offline-driver.ts +403 -0
  68. package/extensions/openspec/archive-gate.ts +164 -0
  69. package/extensions/openspec/branch-cleanup.ts +64 -0
  70. package/extensions/openspec/dashboard-state.ts +50 -0
  71. package/extensions/openspec/index.ts +1917 -0
  72. package/extensions/openspec/lifecycle-emitter.ts +65 -0
  73. package/extensions/openspec/lifecycle-files.ts +70 -0
  74. package/extensions/openspec/lifecycle.ts +50 -0
  75. package/extensions/openspec/reconcile.ts +187 -0
  76. package/extensions/openspec/spec.ts +1385 -0
  77. package/extensions/openspec/types.ts +98 -0
  78. package/extensions/project-memory/DESIGN-global-mind.md +198 -0
  79. package/extensions/project-memory/README.md +202 -0
  80. package/extensions/project-memory/api-types.ts +382 -0
  81. package/extensions/project-memory/compaction-policy.ts +29 -0
  82. package/extensions/project-memory/core.ts +164 -0
  83. package/extensions/project-memory/embeddings.ts +230 -0
  84. package/extensions/project-memory/extraction-v2.ts +861 -0
  85. package/extensions/project-memory/factstore.ts +2177 -0
  86. package/extensions/project-memory/index.ts +3459 -0
  87. package/extensions/project-memory/injection-metrics.ts +91 -0
  88. package/extensions/project-memory/jsonl-io.ts +12 -0
  89. package/extensions/project-memory/lifecycle.ts +331 -0
  90. package/extensions/project-memory/migration.ts +293 -0
  91. package/extensions/project-memory/package.json +9 -0
  92. package/extensions/project-memory/sci-renderers.ts +7 -0
  93. package/extensions/project-memory/template.ts +103 -0
  94. package/extensions/project-memory/triggers.ts +52 -0
  95. package/extensions/project-memory/types.ts +102 -0
  96. package/extensions/render/composition/fonts/Inter-Bold.ttf +0 -0
  97. package/extensions/render/composition/fonts/Inter-Regular.ttf +0 -0
  98. package/extensions/render/composition/fonts/Tomorrow-Bold.ttf +0 -0
  99. package/extensions/render/composition/fonts/Tomorrow-Regular.ttf +0 -0
  100. package/extensions/render/composition/package-lock.json +534 -0
  101. package/extensions/render/composition/package.json +22 -0
  102. package/extensions/render/composition/render.mjs +246 -0
  103. package/extensions/render/composition/test-comp.tsx +87 -0
  104. package/extensions/render/composition/types.ts +24 -0
  105. package/extensions/render/excalidraw/UPSTREAM.md +81 -0
  106. package/extensions/render/excalidraw/elements.ts +764 -0
  107. package/extensions/render/excalidraw/index.ts +66 -0
  108. package/extensions/render/excalidraw/types.ts +223 -0
  109. package/extensions/render/excalidraw-renderer/pyproject.toml +8 -0
  110. package/extensions/render/excalidraw-renderer/render_excalidraw.py +182 -0
  111. package/extensions/render/excalidraw-renderer/render_template.html +59 -0
  112. package/extensions/render/index.ts +830 -0
  113. package/extensions/render/native-diagrams/index.ts +57 -0
  114. package/extensions/render/native-diagrams/motifs.ts +542 -0
  115. package/extensions/render/native-diagrams/raster.ts +8 -0
  116. package/extensions/render/native-diagrams/scene.ts +75 -0
  117. package/extensions/render/native-diagrams/spec.ts +204 -0
  118. package/extensions/render/native-diagrams/svg.ts +116 -0
  119. package/extensions/sci-ui.ts +304 -0
  120. package/extensions/session-log.ts +174 -0
  121. package/extensions/shared-state.ts +146 -0
  122. package/extensions/spinner-verbs.ts +91 -0
  123. package/extensions/style.ts +281 -0
  124. package/extensions/terminal-title.ts +191 -0
  125. package/extensions/tool-profile/index.ts +291 -0
  126. package/extensions/tool-profile/profiles.ts +290 -0
  127. package/extensions/types.d.ts +9 -0
  128. package/extensions/vault/index.ts +185 -0
  129. package/extensions/version-check.ts +90 -0
  130. package/extensions/view/index.ts +859 -0
  131. package/extensions/view/uri-resolver.ts +148 -0
  132. package/extensions/web-search/index.ts +182 -0
  133. package/extensions/web-search/providers.ts +121 -0
  134. package/extensions/web-ui/index.ts +110 -0
  135. package/extensions/web-ui/server.ts +265 -0
  136. package/extensions/web-ui/state.ts +462 -0
  137. package/extensions/web-ui/static/index.html +145 -0
  138. package/extensions/web-ui/types.ts +284 -0
  139. package/package.json +76 -0
  140. package/prompts/init.md +75 -0
  141. package/prompts/new-repo.md +54 -0
  142. package/prompts/oci-login.md +56 -0
  143. package/prompts/status.md +50 -0
  144. package/settings.json +4 -0
  145. package/skills/cleave/SKILL.md +218 -0
  146. package/skills/git/SKILL.md +209 -0
  147. package/skills/git/_reference/ci-validation.md +204 -0
  148. package/skills/oci/SKILL.md +338 -0
  149. package/skills/openspec/SKILL.md +346 -0
  150. package/skills/pi-extensions/SKILL.md +191 -0
  151. package/skills/pi-tui/SKILL.md +517 -0
  152. package/skills/python/SKILL.md +189 -0
  153. package/skills/rust/SKILL.md +268 -0
  154. package/skills/security/SKILL.md +206 -0
  155. package/skills/style/SKILL.md +264 -0
  156. package/skills/typescript/SKILL.md +225 -0
  157. package/skills/vault/SKILL.md +102 -0
  158. package/themes/alpharius-legacy.json +85 -0
  159. package/themes/alpharius.conf +59 -0
  160. package/themes/alpharius.json +88 -0
@@ -0,0 +1,246 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * render.mjs — CLI entry point for the composition render pipeline.
4
+ * Browser-free React → still/GIF/MP4 using Satori + resvg + gifenc.
5
+ *
6
+ * Usage:
7
+ * node render.mjs --composition <path> --mode still|gif|mp4
8
+ * [--frame <n>] [--width <px>] [--height <px>] [--fps <n>]
9
+ * [--duration <frames>] [--output <path>] [--props <json>]
10
+ *
11
+ * All output is written to stdout as JSON: {ok:true,...} or {ok:false,error}.
12
+ */
13
+
14
+ import { readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
15
+ import { resolve, dirname, extname, basename } from 'node:path';
16
+ import { tmpdir } from 'node:os';
17
+ import { spawnSync } from 'node:child_process';
18
+ import { fileURLToPath } from 'node:url';
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Arg parsing
22
+ // ---------------------------------------------------------------------------
23
+
24
+ function parseArgs(argv) {
25
+ const args = {};
26
+ for (let i = 0; i < argv.length; i++) {
27
+ const arg = argv[i];
28
+ if (arg.startsWith('--')) {
29
+ const key = arg.slice(2);
30
+ const val = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[++i] : 'true';
31
+ args[key] = val;
32
+ }
33
+ }
34
+ return args;
35
+ }
36
+
37
+ const args = parseArgs(process.argv.slice(2));
38
+
39
+ const compositionPath = args.composition;
40
+ const mode = (args.mode || 'still').toLowerCase();
41
+ const startFrame = parseInt(args.frame ?? '0', 10);
42
+ const width = parseInt(args.width ?? '1920', 10);
43
+ const height = parseInt(args.height ?? '1080', 10);
44
+ const fps = parseInt(args.fps ?? '30', 10);
45
+ const duration = parseInt(args.duration ?? '1', 10); // frames
46
+ const outputPath = args.output ?? './out.png';
47
+ let userProps = {};
48
+
49
+ try {
50
+ if (args.props) userProps = JSON.parse(args.props);
51
+ } catch {
52
+ fail('--props must be valid JSON');
53
+ }
54
+
55
+ // ---------------------------------------------------------------------------
56
+ // Helpers
57
+ // ---------------------------------------------------------------------------
58
+
59
+ function ok(extra) {
60
+ console.log(JSON.stringify({ ok: true, ...extra }));
61
+ process.exit(0);
62
+ }
63
+
64
+ function fail(error) {
65
+ console.log(JSON.stringify({ ok: false, error: String(error) }));
66
+ process.exit(1);
67
+ }
68
+
69
+ if (!compositionPath) fail('--composition is required');
70
+
71
+ // ---------------------------------------------------------------------------
72
+ // Font loading
73
+ // ---------------------------------------------------------------------------
74
+
75
+ const __filename = fileURLToPath(import.meta.url);
76
+ const __dirname = dirname(__filename);
77
+ const fontsDir = resolve(__dirname, 'fonts');
78
+
79
+ function loadFonts() {
80
+ const fonts = [];
81
+ let entries;
82
+ try {
83
+ entries = readdirSync(fontsDir);
84
+ } catch {
85
+ return fonts; // no fonts dir — satori will use system defaults
86
+ }
87
+
88
+ for (const file of entries) {
89
+ if (extname(file).toLowerCase() !== '.ttf') continue;
90
+ const namePart = basename(file, '.ttf'); // e.g. "Inter-Bold"
91
+ const [family, weightStr] = namePart.split('-');
92
+ const weightMap = {
93
+ thin: 100, extralight: 200, light: 300,
94
+ regular: 400, medium: 500, semibold: 600,
95
+ bold: 700, extrabold: 800, black: 900,
96
+ };
97
+ const weight = weightMap[(weightStr ?? 'regular').toLowerCase()] ?? 400;
98
+ const style = namePart.toLowerCase().includes('italic') ? 'italic' : 'normal';
99
+ try {
100
+ const data = readFileSync(resolve(fontsDir, file));
101
+ fonts.push({ name: family, data: data.buffer, weight, style });
102
+ } catch {
103
+ // skip unreadable fonts
104
+ }
105
+ }
106
+ return fonts;
107
+ }
108
+
109
+ // ---------------------------------------------------------------------------
110
+ // Import user composition via jiti (handles .tsx / .ts / .js)
111
+ // ---------------------------------------------------------------------------
112
+
113
+ async function loadComposition(path) {
114
+ const { createJiti } = await import('jiti');
115
+ const jiti = createJiti(import.meta.url, { jsx: true });
116
+ const absPath = resolve(process.cwd(), path);
117
+ const mod = await jiti.import(absPath);
118
+ // Support both default export and named Component export
119
+ const Component = mod?.default ?? mod?.Component;
120
+ if (typeof Component !== 'function') {
121
+ throw new Error(`Composition at "${path}" must have a default export that is a React component function`);
122
+ }
123
+ return Component;
124
+ }
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // Render a single frame → raw RGBA Uint8Array
128
+ // ---------------------------------------------------------------------------
129
+
130
+ async function renderFrame(Component, frameIndex, fonts, React, satori, Resvg) {
131
+ const frameProps = {
132
+ frame: frameIndex,
133
+ fps,
134
+ durationInFrames: duration,
135
+ width,
136
+ height,
137
+ props: userProps,
138
+ };
139
+
140
+ // Build the React element (no JSX — render.mjs is plain .mjs)
141
+ const element = React.createElement(Component, frameProps);
142
+
143
+ const svg = await satori(element, { width, height, fonts });
144
+
145
+ const resvg = new Resvg(svg, {
146
+ fitTo: { mode: 'width', value: width },
147
+ });
148
+ const rendered = resvg.render();
149
+ return rendered; // ResvgRenderImage with .asPng() and .pixels (RGBA Uint8Array)
150
+ }
151
+
152
+ // ---------------------------------------------------------------------------
153
+ // Main
154
+ // ---------------------------------------------------------------------------
155
+
156
+ async function main() {
157
+ const fonts = loadFonts();
158
+ const Component = await loadComposition(compositionPath);
159
+
160
+ // Lazy-import heavy deps after validation
161
+ const React = (await import('react')).default;
162
+ const { default: satori } = await import('satori');
163
+ const { Resvg } = await import('@resvg/resvg-js');
164
+
165
+ // -------------------------------------------------------------------------
166
+ // STILL mode
167
+ // -------------------------------------------------------------------------
168
+ if (mode === 'still') {
169
+ const rendered = await renderFrame(Component, startFrame, fonts, React, satori, Resvg);
170
+ const png = rendered.asPng();
171
+ const outPath = resolve(process.cwd(), outputPath);
172
+ writeFileSync(outPath, png);
173
+ ok({ path: outPath, sizeBytes: png.byteLength });
174
+ }
175
+
176
+ // -------------------------------------------------------------------------
177
+ // GIF mode
178
+ // -------------------------------------------------------------------------
179
+ else if (mode === 'gif') {
180
+ const gifencMod = await import('gifenc');
181
+ const gifencExports = gifencMod.default ?? gifencMod['module.exports'] ?? gifencMod;
182
+ const { GIFEncoder, quantize, applyPalette } = gifencExports;
183
+
184
+ const gif = GIFEncoder();
185
+ const delayMs = Math.round(1000 / fps);
186
+
187
+ for (let f = 0; f < duration; f++) {
188
+ const rendered = await renderFrame(Component, f, fonts, React, satori, Resvg);
189
+ // pixels is a Uint8Array of RGBA bytes, width×height×4
190
+ const pixels = new Uint8Array(rendered.pixels);
191
+ const palette = quantize(pixels, 256, { format: 'rgba4444' });
192
+ const index = applyPalette(pixels, palette, 'rgba4444');
193
+ gif.writeFrame(index, width, height, { palette, delay: delayMs });
194
+ }
195
+
196
+ gif.finish();
197
+ const data = gif.bytesView();
198
+ const outPath = resolve(process.cwd(), outputPath);
199
+ writeFileSync(outPath, data);
200
+ ok({ path: outPath, sizeBytes: data.byteLength });
201
+ }
202
+
203
+ // -------------------------------------------------------------------------
204
+ // MP4 mode
205
+ // -------------------------------------------------------------------------
206
+ else if (mode === 'mp4') {
207
+ const tmp = resolve(tmpdir(), `pi-render-${Date.now()}`);
208
+ mkdirSync(tmp, { recursive: true });
209
+
210
+ const padLen = String(duration - 1).length;
211
+
212
+ for (let f = 0; f < duration; f++) {
213
+ const rendered = await renderFrame(Component, f, fonts, React, satori, Resvg);
214
+ const png = rendered.asPng();
215
+ const frameName = String(f).padStart(padLen, '0') + '.png';
216
+ writeFileSync(resolve(tmp, frameName), png);
217
+ }
218
+
219
+ const outPath = resolve(process.cwd(), outputPath);
220
+ const result = spawnSync('ffmpeg', [
221
+ '-y',
222
+ '-framerate', String(fps),
223
+ '-i', resolve(tmp, `%0${padLen}d.png`),
224
+ '-c:v', 'libx264',
225
+ '-pix_fmt', 'yuv420p',
226
+ '-movflags', '+faststart',
227
+ outPath,
228
+ ], { stdio: 'pipe' });
229
+
230
+ if (result.status !== 0) {
231
+ const stderr = result.stderr?.toString() ?? 'unknown ffmpeg error';
232
+ fail(`ffmpeg failed: ${stderr}`);
233
+ }
234
+
235
+ ok({ path: outPath, sizeBytes: readFileSync(outPath).byteLength });
236
+ }
237
+
238
+ // -------------------------------------------------------------------------
239
+ // Unknown mode
240
+ // -------------------------------------------------------------------------
241
+ else {
242
+ fail(`Unknown --mode "${mode}". Valid values: still, gif, mp4`);
243
+ }
244
+ }
245
+
246
+ main().catch((err) => fail(err?.message ?? String(err)));
@@ -0,0 +1,87 @@
1
+ /**
2
+ * test-comp.tsx — minimal smoke-test composition.
3
+ *
4
+ * Renders a progress bar animating from 0% → 100% over durationInFrames.
5
+ * Uses Verdant palette: bg #0a0a0a, teal #7ec8c8, text #e0e0e0.
6
+ * Props-based (no Remotion hooks).
7
+ */
8
+
9
+ import React from 'react';
10
+ import type { FrameProps } from './types.js';
11
+
12
+ export default function TestComp({
13
+ frame,
14
+ durationInFrames,
15
+ width,
16
+ height,
17
+ }: FrameProps): React.ReactElement {
18
+ const progress = durationInFrames > 1 ? frame / (durationInFrames - 1) : 1;
19
+ const pct = Math.round(progress * 100);
20
+ const barWidth = Math.round(progress * (width - 80));
21
+
22
+ return (
23
+ <div
24
+ style={{
25
+ display: 'flex',
26
+ flexDirection: 'column',
27
+ alignItems: 'center',
28
+ justifyContent: 'center',
29
+ width,
30
+ height,
31
+ background: '#0a0a0a',
32
+ fontFamily: 'Inter, sans-serif',
33
+ color: '#e0e0e0',
34
+ }}
35
+ >
36
+ {/* Title */}
37
+ <div
38
+ style={{
39
+ display: 'flex',
40
+ fontSize: 32,
41
+ fontWeight: 700,
42
+ marginBottom: 40,
43
+ color: '#7ec8c8',
44
+ letterSpacing: 2,
45
+ }}
46
+ >
47
+ pi-kit render pipeline
48
+ </div>
49
+
50
+ {/* Bar track */}
51
+ <div
52
+ style={{
53
+ display: 'flex',
54
+ width: width - 80,
55
+ height: 24,
56
+ background: '#1a1a1a',
57
+ borderRadius: 12,
58
+ overflow: 'hidden',
59
+ }}
60
+ >
61
+ {/* Bar fill */}
62
+ <div
63
+ style={{
64
+ display: 'flex',
65
+ width: barWidth,
66
+ height: 24,
67
+ background: '#7ec8c8',
68
+ borderRadius: 12,
69
+ }}
70
+ />
71
+ </div>
72
+
73
+ {/* Percentage label */}
74
+ <div
75
+ style={{
76
+ display: 'flex',
77
+ marginTop: 20,
78
+ fontSize: 24,
79
+ color: '#e0e0e0',
80
+ fontVariantNumeric: 'tabular-nums',
81
+ }}
82
+ >
83
+ {pct}%
84
+ </div>
85
+ </div>
86
+ );
87
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Core types for the composition render pipeline.
3
+ * Browser-free React → video/image renderer using Satori + resvg + gifenc.
4
+ */
5
+
6
+ /**
7
+ * Props injected into every frame component.
8
+ * Mirrors the Remotion useCurrentFrame() / useVideoConfig() pattern
9
+ * but passed directly as props instead of via hooks.
10
+ */
11
+ export interface FrameProps {
12
+ /** Current frame index (0-based) */
13
+ frame: number;
14
+ /** Frames per second */
15
+ fps: number;
16
+ /** Total number of frames in the composition */
17
+ durationInFrames: number;
18
+ /** Canvas width in pixels */
19
+ width: number;
20
+ /** Canvas height in pixels */
21
+ height: number;
22
+ /** User-defined props passed through from the render tool */
23
+ props?: Record<string, unknown>;
24
+ }
@@ -0,0 +1,81 @@
1
+ # Excalidraw Factory Layer — Upstream Tracking
2
+
3
+ ## Source
4
+
5
+ Vendored from [`@swiftlysingh/excalidraw-cli`](https://github.com/swiftlysingh/excalidraw-cli)
6
+ - **Version at vendor time:** 1.1.0
7
+ - **Commit:** `e1338e8342dfa740119aac5e83aab71673c70c68` (main, 2026-03-03)
8
+ - **License:** MIT
9
+ - **npm:** `@swiftlysingh/excalidraw-cli@1.1.0`
10
+
11
+ ## What We Vendored
12
+
13
+ | Upstream File | Local File | Changes |
14
+ |---|---|---|
15
+ | `src/types/excalidraw.ts` | `types.ts` | Minimal — removed unused `ExcalidrawImage`, `ExcalidrawFileData`; added our `SemanticPurpose` and palette types |
16
+ | `src/factory/element-factory.ts` | `elements.ts` | Merged node-factory, connection-factory, text-factory into single module; replaced `nanoid` with built-in `crypto.randomUUID`; added semantic palette integration |
17
+ | `src/factory/node-factory.ts` | (merged into `elements.ts`) | — |
18
+ | `src/factory/connection-factory.ts` | (merged into `elements.ts`) | — |
19
+ | `src/factory/text-factory.ts` | (merged into `elements.ts`) | — |
20
+
21
+ ### Files NOT vendored (unnecessary for our use case)
22
+
23
+ - `src/cli.ts` — CLI entry point
24
+ - `src/parser/dsl-parser.ts` — DSL parser (`(Start) -> [Process]` syntax)
25
+ - `src/parser/dot-parser.ts` — Graphviz DOT parser
26
+ - `src/parser/json-parser.ts` — JSON input parser
27
+ - `src/layout/elk-layout.ts` — ELK.js auto-layout (we do manual positioning)
28
+ - `src/layout/arrow-router.ts` — Arrow routing (simplified inline)
29
+ - `src/generator/excalidraw-generator.ts` — Full pipeline orchestrator
30
+ - `src/factory/image-factory.ts` — Image element support
31
+
32
+ ## How to Sync with Upstream
33
+
34
+ When upstream updates (or if something breaks):
35
+
36
+ ```bash
37
+ # 1. Check what changed
38
+ cd /tmp && git clone https://github.com/swiftlysingh/excalidraw-cli.git
39
+ diff -u excalidraw-cli/src/types/excalidraw.ts \
40
+ ~/.pi/agent/git/github.com/cwilson613/pi-kit/extensions/render/excalidraw/types.ts
41
+
42
+ # 2. Key files to diff:
43
+ # src/types/excalidraw.ts → types.ts
44
+ # src/factory/element-factory.ts → elements.ts (base element creation)
45
+ # src/factory/node-factory.ts → elements.ts (shape creation)
46
+ # src/factory/connection-factory.ts → elements.ts (arrow creation)
47
+ # src/factory/text-factory.ts → elements.ts (text creation)
48
+
49
+ # 3. Watch for:
50
+ # - New required properties in ExcalidrawElementBase
51
+ # - Changed binding format (ExcalidrawArrowBinding)
52
+ # - New element types
53
+ # - Changed default values
54
+ ```
55
+
56
+ ## Excalidraw Format Version Risks
57
+
58
+ The `.excalidraw` JSON format is defined by the Excalidraw project, not by excalidraw-cli.
59
+ Key things that could break:
60
+
61
+ 1. **New required fields on elements** — Excalidraw sometimes adds mandatory fields.
62
+ Check: https://github.com/excalidraw/excalidraw/blob/master/packages/element/src/types.ts
63
+ 2. **Binding format changes** — Arrow bindings (`startBinding`/`endBinding`) have changed before.
64
+ The `mode: "orbit" | "point"` and `fixedPoint: [number, number]` format is current as of v0.18.
65
+ 3. **Version field** — Currently `"version": 2`. If they bump to 3, older files may not load.
66
+ 4. **`index` field** — Fractional indexing for element ordering. Currently alphabetic (`"a"`, `"ab"`).
67
+
68
+ ## Also Relevant
69
+
70
+ - **Render pipeline** (`references/excalidraw/`): Uses `@excalidraw/excalidraw` from esm.sh CDN.
71
+ If the CDN version updates and introduces breaking changes to `exportToSvg`, the renderer breaks.
72
+ Pin version in `render_template.html` import URL to mitigate.
73
+ - **Official skeleton API**: `@excalidraw/element` has `convertToExcalidrawElements()` but is
74
+ prerelease-only with a hard DOM canvas dependency. If they ship a stable Node-compatible version,
75
+ consider switching from vendored factories to the official API.
76
+
77
+ ## Last Synced
78
+
79
+ - **Date:** 2026-03-03
80
+ - **Upstream version:** 1.1.0
81
+ - **By:** cwilson (omegon session)