@wooojin/forgen 0.4.12 → 0.5.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 +1 -1
- package/CHANGELOG.md +109 -0
- package/README.ja.md +4 -4
- package/README.md +89 -27
- package/README.zh.md +4 -4
- package/assets/claude/commands/forge-loop.md +7 -1
- package/assets/claude/commands/ship.md +18 -1
- package/assets/opencode/forgen.ts +52 -0
- package/dist/checks/_shared/meta-guard-dispatch.d.ts +7 -0
- package/dist/checks/_shared/meta-guard-dispatch.js +12 -2
- package/dist/checks/_shared/model-profile.d.ts +25 -0
- package/dist/checks/_shared/model-profile.js +63 -0
- package/dist/cli.js +99 -153
- package/dist/core/auto-compound-runner.d.ts +0 -11
- package/dist/core/auto-compound-runner.js +127 -79
- package/dist/core/compound-consent.d.ts +15 -0
- package/dist/core/compound-consent.js +48 -0
- package/dist/core/compound-sweep-cli.d.ts +57 -0
- package/dist/core/compound-sweep-cli.js +354 -0
- package/dist/core/config-injector.d.ts +16 -1
- package/dist/core/config-injector.js +47 -28
- package/dist/core/dashboard-cli.js +40 -16
- package/dist/core/dashboard.d.ts +3 -4
- package/dist/core/dashboard.js +15 -34
- package/dist/core/dev-cli.d.ts +13 -0
- package/dist/core/dev-cli.js +70 -0
- package/dist/core/doctor.d.ts +16 -5
- package/dist/core/doctor.js +161 -176
- package/dist/core/drift-score.d.ts +2 -0
- package/dist/core/drift-score.js +9 -1
- package/dist/core/harness.js +115 -43
- package/dist/core/health-cli.d.ts +2 -0
- package/dist/core/health-cli.js +6 -1
- package/dist/core/host-detect.d.ts +3 -1
- package/dist/core/host-detect.js +26 -1
- package/dist/core/migrate-cli.js +15 -0
- package/dist/core/migrate-evidence-host.d.ts +2 -1
- package/dist/core/migrate-tenetx.d.ts +50 -0
- package/dist/core/migrate-tenetx.js +262 -0
- package/dist/core/probe-workflow-cli.d.ts +3 -3
- package/dist/core/probe-workflow-cli.js +13 -13
- package/dist/core/recall-cli.js +1 -1
- package/dist/core/regress-map-cli.js +1 -1
- package/dist/core/rendered-rules-manifest.d.ts +29 -0
- package/dist/core/rendered-rules-manifest.js +60 -0
- package/dist/core/session-store.js +14 -3
- package/dist/core/settings-injector.d.ts +3 -0
- package/dist/core/settings-injector.js +12 -16
- package/dist/core/spawn.d.ts +11 -1
- package/dist/core/spawn.js +79 -7
- package/dist/core/state-gc.js +1 -0
- package/dist/core/status-cli.d.ts +20 -0
- package/dist/core/status-cli.js +100 -0
- package/dist/core/statusline-cli.d.ts +7 -0
- package/dist/core/statusline-cli.js +63 -19
- package/dist/core/transcript-summary.d.ts +18 -0
- package/dist/core/transcript-summary.js +81 -0
- package/dist/core/trust-layer-intent.d.ts +21 -1
- package/dist/core/trust-layer-intent.js +7 -0
- package/dist/core/types.d.ts +3 -2
- package/dist/core/uninstall.js +12 -0
- package/dist/core/usage-telemetry.d.ts +7 -1
- package/dist/core/usage-telemetry.js +7 -9
- package/dist/core/v1-bootstrap.js +1 -1
- package/dist/core/watch-cli.js +1 -1
- package/dist/engine/compound-extractor.js +10 -0
- package/dist/engine/compound-loop.js +35 -6
- package/dist/engine/compound-share.d.ts +85 -0
- package/dist/engine/compound-share.js +606 -0
- package/dist/engine/correction-cluster-runner.d.ts +38 -0
- package/dist/engine/correction-cluster-runner.js +188 -0
- package/dist/engine/correction-clustering.d.ts +80 -0
- package/dist/engine/correction-clustering.js +167 -0
- package/dist/engine/enforce-classifier.d.ts +10 -1
- package/dist/engine/enforce-classifier.js +111 -22
- package/dist/engine/extraction-session.js +10 -3
- package/dist/engine/private-filter.d.ts +36 -0
- package/dist/engine/private-filter.js +100 -0
- package/dist/engine/ranking-pipeline.js +4 -2
- package/dist/engine/relevance-gate.d.ts +12 -0
- package/dist/engine/relevance-gate.js +12 -0
- package/dist/engine/roi-demotion.d.ts +79 -0
- package/dist/engine/roi-demotion.js +159 -0
- package/dist/engine/solution-format.d.ts +1 -0
- package/dist/engine/solution-format.js +26 -0
- package/dist/engine/solution-matcher.js +10 -1
- package/dist/fgx.js +19 -6
- package/dist/forge/cli.js +8 -2
- package/dist/hooks/compound-reflection.js +6 -1
- package/dist/hooks/context-guard.d.ts +16 -1
- package/dist/hooks/context-guard.js +92 -46
- package/dist/hooks/post-tool-use.js +2 -3
- package/dist/hooks/pre-compact.js +14 -0
- package/dist/hooks/pre-tool-use.js +5 -1
- package/dist/hooks/shared/stop-triggers.d.ts +29 -2
- package/dist/hooks/shared/stop-triggers.js +35 -2
- package/dist/hooks/solution-injector.d.ts +28 -0
- package/dist/hooks/solution-injector.js +126 -28
- package/dist/hooks/stop-guard.js +5 -2
- package/dist/hooks/subagent-stop-guard.js +4 -1
- package/dist/host/capabilities-claude.js +1 -0
- package/dist/host/capabilities-codex.js +1 -0
- package/dist/host/capabilities-opencode.d.ts +26 -0
- package/dist/host/capabilities-opencode.js +78 -0
- package/dist/host/capabilities-registry.d.ts +7 -0
- package/dist/host/capabilities-registry.js +14 -0
- package/dist/host/exec-host.d.ts +4 -3
- package/dist/host/exec-host.js +2 -0
- package/dist/host/host-binding.d.ts +27 -0
- package/dist/host/host-binding.js +11 -0
- package/dist/host/host-runtime.js +18 -0
- package/dist/host/install-codex.d.ts +8 -0
- package/dist/host/install-codex.js +2 -2
- package/dist/host/install-opencode.d.ts +38 -0
- package/dist/host/install-opencode.js +148 -0
- package/dist/host/install-orchestrator.d.ts +4 -1
- package/dist/host/install-orchestrator.js +20 -1
- package/dist/host/invoke-agent.d.ts +3 -2
- package/dist/host/opencode/context-cli.d.ts +15 -0
- package/dist/host/opencode/context-cli.js +25 -0
- package/dist/host/opencode/guard-cli.d.ts +13 -0
- package/dist/host/opencode/guard-cli.js +39 -0
- package/dist/host/opencode/plugin/forgen.d.ts +31 -0
- package/dist/host/opencode/plugin/forgen.js +60 -0
- package/dist/host/opencode/translate.d.ts +49 -0
- package/dist/host/opencode/translate.js +96 -0
- package/dist/host/parity-harness.d.ts +10 -2
- package/dist/host/projection.js +11 -0
- package/dist/mcp/tools.js +17 -4
- package/dist/store/evidence-store.d.ts +2 -6
- package/dist/store/evidence-store.js +68 -14
- package/dist/store/host-mismatch.d.ts +2 -1
- package/dist/store/host-mismatch.js +8 -8
- package/dist/store/profile-store.d.ts +3 -2
- package/dist/store/types.d.ts +9 -2
- package/package.json +3 -2
- package/plugin.json +2 -2
- package/skills/forge-loop/SKILL.md +7 -1
- package/skills/ship/SKILL.md +18 -1
|
@@ -21,6 +21,7 @@ import { getNewCommits, getCommitMessages, getGitDiff, getDiffStats } from './ex
|
|
|
21
21
|
import { gate0, evaluateExtractedSolution } from './extraction-gates.js';
|
|
22
22
|
import { extractFromDiff } from './extraction-diff.js';
|
|
23
23
|
import { extractFromSessionContext } from './extraction-session.js';
|
|
24
|
+
import { stripPrivate, isFullyPrivate } from './private-filter.js';
|
|
24
25
|
import { loadLastExtraction, saveLastExtraction, saveExtractedSolution, updateReExtractedCounter, emitCompoundExtractActedOn, } from './extraction-persistence.js';
|
|
25
26
|
const log = createLogger('compound-extractor');
|
|
26
27
|
const MAX_EXTRACTIONS_PER_DAY = 5;
|
|
@@ -172,6 +173,15 @@ export function processExtractionResults(rawJson, sessionId) {
|
|
|
172
173
|
return { saved, skipped };
|
|
173
174
|
}
|
|
174
175
|
for (const sol of solutions.slice(0, 3)) {
|
|
176
|
+
// W2-5 (private 태그): <private> 범위를 솔루션 content/context 에서 제거 후 저장.
|
|
177
|
+
// 통째로 private 이면 저장 skip (학습 코퍼스에 남기지 않음).
|
|
178
|
+
if (isFullyPrivate(sol.content) || isFullyPrivate(sol.context ?? '')) {
|
|
179
|
+
skipped.push(`${sol.name}: <private> (제외)`);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
sol.content = stripPrivate(sol.content).cleaned;
|
|
183
|
+
if (sol.context)
|
|
184
|
+
sol.context = stripPrivate(sol.context).cleaned;
|
|
175
185
|
const evaluation = evaluateExtractedSolution(sol);
|
|
176
186
|
if (evaluation.action === 'skip' || evaluation.action === 'duplicate') {
|
|
177
187
|
skipped.push(evaluation.message ?? `${sol.name}: skipped`);
|
|
@@ -190,11 +190,17 @@ export async function handleCompound(args) {
|
|
|
190
190
|
forgen compound --lifecycle Run promotion/demotion/circuit-breaker check
|
|
191
191
|
forgen compound --verify <name> Manually promote solution to verified
|
|
192
192
|
|
|
193
|
-
Export/Import:
|
|
193
|
+
Export/Import (whole store, tar.gz):
|
|
194
194
|
forgen compound export [--output path]
|
|
195
195
|
Export knowledge to tar.gz archive
|
|
196
196
|
forgen compound import <path> Import knowledge from archive (skip existing)
|
|
197
197
|
|
|
198
|
+
Export/Import (per-pattern, JSON — with confidence + provenance):
|
|
199
|
+
forgen compound export <name...> [--out file]
|
|
200
|
+
Export named solutions/rules as a portable bundle
|
|
201
|
+
forgen compound import <bundle.json> [--dry-run]
|
|
202
|
+
Import a bundle (probation trust, never overwrites)
|
|
203
|
+
|
|
198
204
|
Auto-extraction:
|
|
199
205
|
forgen compound --pause-auto Pause auto-extraction
|
|
200
206
|
forgen compound --resume-auto Resume auto-extraction
|
|
@@ -205,15 +211,38 @@ export async function handleCompound(args) {
|
|
|
205
211
|
return;
|
|
206
212
|
}
|
|
207
213
|
// --- export command ---
|
|
214
|
+
// 통짜 tar.gz export(플래그만, positional 없음) vs 이름 지정 패턴 export를
|
|
215
|
+
// positional 인자 유무로 구분한다. `compound export foo` = 패턴 export,
|
|
216
|
+
// `compound export --output x` = 기존 whole-store export.
|
|
208
217
|
if (args[0] === 'export') {
|
|
209
|
-
const
|
|
210
|
-
|
|
218
|
+
const rest = args.slice(1);
|
|
219
|
+
const hasPatternNames = rest.length > 0 && !rest[0].startsWith('--');
|
|
220
|
+
if (hasPatternNames) {
|
|
221
|
+
const { handleShareExport } = await import('./compound-share.js');
|
|
222
|
+
await handleShareExport(rest);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
const { handleExport } = await import('./compound-export.js');
|
|
226
|
+
await handleExport(rest);
|
|
227
|
+
}
|
|
211
228
|
return;
|
|
212
229
|
}
|
|
213
230
|
// --- import command ---
|
|
231
|
+
// 대상 파일이 JSON 패턴 번들인지 tar.gz 아카이브인지 확장자/매직바이트로
|
|
232
|
+
// sniff해서 라우팅한다 (looksLikeShareBundle). 기존 tar.gz import 동작은
|
|
233
|
+
// 그대로 유지.
|
|
214
234
|
if (args[0] === 'import') {
|
|
215
|
-
const
|
|
216
|
-
|
|
235
|
+
const rest = args.slice(1);
|
|
236
|
+
const filePath = rest.find(a => !a.startsWith('--'));
|
|
237
|
+
const { looksLikeShareBundle } = await import('./compound-share.js');
|
|
238
|
+
if (filePath && looksLikeShareBundle(path.resolve(filePath))) {
|
|
239
|
+
const { handleShareImport } = await import('./compound-share.js');
|
|
240
|
+
await handleShareImport(rest);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
const { handleImport } = await import('./compound-export.js');
|
|
244
|
+
await handleImport(rest);
|
|
245
|
+
}
|
|
217
246
|
return;
|
|
218
247
|
}
|
|
219
248
|
// --pause-auto / --resume-auto
|
|
@@ -399,7 +428,7 @@ export async function handleCompound(args) {
|
|
|
399
428
|
'--lifecycle', '--verify', '--save', '--interactive',
|
|
400
429
|
'list', 'inspect', 'remove', 'rollback', 'retag', 'lifecycle',
|
|
401
430
|
'--list', '--inspect', '--remove', '--rollback', '--retag', '--since', 'interactive',
|
|
402
|
-
'export', 'import', '--output',
|
|
431
|
+
'export', 'import', '--output', '--out', '--dry-run',
|
|
403
432
|
];
|
|
404
433
|
const hasTypeFlag = knownFlags.some(f => args.includes(f));
|
|
405
434
|
if (!hasTypeFlag) {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgen — Compound Pattern Share (패턴별 export/import)
|
|
3
|
+
*
|
|
4
|
+
* `compound-export.ts`의 `export`/`import`는 `~/.forgen/me/` 전체를 tar.gz로
|
|
5
|
+
* 통짜 백업/이관한다. 이 모듈은 그와 달리 **이름 지정된 패턴 단위**로 신뢰도
|
|
6
|
+
* (confidence/status/evidence)와 provenance를 함께 담은 JSON 번들을 만들고,
|
|
7
|
+
* 받는 쪽에서 안전하게 병합한다 (ECC `/instinct-import/export` 대응, OSS gap #1).
|
|
8
|
+
*
|
|
9
|
+
* 핵심 설계:
|
|
10
|
+
* - 번들은 스키마 버전 고정 JSON. 최상위/패턴 필드 모두 화이트리스트 검증 —
|
|
11
|
+
* 예상 못한 필드가 있으면 통째로 reject (실행 가능한 콘텐츠가 섞여 들어올
|
|
12
|
+
* 여지 자체를 차단).
|
|
13
|
+
* - 패턴마다 contentHash(sha256)를 동봉 — import 시 재계산해 일치하지 않으면
|
|
14
|
+
* reject (변조/손상 탐지).
|
|
15
|
+
* - 이름 충돌 시: 로컬 콘텐츠 해시가 같으면 "동일 패턴 재발견"으로 간주해
|
|
16
|
+
* `reExtracted` 카운터만 증가(기존 신뢰도는 건드리지 않음 — 이미 solution-writer.ts
|
|
17
|
+
* 의 dual-path 금지 불변식과 일치). 다르면 절대 덮어쓰지 않고 suffix된
|
|
18
|
+
* 이름으로 새로 생성.
|
|
19
|
+
* - 신규 생성되는 패턴은 항상 probation: status='experiment', confidence는
|
|
20
|
+
* 신규 솔루션 표준 베이스라인(statusConfidence('experiment')=0.3, 참고:
|
|
21
|
+
* extraction-persistence.ts saveExtractedSolution)을 상한으로 export 시점
|
|
22
|
+
* confidence의 절반만 반영. evidence는 전부 0으로 리셋 — exporter의 로컬
|
|
23
|
+
* 사용 이력을 이 머신이 검증 없이 물려받지 않는다. 이후 승급은 기존
|
|
24
|
+
* compound-lifecycle.ts의 단일 경로(runLifecycleCheck)로만 진행된다.
|
|
25
|
+
*/
|
|
26
|
+
import { type SolutionFrontmatter } from './solution-format.js';
|
|
27
|
+
export declare const SHARE_BUNDLE_SCHEMA_VERSION: 1;
|
|
28
|
+
export interface ShareBundlePatternV1 {
|
|
29
|
+
name: string;
|
|
30
|
+
category: 'solution' | 'rule';
|
|
31
|
+
frontmatter: SolutionFrontmatter;
|
|
32
|
+
context: string;
|
|
33
|
+
content: string;
|
|
34
|
+
/** sha256(type + sorted tags/identifiers + context + content) — confidence/evidence/timestamp 제외한 콘텐츠 지문. */
|
|
35
|
+
contentHash: string;
|
|
36
|
+
}
|
|
37
|
+
export interface ShareBundleV1 {
|
|
38
|
+
schemaVersion: 1;
|
|
39
|
+
exportedAt: string;
|
|
40
|
+
/** sha256(hostname:username) 앞 16자 — 원본 식별용, PII 비가역. */
|
|
41
|
+
originHash: string;
|
|
42
|
+
patterns: ShareBundlePatternV1[];
|
|
43
|
+
}
|
|
44
|
+
export interface BuildBundleResult {
|
|
45
|
+
bundle: ShareBundleV1;
|
|
46
|
+
notFound: string[];
|
|
47
|
+
rejectedSecrets: string[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 이름 목록으로 패턴 번들 생성.
|
|
51
|
+
*
|
|
52
|
+
* 시크릿 감지된 패턴은 조용히 스킵하지 않고 `rejectedSecrets`로 보고하되,
|
|
53
|
+
* 번들 자체는 나머지 clean한 패턴으로 계속 진행한다 (부분 실패 허용).
|
|
54
|
+
*/
|
|
55
|
+
export declare function buildShareBundle(names: string[]): BuildBundleResult;
|
|
56
|
+
export interface ShareBundleValidation {
|
|
57
|
+
ok: boolean;
|
|
58
|
+
bundle: ShareBundleV1 | null;
|
|
59
|
+
errors: string[];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 번들 검증: 크기 캡 → 최상위 필드 화이트리스트 → 패턴별 필드 화이트리스트 →
|
|
63
|
+
* frontmatter 정합성 → contentHash 재계산 일치. 하나라도 실패하면 번들 전체를
|
|
64
|
+
* reject한다 (부분 신뢰 없음 — 손상/변조된 번들은 통째로 버린다).
|
|
65
|
+
*/
|
|
66
|
+
export declare function validateShareBundle(raw: unknown, rawSize: number): ShareBundleValidation;
|
|
67
|
+
export interface ShareImportAction {
|
|
68
|
+
sourceName: string;
|
|
69
|
+
category: 'solution' | 'rule';
|
|
70
|
+
action: 'merge-reextract' | 'create' | 'create-suffixed';
|
|
71
|
+
targetName: string;
|
|
72
|
+
detail: string;
|
|
73
|
+
}
|
|
74
|
+
export interface ShareImportSummary {
|
|
75
|
+
dryRun: boolean;
|
|
76
|
+
actions: ShareImportAction[];
|
|
77
|
+
}
|
|
78
|
+
/** dry-run과 실제 실행이 공유하는 계획 수립 — 파일시스템에 아무것도 쓰지 않는다. */
|
|
79
|
+
export declare function planShareImport(bundle: ShareBundleV1): ShareImportAction[];
|
|
80
|
+
export declare function executeShareImport(bundle: ShareBundleV1, opts?: {
|
|
81
|
+
dryRun?: boolean;
|
|
82
|
+
}): ShareImportSummary;
|
|
83
|
+
export declare function looksLikeShareBundle(filePath: string): boolean;
|
|
84
|
+
export declare function handleShareExport(args: string[]): Promise<void>;
|
|
85
|
+
export declare function handleShareImport(args: string[]): Promise<void>;
|