memorix 1.1.7 → 1.1.8
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.
- package/CHANGELOG.md +13 -0
- package/CLAUDE.md +6 -1
- package/README.md +21 -0
- package/README.zh-CN.md +21 -0
- package/TEAM.md +86 -86
- package/dist/cli/index.js +730 -150
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/static/index.html +201 -201
- package/dist/dashboard/static/style.css +3584 -3584
- package/dist/index.js +15 -8
- package/dist/index.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +13 -0
- package/dist/memcode-runtime/package.json +4 -4
- package/dist/sdk.js +15 -8
- package/dist/sdk.js.map +1 -1
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +18 -0
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/SETUP.md +10 -0
- package/docs/dev-log/progress.txt +35 -26
- package/package.json +1 -1
- package/src/audit/index.ts +156 -156
- package/src/cli/commands/agent-integrations.ts +623 -0
- package/src/cli/commands/audit-list.ts +89 -89
- package/src/cli/commands/background.ts +659 -659
- package/src/cli/commands/cleanup.ts +255 -255
- package/src/cli/commands/doctor.ts +23 -0
- package/src/cli/commands/formation.ts +48 -48
- package/src/cli/commands/git-hook-install.ts +111 -111
- package/src/cli/commands/handoff.ts +66 -66
- package/src/cli/commands/hooks-status.ts +63 -63
- package/src/cli/commands/ingest-commit.ts +153 -153
- package/src/cli/commands/ingest-image.ts +73 -73
- package/src/cli/commands/ingest-log.ts +180 -180
- package/src/cli/commands/ingest.ts +44 -44
- package/src/cli/commands/integrate-shared.ts +15 -15
- package/src/cli/commands/lock.ts +96 -96
- package/src/cli/commands/message.ts +121 -121
- package/src/cli/commands/poll.ts +70 -70
- package/src/cli/commands/purge-all-memory.ts +85 -85
- package/src/cli/commands/purge-project-memory.ts +83 -83
- package/src/cli/commands/reasoning.ts +132 -132
- package/src/cli/commands/repair.ts +60 -0
- package/src/cli/commands/retention.ts +108 -108
- package/src/cli/commands/serve-shared.ts +118 -118
- package/src/cli/commands/setup.ts +3 -3
- package/src/cli/commands/skills.ts +123 -123
- package/src/cli/commands/task.ts +192 -192
- package/src/cli/commands/transfer.ts +73 -73
- package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
- package/src/cli/index.ts +3 -1
- package/src/cli/tui/ChatView.tsx +234 -234
- package/src/cli/tui/CommandBar.tsx +312 -312
- package/src/cli/tui/ContextRail.tsx +118 -118
- package/src/cli/tui/HeaderBar.tsx +72 -72
- package/src/cli/tui/LogoBanner.tsx +51 -51
- package/src/cli/tui/Panels.tsx +632 -632
- package/src/cli/tui/Sidebar.tsx +179 -179
- package/src/cli/tui/chat-service.ts +742 -742
- package/src/cli/tui/data.ts +547 -547
- package/src/cli/tui/index.ts +41 -41
- package/src/cli/tui/markdown-render.tsx +371 -371
- package/src/cli/tui/theme.ts +178 -178
- package/src/cli/tui/use-mouse.ts +157 -157
- package/src/cli/tui/useNavigation.ts +56 -56
- package/src/cli/update-checker.ts +211 -211
- package/src/cli/version.ts +7 -7
- package/src/cli/workbench.ts +1 -1
- package/src/codegraph/context-pack.ts +5 -1
- package/src/codegraph/lite-provider.ts +5 -1
- package/src/codegraph/project-context.ts +5 -1
- package/src/compact/token-budget.ts +74 -74
- package/src/config/behavior.ts +59 -59
- package/src/dashboard/project-classification.ts +64 -64
- package/src/dashboard/static/index.html +201 -201
- package/src/dashboard/static/style.css +3584 -3584
- package/src/embedding/fastembed-provider.ts +142 -142
- package/src/embedding/transformers-provider.ts +111 -111
- package/src/git/extractor.ts +209 -209
- package/src/git/hooks-path.ts +85 -85
- package/src/git/noise-filter.ts +210 -210
- package/src/hooks/installers/index.ts +4 -4
- package/src/hooks/official-skills.ts +1 -1
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/rules/memorix-agent-rules.md +2 -2
- package/src/hooks/significance-filter.ts +250 -250
- package/src/llm/memory-manager.ts +328 -328
- package/src/llm/provider.ts +885 -885
- package/src/llm/quality.ts +248 -248
- package/src/memory/attribution-guard.ts +249 -249
- package/src/memory/auto-relations.ts +107 -107
- package/src/memory/consolidation.ts +302 -302
- package/src/memory/disclosure-policy.ts +141 -141
- package/src/memory/entity-extractor.ts +197 -197
- package/src/memory/formation/evaluate.ts +217 -217
- package/src/memory/formation/extract.ts +361 -361
- package/src/memory/formation/index.ts +417 -417
- package/src/memory/formation/resolve.ts +344 -344
- package/src/memory/formation/types.ts +315 -315
- package/src/memory/freshness.ts +122 -122
- package/src/memory/graph.ts +197 -197
- package/src/memory/refs.ts +94 -94
- package/src/memory/retention.ts +433 -433
- package/src/memory/secret-filter.ts +79 -79
- package/src/memory/session.ts +523 -523
- package/src/multimodal/image-loader.ts +143 -143
- package/src/orchestrate/adapters/claude-stream.ts +192 -192
- package/src/orchestrate/adapters/claude.ts +111 -111
- package/src/orchestrate/adapters/codex-stream.ts +134 -134
- package/src/orchestrate/adapters/codex.ts +41 -41
- package/src/orchestrate/adapters/gemini-stream.ts +166 -166
- package/src/orchestrate/adapters/gemini.ts +42 -42
- package/src/orchestrate/adapters/index.ts +73 -73
- package/src/orchestrate/adapters/opencode-stream.ts +143 -143
- package/src/orchestrate/adapters/opencode.ts +47 -47
- package/src/orchestrate/adapters/spawn-helper.ts +286 -286
- package/src/orchestrate/adapters/types.ts +77 -77
- package/src/orchestrate/capability-router.ts +284 -284
- package/src/orchestrate/context-compact.ts +188 -188
- package/src/orchestrate/cost-tracker.ts +219 -219
- package/src/orchestrate/error-recovery.ts +191 -191
- package/src/orchestrate/evidence.ts +140 -140
- package/src/orchestrate/ledger.ts +110 -110
- package/src/orchestrate/memorix-bridge.ts +380 -380
- package/src/orchestrate/output-budget.ts +80 -80
- package/src/orchestrate/permission.ts +152 -152
- package/src/orchestrate/pipeline-trace.ts +131 -131
- package/src/orchestrate/prompt-builder.ts +155 -155
- package/src/orchestrate/ring-buffer.ts +37 -37
- package/src/orchestrate/task-graph.ts +389 -389
- package/src/orchestrate/verify-gate.ts +219 -219
- package/src/orchestrate/worktree.ts +232 -232
- package/src/project/aliases.ts +374 -374
- package/src/project/detector.ts +268 -268
- package/src/rules/adapters/claude-code.ts +99 -99
- package/src/rules/adapters/codex.ts +97 -97
- package/src/rules/adapters/copilot.ts +124 -124
- package/src/rules/adapters/cursor.ts +114 -114
- package/src/rules/adapters/kiro.ts +126 -126
- package/src/rules/adapters/trae.ts +56 -56
- package/src/rules/adapters/windsurf.ts +83 -83
- package/src/rules/syncer.ts +235 -235
- package/src/sdk.ts +327 -327
- package/src/search/intent-detector.ts +289 -289
- package/src/search/query-expansion.ts +52 -52
- package/src/server/formation-timeout.ts +27 -27
- package/src/skills/mini-skills.ts +386 -386
- package/src/store/chat-store.ts +119 -119
- package/src/store/file-lock.ts +100 -100
- package/src/store/graph-store.ts +249 -249
- package/src/store/mini-skill-store.ts +349 -349
- package/src/store/obs-store.ts +255 -255
- package/src/store/persistence-json.ts +212 -212
- package/src/store/persistence.ts +291 -291
- package/src/store/project-affinity.ts +195 -195
- package/src/store/session-store.ts +259 -259
- package/src/store/sqlite-store.ts +339 -339
- package/src/team/event-bus.ts +76 -76
- package/src/team/file-locks.ts +173 -173
- package/src/team/handoff.ts +167 -167
- package/src/team/messages.ts +203 -203
- package/src/team/poll.ts +132 -132
- package/src/team/tasks.ts +211 -211
- package/src/wiki/generator.ts +237 -237
- package/src/wiki/knowledge-graph.ts +334 -334
- package/src/wiki/types.ts +85 -85
- package/src/workspace/mcp-adapters/codex.ts +191 -191
- package/src/workspace/mcp-adapters/copilot.ts +105 -105
- package/src/workspace/mcp-adapters/cursor.ts +53 -53
- package/src/workspace/mcp-adapters/kiro.ts +64 -64
- package/src/workspace/mcp-adapters/opencode.ts +123 -123
- package/src/workspace/mcp-adapters/trae.ts +134 -134
- package/src/workspace/mcp-adapters/windsurf.ts +91 -91
- package/src/workspace/sanitizer.ts +60 -60
- 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
|
+
}
|