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
51 changes: 51 additions & 0 deletions DEV-LOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,56 @@
# DEV-LOG

## Enable SHOT_STATS, TOKEN_BUDGET, PROMPT_CACHE_BREAK_DETECTION (2026-04-05)

**PR**: [claude-code-best/claude-code#140](https://github.com/claude-code-best/claude-code/pull/140)
**分支**: `feat/enable-safe-feature-flags`

对 22 个被标记为 "COMPLETE" 的编译时 feature flag 进行实际源码验证(6 个并行子代理 + Codex CLI 独立复核),发现审计报告存在大量误判。最终确认仅 3 个 flag 为真正 compile-only,安全启用。

**验证流程:**

1. 6 个并行子代理分别检查每个 flag 的 `feature('FLAG_NAME')` 引用点、依赖模块完整性、外部服务依赖
2. Codex CLI (v0.118.0, 240K tokens) 独立复核,将原 7 个 "compile-only" 进一步缩减为 3 个
3. 3 个专项代理逐一验证代码路径完整性和运行时安全性

**新启用的 3 个 flag:**

| Flag | 功能 | 用户可感知效果 |
|------|------|---------------|
| `SHOT_STATS` | shot 分布统计 | `/stats` 面板显示 shot 分布和 one-shot rate |
| `TOKEN_BUDGET` | token 预算目标 | 支持 `+500k` / `spend 2M tokens` 语法,自动续写直到达标,带进度条 |
| `PROMPT_CACHE_BREAK_DETECTION` | cache key 变化检测 | 内部诊断,`--debug` 模式可见,写 diff 到临时目录 |

**修改文件:**

| 文件 | 变更 |
|------|------|
| `build.ts` | `DEFAULT_BUILD_FEATURES` 新增 3 个 flag |
| `scripts/dev.ts` | `DEFAULT_FEATURES` 新增 3 个 flag |
| `package.json` / `bun.lock` | 新增 `openai` 依赖(OpenAI 兼容层需要) |

**新增文档:**

| 文件 | 说明 |
|------|------|
| `docs/features/feature-flags-codex-review.md` | Codex 独立复核报告:修正后的 5 类分类、恢复优先级、三轴分类标准建议 |
| `docs/features/feature-flags-audit-complete.md` | 标记所有已启用 flag 的状态(`[build: ON]` / `[dev: ON]`) |

**Codex 复核关键发现:**

- 原 22 个 "COMPLETE" flag 中,8 个核心模块是 stub,3 个依赖远程服务
- `TEAMMEM`、`AGENT_TRIGGERS`、`EXTRACT_MEMORIES`、`KAIROS_BRIEF` 被降级为"有条件可用"(受 GrowthBook 门控)
- 建议审计分类标准改为三轴:实现完整度 × 激活条件 × 运行风险
- 恢复优先级:REACTIVE_COMPACT > BG_SESSIONS > PROACTIVE > CONTEXT_COLLAPSE

**验证结果:**

- `bun run build` → 475 files ✅
- `bun test` → 零新增失败 ✅
- 3 个 flag 代码路径全部完整,无缺失依赖,无 crash 风险 ✅

---

## /dream 手动触发 + DreamTask 类型补全 (2026-04-04)

将 `/dream` 命令从 KAIROS feature gate 中解耦,作为 bundled skill 无条件注册;补全 DreamTask 类型存根。
Expand Down
109 changes: 58 additions & 51 deletions build.ts
Original file line number Diff line number Diff line change
@@ -1,81 +1,88 @@
import { readdir, readFile, writeFile, cp } from "fs/promises";
import { join } from "path";
import { getMacroDefines } from "./scripts/defines.ts";
import { readdir, readFile, writeFile, cp } from 'fs/promises'
import { join } from 'path'
import { getMacroDefines } from './scripts/defines.ts'

const outdir = "dist";
const outdir = 'dist'

// Step 1: Clean output directory
const { rmSync } = await import("fs");
rmSync(outdir, { recursive: true, force: true });
const { rmSync } = await import('fs')
rmSync(outdir, { recursive: true, force: true })

// Default features that match the official CLI build.
// Additional features can be enabled via FEATURE_<NAME>=1 env vars.
const DEFAULT_BUILD_FEATURES = ["AGENT_TRIGGERS_REMOTE", "CHICAGO_MCP", "VOICE_MODE"];
const DEFAULT_BUILD_FEATURES = [
'AGENT_TRIGGERS_REMOTE',
'CHICAGO_MCP',
'VOICE_MODE',
'SHOT_STATS',
'PROMPT_CACHE_BREAK_DETECTION',
'TOKEN_BUDGET',
]

// Collect FEATURE_* env vars → Bun.build features
const envFeatures = Object.keys(process.env)
.filter(k => k.startsWith("FEATURE_"))
.map(k => k.replace("FEATURE_", ""));
const features = [...new Set([...DEFAULT_BUILD_FEATURES, ...envFeatures])];
.filter(k => k.startsWith('FEATURE_'))
.map(k => k.replace('FEATURE_', ''))
const features = [...new Set([...DEFAULT_BUILD_FEATURES, ...envFeatures])]

// Step 2: Bundle with splitting
const result = await Bun.build({
entrypoints: ["src/entrypoints/cli.tsx"],
outdir,
target: "bun",
splitting: true,
define: getMacroDefines(),
features,
});
entrypoints: ['src/entrypoints/cli.tsx'],
outdir,
target: 'bun',
splitting: true,
define: getMacroDefines(),
features,
})

