Skip to content

Commit f34da7d

Browse files
authored
Merge pull request #107 from TrueNine/dev
Update md-compiler MDX-to-Markdown pipeline and tests
2 parents b728091 + 669f409 commit f34da7d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+2894
-936
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ members = [
99
]
1010

1111
[workspace.package]
12-
version = "2026.10329.110"
12+
version = "2026.10330.108"
1313
edition = "2024"
1414
license = "AGPL-3.0-only"
1515
authors = ["TrueNine"]

cli/npm/darwin-arm64/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@truenine/memory-sync-cli-darwin-arm64",
3-
"version": "2026.10329.110",
3+
"version": "2026.10330.108",
44
"os": [
55
"darwin"
66
],

cli/npm/darwin-x64/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@truenine/memory-sync-cli-darwin-x64",
3-
"version": "2026.10329.110",
3+
"version": "2026.10330.108",
44
"os": [
55
"darwin"
66
],

cli/npm/linux-arm64-gnu/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@truenine/memory-sync-cli-linux-arm64-gnu",
3-
"version": "2026.10329.110",
3+
"version": "2026.10330.108",
44
"os": [
55
"linux"
66
],

cli/npm/linux-x64-gnu/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@truenine/memory-sync-cli-linux-x64-gnu",
3-
"version": "2026.10329.110",
3+
"version": "2026.10330.108",
44
"os": [
55
"linux"
66
],

cli/npm/win32-x64-msvc/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@truenine/memory-sync-cli-win32-x64-msvc",
3-
"version": "2026.10329.110",
3+
"version": "2026.10330.108",
44
"os": [
55
"win32"
66
],

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.10329.110",
4+
"version": "2026.10330.108",
55
"description": "TrueNine Memory Synchronization CLI",
66
"author": "TrueNine",
77
"license": "AGPL-3.0-only",

cli/scripts/benchmark-cleanup.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ delete process.env['VITEST']
1010
delete process.env['VITEST_WORKER_ID']
1111

1212
const cleanupModule = await import('../src/commands/CleanupUtils')
13-
const fallbackModule = await import('../src/commands/CleanupUtils.fallback')
1413
const pluginCore = await import('../src/plugins/plugin-core')
1514

