structured-context 0.9.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 (112) hide show
  1. package/README.md +348 -0
  2. package/dist/commands/diagram.d.ts +5 -0
  3. package/dist/commands/diagram.js +12 -0
  4. package/dist/commands/docs.d.ts +1 -0
  5. package/dist/commands/docs.js +67 -0
  6. package/dist/commands/dump.d.ts +2 -0
  7. package/dist/commands/dump.js +6 -0
  8. package/dist/commands/plugins.d.ts +1 -0
  9. package/dist/commands/plugins.js +23 -0
  10. package/dist/commands/render.d.ts +6 -0
  11. package/dist/commands/render.js +35 -0
  12. package/dist/commands/schemas.d.ts +6 -0
  13. package/dist/commands/schemas.js +268 -0
  14. package/dist/commands/show.d.ts +4 -0
  15. package/dist/commands/show.js +7 -0
  16. package/dist/commands/spaces.d.ts +1 -0
  17. package/dist/commands/spaces.js +36 -0
  18. package/dist/commands/template-sync.d.ts +3 -0
  19. package/dist/commands/template-sync.js +13 -0
  20. package/dist/commands/validate-file.d.ts +28 -0
  21. package/dist/commands/validate-file.js +133 -0
  22. package/dist/commands/validate.d.ts +16 -0
  23. package/dist/commands/validate.js +349 -0
  24. package/dist/config.d.ts +38 -0
  25. package/dist/config.js +179 -0
  26. package/dist/constants.d.ts +6 -0
  27. package/dist/constants.js +6 -0
  28. package/dist/filter/augment-nodes.d.ts +23 -0
  29. package/dist/filter/augment-nodes.js +95 -0
  30. package/dist/filter/expand-include.d.ts +62 -0
  31. package/dist/filter/expand-include.js +181 -0
  32. package/dist/filter/filter-nodes.d.ts +21 -0
  33. package/dist/filter/filter-nodes.js +73 -0
  34. package/dist/filter/parse-expression.d.ts +20 -0
  35. package/dist/filter/parse-expression.js +60 -0
  36. package/dist/index.d.ts +3 -0
  37. package/dist/index.js +161 -0
  38. package/dist/integrations/miro/cache.d.ts +21 -0
  39. package/dist/integrations/miro/cache.js +55 -0
  40. package/dist/integrations/miro/client.d.ts +99 -0
  41. package/dist/integrations/miro/client.js +118 -0
  42. package/dist/integrations/miro/layout.d.ts +28 -0
  43. package/dist/integrations/miro/layout.js +72 -0
  44. package/dist/integrations/miro/styles.d.ts +11 -0
  45. package/dist/integrations/miro/styles.js +65 -0
  46. package/dist/integrations/miro/sync.d.ts +8 -0
  47. package/dist/integrations/miro/sync.js +347 -0
  48. package/dist/plugin-api.d.ts +12 -0
  49. package/dist/plugin-api.js +7 -0
  50. package/dist/plugins/index.d.ts +3 -0
  51. package/dist/plugins/index.js +3 -0
  52. package/dist/plugins/loader.d.ts +21 -0
  53. package/dist/plugins/loader.js +104 -0
  54. package/dist/plugins/markdown/index.d.ts +48 -0
  55. package/dist/plugins/markdown/index.js +51 -0
  56. package/dist/plugins/markdown/parse-embedded.d.ts +90 -0
  57. package/dist/plugins/markdown/parse-embedded.js +663 -0
  58. package/dist/plugins/markdown/read-space.d.ts +7 -0
  59. package/dist/plugins/markdown/read-space.js +89 -0
  60. package/dist/plugins/markdown/render-bullets.d.ts +2 -0
  61. package/dist/plugins/markdown/render-bullets.js +42 -0
  62. package/dist/plugins/markdown/render-mermaid.d.ts +2 -0
  63. package/dist/plugins/markdown/render-mermaid.js +57 -0
  64. package/dist/plugins/markdown/template-sync.d.ts +16 -0
  65. package/dist/plugins/markdown/template-sync.js +294 -0
  66. package/dist/plugins/markdown/util.d.ts +19 -0
  67. package/dist/plugins/markdown/util.js +80 -0
  68. package/dist/plugins/util.d.ts +60 -0
  69. package/dist/plugins/util.js +7 -0
  70. package/dist/read/read-space.d.ts +2 -0
  71. package/dist/read/read-space.js +22 -0
  72. package/dist/read/resolve-graph-edges.d.ts +11 -0
  73. package/dist/read/resolve-graph-edges.js +201 -0
  74. package/dist/read/wikilink-utils.d.ts +16 -0
  75. package/dist/read/wikilink-utils.js +38 -0
  76. package/dist/render/registry.d.ts +13 -0
  77. package/dist/render/registry.js +22 -0
  78. package/dist/render/render.d.ts +4 -0
  79. package/dist/render/render.js +28 -0
  80. package/dist/schema/evaluate-rule.d.ts +30 -0
  81. package/dist/schema/evaluate-rule.js +82 -0
  82. package/dist/schema/metadata-contract.d.ts +538 -0
  83. package/dist/schema/metadata-contract.js +115 -0
  84. package/dist/schema/schema-refs.d.ts +22 -0
  85. package/dist/schema/schema-refs.js +168 -0
  86. package/dist/schema/schema.d.ts +27 -0
  87. package/dist/schema/schema.js +378 -0
  88. package/dist/schema/validate-graph.d.ts +24 -0
  89. package/dist/schema/validate-graph.js +141 -0
  90. package/dist/schema/validate-rules.d.ts +10 -0
  91. package/dist/schema/validate-rules.js +51 -0
  92. package/dist/schemas/_ost_strict.json +81 -0
  93. package/dist/schemas/_sctx_base.json +72 -0
  94. package/dist/schemas/general.json +261 -0
  95. package/dist/schemas/generated/_structured_context_schema_meta.json +191 -0
  96. package/dist/schemas/knowledge_wiki.json +206 -0
  97. package/dist/schemas/strict_ost.json +97 -0
  98. package/dist/space-graph.d.ts +28 -0
  99. package/dist/space-graph.js +82 -0
  100. package/dist/types.d.ts +145 -0
  101. package/dist/types.js +0 -0
  102. package/docs/concepts.md +391 -0
  103. package/docs/config.md +140 -0
  104. package/docs/rules.md +120 -0
  105. package/docs/schemas.md +340 -0
  106. package/package.json +69 -0
  107. package/schemas/_ost_strict.json +81 -0
  108. package/schemas/_sctx_base.json +72 -0
  109. package/schemas/general.json +261 -0
  110. package/schemas/generated/_structured_context_schema_meta.json +191 -0
  111. package/schemas/knowledge_wiki.json +206 -0
  112. package/schemas/strict_ost.json +97 -0
