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
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": {
"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:only": "vite build",
"build:bun": "bun run build.ts",
@ -207,6 +206,7 @@
"turndown": "^7.2.2",
"type-fest": "^5.5.0",
"typescript": "^6.0.2",
"undici": "^7.24.6",
"url-handler-napi": "workspace:*",
"usehooks-ts": "^3.1.1",
"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")
}
}
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 })

View File

@ -1,5 +1,4 @@
import type { ToolUseContext } from '../../Tool.js'
import { clearCommandsCache } from '../../commands.js'
import type {
LocalJSXCommandContext,
LocalJSXCommandOnDone,
@ -10,7 +9,6 @@ import {
getLanguageDisplayName,
getResolvedLanguage,
} from '../../utils/language.js'
import { Extension as ReviewExtension } from '../../costrict/review/index.js'
const VALID_LANGS: readonly PreferredLanguage[] = ['en', 'zh', 'auto']
@ -42,10 +40,6 @@ export async function call(
const lang = arg as PreferredLanguage
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 suffix = lang === 'auto' ? `${getLanguageDisplayName(resolved)}` : ''
onDone(`Language set to ${getLanguageDisplayName(lang)}${suffix}`, {

View File

@ -1,5 +1,5 @@
# 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.

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'
import { getClaudeConfigHomeDir } from 'src/utils/envUtils.js'
/**
* CoStrict Skill Extension
*
* 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 {
extractBundledSkill,
getBuiltinSkillVersion,
listBuiltinSkillNames,
} from './skill/builtin.js'
import * as Builtin from './skill/builtin.js'
const LOCALE_MAP: Record<string, string> = { zh: 'zh-CN', en: 'en' }
function getLocale(): string {
const lang = getResolvedLanguage()
return LOCALE_MAP[lang] ?? 'zh-CN'
}
function getReviewSkillsDir(): string {
return join(getClaudeConfigHomeDir(), 'skills')
function getSkillCacheDir(): string {
const home = process.env.HOME ?? process.env.USERPROFILE ?? ''
return path.join(home, '.claude', 'skills')
}
function getVersionFilePath(skillDir: string): string {
return join(skillDir, '.version')
return path.join(skillDir, '.version')
}
async function getInstalledVersion(skillDir: string): Promise<string | null> {
try {
return await readFile(getVersionFilePath(skillDir), 'utf-8')
const content = await readFile(getVersionFilePath(skillDir), 'utf-8')
return content.trim()
} catch {
return null
}
}
async function writeVersionFile(
skillDir: string,
skillName: string,
locale: string,
): Promise<void> {
const builtinVersion = getBuiltinSkillVersion(skillName)
async function needsUpdate(skillDir: string, skillName: string, locale: string): Promise<boolean> {
const builtinVersion = Builtin.getBuiltinSkillVersion(skillName)
if (!builtinVersion) return true
const installedVersion = await getInstalledVersion(skillDir)
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
await mkdir(skillDir, { recursive: true })
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> {
const locale = getLocale()
const skillsDir = getReviewSkillsDir()
const skillNames = listBuiltinSkillNames()
const lang = getResolvedLanguage()
const locale = LOCALE_MAP[lang] ?? 'zh-CN'
for (const skillName of skillNames) {
const skillDir = join(skillsDir, skillName)
if (!(await needsUpdate(skillDir, skillName, locale))) continue
const cacheDir = getSkillCacheDir()
const skillNames = Builtin.listBuiltinSkills()
await mkdir(skillDir, { recursive: true })
await extractBundledSkill(skillName, skillDir, locale)
await writeVersionFile(skillDir, skillName, locale)
for (const name of skillNames) {
const skillDir = path.join(cacheDir, name)
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 {
return getReviewSkillsDir()
return getSkillCacheDir()
}

View File

@ -1,9 +1,15 @@
/**
* CoStrict Review Module
*
* Provides builtin review skills that are extracted to disk at runtime
* and discovered by the standard skill scanner.
* Provides builtin review skills and agents that are embedded
* 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 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 { 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 { registerClaudeInChromeSkill } from './claudeInChrome.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
*/
export function initBundledSkills(): void {
// Extract review skills to disk for standard scanner discovery
ReviewExtension.initializeBuiltinSkills().catch(() => {})
// Initialize builtin review skills (extract to cache if needed)
initializeBuiltinSkills().catch(() => {})
registerUpdateConfigSkill()
registerProjectWikiSkill()