@milehimikey/em 0.2.1 → 1.1.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 (57) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +75 -24
  2. package/.claude/skills/event-modeling/reference/em-dsl.md +15 -0
  3. package/.claude/skills/event-modeling/reference/frontmatter.md +129 -0
  4. package/.claude/skills/event-modeling/reference/methodology.md +10 -0
  5. package/.claude/skills/event-modeling/templates/model-readme.md +4 -4
  6. package/.claude/skills/event-modeling/templates/slice.md +4 -2
  7. package/.claude/skills/event-modeling/templates/state.md +9 -3
  8. package/README.md +43 -2
  9. package/dist/cli.js +227 -4
  10. package/dist/cli.js.map +1 -1
  11. package/dist/config.d.ts +15 -0
  12. package/dist/config.js +60 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/migrate/migrate.d.ts +26 -0
  15. package/dist/migrate/migrate.js +276 -0
  16. package/dist/migrate/migrate.js.map +1 -0
  17. package/dist/model/deriveSliceFields.d.ts +21 -0
  18. package/dist/model/deriveSliceFields.js +60 -0
  19. package/dist/model/deriveSliceFields.js.map +1 -0
  20. package/dist/model/edges.js +25 -1
  21. package/dist/model/edges.js.map +1 -1
  22. package/dist/model/frontmatter.d.ts +21 -0
  23. package/dist/model/frontmatter.js +38 -0
  24. package/dist/model/frontmatter.js.map +1 -0
  25. package/dist/model/model.d.ts +4 -0
  26. package/dist/model/model.js +15 -17
  27. package/dist/model/model.js.map +1 -1
  28. package/dist/model/sliceDoc.d.ts +56 -0
  29. package/dist/model/sliceDoc.js +28 -0
  30. package/dist/model/sliceDoc.js.map +1 -0
  31. package/dist/model/validate.d.ts +2 -0
  32. package/dist/model/validate.js +51 -6
  33. package/dist/model/validate.js.map +1 -1
  34. package/dist/model/validateSliceDocs.d.ts +7 -0
  35. package/dist/model/validateSliceDocs.js +195 -0
  36. package/dist/model/validateSliceDocs.js.map +1 -0
  37. package/dist/parser/ast.d.ts +2 -0
  38. package/dist/parser/parser.js +9 -0
  39. package/dist/parser/parser.js.map +1 -1
  40. package/dist/render/drawEdges.js +46 -1
  41. package/dist/render/drawEdges.js.map +1 -1
  42. package/dist/slice/generate.d.ts +17 -0
  43. package/dist/slice/generate.js +61 -0
  44. package/dist/slice/generate.js.map +1 -0
  45. package/dist/slice/query.d.ts +42 -0
  46. package/dist/slice/query.js +96 -0
  47. package/dist/slice/query.js.map +1 -0
  48. package/dist/slice/sync.d.ts +18 -0
  49. package/dist/slice/sync.js +104 -0
  50. package/dist/slice/sync.js.map +1 -0
  51. package/dist/util/findModel.d.ts +1 -0
  52. package/dist/util/findModel.js +27 -0
  53. package/dist/util/findModel.js.map +1 -0
  54. package/dist/util/slug.d.ts +4 -0
  55. package/dist/util/slug.js +32 -0
  56. package/dist/util/slug.js.map +1 -0
  57. package/package.json +6 -5
