fix: resolve merge conflicts with main, keep branch review architecture

- Remove duplicate extractBundledSkill from generated builtin.ts template
- Keep extract-to-disk extension.ts (branch version)
- Keep SSH clone URL for generate-review-builtin
- Revert .gitignore, package.json, locales to branch versions

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
kingboung 2026-05-12 15:16:17 +08:00
parent 8599340c68
commit a645f34ee5
9 changed files with 86 additions and 80 deletions

13
.gitignore vendored
View File

@ -49,4 +49,15 @@ src/costrict/review/skill/builtin.ts
# Review builtin cache # Review builtin cache
packages/builtin-tools/bundled-review/ packages/builtin-tools/bundled-review/
bundled-review/
# Upstream additions
teach-me
credentials.json
# Session-scoped progress / state files written by agents and skills
.claude-impl-state.md
.claude-progress.md
.claude-recovery.md
.test-progress.md
.squash-tmp/
.git.*-backup

View File

@ -41,7 +41,6 @@
], ],
"scripts": { "scripts": {
"build": "bun run build.ts", "build": "bun run build.ts",
"build:builtin-review": "bun run scripts/generate-review-builtin.ts",
"build:vite": "vite build && bun run scripts/post-build.ts", "build:vite": "vite build && bun run scripts/post-build.ts",
"build:vite:only": "vite build", "build:vite:only": "vite build",
"build:bun": "bun run build.ts", "build:bun": "bun run build.ts",
@ -207,6 +206,7 @@
"turndown": "^7.2.2", "turndown": "^7.2.2",
"type-fest": "^5.5.0", "type-fest": "^5.5.0",
"typescript": "^6.0.2", "typescript": "^6.0.2",
"undici": "^7.24.6",
"url-handler-napi": "workspace:*", "url-handler-napi": "workspace:*",
"usehooks-ts": "^3.1.1", "usehooks-ts": "^3.1.1",
"vite": "^8.0.8", "vite": "^8.0.8",

View File

@ -293,26 +293,6 @@ export async function extractBundledSkill(skillName: string, targetDir: string,
await writeFile(join(targetDir, relativePath), content, "utf-8") await writeFile(join(targetDir, relativePath), content, "utf-8")
} }
} }
export async function extractBundledSkill(skillName: string, targetDir: string, locale: string): Promise<void> {
const localeData = SKILL_FILES[locale]
if (!localeData) {
throw new Error(\`Locale not found: \${locale}\`)
}
const skillFiles = localeData[skillName]
if (!skillFiles) {
throw new Error(\`Skill not found: \${skillName}\`)
}
const { mkdir: mkdirSync, writeFile: writeFileSync } = await import('fs/promises')
const { join: pathJoin, dirname: pathDirname } = await import('path')
await mkdirSync(targetDir, { recursive: true })
for (const [relativePath, fileContent] of Object.entries(skillFiles)) {
await mkdirSync(pathJoin(targetDir, pathDirname(relativePath)), { recursive: true })
await writeFileSync(pathJoin(targetDir, relativePath), fileContent, 'utf-8')
}
}
` `
await mkdir(path.dirname(builtinSkillsFile), { recursive: true }) await mkdir(path.dirname(builtinSkillsFile), { recursive: true })

View File

@ -1,5 +1,4 @@
import type { ToolUseContext } from '../../Tool.js' import type { ToolUseContext } from '../../Tool.js'
import { clearCommandsCache } from '../../commands.js'
import type { import type {
LocalJSXCommandContext, LocalJSXCommandContext,
LocalJSXCommandOnDone, LocalJSXCommandOnDone,
@ -10,7 +9,6 @@ import {
getLanguageDisplayName, getLanguageDisplayName,
getResolvedLanguage, getResolvedLanguage,
} from '../../utils/language.js' } from '../../utils/language.js'
import { Extension as ReviewExtension } from '../../costrict/review/index.js'
const VALID_LANGS: readonly PreferredLanguage[] = ['en', 'zh', 'auto'] const VALID_LANGS: readonly PreferredLanguage[] = ['en', 'zh', 'auto']
@ -42,10 +40,6 @@ export async function call(
const lang = arg as PreferredLanguage const lang = arg as PreferredLanguage
saveGlobalConfig(current => ({ ...current, preferredLanguage: lang })) saveGlobalConfig(current => ({ ...current, preferredLanguage: lang }))
// Re-extract review skills for the new locale and refresh skill caches
await ReviewExtension.initializeBuiltinSkills().catch(() => {})
clearCommandsCache()
const resolved = getResolvedLanguage() const resolved = getResolvedLanguage()
const suffix = lang === 'auto' ? `${getLanguageDisplayName(resolved)}` : '' const suffix = lang === 'auto' ? `${getLanguageDisplayName(resolved)}` : ''
onDone(`Language set to ${getLanguageDisplayName(lang)}${suffix}`, { onDone(`Language set to ${getLanguageDisplayName(lang)}${suffix}`, {

View File

@ -1,5 +1,5 @@
# Code Review # Code Review
Please use the Skill tool to load the `review` skill to perform a code review on: $ARGUMENTS Please perform a code review on: $ARGUMENTS
Please respond and write all files in English throughout the entire process. Please respond and write all files in English throughout the entire process.

View File

@ -1,5 +1,5 @@
# 代码审查 # 代码审查
使用 Skill 工具加载 `review` 技能来对以下内容执行代码审查:$ARGUMENTS 请对以下内容执行代码审查:$ARGUMENTS
全程请使用中文进行回答与文件写入。 全程请使用中文进行回答与文件写入。

View File

@ -1,72 +1,87 @@
import { mkdir, readFile, writeFile } from 'fs/promises' /**
import { join } from 'path' * CoStrict Skill Extension
import { getClaudeConfigHomeDir } from 'src/utils/envUtils.js' *
* Initializes builtin review skills by extracting them from embedded
* SKILL_FILES to a cache directory on disk.
*
* Version tracking uses commit SHA + locale in a .version file.
* Skills are re-extracted when version or locale changes.
*/
import path from 'path'
import { writeFile, readFile, rm, mkdir, stat } from 'fs/promises'
import { getResolvedLanguage } from 'src/utils/language.js' import { getResolvedLanguage } from 'src/utils/language.js'
import { import * as Builtin from './skill/builtin.js'
extractBundledSkill,
getBuiltinSkillVersion,
listBuiltinSkillNames,
} from './skill/builtin.js'
const LOCALE_MAP: Record<string, string> = { zh: 'zh-CN', en: 'en' } const LOCALE_MAP: Record<string, string> = { zh: 'zh-CN', en: 'en' }
function getLocale(): string { function getSkillCacheDir(): string {
const lang = getResolvedLanguage() const home = process.env.HOME ?? process.env.USERPROFILE ?? ''
return LOCALE_MAP[lang] ?? 'zh-CN' return path.join(home, '.claude', 'skills')
}
function getReviewSkillsDir(): string {
return join(getClaudeConfigHomeDir(), 'skills')
} }
function getVersionFilePath(skillDir: string): string { function getVersionFilePath(skillDir: string): string {
return join(skillDir, '.version') return path.join(skillDir, '.version')
} }
async function getInstalledVersion(skillDir: string): Promise<string | null> { async function getInstalledVersion(skillDir: string): Promise<string | null> {
try { try {
return await readFile(getVersionFilePath(skillDir), 'utf-8') const content = await readFile(getVersionFilePath(skillDir), 'utf-8')
return content.trim()
} catch { } catch {
return null return null
} }
} }
async function writeVersionFile( async function needsUpdate(skillDir: string, skillName: string, locale: string): Promise<boolean> {
skillDir: string, const builtinVersion = Builtin.getBuiltinSkillVersion(skillName)
skillName: string, if (!builtinVersion) return true
locale: string,
): Promise<void> { const installedVersion = await getInstalledVersion(skillDir)
const builtinVersion = getBuiltinSkillVersion(skillName) const expectedVersion = `${builtinVersion}:${locale}`
return installedVersion !== expectedVersion
}
async function writeVersionFile(skillDir: string, skillName: string, locale: string): Promise<void> {
const builtinVersion = Builtin.getBuiltinSkillVersion(skillName)
if (!builtinVersion) return if (!builtinVersion) return
await mkdir(skillDir, { recursive: true })
await writeFile(getVersionFilePath(skillDir), `${builtinVersion}:${locale}`, 'utf-8') await writeFile(getVersionFilePath(skillDir), `${builtinVersion}:${locale}`, 'utf-8')
} }
async function needsUpdate(
skillDir: string,
skillName: string,
locale: string,
): Promise<boolean> {
const builtinVersion = getBuiltinSkillVersion(skillName)
if (!builtinVersion) return true
const installed = await getInstalledVersion(skillDir)
return installed !== `${builtinVersion}:${locale}`
}
export async function initializeBuiltinSkills(): Promise<void> { export async function initializeBuiltinSkills(): Promise<void> {
const locale = getLocale() const lang = getResolvedLanguage()
const skillsDir = getReviewSkillsDir() const locale = LOCALE_MAP[lang] ?? 'zh-CN'
const skillNames = listBuiltinSkillNames()
for (const skillName of skillNames) { const cacheDir = getSkillCacheDir()
const skillDir = join(skillsDir, skillName) const skillNames = Builtin.listBuiltinSkills()
if (!(await needsUpdate(skillDir, skillName, locale))) continue
await mkdir(skillDir, { recursive: true }) for (const name of skillNames) {
await extractBundledSkill(skillName, skillDir, locale) const skillDir = path.join(cacheDir, name)
await writeVersionFile(skillDir, skillName, locale)
const dirExists = await stat(skillDir).then(s => s.isDirectory()).catch(() => false)
if (dirExists) {
const updateNeeded = await needsUpdate(skillDir, name, locale)
if (!updateNeeded) continue
try {
await rm(skillDir, { recursive: true, force: true })
} catch {
// Continue with copy over existing files
}
}
await Builtin.extractBundledSkill(name, skillDir, locale)
await writeVersionFile(skillDir, name, locale)
const skillFiles = Builtin.listSkillFiles(name, locale)
const builtinVersion = Builtin.getBuiltinSkillVersion(name)
console.log(` [review] initialized skill "${name}" (${locale}, ${skillFiles.length} files, v${builtinVersion?.slice(0, 7)})`)
} }
} }
export function getBuiltinSkillsDir(): string { export function getBuiltinSkillsDir(): string {
return getReviewSkillsDir() return getSkillCacheDir()
} }

View File

@ -1,9 +1,15 @@
/** /**
* CoStrict Review Module * CoStrict Review Module
* *
* Provides builtin review skills that are extracted to disk at runtime * Provides builtin review skills and agents that are embedded
* and discovered by the standard skill scanner. * in the binary and extracted to cache on first run.
*/ */
export * as SkillBuiltin from './skill/builtin.js'
export * as Extension from './extension.js' export * as Extension from './extension.js'
export * as SkillBuiltin from './skill/builtin.js'
export {
REVIEW_AGENTS,
AGENT_VERSIONS,
PRIMARY_REVIEW_AGENT,
SUB_REVIEW_AGENT,
} from './agent/builtin.js'

View File

@ -1,6 +1,6 @@
import { feature } from 'bun:bundle' import { feature } from 'bun:bundle'
import { shouldAutoEnableClaudeInChrome } from 'src/utils/claudeInChrome/setup.js' import { shouldAutoEnableClaudeInChrome } from 'src/utils/claudeInChrome/setup.js'
import { Extension as ReviewExtension } from 'src/costrict/review/index.js' import { initializeBuiltinSkills } from 'src/costrict/review/extension.js'
import { registerBatchSkill } from './batch.js' import { registerBatchSkill } from './batch.js'
import { registerClaudeInChromeSkill } from './claudeInChrome.js' import { registerClaudeInChromeSkill } from './claudeInChrome.js'
import { registerDebugSkill } from './debug.js' import { registerDebugSkill } from './debug.js'
@ -30,8 +30,8 @@ import { registerTddSkill } from 'src/costrict/skills/tdd.js'
* 3. Import and call that function here * 3. Import and call that function here
*/ */
export function initBundledSkills(): void { export function initBundledSkills(): void {
// Extract review skills to disk for standard scanner discovery // Initialize builtin review skills (extract to cache if needed)
ReviewExtension.initializeBuiltinSkills().catch(() => {}) initializeBuiltinSkills().catch(() => {})
registerUpdateConfigSkill() registerUpdateConfigSkill()
registerProjectWikiSkill() registerProjectWikiSkill()