Skip to content

Commit 23a8066

Browse files
authored
Merge pull request #23 from TrueNine/dev
feat: subSeries 重构为子目录前缀映射
2 parents 18e892b + 83fe32b commit 23a8066

19 files changed

+436
-344
lines changed

cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@truenine/memory-sync-cli",
33
"type": "module",
4-
"version": "2026.10219.10305",
4+
"version": "2026.10219.10518",
55
"description": "TrueNine Memory Synchronization CLI",
66
"author": "TrueNine",
77
"license": "AGPL-3.0-only",

cli/public/tnmsc.example.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "2026.10219.10305",
2+
"version": "2026.10219.10518",
33
"workspaceDir": "~/project",
44
"shadowSourceProject": {
55
"name": "tnmsc-shadow",

cli/src/plugins/ClaudeCodeCLIOutputPlugin.projectConfig.test.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -124,30 +124,6 @@ describe('claudeCodeCLIOutputPlugin - projectConfig filtering', () => {
124124
expect(fileNames).toContain('rule-test-rule2.md')
125125
})
126126

127-
it('should expand include with subSeries', async () => {
128-
const rules = [
129-
createMockRulePrompt('test', 'rule1', 'uniapp', 'project'),
130-
createMockRulePrompt('test', 'rule2', 'uniapp3', 'project'),
131-
createMockRulePrompt('test', 'rule3', 'vue', 'project')
132-
]
133-
const projects = [
134-
createMockProject('proj1', tempDir, 'proj1', {
135-
rules: {
136-
include: ['uniapp'],
137-
subSeries: {uniapp: ['uniapp3']}
138-
}
139-
})
140-
]
141-
const ctx = createMockContext(tempDir, rules, projects)
142-
143-
const results = await plugin.registerProjectOutputFiles(ctx)
144-
const fileNames = collectFileNames(results)
145-
146-
expect(fileNames).toContain('rule-test-rule1.md')
147-
expect(fileNames).toContain('rule-test-rule2.md')
148-
expect(fileNames).not.toContain('rule-test-rule3.md')
149-
})
150-
151127
it('should include rules without seriName regardless of include filter', async () => {
152128
const rules = [
153129
createMockRulePrompt('test', 'rule1', void 0, 'project'),

cli/src/plugins/ClaudeCodeCLIOutputPlugin.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type {OutputPluginContext, OutputWriteContext, RulePrompt, WriteResults} from '@/types'
22
import type {RelativePath} from '@/types/FileSystemTypes'
33
import * as path from 'node:path'
4-
import {filterRulesByProjectConfig} from '@/utils/ruleFilter'
4+
import {applySubSeriesGlobPrefix, filterRulesByProjectConfig} from '@/utils/ruleFilter'
55
import {BaseCLIOutputPlugin} from './BaseCLIOutputPlugin'
66

77
const PROJECT_MEMORY_FILE = 'CLAUDE.md'
@@ -52,8 +52,11 @@ export class ClaudeCodeCLIOutputPlugin extends BaseCLIOutputPlugin {
5252
if (rules == null || rules.length === 0) return results
5353
for (const project of ctx.collectedInputContext.workspace.projects) {
5454
if (project.dirFromWorkspacePath == null) continue
55-
const projectRules = filterRulesByProjectConfig(
56-
rules.filter(r => this.normalizeRuleScope(r) === 'project'),
55+
const projectRules = applySubSeriesGlobPrefix(
56+
filterRulesByProjectConfig(
57+
rules.filter(r => this.normalizeRuleScope(r) === 'project'),
58+
project.projectConfig
59+
),
5760
project.projectConfig
5861
)
5962
if (projectRules.length === 0) continue
@@ -69,8 +72,11 @@ export class ClaudeCodeCLIOutputPlugin extends BaseCLIOutputPlugin {
6972
if (rules == null || rules.length === 0) return results
7073
for (const project of ctx.collectedInputContext.workspace.projects) {
7174
if (project.dirFromWorkspacePath == null) continue
72-
const projectRules = filterRulesByProjectConfig(
73-
rules.filter(r => this.normalizeRuleScope(r) === 'project'),
75+
const projectRules = applySubSeriesGlobPrefix(
76+
filterRulesByProjectConfig(
77+
rules.filter(r => this.normalizeRuleScope(r) === 'project'),
78+
project.projectConfig
79+
),
7480
project.projectConfig
7581
)
7682
for (const rule of projectRules) {
@@ -103,8 +109,11 @@ export class ClaudeCodeCLIOutputPlugin extends BaseCLIOutputPlugin {
103109
const ruleResults = []
104110
for (const project of ctx.collectedInputContext.workspace.projects) {
105111
if (project.dirFromWorkspacePath == null) continue
106-
const projectRules = filterRulesByProjectConfig(
107-
rules.filter(r => this.normalizeRuleScope(r) === 'project'),
112+
const projectRules = applySubSeriesGlobPrefix(
113+
filterRulesByProjectConfig(
114+
rules.filter(r => this.normalizeRuleScope(r) === 'project'),
115+
project.projectConfig
116+
),
108117
project.projectConfig
109118
)
110119
if (projectRules.length === 0) continue

cli/src/plugins/CursorOutputPlugin.projectConfig.test.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -123,30 +123,6 @@ describe('cursorOutputPlugin - projectConfig filtering', () => {
123123
expect(fileNames).toContain('rule-test-rule2.mdc')
124124
})
125125

126-
it('should expand include with subSeries', async () => {
127-
const rules = [
128-
createMockRulePrompt('test', 'rule1', 'uniapp', 'project'),
129-
createMockRulePrompt('test', 'rule2', 'uniapp3', 'project'),
130-
createMockRulePrompt('test', 'rule3', 'vue', 'project')
131-
]
132-
const projects = [
133-
createMockProject('proj1', tempDir, 'proj1', {
134-
rules: {
135-
include: ['uniapp'],
136-
subSeries: {uniapp: ['uniapp3']}
137-
}
138-
})
139-
]
140-
const ctx = createMockContext(tempDir, rules, projects)
141-
142-
const results = await plugin.registerProjectOutputFiles(ctx)
143-
const fileNames = collectFileNames(results)
144-
145-
expect(fileNames).toContain('rule-test-rule1.mdc')
146-
expect(fileNames).toContain('rule-test-rule2.mdc')
147-
expect(fileNames).not.toContain('rule-test-rule3.mdc')
148-
})
149-
150126
it('should include rules without seriName regardless of include filter', async () => {
151127
const rules = [
152128
createMockRulePrompt('test', 'rule1', void 0, 'project'),

cli/src/plugins/CursorOutputPlugin.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import * as fs from 'node:fs'
1414
import * as path from 'node:path'
1515
import {buildMarkdownWithFrontMatter} from '@truenine/md-compiler/markdown'
1616
import {FilePathKind} from '@/types'
17-
import {filterRulesByProjectConfig} from '@/utils/ruleFilter'
17+
import {applySubSeriesGlobPrefix, filterRulesByProjectConfig} from '@/utils/ruleFilter'
1818
import {AbstractOutputPlugin} from './AbstractOutputPlugin'
1919

2020
const GLOBAL_CONFIG_DIR = '.cursor'
@@ -257,8 +257,11 @@ export class CursorOutputPlugin extends AbstractOutputPlugin {
257257
for (const project of workspace.projects) {
258258
const projectDir = project.dirFromWorkspacePath
259259
if (projectDir == null) continue
260-
const projectRules = filterRulesByProjectConfig(
261-
rules.filter(r => this.normalizeRuleScope(r) === 'project'),
260+
const projectRules = applySubSeriesGlobPrefix(
261+
filterRulesByProjectConfig(
262+
rules.filter(r => this.normalizeRuleScope(r) === 'project'),
263+
project.projectConfig
264+
),
262265
project.projectConfig
263266
)
264267
for (const rule of projectRules) {
@@ -344,8 +347,11 @@ export class CursorOutputPlugin extends AbstractOutputPlugin {
344347
for (const project of workspace.projects) {
345348
const projectDir = project.dirFromWorkspacePath
346349
if (projectDir == null) continue
347-
const projectRules = filterRulesByProjectConfig(
348-
rules.filter(r => this.normalizeRuleScope(r) === 'project'),
350+
const projectRules = applySubSeriesGlobPrefix(
351+
filterRulesByProjectConfig(
352+
rules.filter(r => this.normalizeRuleScope(r) === 'project'),
353+
project.projectConfig
354+
),
349355
project.projectConfig
350356
)
351357
if (projectRules.length === 0) continue

cli/src/plugins/KiroCLIOutputPlugin.projectConfig.test.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -123,30 +123,6 @@ describe('kiroCLIOutputPlugin - projectConfig filtering', () => {
123123
expect(fileNames).toContain('rule-test-rule2.md')
124124
})
125125

126-
it('should expand include with subSeries', async () => {
127-
const rules = [
128-
createMockRulePrompt('test', 'rule1', 'uniapp', 'project'),
129-
createMockRulePrompt('test', 'rule2', 'uniapp3', 'project'),
130-
createMockRulePrompt('test', 'rule3', 'vue', 'project')
131-
]
132-
const projects = [
133-
createMockProject('proj1', tempDir, 'proj1', {
134-
rules: {
135-
include: ['uniapp'],
136-
subSeries: {uniapp: ['uniapp3']}
137-
}
138-
})
139-
]
140-
const ctx = createMockContext(tempDir, rules, projects)
141-
142-
const results = await plugin.registerProjectOutputFiles(ctx)
143-
const fileNames = collectFileNames(results)
144-
145-
expect(fileNames).toContain('rule-test-rule1.md')
146-
expect(fileNames).toContain('rule-test-rule2.md')
147-
expect(fileNames).not.toContain('rule-test-rule3.md')
148-
})
149-
150126
it('should include rules without seriName regardless of include filter', async () => {
151127
const rules = [
152128
createMockRulePrompt('test', 'rule1', void 0, 'project'),

cli/src/plugins/KiroCLIOutputPlugin.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type {
1212
WriteResults
1313
} from '@/types'
1414
import type {RelativePath} from '@/types/FileSystemTypes'
15-
import {filterRulesByProjectConfig} from '@/utils/ruleFilter'
15+
import {applySubSeriesGlobPrefix, filterRulesByProjectConfig} from '@/utils/ruleFilter'
1616
import {AbstractOutputPlugin} from './AbstractOutputPlugin'
1717
import {KiroPowersRegistryWriter} from './registry/KiroPowersRegistryWriter'
1818

@@ -97,8 +97,11 @@ export class KiroCLIOutputPlugin extends AbstractOutputPlugin {
9797
}
9898

9999
if (rules != null && rules.length > 0) {
100-
const projectRules = filterRulesByProjectConfig(
101-
rules.filter(r => r.scope === 'project'),
100+
const projectRules = applySubSeriesGlobPrefix(
101+
filterRulesByProjectConfig(
102+
rules.filter(r => r.scope === 'project'),
103+
project.projectConfig
104+
),
102105
project.projectConfig
103106
)
104107
for (const rule of projectRules) {
@@ -228,8 +231,11 @@ export class KiroCLIOutputPlugin extends AbstractOutputPlugin {
228231
}
229232

230233
if (rules != null && rules.length > 0) {
231-
const projectRules = filterRulesByProjectConfig(
232-
rules.filter(r => r.scope === 'project'),
234+
const projectRules = applySubSeriesGlobPrefix(
235+
filterRulesByProjectConfig(
236+
rules.filter(r => r.scope === 'project'),
237+
project.projectConfig
238+
),
233239
project.projectConfig
234240
)
235241
for (const rule of projectRules) fileResults.push(await this.writeRuleSteeringFile(ctx, project, rule))

cli/src/plugins/WindsurfOutputPlugin.projectConfig.test.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -123,30 +123,6 @@ describe('windsurfOutputPlugin - projectConfig filtering', () => {
123123
expect(fileNames).toContain('rule-test-rule2.md')
124124
})
125125

126-
it('should expand include with subSeries', async () => {
127-
const rules = [
128-
createMockRulePrompt('test', 'rule1', 'uniapp', 'project'),
129-
createMockRulePrompt('test', 'rule2', 'uniapp3', 'project'),
130-
createMockRulePrompt('test', 'rule3', 'vue', 'project')
131-
]
132-
const projects = [
133-
createMockProject('proj1', tempDir, 'proj1', {
134-
rules: {
135-
include: ['uniapp'],
136-
subSeries: {uniapp: ['uniapp3']}
137-
}
138-
})
139-
]
140-
const ctx = createMockContext(tempDir, rules, projects)
141-
142-
const results = await plugin.registerProjectOutputFiles(ctx)
143-
const fileNames = collectFileNames(results)
144-
145-
expect(fileNames).toContain('rule-test-rule1.md')
146-
expect(fileNames).toContain('rule-test-rule2.md')
147-
expect(fileNames).not.toContain('rule-test-rule3.md')
148-
})
149-
150126
it('should include rules without seriName regardless of include filter', async () => {
151127
const rules = [
152128
createMockRulePrompt('test', 'rule1', void 0, 'project'),

cli/src/plugins/WindsurfOutputPlugin.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import * as fs from 'node:fs'
1313
import * as path from 'node:path'
1414
import {buildMarkdownWithFrontMatter} from '@truenine/md-compiler/markdown'
1515
import {FilePathKind} from '@/types'
16-
import {filterRulesByProjectConfig} from '@/utils/ruleFilter'
16+
import {applySubSeriesGlobPrefix, filterRulesByProjectConfig} from '@/utils/ruleFilter'
1717
import {AbstractOutputPlugin} from './AbstractOutputPlugin'
1818

1919
const CODEIUM_WINDSURF_DIR = '.codeium/windsurf'
@@ -225,8 +225,11 @@ export class WindsurfOutputPlugin extends AbstractOutputPlugin {
225225
for (const project of workspace.projects) {
226226
const projectDir = project.dirFromWorkspacePath
227227
if (projectDir == null) continue
228-
const projectRules = filterRulesByProjectConfig(
229-
rules.filter(r => r.scope === 'project'),
228+
const projectRules = applySubSeriesGlobPrefix(
229+
filterRulesByProjectConfig(
230+
rules.filter(r => r.scope === 'project'),
231+
project.projectConfig
232+
),
230233
project.projectConfig
231234
)
232235
if (projectRules.length === 0) continue
@@ -250,8 +253,11 @@ export class WindsurfOutputPlugin extends AbstractOutputPlugin {
250253
for (const project of workspace.projects) {
251254
const projectDir = project.dirFromWorkspacePath
252255
if (projectDir == null) continue
253-
const projectRules = filterRulesByProjectConfig(
254-
rules.filter(r => r.scope === 'project'),
256+
const projectRules = applySubSeriesGlobPrefix(
257+
filterRulesByProjectConfig(
258+
rules.filter(r => r.scope === 'project'),
259+
project.projectConfig
260+
),
255261
project.projectConfig
256262
)
257263
for (const rule of projectRules) {
@@ -280,8 +286,11 @@ export class WindsurfOutputPlugin extends AbstractOutputPlugin {
280286
for (const project of workspace.projects) {
281287
const projectDir = project.dirFromWorkspacePath
282288
if (projectDir == null) continue
283-
const projectRules = filterRulesByProjectConfig(
284-
rules.filter(r => r.scope === 'project'),
289+
const projectRules = applySubSeriesGlobPrefix(
290+
filterRulesByProjectConfig(
291+
rules.filter(r => r.scope === 'project'),
292+
project.projectConfig
293+
),
285294
project.projectConfig
286295
)
287296
if (projectRules.length === 0) continue

0 commit comments

Comments
 (0)