Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions .githooks/sync-versions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ if (eslintConfigVersion) {
console.log(`🔄 Catalog @truenine/eslint10-config: ^${eslintConfigVersion}`)
}

const packages: readonly PackageEntry[] = [
const topLevelWorkspacePackages: readonly PackageEntry[] = [
{ path: 'cli/package.json', name: 'cli' },
{ path: 'mcp/package.json', name: 'mcp' },
{ path: 'gui/package.json', name: 'gui' },
{ path: 'doc/package.json', name: 'doc' },
]
Expand Down Expand Up @@ -109,7 +110,7 @@ const cliNpmPackages = discoverNpmSubPackages('cli', 'cli-napi')

let changed = false

for (const pkg of [...packages, ...libraryPackages, ...packagesPackages, ...cliNpmPackages]) {
for (const pkg of [...topLevelWorkspacePackages, ...libraryPackages, ...packagesPackages, ...cliNpmPackages]) {
const fullPath = resolve(pkg.path)
try {
const content = readFileSync(fullPath, 'utf-8').replace(/^\uFEFF/, '')
Expand Down Expand Up @@ -199,12 +200,10 @@ if (changed) {
const filesToStage = [
'package.json',
'Cargo.toml',
'cli/package.json',
'gui/package.json',
'doc/package.json',
'gui/src-tauri/Cargo.toml',
'gui/src-tauri/tauri.conf.json',
'libraries/init-bundle/public/public/tnmsc.example.json',
...topLevelWorkspacePackages.map(p => p.path),
...libraryPackages.map(p => p.path),
...packagesPackages.map(p => p.path),
...cliNpmPackages.map(p => p.path),
Expand Down
87 changes: 72 additions & 15 deletions .github/workflows/release-cli.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Release CLI
name: Release Packages

env:
NODE_VERSION: '25'
Expand Down Expand Up @@ -27,6 +27,7 @@ on:
- .github/workflows/release-*.yml
- assets/**
- cli/**
- mcp/**
- gui/**
- libraries/**
- scripts/**
Expand All @@ -47,7 +48,9 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 10
outputs:
publish: ${{ steps.check.outputs.publish }}
publish_cli: ${{ steps.check.outputs.publish_cli }}
publish_mcp: ${{ steps.check.outputs.publish_mcp }}
publish_npm: ${{ steps.check.outputs.publish_npm }}
version: ${{ steps.check.outputs.version }}
steps:
- uses: actions/checkout@v4
Expand All @@ -58,16 +61,46 @@ jobs:
- name: Check if should publish
id: check
run: |
version=$(jq -r '.version' cli/package.json)
name=$(jq -r '.name' cli/package.json)
npm_version=$(npm view "$name" version --registry https://registry.npmjs.org/ 2>/dev/null || echo "")
if [[ "$version" != "$npm_version" ]]; then
echo "Version $version not published to npm, will publish"
echo "publish=true" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
check_publish_state() {
local package_json_path="$1"
local output_key="$2"
local version
local name
local npm_version

version=$(jq -r '.version' "$package_json_path")
name=$(jq -r '.name' "$package_json_path")
npm_version=$(npm view "$name" version --registry https://registry.npmjs.org/ 2>/dev/null || echo "")

if [[ "$version" != "$npm_version" ]]; then
echo "$name@$version is not published to npm, will publish"
echo "${output_key}=true" >> "$GITHUB_OUTPUT"
return 0
fi

echo "$name@$version already published to npm, skipping"
echo "${output_key}=false" >> "$GITHUB_OUTPUT"
return 1
}

version=$(jq -r '.version' package.json)
echo "version=$version" >> "$GITHUB_OUTPUT"

cli_needs_publish=false
mcp_needs_publish=false

if check_publish_state cli/package.json publish_cli; then
cli_needs_publish=true
fi

if check_publish_state mcp/package.json publish_mcp; then
mcp_needs_publish=true
fi

if [[ "$cli_needs_publish" == "true" || "$mcp_needs_publish" == "true" ]]; then
echo "publish_npm=true" >> "$GITHUB_OUTPUT"
else
echo "Version $version already published to npm, skipping"
echo "publish=false" >> "$GITHUB_OUTPUT"
echo "publish_npm=false" >> "$GITHUB_OUTPUT"
fi

# 1.5. GUI 版本检查(独立于 npm,检查 GitHub Release)
Expand Down Expand Up @@ -101,7 +134,7 @@ jobs:
# 2. 构建 NAPI 二进制(5 平台矩阵)
build-napi:
needs: check-version
if: needs.check-version.outputs.publish == 'true'
if: needs.check-version.outputs.publish_cli == 'true'
timeout-minutes: 45
strategy:
fail-fast: false
Expand Down Expand Up @@ -196,7 +229,7 @@ jobs:
# 3. 收集并发布 NAPI 平台子包到 npm
publish-napi:
needs: [check-version, build-napi]
if: needs.check-version.outputs.publish == 'true'
if: needs.check-version.outputs.publish_cli == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
Expand Down Expand Up @@ -271,7 +304,7 @@ jobs:
# 4. 架构包就绪后,发布主包到 npm
publish-cli:
needs: [check-version, publish-napi]
if: needs.check-version.outputs.publish == 'true'
if: needs.check-version.outputs.publish_cli == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
Expand All @@ -290,11 +323,35 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

# 4.5. CLI 可用后,发布 MCP 包到 npm
publish-mcp:
needs: [check-version, publish-cli]
if: |
needs.check-version.outputs.publish_mcp == 'true' &&
(needs.publish-cli.result == 'success' || needs.publish-cli.result == 'skipped')
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-node-pnpm
- name: Setup npm registry
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: https://registry.npmjs.org/
- name: Build
run: pnpm exec turbo run build --filter=@truenine/memory-sync-mcp
- name: Publish to npm
working-directory: ./mcp
run: pnpm publish --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

# 5. 构建 CLI 独立二进制(仅 artifact,不发 Release)
build-binary:
needs: [check-version, check-gui-version, publish-napi]
if: |
(needs.check-version.outputs.publish == 'true' || needs.check-gui-version.outputs.should_release == 'true') &&
(needs.check-version.outputs.publish_cli == 'true' || needs.check-gui-version.outputs.should_release == 'true') &&
(needs.publish-napi.result == 'success' || needs.publish-napi.result == 'skipped')
timeout-minutes: 60
strategy:
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ members = [
]

[workspace.package]
version = "2026.10317.12338"
version = "2026.10318.10339"
edition = "2024"
license = "AGPL-3.0-only"
authors = ["TrueNine"]
Expand Down
2 changes: 1 addition & 1 deletion cli/npm/darwin-arm64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@truenine/memory-sync-cli-darwin-arm64",
"version": "2026.10317.12338",
"version": "2026.10318.10339",
"os": [
"darwin"
],
Expand Down
2 changes: 1 addition & 1 deletion cli/npm/darwin-x64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@truenine/memory-sync-cli-darwin-x64",
"version": "2026.10317.12338",
"version": "2026.10318.10339",
"os": [
"darwin"
],
Expand Down
2 changes: 1 addition & 1 deletion cli/npm/linux-arm64-gnu/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@truenine/memory-sync-cli-linux-arm64-gnu",
"version": "2026.10317.12338",
"version": "2026.10318.10339",
"os": [
"linux"
],
Expand Down
2 changes: 1 addition & 1 deletion cli/npm/linux-x64-gnu/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@truenine/memory-sync-cli-linux-x64-gnu",
"version": "2026.10317.12338",
"version": "2026.10318.10339",
"os": [
"linux"
],
Expand Down
2 changes: 1 addition & 1 deletion cli/npm/win32-x64-msvc/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@truenine/memory-sync-cli-win32-x64-msvc",
"version": "2026.10317.12338",
"version": "2026.10318.10339",
"os": [
"win32"
],
Expand Down
6 changes: 4 additions & 2 deletions cli/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@truenine/memory-sync-cli",
"type": "module",
"version": "2026.10317.12338",
"version": "2026.10318.10339",
"description": "TrueNine Memory Synchronization CLI",
"author": "TrueNine",
"license": "AGPL-3.0-only",
Expand All @@ -13,6 +13,7 @@
},
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
Expand All @@ -27,8 +28,9 @@
},
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"bin": {
"tnmsc": "./dist/index.mjs"
"tnmsc": "./dist/main.mjs"
},
"files": [
"dist",
Expand Down
41 changes: 41 additions & 0 deletions cli/src/cli-runtime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import process from 'node:process'
import {toJsonCommandResult} from '@/commands/JsonOutputCommand'
import {buildUnhandledExceptionDiagnostic} from '@/diagnostics'
import {PluginPipeline} from '@/PluginPipeline'
import {createDefaultPluginConfig} from './plugin.config'
import {createLogger, drainBufferedDiagnostics} from './plugins/plugin-core'

export function isJsonMode(argv: readonly string[]): boolean {
return argv.some(arg => arg === '--json' || arg === '-j' || /^-[^-]*j/.test(arg))
}

function writeJsonFailure(error: unknown): void {
const errorMessage = error instanceof Error ? error.message : String(error)
const logger = createLogger('main', 'silent')
logger.error(buildUnhandledExceptionDiagnostic('main', error))
process.stdout.write(`${JSON.stringify(toJsonCommandResult({
success: false,
filesAffected: 0,
dirsAffected: 0,
message: errorMessage
}, drainBufferedDiagnostics()))}\n`)
}

export async function runCli(argv: readonly string[] = process.argv): Promise<number> {
try {
const pipeline = new PluginPipeline(...argv)
const userPluginConfig = await createDefaultPluginConfig(argv)
const result = await pipeline.run(userPluginConfig)
return result.success ? 0 : 1
}
catch (error) {
if (isJsonMode(argv)) {
writeJsonFailure(error)
return 1
}

const logger = createLogger('main', 'error')
logger.error(buildUnhandledExceptionDiagnostic('main', error))
return 1
}
}
2 changes: 1 addition & 1 deletion cli/src/commands/CleanupUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ describe('collectDeletionTargets', () => {
expect(result.violations).toEqual([expect.objectContaining({
targetPath: path.resolve(path.join(workspaceDir, 'aindex', 'app')),
protectionMode: 'direct',
protectedPath: path.resolve(protectedAppMdxFile)
protectedPath: path.resolve(path.join(workspaceDir, 'aindex', 'app', 'workspace.src.mdx'))
})])
}
finally {
Expand Down
6 changes: 3 additions & 3 deletions cli/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ const DEFAULT_AINDEX: Required<AindexConfig> = {
commands: {src: 'commands', dist: 'dist/commands'},
subAgents: {src: 'subagents', dist: 'dist/subagents'},
rules: {src: 'rules', dist: 'dist/rules'},
globalPrompt: {src: 'global.src.mdx', dist: 'dist/global.mdx'},
workspacePrompt: {src: 'workspace.src.mdx', dist: 'dist/workspace.mdx'},
globalPrompt: {src: 'app/global.src.mdx', dist: 'dist/global.mdx'},
workspacePrompt: {src: 'app/workspace.src.mdx', dist: 'dist/workspace.mdx'},
app: {src: 'app', dist: 'dist/app'},
ext: {src: 'ext', dist: 'dist/ext'},
arch: {src: 'arch', dist: 'dist/arch'}
Expand All @@ -70,7 +70,7 @@ const DEFAULT_OPTIONS: Required<PluginOptions> = {
* Convert UserConfigFile to PluginOptions
* UserConfigFile is the JSON schema, PluginOptions includes plugins
*/
function userConfigToPluginOptions(userConfig: UserConfigFile): Partial<PluginOptions> {
export function userConfigToPluginOptions(userConfig: UserConfigFile): Partial<PluginOptions> {
return {
...userConfig.version != null ? {version: userConfig.version} : {},
...userConfig.workspaceDir != null ? {workspaceDir: userConfig.workspaceDir} : {},
Expand Down
2 changes: 1 addition & 1 deletion cli/src/core/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ mod tests {
"subAgents": {"src": "src/agents", "dist": "dist/agents"},
"rules": {"src": "src/rules", "dist": "dist/rules"},
"globalPrompt": {"src": "app/global.src.mdx", "dist": "dist/global.mdx"},
"workspacePrompt": {"src": "app/workspace.src.mdx", "dist": "dist/app/workspace.mdx"},
"workspacePrompt": {"src": "app/workspace.src.mdx", "dist": "dist/workspace.mdx"},
"app": {"src": "app", "dist": "dist/app"},
"ext": {"src": "ext", "dist": "dist/ext"},
"arch": {"src": "arch", "dist": "dist/arch"}
Expand Down
11 changes: 11 additions & 0 deletions cli/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {describe, expect, it} from 'vitest'

describe('library entrypoint', () => {
it('can be imported without executing the CLI runtime', async () => {
const mod = await import('./index')

expect(typeof mod.runCli).toBe('function')
expect(typeof mod.createDefaultPluginConfig).toBe('function')
expect(typeof mod.listPrompts).toBe('function')
})
})
Loading
Loading