1615
function createMockLogger(): ILogger {
@@ -136,15 +135,9 @@ async function main(): Promise<void> {
136135
const iterations = 25
137136

138137
process.stdout.write(`cleanup benchmark iterations=${iterations}\n`)
139-
const fallbackAvg = await measure('fallback-plan', iterations, async () => {
140-
await fallbackModule.collectDeletionTargets([plugin], cleanCtx)
141-
})
142-
const nativeAvg = await measure('native-plan', iterations, async () => {
138+
await measure('native-plan', iterations, async () => {
143139
await cleanupModule.collectDeletionTargets([plugin], cleanCtx)
144140
})
145-
146-
const delta = nativeAvg - fallbackAvg
147-
process.stdout.write(`delta=${delta.toFixed(2)}ms (${((delta / fallbackAvg) * 100).toFixed(2)}%)\n`)
148141
}
149142
finally {
150143
fs.rmSync(tempDir, {recursive: true, force: true})

cli/scripts/cleanup-native-smoke.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ delete process.env['VITEST']
99
delete process.env['VITEST_WORKER_ID']
1010

1111
const cleanupModule = await import('../src/commands/CleanupUtils')
12-
const fallbackModule = await import('../src/commands/CleanupUtils.fallback')
1312
const pluginCore = await import('../src/plugins/plugin-core')
1413

1514
function createMockLogger(): ILogger {
@@ -106,21 +105,18 @@ async function main(): Promise<void> {
106105
const cleanCtx = createCleanContext(workspaceDir)
107106

108107
const nativePlan = await cleanupModule.collectDeletionTargets([plugin], cleanCtx)
109-
const fallbackPlan = await fallbackModule.collectDeletionTargets([plugin], cleanCtx)
110-
111-
const sortPaths = (value: {filesToDelete: string[], dirsToDelete: string[], excludedScanGlobs: string[]}) => ({
112-
...value,
113-
filesToDelete: [...value.filesToDelete].sort(),
114-
dirsToDelete: [...value.dirsToDelete].sort(),
115-
excludedScanGlobs: [...value.excludedScanGlobs].sort()
116-
})
117-
118-
if (JSON.stringify(sortPaths(nativePlan)) !== JSON.stringify(sortPaths(fallbackPlan))) {
119-
throw new Error(`Native cleanup plan mismatch.\nNative: ${JSON.stringify(nativePlan, null, 2)}\nFallback: ${JSON.stringify(fallbackPlan, null, 2)}`)
108+
expectSetEqual(nativePlan.filesToDelete, [rootOutput, childOutput], 'native cleanup plan files')
109+
expectSetEqual(nativePlan.dirsToDelete, [
110+
legacySkillDir,
111+
path.join(workspaceDir, 'project-a', 'commands'),
112+
path.join(workspaceDir, 'project-a')
113+
], 'native cleanup plan directories')
114+
if (nativePlan.violations.length > 0 || nativePlan.conflicts.length > 0) {
115+
throw new Error(`Unexpected native cleanup plan: ${JSON.stringify(nativePlan, null, 2)}`)
120116
}
121117

122118
const result = await cleanupModule.performCleanup([plugin], cleanCtx, createMockLogger())
123-
if (result.deletedFiles !== 2 || result.deletedDirs !== 1 || result.errors.length > 0) {
119+
if (result.deletedFiles !== 2 || result.deletedDirs !== 3 || result.errors.length > 0) {
124120
throw new Error(`Unexpected native cleanup result: ${JSON.stringify(result, null, 2)}`)
125121
}
126122

@@ -138,4 +134,12 @@ async function main(): Promise<void> {
138134
}
139135
}
140136

137+
function expectSetEqual(actual: readonly string[], expected: readonly string[], label: string): void {
138+
const actualSorted = [...actual].sort()
139+
const expectedSorted = [...expected].sort()
140+
if (JSON.stringify(actualSorted) !== JSON.stringify(expectedSorted)) {
141+
throw new Error(`Unexpected ${label}: ${JSON.stringify(actualSorted)} !== ${JSON.stringify(expectedSorted)}`)
142+
}
143+
}
144+
141145
await main()
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import type * as fs from 'node:fs'
2+
import {resolveAbsolutePath} from '../ProtectedDeletionGuard'
3+
4+
const EMPTY_DIRECTORY_SCAN_EXCLUDED_BASENAMES = new Set([
5+
'.git',
6+
'node_modules',
7+
'dist',
8+
'target',
9+
'.next',
10+
'.turbo',
11+
'coverage',
12+
'.nyc_output',
13+
'.cache',
14+
'.vite',
15+
'.vite-temp',
16+
'.pnpm-store',
17+
'.yarn',
18+
'.idea',
19+
'.vscode'
20+
])
21+
22+
export interface WorkspaceEmptyDirectoryPlan {
23+
readonly emptyDirsToDelete: string[]
24+
}
25+
26+
export interface WorkspaceEmptyDirectoryPlannerOptions {
27+
readonly fs: typeof import('node:fs')
28+
readonly path: typeof import('node:path')
29+
readonly workspaceDir: string
30+
readonly filesToDelete: readonly string[]
31+
readonly dirsToDelete: readonly string[]
32+
}
33+
34+
function shouldSkipEmptyDirectoryTree(
35+
nodePath: typeof import('node:path'),
36+
workspaceDir: string,
37+
currentDir: string
38+
): boolean {
39+
if (currentDir === workspaceDir) return false
40+
return EMPTY_DIRECTORY_SCAN_EXCLUDED_BASENAMES.has(nodePath.basename(currentDir))
41+
}
42+
43+
export function planWorkspaceEmptyDirectoryCleanup(
44+
options: WorkspaceEmptyDirectoryPlannerOptions
45+
): WorkspaceEmptyDirectoryPlan {
46+
const workspaceDir = resolveAbsolutePath(options.workspaceDir)
47+
const filesToDelete = new Set(options.filesToDelete.map(resolveAbsolutePath))
48+
const dirsToDelete = new Set(options.dirsToDelete.map(resolveAbsolutePath))
49+
const emptyDirsToDelete = new Set<string>()
50+
51+
const collectEmptyDirectories = (currentDir: string): boolean => {
52+
if (dirsToDelete.has(currentDir)) return true
53+
if (shouldSkipEmptyDirectoryTree(options.path, workspaceDir, currentDir)) return false
54+
55+
let entries: fs.Dirent[]
56+
try {
57+
entries = options.fs.readdirSync(currentDir, {withFileTypes: true})
58+
}
59+
catch {
60+
return false
61+
}
62+
63+
let hasRetainedEntries = false
64+
65+
for (const entry of entries) {
66+
const entryPath = resolveAbsolutePath(options.path.join(currentDir, entry.name))
67+
68+
if (dirsToDelete.has(entryPath)) continue
69+
70+
if (entry.isDirectory()) {
71+
if (shouldSkipEmptyDirectoryTree(options.path, workspaceDir, entryPath)) {
72+
hasRetainedEntries = true
73+
continue
74+
}
75+
76+
if (collectEmptyDirectories(entryPath)) {
77+
emptyDirsToDelete.add(entryPath)
78+
continue
79+
}
80+
81+
hasRetainedEntries = true
82+
continue
83+
}
84+
85+
if (filesToDelete.has(entryPath)) continue
86+
hasRetainedEntries = true
87+
}
88+
89+
return !hasRetainedEntries
90+
}
91+
92+
collectEmptyDirectories(workspaceDir)
93+
94+
return {
95+
emptyDirsToDelete: [...emptyDirsToDelete].sort((a, b) => a.localeCompare(b))
96+
}
97+
}

0 commit comments

Comments
 (0)