@@ -0,0 +1,21 @@
1
+ import type { SpaceNode } from '../../types';
2
+ export interface CachedNode {
3
+ miroCardId: string;
4
+ contentHash: string;
5
+ }
6
+ export interface CachedConnector {
7
+ miroConnectorId: string;
8
+ }
9
+ export interface SyncCache {
10
+ boardId: string;
11
+ frameId: string;
12
+ spaceName?: string;
13
+ lastSync: string;
14
+ nodes: Record<string, CachedNode>;
15
+ connectors: Record<string, CachedConnector>;
16
+ }
17
+ export declare function loadCache(boardId: string, frameId: string): SyncCache;
18
+ export declare function saveCache(cache: SyncCache): void;
19
+ export declare function computeNodeHash(node: SpaceNode): string;
20
+ /** Compute hash from Miro card data (title + description) to compare against node hash */
21
+ export declare function computeMiroCardHash(title: string, description: string): string;
@@ -0,0 +1,55 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ const CACHE_DIR = '.miro-cache';
5
+ function cachePath(boardId, frameId) {
6
+ return join(CACHE_DIR, `${boardId}-${frameId}.json`);
7
+ }
8
+ export function loadCache(boardId, frameId) {
9
+ const path = cachePath(boardId, frameId);
10
+ if (existsSync(path)) {
11
+ return JSON.parse(readFileSync(path, 'utf-8'));
12
+ }
13
+ return {
14
+ boardId,
15
+ frameId,
16
+ lastSync: '',
17
+ nodes: {},
18
+ connectors: {},
19
+ };
20
+ }
21
+ export function saveCache(cache) {
22
+ mkdirSync(CACHE_DIR, { recursive: true });
23
+ const path = cachePath(cache.boardId, cache.frameId);
24
+ writeFileSync(path, `${JSON.stringify(cache, null, 2)}\n`);
25
+ }
26
+ export function computeNodeHash(node) {
27
+ const relevant = {
28
+ title: node.schemaData.title,
29
+ type: node.schemaData.type,
30
+ status: node.schemaData.status,
31
+ summary: node.schemaData.summary,
32
+ priority: node.schemaData.priority,
33
+ parents: node.resolvedParents.filter((r) => r.source === 'hierarchy').map((r) => r.title),
34
+ };
35
+ return createHash('sha256').update(JSON.stringify(relevant)).digest('hex').slice(0, 16);
36
+ }
37
+ /** Normalize HTML entities in text (e.g., &amp; → &) */
38
+ function normalizeHtmlEntities(text) {
39
+ return text
40
+ .replace(/&amp;/g, '&')
41
+ .replace(/&lt;/g, '<')
42
+ .replace(/&gt;/g, '>')
43
+ .replace(/&quot;/g, '"')
44
+ .replace(/&#39;/g, "'")
45
+ .replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(parseInt(dec, 10)))
46
+ .replace(/&#x([0-9a-f]+);/gi, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
47
+ }
48
+ /** Compute hash from Miro card data (title + description) to compare against node hash */
49
+ export function computeMiroCardHash(title, description) {
50
+ const normalized = {
51
+ title: normalizeHtmlEntities(title),
52
+ description: normalizeHtmlEntities(description),
53
+ };
54
+ return createHash('sha256').update(JSON.stringify(normalized)).digest('hex').slice(0, 16);
55
+ }
@@ -0,0 +1,99 @@
1
+ export interface MiroCard {
2
+ id: string;
3
+ type: 'card';
4
+ data: {
5
+ title: string;
6
+ description?: string;
7
+ };
8
+ style?: {
9
+ cardTheme?: string;
10
+ };
11
+ position?: {
12
+ x: number;
13
+ y: number;
14
+ };
15
+ parent?: {
16
+ id: string;
17
+ };
18
+ }
19
+ export interface MiroConnector {
20
+ id: string;
21
+ type: 'connector';
22
+ startItem: {
23
+ id: string;
24
+ };
25
+ endItem: {
26
+ id: string;
27
+ };
28
+ }
29
+ export interface MiroFrame {
30
+ id: string;
31
+ type: 'frame';
32
+ data: {
33
+ title: string;
34
+ };
35
+ }
36
+ export declare class MiroClient {
37
+ private token;
38
+ private boardId;
39
+ constructor(boardId: string, token: string);
40
+ private request;
41
+ createCard(payload: {
42
+ data: {
43
+ title: string;
44
+ description?: string;
45
+ };
46
+ style?: {
47
+ cardTheme?: string;
48
+ };
49
+ position?: {
50
+ x: number;
51
+ y: number;
52
+ origin?: string;
53
+ };
54
+ parent?: {
55
+ id: string;
56
+ };
57
+ geometry?: {
58
+ width?: number;
59
+ };
60
+ }): Promise<MiroCard>;
61
+ updateCard(id: string, payload: {
62
+ data?: {
63
+ title?: string;
64
+ description?: string;
65
+ };
66
+ style?: {
67
+ cardTheme?: string;
68
+ };
69
+ }): Promise<MiroCard>;
70
+ getCard(id: string): Promise<MiroCard>;
71
+ createConnector(startId: string, endId: string, style?: {
72
+ strokeColor?: string;
73
+ strokeStyle?: string;
74
+ strokeWidth?: string;
75
+ }): Promise<MiroConnector>;
76
+ getConnectors(): Promise<MiroConnector[]>;
77
+ deleteConnector(id: string): Promise<void>;
78
+ createFrame(payload: {
79
+ data: {
80
+ title: string;
81
+ type?: string;
82
+ };
83
+ position?: {
84
+ x: number;
85
+ y: number;
86
+ origin?: string;
87
+ };
88
+ geometry?: {
89
+ width?: number;
90
+ height?: number;
91
+ };
92
+ }): Promise<MiroFrame>;
93
+ getFrames(): Promise<MiroFrame[]>;
94
+ getFrame(frameId: string): Promise<MiroFrame | undefined>;
95
+ getItemsInFrame(frameId: string): Promise<MiroCard[]>;
96
+ }
97
+ export declare class MiroNotFoundError extends Error {
98
+ constructor(message: string);
99
+ }
@@ -0,0 +1,118 @@
1
+ const API_BASE = 'https://api.miro.com/v2';
2
+ const MAX_RETRIES = 3;
3
+ export class MiroClient {
4
+ token;
5
+ boardId;
6
+ constructor(boardId, token) {
7
+ this.boardId = boardId;
8
+ this.token = token;
9
+ }
10
+ async request(method, path, body) {
11
+ const url = `${API_BASE}/boards/${this.boardId}${path}`;
12
+ let lastError;
13
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
14
+ const res = await fetch(url, {
15
+ method,
16
+ headers: {
17
+ Authorization: `Bearer ${this.token}`,
18
+ 'Content-Type': 'application/json',
19
+ },
20
+ body: body ? JSON.stringify(body) : undefined,
21
+ });
22
+ if (res.status === 429) {
23
+ const retryAfter = parseInt(res.headers.get('Retry-After') ?? '2', 10);
24
+ if (attempt < MAX_RETRIES) {
25
+ await new Promise((r) => setTimeout(r, retryAfter * 1000));
26
+ continue;
27
+ }
28
+ }
29
+ if (res.status === 404) {
30
+ throw new MiroNotFoundError(`${method} ${path}: 404 Not Found`);
31
+ }
32
+ if (!res.ok) {
33
+ const text = await res.text();
34
+ lastError = new Error(`Miro API ${method} ${path}: ${res.status} ${text}`);
35
+ if (attempt < MAX_RETRIES && res.status >= 500) {
36
+ await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));
37
+ continue;
38
+ }
39
+ throw lastError;
40
+ }
41
+ if (res.status === 204)
42
+ return undefined;
43
+ return (await res.json());
44
+ }
45
+ throw lastError ?? new Error('Max retries exceeded');
46
+ }
47
+ // Cards
48
+ async createCard(payload) {
49
+ return this.request('POST', '/cards', payload);
50
+ }
51
+ async updateCard(id, payload) {
52
+ return this.request('PATCH', `/cards/${id}`, payload);
53
+ }
54
+ async getCard(id) {
55
+ return this.request('GET', `/cards/${id}`);
56
+ }
57
+ // Connectors
58
+ async createConnector(startId, endId, style) {
59
+ return this.request('POST', '/connectors', {
60
+ startItem: { id: startId, snapTo: 'auto' },
61
+ endItem: { id: endId, snapTo: 'auto' },
62
+ style,
63
+ });
64
+ }
65
+ async getConnectors() {
66
+ const items = [];
67
+ let cursor;
68
+ do {
69
+ const params = cursor ? `?cursor=${cursor}` : '';
70
+ const res = await this.request('GET', `/connectors${params}`);
71
+ items.push(...res.data);
72
+ cursor = res.cursor;
73
+ } while (cursor);
74
+ return items;
75
+ }
76
+ async deleteConnector(id) {
77
+ await this.request('DELETE', `/connectors/${id}`);
78
+ }
79
+ // Frames
80
+ async createFrame(payload) {
81
+ return this.request('POST', '/frames', payload);
82
+ }
83
+ async getFrames() {
84
+ const items = [];
85
+ let cursor;
86
+ do {
87
+ const params = cursor ? `?cursor=${cursor}` : '';
88
+ const res = await this.request('GET', `/frames${params}`);
89
+ items.push(...res.data);
90
+ cursor = res.cursor;
91
+ } while (cursor);
92
+ return items;
93
+ }
94
+ async getFrame(frameId) {
95
+ const frames = await this.getFrames();
96
+ return frames.find((f) => f.id === frameId);
97
+ }
98
+ // Items in frame
99
+ async getItemsInFrame(frameId) {
100
+ const items = [];
101
+ let cursor;
102
+ do {
103
+ const params = new URLSearchParams({ parent_item_id: frameId });
104
+ if (cursor)
105
+ params.set('cursor', cursor);
106
+ const res = await this.request('GET', `/items?${params}`);
107
+ items.push(...res.data);
108
+ cursor = res.cursor;
109
+ } while (cursor);
110
+ return items;
111
+ }
112
+ }
113
+ export class MiroNotFoundError extends Error {
114
+ constructor(message) {
115
+ super(message);
116
+ this.name = 'MiroNotFoundError';
117
+ }
118
+ }
@@ -0,0 +1,28 @@
1
+ import type { HierarchyLevel, SpaceNode } from '../../types';
2
+ export declare const CARD_WIDTH = 320;
3
+ export interface LayoutResult {
4
+ positions: Map<string, {
5
+ x: number;
6
+ y: number;
7
+ }>;
8
+ /** Bounding box of all cards (new + existing) — useful for sizing the containing frame. */
9
+ bounds: {
10
+ minX: number;
11
+ minY: number;
12
+ maxX: number;
13
+ maxY: number;
14
+ };
15
+ }
16
+ /**
17
+ * Compute positions for new cards only. Existing cards keep their Miro positions.
18
+ * New cards are laid out in rows grouped by OST type depth, starting below
19
+ * the lowest existing card (or at the origin if no existing cards).
20
+ *
21
+ * @param hierarchyLevels - Hierarchy levels from metadata. Depths are computed from level position.
22
+ *
23
+ * Returns positions and a bounding box covering all cards (for frame sizing).
24
+ */
25
+ export declare function layoutNewCards(newNodes: SpaceNode[], existingPositions: Map<string, {
26
+ x: number;
27
+ y: number;
28
+ }>, hierarchyLevels: HierarchyLevel[]): LayoutResult;
@@ -0,0 +1,72 @@
1
+ export const CARD_WIDTH = 320;
2
+ const CARD_HEIGHT = 160;
3
+ const H_GAP = 40;
4
+ const V_GAP = 60;
5
+ const FRAME_PADDING = 60;
6
+ /**
7
+ * DEPRECATED - likely not needed after migration to render plugin and SpaceGraph
8
+ * Build a depth map from hierarchy levels.
9
+ * The position in the hierarchy array determines the depth.
10
+ */
11
+ function buildDepthMap(hierarchyLevels) {
12
+ const depthMap = new Map();
13
+ for (const [i, level] of hierarchyLevels.entries()) {
14
+ depthMap.set(level.type, i);
15
+ }
16
+ return depthMap;
17
+ }
18
+ /**
19
+ * Compute positions for new cards only. Existing cards keep their Miro positions.
20
+ * New cards are laid out in rows grouped by OST type depth, starting below
21
+ * the lowest existing card (or at the origin if no existing cards).
22
+ *
23
+ * @param hierarchyLevels - Hierarchy levels from metadata. Depths are computed from level position.
24
+ *
25
+ * Returns positions and a bounding box covering all cards (for frame sizing).
26
+ */
27
+ export function layoutNewCards(newNodes, existingPositions, hierarchyLevels) {
28
+ // Build depth map from hierarchy levels (position in hierarchy = depth)
29
+ const depthMap = buildDepthMap(hierarchyLevels);
30
+ // Find the lowest y among existing cards
31
+ let lowestY = 0;
32
+ for (const pos of existingPositions.values()) {
33
+ if (pos.y + CARD_HEIGHT / 2 > lowestY) {
34
+ lowestY = pos.y + CARD_HEIGHT / 2;
35
+ }
36
+ }
37
+ const startY = existingPositions.size > 0 ? lowestY + V_GAP * 2 : 0;
38
+ // Group new nodes by depth
39
+ const byDepth = new Map();
40
+ for (const node of newNodes) {
41
+ const depth = depthMap.get(node.schemaData.type) ?? depthMap.size;
42
+ if (!byDepth.has(depth))
43
+ byDepth.set(depth, []);
44
+ byDepth.get(depth)?.push(node);
45
+ }
46
+ const positions = new Map();
47
+ const depths = [...byDepth.keys()].sort((a, b) => a - b);
48
+ let rowY = startY;
49
+ for (const depth of depths) {
50
+ const nodes = byDepth.get(depth);
51
+ const totalWidth = nodes.length * CARD_WIDTH + (nodes.length - 1) * H_GAP;
52
+ let x = -totalWidth / 2 + CARD_WIDTH / 2;
53
+ for (const node of nodes) {
54
+ const title = node.title;
55
+ positions.set(title, { x, y: rowY });
56
+ x += CARD_WIDTH + H_GAP;
57
+ }
58
+ rowY += CARD_HEIGHT + V_GAP;
59
+ }
60
+ // Compute bounding box across all card positions (existing + new)
61
+ const allPositions = [...existingPositions.values(), ...positions.values()];
62
+ if (allPositions.length === 0) {
63
+ return { positions, bounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 } };
64
+ }
65
+ const bounds = {
66
+ minX: Math.min(...allPositions.map((p) => p.x - CARD_WIDTH / 2)) - FRAME_PADDING,
67
+ minY: Math.min(...allPositions.map((p) => p.y - CARD_HEIGHT / 2)) - FRAME_PADDING,
68
+ maxX: Math.max(...allPositions.map((p) => p.x + CARD_WIDTH / 2)) + FRAME_PADDING,
69
+ maxY: Math.max(...allPositions.map((p) => p.y + CARD_HEIGHT / 2)) + FRAME_PADDING,
70
+ };
71
+ return { positions, bounds };
72
+ }
@@ -0,0 +1,11 @@
1
+ import type { HierarchyLevel, SpaceNode } from '../../types';
2
+ /**
3
+ * Get card color based on type and hierarchy levels.
4
+ * Colors are assigned from palette based on level position.
5
+ *
6
+ * @param type - The node's type string
7
+ * @param hierarchyLevels - Hierarchy levels from metadata
8
+ */
9
+ export declare function getCardColor(type: string, hierarchyLevels: HierarchyLevel[]): string;
10
+ export declare function buildCardTitle(node: SpaceNode): string;
11
+ export declare function buildCardDescription(node: SpaceNode): string;
@@ -0,0 +1,65 @@
1
+ // Color palette for hierarchy levels (distinct, visually appealing colors)
2
+ const COLOR_PALETTE = [
3
+ '#ff9999', // Light red
4
+ '#99ccff', // Light blue
5
+ '#99ff99', // Light green
6
+ '#ffcc99', // Light orange
7
+ '#cc99ff', // Light purple
8
+ '#ffccff', // Light pink
9
+ '#ccffcc', // Pale mint
10
+ '#ffffcc', // Light yellow
11
+ '#ccccff', // Light indigo
12
+ '#ffcccc', // Pale red
13
+ ];
14
+ const STATUS_ICONS = {
15
+ active: '*',
16
+ identified: '?',
17
+ wondering: '~',
18
+ exploring: '...',
19
+ paused: '||',
20
+ completed: 'ok',
21
+ archived: 'x',
22
+ };
23
+ /**
24
+ * Get card color based on type and hierarchy levels.
25
+ * Colors are assigned from palette based on level position.
26
+ *
27
+ * @param type - The node's type string
28
+ * @param hierarchyLevels - Hierarchy levels from metadata
29
+ */
30
+ export function getCardColor(type, hierarchyLevels) {
31
+ // Find the level index for this type
32
+ const levelIndex = hierarchyLevels.findIndex((level) => level.type === type);
33
+ if (levelIndex >= 0) {
34
+ // Assign color from palette based on level position
35
+ const color = COLOR_PALETTE[levelIndex % COLOR_PALETTE.length];
36
+ if (color)
37
+ return color;
38
+ }
39
+ // Type not found in hierarchy - return default gray
40
+ return '#e0e0e0';
41
+ }
42
+ export function buildCardTitle(node) {
43
+ const title = node.title;
44
+ const status = node.schemaData.status;
45
+ const priority = node.schemaData.priority;
46
+ const icon = status ? (STATUS_ICONS[status] ?? status) : '';
47
+ const prefix = icon ? `[${icon}] ` : '';
48
+ const suffix = priority ? ` (${priority})` : '';
49
+ return `${prefix}${title}${suffix}`;
50
+ }
51
+ export function buildCardDescription(node) {
52
+ const parts = [];
53
+ const type = node.schemaData.type;
54
+ const status = node.schemaData.status;
55
+ parts.push(`Type: ${type}`);
56
+ if (status)
57
+ parts.push(`Status: ${status}`);
58
+ const summary = node.schemaData.summary;
59
+ if (summary)
60
+ parts.push(`\n${summary}`);
61
+ const content = node.schemaData.content;
62
+ if (content)
63
+ parts.push(`\n${content}`);
64
+ return parts.join('\n');
65
+ }
@@ -0,0 +1,8 @@
1
+ import type { SpaceContext } from '../../types';
2
+ interface SyncOptions {
3
+ newFrame?: string;
4
+ dryRun?: boolean;
5
+ verbose?: boolean;
6
+ }
7
+ export declare function miroSync(context: SpaceContext, options: SyncOptions): Promise<void>;
8
+ export {};