memorix 1.2.2 → 1.2.3

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 (139) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/TEAM.md +86 -86
  3. package/dist/cli/index.js +34 -18
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/index.js +17 -8
  6. package/dist/index.js.map +1 -1
  7. package/dist/maintenance-runner.js.map +1 -1
  8. package/dist/memcode-runtime/CHANGELOG.md +7 -0
  9. package/dist/sdk.js +17 -8
  10. package/dist/sdk.js.map +1 -1
  11. package/docs/DESIGN_DECISIONS.md +357 -357
  12. package/docs/dev-log/progress.txt +18 -8
  13. package/package.json +1 -1
  14. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  15. package/src/audit/index.ts +156 -156
  16. package/src/cli/commands/audit-list.ts +89 -89
  17. package/src/cli/commands/background.ts +659 -659
  18. package/src/cli/commands/formation.ts +48 -48
  19. package/src/cli/commands/git-hook-install.ts +111 -111
  20. package/src/cli/commands/handoff.ts +54 -54
  21. package/src/cli/commands/hooks-status.ts +63 -63
  22. package/src/cli/commands/ingest-commit.ts +153 -153
  23. package/src/cli/commands/ingest-image.ts +66 -66
  24. package/src/cli/commands/ingest-log.ts +180 -180
  25. package/src/cli/commands/ingest.ts +44 -44
  26. package/src/cli/commands/integrate-shared.ts +15 -15
  27. package/src/cli/commands/lock.ts +82 -82
  28. package/src/cli/commands/message.ts +104 -104
  29. package/src/cli/commands/poll.ts +58 -58
  30. package/src/cli/commands/purge-all-memory.ts +85 -85
  31. package/src/cli/commands/purge-project-memory.ts +83 -83
  32. package/src/cli/commands/reasoning.ts +118 -118
  33. package/src/cli/commands/serve-shared.ts +118 -118
  34. package/src/cli/commands/session.ts +15 -7
  35. package/src/cli/commands/skills.ts +114 -114
  36. package/src/cli/commands/task.ts +167 -167
  37. package/src/cli/commands/transfer.ts +47 -47
  38. package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
  39. package/src/cli/tui/ChatView.tsx +234 -234
  40. package/src/cli/tui/CommandBar.tsx +312 -312
  41. package/src/cli/tui/ContextRail.tsx +118 -118
  42. package/src/cli/tui/HeaderBar.tsx +72 -72
  43. package/src/cli/tui/LogoBanner.tsx +51 -51
  44. package/src/cli/tui/Sidebar.tsx +179 -179
  45. package/src/cli/tui/index.ts +41 -41
  46. package/src/cli/tui/markdown-render.tsx +371 -371
  47. package/src/cli/tui/session-service.ts +3 -2
  48. package/src/cli/tui/use-mouse.ts +157 -157
  49. package/src/cli/tui/useNavigation.ts +56 -56
  50. package/src/cli/update-checker.ts +211 -211
  51. package/src/cli/version.ts +7 -7
  52. package/src/cli/workbench.ts +1 -1
  53. package/src/compact/token-budget.ts +74 -74
  54. package/src/dashboard/project-classification.ts +64 -64
  55. package/src/embedding/fastembed-provider.ts +142 -142
  56. package/src/embedding/transformers-provider.ts +111 -111
  57. package/src/git/extractor.ts +209 -209
  58. package/src/git/hooks-path.ts +85 -85
  59. package/src/hooks/pattern-detector.ts +173 -173
  60. package/src/hooks/significance-filter.ts +250 -250
  61. package/src/llm/memory-manager.ts +328 -328
  62. package/src/llm/provider.ts +885 -885
  63. package/src/llm/quality.ts +248 -248
  64. package/src/memory/attribution-guard.ts +249 -249
  65. package/src/memory/disclosure-policy.ts +135 -135
  66. package/src/memory/entity-extractor.ts +197 -197
  67. package/src/memory/formation/evaluate.ts +217 -217
  68. package/src/memory/formation/extract.ts +361 -361
  69. package/src/memory/formation/index.ts +417 -417
  70. package/src/memory/formation/resolve.ts +344 -344
  71. package/src/memory/formation/types.ts +315 -315
  72. package/src/memory/freshness.ts +122 -122
  73. package/src/memory/graph.ts +197 -197
  74. package/src/memory/refs.ts +94 -94
  75. package/src/memory/secret-filter.ts +79 -79
  76. package/src/memory/session.ts +24 -9
  77. package/src/multimodal/image-loader.ts +143 -143
  78. package/src/orchestrate/adapters/claude-stream.ts +192 -192
  79. package/src/orchestrate/adapters/claude.ts +111 -111
  80. package/src/orchestrate/adapters/codex-stream.ts +134 -134
  81. package/src/orchestrate/adapters/codex.ts +41 -41
  82. package/src/orchestrate/adapters/gemini-stream.ts +166 -166
  83. package/src/orchestrate/adapters/gemini.ts +42 -42
  84. package/src/orchestrate/adapters/index.ts +73 -73
  85. package/src/orchestrate/adapters/opencode-stream.ts +143 -143
  86. package/src/orchestrate/adapters/opencode.ts +47 -47
  87. package/src/orchestrate/adapters/spawn-helper.ts +286 -286
  88. package/src/orchestrate/adapters/types.ts +77 -77
  89. package/src/orchestrate/capability-router.ts +284 -284
  90. package/src/orchestrate/context-compact.ts +188 -188
  91. package/src/orchestrate/cost-tracker.ts +219 -219
  92. package/src/orchestrate/error-recovery.ts +191 -191
  93. package/src/orchestrate/evidence.ts +140 -140
  94. package/src/orchestrate/ledger.ts +110 -110
  95. package/src/orchestrate/memorix-bridge.ts +343 -343
  96. package/src/orchestrate/output-budget.ts +80 -80
  97. package/src/orchestrate/permission.ts +152 -152
  98. package/src/orchestrate/pipeline-trace.ts +131 -131
  99. package/src/orchestrate/prompt-builder.ts +155 -155
  100. package/src/orchestrate/ring-buffer.ts +37 -37
  101. package/src/orchestrate/task-graph.ts +389 -389
  102. package/src/orchestrate/worktree.ts +232 -232
  103. package/src/project/aliases.ts +374 -374
  104. package/src/project/detector.ts +268 -268
  105. package/src/rules/adapters/claude-code.ts +99 -99
  106. package/src/rules/adapters/codex.ts +97 -97
  107. package/src/rules/adapters/copilot.ts +124 -124
  108. package/src/rules/adapters/cursor.ts +114 -114
  109. package/src/rules/adapters/kiro.ts +126 -126
  110. package/src/rules/adapters/trae.ts +56 -56
  111. package/src/rules/adapters/windsurf.ts +83 -83
  112. package/src/rules/syncer.ts +235 -235
  113. package/src/sdk.ts +299 -299
  114. package/src/search/intent-detector.ts +289 -289
  115. package/src/search/query-expansion.ts +52 -52
  116. package/src/server/formation-timeout.ts +27 -27
  117. package/src/server.ts +7 -2
  118. package/src/skills/mini-skills.ts +386 -386
  119. package/src/store/chat-store.ts +119 -119
  120. package/src/store/graph-store.ts +249 -249
  121. package/src/store/mini-skill-store.ts +349 -349
  122. package/src/store/persistence-json.ts +212 -212
  123. package/src/store/persistence.ts +291 -291
  124. package/src/store/project-affinity.ts +195 -195
  125. package/src/team/event-bus.ts +76 -76
  126. package/src/team/file-locks.ts +173 -173
  127. package/src/team/handoff.ts +161 -161
  128. package/src/team/messages.ts +203 -203
  129. package/src/team/poll.ts +132 -132
  130. package/src/team/tasks.ts +211 -211
  131. package/src/workspace/mcp-adapters/codex.ts +191 -191
  132. package/src/workspace/mcp-adapters/copilot.ts +105 -105
  133. package/src/workspace/mcp-adapters/cursor.ts +53 -53
  134. package/src/workspace/mcp-adapters/kiro.ts +64 -64
  135. package/src/workspace/mcp-adapters/opencode.ts +123 -123
  136. package/src/workspace/mcp-adapters/trae.ts +134 -134
  137. package/src/workspace/mcp-adapters/windsurf.ts +91 -91
  138. package/src/workspace/sanitizer.ts +60 -60
  139. package/src/workspace/workflow-sync.ts +131 -131
