Skip to content

Commit 00914df

Browse files
committed
Release 2026.10317.12220
1 parent 2f601fd commit 00914df

19 files changed

Lines changed: 186 additions & 44 deletions

File tree

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.10314.11719"
12+
version = "2026.10317.12220"
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.10314.11719",
3+
"version": "2026.10317.12220",
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.10314.11719",
3+
"version": "2026.10317.12220",
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.10314.11719",
3+
"version": "2026.10317.12220",
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.10314.11719",
3+
"version": "2026.10317.12220",
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.10314.11719",
3+
"version": "2026.10317.12220",
44
"os": [
55
"win32"
66
],

cli/package.json

Lines changed: 2 additions & 2 deletions
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.10314.11719",
4+
"version": "2026.10317.12220",
55
"description": "TrueNine Memory Synchronization CLI",
66
"author": "TrueNine",
77
"license": "AGPL-3.0-only",
@@ -68,7 +68,7 @@
6868
"fast-glob": "catalog:",
6969
"fs-extra": "catalog:",
7070
"jiti": "2.6.1",
71-
"jsonc-parser": "catalog:",
71+
"json5": "catalog:",
7272
"lightningcss": "1.31.1",
7373
"picocolors": "catalog:",
7474
"picomatch": "catalog:",
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import type {InputCapabilityContext} from '../plugins/plugin-core'
2+
import * as fs from 'node:fs'
3+
import * as os from 'node:os'
4+
import * as path from 'node:path'
5+
import glob from 'fast-glob'
6+
import {describe, expect, it, vi} from 'vitest'
7+
import {mergeConfig} from '../config'
8+
import {AindexInputCapability} from './input-aindex'
9+
10+
function createLoggerMock(): {
11+
readonly logger: InputCapabilityContext['logger']
12+
readonly warn: ReturnType<typeof vi.fn>
13+
} {
14+
const warn = vi.fn()
15+
16+
return {
17+
logger: {
18+
error: vi.fn(),
19+
warn,
20+
info: vi.fn(),
21+
debug: vi.fn(),
22+
trace: vi.fn(),
23+
fatal: vi.fn()
24+
},
25+
warn
26+
}
27+
}
28+
29+
function createContext(
30+
tempWorkspace: string,
31+
logger: InputCapabilityContext['logger']
32+
): InputCapabilityContext {
33+
return {
34+
logger,
35+
fs,
36+
path,
37+
glob,
38+
userConfigOptions: mergeConfig({workspaceDir: tempWorkspace}),
39+
dependencyContext: {}
40+
} as InputCapabilityContext
41+
}
42+
43+
function createAindexProject(tempWorkspace: string, projectName: string): {
44+
readonly configDir: string
45+
} {
46+
const distProjectDir = path.join(tempWorkspace, 'aindex', 'dist', 'app', projectName)
47+
const configDir = path.join(tempWorkspace, 'aindex', 'app', projectName)
48+
49+
fs.mkdirSync(distProjectDir, {recursive: true})
50+
fs.mkdirSync(configDir, {recursive: true})
51+
52+
return {configDir}
53+
}
54+
55+
describe('aindex input capability project config loading', () => {
56+
it('loads project.json5 using JSON5 features without any jsonc fallback', () => {
57+
const tempWorkspace = fs.mkdtempSync(path.join(os.tmpdir(), 'tnmsc-aindex-project-json5-'))
58+
const {logger, warn} = createLoggerMock()
59+
60+
try {
61+
const {configDir} = createAindexProject(tempWorkspace, 'project-a')
62+
fs.writeFileSync(path.join(configDir, 'project.json5'), [
63+
'{',
64+
' // JSON5 comment support',
65+
' includeSeries: [\'alpha\'],',
66+
' subSeries: {',
67+
' skills: [\'ship-*\'],',
68+
' },',
69+
'}',
70+
''
71+
].join('\n'), 'utf8')
72+
73+
const result = new AindexInputCapability().collect(createContext(tempWorkspace, logger))
74+
const project = result.workspace?.projects[0]
75+
76+
expect(project?.name).toBe('project-a')
77+
expect(project?.projectConfig).toEqual({
78+
includeSeries: ['alpha'],
79+
subSeries: {
80+
skills: ['ship-*']
81+
}
82+
})
83+
expect(warn).not.toHaveBeenCalled()
84+
}
85+
finally {
86+
fs.rmSync(tempWorkspace, {recursive: true, force: true})
87+
}
88+
})
89+
90+
it('ignores legacy project.jsonc after the hard cut', () => {
91+
const tempWorkspace = fs.mkdtempSync(path.join(os.tmpdir(), 'tnmsc-aindex-project-jsonc-legacy-'))
92+
const {logger, warn} = createLoggerMock()
93+
94+
try {
95+
const {configDir} = createAindexProject(tempWorkspace, 'project-b')
96+
fs.writeFileSync(path.join(configDir, 'project.jsonc'), '{"includeSeries":["legacy"]}\n', 'utf8')
97+
98+
const result = new AindexInputCapability().collect(createContext(tempWorkspace, logger))
99+
const project = result.workspace?.projects[0]
100+
101+
expect(project?.name).toBe('project-b')
102+
expect(project?.projectConfig).toBeUndefined()
103+
expect(warn).not.toHaveBeenCalled()
104+
}
105+
finally {
106+
fs.rmSync(tempWorkspace, {recursive: true, force: true})
107+
}
108+
})
109+
110+
it('emits JSON5 diagnostics for invalid project.json5 syntax', () => {
111+
const tempWorkspace = fs.mkdtempSync(path.join(os.tmpdir(), 'tnmsc-aindex-project-json5-invalid-'))
112+
const {logger, warn} = createLoggerMock()
113+
114+
try {
115+
const {configDir} = createAindexProject(tempWorkspace, 'project-c')
116+
fs.writeFileSync(path.join(configDir, 'project.json5'), '{includeSeries: [\'broken\',]} trailing', 'utf8')
117+
118+
const result = new AindexInputCapability().collect(createContext(tempWorkspace, logger))
119+
const project = result.workspace?.projects[0]
120+
const diagnostic = warn.mock.calls[0]?.[0]
121+
122+
expect(project?.name).toBe('project-c')
123+
expect(project?.projectConfig).toBeUndefined()
124+
expect(warn).toHaveBeenCalledTimes(1)
125+
expect(diagnostic).toEqual(expect.objectContaining({
126+
code: 'AINDEX_PROJECT_JSON5_INVALID',
127+
title: 'Failed to parse project.json5 for project-c',
128+
exactFix: ['Fix the JSON5 syntax in project.json5 and rerun tnmsc.']
129+
}))
130+
}
131+
finally {
132+
fs.rmSync(tempWorkspace, {recursive: true, force: true})
133+
}
134+
})
135+
})

