soloforge 1.2.17 → 1.2.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/claude_code/pre_prompt_contract.js +2 -2
- package/dist/adapters/claude_code/pre_prompt_contract.js.map +1 -1
- package/dist/adapters/claude_code/server.d.ts.map +1 -1
- package/dist/adapters/claude_code/server.js +4 -2
- package/dist/adapters/claude_code/server.js.map +1 -1
- package/dist/adapters/claude_code/tools.d.ts +2 -2
- package/dist/adapters/claude_code/tools.d.ts.map +1 -1
- package/dist/adapters/claude_code/tools.js +189 -170
- package/dist/adapters/claude_code/tools.js.map +1 -1
- package/dist/adapters/codex/codex_config.d.ts.map +1 -1
- package/dist/adapters/codex/codex_config.js +16 -4
- package/dist/adapters/codex/codex_config.js.map +1 -1
- package/dist/adapters/trae/trae_config.d.ts +2 -1
- package/dist/adapters/trae/trae_config.d.ts.map +1 -1
- package/dist/adapters/trae/trae_config.js +4 -3
- package/dist/adapters/trae/trae_config.js.map +1 -1
- package/dist/bin/config_commands.d.ts +1 -1
- package/dist/bin/config_commands.js +4 -4
- package/dist/bin/config_commands.js.map +1 -1
- package/dist/bin/soloforge.d.ts.map +1 -1
- package/dist/bin/soloforge.js +85 -110
- package/dist/bin/soloforge.js.map +1 -1
- package/dist/engine/artifact_contract_registry.d.ts.map +1 -1
- package/dist/engine/artifact_contract_registry.js.map +1 -1
- package/dist/engine/audit_pool.d.ts.map +1 -1
- package/dist/engine/audit_pool.js +0 -1
- package/dist/engine/audit_pool.js.map +1 -1
- package/dist/engine/audit_sampler.d.ts.map +1 -1
- package/dist/engine/audit_sampler.js +0 -4
- package/dist/engine/audit_sampler.js.map +1 -1
- package/dist/engine/batch1_manifest.d.ts.map +1 -1
- package/dist/engine/batch1_manifest.js +0 -7
- package/dist/engine/batch1_manifest.js.map +1 -1
- package/dist/engine/batch1_scenario_registry.js +7 -7
- package/dist/engine/batch1_scenario_registry.js.map +1 -1
- package/dist/engine/batch1_scenario_runners.js +32 -32
- package/dist/engine/batch1_scenario_runners.js.map +1 -1
- package/dist/engine/capability_action_advisor.js +16 -16
- package/dist/engine/capability_action_advisor.js.map +1 -1
- package/dist/engine/classifier.d.ts.map +1 -1
- package/dist/engine/classifier.js +4 -3
- package/dist/engine/classifier.js.map +1 -1
- package/dist/engine/code_reviewer.d.ts.map +1 -1
- package/dist/engine/code_reviewer.js +10 -9
- package/dist/engine/code_reviewer.js.map +1 -1
- package/dist/engine/command_execution_contract.js +14 -14
- package/dist/engine/command_execution_contract.js.map +1 -1
- package/dist/engine/debug_log.d.ts +2 -0
- package/dist/engine/debug_log.d.ts.map +1 -0
- package/dist/engine/debug_log.js +7 -0
- package/dist/engine/debug_log.js.map +1 -0
- package/dist/engine/developer_sovereignty.js +15 -15
- package/dist/engine/developer_sovereignty.js.map +1 -1
- package/dist/engine/diff_ownership_store.d.ts.map +1 -1
- package/dist/engine/diff_ownership_store.js +1 -2
- package/dist/engine/diff_ownership_store.js.map +1 -1
- package/dist/engine/dual_layer_mechanism_registry.js +1 -1
- package/dist/engine/dual_layer_mechanism_registry.js.map +1 -1
- package/dist/engine/evolver.d.ts.map +1 -1
- package/dist/engine/evolver.js +11 -10
- package/dist/engine/evolver.js.map +1 -1
- package/dist/engine/governance_report.js +9 -9
- package/dist/engine/governance_report.js.map +1 -1
- package/dist/engine/implementation_roadmap_registry.js +5 -5
- package/dist/engine/implementation_roadmap_registry.js.map +1 -1
- package/dist/engine/input_material_extractor.js +3 -3
- package/dist/engine/input_material_extractor.js.map +1 -1
- package/dist/engine/intent_expander.d.ts.map +1 -1
- package/dist/engine/intent_expander.js +15 -14
- package/dist/engine/intent_expander.js.map +1 -1
- package/dist/engine/intent_router.d.ts.map +1 -1
- package/dist/engine/intent_router.js +17 -16
- package/dist/engine/intent_router.js.map +1 -1
- package/dist/engine/java_quality_guard.js +10 -10
- package/dist/engine/java_quality_guard.js.map +1 -1
- package/dist/engine/knowledge_injection_boundary.d.ts +1 -1
- package/dist/engine/knowledge_injection_boundary.d.ts.map +1 -1
- package/dist/engine/knowledge_injection_boundary.js +61 -60
- package/dist/engine/knowledge_injection_boundary.js.map +1 -1
- package/dist/engine/main_path_integration_contract.js +16 -16
- package/dist/engine/main_path_integration_contract.js.map +1 -1
- package/dist/engine/mechanism_contract_registry.d.ts +3 -4
- package/dist/engine/mechanism_contract_registry.d.ts.map +1 -1
- package/dist/engine/mechanism_contract_registry.js +23 -24
- package/dist/engine/mechanism_contract_registry.js.map +1 -1
- package/dist/engine/onboarding.js +20 -20
- package/dist/engine/onboarding.js.map +1 -1
- package/dist/engine/privacy_secret_contract.d.ts.map +1 -1
- package/dist/engine/privacy_secret_contract.js +49 -48
- package/dist/engine/privacy_secret_contract.js.map +1 -1
- package/dist/engine/regression_matrix.js +21 -21
- package/dist/engine/regression_matrix.js.map +1 -1
- package/dist/engine/route_decision_contract_verifier.js +24 -24
- package/dist/engine/route_decision_contract_verifier.js.map +1 -1
- package/dist/engine/scope_controller.d.ts.map +1 -1
- package/dist/engine/scope_controller.js +4 -3
- package/dist/engine/scope_controller.js.map +1 -1
- package/dist/engine/scope_lease.d.ts.map +1 -1
- package/dist/engine/scope_lease.js +0 -1
- package/dist/engine/scope_lease.js.map +1 -1
- package/dist/engine/task_context.js +3 -3
- package/dist/engine/task_context.js.map +1 -1
- package/dist/engine/test_generator.js +8 -8
- package/dist/engine/test_generator.js.map +1 -1
- package/dist/engine/tool_invocation_contract_registry.js +18 -18
- package/dist/engine/tool_invocation_contract_registry.js.map +1 -1
- package/dist/engine/traceability.js +6 -6
- package/dist/engine/traceability.js.map +1 -1
- package/dist/engine/user_feedback_contract.js +9 -9
- package/dist/engine/user_feedback_contract.js.map +1 -1
- package/dist/engine/verifier.d.ts.map +1 -1
- package/dist/engine/verifier.js +9 -8
- package/dist/engine/verifier.js.map +1 -1
- package/dist/engine/zero_config_init.d.ts.map +1 -1
- package/dist/engine/zero_config_init.js +84 -84
- package/dist/engine/zero_config_init.js.map +1 -1
- package/dist/git/operations.js +1 -1
- package/dist/git/operations.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -11
- package/dist/index.js.map +1 -1
- package/dist/knowledge/index_manager.d.ts.map +1 -1
- package/dist/knowledge/index_manager.js +18 -17
- package/dist/knowledge/index_manager.js.map +1 -1
- package/dist/knowledge/loader.js +2 -2
- package/dist/knowledge/loader.js.map +1 -1
- package/package.json +4 -2
package/dist/bin/soloforge.js
CHANGED
|
@@ -16,6 +16,7 @@ import { cmdConfigResolve, cmdConfigExplain, cmdConfigConfirm, cmdConfigUnset, l
|
|
|
16
16
|
import { resolveCurrentProjectConfigReports, validateConfigPrecedence, } from "../engine/config_precedence_contract.js";
|
|
17
17
|
import { isReadForbidden } from "../engine/privacy_secret_contract.js";
|
|
18
18
|
import { routeIntent } from "../engine/intent_router.js";
|
|
19
|
+
import { debugLog } from "../engine/debug_log.js";
|
|
19
20
|
const command = process.argv[2];
|
|
20
21
|
const args = process.argv.slice(3);
|
|
21
22
|
async function main() {
|
|
@@ -115,7 +116,7 @@ async function main() {
|
|
|
115
116
|
generate-claude-md 生成包含工作流规则的 CLAUDE.md
|
|
116
117
|
generate-trae 生成 .trae/mcp.json 和 .trae/rules/project_rules.md
|
|
117
118
|
generate-codex 生成 .codex/ 配置和 AGENTS.md
|
|
118
|
-
check-write PreToolUse hook,用于范围检查(读取
|
|
119
|
+
check-write PreToolUse hook,用于范围检查(读取 stdin JSON)
|
|
119
120
|
post-bash PostToolUse hook,用于跟踪 Bash 执行结果
|
|
120
121
|
validate 验证项目配置和知识文件(config.yaml 可选,缺失时自动推断)
|
|
121
122
|
validate-mechanisms 验证双层机制承载模型完整性(模板层 + 机制层)
|
|
@@ -147,12 +148,12 @@ async function cmdInitAuto() {
|
|
|
147
148
|
const icon = field.confidence === "high" ? "✅" : field.confidence === "medium" ? "⚠️" : "❓";
|
|
148
149
|
const valStr = typeof field.value === "object" ? JSON.stringify(field.value) : String(field.value);
|
|
149
150
|
console.log(` ${icon} ${key}: ${valStr} (${field.confidence}, ${field.confidence_source})`);
|
|
150
|
-
console.log(`
|
|
151
|
+
console.log(` 证据: ${field.evidence.join(", ")}`);
|
|
151
152
|
}
|
|
152
153
|
console.log("=".repeat(50));
|
|
153
154
|
console.log(`综合置信度: ${draft.confidence}`);
|
|
154
155
|
if (draft.advisory_notes.length > 0) {
|
|
155
|
-
console.log("
|
|
156
|
+
console.log("建议:");
|
|
156
157
|
for (const note of draft.advisory_notes) {
|
|
157
158
|
console.log(` - ${note}`);
|
|
158
159
|
}
|
|
@@ -160,13 +161,13 @@ async function cmdInitAuto() {
|
|
|
160
161
|
// Schema 校验 (advisory)
|
|
161
162
|
const validation = validateConfigDraft(draft);
|
|
162
163
|
if (validation.warnings.length > 0) {
|
|
163
|
-
console.log("\nschema
|
|
164
|
+
console.log("\nschema 建议:");
|
|
164
165
|
for (const w of validation.warnings) {
|
|
165
166
|
console.log(` ⚠️ ${w}`);
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
169
|
if (!validation.valid) {
|
|
169
|
-
console.log("\nschema
|
|
170
|
+
console.log("\nschema 错误:");
|
|
170
171
|
for (const e of validation.errors) {
|
|
171
172
|
console.log(` ❌ ${e}`);
|
|
172
173
|
}
|
|
@@ -224,9 +225,9 @@ function writeConfigWithEvidence(projectPath, draft, evidence) {
|
|
|
224
225
|
scope: draft.scope,
|
|
225
226
|
};
|
|
226
227
|
fss.writeFileSync(configPath, YAML.stringify(yamlConfig), "utf-8");
|
|
227
|
-
//
|
|
228
|
+
// 写入旧版 evidence 格式(向后兼容)+ schema v2 条目
|
|
228
229
|
fss.writeFileSync(evidencePath, JSON.stringify(evidence, null, 2), "utf-8");
|
|
229
|
-
//
|
|
230
|
+
// 同时写入 schema v2 条目
|
|
230
231
|
const v2Entries = Object.entries(evidence.fields).map(([key, field]) => ({
|
|
231
232
|
field_path: key,
|
|
232
233
|
value_hash: hashConfigValue(JSON.stringify(field.value)),
|
|
@@ -236,7 +237,7 @@ function writeConfigWithEvidence(projectPath, draft, evidence) {
|
|
|
236
237
|
lifetime: "project_persistent",
|
|
237
238
|
collected_at: evidence.generated_at || new Date().toISOString(),
|
|
238
239
|
}));
|
|
239
|
-
//
|
|
240
|
+
// 合并 v2 条目到 evidence 文件
|
|
240
241
|
try {
|
|
241
242
|
const existing = JSON.parse(fss.readFileSync(evidencePath, "utf-8"));
|
|
242
243
|
existing.entries = v2Entries;
|
|
@@ -274,7 +275,7 @@ async function cmdInitBlueprint() {
|
|
|
274
275
|
console.log("支持的框架关键词: Spring Boot, Go, Rust, Gradle, React, Vue, Angular, Next.js, Nuxt");
|
|
275
276
|
return;
|
|
276
277
|
}
|
|
277
|
-
//
|
|
278
|
+
// 从解析的蓝图构建草稿
|
|
278
279
|
const draft = {
|
|
279
280
|
name: path.basename(projectPath),
|
|
280
281
|
tech_stack: {
|
|
@@ -294,7 +295,7 @@ async function cmdInitBlueprint() {
|
|
|
294
295
|
confidence: "high",
|
|
295
296
|
};
|
|
296
297
|
const evidence = generateBlueprintEvidence(blueprintText, draft, projectPath);
|
|
297
|
-
//
|
|
298
|
+
// 创建目录 + 写入配置 + evidence
|
|
298
299
|
const dirs = [
|
|
299
300
|
".soloforge/state", ".soloforge/state/debt",
|
|
300
301
|
".soloforge/knowledge/patterns", ".soloforge/knowledge/patterns/core",
|
|
@@ -363,7 +364,7 @@ async function cmdInit() {
|
|
|
363
364
|
if (interactive) {
|
|
364
365
|
const config = await interactiveConfig(projectPath);
|
|
365
366
|
if (config) {
|
|
366
|
-
// config
|
|
367
|
+
// config 结构为 { draft, evidence }
|
|
367
368
|
writeConfigWithEvidence(projectPath, config.draft, config.evidence);
|
|
368
369
|
}
|
|
369
370
|
else if (!process.stdin.isTTY) {
|
|
@@ -388,7 +389,7 @@ async function cmdInit() {
|
|
|
388
389
|
},
|
|
389
390
|
};
|
|
390
391
|
fss.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2), "utf-8");
|
|
391
|
-
console.log("✅ .mcp.json
|
|
392
|
+
console.log("✅ .mcp.json 已创建(soloforge + playwright)");
|
|
392
393
|
}
|
|
393
394
|
// 生成 hooks
|
|
394
395
|
await cmdGenerateHooks(projectPath);
|
|
@@ -427,7 +428,7 @@ async function copyGlobalPatterns(projectPath) {
|
|
|
427
428
|
fss.copyFileSync(src, dest);
|
|
428
429
|
}
|
|
429
430
|
}
|
|
430
|
-
console.log(`✅ ${files.length}
|
|
431
|
+
console.log(`✅ ${files.length} 个默认模板已复制到 ~/.soloforge/patterns/`);
|
|
431
432
|
}
|
|
432
433
|
}
|
|
433
434
|
async function copyKnowledgeTemplates(projectPath) {
|
|
@@ -452,7 +453,7 @@ async function copyKnowledgeTemplates(projectPath) {
|
|
|
452
453
|
}
|
|
453
454
|
}
|
|
454
455
|
if (copiedCount > 0) {
|
|
455
|
-
console.log(`✅ ${copiedCount}
|
|
456
|
+
console.log(`✅ ${copiedCount} 个知识模板已复制到 .soloforge/knowledge/`);
|
|
456
457
|
}
|
|
457
458
|
}
|
|
458
459
|
}
|
|
@@ -467,7 +468,7 @@ async function generateAdapterConfigs(projectPath, adapter) {
|
|
|
467
468
|
},
|
|
468
469
|
};
|
|
469
470
|
fss.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2), "utf-8");
|
|
470
|
-
console.log("✅ .mcp.json
|
|
471
|
+
console.log("✅ .mcp.json 已创建(soloforge + playwright)");
|
|
471
472
|
}
|
|
472
473
|
await cmdGenerateHooks(projectPath);
|
|
473
474
|
await cmdGenerateClaudeMdInner(projectPath);
|
|
@@ -485,7 +486,7 @@ function ensureGitignore(projectPath) {
|
|
|
485
486
|
if (missing.length > 0) {
|
|
486
487
|
const addition = (content.endsWith("\n") ? "" : "\n") + missing.join("\n") + "\n";
|
|
487
488
|
fss.appendFileSync(gitignorePath, addition, "utf-8");
|
|
488
|
-
console.log(`✅ .gitignore
|
|
489
|
+
console.log(`✅ .gitignore 已更新: ${missing.join(", ")}`);
|
|
489
490
|
}
|
|
490
491
|
}
|
|
491
492
|
async function cmdGenerateHooks(projectPath) {
|
|
@@ -506,7 +507,7 @@ async function cmdGenerateHooks(projectPath) {
|
|
|
506
507
|
// 仅 merge hooks 字段,保留用户的 permissions/env 等设置
|
|
507
508
|
existing.hooks = hooksConfig.hooks;
|
|
508
509
|
fss.writeFileSync(settingsPath, JSON.stringify(existing, null, 2), "utf-8");
|
|
509
|
-
console.log("✅ .claude/settings.json
|
|
510
|
+
console.log("✅ .claude/settings.json 已更新 hooks 配置");
|
|
510
511
|
}
|
|
511
512
|
async function cmdGenerateClaudeMd() {
|
|
512
513
|
await cmdGenerateClaudeMdInner(process.cwd());
|
|
@@ -523,23 +524,23 @@ async function cmdGenerateCodexInner(projectPath) {
|
|
|
523
524
|
fss.mkdirSync(codexDir, { recursive: true });
|
|
524
525
|
const tomlContent = generateCodexMcpConfig(projectPath);
|
|
525
526
|
fss.writeFileSync(path.join(codexDir, "config.toml"), tomlContent, "utf-8");
|
|
526
|
-
console.log("✅ .codex/config.toml
|
|
527
|
+
console.log("✅ .codex/config.toml 已创建");
|
|
527
528
|
// 生成 .codex/hooks.json
|
|
528
529
|
const hooksContent = generateCodexHooksConfig();
|
|
529
530
|
fss.writeFileSync(path.join(codexDir, "hooks.json"), hooksContent, "utf-8");
|
|
530
|
-
console.log("✅ .codex/hooks.json
|
|
531
|
+
console.log("✅ .codex/hooks.json 已创建");
|
|
531
532
|
// 生成 AGENTS.md
|
|
532
533
|
const { config } = await resolveProjectConfig(projectPath);
|
|
533
534
|
const agentsContent = generateCodexAgentsMd(config);
|
|
534
535
|
fss.writeFileSync(path.join(projectPath, "AGENTS.md"), agentsContent, "utf-8");
|
|
535
|
-
console.log("✅ AGENTS.md
|
|
536
|
+
console.log("✅ AGENTS.md 已创建");
|
|
536
537
|
}
|
|
537
538
|
async function cmdGenerateTraeInner(projectPath) {
|
|
538
539
|
// 生成 .trae/mcp.json
|
|
539
540
|
const traeDir = path.join(projectPath, ".trae");
|
|
540
541
|
fss.mkdirSync(traeDir, { recursive: true });
|
|
541
542
|
const traeMcpPath = path.join(traeDir, "mcp.json");
|
|
542
|
-
const traeMcpConfig = generateTraeMcpConfig();
|
|
543
|
+
const traeMcpConfig = generateTraeMcpConfig(projectPath);
|
|
543
544
|
fss.writeFileSync(traeMcpPath, JSON.stringify(traeMcpConfig, null, 2), "utf-8");
|
|
544
545
|
console.log("✅ .trae/mcp.json created");
|
|
545
546
|
// 生成 .trae/rules/project_rules.md
|
|
@@ -557,16 +558,11 @@ async function cmdGenerateClaudeMdInner(projectPath) {
|
|
|
557
558
|
console.log("✅ CLAUDE.md created");
|
|
558
559
|
}
|
|
559
560
|
async function cmdCheckWrite() {
|
|
560
|
-
|
|
561
|
+
debugLog("CLI: 执行 cmdCheckWrite");
|
|
561
562
|
try {
|
|
562
|
-
//
|
|
563
|
-
const isClaudeCode = !!process.env.TOOL_INPUT;
|
|
563
|
+
// 所有适配器均通过 stdin 读取 JSON(Claude Code / Codex 统一)
|
|
564
564
|
let toolInput;
|
|
565
|
-
|
|
566
|
-
toolInput = process.env.TOOL_INPUT;
|
|
567
|
-
}
|
|
568
|
-
else {
|
|
569
|
-
// Codex: 从 stdin 读取 JSON
|
|
565
|
+
{
|
|
570
566
|
const chunks = [];
|
|
571
567
|
for await (const chunk of process.stdin) {
|
|
572
568
|
chunks.push(chunk);
|
|
@@ -585,13 +581,11 @@ async function cmdCheckWrite() {
|
|
|
585
581
|
const parsed = JSON.parse(toolInput);
|
|
586
582
|
let filePath;
|
|
587
583
|
let content;
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
// Codex stdin JSON 使用不同的字段名
|
|
594
|
-
const input = parsed.input || parsed;
|
|
584
|
+
{
|
|
585
|
+
// Claude Code: { tool_input: { file_path, content/new_string } } 格式
|
|
586
|
+
// Codex: { input: { file_path, content/new_string } } 格式
|
|
587
|
+
// 回退: 顶层 { file_path, content/new_string }
|
|
588
|
+
const input = parsed.tool_input || parsed.input || parsed;
|
|
595
589
|
filePath = input.file_path || input.path || "";
|
|
596
590
|
content = input.content || input.new_string || "";
|
|
597
591
|
}
|
|
@@ -607,57 +601,36 @@ async function cmdCheckWrite() {
|
|
|
607
601
|
// 使用 scope_controller 统一检查
|
|
608
602
|
const { checkScope } = await import("../engine/scope_controller.js");
|
|
609
603
|
const result = checkScope(filePath, allowedPaths, content || undefined);
|
|
610
|
-
//
|
|
604
|
+
// 隐私/秘密契约:禁止读取的模式
|
|
611
605
|
if (isReadForbidden(filePath)) {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
}
|
|
621
|
-
else {
|
|
622
|
-
process.stdout.write(JSON.stringify({
|
|
623
|
-
hookSpecificOutput: {
|
|
624
|
-
hookEventName: "PreToolUse",
|
|
625
|
-
permissionDecision: "deny",
|
|
626
|
-
permissionDecisionReason: `隐私策略: ${filePath} 匹配禁止读取模式`,
|
|
627
|
-
},
|
|
628
|
-
}));
|
|
629
|
-
process.exit(0);
|
|
630
|
-
}
|
|
606
|
+
process.stdout.write(JSON.stringify({
|
|
607
|
+
hookSpecificOutput: {
|
|
608
|
+
hookEventName: "PreToolUse",
|
|
609
|
+
permissionDecision: "deny",
|
|
610
|
+
permissionDecisionReason: `隐私策略: ${filePath} 匹配禁止读取模式`,
|
|
611
|
+
},
|
|
612
|
+
}));
|
|
613
|
+
process.exit(0);
|
|
631
614
|
}
|
|
632
615
|
if (result.severity === "blocked") {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
permissionDecision: "deny",
|
|
643
|
-
permissionDecisionReason: result.reason || "Write blocked by scope check",
|
|
644
|
-
},
|
|
645
|
-
};
|
|
646
|
-
process.stdout.write(JSON.stringify(denyOutput));
|
|
647
|
-
process.exit(0);
|
|
648
|
-
}
|
|
616
|
+
const denyOutput = {
|
|
617
|
+
hookSpecificOutput: {
|
|
618
|
+
hookEventName: "PreToolUse",
|
|
619
|
+
permissionDecision: "deny",
|
|
620
|
+
permissionDecisionReason: result.reason || "范围检查阻止了写入",
|
|
621
|
+
},
|
|
622
|
+
};
|
|
623
|
+
process.stdout.write(JSON.stringify(denyOutput));
|
|
624
|
+
process.exit(0);
|
|
649
625
|
}
|
|
650
626
|
if (result.severity === "warning") {
|
|
651
|
-
|
|
652
|
-
console.error(JSON.stringify(result));
|
|
653
|
-
}
|
|
654
|
-
// 警告不阻止操作
|
|
627
|
+
console.error(JSON.stringify(result));
|
|
655
628
|
}
|
|
656
629
|
process.exit(0); // 允许
|
|
657
630
|
}
|
|
658
631
|
catch (e) {
|
|
659
|
-
console.error(`SoloForge
|
|
660
|
-
process.exit(1);
|
|
632
|
+
console.error(`SoloForge 写入检查错误: ${e instanceof Error ? e.message : String(e)}`);
|
|
633
|
+
process.exit(1);
|
|
661
634
|
}
|
|
662
635
|
}
|
|
663
636
|
function resolveProjectPath() {
|
|
@@ -679,16 +652,16 @@ function resolveProjectPath() {
|
|
|
679
652
|
return process.cwd();
|
|
680
653
|
}
|
|
681
654
|
async function cmdPostBash() {
|
|
682
|
-
|
|
655
|
+
debugLog("CLI: 执行 cmdPostBash");
|
|
683
656
|
// Bash 的 PostToolUse hook — 目前为空操作占位符
|
|
684
657
|
// 未来: 跟踪命令结果,检测构建/测试失败
|
|
685
658
|
process.exit(0);
|
|
686
659
|
}
|
|
687
660
|
export { buildHookAdditionalContext } from "../adapters/claude_code/pre_prompt_contract.js";
|
|
688
661
|
async function cmdPrePrompt() {
|
|
689
|
-
|
|
662
|
+
debugLog("CLI: 执行 cmdPrePrompt");
|
|
690
663
|
try {
|
|
691
|
-
// UserPromptSubmit hook
|
|
664
|
+
// UserPromptSubmit hook 从 stdin 接收 JSON { prompt: "..." }
|
|
692
665
|
const chunks = [];
|
|
693
666
|
for await (const chunk of process.stdin) {
|
|
694
667
|
chunks.push(chunk);
|
|
@@ -710,21 +683,23 @@ async function cmdPrePrompt() {
|
|
|
710
683
|
process.exit(0);
|
|
711
684
|
return;
|
|
712
685
|
}
|
|
713
|
-
//
|
|
686
|
+
// 使用意图路由器分类 — 如果路由属于 SoloForge 范畴,注入结构化上下文
|
|
714
687
|
const decision = routeIntent({ intent: userMessage });
|
|
715
688
|
const additionalContext = buildHookAdditionalContext(decision);
|
|
716
689
|
if (additionalContext) {
|
|
717
690
|
const output = {
|
|
718
691
|
hookSpecificOutput: {
|
|
719
692
|
hookEventName: "UserPromptSubmit",
|
|
720
|
-
additionalContext
|
|
693
|
+
additionalContext: typeof additionalContext === "string"
|
|
694
|
+
? additionalContext
|
|
695
|
+
: JSON.stringify(additionalContext),
|
|
721
696
|
},
|
|
722
697
|
};
|
|
723
698
|
process.stdout.write(JSON.stringify(output));
|
|
724
699
|
}
|
|
725
700
|
}
|
|
726
701
|
catch (e) {
|
|
727
|
-
console.error("[soloForge] pre-prompt hook
|
|
702
|
+
console.error("[soloForge] pre-prompt hook 错误:", e);
|
|
728
703
|
}
|
|
729
704
|
process.exit(0);
|
|
730
705
|
}
|
|
@@ -736,19 +711,19 @@ async function checkEvidenceWarnings(projectPath) {
|
|
|
736
711
|
if (!fss.existsSync(configPath))
|
|
737
712
|
return warnings;
|
|
738
713
|
if (!fss.existsSync(evidencePath)) {
|
|
739
|
-
warnings.push("config.yaml
|
|
714
|
+
warnings.push("config.yaml 存在但 config.evidence.json 缺失 — 来源追溯性丢失");
|
|
740
715
|
return warnings;
|
|
741
716
|
}
|
|
742
|
-
//
|
|
717
|
+
// 检查 mtime 过期
|
|
743
718
|
try {
|
|
744
719
|
const configStat = fss.statSync(configPath);
|
|
745
720
|
const evidenceStat = fss.statSync(evidencePath);
|
|
746
721
|
if (configStat.mtimeMs > evidenceStat.mtimeMs) {
|
|
747
|
-
warnings.push("config.evidence.json
|
|
722
|
+
warnings.push("config.evidence.json 已过期(config.yaml 在 evidence 生成后被修改)");
|
|
748
723
|
}
|
|
749
724
|
}
|
|
750
725
|
catch { }
|
|
751
|
-
//
|
|
726
|
+
// 旧版字段一致性检查(schema v1 evidence)
|
|
752
727
|
try {
|
|
753
728
|
const evidenceRaw = JSON.parse(fss.readFileSync(evidencePath, "utf-8"));
|
|
754
729
|
const configContent = fss.readFileSync(configPath, "utf-8");
|
|
@@ -756,23 +731,23 @@ async function checkEvidenceWarnings(projectPath) {
|
|
|
756
731
|
for (const [key, field] of Object.entries(evidenceRaw.fields)) {
|
|
757
732
|
const actualValue = getConfigFieldValue(config, key);
|
|
758
733
|
if (actualValue !== undefined && JSON.stringify(field.value) !== JSON.stringify(actualValue)) {
|
|
759
|
-
warnings.push(`evidence
|
|
734
|
+
warnings.push(`evidence 字段 '${key}' 的值与 config.yaml 不匹配`);
|
|
760
735
|
}
|
|
761
736
|
}
|
|
762
737
|
}
|
|
763
738
|
catch { }
|
|
764
|
-
//
|
|
739
|
+
// 使用 schema v2 条目验证配置优先级
|
|
765
740
|
try {
|
|
766
741
|
const entries = loadConfigEvidenceEntries(projectPath);
|
|
767
|
-
//
|
|
742
|
+
// 检查 C 类字段自动写入违规
|
|
768
743
|
for (const e of entries) {
|
|
769
744
|
if (e.source === "cli_flag" || e.source === "mcp_param") {
|
|
770
745
|
if (e.lifetime === "project_persistent") {
|
|
771
|
-
warnings.push(`CLI/MCP
|
|
746
|
+
warnings.push(`CLI/MCP 参数(${e.source})已持久化到 config — 字段: ${e.field_path}`);
|
|
772
747
|
}
|
|
773
748
|
}
|
|
774
749
|
}
|
|
775
|
-
//
|
|
750
|
+
// 运行实时解析的配置优先级验证
|
|
776
751
|
try {
|
|
777
752
|
const { reports } = await resolveCurrentProjectConfigReports(projectPath);
|
|
778
753
|
const findings = validateConfigPrecedence(reports, entries);
|
|
@@ -803,13 +778,13 @@ async function cmdValidate() {
|
|
|
803
778
|
try {
|
|
804
779
|
const { config, source } = await resolveProjectConfig(projectPath);
|
|
805
780
|
if (source === "inferred") {
|
|
806
|
-
console.log("ℹ️
|
|
807
|
-
console.log("
|
|
781
|
+
console.log("ℹ️ 未找到 .soloforge/config.yaml — 使用自动推断的配置。");
|
|
782
|
+
console.log(" 运行 'soloforge init --auto' 可持久化。");
|
|
808
783
|
}
|
|
809
784
|
else {
|
|
810
|
-
console.log("✅ config.yaml
|
|
785
|
+
console.log("✅ config.yaml 有效");
|
|
811
786
|
}
|
|
812
|
-
//
|
|
787
|
+
// 使用共享函数验证配置优先级
|
|
813
788
|
let hasHardFail = false;
|
|
814
789
|
try {
|
|
815
790
|
const { reports, entries } = await resolveCurrentProjectConfigReports(projectPath);
|
|
@@ -834,7 +809,7 @@ async function cmdValidate() {
|
|
|
834
809
|
}
|
|
835
810
|
}
|
|
836
811
|
catch { }
|
|
837
|
-
// Evidence
|
|
812
|
+
// Evidence 检查
|
|
838
813
|
const evidenceWarnings = await checkEvidenceWarnings(projectPath);
|
|
839
814
|
for (const w of evidenceWarnings) {
|
|
840
815
|
console.log(`⚠️ evidence: ${w}`);
|
|
@@ -846,10 +821,10 @@ async function cmdValidate() {
|
|
|
846
821
|
const index = new KnowledgeIndexManager(config);
|
|
847
822
|
await index.build();
|
|
848
823
|
const entries = index.getAllEntries();
|
|
849
|
-
console.log(`✅ ${entries.project.length}
|
|
824
|
+
console.log(`✅ ${entries.project.length} 条项目知识已索引`);
|
|
850
825
|
const issues = await index.checkHealth();
|
|
851
826
|
if (issues.length > 0) {
|
|
852
|
-
console.log(`⚠️ ${issues.length}
|
|
827
|
+
console.log(`⚠️ ${issues.length} 个健康问题:`);
|
|
853
828
|
for (const { entry, issue } of issues) {
|
|
854
829
|
console.log(` - ${entry.name}: ${issue}`);
|
|
855
830
|
}
|
|
@@ -872,7 +847,7 @@ async function cmdValidate() {
|
|
|
872
847
|
else {
|
|
873
848
|
console.log(`✅ 双层机制验证: ${dlAdvisory.length} advisory, 0 hard_fail`);
|
|
874
849
|
}
|
|
875
|
-
} //
|
|
850
|
+
} // 双层机制检查结束
|
|
876
851
|
if (hasHardFail) {
|
|
877
852
|
console.error("❌ validate 失败: 配置优先级存在 hard_fail");
|
|
878
853
|
process.exit(1);
|
|
@@ -885,7 +860,7 @@ async function cmdValidate() {
|
|
|
885
860
|
}
|
|
886
861
|
async function cmdValidateMechanisms() {
|
|
887
862
|
console.error("[soloForge] CLI: 执行 cmdValidateMechanisms");
|
|
888
|
-
//
|
|
863
|
+
// 从编译后的二进制位置推导根目录: dist/bin/soloforge.js -> 项目根
|
|
889
864
|
const soloforgeRoot = path.resolve(import.meta.dirname, "..", "..");
|
|
890
865
|
const projectPath = fss.existsSync(path.join(soloforgeRoot, "src", "engine"))
|
|
891
866
|
? soloforgeRoot
|
|
@@ -915,7 +890,7 @@ async function cmdValidateMechanisms() {
|
|
|
915
890
|
process.exit(1);
|
|
916
891
|
}
|
|
917
892
|
console.log(`\n✅ 双层机制验证通过,${advisory.length} advisory findings`);
|
|
918
|
-
//
|
|
893
|
+
// 审计摘要
|
|
919
894
|
try {
|
|
920
895
|
const { auditTemplateMechanisms } = await import("../engine/template_mechanism_auditor.js");
|
|
921
896
|
const audit = auditTemplateMechanisms(projectPath);
|
|
@@ -927,7 +902,7 @@ async function cmdValidateMechanisms() {
|
|
|
927
902
|
}
|
|
928
903
|
}
|
|
929
904
|
catch {
|
|
930
|
-
//
|
|
905
|
+
// 审计仅为建议,不阻断
|
|
931
906
|
}
|
|
932
907
|
}
|
|
933
908
|
catch (e) {
|
|
@@ -939,7 +914,7 @@ async function cmdAuditTemplateMechanisms() {
|
|
|
939
914
|
console.error("[soloForge] CLI: 执行 cmdAuditTemplateMechanisms");
|
|
940
915
|
const asJson = args.includes("--json");
|
|
941
916
|
const changedOnly = args.includes("--changed-only");
|
|
942
|
-
//
|
|
917
|
+
// 从编译后的二进制位置推导根目录
|
|
943
918
|
const soloforgeRoot = path.resolve(import.meta.dirname, "..", "..");
|
|
944
919
|
const projectPath = fss.existsSync(path.join(soloforgeRoot, "src", "engine"))
|
|
945
920
|
? soloforgeRoot
|
|
@@ -954,7 +929,7 @@ async function cmdAuditTemplateMechanisms() {
|
|
|
954
929
|
}
|
|
955
930
|
return;
|
|
956
931
|
}
|
|
957
|
-
//
|
|
932
|
+
// 人类可读输出
|
|
958
933
|
console.log(`模板-机制审计报告 (${report.generated_at})`);
|
|
959
934
|
console.log(` 模板文件: ${report.total_template_files}`);
|
|
960
935
|
console.log(` 注册资产: ${report.total_registered_assets}`);
|
|
@@ -1067,7 +1042,7 @@ async function cmdValidateBatch1() {
|
|
|
1067
1042
|
console.log(`${ri} ${r.rule}: ${r.evidence}`);
|
|
1068
1043
|
}
|
|
1069
1044
|
}
|
|
1070
|
-
console.log(allPass ? "\
|
|
1045
|
+
console.log(allPass ? "\n所有场景通过" : "\n部分场景失败");
|
|
1071
1046
|
}
|
|
1072
1047
|
process.exit(allPass ? 0 : 1);
|
|
1073
1048
|
}
|
|
@@ -1080,7 +1055,7 @@ async function cmdStatus() {
|
|
|
1080
1055
|
console.log(` 产品: ${config.product_profile}`);
|
|
1081
1056
|
console.log(` 后端: ${config.tech_stack.backend.framework || "未配置"}`);
|
|
1082
1057
|
console.log(` 前端: ${config.tech_stack.frontend.framework || "未配置"}`);
|
|
1083
|
-
//
|
|
1058
|
+
// 逐字段配置优先级状态
|
|
1084
1059
|
try {
|
|
1085
1060
|
const { resolutions, entries } = await resolveCurrentProjectConfigReports(projectPath);
|
|
1086
1061
|
if (resolutions.length > 0) {
|
|
@@ -1096,7 +1071,7 @@ async function cmdStatus() {
|
|
|
1096
1071
|
if (fss.existsSync(schemaPath)) {
|
|
1097
1072
|
try {
|
|
1098
1073
|
const raw = JSON.parse(fss.readFileSync(schemaPath, "utf-8"));
|
|
1099
|
-
console.log(` evidence: schema_version=${raw.schema_version ?? 1}, ${entries.length}
|
|
1074
|
+
console.log(` evidence: schema_version=${raw.schema_version ?? 1}, ${entries.length} 条`);
|
|
1100
1075
|
}
|
|
1101
1076
|
catch {
|
|
1102
1077
|
console.log(" evidence: 无法解析");
|
|
@@ -1107,7 +1082,7 @@ async function cmdStatus() {
|
|
|
1107
1082
|
}
|
|
1108
1083
|
}
|
|
1109
1084
|
catch { }
|
|
1110
|
-
// Evidence
|
|
1085
|
+
// Evidence 警告
|
|
1111
1086
|
const evidenceWarnings = await checkEvidenceWarnings(projectPath);
|
|
1112
1087
|
for (const w of evidenceWarnings) {
|
|
1113
1088
|
console.log(` ⚠️ evidence: ${w}`);
|