@wooojin/forgen 0.1.0
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/.claude-plugin/plugin.json +20 -0
- package/CHANGELOG.md +353 -0
- package/CONTRIBUTING.md +98 -0
- package/LICENSE +21 -0
- package/README.ja.md +469 -0
- package/README.ko.md +469 -0
- package/README.md +483 -0
- package/README.zh.md +469 -0
- package/agents/analyst.md +98 -0
- package/agents/architect.md +62 -0
- package/agents/code-reviewer.md +120 -0
- package/agents/code-simplifier.md +197 -0
- package/agents/critic.md +70 -0
- package/agents/debugger.md +117 -0
- package/agents/designer.md +131 -0
- package/agents/executor.md +54 -0
- package/agents/explore.md +145 -0
- package/agents/git-master.md +212 -0
- package/agents/performance-reviewer.md +172 -0
- package/agents/planner.md +29 -0
- package/agents/qa-tester.md +158 -0
- package/agents/refactoring-expert.md +168 -0
- package/agents/scientist.md +144 -0
- package/agents/security-reviewer.md +137 -0
- package/agents/test-engineer.md +153 -0
- package/agents/verifier.md +133 -0
- package/agents/writer.md +184 -0
- package/commands/api-design.md +268 -0
- package/commands/architecture-decision.md +314 -0
- package/commands/ci-cd.md +270 -0
- package/commands/code-review.md +233 -0
- package/commands/compound.md +117 -0
- package/commands/database.md +263 -0
- package/commands/debug-detective.md +99 -0
- package/commands/docker.md +274 -0
- package/commands/documentation.md +276 -0
- package/commands/ecomode.md +51 -0
- package/commands/frontend.md +271 -0
- package/commands/git-master.md +90 -0
- package/commands/incident-response.md +292 -0
- package/commands/migrate.md +101 -0
- package/commands/performance.md +288 -0
- package/commands/refactor.md +105 -0
- package/commands/security-review.md +288 -0
- package/commands/tdd.md +183 -0
- package/commands/testing-strategy.md +265 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +295 -0
- package/dist/core/auto-compound-runner.d.ts +12 -0
- package/dist/core/auto-compound-runner.js +460 -0
- package/dist/core/config-hooks.d.ts +10 -0
- package/dist/core/config-hooks.js +112 -0
- package/dist/core/config-injector.d.ts +50 -0
- package/dist/core/config-injector.js +455 -0
- package/dist/core/doctor.d.ts +1 -0
- package/dist/core/doctor.js +163 -0
- package/dist/core/errors.d.ts +81 -0
- package/dist/core/errors.js +133 -0
- package/dist/core/global-config.d.ts +43 -0
- package/dist/core/global-config.js +25 -0
- package/dist/core/harness.d.ts +24 -0
- package/dist/core/harness.js +621 -0
- package/dist/core/init.d.ts +7 -0
- package/dist/core/init.js +37 -0
- package/dist/core/inspect-cli.d.ts +7 -0
- package/dist/core/inspect-cli.js +47 -0
- package/dist/core/legacy-detector.d.ts +33 -0
- package/dist/core/legacy-detector.js +66 -0
- package/dist/core/logger.d.ts +34 -0
- package/dist/core/logger.js +121 -0
- package/dist/core/mcp-config.d.ts +44 -0
- package/dist/core/mcp-config.js +177 -0
- package/dist/core/notepad.d.ts +31 -0
- package/dist/core/notepad.js +88 -0
- package/dist/core/paths.d.ts +85 -0
- package/dist/core/paths.js +101 -0
- package/dist/core/plugin-detector.d.ts +44 -0
- package/dist/core/plugin-detector.js +226 -0
- package/dist/core/runtime-detector.d.ts +8 -0
- package/dist/core/runtime-detector.js +49 -0
- package/dist/core/scope-resolver.d.ts +8 -0
- package/dist/core/scope-resolver.js +45 -0
- package/dist/core/session-logger.d.ts +6 -0
- package/dist/core/session-logger.js +111 -0
- package/dist/core/session-store.d.ts +28 -0
- package/dist/core/session-store.js +218 -0
- package/dist/core/settings-lock.d.ts +18 -0
- package/dist/core/settings-lock.js +125 -0
- package/dist/core/spawn.d.ts +3 -0
- package/dist/core/spawn.js +135 -0
- package/dist/core/types.d.ts +108 -0
- package/dist/core/types.js +1 -0
- package/dist/core/uninstall.d.ts +4 -0
- package/dist/core/uninstall.js +307 -0
- package/dist/core/v1-bootstrap.d.ts +26 -0
- package/dist/core/v1-bootstrap.js +155 -0
- package/dist/engine/compound-cli.d.ts +24 -0
- package/dist/engine/compound-cli.js +250 -0
- package/dist/engine/compound-extractor.d.ts +68 -0
- package/dist/engine/compound-extractor.js +860 -0
- package/dist/engine/compound-lifecycle.d.ts +32 -0
- package/dist/engine/compound-lifecycle.js +305 -0
- package/dist/engine/compound-loop.d.ts +32 -0
- package/dist/engine/compound-loop.js +511 -0
- package/dist/engine/match-eval-log.d.ts +139 -0
- package/dist/engine/match-eval-log.js +270 -0
- package/dist/engine/phrase-blocklist.d.ts +119 -0
- package/dist/engine/phrase-blocklist.js +208 -0
- package/dist/engine/skill-promoter.d.ts +20 -0
- package/dist/engine/skill-promoter.js +115 -0
- package/dist/engine/solution-format.d.ts +160 -0
- package/dist/engine/solution-format.js +432 -0
- package/dist/engine/solution-index.d.ts +13 -0
- package/dist/engine/solution-index.js +252 -0
- package/dist/engine/solution-matcher.d.ts +364 -0
- package/dist/engine/solution-matcher.js +656 -0
- package/dist/engine/solution-writer.d.ts +76 -0
- package/dist/engine/solution-writer.js +157 -0
- package/dist/engine/term-matcher.d.ts +81 -0
- package/dist/engine/term-matcher.js +268 -0
- package/dist/engine/term-normalizer.d.ts +116 -0
- package/dist/engine/term-normalizer.js +171 -0
- package/dist/fgx.d.ts +6 -0
- package/dist/fgx.js +42 -0
- package/dist/forge/cli.d.ts +11 -0
- package/dist/forge/cli.js +100 -0
- package/dist/forge/evidence-processor.d.ts +21 -0
- package/dist/forge/evidence-processor.js +87 -0
- package/dist/forge/mismatch-detector.d.ts +44 -0
- package/dist/forge/mismatch-detector.js +83 -0
- package/dist/forge/onboarding-cli.d.ts +6 -0
- package/dist/forge/onboarding-cli.js +89 -0
- package/dist/forge/onboarding.d.ts +25 -0
- package/dist/forge/onboarding.js +122 -0
- package/dist/hooks/compound-reflection.d.ts +45 -0
- package/dist/hooks/compound-reflection.js +82 -0
- package/dist/hooks/context-guard.d.ts +24 -0
- package/dist/hooks/context-guard.js +156 -0
- package/dist/hooks/dangerous-patterns.json +18 -0
- package/dist/hooks/db-guard.d.ts +17 -0
- package/dist/hooks/db-guard.js +105 -0
- package/dist/hooks/hook-config.d.ts +29 -0
- package/dist/hooks/hook-config.js +92 -0
- package/dist/hooks/hook-registry.d.ts +43 -0
- package/dist/hooks/hook-registry.js +31 -0
- package/dist/hooks/hooks-generator.d.ts +49 -0
- package/dist/hooks/hooks-generator.js +99 -0
- package/dist/hooks/intent-classifier.d.ts +12 -0
- package/dist/hooks/intent-classifier.js +62 -0
- package/dist/hooks/keyword-detector.d.ts +25 -0
- package/dist/hooks/keyword-detector.js +389 -0
- package/dist/hooks/notepad-injector.d.ts +18 -0
- package/dist/hooks/notepad-injector.js +51 -0
- package/dist/hooks/permission-handler.d.ts +14 -0
- package/dist/hooks/permission-handler.js +114 -0
- package/dist/hooks/post-tool-failure.d.ts +11 -0
- package/dist/hooks/post-tool-failure.js +118 -0
- package/dist/hooks/post-tool-handlers.d.ts +17 -0
- package/dist/hooks/post-tool-handlers.js +115 -0
- package/dist/hooks/post-tool-use.d.ts +29 -0
- package/dist/hooks/post-tool-use.js +151 -0
- package/dist/hooks/pre-compact.d.ts +10 -0
- package/dist/hooks/pre-compact.js +165 -0
- package/dist/hooks/pre-tool-use.d.ts +31 -0
- package/dist/hooks/pre-tool-use.js +325 -0
- package/dist/hooks/prompt-injection-filter.d.ts +56 -0
- package/dist/hooks/prompt-injection-filter.js +287 -0
- package/dist/hooks/rate-limiter.d.ts +21 -0
- package/dist/hooks/rate-limiter.js +86 -0
- package/dist/hooks/secret-filter.d.ts +14 -0
- package/dist/hooks/secret-filter.js +65 -0
- package/dist/hooks/session-recovery.d.ts +27 -0
- package/dist/hooks/session-recovery.js +406 -0
- package/dist/hooks/shared/atomic-write.d.ts +41 -0
- package/dist/hooks/shared/atomic-write.js +148 -0
- package/dist/hooks/shared/context-budget.d.ts +37 -0
- package/dist/hooks/shared/context-budget.js +45 -0
- package/dist/hooks/shared/file-lock.d.ts +56 -0
- package/dist/hooks/shared/file-lock.js +253 -0
- package/dist/hooks/shared/hook-response.d.ts +33 -0
- package/dist/hooks/shared/hook-response.js +62 -0
- package/dist/hooks/shared/injection-caps.d.ts +39 -0
- package/dist/hooks/shared/injection-caps.js +52 -0
- package/dist/hooks/shared/plugin-signal.d.ts +23 -0
- package/dist/hooks/shared/plugin-signal.js +104 -0
- package/dist/hooks/shared/read-stdin.d.ts +8 -0
- package/dist/hooks/shared/read-stdin.js +63 -0
- package/dist/hooks/shared/sanitize-id.d.ts +7 -0
- package/dist/hooks/shared/sanitize-id.js +9 -0
- package/dist/hooks/shared/sanitize.d.ts +7 -0
- package/dist/hooks/shared/sanitize.js +22 -0
- package/dist/hooks/skill-injector.d.ts +38 -0
- package/dist/hooks/skill-injector.js +285 -0
- package/dist/hooks/slop-detector.d.ts +18 -0
- package/dist/hooks/slop-detector.js +93 -0
- package/dist/hooks/solution-injector.d.ts +58 -0
- package/dist/hooks/solution-injector.js +436 -0
- package/dist/hooks/subagent-tracker.d.ts +10 -0
- package/dist/hooks/subagent-tracker.js +90 -0
- package/dist/i18n/index.d.ts +43 -0
- package/dist/i18n/index.js +224 -0
- package/dist/lib.d.ts +14 -0
- package/dist/lib.js +14 -0
- package/dist/mcp/server.d.ts +8 -0
- package/dist/mcp/server.js +40 -0
- package/dist/mcp/solution-reader.d.ts +90 -0
- package/dist/mcp/solution-reader.js +273 -0
- package/dist/mcp/tools.d.ts +16 -0
- package/dist/mcp/tools.js +302 -0
- package/dist/preset/facet-catalog.d.ts +17 -0
- package/dist/preset/facet-catalog.js +46 -0
- package/dist/preset/preset-manager.d.ts +31 -0
- package/dist/preset/preset-manager.js +111 -0
- package/dist/renderer/inspect-renderer.d.ts +11 -0
- package/dist/renderer/inspect-renderer.js +123 -0
- package/dist/renderer/rule-renderer.d.ts +18 -0
- package/dist/renderer/rule-renderer.js +159 -0
- package/dist/store/evidence-store.d.ts +23 -0
- package/dist/store/evidence-store.js +58 -0
- package/dist/store/profile-store.d.ts +12 -0
- package/dist/store/profile-store.js +53 -0
- package/dist/store/recommendation-store.d.ts +22 -0
- package/dist/store/recommendation-store.js +64 -0
- package/dist/store/rule-store.d.ts +22 -0
- package/dist/store/rule-store.js +62 -0
- package/dist/store/session-state-store.d.ts +11 -0
- package/dist/store/session-state-store.js +44 -0
- package/dist/store/types.d.ts +159 -0
- package/dist/store/types.js +7 -0
- package/hooks/hook-registry.json +21 -0
- package/hooks/hooks.json +185 -0
- package/package.json +89 -0
- package/plugin.json +20 -0
- package/scripts/postinstall.js +826 -0
- package/skills/api-design/SKILL.md +262 -0
- package/skills/architecture-decision/SKILL.md +309 -0
- package/skills/ci-cd/SKILL.md +264 -0
- package/skills/code-review/SKILL.md +228 -0
- package/skills/compound/SKILL.md +101 -0
- package/skills/database/SKILL.md +257 -0
- package/skills/debug-detective/SKILL.md +95 -0
- package/skills/docker/SKILL.md +268 -0
- package/skills/documentation/SKILL.md +270 -0
- package/skills/ecomode/SKILL.md +46 -0
- package/skills/frontend/SKILL.md +265 -0
- package/skills/git-master/SKILL.md +86 -0
- package/skills/incident-response/SKILL.md +286 -0
- package/skills/migrate/SKILL.md +96 -0
- package/skills/performance/SKILL.md +282 -0
- package/skills/refactor/SKILL.md +100 -0
- package/skills/security-review/SKILL.md +282 -0
- package/skills/tdd/SKILL.md +178 -0
- package/skills/testing-strategy/SKILL.md +260 -0
- package/starter-pack/solutions/starter-api-error-responses.md +37 -0
- package/starter-pack/solutions/starter-async-patterns.md +40 -0
- package/starter-pack/solutions/starter-caching-strategy.md +40 -0
- package/starter-pack/solutions/starter-code-review-checklist.md +39 -0
- package/starter-pack/solutions/starter-debugging-systematic.md +40 -0
- package/starter-pack/solutions/starter-dependency-injection.md +40 -0
- package/starter-pack/solutions/starter-error-handling-patterns.md +38 -0
- package/starter-pack/solutions/starter-git-atomic-commits.md +36 -0
- package/starter-pack/solutions/starter-input-validation.md +40 -0
- package/starter-pack/solutions/starter-n-plus-one-queries.md +37 -0
- package/starter-pack/solutions/starter-refactor-safely.md +38 -0
- package/starter-pack/solutions/starter-secret-management.md +37 -0
- package/starter-pack/solutions/starter-separation-of-concerns.md +36 -0
- package/starter-pack/solutions/starter-tdd-red-green-refactor.md +40 -0
- package/starter-pack/solutions/starter-typescript-strict-types.md +39 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgen v1 — Preset Manager
|
|
3
|
+
*
|
|
4
|
+
* pack + overlay + trust policy 합성 엔진.
|
|
5
|
+
* Authoritative spec: docs/plans/2026-04-03-forgen-preset-system-design.md §7
|
|
6
|
+
*
|
|
7
|
+
* 합성 순서:
|
|
8
|
+
* 1. 글로벌 안전 불변식
|
|
9
|
+
* 2. base pack
|
|
10
|
+
* 3. 개인 장기 overlay
|
|
11
|
+
* 4. 현재 세션 임시 overlay
|
|
12
|
+
* 5. runtime capability detection
|
|
13
|
+
* 6. effective trust policy 계산
|
|
14
|
+
*
|
|
15
|
+
* 충돌 해소: 세션 임시 > 개인 장기 > base pack (글로벌 안전은 hard constraint)
|
|
16
|
+
*/
|
|
17
|
+
import type { Profile, Rule, SessionEffectiveState, RuntimeCapabilityState, TrustPolicy } from '../store/types.js';
|
|
18
|
+
declare const GLOBAL_SAFETY_RULES: Rule[];
|
|
19
|
+
export interface TrustComputeResult {
|
|
20
|
+
effective: TrustPolicy;
|
|
21
|
+
warning: string | null;
|
|
22
|
+
}
|
|
23
|
+
export declare function computeEffectiveTrust(desired: TrustPolicy, runtime: RuntimeCapabilityState): TrustComputeResult;
|
|
24
|
+
export declare function composeSession(params: {
|
|
25
|
+
session_id: string;
|
|
26
|
+
profile: Profile;
|
|
27
|
+
personalRules: Rule[];
|
|
28
|
+
sessionOverlays: Rule[];
|
|
29
|
+
runtime: RuntimeCapabilityState;
|
|
30
|
+
}): SessionEffectiveState;
|
|
31
|
+
export { GLOBAL_SAFETY_RULES };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgen v1 — Preset Manager
|
|
3
|
+
*
|
|
4
|
+
* pack + overlay + trust policy 합성 엔진.
|
|
5
|
+
* Authoritative spec: docs/plans/2026-04-03-forgen-preset-system-design.md §7
|
|
6
|
+
*
|
|
7
|
+
* 합성 순서:
|
|
8
|
+
* 1. 글로벌 안전 불변식
|
|
9
|
+
* 2. base pack
|
|
10
|
+
* 3. 개인 장기 overlay
|
|
11
|
+
* 4. 현재 세션 임시 overlay
|
|
12
|
+
* 5. runtime capability detection
|
|
13
|
+
* 6. effective trust policy 계산
|
|
14
|
+
*
|
|
15
|
+
* 충돌 해소: 세션 임시 > 개인 장기 > base pack (글로벌 안전은 hard constraint)
|
|
16
|
+
*/
|
|
17
|
+
// ── Global Safety Invariants ──
|
|
18
|
+
const GLOBAL_SAFETY_RULES = [
|
|
19
|
+
{
|
|
20
|
+
rule_id: 'global-no-credentials',
|
|
21
|
+
category: 'safety',
|
|
22
|
+
scope: 'me',
|
|
23
|
+
trigger: 'always',
|
|
24
|
+
policy: '.env, credentials, API 키를 절대 커밋하거나 노출하지 마라.',
|
|
25
|
+
strength: 'hard',
|
|
26
|
+
source: 'pack_overlay',
|
|
27
|
+
status: 'active',
|
|
28
|
+
evidence_refs: [],
|
|
29
|
+
render_key: 'safety.no_credentials',
|
|
30
|
+
created_at: '2026-01-01T00:00:00Z',
|
|
31
|
+
updated_at: '2026-01-01T00:00:00Z',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
rule_id: 'global-no-destructive-unconfirmed',
|
|
35
|
+
category: 'safety',
|
|
36
|
+
scope: 'me',
|
|
37
|
+
trigger: 'destructive command detected',
|
|
38
|
+
policy: '파괴적 명령(rm -rf, DROP, force-push)은 사용자 확인 없이 실행하지 마라.',
|
|
39
|
+
strength: 'hard',
|
|
40
|
+
source: 'pack_overlay',
|
|
41
|
+
status: 'active',
|
|
42
|
+
evidence_refs: [],
|
|
43
|
+
render_key: 'safety.no_destructive_unconfirmed',
|
|
44
|
+
created_at: '2026-01-01T00:00:00Z',
|
|
45
|
+
updated_at: '2026-01-01T00:00:00Z',
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
// ── Trust Policy Computation ──
|
|
49
|
+
const TRUST_ORDER = ['가드레일 우선', '승인 완화', '완전 신뢰 실행'];
|
|
50
|
+
function trustRank(policy) {
|
|
51
|
+
return TRUST_ORDER.indexOf(policy);
|
|
52
|
+
}
|
|
53
|
+
export function computeEffectiveTrust(desired, runtime) {
|
|
54
|
+
// runtime capability → trust level 매핑
|
|
55
|
+
let runtimeTrust;
|
|
56
|
+
if (runtime.dangerous_skip_permissions) {
|
|
57
|
+
runtimeTrust = '완전 신뢰 실행';
|
|
58
|
+
}
|
|
59
|
+
else if (runtime.permission_mode === 'bypassed') {
|
|
60
|
+
runtimeTrust = '완전 신뢰 실행';
|
|
61
|
+
}
|
|
62
|
+
else if (runtime.permission_mode === 'relaxed') {
|
|
63
|
+
runtimeTrust = '승인 완화';
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
runtimeTrust = '가드레일 우선';
|
|
67
|
+
}
|
|
68
|
+
// effective = min(desired, runtime) — runtime이 최상위 사실
|
|
69
|
+
const desiredRank = trustRank(desired);
|
|
70
|
+
const runtimeRank = trustRank(runtimeTrust);
|
|
71
|
+
if (runtimeRank < desiredRank) {
|
|
72
|
+
// runtime < desired → 세션 시작 시 안내
|
|
73
|
+
return {
|
|
74
|
+
effective: runtimeTrust,
|
|
75
|
+
warning: `Trust 하향: desired=${desired}, runtime=${runtimeTrust} (${runtime.permission_mode})`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (runtimeRank > desiredRank) {
|
|
79
|
+
// runtime > desired → 조용히 진행, effective만 상향
|
|
80
|
+
return { effective: runtimeTrust, warning: null };
|
|
81
|
+
}
|
|
82
|
+
return { effective: desired, warning: null };
|
|
83
|
+
}
|
|
84
|
+
// ── Session Effective State 합성 ──
|
|
85
|
+
export function composeSession(params) {
|
|
86
|
+
const { session_id, profile, personalRules, sessionOverlays, runtime } = params;
|
|
87
|
+
// trust 계산
|
|
88
|
+
const trustResult = computeEffectiveTrust(profile.trust_preferences.desired_policy, runtime);
|
|
89
|
+
// rule 합성: global safety + personal + session overlay
|
|
90
|
+
const allRules = [...GLOBAL_SAFETY_RULES, ...personalRules, ...sessionOverlays];
|
|
91
|
+
const activeRuleIds = allRules.filter(r => r.status === 'active').map(r => r.rule_id);
|
|
92
|
+
const warnings = [];
|
|
93
|
+
if (trustResult.warning)
|
|
94
|
+
warnings.push(trustResult.warning);
|
|
95
|
+
return {
|
|
96
|
+
session_id,
|
|
97
|
+
profile_version: profile.model_version,
|
|
98
|
+
quality_pack: profile.base_packs.quality_pack,
|
|
99
|
+
autonomy_pack: profile.base_packs.autonomy_pack,
|
|
100
|
+
judgment_pack: profile.base_packs.judgment_pack,
|
|
101
|
+
communication_pack: profile.base_packs.communication_pack,
|
|
102
|
+
effective_trust_policy: trustResult.effective,
|
|
103
|
+
active_rule_ids: activeRuleIds,
|
|
104
|
+
temporary_overlays: sessionOverlays,
|
|
105
|
+
runtime_capability_state: runtime,
|
|
106
|
+
warnings,
|
|
107
|
+
started_at: new Date().toISOString(),
|
|
108
|
+
ended_at: null,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export { GLOBAL_SAFETY_RULES };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgen v1 — Inspect Renderer
|
|
3
|
+
*
|
|
4
|
+
* forgen inspect profile/rules/evidence/session 출력 생성.
|
|
5
|
+
* Authoritative spec: docs/plans/2026-04-03-forgen-rule-renderer-spec.md §6
|
|
6
|
+
*/
|
|
7
|
+
import type { Profile, Rule, Evidence, SessionEffectiveState } from '../store/types.js';
|
|
8
|
+
export declare function renderProfile(profile: Profile): string;
|
|
9
|
+
export declare function renderRules(rules: Rule[]): string;
|
|
10
|
+
export declare function renderEvidence(evidence: Evidence[]): string;
|
|
11
|
+
export declare function renderSession(state: SessionEffectiveState): string;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgen v1 — Inspect Renderer
|
|
3
|
+
*
|
|
4
|
+
* forgen inspect profile/rules/evidence/session 출력 생성.
|
|
5
|
+
* Authoritative spec: docs/plans/2026-04-03-forgen-rule-renderer-spec.md §6
|
|
6
|
+
*/
|
|
7
|
+
import { initLocaleFromConfig, getLocale, qualityName, autonomyName, judgmentName, communicationName, trustName } from '../i18n/index.js';
|
|
8
|
+
export function renderProfile(profile) {
|
|
9
|
+
initLocaleFromConfig();
|
|
10
|
+
const l = getLocale();
|
|
11
|
+
const lines = [
|
|
12
|
+
`Profile v${profile.model_version} | updated: ${profile.metadata.updated_at}`,
|
|
13
|
+
'',
|
|
14
|
+
`Quality pack: ${qualityName(profile.base_packs.quality_pack, l)}`,
|
|
15
|
+
`Autonomy pack: ${autonomyName(profile.base_packs.autonomy_pack, l)}`,
|
|
16
|
+
`Judgment pack: ${judgmentName(profile.base_packs.judgment_pack, l)}`,
|
|
17
|
+
`Communication pack: ${communicationName(profile.base_packs.communication_pack, l)}`,
|
|
18
|
+
`Trust policy: ${trustName(profile.trust_preferences.desired_policy, l)} (source: ${profile.trust_preferences.source})`,
|
|
19
|
+
'',
|
|
20
|
+
'── 4축 상위 score ──',
|
|
21
|
+
` 품질/안전: ${profile.axes.quality_safety.score.toFixed(2)} (confidence: ${profile.axes.quality_safety.confidence.toFixed(2)})`,
|
|
22
|
+
` 자율성: ${profile.axes.autonomy.score.toFixed(2)} (confidence: ${profile.axes.autonomy.confidence.toFixed(2)})`,
|
|
23
|
+
` 판단철학: ${profile.axes.judgment_philosophy.score.toFixed(2)} (confidence: ${profile.axes.judgment_philosophy.confidence.toFixed(2)})`,
|
|
24
|
+
` 커뮤니케이션: ${profile.axes.communication_style.score.toFixed(2)} (confidence: ${profile.axes.communication_style.confidence.toFixed(2)})`,
|
|
25
|
+
'',
|
|
26
|
+
'── Quality facets ──',
|
|
27
|
+
` verification_depth: ${profile.axes.quality_safety.facets.verification_depth.toFixed(2)}`,
|
|
28
|
+
` stop_threshold: ${profile.axes.quality_safety.facets.stop_threshold.toFixed(2)}`,
|
|
29
|
+
` change_conservatism: ${profile.axes.quality_safety.facets.change_conservatism.toFixed(2)}`,
|
|
30
|
+
'',
|
|
31
|
+
'── Autonomy facets ──',
|
|
32
|
+
` confirmation_independence: ${profile.axes.autonomy.facets.confirmation_independence.toFixed(2)}`,
|
|
33
|
+
` assumption_tolerance: ${profile.axes.autonomy.facets.assumption_tolerance.toFixed(2)}`,
|
|
34
|
+
` scope_expansion_tolerance: ${profile.axes.autonomy.facets.scope_expansion_tolerance.toFixed(2)}`,
|
|
35
|
+
` approval_threshold: ${profile.axes.autonomy.facets.approval_threshold.toFixed(2)}`,
|
|
36
|
+
'',
|
|
37
|
+
'── Judgment facets ──',
|
|
38
|
+
` minimal_change_bias: ${profile.axes.judgment_philosophy.facets.minimal_change_bias.toFixed(2)}`,
|
|
39
|
+
` abstraction_bias: ${profile.axes.judgment_philosophy.facets.abstraction_bias.toFixed(2)}`,
|
|
40
|
+
` evidence_first_bias: ${profile.axes.judgment_philosophy.facets.evidence_first_bias.toFixed(2)}`,
|
|
41
|
+
'',
|
|
42
|
+
'── Communication facets ──',
|
|
43
|
+
` verbosity: ${profile.axes.communication_style.facets.verbosity.toFixed(2)}`,
|
|
44
|
+
` structure: ${profile.axes.communication_style.facets.structure.toFixed(2)}`,
|
|
45
|
+
` teaching_bias: ${profile.axes.communication_style.facets.teaching_bias.toFixed(2)}`,
|
|
46
|
+
];
|
|
47
|
+
return lines.join('\n');
|
|
48
|
+
}
|
|
49
|
+
export function renderRules(rules) {
|
|
50
|
+
const active = rules.filter(r => r.status === 'active');
|
|
51
|
+
const suppressed = rules.filter(r => r.status === 'suppressed');
|
|
52
|
+
const lines = [];
|
|
53
|
+
if (active.length > 0) {
|
|
54
|
+
lines.push(`── Active rules (${active.length}) ──`);
|
|
55
|
+
for (const r of active) {
|
|
56
|
+
lines.push(` [${r.category}/${r.strength}] ${r.render_key} — ${r.policy}`);
|
|
57
|
+
lines.push(` source: ${r.source} | evidence: ${r.evidence_refs.length}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (suppressed.length > 0) {
|
|
61
|
+
lines.push('');
|
|
62
|
+
lines.push(`── Suppressed rules (${suppressed.length}) ──`);
|
|
63
|
+
for (const r of suppressed) {
|
|
64
|
+
lines.push(` [${r.category}/${r.strength}] ${r.render_key} — ${r.policy}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (lines.length === 0)
|
|
68
|
+
lines.push('No rules.');
|
|
69
|
+
return lines.join('\n');
|
|
70
|
+
}
|
|
71
|
+
export function renderEvidence(evidence) {
|
|
72
|
+
const corrections = evidence.filter(e => e.type === 'explicit_correction');
|
|
73
|
+
const observations = evidence.filter(e => e.type === 'behavior_observation');
|
|
74
|
+
const summaries = evidence.filter(e => e.type === 'session_summary');
|
|
75
|
+
const lines = [];
|
|
76
|
+
if (corrections.length > 0) {
|
|
77
|
+
lines.push(`── Explicit corrections (${corrections.length}) ──`);
|
|
78
|
+
for (const e of corrections.slice(0, 10)) {
|
|
79
|
+
lines.push(` ${e.timestamp.slice(0, 10)} [${e.confidence.toFixed(2)}] ${e.summary}`);
|
|
80
|
+
if (e.candidate_rule_refs.length > 0)
|
|
81
|
+
lines.push(` → rules: ${e.candidate_rule_refs.join(', ')}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (observations.length > 0) {
|
|
85
|
+
lines.push('');
|
|
86
|
+
lines.push(`── Behavior observations (${observations.length}) ──`);
|
|
87
|
+
for (const e of observations.slice(0, 10)) {
|
|
88
|
+
lines.push(` ${e.timestamp.slice(0, 10)} [${e.confidence.toFixed(2)}] ${e.summary}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (summaries.length > 0) {
|
|
92
|
+
lines.push('');
|
|
93
|
+
lines.push(`── Session summaries (${summaries.length}) ──`);
|
|
94
|
+
for (const e of summaries.slice(0, 5)) {
|
|
95
|
+
lines.push(` ${e.timestamp.slice(0, 10)} [${e.confidence.toFixed(2)}] ${e.summary}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (lines.length === 0)
|
|
99
|
+
lines.push('No evidence.');
|
|
100
|
+
return lines.join('\n');
|
|
101
|
+
}
|
|
102
|
+
export function renderSession(state) {
|
|
103
|
+
initLocaleFromConfig();
|
|
104
|
+
const l = getLocale();
|
|
105
|
+
const lines = [
|
|
106
|
+
`Session: ${state.session_id}`,
|
|
107
|
+
`Quality: ${qualityName(state.quality_pack, l)} | Autonomy: ${autonomyName(state.autonomy_pack, l)} | Judgment: ${judgmentName(state.judgment_pack, l)} | Communication: ${communicationName(state.communication_pack, l)}`,
|
|
108
|
+
`Runtime: ${state.runtime_capability_state.permission_mode} (detected from: ${state.runtime_capability_state.detected_from})`,
|
|
109
|
+
`Effective trust: ${trustName(state.effective_trust_policy, l)}`,
|
|
110
|
+
];
|
|
111
|
+
if (state.warnings.length > 0) {
|
|
112
|
+
lines.push(`Warnings: ${state.warnings.join('; ')}`);
|
|
113
|
+
}
|
|
114
|
+
if (state.temporary_overlays.length > 0) {
|
|
115
|
+
lines.push(`Temporary overlays: ${state.temporary_overlays.length}`);
|
|
116
|
+
for (const o of state.temporary_overlays) {
|
|
117
|
+
lines.push(` - ${o.render_key}: ${o.policy}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
lines.push(`Active rules: ${state.active_rule_ids.length}`);
|
|
121
|
+
lines.push(`Started: ${state.started_at}${state.ended_at ? ` | Ended: ${state.ended_at}` : ' (active)'}`);
|
|
122
|
+
return lines.join('\n');
|
|
123
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgen v1 — Rule Renderer
|
|
3
|
+
*
|
|
4
|
+
* Rule[] + SessionEffectiveState → Claude Code용 자연어 규칙 세트 변환.
|
|
5
|
+
* Authoritative spec: docs/plans/2026-04-03-forgen-rule-renderer-spec.md
|
|
6
|
+
*
|
|
7
|
+
* 파이프라인: filter → dedupe(render_key) → group(category) → order → template → budget
|
|
8
|
+
*/
|
|
9
|
+
import type { Rule, SessionEffectiveState, Profile } from '../store/types.js';
|
|
10
|
+
export type RenderSurface = 'session_start' | 'recompose' | 'inspect';
|
|
11
|
+
export interface RenderContext {
|
|
12
|
+
surface: RenderSurface;
|
|
13
|
+
max_rules: number;
|
|
14
|
+
max_chars: number;
|
|
15
|
+
include_pack_summary: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare const DEFAULT_CONTEXT: RenderContext;
|
|
18
|
+
export declare function renderRules(rules: Rule[], state: SessionEffectiveState, _profile: Profile, ctx?: RenderContext): string;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgen v1 — Rule Renderer
|
|
3
|
+
*
|
|
4
|
+
* Rule[] + SessionEffectiveState → Claude Code용 자연어 규칙 세트 변환.
|
|
5
|
+
* Authoritative spec: docs/plans/2026-04-03-forgen-rule-renderer-spec.md
|
|
6
|
+
*
|
|
7
|
+
* 파이프라인: filter → dedupe(render_key) → group(category) → order → template → budget
|
|
8
|
+
*/
|
|
9
|
+
import { initLocaleFromConfig, getLocale, qualityName, autonomyName, judgmentName, communicationName, RULE_RENDERER } from '../i18n/index.js';
|
|
10
|
+
export const DEFAULT_CONTEXT = {
|
|
11
|
+
surface: 'session_start',
|
|
12
|
+
max_rules: 30,
|
|
13
|
+
max_chars: 4000,
|
|
14
|
+
include_pack_summary: true,
|
|
15
|
+
};
|
|
16
|
+
// ── Output Sections ──
|
|
17
|
+
const SECTION_ORDER = ['Must Not', 'Working Defaults', 'When To Ask', 'How To Validate', 'How To Report'];
|
|
18
|
+
const CATEGORY_TO_SECTION = {
|
|
19
|
+
safety: 'Must Not',
|
|
20
|
+
quality: 'How To Validate',
|
|
21
|
+
autonomy: 'When To Ask',
|
|
22
|
+
judgment: 'Working Defaults',
|
|
23
|
+
communication: 'How To Report',
|
|
24
|
+
workflow: 'Working Defaults',
|
|
25
|
+
};
|
|
26
|
+
// ── Dedupe: render_key 충돌 해소 ──
|
|
27
|
+
const SCOPE_RANK = { session: 0, me: 1 };
|
|
28
|
+
const STRENGTH_RANK = { hard: 0, strong: 1, default: 2, soft: 3 };
|
|
29
|
+
const SOURCE_RANK = { explicit_correction: 0, onboarding: 1, behavior_inference: 2, pack_overlay: 3 };
|
|
30
|
+
function dedupeByRenderKey(rules) {
|
|
31
|
+
const groups = new Map();
|
|
32
|
+
for (const r of rules) {
|
|
33
|
+
const existing = groups.get(r.render_key) ?? [];
|
|
34
|
+
existing.push(r);
|
|
35
|
+
groups.set(r.render_key, existing);
|
|
36
|
+
}
|
|
37
|
+
const result = [];
|
|
38
|
+
for (const group of groups.values()) {
|
|
39
|
+
if (group.length === 1) {
|
|
40
|
+
result.push(group[0]);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
group.sort((a, b) => {
|
|
44
|
+
const scopeDiff = SCOPE_RANK[a.scope] - SCOPE_RANK[b.scope];
|
|
45
|
+
if (scopeDiff !== 0)
|
|
46
|
+
return scopeDiff;
|
|
47
|
+
const strengthDiff = STRENGTH_RANK[a.strength] - STRENGTH_RANK[b.strength];
|
|
48
|
+
if (strengthDiff !== 0)
|
|
49
|
+
return strengthDiff;
|
|
50
|
+
const sourceDiff = SOURCE_RANK[a.source] - SOURCE_RANK[b.source];
|
|
51
|
+
if (sourceDiff !== 0)
|
|
52
|
+
return sourceDiff;
|
|
53
|
+
const timeDiff = b.updated_at.localeCompare(a.updated_at);
|
|
54
|
+
if (timeDiff !== 0)
|
|
55
|
+
return timeDiff;
|
|
56
|
+
return a.rule_id.localeCompare(b.rule_id);
|
|
57
|
+
});
|
|
58
|
+
result.push(group[0]);
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
// ── Template ──
|
|
63
|
+
function ruleToText(rule) {
|
|
64
|
+
// policy 필드가 이미 사람이 읽을 수 있는 문장이면 그대로 사용
|
|
65
|
+
// render_key로 템플릿을 찾을 수도 있지만 v1은 policy 직접 사용
|
|
66
|
+
return rule.policy;
|
|
67
|
+
}
|
|
68
|
+
function trustPolicySummary(policy) {
|
|
69
|
+
const s = RULE_RENDERER[getLocale()];
|
|
70
|
+
switch (policy) {
|
|
71
|
+
case '가드레일 우선': return s.trustGuardrails;
|
|
72
|
+
case '승인 완화': return s.trustRelaxed;
|
|
73
|
+
case '완전 신뢰 실행': return s.trustFullTrust;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function judgmentPackRules(pack) {
|
|
77
|
+
const s = RULE_RENDERER[getLocale()];
|
|
78
|
+
switch (pack) {
|
|
79
|
+
case '최소변경형': return s.judgmentMinimalChange;
|
|
80
|
+
case '구조적접근형': return s.judgmentStructural;
|
|
81
|
+
case '균형형': return s.judgmentBalanced;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function communicationPackRules(pack) {
|
|
85
|
+
const s = RULE_RENDERER[getLocale()];
|
|
86
|
+
switch (pack) {
|
|
87
|
+
case '간결형': return s.commConcise;
|
|
88
|
+
case '상세형': return s.commDetailed;
|
|
89
|
+
case '균형형': return s.commBalanced;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// ── Main Render ──
|
|
93
|
+
export function renderRules(rules, state, _profile, ctx = DEFAULT_CONTEXT) {
|
|
94
|
+
// 1. active만 수집
|
|
95
|
+
const active = rules.filter(r => r.status === 'active');
|
|
96
|
+
// 2. dedupe by render_key
|
|
97
|
+
const deduped = dedupeByRenderKey(active);
|
|
98
|
+
// 3. hard constraints 먼저
|
|
99
|
+
const hardRules = deduped.filter(r => r.strength === 'hard');
|
|
100
|
+
const otherRules = deduped.filter(r => r.strength !== 'hard');
|
|
101
|
+
// 4. category별 그룹
|
|
102
|
+
const sections = new Map();
|
|
103
|
+
for (const name of SECTION_ORDER)
|
|
104
|
+
sections.set(name, []);
|
|
105
|
+
for (const rule of hardRules) {
|
|
106
|
+
sections.get('Must Not').push(ruleToText(rule));
|
|
107
|
+
}
|
|
108
|
+
for (const rule of otherRules) {
|
|
109
|
+
const section = CATEGORY_TO_SECTION[rule.category] ?? 'Working Defaults';
|
|
110
|
+
sections.get(section).push(ruleToText(rule));
|
|
111
|
+
}
|
|
112
|
+
// 5. trust policy + pack 기본 규칙 주입
|
|
113
|
+
if (ctx.include_pack_summary) {
|
|
114
|
+
sections.get('Working Defaults').unshift(`Trust: ${trustPolicySummary(state.effective_trust_policy)}`);
|
|
115
|
+
// judgment pack 기본 규칙
|
|
116
|
+
for (const rule of judgmentPackRules(state.judgment_pack)) {
|
|
117
|
+
sections.get('Working Defaults').push(rule);
|
|
118
|
+
}
|
|
119
|
+
// communication pack 기본 규칙
|
|
120
|
+
for (const rule of communicationPackRules(state.communication_pack)) {
|
|
121
|
+
sections.get('How To Report').push(rule);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// 6. 섹션 조립
|
|
125
|
+
const parts = [];
|
|
126
|
+
if (ctx.include_pack_summary) {
|
|
127
|
+
const l = getLocale();
|
|
128
|
+
parts.push(`[${qualityName(state.quality_pack, l)} quality / ${autonomyName(state.autonomy_pack, l)} autonomy / ${judgmentName(state.judgment_pack, l)} judgment / ${communicationName(state.communication_pack, l)} communication]`);
|
|
129
|
+
}
|
|
130
|
+
let totalChars = parts.reduce((sum, p) => sum + p.length, 0);
|
|
131
|
+
let totalRules = 0;
|
|
132
|
+
for (const name of SECTION_ORDER) {
|
|
133
|
+
const items = sections.get(name);
|
|
134
|
+
if (items.length === 0)
|
|
135
|
+
continue;
|
|
136
|
+
const header = `## ${name}`;
|
|
137
|
+
const body = items.map(item => `- ${item}`).join('\n');
|
|
138
|
+
const section = `${header}\n${body}`;
|
|
139
|
+
if (totalChars + section.length > ctx.max_chars)
|
|
140
|
+
break;
|
|
141
|
+
if (totalRules + items.length > ctx.max_rules)
|
|
142
|
+
break;
|
|
143
|
+
parts.push(section);
|
|
144
|
+
totalChars += section.length;
|
|
145
|
+
totalRules += items.length;
|
|
146
|
+
}
|
|
147
|
+
// 7. Evidence Collection 지시 (항상 포함, 로케일)
|
|
148
|
+
initLocaleFromConfig();
|
|
149
|
+
const ecStrings = RULE_RENDERER[getLocale()];
|
|
150
|
+
parts.push([
|
|
151
|
+
`## ${ecStrings.evidenceCollectionHeader}`,
|
|
152
|
+
...ecStrings.evidenceCollectionRules.map(r => `- ${r}`),
|
|
153
|
+
].join('\n'));
|
|
154
|
+
// 8. warnings
|
|
155
|
+
if (state.warnings.length > 0) {
|
|
156
|
+
parts.push(`## Warnings\n${state.warnings.map(w => `- ${w}`).join('\n')}`);
|
|
157
|
+
}
|
|
158
|
+
return parts.join('\n\n');
|
|
159
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgen v1 — Evidence Store
|
|
3
|
+
*
|
|
4
|
+
* explicit_correction, behavior_observation, session_summary CRUD.
|
|
5
|
+
* Authoritative schema: docs/plans/2026-04-03-forgen-data-model-storage-spec.md §4
|
|
6
|
+
*/
|
|
7
|
+
import type { Evidence, EvidenceType } from './types.js';
|
|
8
|
+
export declare function createEvidence(params: {
|
|
9
|
+
type: EvidenceType;
|
|
10
|
+
session_id: string;
|
|
11
|
+
source_component: string;
|
|
12
|
+
summary: string;
|
|
13
|
+
axis_refs?: string[];
|
|
14
|
+
candidate_rule_refs?: string[];
|
|
15
|
+
confidence: number;
|
|
16
|
+
raw_payload?: Record<string, unknown>;
|
|
17
|
+
}): Evidence;
|
|
18
|
+
export declare function saveEvidence(evidence: Evidence): void;
|
|
19
|
+
export declare function loadEvidence(evidenceId: string): Evidence | null;
|
|
20
|
+
export declare function loadAllEvidence(): Evidence[];
|
|
21
|
+
export declare function loadEvidenceBySession(sessionId: string): Evidence[];
|
|
22
|
+
export declare function loadEvidenceByType(type: EvidenceType): Evidence[];
|
|
23
|
+
export declare function loadRecentEvidence(limit?: number): Evidence[];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgen v1 — Evidence Store
|
|
3
|
+
*
|
|
4
|
+
* explicit_correction, behavior_observation, session_summary CRUD.
|
|
5
|
+
* Authoritative schema: docs/plans/2026-04-03-forgen-data-model-storage-spec.md §4
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import * as crypto from 'node:crypto';
|
|
10
|
+
import { V1_EVIDENCE_DIR } from '../core/paths.js';
|
|
11
|
+
import { atomicWriteJSON, safeReadJSON } from '../hooks/shared/atomic-write.js';
|
|
12
|
+
function evidencePath(evidenceId) {
|
|
13
|
+
return path.join(V1_EVIDENCE_DIR, `${evidenceId}.json`);
|
|
14
|
+
}
|
|
15
|
+
export function createEvidence(params) {
|
|
16
|
+
return {
|
|
17
|
+
evidence_id: crypto.randomUUID(),
|
|
18
|
+
type: params.type,
|
|
19
|
+
session_id: params.session_id,
|
|
20
|
+
timestamp: new Date().toISOString(),
|
|
21
|
+
source_component: params.source_component,
|
|
22
|
+
summary: params.summary,
|
|
23
|
+
axis_refs: params.axis_refs ?? [],
|
|
24
|
+
candidate_rule_refs: params.candidate_rule_refs ?? [],
|
|
25
|
+
confidence: params.confidence,
|
|
26
|
+
raw_payload: params.raw_payload ?? {},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function saveEvidence(evidence) {
|
|
30
|
+
atomicWriteJSON(evidencePath(evidence.evidence_id), evidence, { pretty: true });
|
|
31
|
+
}
|
|
32
|
+
export function loadEvidence(evidenceId) {
|
|
33
|
+
return safeReadJSON(evidencePath(evidenceId), null);
|
|
34
|
+
}
|
|
35
|
+
export function loadAllEvidence() {
|
|
36
|
+
if (!fs.existsSync(V1_EVIDENCE_DIR))
|
|
37
|
+
return [];
|
|
38
|
+
const items = [];
|
|
39
|
+
for (const file of fs.readdirSync(V1_EVIDENCE_DIR)) {
|
|
40
|
+
if (!file.endsWith('.json'))
|
|
41
|
+
continue;
|
|
42
|
+
const ev = safeReadJSON(path.join(V1_EVIDENCE_DIR, file), null);
|
|
43
|
+
if (ev)
|
|
44
|
+
items.push(ev);
|
|
45
|
+
}
|
|
46
|
+
return items;
|
|
47
|
+
}
|
|
48
|
+
export function loadEvidenceBySession(sessionId) {
|
|
49
|
+
return loadAllEvidence().filter(e => e.session_id === sessionId);
|
|
50
|
+
}
|
|
51
|
+
export function loadEvidenceByType(type) {
|
|
52
|
+
return loadAllEvidence().filter(e => e.type === type);
|
|
53
|
+
}
|
|
54
|
+
export function loadRecentEvidence(limit = 20) {
|
|
55
|
+
return loadAllEvidence()
|
|
56
|
+
.sort((a, b) => b.timestamp.localeCompare(a.timestamp))
|
|
57
|
+
.slice(0, limit);
|
|
58
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgen v1 — Profile Store
|
|
3
|
+
*
|
|
4
|
+
* Profile CRUD. 4축 + facet + trust preferences.
|
|
5
|
+
* Authoritative schema: docs/plans/2026-04-03-forgen-data-model-storage-spec.md §2
|
|
6
|
+
*/
|
|
7
|
+
import type { Profile, QualityPack, AutonomyPack, JudgmentPack, CommunicationPack, TrustPolicy } from './types.js';
|
|
8
|
+
export declare function createProfile(userId: string, qualityPack: QualityPack, autonomyPack: AutonomyPack, trustPolicy: TrustPolicy, trustSource: Profile['trust_preferences']['source'], judgmentPack?: JudgmentPack, communicationPack?: CommunicationPack): Profile;
|
|
9
|
+
export declare function loadProfile(): Profile | null;
|
|
10
|
+
export declare function saveProfile(profile: Profile): void;
|
|
11
|
+
export declare function profileExists(): boolean;
|
|
12
|
+
export declare function isV1Profile(data: unknown): data is Profile;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgen v1 — Profile Store
|
|
3
|
+
*
|
|
4
|
+
* Profile CRUD. 4축 + facet + trust preferences.
|
|
5
|
+
* Authoritative schema: docs/plans/2026-04-03-forgen-data-model-storage-spec.md §2
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import { V1_PROFILE } from '../core/paths.js';
|
|
9
|
+
import { atomicWriteJSON, safeReadJSON } from '../hooks/shared/atomic-write.js';
|
|
10
|
+
import { qualityCentroid, autonomyCentroid, judgmentCentroid, communicationCentroid, } from '../preset/facet-catalog.js';
|
|
11
|
+
const MODEL_VERSION = '2.0';
|
|
12
|
+
export function createProfile(userId, qualityPack, autonomyPack, trustPolicy, trustSource, judgmentPack = '균형형', communicationPack = '균형형') {
|
|
13
|
+
const now = new Date().toISOString();
|
|
14
|
+
return {
|
|
15
|
+
user_id: userId,
|
|
16
|
+
model_version: MODEL_VERSION,
|
|
17
|
+
axes: {
|
|
18
|
+
quality_safety: { score: 0.5, facets: qualityCentroid(qualityPack), confidence: 0.45 },
|
|
19
|
+
autonomy: { score: 0.5, facets: autonomyCentroid(autonomyPack), confidence: 0.45 },
|
|
20
|
+
judgment_philosophy: { score: 0.5, facets: judgmentCentroid(judgmentPack), confidence: 0.45 },
|
|
21
|
+
communication_style: { score: 0.5, facets: communicationCentroid(communicationPack), confidence: 0.45 },
|
|
22
|
+
},
|
|
23
|
+
base_packs: {
|
|
24
|
+
quality_pack: qualityPack,
|
|
25
|
+
autonomy_pack: autonomyPack,
|
|
26
|
+
judgment_pack: judgmentPack,
|
|
27
|
+
communication_pack: communicationPack,
|
|
28
|
+
},
|
|
29
|
+
trust_preferences: { desired_policy: trustPolicy, source: trustSource },
|
|
30
|
+
metadata: {
|
|
31
|
+
created_at: now,
|
|
32
|
+
updated_at: now,
|
|
33
|
+
last_onboarding_at: now,
|
|
34
|
+
last_reclassification_at: null,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function loadProfile() {
|
|
39
|
+
return safeReadJSON(V1_PROFILE, null);
|
|
40
|
+
}
|
|
41
|
+
export function saveProfile(profile) {
|
|
42
|
+
profile.metadata.updated_at = new Date().toISOString();
|
|
43
|
+
atomicWriteJSON(V1_PROFILE, profile, { pretty: true });
|
|
44
|
+
}
|
|
45
|
+
export function profileExists() {
|
|
46
|
+
return fs.existsSync(V1_PROFILE);
|
|
47
|
+
}
|
|
48
|
+
export function isV1Profile(data) {
|
|
49
|
+
if (!data || typeof data !== 'object')
|
|
50
|
+
return false;
|
|
51
|
+
const p = data;
|
|
52
|
+
return typeof p.model_version === 'string' && p.model_version.startsWith('2.');
|
|
53
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgen v1 — Pack Recommendation Store
|
|
3
|
+
*
|
|
4
|
+
* Authoritative schema: docs/plans/2026-04-03-forgen-data-model-storage-spec.md §6
|
|
5
|
+
*/
|
|
6
|
+
import type { PackRecommendation, QualityPack, AutonomyPack, JudgmentPack, CommunicationPack, TrustPolicy, RecommendationSource, RecommendationStatus } from './types.js';
|
|
7
|
+
export declare function createRecommendation(params: {
|
|
8
|
+
source: RecommendationSource;
|
|
9
|
+
quality_pack: QualityPack;
|
|
10
|
+
autonomy_pack: AutonomyPack;
|
|
11
|
+
judgment_pack?: JudgmentPack;
|
|
12
|
+
communication_pack?: CommunicationPack;
|
|
13
|
+
suggested_trust_policy: TrustPolicy;
|
|
14
|
+
confidence: number;
|
|
15
|
+
reason_summary: string;
|
|
16
|
+
}): PackRecommendation;
|
|
17
|
+
export declare function saveRecommendation(rec: PackRecommendation): void;
|
|
18
|
+
export declare function loadRecommendation(id: string): PackRecommendation | null;
|
|
19
|
+
export declare function loadAllRecommendations(): PackRecommendation[];
|
|
20
|
+
export declare function updateRecommendationStatus(id: string, status: RecommendationStatus): boolean;
|
|
21
|
+
export declare function loadAcceptedRecommendation(): PackRecommendation | null;
|
|
22
|
+
export declare function loadLatestRecommendation(): PackRecommendation | null;
|