if (!result.success) {
console.error("Build failed:");
for (const log of result.logs) {
console.error(log);
}
process.exit(1);
console.error('Build failed:')
for (const log of result.logs) {
console.error(log)
}
process.exit(1)
}

// Step 3: Post-process — replace Bun-only `import.meta.require` with Node.js compatible version
const files = await readdir(outdir);
const IMPORT_META_REQUIRE = "var __require = import.meta.require;";
const COMPAT_REQUIRE = `var __require = typeof import.meta.require === "function" ? import.meta.require : (await import("module")).createRequire(import.meta.url);`;
const files = await readdir(outdir)
const IMPORT_META_REQUIRE = 'var __require = import.meta.require;'
const COMPAT_REQUIRE = `var __require = typeof import.meta.require === "function" ? import.meta.require : (await import("module")).createRequire(import.meta.url);`

let patched = 0;
let patched = 0
for (const file of files) {
if (!file.endsWith(".js")) continue;
const filePath = join(outdir, file);
const content = await readFile(filePath, "utf-8");
if (content.includes(IMPORT_META_REQUIRE)) {
await writeFile(
filePath,
content.replace(IMPORT_META_REQUIRE, COMPAT_REQUIRE),
);
patched++;
}
if (!file.endsWith('.js')) continue
const filePath = join(outdir, file)
const content = await readFile(filePath, 'utf-8')
if (content.includes(IMPORT_META_REQUIRE)) {
await writeFile(
filePath,
content.replace(IMPORT_META_REQUIRE, COMPAT_REQUIRE),
)
patched++
}
}

console.log(
`Bundled ${result.outputs.length} files to ${outdir}/ (patched ${patched} for Node.js compat)`,
);
`Bundled ${result.outputs.length} files to ${outdir}/ (patched ${patched} for Node.js compat)`,
)

// Step 4: Copy native .node addon files (audio-capture)
const vendorDir = join(outdir, "vendor", "audio-capture");
await cp("vendor/audio-capture", vendorDir, { recursive: true });
console.log(`Copied vendor/audio-capture/ → ${vendorDir}/`);
const vendorDir = join(outdir, 'vendor', 'audio-capture')
await cp('vendor/audio-capture', vendorDir, { recursive: true })
console.log(`Copied vendor/audio-capture/ → ${vendorDir}/`)

// Step 5: Bundle download-ripgrep script as standalone JS for postinstall
const rgScript = await Bun.build({
entrypoints: ["scripts/download-ripgrep.ts"],
outdir,
target: "node",
});
entrypoints: ['scripts/download-ripgrep.ts'],
outdir,
target: 'node',
})
if (!rgScript.success) {
console.error("Failed to bundle download-ripgrep script:");
for (const log of rgScript.logs) {
console.error(log);
}
// Non-fatal — postinstall fallback to bun run scripts/download-ripgrep.ts
console.error('Failed to bundle download-ripgrep script:')
for (const log of rgScript.logs) {
console.error(log)
}
// Non-fatal — postinstall fallback to bun run scripts/download-ripgrep.ts
} else {
console.log(`Bundled download-ripgrep script to ${outdir}/`);
console.log(`Bundled download-ripgrep script to ${outdir}/`)
}
Loading
Loading