@@ -1,114 +1,114 @@
1
- /**
2
- * Cursor Rule Format Adapter
3
- *
4
- * Parses and generates rules in Cursor's formats:
5
- * - .cursor/rules/*.mdc (Markdown + frontmatter with description, alwaysApply, globs)
6
- * - .cursorrules (legacy plain text)
7
- * - AGENTS.md (pure Markdown)
8
- *
9
- * Source: Cursor official documentation on Project Rules.
10
- */
11
-
12
- import matter from 'gray-matter';
13
- import type { RuleFormatAdapter, UnifiedRule, RuleSource } from '../../types.js';
14
- import { hashContent, generateRuleId } from '../utils.js';
15
-
16
- export class CursorAdapter implements RuleFormatAdapter {
17
- readonly source: RuleSource = 'cursor';
18
-
19
- readonly filePatterns = [
20
- '.cursor/rules/*.mdc',
21
- '.cursorrules',
22
- 'AGENTS.md',
23
- ];
24
-
25
- parse(filePath: string, content: string): UnifiedRule[] {
26
- if (filePath.endsWith('.mdc')) {
27
- return this.parseMdc(filePath, content);
28
- }
29
- if (filePath === '.cursorrules' || filePath.endsWith('/.cursorrules')) {
30
- return this.parseLegacy(filePath, content);
31
- }
32
- if (filePath.endsWith('AGENTS.md')) {
33
- return this.parseAgentsMd(filePath, content);
34
- }
35
- return [];
36
- }
37
-
38
- generate(rules: UnifiedRule[]): { filePath: string; content: string }[] {
39
- return rules.map((rule, i) => {
40
- const fm: Record<string, unknown> = {};
41
- if (rule.description) fm.description = rule.description;
42
- if (rule.alwaysApply !== undefined) fm.alwaysApply = rule.alwaysApply;
43
- if (rule.paths && rule.paths.length > 0) fm.globs = rule.paths;
44
-
45
- const fileName = rule.id
46
- .replace(/^cursor:/, '')
47
- .replace(/[^a-zA-Z0-9-_]/g, '-')
48
- || `rule-${i}`;
49
-
50
- const body = Object.keys(fm).length > 0
51
- ? matter.stringify(rule.content, fm)
52
- : rule.content;
53
-
54
- return {
55
- filePath: `.cursor/rules/${fileName}.mdc`,
56
- content: body,
57
- };
58
- });
59
- }
60
-
61
- private parseMdc(filePath: string, content: string): UnifiedRule[] {
62
- const { data, content: body } = matter(content);
63
- const trimmed = body.trim();
64
- if (!trimmed) return [];
65
-
66
- const globs = data.globs as string[] | undefined;
67
- const hasGlobs = Array.isArray(globs) && globs.length > 0;
68
- const alwaysApply = data.alwaysApply === true;
69
-
70
- let scope: UnifiedRule['scope'] = 'project';
71
- if (alwaysApply) scope = 'global';
72
- else if (hasGlobs) scope = 'path-specific';
73
-
74
- return [{
75
- id: generateRuleId('cursor', filePath),
76
- content: trimmed,
77
- description: data.description as string | undefined,
78
- source: 'cursor',
79
- scope,
80
- paths: hasGlobs ? globs : undefined,
81
- alwaysApply,
82
- priority: alwaysApply ? 10 : 5,
83
- hash: hashContent(trimmed),
84
- }];
85
- }
86
-
87
- private parseLegacy(filePath: string, content: string): UnifiedRule[] {
88
- const trimmed = content.trim();
89
- if (!trimmed) return [];
90
-
91
- return [{
92
- id: generateRuleId('cursor', filePath),
93
- content: trimmed,
94
- source: 'cursor',
95
- scope: 'project',
96
- priority: 3,
97
- hash: hashContent(trimmed),
98
- }];
99
- }
100
-
101
- private parseAgentsMd(filePath: string, content: string): UnifiedRule[] {
102
- const trimmed = content.trim();
103
- if (!trimmed) return [];
104
-
105
- return [{
106
- id: generateRuleId('cursor', filePath),
107
- content: trimmed,
108
- source: 'cursor',
109
- scope: 'project',
110
- priority: 5,
111
- hash: hashContent(trimmed),
112
- }];
113
- }
114
- }
1
+ /**
2
+ * Cursor Rule Format Adapter
3
+ *
4
+ * Parses and generates rules in Cursor's formats:
5
+ * - .cursor/rules/*.mdc (Markdown + frontmatter with description, alwaysApply, globs)
6
+ * - .cursorrules (legacy plain text)
7
+ * - AGENTS.md (pure Markdown)
8
+ *
9
+ * Source: Cursor official documentation on Project Rules.
10
+ */
11
+
12
+ import matter from 'gray-matter';
13
+ import type { RuleFormatAdapter, UnifiedRule, RuleSource } from '../../types.js';
14
+ import { hashContent, generateRuleId } from '../utils.js';
15
+
16
+ export class CursorAdapter implements RuleFormatAdapter {
17
+ readonly source: RuleSource = 'cursor';
18
+
19
+ readonly filePatterns = [
20
+ '.cursor/rules/*.mdc',
21
+ '.cursorrules',
22
+ 'AGENTS.md',
23
+ ];
24
+
25
+ parse(filePath: string, content: string): UnifiedRule[] {
26
+ if (filePath.endsWith('.mdc')) {
27
+ return this.parseMdc(filePath, content);
28
+ }
29
+ if (filePath === '.cursorrules' || filePath.endsWith('/.cursorrules')) {
30
+ return this.parseLegacy(filePath, content);
31
+ }
32
+ if (filePath.endsWith('AGENTS.md')) {
33
+ return this.parseAgentsMd(filePath, content);
34
+ }
35
+ return [];
36
+ }
37
+
38
+ generate(rules: UnifiedRule[]): { filePath: string; content: string }[] {
39
+ return rules.map((rule, i) => {
40
+ const fm: Record<string, unknown> = {};
41
+ if (rule.description) fm.description = rule.description;
42
+ if (rule.alwaysApply !== undefined) fm.alwaysApply = rule.alwaysApply;
43
+ if (rule.paths && rule.paths.length > 0) fm.globs = rule.paths;
44
+
45
+ const fileName = rule.id
46
+ .replace(/^cursor:/, '')
47
+ .replace(/[^a-zA-Z0-9-_]/g, '-')
48
+ || `rule-${i}`;
49
+
50
+ const body = Object.keys(fm).length > 0
51
+ ? matter.stringify(rule.content, fm)
52
+ : rule.content;
53
+
54
+ return {
55
+ filePath: `.cursor/rules/${fileName}.mdc`,
56
+ content: body,
57
+ };
58
+ });
59
+ }
60
+
61
+ private parseMdc(filePath: string, content: string): UnifiedRule[] {
62
+ const { data, content: body } = matter(content);
63
+ const trimmed = body.trim();
64
+ if (!trimmed) return [];
65
+
66
+ const globs = data.globs as string[] | undefined;
67
+ const hasGlobs = Array.isArray(globs) && globs.length > 0;
68
+ const alwaysApply = data.alwaysApply === true;
69
+
70
+ let scope: UnifiedRule['scope'] = 'project';
71
+ if (alwaysApply) scope = 'global';
72
+ else if (hasGlobs) scope = 'path-specific';
73
+
74
+ return [{
75
+ id: generateRuleId('cursor', filePath),
76
+ content: trimmed,
77
+ description: data.description as string | undefined,
78
+ source: 'cursor',
79
+ scope,
80
+ paths: hasGlobs ? globs : undefined,
81
+ alwaysApply,
82
+ priority: alwaysApply ? 10 : 5,
83
+ hash: hashContent(trimmed),
84
+ }];
85
+ }
86
+
87
+ private parseLegacy(filePath: string, content: string): UnifiedRule[] {
88
+ const trimmed = content.trim();
89
+ if (!trimmed) return [];
90
+
91
+ return [{
92
+ id: generateRuleId('cursor', filePath),
93
+ content: trimmed,
94
+ source: 'cursor',
95
+ scope: 'project',
96
+ priority: 3,
97
+ hash: hashContent(trimmed),
98
+ }];
99
+ }
100
+
101
+ private parseAgentsMd(filePath: string, content: string): UnifiedRule[] {
102
+ const trimmed = content.trim();
103
+ if (!trimmed) return [];
104
+
105
+ return [{
106
+ id: generateRuleId('cursor', filePath),
107
+ content: trimmed,
108
+ source: 'cursor',
109
+ scope: 'project',
110
+ priority: 5,
111
+ hash: hashContent(trimmed),
112
+ }];
113
+ }
114
+ }
@@ -1,126 +1,126 @@
1
- /**
2
- * Kiro Rule Format Adapter
3
- *
4
- * Parses and generates rules in Kiro's formats:
5
- * - .kiro/steering/*.md (Markdown steering rules with optional frontmatter)
6
- * - AGENTS.md (always included, pure Markdown)
7
- *
8
- * Source: Kiro official documentation on Steering Rules.
9
- * https://kiro.dev/docs/steering/
10
- *
11
- * Kiro uses ".kiro/steering/" for project-level rules
12
- * and "~/.kiro/steering/" for user-level (global) rules.
13
- *
14
- * Frontmatter inclusion modes:
15
- * - always (default): loaded into every interaction
16
- * - fileMatch + fileMatchPattern: conditional on file globs
17
- * - manual: on-demand via #name in chat
18
- * - auto + name + description: auto-included when relevant
19
- */
20
-
21
- import matter from 'gray-matter';
22
- import type { RuleFormatAdapter, UnifiedRule, RuleSource } from '../../types.js';
23
- import { hashContent, generateRuleId } from '../utils.js';
24
-
25
- /** Kiro inclusion mode values */
26
- type KiroInclusion = 'always' | 'fileMatch' | 'manual' | 'auto';
27
-
28
- export class KiroAdapter implements RuleFormatAdapter {
29
- readonly source: RuleSource = 'kiro';
30
-
31
- readonly filePatterns = [
32
- '.kiro/steering/*.md',
33
- 'AGENTS.md',
34
- ];
35
-
36
- parse(filePath: string, content: string): UnifiedRule[] {
37
- if (filePath.includes('.kiro/steering/')) {
38
- return this.parseSteeringRule(filePath, content);
39
- }
40
- if (filePath.endsWith('AGENTS.md')) {
41
- return this.parseAgentsMd(filePath, content);
42
- }
43
- return [];
44
- }
45
-
46
- generate(rules: UnifiedRule[]): { filePath: string; content: string }[] {
47
- return rules.map((rule, i) => {
48
- const fm: Record<string, unknown> = {};
49
- if (rule.description) fm.description = rule.description;
50
-
51
- // Map unified scope → Kiro inclusion mode
52
- if (rule.paths && rule.paths.length > 0) {
53
- fm.inclusion = 'fileMatch';
54
- fm.fileMatchPattern = rule.paths.length === 1
55
- ? rule.paths[0]
56
- : rule.paths;
57
- } else if (rule.alwaysApply) {
58
- fm.inclusion = 'always';
59
- }
60
-
61
- const fileName = rule.id
62
- .replace(/^kiro:/, '')
63
- .replace(/[^a-zA-Z0-9-_]/g, '-')
64
- || `rule-${i}`;
65
-
66
- const body = Object.keys(fm).length > 0
67
- ? matter.stringify(rule.content, fm)
68
- : rule.content;
69
-
70
- return {
71
- filePath: `.kiro/steering/${fileName}.md`,
72
- content: body,
73
- };
74
- });
75
- }
76
-
77
- private parseSteeringRule(filePath: string, content: string): UnifiedRule[] {
78
- const { data, content: body } = matter(content);
79
- const trimmed = body.trim();
80
- if (!trimmed) return [];
81
-
82
- // Kiro uses "inclusion" field: always | fileMatch | manual | auto
83
- const inclusion = (data.inclusion as KiroInclusion | undefined) ?? 'always';
84
- const alwaysApply = inclusion === 'always' || inclusion === 'auto';
85
-
86
- // fileMatchPattern can be a string or string[]
87
- let paths: string[] | undefined;
88
- if (inclusion === 'fileMatch' && data.fileMatchPattern) {
89
- paths = Array.isArray(data.fileMatchPattern)
90
- ? data.fileMatchPattern
91
- : [data.fileMatchPattern];
92
- }
93
-
94
- let scope: UnifiedRule['scope'] = 'project';
95
- if (alwaysApply) scope = 'global';
96
- else if (paths && paths.length > 0) scope = 'path-specific';
97
-
98
- return [{
99
- id: generateRuleId('kiro', filePath),
100
- content: trimmed,
101
- description: data.description as string | undefined,
102
- source: 'kiro',
103
- scope,
104
- paths,
105
- alwaysApply,
106
- priority: alwaysApply ? 10 : 5,
107
- hash: hashContent(trimmed),
108
- }];
109
- }
110
-
111
- private parseAgentsMd(filePath: string, content: string): UnifiedRule[] {
112
- const trimmed = content.trim();
113
- if (!trimmed) return [];
114
-
115
- return [{
116
- id: generateRuleId('kiro', filePath),
117
- content: trimmed,
118
- source: 'kiro',
119
- scope: 'project',
120
- alwaysApply: true,
121
- priority: 10,
122
- hash: hashContent(trimmed),
123
- }];
124
- }
125
- }
126
-
1
+ /**
2
+ * Kiro Rule Format Adapter
3
+ *
4
+ * Parses and generates rules in Kiro's formats:
5
+ * - .kiro/steering/*.md (Markdown steering rules with optional frontmatter)
6
+ * - AGENTS.md (always included, pure Markdown)
7
+ *
8
+ * Source: Kiro official documentation on Steering Rules.
9
+ * https://kiro.dev/docs/steering/
10
+ *
11
+ * Kiro uses ".kiro/steering/" for project-level rules
12
+ * and "~/.kiro/steering/" for user-level (global) rules.
13
+ *
14
+ * Frontmatter inclusion modes:
15
+ * - always (default): loaded into every interaction
16
+ * - fileMatch + fileMatchPattern: conditional on file globs
17
+ * - manual: on-demand via #name in chat
18
+ * - auto + name + description: auto-included when relevant
19
+ */
20
+
21
+ import matter from 'gray-matter';
22
+ import type { RuleFormatAdapter, UnifiedRule, RuleSource } from '../../types.js';
23
+ import { hashContent, generateRuleId } from '../utils.js';
24
+
25
+ /** Kiro inclusion mode values */
26
+ type KiroInclusion = 'always' | 'fileMatch' | 'manual' | 'auto';
27
+
28
+ export class KiroAdapter implements RuleFormatAdapter {
29
+ readonly source: RuleSource = 'kiro';
30
+
31
+ readonly filePatterns = [
32
+ '.kiro/steering/*.md',
33
+ 'AGENTS.md',
34
+ ];
35
+
36
+ parse(filePath: string, content: string): UnifiedRule[] {
37
+ if (filePath.includes('.kiro/steering/')) {
38
+ return this.parseSteeringRule(filePath, content);
39
+ }
40
+ if (filePath.endsWith('AGENTS.md')) {
41
+ return this.parseAgentsMd(filePath, content);
42
+ }
43
+ return [];
44
+ }
45
+
46
+ generate(rules: UnifiedRule[]): { filePath: string; content: string }[] {
47
+ return rules.map((rule, i) => {
48
+ const fm: Record<string, unknown> = {};
49
+ if (rule.description) fm.description = rule.description;
50
+
51
+ // Map unified scope → Kiro inclusion mode
52
+ if (rule.paths && rule.paths.length > 0) {
53
+ fm.inclusion = 'fileMatch';
54
+ fm.fileMatchPattern = rule.paths.length === 1
55
+ ? rule.paths[0]
56
+ : rule.paths;
57
+ } else if (rule.alwaysApply) {
58
+ fm.inclusion = 'always';
59
+ }
60
+
61
+ const fileName = rule.id
62
+ .replace(/^kiro:/, '')
63
+ .replace(/[^a-zA-Z0-9-_]/g, '-')
64
+ || `rule-${i}`;
65
+
66
+ const body = Object.keys(fm).length > 0
67
+ ? matter.stringify(rule.content, fm)
68
+ : rule.content;
69
+
70
+ return {
71
+ filePath: `.kiro/steering/${fileName}.md`,
72
+ content: body,
73
+ };
74
+ });
75
+ }
76
+
77
+ private parseSteeringRule(filePath: string, content: string): UnifiedRule[] {
78
+ const { data, content: body } = matter(content);
79
+ const trimmed = body.trim();
80
+ if (!trimmed) return [];
81
+
82
+ // Kiro uses "inclusion" field: always | fileMatch | manual | auto
83
+ const inclusion = (data.inclusion as KiroInclusion | undefined) ?? 'always';
84
+ const alwaysApply = inclusion === 'always' || inclusion === 'auto';
85
+
86
+ // fileMatchPattern can be a string or string[]
87
+ let paths: string[] | undefined;
88
+ if (inclusion === 'fileMatch' && data.fileMatchPattern) {
89
+ paths = Array.isArray(data.fileMatchPattern)
90
+ ? data.fileMatchPattern
91
+ : [data.fileMatchPattern];
92
+ }
93
+
94
+ let scope: UnifiedRule['scope'] = 'project';
95
+ if (alwaysApply) scope = 'global';
96
+ else if (paths && paths.length > 0) scope = 'path-specific';
97
+
98
+ return [{
99
+ id: generateRuleId('kiro', filePath),
100
+ content: trimmed,
101
+ description: data.description as string | undefined,
102
+ source: 'kiro',
103
+ scope,
104
+ paths,
105
+ alwaysApply,
106
+ priority: alwaysApply ? 10 : 5,
107
+ hash: hashContent(trimmed),
108
+ }];
109
+ }
110
+
111
+ private parseAgentsMd(filePath: string, content: string): UnifiedRule[] {
112
+ const trimmed = content.trim();
113
+ if (!trimmed) return [];
114
+
115
+ return [{
116
+ id: generateRuleId('kiro', filePath),
117
+ content: trimmed,
118
+ source: 'kiro',
119
+ scope: 'project',
120
+ alwaysApply: true,
121
+ priority: 10,
122
+ hash: hashContent(trimmed),
123
+ }];
124
+ }
125
+ }
126
+
@@ -1,56 +1,56 @@
1
- /**
2
- * Trae IDE Rule Format Adapter
3
- *
4
- * Parses and generates rules in Trae's format:
5
- * - .trae/rules/project_rules.md (project-level rules, plain Markdown)
6
- *
7
- * Trae also supports user-level rules (user_rules.md) created via the UI,
8
- * but those are managed by Trae itself and not project-scoped.
9
- *
10
- * Rules are plain Markdown — no frontmatter, no special syntax.
11
- * Project rules override personal rules when there are conflicts.
12
- *
13
- * Source: https://docs.trae.ai/ide/rules
14
- */
15
-
16
- import type { RuleFormatAdapter, UnifiedRule, RuleSource } from '../../types.js';
17
- import { hashContent, generateRuleId } from '../utils.js';
18
-
19
- export class TraeAdapter implements RuleFormatAdapter {
20
- readonly source: RuleSource = 'trae';
21
-
22
- readonly filePatterns = [
23
- '.trae/rules/project_rules.md',
24
- '.trae/rules/*.md',
25
- ];
26
-
27
- parse(filePath: string, content: string): UnifiedRule[] {
28
- const trimmed = content.trim();
29
- if (!trimmed) return [];
30
-
31
- const isProjectRules = filePath.includes('project_rules.md');
32
-
33
- return [{
34
- id: generateRuleId('trae', filePath),
35
- content: trimmed,
36
- description: isProjectRules ? 'Trae project rules' : undefined,
37
- source: 'trae',
38
- scope: 'project',
39
- alwaysApply: true,
40
- priority: isProjectRules ? 10 : 5,
41
- hash: hashContent(trimmed),
42
- }];
43
- }
44
-
45
- generate(rules: UnifiedRule[]): { filePath: string; content: string }[] {
46
- if (rules.length === 0) return [];
47
-
48
- // Trae uses a single project_rules.md file — merge all rules into one
49
- const combined = rules.map(r => r.content).join('\n\n---\n\n');
50
-
51
- return [{
52
- filePath: '.trae/rules/project_rules.md',
53
- content: combined,
54
- }];
55
- }
56
- }
1
+ /**
2
+ * Trae IDE Rule Format Adapter
3
+ *
4
+ * Parses and generates rules in Trae's format:
5
+ * - .trae/rules/project_rules.md (project-level rules, plain Markdown)
6
+ *
7
+ * Trae also supports user-level rules (user_rules.md) created via the UI,
8
+ * but those are managed by Trae itself and not project-scoped.
9
+ *
10
+ * Rules are plain Markdown — no frontmatter, no special syntax.
11
+ * Project rules override personal rules when there are conflicts.
12
+ *
13
+ * Source: https://docs.trae.ai/ide/rules
14
+ */
15
+
16
+ import type { RuleFormatAdapter, UnifiedRule, RuleSource } from '../../types.js';
17
+ import { hashContent, generateRuleId } from '../utils.js';
18
+
19
+ export class TraeAdapter implements RuleFormatAdapter {
20
+ readonly source: RuleSource = 'trae';
21
+
22
+ readonly filePatterns = [
23
+ '.trae/rules/project_rules.md',
24
+ '.trae/rules/*.md',
25
+ ];
26
+
27
+ parse(filePath: string, content: string): UnifiedRule[] {
28
+ const trimmed = content.trim();
29
+ if (!trimmed) return [];
30
+
31
+ const isProjectRules = filePath.includes('project_rules.md');
32
+
33
+ return [{
34
+ id: generateRuleId('trae', filePath),
35
+ content: trimmed,
36
+ description: isProjectRules ? 'Trae project rules' : undefined,
37
+ source: 'trae',
38
+ scope: 'project',
39
+ alwaysApply: true,
40
+ priority: isProjectRules ? 10 : 5,
41
+ hash: hashContent(trimmed),
42
+ }];
43
+ }
44
+
45
+ generate(rules: UnifiedRule[]): { filePath: string; content: string }[] {
46
+ if (rules.length === 0) return [];
47
+
48
+ // Trae uses a single project_rules.md file — merge all rules into one
49
+ const combined = rules.map(r => r.content).join('\n\n---\n\n');
50
+
51
+ return [{
52
+ filePath: '.trae/rules/project_rules.md',
53
+ content: combined,
54
+ }];
55
+ }
56
+ }