cli/src/inputs/input-aindex.ts

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type {InputCapabilityContext, InputCollectedContext, Project, ProjectConfig, Workspace} from '../plugins/plugin-core'
22

3-
import {parse as parseJsonc} from 'jsonc-parser'
3+
import JSON5 from 'json5'
44
import {
55
buildConfigDiagnostic,
66
buildFileOperationDiagnostic,
@@ -9,6 +9,8 @@ import {
99
import {AbstractInputCapability, FilePathKind} from '../plugins/plugin-core'
1010

1111
export class AindexInputCapability extends AbstractInputCapability {
12+
private static readonly projectConfigFileName = 'project.json5'
13+
1214
constructor() {
1315
super('AindexInputCapability')
1416
}
@@ -21,41 +23,51 @@ export class AindexInputCapability extends AbstractInputCapability {
2123
path: InputCapabilityContext['path'],
2224
logger: InputCapabilityContext['logger']
2325
): ProjectConfig | undefined {
24-
const configPath = path.join(aindexDir, srcPath, projectName, 'project.jsonc')
26+
const configPath = path.join(
27+
aindexDir,
28+
srcPath,
29+
projectName,
30+
AindexInputCapability.projectConfigFileName
31+
)
2532
if (!fs.existsSync(configPath)) return void 0
33+
2634
try {
2735
const raw = fs.readFileSync(configPath, 'utf8')
28-
const errors: import('jsonc-parser').ParseError[] = []
29-
const result = parseJsonc(raw, errors) as ProjectConfig
30-
if (errors.length > 0) {
36+
37+
try {
38+
return JSON5.parse(raw) as ProjectConfig
39+
}
40+
catch (e) {
3141
logger.warn(buildConfigDiagnostic({
32-
code: 'AINDEX_PROJECT_JSONC_INVALID',
33-
title: `Failed to parse project.jsonc for ${projectName}`,
42+
code: 'AINDEX_PROJECT_JSON5_INVALID',
43+
title: `Failed to parse ${AindexInputCapability.projectConfigFileName} for ${projectName}`,
3444
reason: diagnosticLines(
35-
`tnmsc could not parse the project.jsonc file for "${projectName}".`,
36-
`The file contains ${errors.length} JSONC parser issue(s).`
45+
`tnmsc could not parse the ${AindexInputCapability.projectConfigFileName} file for "${projectName}".`,
46+
`Underlying error: ${e instanceof Error ? e.message : String(e)}`
3747
),
3848
configPath,
39-
exactFix: diagnosticLines('Fix the JSONC syntax in project.jsonc and rerun tnmsc.'),
49+
exactFix: diagnosticLines(
50+
`Fix the JSON5 syntax in ${AindexInputCapability.projectConfigFileName} and rerun tnmsc.`
51+
),
4052
details: {
4153
projectName,
42-
errors
54+
errorMessage: e instanceof Error ? e.message : String(e)
4355
}
4456
}))
4557
return void 0
4658
}
47-
return result
48-
} catch (e) {
59+
}
60+
catch (e) {
4961
logger.warn(buildConfigDiagnostic({
50-
code: 'AINDEX_PROJECT_JSONC_READ_FAILED',
51-
title: `Failed to load project.jsonc for ${projectName}`,
62+
code: 'AINDEX_PROJECT_JSON5_READ_FAILED',
63+
title: `Failed to load ${AindexInputCapability.projectConfigFileName} for ${projectName}`,
5264
reason: diagnosticLines(
53-
`tnmsc could not read or parse the project.jsonc file for "${projectName}".`,
65+
`tnmsc could not read the ${AindexInputCapability.projectConfigFileName} file for "${projectName}".`,
5466
`Underlying error: ${e instanceof Error ? e.message : String(e)}`
5567
),
5668
configPath,
5769
exactFix: diagnosticLines(
58-
'Ensure project.jsonc exists, is readable, and contains valid JSONC.'
70+
`Ensure ${AindexInputCapability.projectConfigFileName} exists, is readable, and contains valid JSON5.`
5971
),
6072
details: {
6173
projectName,

doc/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-docs",
3-
"version": "2026.10314.11719",
3+
"version": "2026.10317.12220",
44
"private": true,
55
"description": "Documentation site for @truenine/memory-sync, built with Next.js 16 and MDX.",
66
"engines": {

0 commit comments

Comments
 (0)