@@ -0,0 +1,61 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // `em slice new` — scaffolds slices/<id>.md: resolves the configured (or
3
+ // default) body template, injects the required frontmatter block, and never
4
+ // touches the .em (wiring `note "slices/<id>.md"` onto an element stays the
5
+ // agent's job — see docs/1.0.0-spec.md §6).
6
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
+ import { join, relative } from "node:path";
8
+ import { resolveConfig, resolveSliceTemplatePath } from "../config.js";
9
+ import { frontmatterFromData, stringifyFrontmatter } from "../model/frontmatter.js";
10
+ import { loadSliceDocs } from "../model/sliceDoc.js";
11
+ import { CURRENT_SCHEMA_VERSION } from "../model/validateSliceDocs.js";
12
+ import { findModelFile } from "../util/findModel.js";
13
+ import { dedupe, kebabSlug } from "../util/slug.js";
14
+ import { syncSlice } from "./sync.js";
15
+ export function newSlice(name, opts = {}) {
16
+ const dir = opts.dir ?? process.cwd();
17
+ const modelPath = findModelFile(dir, opts.modelPath);
18
+ const slicesDir = join(dir, "slices");
19
+ mkdirSync(slicesDir, { recursive: true });
20
+ const existingIds = new Set(loadSliceDocs(dir)
21
+ .map((d) => d.frontmatter?.id)
22
+ .filter((id) => typeof id === "string"));
23
+ let id;
24
+ if (opts.id) {
25
+ if (existingIds.has(opts.id)) {
26
+ throw new Error(`id "${opts.id}" is already used by another slice doc in ${slicesDir}`);
27
+ }
28
+ id = opts.id;
29
+ }
30
+ else {
31
+ id = dedupe(kebabSlug(name), existingIds, "-");
32
+ }
33
+ const path = join(slicesDir, `${id}.md`);
34
+ if (existsSync(path) && !opts.force) {
35
+ throw new Error(`refusing to overwrite ${path} (use --force)`);
36
+ }
37
+ const resolvedConfig = resolveConfig(dir, opts.configPath);
38
+ const templatePath = resolveSliceTemplatePath(resolvedConfig);
39
+ const bodyTemplate = readFileSync(templatePath, "utf8");
40
+ const body = bodyTemplate.replaceAll("{{Slice Name}}", name);
41
+ const today = new Date().toISOString().slice(0, 10);
42
+ const data = {
43
+ schemaVersion: CURRENT_SCHEMA_VERSION,
44
+ id,
45
+ title: name,
46
+ pattern: opts.pattern ?? "state-change",
47
+ status: "draft",
48
+ version: 1,
49
+ model: relative(slicesDir, modelPath),
50
+ created: today,
51
+ updated: today,
52
+ };
53
+ if (opts.sliceElement)
54
+ data.sliceElement = opts.sliceElement;
55
+ writeFileSync(path, stringifyFrontmatter(frontmatterFromData(data), body));
56
+ if (opts.sliceElement) {
57
+ syncSlice(id, { dir, modelPath });
58
+ }
59
+ return { path, id, modelPath };
60
+ }
61
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/slice/generate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { SliceDoc, SlicePattern, SliceStatus } from "../model/sliceDoc.js";
2
+ export interface SliceSummary {
3
+ id: string;
4
+ title: string;
5
+ pattern: SlicePattern | undefined;
6
+ status: SliceStatus | undefined;
7
+ version: number | undefined;
8
+ file: string;
9
+ }
10
+ /**
11
+ * A doc's complete frontmatter, plus the `file` it came from so a caller can
12
+ * open it without a second lookup. `file` is set after the spread, so it wins
13
+ * over a frontmatter key of the same name — treat `file` as reserved.
14
+ */
15
+ export type SliceRecord = Record<string, unknown> & {
16
+ file: string;
17
+ };
18
+ export interface ListOptions {
19
+ dir?: string;
20
+ status?: SliceStatus;
21
+ pattern?: SlicePattern;
22
+ context?: string;
23
+ tag?: string;
24
+ }
25
+ export declare function listSlices(opts?: ListOptions): SliceSummary[];
26
+ /** Same selection as listSlices, but every frontmatter field rather than the summary. */
27
+ export declare function listSliceRecords(opts?: ListOptions): SliceRecord[];
28
+ export declare function showSlice(id: string, opts?: {
29
+ dir?: string;
30
+ }): SliceDoc | undefined;
31
+ export type SearchOptions = ListOptions;
32
+ export declare function searchSlices(query: string, opts?: SearchOptions): SliceSummary[];
33
+ /** Same selection as searchSlices, but every frontmatter field rather than the summary. */
34
+ export declare function searchSliceRecords(query: string, opts?: SearchOptions): SliceRecord[];
35
+ export interface UpdateOptions {
36
+ dir?: string;
37
+ status?: SliceStatus;
38
+ bumpVersion?: boolean;
39
+ }
40
+ export declare function updateSlice(id: string, opts?: UpdateOptions): {
41
+ path: string;
42
+ };
@@ -0,0 +1,96 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // `em slice list|show|search|update` — reads (and, for `update`, writes)
3
+ // frontmatter only. Never the doc body, never the .em. That's what keeps
4
+ // these cheap across hundreds of slices — see docs/1.0.0-spec.md §6.
5
+ import { writeFileSync } from "node:fs";
6
+ import { mergeFrontmatter, stringifyFrontmatter } from "../model/frontmatter.js";
7
+ import { loadSliceDocs } from "../model/sliceDoc.js";
8
+ /** The one filter path. Every list/search entry point narrows docs through this. */
9
+ function selectDocs(opts, query = "") {
10
+ const dir = opts.dir ?? process.cwd();
11
+ const q = query.trim().toLowerCase();
12
+ return loadSliceDocs(dir)
13
+ .filter((d) => typeof d.frontmatter?.id === "string")
14
+ .filter((d) => !opts.status || d.frontmatter.status === opts.status)
15
+ .filter((d) => !opts.pattern || d.frontmatter.pattern === opts.pattern)
16
+ .filter((d) => !opts.context || asStringArray(d.frontmatter.contexts).includes(opts.context))
17
+ .filter((d) => !opts.tag || asStringArray(d.frontmatter.tags).includes(opts.tag))
18
+ .filter((d) => q.length === 0 || matchesQuery(d.frontmatter, q));
19
+ }
20
+ export function listSlices(opts = {}) {
21
+ return selectDocs(opts).map(toSummary).filter((s) => s !== null);
22
+ }
23
+ /** Same selection as listSlices, but every frontmatter field rather than the summary. */
24
+ export function listSliceRecords(opts = {}) {
25
+ return selectDocs(opts).map(toRecord);
26
+ }
27
+ export function showSlice(id, opts = {}) {
28
+ const dir = opts.dir ?? process.cwd();
29
+ return loadSliceDocs(dir).find((d) => d.frontmatter?.id === id);
30
+ }
31
+ export function searchSlices(query, opts = {}) {
32
+ return selectDocs(opts, query).map(toSummary).filter((s) => s !== null);
33
+ }
34
+ /** Same selection as searchSlices, but every frontmatter field rather than the summary. */
35
+ export function searchSliceRecords(query, opts = {}) {
36
+ return selectDocs(opts, query).map(toRecord);
37
+ }
38
+ export function updateSlice(id, opts = {}) {
39
+ const dir = opts.dir ?? process.cwd();
40
+ const doc = loadSliceDocs(dir).find((d) => d.frontmatter?.id === id);
41
+ if (!doc || !doc.document || !doc.frontmatter) {
42
+ throw new Error(`no slice doc with id "${id}" (with valid frontmatter) found in ${dir}`);
43
+ }
44
+ const patch = {};
45
+ if (opts.status)
46
+ patch.status = opts.status;
47
+ if (opts.bumpVersion) {
48
+ const current = typeof doc.frontmatter.version === "number" ? doc.frontmatter.version : 0;
49
+ patch.version = current + 1;
50
+ }
51
+ if (Object.keys(patch).length === 0) {
52
+ throw new Error("nothing to update — pass --status and/or --bump-version");
53
+ }
54
+ patch.updated = new Date().toISOString().slice(0, 10);
55
+ mergeFrontmatter(doc.document, patch);
56
+ writeFileSync(doc.path, stringifyFrontmatter(doc.document, doc.body));
57
+ return { path: doc.path };
58
+ }
59
+ function toSummary(doc) {
60
+ const data = doc.frontmatter;
61
+ if (!data || typeof data.id !== "string")
62
+ return null;
63
+ return {
64
+ id: data.id,
65
+ title: typeof data.title === "string" ? data.title : data.id,
66
+ pattern: data.pattern,
67
+ status: data.status,
68
+ version: typeof data.version === "number" ? data.version : undefined,
69
+ file: doc.file,
70
+ };
71
+ }
72
+ function toRecord(doc) {
73
+ return { ...doc.frontmatter, file: doc.file };
74
+ }
75
+ function matchesQuery(data, q) {
76
+ const haystack = [
77
+ data.title,
78
+ ...asStringArray(data.tags),
79
+ ...asStringArray(data.commands),
80
+ ...asStringArray(data.events),
81
+ ...asStringArray(data.readModels),
82
+ // `compliance` is a reserved, unvalidated block (docs/1.0.0-spec.md §2.6) — its shape isn't
83
+ // known, so flatten whatever's there rather than reading named sub-fields. Without this,
84
+ // freeform compliance data (PCI-DSS, SOX, HIPAA, ...) would be the one thing in frontmatter
85
+ // an agent couldn't search for.
86
+ data.compliance !== undefined ? JSON.stringify(data.compliance) : undefined,
87
+ ]
88
+ .filter((v) => typeof v === "string")
89
+ .join(" ")
90
+ .toLowerCase();
91
+ return haystack.includes(q);
92
+ }
93
+ function asStringArray(v) {
94
+ return Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
95
+ }
96
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/slice/query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface SyncOptions {
2
+ dir?: string;
3
+ modelPath?: string;
4
+ }
5
+ export interface SyncResult {
6
+ id: string;
7
+ path: string;
8
+ changed: boolean;
9
+ }
10
+ export interface SyncAllResult {
11
+ synced: SyncResult[];
12
+ skipped: {
13
+ file: string;
14
+ reason: string;
15
+ }[];
16
+ }
17
+ export declare function syncSlice(id: string, opts?: SyncOptions): SyncResult;
18
+ export declare function syncAll(opts?: SyncOptions): SyncAllResult;
@@ -0,0 +1,104 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // `em slice sync` — the one command that reads the .em and (re)writes the
3
+ // generated frontmatter fields (commands/events/readModels/contexts/personas/
4
+ // triggers/triggeredBy). Everything else (`list`/`show`/`search`) trusts this
5
+ // cache instead of re-deriving it. See docs/1.0.0-spec.md §6.
6
+ import { readFileSync, writeFileSync } from "node:fs";
7
+ import { dirname, resolve } from "node:path";
8
+ import { parse } from "../parser/parser.js";
9
+ import { normalize } from "../model/model.js";
10
+ import { buildDocIdByElementId, deriveGeneratedFields } from "../model/deriveSliceFields.js";
11
+ import { mergeFrontmatter, stringifyFrontmatter } from "../model/frontmatter.js";
12
+ import { loadSliceDocs } from "../model/sliceDoc.js";
13
+ import { findModelFile } from "../util/findModel.js";
14
+ const GENERATED_KEYS = ["commands", "events", "readModels", "contexts", "personas"];
15
+ /**
16
+ * Parse the .em and load every slice doc once. Shared by syncSlice and syncAll.
17
+ *
18
+ * Every doc's sliceElement is resolved up front — from frontmatter, or discovered
19
+ * from the .em's `note` back-reference — so `docIdByElementId` already covers docs
20
+ * that haven't had their sliceElement written yet. That makes a `--all` run
21
+ * order-independent: triggers/triggeredBy resolve between two freshly-scaffolded
22
+ * docs regardless of which one syncs first.
23
+ */
24
+ function loadContext(opts) {
25
+ const dir = opts.dir ?? process.cwd();
26
+ const modelPath = findModelFile(dir, opts.modelPath);
27
+ const model = normalize(parse(readFileSync(modelPath, "utf8")));
28
+ const docs = loadSliceDocs(dir);
29
+ const elementByDocId = new Map();
30
+ for (const doc of docs) {
31
+ const id = doc.frontmatter?.id;
32
+ if (typeof id !== "string")
33
+ continue;
34
+ const raw = doc.frontmatter.sliceElement;
35
+ if (typeof raw === "string" && raw.length > 0) {
36
+ elementByDocId.set(id, raw);
37
+ continue;
38
+ }
39
+ const discovered = model.elements.find((el) => el.note && resolve(dirname(modelPath), el.note) === resolve(doc.path));
40
+ if (discovered)
41
+ elementByDocId.set(id, discovered.id);
42
+ }
43
+ const docIdByElementId = buildDocIdByElementId(model, docs);
44
+ for (const [docId, elementId] of elementByDocId)
45
+ docIdByElementId.set(elementId, docId);
46
+ return { dir, modelPath, model, docs, elementByDocId, docIdByElementId };
47
+ }
48
+ export function syncSlice(id, opts = {}) {
49
+ return syncOne(id, loadContext(opts));
50
+ }
51
+ function syncOne(id, ctx) {
52
+ const { dir, modelPath, model, docs, elementByDocId, docIdByElementId } = ctx;
53
+ const doc = docs.find((d) => d.frontmatter?.id === id);
54
+ if (!doc || !doc.frontmatter || !doc.document) {
55
+ throw new Error(`no slice doc with id "${id}" (with valid frontmatter) found in ${dir}`);
56
+ }
57
+ const sliceElementId = elementByDocId.get(id);
58
+ if (!sliceElementId) {
59
+ throw new Error(`slice "${id}" has no sliceElement set — wire \`note "slices/${doc.file}"\` onto an ` +
60
+ `element in ${modelPath} first, then re-run sync`);
61
+ }
62
+ const derived = deriveGeneratedFields(model, sliceElementId, docIdByElementId);
63
+ if (!derived) {
64
+ throw new Error(`sliceElement "${sliceElementId}" does not match any element in ${modelPath}`);
65
+ }
66
+ const before = doc.document.toJS();
67
+ const changed = before.sliceElement !== sliceElementId ||
68
+ GENERATED_KEYS.some((key) => JSON.stringify(before[key] ?? []) !== JSON.stringify(derived[key])) ||
69
+ before.triggers !== derived.triggers ||
70
+ before.triggeredBy !== derived.triggeredBy;
71
+ mergeFrontmatter(doc.document, { sliceElement: sliceElementId });
72
+ for (const key of GENERATED_KEYS)
73
+ mergeFrontmatter(doc.document, { [key]: derived[key] });
74
+ setOrDelete(doc, "triggers", derived.triggers);
75
+ setOrDelete(doc, "triggeredBy", derived.triggeredBy);
76
+ writeFileSync(doc.path, stringifyFrontmatter(doc.document, doc.body));
77
+ return { id, path: doc.path, changed };
78
+ }
79
+ export function syncAll(opts = {}) {
80
+ const ctx = loadContext(opts);
81
+ const synced = [];
82
+ const skipped = [];
83
+ for (const doc of ctx.docs) {
84
+ const id = doc.frontmatter?.id;
85
+ if (typeof id !== "string") {
86
+ skipped.push({ file: doc.file, reason: "no frontmatter (or no `id`); run `em migrate`" });
87
+ continue;
88
+ }
89
+ try {
90
+ synced.push(syncOne(id, ctx));
91
+ }
92
+ catch (e) {
93
+ skipped.push({ file: doc.file, reason: e instanceof Error ? e.message : String(e) });
94
+ }
95
+ }
96
+ return { synced, skipped };
97
+ }
98
+ function setOrDelete(doc, key, value) {
99
+ if (value === undefined)
100
+ doc.document.delete(key);
101
+ else
102
+ mergeFrontmatter(doc.document, { [key]: value });
103
+ }
104
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/slice/sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function findModelFile(dir: string, explicitPath?: string): string;
@@ -0,0 +1,27 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Resolves "the .em file" for a model directory, per the skill's existing
3
+ // one-model-per-directory convention. Shared by every `em slice`/`em migrate`
4
+ // command so an explicit --model always wins and the "which .em?" ambiguity
5
+ // error is worded the same way everywhere.
6
+ import { existsSync, readdirSync } from "node:fs";
7
+ import { resolve } from "node:path";
8
+ export function findModelFile(dir, explicitPath) {
9
+ if (explicitPath) {
10
+ // Relative to cwd, not `dir` — an explicit --model is a path the user typed
11
+ // at the shell, so it must resolve the same way their shell would resolve it.
12
+ // Checked eagerly: callers like `em slice new` only ever store this path in
13
+ // frontmatter, so a wrong one would otherwise sit undetected until validate.
14
+ const path = resolve(explicitPath);
15
+ if (!existsSync(path))
16
+ throw new Error(`model file not found: ${path}`);
17
+ return path;
18
+ }
19
+ const candidates = readdirSync(dir).filter((f) => f.endsWith(".em"));
20
+ if (candidates.length === 1)
21
+ return resolve(dir, candidates[0]);
22
+ if (candidates.length === 0) {
23
+ throw new Error(`no .em file found in ${dir} (pass --model <path.em>)`);
24
+ }
25
+ throw new Error(`multiple .em files found in ${dir} (${candidates.join(", ")}); pass --model <path.em>`);
26
+ }
27
+ //# sourceMappingURL=findModel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findModel.js","sourceRoot":"","sources":["../../src/util/findModel.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,0EAA0E;AAC1E,8EAA8E;AAC9E,4EAA4E;AAC5E,2CAA2C;AAE3C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,YAAqB;IAC9D,IAAI,YAAY,EAAE,CAAC;QACjB,4EAA4E;QAC5E,8EAA8E;QAC9E,4EAA4E;QAC5E,6EAA6E;QAC7E,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;QACnC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;QACxE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IACrE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,2BAA2B,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,IAAI,KAAK,CACb,+BAA+B,GAAG,KAAK,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,2BAA2B,CACxF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function slug(name: string): string;
2
+ export declare function kebabSlug(name: string): string;
3
+ /** Append a numeric suffix (`${sep}2`, `${sep}3`, ...) until `id` isn't in `used`. Adds the result to `used`. */
4
+ export declare function dedupe(id: string, used: Set<string>, sep: string): string;
@@ -0,0 +1,32 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Shared name -> identifier slugging. Two conventions, two purposes:
3
+ // - slug(): underscore-separated, used for .em Element ids (model.ts).
4
+ // - kebabSlug(): hyphen-separated, used for slice-doc frontmatter `id` and
5
+ // filenames (matches the skill's existing "kebab-case the slice name"
6
+ // convention). These are distinct namespaces — do not conflate them.
7
+ export function slug(name) {
8
+ const s = name
9
+ .trim()
10
+ .toLowerCase()
11
+ .replace(/[^a-z0-9]+/g, "_")
12
+ .replace(/^_+|_+$/g, "");
13
+ return s || "n";
14
+ }
15
+ export function kebabSlug(name) {
16
+ const s = name
17
+ .trim()
18
+ .toLowerCase()
19
+ .replace(/[^a-z0-9]+/g, "-")
20
+ .replace(/^-+|-+$/g, "");
21
+ return s || "n";
22
+ }
23
+ /** Append a numeric suffix (`${sep}2`, `${sep}3`, ...) until `id` isn't in `used`. Adds the result to `used`. */
24
+ export function dedupe(id, used, sep) {
25
+ let candidate = id;
26
+ let n = 2;
27
+ while (used.has(candidate))
28
+ candidate = `${id}${sep}${n++}`;
29
+ used.add(candidate);
30
+ return candidate;
31
+ }
32
+ //# sourceMappingURL=slug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug.js","sourceRoot":"","sources":["../../src/util/slug.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,qEAAqE;AACrE,wEAAwE;AACxE,4EAA4E;AAC5E,yEAAyE;AACzE,wEAAwE;AAExE,MAAM,UAAU,IAAI,CAAC,IAAY;IAC/B,MAAM,CAAC,GAAG,IAAI;SACX,IAAI,EAAE;SACN,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,OAAO,CAAC,IAAI,GAAG,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,CAAC,GAAG,IAAI;SACX,IAAI,EAAE;SACN,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,OAAO,CAAC,IAAI,GAAG,CAAC;AAClB,CAAC;AAED,iHAAiH;AACjH,MAAM,UAAU,MAAM,CAAC,EAAU,EAAE,IAAiB,EAAE,GAAW;IAC/D,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;QAAE,SAAS,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,CAAC,EAAE,EAAE,CAAC;IAC5D,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACpB,OAAO,SAAS,CAAC;AACnB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@milehimikey/em",
3
- "version": "0.2.1",
4
- "description": "AI-friendly Event Modeling — a slice-first text DSL rendered to a strict, self-contained Graphviz grid (SVG/PNG)",
3
+ "version": "1.1.0",
4
+ "description": "AI-friendly Event Modeling \u2014 a slice-first text DSL rendered to a strict, self-contained Graphviz grid (SVG/PNG)",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "em": "./dist/cli.js"
@@ -11,7 +11,7 @@
11
11
  "dist",
12
12
  "README.md",
13
13
  "LICENSE",
14
- ".claude"
14
+ ".claude/skills"
15
15
  ],
16
16
  "scripts": {
17
17
  "build": "tsc -p tsconfig.json",
@@ -51,7 +51,8 @@
51
51
  "@hpcc-js/wasm-graphviz": "^1.22.0",
52
52
  "@resvg/resvg-js": "^2.6.2",
53
53
  "chokidar": "^3.6.0",
54
- "commander": "^12.1.0"
54
+ "commander": "^12.1.0",
55
+ "yaml": "^2.9.0"
55
56
  },
56
57
  "devDependencies": {
57
58
  "@types/node": "^22.0.0",
@@ -59,4 +60,4 @@
59
60
  "typescript": "^5.6.0",
60
61
  "vitest": "^2.1.0"
61
62
  }
62
- }
63
+ }