diff --git a/src/commands/change.ts b/src/commands/change.ts index 5a1fcc034..87538a7cd 100644 --- a/src/commands/change.ts +++ b/src/commands/change.ts @@ -226,7 +226,7 @@ export class ChangeCommand { } else { console.error(`变更 "${changeName}" 存在问题`); report.issues.forEach(issue => { - const label = issue.level === 'ERROR' ? 'ERROR' : 'WARNING'; + const label = issue.level === 'ERROR' ? '错误' : '警告'; const prefix = issue.level === 'ERROR' ? '✗' : '⚠'; console.error(`${prefix} [${label}] ${issue.path}: ${issue.message}`); }); diff --git a/src/commands/spec.ts b/src/commands/spec.ts index 593828dd2..f27aab3f1 100644 --- a/src/commands/spec.ts +++ b/src/commands/spec.ts @@ -234,7 +234,7 @@ export function registerSpecCommand(rootProgram: typeof program) { } else { console.error(`规范 '${specId}' 存在问题`); report.issues.forEach(issue => { - const label = issue.level === 'ERROR' ? 'ERROR' : issue.level; + const label = issue.level === 'ERROR' ? '错误' : issue.level === 'WARNING' ? '警告' : '信息'; const prefix = issue.level === 'ERROR' ? '✗' : issue.level === 'WARNING' ? '⚠' : 'ℹ'; console.error(`${prefix} [${label}] ${issue.path}: ${issue.message}`); }); diff --git a/src/commands/validate.ts b/src/commands/validate.ts index 5551b3e66..0403b9e5b 100644 --- a/src/commands/validate.ts +++ b/src/commands/validate.ts @@ -158,7 +158,7 @@ export class ValidateCommand { } else { console.error(`${type === 'change' ? '变更' : '规范'} '${id}' 存在问题`); for (const issue of report.issues) { - const label = issue.level === 'ERROR' ? 'ERROR' : issue.level; + const label = issue.level === 'ERROR' ? '错误' : issue.level === 'WARNING' ? '警告' : '信息'; const prefix = issue.level === 'ERROR' ? '✗' : issue.level === 'WARNING' ? '⚠' : 'ℹ'; console.error(`${prefix} [${label}] ${issue.path}: ${issue.message}`); } @@ -247,14 +247,14 @@ export class ValidateCommand { const currentIndex = index++; const task = queue[currentIndex]; running++; - if (spinner) spinner.text = `Validating (${currentIndex + 1}/${queue.length})...`; + if (spinner) spinner.text = `正在验证 (${currentIndex + 1}/${queue.length})...`; task() .then(res => { results.push(res); if (res.valid) passed++; else failed++; }) .catch((error: any) => { - const message = error?.message || 'Unknown error'; + const message = error?.message || '未知错误'; const res: BulkItemResult = { id: getPlannedId(currentIndex, changeIds, specIds) ?? 'unknown', type: getPlannedType(currentIndex, changeIds, specIds) ?? 'change', valid: false, issues: [{ level: 'ERROR', path: 'file', message }], durationMs: 0 }; results.push(res); failed++; diff --git a/src/commands/workflow/templates.ts b/src/commands/workflow/templates.ts index 3bd6fbb40..156e6904f 100644 --- a/src/commands/workflow/templates.ts +++ b/src/commands/workflow/templates.ts @@ -83,7 +83,7 @@ export async function templatesCommand(options: TemplatesOptions): Promise return; } - console.log(`Schema: ${schemaName}`); + console.log(`架构: ${schemaName}`); console.log(`来源:${source}`); console.log(); diff --git a/src/core/artifact-graph/instruction-loader.ts b/src/core/artifact-graph/instruction-loader.ts index b95e81651..4454edc09 100644 --- a/src/core/artifact-graph/instruction-loader.ts +++ b/src/core/artifact-graph/instruction-loader.ts @@ -132,7 +132,7 @@ export function loadTemplate( const schemaDir = getSchemaDir(schemaName, projectRoot); if (!schemaDir) { throw new TemplateLoadError( - `Schema '${schemaName}' not found`, + `架构 '${schemaName}' 未找到`, templatePath ); } @@ -141,7 +141,7 @@ export function loadTemplate( if (!fs.existsSync(fullPath)) { throw new TemplateLoadError( - `Template not found: ${fullPath}`, + `模板文件未找到:${fullPath}`, fullPath ); } @@ -151,7 +151,7 @@ export function loadTemplate( } catch (err) { const ioError = err instanceof Error ? err : new Error(String(err)); throw new TemplateLoadError( - `Failed to read template: ${ioError.message}`, + `读取模板文件失败:${ioError.message}`, fullPath ); } diff --git a/src/core/artifact-graph/resolver.ts b/src/core/artifact-graph/resolver.ts index 9ccd48aba..8c753d0dc 100644 --- a/src/core/artifact-graph/resolver.ts +++ b/src/core/artifact-graph/resolver.ts @@ -114,7 +114,7 @@ export function resolveSchema(name: string, projectRoot?: string): SchemaYaml { if (!schemaDir) { const availableSchemas = listSchemas(projectRoot); throw new Error( - `Schema '${normalizedName}' not found. Available schemas: ${availableSchemas.join(', ')}` + `架构 '${normalizedName}' 未找到。可用架构:${availableSchemas.join(', ')}` ); } @@ -127,7 +127,7 @@ export function resolveSchema(name: string, projectRoot?: string): SchemaYaml { } catch (err) { const ioError = err instanceof Error ? err : new Error(String(err)); throw new SchemaLoadError( - `Failed to read schema at '${schemaPath}': ${ioError.message}`, + `读取架构文件失败 '${schemaPath}':${ioError.message}`, schemaPath, ioError ); @@ -138,14 +138,14 @@ export function resolveSchema(name: string, projectRoot?: string): SchemaYaml { } catch (err) { if (err instanceof SchemaValidationError) { throw new SchemaLoadError( - `Invalid schema at '${schemaPath}': ${err.message}`, + `架构文件无效 '${schemaPath}':${err.message}`, schemaPath, err ); } const parseError = err instanceof Error ? err : new Error(String(err)); throw new SchemaLoadError( - `Failed to parse schema at '${schemaPath}': ${parseError.message}`, + `解析架构文件失败 '${schemaPath}':${parseError.message}`, schemaPath, parseError ); diff --git a/src/core/project-config.ts b/src/core/project-config.ts index 6c1ea04a5..43263abf4 100644 --- a/src/core/project-config.ts +++ b/src/core/project-config.ts @@ -78,7 +78,7 @@ export function readProjectConfig(projectRoot: string): ProjectConfig | null { const raw = parseYaml(content); if (!raw || typeof raw !== 'object') { - console.warn(`openspec/config.yaml is not a valid YAML object`); + console.warn(`openspec/config.yaml 不是有效的 YAML 对象`); return null; } @@ -90,7 +90,7 @@ export function readProjectConfig(projectRoot: string): ProjectConfig | null { if (schemaResult.success) { config.schema = schemaResult.data; } else if (raw.schema !== undefined) { - console.warn(`Invalid 'schema' field in config (must be non-empty string)`); + console.warn(`配置中的 'schema' 字段无效(必须是非空字符串)`); } // Parse context field with size limit @@ -102,14 +102,14 @@ export function readProjectConfig(projectRoot: string): ProjectConfig | null { const contextSize = Buffer.byteLength(contextResult.data, 'utf-8'); if (contextSize > MAX_CONTEXT_SIZE) { console.warn( - `Context too large (${(contextSize / 1024).toFixed(1)}KB, limit: ${MAX_CONTEXT_SIZE / 1024}KB)` + `上下文过大(${(contextSize / 1024).toFixed(1)}KB,限制:${MAX_CONTEXT_SIZE / 1024}KB)` ); - console.warn(`Ignoring context field`); + console.warn(`忽略 context 字段`); } else { config.context = contextResult.data; } } else { - console.warn(`Invalid 'context' field in config (must be string)`); + console.warn(`配置中的 'context' 字段无效(必须是字符串)`); } } @@ -134,12 +134,12 @@ export function readProjectConfig(projectRoot: string): ProjectConfig | null { } if (validRules.length < rulesArrayResult.data.length) { console.warn( - `Some rules for '${artifactId}' are empty strings, ignoring them` + `'${artifactId}' 的某些规则是空字符串,已忽略` ); } } else { console.warn( - `Rules for '${artifactId}' must be an array of strings, ignoring this artifact's rules` + `'${artifactId}' 的规则必须是字符串数组,已忽略该产出物的规则` ); } } @@ -148,14 +148,14 @@ export function readProjectConfig(projectRoot: string): ProjectConfig | null { config.rules = parsedRules; } } else { - console.warn(`Invalid 'rules' field in config (must be object)`); + console.warn(`配置中的 'rules' 字段无效(必须是对象)`); } } // Return partial config even if some fields failed return Object.keys(config).length > 0 ? (config as ProjectConfig) : null; } catch (error) { - console.warn(`Failed to parse openspec/config.yaml:`, error); + console.warn(`解析 openspec/config.yaml 失败:`, error); return null; } } @@ -181,8 +181,8 @@ export function validateConfigRules( if (!validArtifactIds.has(artifactId)) { const validIds = Array.from(validArtifactIds).sort().join(', '); warnings.push( - `Unknown artifact ID in rules: "${artifactId}". ` + - `Valid IDs for schema "${schemaName}": ${validIds}` + `规则中存在未知的产出物 ID:"${artifactId}"。` + + `架构 "${schemaName}" 的有效 ID:${validIds}` ); } } @@ -237,28 +237,28 @@ export function suggestSchemas( const builtIn = availableSchemas.filter((s) => s.isBuiltIn).map((s) => s.name); const projectLocal = availableSchemas.filter((s) => !s.isBuiltIn).map((s) => s.name); - let message = `Schema '${invalidSchemaName}' not found in openspec/config.yaml\n\n`; + let message = `架构 '${invalidSchemaName}' 在 openspec/config.yaml 中未找到\n\n`; if (suggestions.length > 0) { - message += `Did you mean one of these?\n`; + message += `您是否想要其中之一?\n`; suggestions.forEach((s) => { - const type = s.isBuiltIn ? 'built-in' : 'project-local'; + const type = s.isBuiltIn ? '内置' : '项目本地'; message += ` - ${s.name} (${type})\n`; }); message += '\n'; } - message += `Available schemas:\n`; + message += `可用架构:\n`; if (builtIn.length > 0) { - message += ` Built-in: ${builtIn.join(', ')}\n`; + message += ` 内置:${builtIn.join(', ')}\n`; } if (projectLocal.length > 0) { - message += ` Project-local: ${projectLocal.join(', ')}\n`; + message += ` 项目本地:${projectLocal.join(', ')}\n`; } else { - message += ` Project-local: (none found)\n`; + message += ` 项目本地:(未找到)\n`; } - message += `\nFix: Edit openspec/config.yaml and change 'schema: ${invalidSchemaName}' to a valid schema name`; + message += `\n修复方法:编辑 openspec/config.yaml,将 'schema: ${invalidSchemaName}' 改为有效的架构名称`; return message; } diff --git a/test/commands/artifact-workflow.test.ts b/test/commands/artifact-workflow.test.ts index 0d8a91a16..365d32946 100644 --- a/test/commands/artifact-workflow.test.ts +++ b/test/commands/artifact-workflow.test.ts @@ -290,7 +290,7 @@ describe('artifact-workflow CLI commands', () => { it('shows template paths for default schema', async () => { const result = await runCLI(['templates'], { cwd: tempDir }); expect(result.exitCode).toBe(0); - expect(result.stdout).toContain('Schema: spec-driven'); + expect(result.stdout).toContain('架构: spec-driven'); expect(result.stdout).toContain('proposal:'); expect(result.stdout).toContain('design:'); expect(result.stdout).toContain('specs:'); @@ -300,7 +300,7 @@ describe('artifact-workflow CLI commands', () => { it('shows template paths for specified schema', async () => { const result = await runCLI(['templates', '--schema', 'spec-driven'], { cwd: tempDir }); expect(result.exitCode).toBe(0); - expect(result.stdout).toContain('Schema: spec-driven'); + expect(result.stdout).toContain('架构: spec-driven'); expect(result.stdout).toContain('proposal:'); expect(result.stdout).toContain('design:'); }); diff --git a/test/core/artifact-graph/instruction-loader.test.ts b/test/core/artifact-graph/instruction-loader.test.ts index 64b1c5f12..513b3c553 100644 --- a/test/core/artifact-graph/instruction-loader.test.ts +++ b/test/core/artifact-graph/instruction-loader.test.ts @@ -443,7 +443,7 @@ rules: generateInstructions(context, 'proposal', tempDir); expect(consoleWarnSpy).toHaveBeenCalledWith( - expect.stringContaining('Unknown artifact ID in rules: "invalid-artifact"') + expect.stringContaining('规则中存在未知的产出物 ID:"invalid-artifact"') ); }); @@ -475,7 +475,7 @@ rules: // Note: We may have gotten warnings from other tests, so check that // the count didn't increase by more than 1 from the first call const callCount = consoleWarnSpy.mock.calls.filter(call => - call[0]?.includes('Unknown artifact ID in rules') + call[0]?.includes('规则中存在未知的产出物 ID') ).length; expect(callCount).toBeGreaterThanOrEqual(1); diff --git a/test/core/artifact-graph/resolver.test.ts b/test/core/artifact-graph/resolver.test.ts index 484cc3b40..b34a9de9a 100644 --- a/test/core/artifact-graph/resolver.test.ts +++ b/test/core/artifact-graph/resolver.test.ts @@ -227,7 +227,7 @@ version: [[[invalid yaml } catch (e) { expect(e).toBeInstanceOf(SchemaLoadError); const error = e as SchemaLoadError; - expect(error.message).toContain('Failed to parse'); + expect(error.message).toContain('解析架构文件失败'); expect(error.message).toContain(schemaPath); } }); @@ -243,7 +243,7 @@ version: [[[invalid yaml }); it('should throw when schema not found', () => { - expect(() => resolveSchema('nonexistent-schema')).toThrow(/not found/); + expect(() => resolveSchema('nonexistent-schema')).toThrow(/未找到/); }); it('should list available schemas in error message', () => { diff --git a/test/core/project-config.test.ts b/test/core/project-config.test.ts index 88944659d..d10b663db 100644 --- a/test/core/project-config.test.ts +++ b/test/core/project-config.test.ts @@ -90,7 +90,7 @@ rules: }, }); expect(consoleWarnSpy).toHaveBeenCalledWith( - expect.stringContaining("Invalid 'schema' field") + expect.stringContaining("配置中的 'schema' 字段无效") ); }); @@ -116,7 +116,7 @@ rules: }, }); expect(consoleWarnSpy).toHaveBeenCalledWith( - expect.stringContaining("Invalid 'context' field") + expect.stringContaining("配置中的 'context' 字段无效") ); }); @@ -138,7 +138,7 @@ rules: ["not", "an", "object"] context: 'Valid context', }); expect(consoleWarnSpy).toHaveBeenCalledWith( - expect.stringContaining("Invalid 'rules' field") + expect.stringContaining("配置中的 'rules' 字段无效") ); }); @@ -162,7 +162,7 @@ rules: context: 'Valid context', }); expect(consoleWarnSpy).toHaveBeenCalledWith( - expect.stringContaining("Invalid 'rules' field") + expect.stringContaining("配置中的 'rules' 字段无效") ); }); @@ -191,7 +191,7 @@ rules: }, }); expect(consoleWarnSpy).toHaveBeenCalledWith( - expect.stringContaining("Rules for 'specs' must be an array of strings") + expect.stringContaining("'specs' 的规则必须是字符串数组") ); }); @@ -219,7 +219,7 @@ rules: }, }); expect(consoleWarnSpy).toHaveBeenCalledWith( - expect.stringContaining("Some rules for 'proposal' are empty strings") + expect.stringContaining("'proposal' 的某些规则是空字符串") ); }); @@ -257,7 +257,7 @@ rules: expect(config).toBeNull(); expect(consoleWarnSpy).toHaveBeenCalledWith( - expect.stringContaining('Failed to parse openspec/config.yaml'), + expect.stringContaining('解析 openspec/config.yaml 失败'), expect.anything() ); }); @@ -271,7 +271,7 @@ rules: expect(config).toBeNull(); expect(consoleWarnSpy).toHaveBeenCalledWith( - expect.stringContaining('not a valid YAML object') + expect.stringContaining('不是有效的 YAML 对象') ); }); @@ -300,7 +300,7 @@ rules: expect(config?.context).toBe(smallContext); expect(consoleWarnSpy).not.toHaveBeenCalledWith( - expect.stringContaining('Context too large') + expect.stringContaining('上下文过大') ); }); @@ -318,10 +318,10 @@ rules: expect(config).toEqual({ schema: 'spec-driven' }); expect(config?.context).toBeUndefined(); expect(consoleWarnSpy).toHaveBeenCalledWith( - expect.stringContaining('Context too large (51.0KB, limit: 50KB)') + expect.stringContaining('上下文过大(51.0KB,限制:50KB)') ); expect(consoleWarnSpy).toHaveBeenCalledWith( - expect.stringContaining('Ignoring context field') + expect.stringContaining('忽略 context 字段') ); }); @@ -338,7 +338,7 @@ rules: expect(config?.context).toBe(exactContext); expect(consoleWarnSpy).not.toHaveBeenCalledWith( - expect.stringContaining('Context too large') + expect.stringContaining('上下文过大') ); }); @@ -359,7 +359,7 @@ context: | expect(config?.context).toBeUndefined(); expect(consoleWarnSpy).toHaveBeenCalledWith( - expect.stringContaining('Context too large') + expect.stringContaining('上下文过大') ); }); }); @@ -507,9 +507,9 @@ rules: const warnings = validateConfigRules(rules, validIds, 'spec-driven'); expect(warnings).toHaveLength(2); - expect(warnings[0]).toContain('Unknown artifact ID in rules: "testplan"'); - expect(warnings[0]).toContain('Valid IDs for schema "spec-driven": design, proposal, specs, tasks'); - expect(warnings[1]).toContain('Unknown artifact ID in rules: "documentation"'); + expect(warnings[0]).toContain('规则中存在未知的产出物 ID:"testplan"'); + expect(warnings[0]).toContain('架构 "spec-driven" 的有效 ID:design, proposal, specs, tasks'); + expect(warnings[1]).toContain('规则中存在未知的产出物 ID:"documentation"'); }); it('should return warnings for all unknown artifact IDs', () => { @@ -545,24 +545,24 @@ rules: it('should suggest close matches using fuzzy matching', () => { const message = suggestSchemas('spec-drven', availableSchemas); // Missing 'i' - expect(message).toContain("Schema 'spec-drven' not found"); - expect(message).toContain('Did you mean one of these?'); - expect(message).toContain('spec-driven (built-in)'); + expect(message).toContain("架构 'spec-drven' 在 openspec/config.yaml 中未找到"); + expect(message).toContain('您是否想要其中之一?'); + expect(message).toContain('spec-driven (内置)'); }); it('should suggest custom-workflow for workflow typo', () => { const message = suggestSchemas('custom-workflo', availableSchemas); - expect(message).toContain('Did you mean one of these?'); + expect(message).toContain('您是否想要其中之一?'); expect(message).toContain('custom-workflow'); }); it('should list all available schemas', () => { const message = suggestSchemas('nonexistent', availableSchemas); - expect(message).toContain('Available schemas:'); - expect(message).toContain('Built-in: spec-driven'); - expect(message).toContain('Project-local: custom-workflow, team-process'); + expect(message).toContain('可用架构:'); + expect(message).toContain('内置:spec-driven'); + expect(message).toContain('项目本地:custom-workflow, team-process'); }); it('should handle case when no project-local schemas exist', () => { @@ -571,15 +571,15 @@ rules: ]; const message = suggestSchemas('invalid', builtInOnly); - expect(message).toContain('Built-in: spec-driven'); - expect(message).toContain('Project-local: (none found)'); + expect(message).toContain('内置:spec-driven'); + expect(message).toContain('项目本地:(未找到)'); }); it('should include fix instruction', () => { const message = suggestSchemas('wrong-schema', availableSchemas); expect(message).toContain( - "Fix: Edit openspec/config.yaml and change 'schema: wrong-schema' to a valid schema name" + "修复方法:编辑 openspec/config.yaml,将 'schema: wrong-schema' 改为有效的架构名称" ); }); @@ -603,8 +603,8 @@ rules: const message = suggestSchemas('abcdefghijk', availableSchemas); // 'abcdefghijk' has large Levenshtein distance from all schemas - expect(message).not.toContain('Did you mean'); - expect(message).toContain('Available schemas:'); + expect(message).not.toContain('您是否想要'); + expect(message).toContain('可用架构:'); }); }); });