@su-record/vibe 2.16.3 → 3.0.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/.env.example +15 -0
- package/CLAUDE.md +8 -8
- package/README.en.md +46 -29
- package/README.md +41 -24
- package/agents/acceptance-tester.md +56 -0
- package/agents/architect.md +25 -67
- package/agents/build-error-resolver.md +28 -97
- package/agents/code-reviewer.md +89 -0
- package/agents/diagrammer.md +34 -165
- package/agents/documenter.md +43 -0
- package/agents/e2e-tester.md +46 -277
- package/agents/event/event-ops.md +54 -77
- package/agents/event/event-planner.md +63 -0
- package/agents/figma/figma-engineer.md +76 -0
- package/agents/implementer.md +26 -41
- package/agents/security-reviewer.md +56 -0
- package/agents/tester.md +37 -33
- package/agents/ui/design-reviewer.md +62 -0
- package/agents/ui/design-system-gen.md +63 -0
- package/dist/__tests__/agent-model-sync.test.d.ts +2 -0
- package/dist/__tests__/agent-model-sync.test.d.ts.map +1 -0
- package/dist/__tests__/agent-model-sync.test.js +71 -0
- package/dist/__tests__/agent-model-sync.test.js.map +1 -0
- package/dist/cli/auth.d.ts.map +1 -1
- package/dist/cli/auth.js +8 -1
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/info.d.ts.map +1 -1
- package/dist/cli/commands/info.js +2 -0
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/llm.d.ts +11 -0
- package/dist/cli/commands/llm.d.ts.map +1 -0
- package/dist/cli/commands/llm.js +62 -0
- package/dist/cli/commands/llm.js.map +1 -0
- package/dist/cli/commands/remove.d.ts.map +1 -1
- package/dist/cli/commands/remove.js +8 -10
- package/dist/cli/commands/remove.js.map +1 -1
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +35 -1
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/index.d.ts +0 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +59 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/llm/config.d.ts.map +1 -1
- package/dist/cli/llm/config.js +7 -0
- package/dist/cli/llm/config.js.map +1 -1
- package/dist/cli/llm/index.d.ts +1 -0
- package/dist/cli/llm/index.d.ts.map +1 -1
- package/dist/cli/llm/index.js +1 -0
- package/dist/cli/llm/index.js.map +1 -1
- package/dist/cli/llm/model-refresh.d.ts +29 -0
- package/dist/cli/llm/model-refresh.d.ts.map +1 -0
- package/dist/cli/llm/model-refresh.js +139 -0
- package/dist/cli/llm/model-refresh.js.map +1 -0
- package/dist/cli/llm/zai-commands.d.ts +15 -0
- package/dist/cli/llm/zai-commands.d.ts.map +1 -0
- package/dist/cli/llm/zai-commands.js +64 -0
- package/dist/cli/llm/zai-commands.js.map +1 -0
- package/dist/cli/postinstall/claude-agents.d.ts.map +1 -1
- package/dist/cli/postinstall/claude-agents.js +16 -77
- package/dist/cli/postinstall/claude-agents.js.map +1 -1
- package/dist/cli/postinstall/constants.d.ts.map +1 -1
- package/dist/cli/postinstall/constants.js +81 -296
- package/dist/cli/postinstall/constants.js.map +1 -1
- package/dist/cli/postinstall/cursor-agents.d.ts +5 -0
- package/dist/cli/postinstall/cursor-agents.d.ts.map +1 -1
- package/dist/cli/postinstall/cursor-agents.js +26 -68
- package/dist/cli/postinstall/cursor-agents.js.map +1 -1
- package/dist/cli/postinstall/cursor-skills.d.ts.map +1 -1
- package/dist/cli/postinstall/cursor-skills.js +26 -30
- package/dist/cli/postinstall/cursor-skills.js.map +1 -1
- package/dist/cli/postinstall/main.d.ts.map +1 -1
- package/dist/cli/postinstall/main.js +0 -2
- package/dist/cli/postinstall/main.js.map +1 -1
- package/dist/cli/setup/Provisioner.d.ts.map +1 -1
- package/dist/cli/setup/Provisioner.js +19 -21
- package/dist/cli/setup/Provisioner.js.map +1 -1
- package/dist/cli/types.d.ts +15 -0
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/utils.d.ts +4 -1
- package/dist/cli/utils.d.ts.map +1 -1
- package/dist/cli/utils.js +4 -1
- package/dist/cli/utils.js.map +1 -1
- package/dist/infra/lib/FrameworkDetector.js +4 -4
- package/dist/infra/lib/FrameworkDetector.js.map +1 -1
- package/dist/infra/lib/OrchestrateWorkflow.js +2 -2
- package/dist/infra/lib/OrchestrateWorkflow.js.map +1 -1
- package/dist/infra/lib/browser/launch.js +1 -1
- package/dist/infra/lib/browser/launch.js.map +1 -1
- package/dist/infra/lib/browser/types.d.ts +1 -1
- package/dist/infra/lib/browser/types.js +1 -1
- package/dist/infra/lib/config/GlobalConfigManager.d.ts +2 -0
- package/dist/infra/lib/config/GlobalConfigManager.d.ts.map +1 -1
- package/dist/infra/lib/config/GlobalConfigManager.js +6 -0
- package/dist/infra/lib/config/GlobalConfigManager.js.map +1 -1
- package/dist/infra/lib/constants.d.ts +1 -1
- package/dist/infra/lib/constants.d.ts.map +1 -1
- package/dist/infra/lib/constants.js +4 -5
- package/dist/infra/lib/constants.js.map +1 -1
- package/dist/infra/lib/evolution/__tests__/integration.test.js +7 -7
- package/dist/infra/lib/evolution/__tests__/integration.test.js.map +1 -1
- package/dist/infra/lib/llm-availability.d.ts +4 -0
- package/dist/infra/lib/llm-availability.d.ts.map +1 -1
- package/dist/infra/lib/llm-availability.js +9 -1
- package/dist/infra/lib/llm-availability.js.map +1 -1
- package/dist/infra/lib/memory/index.d.ts +0 -2
- package/dist/infra/lib/memory/index.d.ts.map +1 -1
- package/dist/infra/lib/memory/index.js +0 -2
- package/dist/infra/lib/memory/index.js.map +1 -1
- package/dist/infra/lib/telemetry/SkillTelemetry.test.js +1 -1
- package/dist/infra/lib/telemetry/SkillTelemetry.test.js.map +1 -1
- package/dist/infra/lib/zai/client.d.ts +30 -0
- package/dist/infra/lib/zai/client.d.ts.map +1 -0
- package/dist/infra/lib/zai/client.js +124 -0
- package/dist/infra/lib/zai/client.js.map +1 -0
- package/dist/infra/lib/zai/index.d.ts +11 -0
- package/dist/infra/lib/zai/index.d.ts.map +1 -0
- package/dist/infra/lib/zai/index.js +10 -0
- package/dist/infra/lib/zai/index.js.map +1 -0
- package/dist/infra/lib/zai/orchestration.d.ts +15 -0
- package/dist/infra/lib/zai/orchestration.d.ts.map +1 -0
- package/dist/infra/lib/zai/orchestration.js +54 -0
- package/dist/infra/lib/zai/orchestration.js.map +1 -0
- package/dist/infra/lib/zai/types.d.ts +40 -0
- package/dist/infra/lib/zai/types.d.ts.map +1 -0
- package/dist/infra/lib/zai/types.js +5 -0
- package/dist/infra/lib/zai/types.js.map +1 -0
- package/dist/infra/orchestrator/SmartRouter.d.ts.map +1 -1
- package/dist/infra/orchestrator/SmartRouter.js +16 -34
- package/dist/infra/orchestrator/SmartRouter.js.map +1 -1
- package/dist/infra/orchestrator/index.d.ts +1 -0
- package/dist/infra/orchestrator/index.d.ts.map +1 -1
- package/dist/infra/orchestrator/index.js +11 -9
- package/dist/infra/orchestrator/index.js.map +1 -1
- package/dist/infra/orchestrator/types.d.ts +32 -9
- package/dist/infra/orchestrator/types.d.ts.map +1 -1
- package/dist/infra/orchestrator/types.js +68 -37
- package/dist/infra/orchestrator/types.js.map +1 -1
- package/dist/infra/orchestrator/types.test.d.ts +2 -0
- package/dist/infra/orchestrator/types.test.d.ts.map +1 -0
- package/dist/infra/orchestrator/types.test.js +57 -0
- package/dist/infra/orchestrator/types.test.js.map +1 -0
- package/dist/tools/index.d.ts +0 -4
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +0 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/memory/autoSaveContext.d.ts +0 -1
- package/dist/tools/memory/autoSaveContext.d.ts.map +1 -1
- package/dist/tools/memory/autoSaveContext.js +13 -27
- package/dist/tools/memory/autoSaveContext.js.map +1 -1
- package/hooks/scripts/__tests__/code-check-detectors.test.js +2 -2
- package/hooks/scripts/__tests__/run-ledger.test.js +1 -1
- package/hooks/scripts/__tests__/scope-from-spec.test.js +12 -12
- package/hooks/scripts/__tests__/step-counter.test.js +1 -1
- package/hooks/scripts/code-check.js +16 -203
- package/hooks/scripts/context-save.js +0 -11
- package/hooks/scripts/lib/scope-from-spec.js +9 -6
- package/hooks/scripts/llm-orchestrate.js +49 -0
- package/hooks/scripts/post-edit-dispatcher.js +1 -1
- package/hooks/scripts/prompt-dispatcher.js +7 -18
- package/hooks/scripts/session-start.js +25 -35
- package/package.json +4 -4
- package/skills/agents-md/SKILL.md +62 -15
- package/skills/arch-guard/SKILL.md +1 -1
- package/skills/brand-assets/SKILL.md +2 -2
- package/skills/capability-loop/SKILL.md +1 -1
- package/skills/chub-usage/SKILL.md +1 -1
- package/skills/commerce-patterns/SKILL.md +2 -2
- package/skills/commit-push-pr/SKILL.md +0 -1
- package/skills/context7-usage/SKILL.md +1 -1
- package/skills/create-prd/agents/edge-case-finder.md +4 -4
- package/skills/create-prd/agents/prioritizer.md +4 -4
- package/skills/create-prd/agents/requirements-writer.md +3 -3
- package/skills/create-prd/agents/researcher.md +3 -3
- package/skills/create-prd/agents/reviewer.md +3 -3
- package/skills/create-prd/orchestrator.md +2 -0
- package/skills/design-refine/SKILL.md +140 -0
- package/skills/{design-polish → design-refine}/templates/polish-report.md +1 -1
- package/skills/{design-normalize → design-refine}/templates/token-audit.md +3 -3
- package/skills/design-review/SKILL.md +143 -0
- package/skills/{design-audit → design-review}/agents/a11y-auditor.md +1 -1
- package/skills/{design-audit → design-review}/agents/performance-auditor.md +1 -1
- package/skills/{design-audit → design-review}/agents/responsive-auditor.md +1 -1
- package/skills/{design-audit → design-review}/agents/slop-detector.md +1 -1
- package/skills/{design-audit → design-review}/frameworks/core-web-vitals.md +1 -1
- package/skills/{design-audit → design-review}/frameworks/wcag-checklist.md +1 -1
- package/skills/{design-audit → design-review}/orchestrator.md +5 -3
- package/skills/{design-critique → design-review}/templates/critique-report.md +3 -3
- package/skills/{design-audit → design-review}/templates/report.md +3 -3
- package/skills/design-teach/SKILL.md +11 -11
- package/skills/docs/SKILL.md +14 -16
- package/skills/e2e-commerce/SKILL.md +1 -1
- package/skills/event-comms/SKILL.md +1 -1
- package/skills/event-ops/SKILL.md +2 -2
- package/skills/event-planning/SKILL.md +1 -1
- package/skills/exec-plan/SKILL.md +2 -2
- package/skills/exec-plan/orchestrator.md +2 -0
- package/skills/figma/SKILL.md +240 -148
- package/skills/figma/rubrics/css-mapping.md +125 -0
- package/skills/git-worktree/SKILL.md +1 -1
- package/skills/handoff/SKILL.md +1 -1
- package/skills/parallel-research/SKILL.md +14 -28
- package/skills/parallel-research/orchestrator.md +2 -0
- package/skills/priority-todos/SKILL.md +1 -1
- package/skills/regress/SKILL.md +11 -0
- package/skills/restraint/SKILL.md +72 -0
- package/skills/seo-checklist/SKILL.md +2 -2
- package/skills/spec/SKILL.md +65 -836
- package/skills/test/SKILL.md +3 -3
- package/skills/tool-fallback/SKILL.md +2 -2
- package/skills/vercel-react-best-practices/SKILL.md +1 -1
- package/skills/vibe/SKILL.md +20 -35
- package/skills/vibe.analyze/SKILL.md +7 -9
- package/skills/vibe.docs/SKILL.md +1 -1
- package/skills/vibe.event/SKILL.md +27 -27
- package/skills/vibe.figma/SKILL.md +14 -18
- package/skills/vibe.harness/SKILL.md +5 -5
- package/skills/vibe.llm/SKILL.md +50 -0
- package/skills/vibe.reason/SKILL.md +0 -2
- package/skills/vibe.review/SKILL.md +45 -49
- package/skills/vibe.run/SKILL.md +2 -5
- package/skills/vibe.run/references/parallel-agents.md +33 -98
- package/skills/vibe.spec/SKILL.md +38 -535
- package/skills/vibe.utils/SKILL.md +3 -3
- package/skills/vibe.verify/SKILL.md +70 -542
- package/skills/video-production/SKILL.md +1 -1
- package/vibe/rules/orchestrator-contract.md +57 -0
- package/vibe/rules/quality/performance.md +1 -1
- package/vibe/templates/claudemd-template.md +1 -1
- package/vibe/templates/spec-template.md +35 -185
- package/agents/architect-low.md +0 -41
- package/agents/architect-medium.md +0 -59
- package/agents/compounder.md +0 -261
- package/agents/docs/api-documenter.md +0 -99
- package/agents/docs/changelog-writer.md +0 -93
- package/agents/event/event-comms.md +0 -78
- package/agents/event/event-content.md +0 -68
- package/agents/event/event-image.md +0 -95
- package/agents/event/event-scheduler.md +0 -69
- package/agents/event/event-speaker.md +0 -86
- package/agents/explorer-low.md +0 -42
- package/agents/explorer-medium.md +0 -59
- package/agents/explorer.md +0 -48
- package/agents/figma/figma-analyst.md +0 -52
- package/agents/figma/figma-architect.md +0 -112
- package/agents/figma/figma-auditor.md +0 -82
- package/agents/figma/figma-builder.md +0 -100
- package/agents/implementer-low.md +0 -43
- package/agents/implementer-medium.md +0 -52
- package/agents/junior-mentor.md +0 -141
- package/agents/planning/requirements-analyst.md +0 -84
- package/agents/planning/ux-advisor.md +0 -83
- package/agents/qa/acceptance-tester.md +0 -86
- package/agents/qa/edge-case-finder.md +0 -93
- package/agents/qa/qa-coordinator.md +0 -131
- package/agents/refactor-cleaner.md +0 -143
- package/agents/research/best-practices.md +0 -199
- package/agents/research/codebase-patterns.md +0 -157
- package/agents/research/framework-docs.md +0 -188
- package/agents/research/security-advisory.md +0 -213
- package/agents/review/architecture-reviewer.md +0 -107
- package/agents/review/complexity-reviewer.md +0 -116
- package/agents/review/data-integrity-reviewer.md +0 -88
- package/agents/review/git-history-reviewer.md +0 -103
- package/agents/review/performance-reviewer.md +0 -86
- package/agents/review/python-reviewer.md +0 -150
- package/agents/review/rails-reviewer.md +0 -139
- package/agents/review/react-reviewer.md +0 -144
- package/agents/review/security-reviewer.md +0 -80
- package/agents/review/simplicity-reviewer.md +0 -140
- package/agents/review/test-coverage-reviewer.md +0 -116
- package/agents/review/typescript-reviewer.md +0 -127
- package/agents/searcher.md +0 -54
- package/agents/simplifier.md +0 -120
- package/agents/teams/debug-team.md +0 -70
- package/agents/teams/dev-team.md +0 -88
- package/agents/teams/docs-team.md +0 -80
- package/agents/teams/figma-team.md +0 -85
- package/agents/teams/fullstack-team.md +0 -83
- package/agents/teams/lite-team.md +0 -69
- package/agents/teams/migration-team.md +0 -78
- package/agents/teams/refactor-team.md +0 -94
- package/agents/teams/research-team.md +0 -86
- package/agents/teams/review-debate-team.md +0 -125
- package/agents/teams/security-team.md +0 -81
- package/agents/ui/ui-a11y-auditor.md +0 -93
- package/agents/ui/ui-antipattern-detector.md +0 -102
- package/agents/ui/ui-dataviz-advisor.md +0 -69
- package/agents/ui/ui-design-system-gen.md +0 -57
- package/agents/ui/ui-industry-analyzer.md +0 -49
- package/agents/ui/ui-layout-architect.md +0 -65
- package/agents/ui/ui-stack-implementer.md +0 -68
- package/agents/ui/ux-compliance-reviewer.md +0 -81
- package/agents/ui-previewer.md +0 -258
- package/hooks/scripts/__tests__/keyword-detector.test.js +0 -242
- package/hooks/scripts/keyword-detector.js +0 -226
- package/skills/characterization-test/SKILL.md +0 -209
- package/skills/characterization-test/agents/behavior-capturer.md +0 -50
- package/skills/characterization-test/agents/coverage-checker.md +0 -54
- package/skills/characterization-test/agents/reporter.md +0 -50
- package/skills/characterization-test/agents/test-writer.md +0 -49
- package/skills/characterization-test/rubrics/coverage-criteria.md +0 -53
- package/skills/characterization-test/templates/test-template.ts +0 -101
- package/skills/claude-md-guide/SKILL.md +0 -353
- package/skills/claude-md-guide/rubrics/anti-patterns.md +0 -88
- package/skills/design-audit/SKILL.md +0 -154
- package/skills/design-critique/SKILL.md +0 -140
- package/skills/design-distill/SKILL.md +0 -131
- package/skills/design-normalize/SKILL.md +0 -135
- package/skills/design-polish/SKILL.md +0 -132
- package/skills/figma-convert/SKILL.md +0 -236
- package/skills/figma-extract/SKILL.md +0 -242
- package/skills/interview/SKILL.md +0 -358
- package/skills/interview/checklists/api.md +0 -101
- package/skills/interview/checklists/feature.md +0 -88
- package/skills/interview/checklists/library.md +0 -95
- package/skills/interview/checklists/mobile.md +0 -89
- package/skills/interview/checklists/webapp.md +0 -97
- package/skills/interview/checklists/website.md +0 -99
- package/skills/plan/SKILL.md +0 -254
- package/skills/rob-pike/SKILL.md +0 -66
- package/skills/spec/references/askuser-examples.md +0 -57
- package/skills/spec/references/example-session.md +0 -87
- package/skills/spec/references/templates.md +0 -194
- package/skills/spec-review/SKILL.md +0 -725
- package/skills/systematic-debugging/SKILL.md +0 -142
- package/skills/techdebt/SKILL.md +0 -126
- package/skills/techdebt/agents/analyzer.md +0 -50
- package/skills/techdebt/agents/fixer.md +0 -41
- package/skills/techdebt/agents/reviewer.md +0 -47
- package/skills/techdebt/agents/scanner.md +0 -44
- package/skills/techdebt/orchestrator.md +0 -70
- package/skills/techdebt/rubrics/severity.md +0 -51
- package/skills/techdebt/scripts/scan.js +0 -90
- package/skills/techdebt/templates/report.md +0 -86
- package/skills/typescript-advanced-types/SKILL.md +0 -68
- package/skills/typescript-advanced-types/rubrics/type-patterns.md +0 -109
- package/skills/yagni-ladder/SKILL.md +0 -90
- /package/skills/{claude-md-guide → agents-md}/templates/claude-md.md +0 -0
- /package/skills/{design-polish → design-refine}/rubrics/polish-checklist.md +0 -0
- /package/skills/{design-normalize → design-refine}/rubrics/token-naming.md +0 -0
- /package/skills/{design-distill → design-refine}/templates/design-system.md +0 -0
- /package/skills/{design-audit → design-review}/agents/scorer.md +0 -0
- /package/skills/{design-audit → design-review}/rubrics/ai-slop-patterns.md +0 -0
- /package/skills/{design-audit → design-review}/rubrics/scoring.md +0 -0
- /package/skills/{design-critique → design-review}/rubrics/ux-heuristics.md +0 -0
- /package/skills/{figma-convert → figma}/rubrics/conversion-rules.md +0 -0
- /package/skills/{figma-extract → figma}/rubrics/image-rules.md +0 -0
- /package/skills/{figma-convert → figma}/templates/component.md +0 -0
|
@@ -37,44 +37,44 @@ function writeSpec(dir, filename, content, status = 'in-progress') {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// ══════════════════════════════════════════════════
|
|
40
|
-
// isScopeGuardEnabled — 기본
|
|
40
|
+
// isScopeGuardEnabled — 기본 OFF (opt-in), CLI isScopeGuardOptedIn 과 동일 기본값
|
|
41
41
|
// ══════════════════════════════════════════════════
|
|
42
42
|
describe('isScopeGuardEnabled', () => {
|
|
43
43
|
let tmpDir;
|
|
44
44
|
beforeEach(() => { tmpDir = makeTempDir(); });
|
|
45
45
|
afterEach(() => { cleanDir(tmpDir); });
|
|
46
46
|
|
|
47
|
-
it('config 없음 →
|
|
48
|
-
expect(isScopeGuardEnabled(tmpDir)).toBe(
|
|
47
|
+
it('config 없음 → false (기본 OFF)', () => {
|
|
48
|
+
expect(isScopeGuardEnabled(tmpDir)).toBe(false);
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
it('scopeGuard 키 없는 config →
|
|
51
|
+
it('scopeGuard 키 없는 config → false', () => {
|
|
52
52
|
writeConfig(tmpDir, undefined);
|
|
53
|
-
expect(isScopeGuardEnabled(tmpDir)).toBe(
|
|
53
|
+
expect(isScopeGuardEnabled(tmpDir)).toBe(false);
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
it('scopeGuard.enabled 없는 config →
|
|
56
|
+
it('scopeGuard.enabled 없는 config → false', () => {
|
|
57
57
|
const vibeDir = path.join(tmpDir, '.vibe');
|
|
58
58
|
fs.mkdirSync(vibeDir, { recursive: true });
|
|
59
59
|
fs.writeFileSync(path.join(vibeDir, 'config.json'), JSON.stringify({ scopeGuard: {} }));
|
|
60
|
-
expect(isScopeGuardEnabled(tmpDir)).toBe(
|
|
60
|
+
expect(isScopeGuardEnabled(tmpDir)).toBe(false);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
it('scopeGuard.enabled=true → true', () => {
|
|
63
|
+
it('scopeGuard.enabled=true → true (명시적 opt-in)', () => {
|
|
64
64
|
writeConfig(tmpDir, { enabled: true });
|
|
65
65
|
expect(isScopeGuardEnabled(tmpDir)).toBe(true);
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
it('scopeGuard.enabled=false → false
|
|
68
|
+
it('scopeGuard.enabled=false → false', () => {
|
|
69
69
|
writeConfig(tmpDir, { enabled: false });
|
|
70
70
|
expect(isScopeGuardEnabled(tmpDir)).toBe(false);
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
it('legacy .claude/vibe/config.json에서도 enabled=
|
|
73
|
+
it('legacy .claude/vibe/config.json에서도 enabled=true 인식', () => {
|
|
74
74
|
const legacyDir = path.join(tmpDir, '.claude', 'vibe');
|
|
75
75
|
fs.mkdirSync(legacyDir, { recursive: true });
|
|
76
|
-
fs.writeFileSync(path.join(legacyDir, 'config.json'), JSON.stringify({ scopeGuard: { enabled:
|
|
77
|
-
expect(isScopeGuardEnabled(tmpDir)).toBe(
|
|
76
|
+
fs.writeFileSync(path.join(legacyDir, 'config.json'), JSON.stringify({ scopeGuard: { enabled: true } }));
|
|
77
|
+
expect(isScopeGuardEnabled(tmpDir)).toBe(true);
|
|
78
78
|
});
|
|
79
79
|
});
|
|
80
80
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - 책임 3) error_category 분류기 + 3-fail detector → anti-pattern md (Phase 2)
|
|
8
8
|
* - 책임 간 독립성, hot-path 안정성
|
|
9
9
|
*
|
|
10
|
-
* 패턴:
|
|
10
|
+
* 패턴: 훅 스크립트 공통의 execFileSync 방식.
|
|
11
11
|
*/
|
|
12
12
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
13
13
|
import { spawnSync } from 'child_process';
|
|
@@ -2,14 +2,18 @@
|
|
|
2
2
|
* PostToolUse Hook - Write/Edit 후 코드 품질 검사 + 관찰 자동 캡처
|
|
3
3
|
*
|
|
4
4
|
* findings를 console.log가 아닌 반환값으로 전달 — 디스패처가 수집해 additionalContext에 주입.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
|
+
* 범위 (harness-review-2026-07-01 P1-4):
|
|
7
|
+
* 오탐률이 낮은 결정론적 하드룰(any/@ts-ignore, console.log)만 탐지한다.
|
|
8
|
+
* 함수 길이·중첩 깊이·매직 넘버 같은 휴리스틱은 모델이 컨텍스트 안에서 더
|
|
9
|
+
* 정확히 판단하므로 주입하지 않으며, 정규식 발견은 커밋 게이트
|
|
10
|
+
* (run-ledger verifyRequired)를 태우지 않는다 — 그 게이트는 결정론적
|
|
11
|
+
* 검증 흐름(vibe.verify) 전용이다.
|
|
6
12
|
*/
|
|
7
13
|
import { getToolsBaseUrl, PROJECT_DIR, readProjectConfig } from './utils.js';
|
|
8
|
-
import { readFileSync
|
|
14
|
+
import { readFileSync } from 'fs';
|
|
9
15
|
import path from 'path';
|
|
10
|
-
import os from 'os';
|
|
11
16
|
import { buildCliCtx, isDirectRun } from './lib/hook-context.js';
|
|
12
|
-
import { recordVerifyRequired } from './lib/run-ledger.js';
|
|
13
17
|
|
|
14
18
|
const BASE_URL = getToolsBaseUrl();
|
|
15
19
|
|
|
@@ -176,180 +180,22 @@ function detectConsoleLogs(lines, filePath) {
|
|
|
176
180
|
}
|
|
177
181
|
|
|
178
182
|
/**
|
|
179
|
-
*
|
|
180
|
-
* @param {string[]} lines
|
|
181
|
-
* @returns {Array<{ line: number, match: string, severity: 'P2' }>}
|
|
182
|
-
*/
|
|
183
|
-
function detectLongFunctions(lines) {
|
|
184
|
-
const findings = [];
|
|
185
|
-
let fnStart = -1;
|
|
186
|
-
let fnName = '';
|
|
187
|
-
let depth = 0;
|
|
188
|
-
|
|
189
|
-
for (let i = 0; i < lines.length; i++) {
|
|
190
|
-
const line = lines[i];
|
|
191
|
-
const fnMatch = line.match(/(?:function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\()/);
|
|
192
|
-
if (fnMatch && depth === 0) {
|
|
193
|
-
fnStart = i;
|
|
194
|
-
fnName = fnMatch[1] || fnMatch[2] || 'anonymous';
|
|
195
|
-
}
|
|
196
|
-
depth += (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length;
|
|
197
|
-
if (fnStart !== -1 && depth <= 0) {
|
|
198
|
-
const length = i - fnStart + 1;
|
|
199
|
-
if (length > 50) {
|
|
200
|
-
findings.push({
|
|
201
|
-
line: fnStart + 1,
|
|
202
|
-
match: `function '${fnName}' is ${length} lines`,
|
|
203
|
-
severity: 'P2',
|
|
204
|
-
suggestion: `Extract lines ${fnStart + 20}–${i + 1} into a separate helper function`,
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
fnStart = -1;
|
|
208
|
-
depth = 0;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
return findings;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* P2: 중첩 깊이 초과 탐지.
|
|
216
|
-
* @param {string[]} lines
|
|
217
|
-
* @returns {Array<{ line: number, match: string, severity: 'P2' }>}
|
|
218
|
-
*/
|
|
219
|
-
function detectDeepNesting(lines) {
|
|
220
|
-
const findings = [];
|
|
221
|
-
let depth = 0;
|
|
222
|
-
let reported = false;
|
|
223
|
-
|
|
224
|
-
lines.forEach((line, i) => {
|
|
225
|
-
depth += (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length;
|
|
226
|
-
if (depth > 3 && !reported) {
|
|
227
|
-
findings.push({
|
|
228
|
-
line: i + 1,
|
|
229
|
-
match: `nesting depth ${depth} at line ${i + 1}`,
|
|
230
|
-
severity: 'P2',
|
|
231
|
-
suggestion: 'Use early return pattern: if (!condition) return; — instead of wrapping in else',
|
|
232
|
-
});
|
|
233
|
-
reported = true;
|
|
234
|
-
}
|
|
235
|
-
if (depth <= 3) reported = false;
|
|
236
|
-
});
|
|
237
|
-
return findings;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// magic number 무시 값 목록 (0, 1, -1, 2, 10, 100, 1000)
|
|
241
|
-
const MAGIC_NUMBER_IGNORE = new Set(['10', '100', '1000']);
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* advisory: 매직 넘버 탐지 (P3, 스팸 방지 적용).
|
|
245
|
-
* 무시 조건: 0/1/-1/2 (단일 digit), ALLCAPS const 선언, 테스트 파일, 배열 인덱스
|
|
246
|
-
* @param {string[]} lines
|
|
247
|
-
* @param {string} filePath
|
|
248
|
-
* @returns {Array<{ line: number, match: string, severity: 'P3' }>}
|
|
249
|
-
*/
|
|
250
|
-
function detectMagicNumbers(lines, filePath) {
|
|
251
|
-
// 테스트 파일은 스킵
|
|
252
|
-
if (/\.(test|spec)\.[jt]sx?$/.test(filePath) || /\/__tests__\//.test(filePath)) {
|
|
253
|
-
return [];
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const findings = [];
|
|
257
|
-
lines.forEach((line, i) => {
|
|
258
|
-
// ALL_CAPS const 선언 줄은 스킵 (예: const LIMIT = 500)
|
|
259
|
-
if (/^\s*(?:export\s+)?const\s+[A-Z][A-Z0-9_]+\s*=/.test(line)) return;
|
|
260
|
-
|
|
261
|
-
const stripped = line.replace(/\/\/.*$/, '').replace(/(['"`]).*?\1/g, '""');
|
|
262
|
-
const nums = (stripped.match(/\b\d{2,}\b/g) || []).filter(n => MAGIC_NUMBER_IGNORE.has(n));
|
|
263
|
-
// 배열 인덱스: [NN] 패턴 제외
|
|
264
|
-
const nonIndexNums = (stripped.match(/\b\d{2,}\b/g) || []).filter(n => {
|
|
265
|
-
if (MAGIC_NUMBER_IGNORE.has(n)) return false;
|
|
266
|
-
// 배열 인덱스 [NN] 체크
|
|
267
|
-
const idx = stripped.indexOf(n);
|
|
268
|
-
if (idx > 0 && stripped[idx - 1] === '[') return false;
|
|
269
|
-
return true;
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
if (nonIndexNums.length > 0) {
|
|
273
|
-
findings.push({
|
|
274
|
-
line: i + 1,
|
|
275
|
-
match: `magic number(s): ${nonIndexNums.join(', ')}`,
|
|
276
|
-
severity: 'P3',
|
|
277
|
-
suggestion: `Extract to named constant: const LIMIT = ${nonIndexNums[0]};`,
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
return findings;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// ─── Failure Escalation Tracking ───
|
|
285
|
-
|
|
286
|
-
const ESCALATION_THRESHOLD = 3;
|
|
287
|
-
const ESCALATION_FILE = path.join(os.homedir(), '.vibe', 'failure-tracker.json');
|
|
288
|
-
|
|
289
|
-
function loadFailureTracker() {
|
|
290
|
-
try {
|
|
291
|
-
if (existsSync(ESCALATION_FILE)) {
|
|
292
|
-
return JSON.parse(readFileSync(ESCALATION_FILE, 'utf8'));
|
|
293
|
-
}
|
|
294
|
-
} catch { /* ignore */ }
|
|
295
|
-
return {};
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function saveFailureTracker(tracker) {
|
|
299
|
-
try {
|
|
300
|
-
writeFileSync(ESCALATION_FILE, JSON.stringify(tracker));
|
|
301
|
-
} catch { /* ignore */ }
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* 실패 추적 — 동일 파일에 P1 이슈가 반복되면 에스컬레이션 메시지 반환.
|
|
183
|
+
* 하드룰 탐지기 실행. findings 배열 반환.
|
|
306
184
|
* @param {string} filePath
|
|
307
|
-
* @
|
|
308
|
-
* @returns {string|null} 에스컬레이션 메시지 (없으면 null)
|
|
309
|
-
*/
|
|
310
|
-
function trackFailure(filePath, issues) {
|
|
311
|
-
const tracker = loadFailureTracker();
|
|
312
|
-
const key = filePath;
|
|
313
|
-
const entry = tracker[key] || { count: 0, issues: [] };
|
|
314
|
-
entry.count += 1;
|
|
315
|
-
entry.issues = issues.slice(0, 3);
|
|
316
|
-
entry.lastSeen = new Date().toISOString();
|
|
317
|
-
tracker[key] = entry;
|
|
318
|
-
saveFailureTracker(tracker);
|
|
319
|
-
|
|
320
|
-
if (entry.count >= ESCALATION_THRESHOLD) {
|
|
321
|
-
const msg = `[ESCALATION] ${path.basename(filePath)}: 동일 이슈 ${entry.count}회 반복 — 수동 개입 필요`;
|
|
322
|
-
entry.count = 0;
|
|
323
|
-
saveFailureTracker(tracker);
|
|
324
|
-
return msg;
|
|
325
|
-
}
|
|
326
|
-
return null;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
function clearFailure(filePath) {
|
|
330
|
-
const tracker = loadFailureTracker();
|
|
331
|
-
delete tracker[filePath];
|
|
332
|
-
saveFailureTracker(tracker);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* 모든 self-heal 탐지기 실행. findings 배열 반환.
|
|
337
|
-
* @param {string} filePath
|
|
338
|
-
* @returns {{ p1: string[], advisory: string[], escalation: string|null }}
|
|
185
|
+
* @returns {{ p1: string[] }}
|
|
339
186
|
*/
|
|
340
187
|
function runDetectors(filePath) {
|
|
341
188
|
let content;
|
|
342
189
|
try {
|
|
343
190
|
content = readFileSync(filePath, 'utf-8');
|
|
344
191
|
} catch {
|
|
345
|
-
return { p1: []
|
|
192
|
+
return { p1: [] };
|
|
346
193
|
}
|
|
347
194
|
|
|
348
195
|
const lines = content.split('\n');
|
|
349
196
|
const isTs = TS_EXT_RE.test(filePath);
|
|
350
197
|
|
|
351
198
|
const p1Findings = [];
|
|
352
|
-
const advisoryFindings = [];
|
|
353
199
|
|
|
354
200
|
// P1: any 탐지 — TS 파일만
|
|
355
201
|
if (isTs) {
|
|
@@ -365,32 +211,7 @@ function runDetectors(filePath) {
|
|
|
365
211
|
p1Findings.push(`P1 console.log line ${f.line}: ${f.match.substring(0, 60)}`);
|
|
366
212
|
}
|
|
367
213
|
|
|
368
|
-
|
|
369
|
-
const fnHits = detectLongFunctions(lines).slice(0, 1);
|
|
370
|
-
for (const f of fnHits) {
|
|
371
|
-
advisoryFindings.push(`P2 ${f.match}`);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// P2: 중첩 깊이
|
|
375
|
-
const nestHits = detectDeepNesting(lines).slice(0, 1);
|
|
376
|
-
for (const f of nestHits) {
|
|
377
|
-
advisoryFindings.push(`P2 ${f.match}`);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// P3: 매직 넘버 (advisory, 스팸 방지)
|
|
381
|
-
const magicHits = detectMagicNumbers(lines, filePath).slice(0, 1);
|
|
382
|
-
for (const f of magicHits) {
|
|
383
|
-
advisoryFindings.push(`P3 ${f.match}`);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
let escalation = null;
|
|
387
|
-
if (p1Findings.length > 0) {
|
|
388
|
-
escalation = trackFailure(filePath, p1Findings);
|
|
389
|
-
} else {
|
|
390
|
-
clearFailure(filePath);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return { p1: p1Findings, advisory: advisoryFindings, escalation };
|
|
214
|
+
return { p1: p1Findings };
|
|
394
215
|
}
|
|
395
216
|
|
|
396
217
|
/**
|
|
@@ -404,20 +225,12 @@ export async function run(ctx) {
|
|
|
404
225
|
const files = getModifiedFiles(ctx);
|
|
405
226
|
if (files.length === 0) return { exitCode: 0, findings };
|
|
406
227
|
|
|
407
|
-
// 1.
|
|
228
|
+
// 1. 하드룰 탐지기 실행 (changed file only) — additionalContext 주입만.
|
|
229
|
+
// 커밋 게이트(verifyRequired)는 태우지 않는다: 정규식 오탐 하나가
|
|
230
|
+
// auto-commit을 차단하는 결합은 제거됨 (harness-review-2026-07-01).
|
|
408
231
|
try {
|
|
409
|
-
const { p1
|
|
410
|
-
|
|
232
|
+
const { p1 } = runDetectors(files[0]);
|
|
411
233
|
for (const msg of p1) findings.push(msg);
|
|
412
|
-
for (const msg of advisory) findings.push(msg);
|
|
413
|
-
if (escalation) findings.push(escalation);
|
|
414
|
-
|
|
415
|
-
// P1 이슈 → run-ledger에 verifyRequired 기록
|
|
416
|
-
if (p1.length > 0) {
|
|
417
|
-
try {
|
|
418
|
-
recordVerifyRequired(PROJECT_DIR, p1[0]);
|
|
419
|
-
} catch { /* fail-open */ }
|
|
420
|
-
}
|
|
421
234
|
} catch {
|
|
422
235
|
// 탐지기 실패 → fail-open, 계속 진행
|
|
423
236
|
}
|
|
@@ -162,17 +162,6 @@ async function main() {
|
|
|
162
162
|
// Mark this urgency level as recently saved
|
|
163
163
|
setDebounceLock(urgency);
|
|
164
164
|
|
|
165
|
-
// Sync TokenBudgetTracker with current context usage
|
|
166
|
-
try {
|
|
167
|
-
const { TokenBudgetTracker } = await import(`${LIB_URL}TokenBudgetTracker.js`);
|
|
168
|
-
const tracker = TokenBudgetTracker.getInstance(PROJECT_DIR);
|
|
169
|
-
const numPercent = Number(percent);
|
|
170
|
-
if (!isNaN(numPercent)) {
|
|
171
|
-
tracker.syncToPercent(numPercent);
|
|
172
|
-
}
|
|
173
|
-
} catch {
|
|
174
|
-
// TokenBudgetTracker sync is non-critical
|
|
175
|
-
}
|
|
176
165
|
} catch {
|
|
177
166
|
// 무시
|
|
178
167
|
}
|
|
@@ -28,10 +28,13 @@ function isRecentlyModified(file) {
|
|
|
28
28
|
/**
|
|
29
29
|
* scope-guard auto-derive 활성 여부.
|
|
30
30
|
*
|
|
31
|
-
* 기본값은 **
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
31
|
+
* 기본값은 **off** — `.vibe/config.json` 의 `scopeGuard.enabled = true` 로
|
|
32
|
+
* 명시적 opt-in 했을 때만 SPEC → scope.json 자동 합성을 실행한다.
|
|
33
|
+
* (자동 ON 은 SPEC 외 편집에 노이즈 경고 회귀를 유발해 CLI 쪽에서 이미
|
|
34
|
+
* 기본 off 로 수정됐다. 이 훅이 기본 on 으로 남아 init/update 와
|
|
35
|
+
* SessionStart 가 서로 다른 scope 상태를 만들던 불일치를 통일 —
|
|
36
|
+
* harness-review-2026-07-01 P1-6. SSOT: src/cli/utils.ts `isScopeGuardOptedIn`
|
|
37
|
+
* 과 반드시 같은 기본값 유지.)
|
|
35
38
|
*
|
|
36
39
|
* 이미 `auto: false` scope.json 을 직접 만들어 둔 사용자는 영향 없음 — scope-guard.js
|
|
37
40
|
* 자체는 scope.json 존재 여부만 본다.
|
|
@@ -45,10 +48,10 @@ export function isScopeGuardEnabled(projectDir) {
|
|
|
45
48
|
for (const p of candidates) {
|
|
46
49
|
if (!fs.existsSync(p)) continue;
|
|
47
50
|
const cfg = JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
48
|
-
if (cfg && cfg.scopeGuard && cfg.scopeGuard.enabled ===
|
|
51
|
+
if (cfg && cfg.scopeGuard && cfg.scopeGuard.enabled === true) return true;
|
|
49
52
|
}
|
|
50
53
|
} catch { /* ignore */ }
|
|
51
|
-
return
|
|
54
|
+
return false;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
/**
|
|
@@ -105,6 +105,7 @@ function resolveModel(providerName, config) {
|
|
|
105
105
|
if (providerName === 'gpt-codex') return config.models?.gptCodex || 'gpt-5.3-codex';
|
|
106
106
|
if (providerName === 'gpt') return config.models?.gpt || 'gpt-5.5';
|
|
107
107
|
if (providerName === 'antigravity') return config.models?.antigravity || 'antigravity';
|
|
108
|
+
if (providerName === 'zai') return config.models?.zaiCoding || config.models?.zai || 'glm-5.2';
|
|
108
109
|
if (providerName === 'claude') return 'claude';
|
|
109
110
|
return providerName;
|
|
110
111
|
}
|
|
@@ -271,6 +272,46 @@ function callCodexCli(prompt, sysPrompt, jsonMode, model, timeoutMs) {
|
|
|
271
272
|
});
|
|
272
273
|
}
|
|
273
274
|
|
|
275
|
+
// ZAI (Z.ai / GLM) — OpenAI 호환 HTTP API (CLI 없음)
|
|
276
|
+
const ZAI_BASE_GENERAL = 'https://api.z.ai/api/paas/v4';
|
|
277
|
+
const ZAI_BASE_CODING = 'https://api.z.ai/api/coding/paas/v4';
|
|
278
|
+
|
|
279
|
+
function callZaiApi(prompt, sysPrompt, jsonMode, model, timeoutMs) {
|
|
280
|
+
const vibeConfig = readVibeConfig();
|
|
281
|
+
const codingKey = vibeConfig.credentials?.zai?.codingApiKey || process.env.ZAI_CODING_API_KEY;
|
|
282
|
+
const generalKey = vibeConfig.credentials?.zai?.apiKey || process.env.ZAI_API_KEY;
|
|
283
|
+
const useCoding = Boolean(codingKey);
|
|
284
|
+
const apiKey = codingKey || generalKey;
|
|
285
|
+
if (!apiKey) return Promise.reject(new Error('ZAI API key not set (vibe zai coding-key <key>)'));
|
|
286
|
+
|
|
287
|
+
const base = useCoding ? ZAI_BASE_CODING : ZAI_BASE_GENERAL;
|
|
288
|
+
const system = sysPrompt && sysPrompt !== DEFAULT_SYSTEM_PROMPT ? sysPrompt : DEFAULT_SYSTEM_PROMPT;
|
|
289
|
+
const content = jsonMode ? `${prompt}\n\nRespond with valid JSON only.` : prompt;
|
|
290
|
+
const controller = new AbortController();
|
|
291
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs || CLI_TIMEOUT_MS);
|
|
292
|
+
|
|
293
|
+
return fetch(`${base}/chat/completions`, {
|
|
294
|
+
method: 'POST',
|
|
295
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
|
|
296
|
+
body: JSON.stringify({
|
|
297
|
+
model,
|
|
298
|
+
messages: [{ role: 'system', content: system }, { role: 'user', content }],
|
|
299
|
+
max_tokens: 4096,
|
|
300
|
+
temperature: 0,
|
|
301
|
+
}),
|
|
302
|
+
signal: controller.signal,
|
|
303
|
+
}).then(async (res) => {
|
|
304
|
+
if (!res.ok) {
|
|
305
|
+
const body = await res.text().catch(() => '');
|
|
306
|
+
throw new Error(`zai api failed (code ${res.status}): ${body.slice(0, 500)}`);
|
|
307
|
+
}
|
|
308
|
+
const data = await res.json();
|
|
309
|
+
const text = data?.choices?.[0]?.message?.content?.trim();
|
|
310
|
+
if (!text) throw new Error('zai api returned empty response');
|
|
311
|
+
return text;
|
|
312
|
+
}).finally(() => clearTimeout(timer));
|
|
313
|
+
}
|
|
314
|
+
|
|
274
315
|
function callAntigravityCli(prompt, sysPrompt, jsonMode, timeoutMs) {
|
|
275
316
|
const fullPrompt = buildCliPrompt(prompt, sysPrompt, jsonMode);
|
|
276
317
|
const args = ['-p', fullPrompt];
|
|
@@ -357,6 +398,14 @@ async function callProvider(providerName, prompt, sysPrompt, jsonMode, timeoutMs
|
|
|
357
398
|
return await callAntigravityCli(prompt, sysPrompt, jsonMode, timeoutMs);
|
|
358
399
|
}
|
|
359
400
|
|
|
401
|
+
if (providerName === 'zai') {
|
|
402
|
+
// coding 키가 있으면 coding plan flagship(5.2), 아니면 일반 API 상한(5.1)
|
|
403
|
+
const hasCoding = Boolean(vibeConfig.credentials?.zai?.codingApiKey || process.env.ZAI_CODING_API_KEY);
|
|
404
|
+
const fallback = hasCoding ? 'glm-5.2' : 'glm-5.1';
|
|
405
|
+
const model = vibeConfig.models?.zaiCoding || vibeConfig.models?.zai || process.env.ZAI_MODEL || fallback;
|
|
406
|
+
return await callZaiApi(prompt, sysPrompt, jsonMode, model, timeoutMs);
|
|
407
|
+
}
|
|
408
|
+
|
|
360
409
|
if (providerName === 'claude') {
|
|
361
410
|
return await callClaudeCli(prompt, sysPrompt, jsonMode, timeoutMs);
|
|
362
411
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* 실행 step (모두 비차단, config.hooks.{name}.enabled로 개별 토글):
|
|
10
10
|
* auto-format — 코드 스타일 정규화 (변경 시 finding 반환)
|
|
11
|
-
* code-check —
|
|
11
|
+
* code-check — 하드룰(any/console.log) 탐지 (additionalContext 주입만, 커밋 게이트 미연동)
|
|
12
12
|
* auto-test — 관련 테스트 실행 (debounce 지원)
|
|
13
13
|
* post-edit — console.log 감지
|
|
14
14
|
*
|
|
@@ -69,21 +69,9 @@ if (/^\s*\/vibe\b/i.test(prompt)) {
|
|
|
69
69
|
|
|
70
70
|
// 패턴 → 실행할 스크립트 매핑
|
|
71
71
|
// 각 항목: { pattern, script, args, label }
|
|
72
|
+
// (참고: 매직 키워드 배너 주입(keyword-detector)은 제거됨 — deprecated 별칭
|
|
73
|
+
// 매핑은 CLAUDE.md 의 "Deprecated aliases" 표가 SSOT이며 모델이 직접 해석한다.)
|
|
72
74
|
const DISPATCH_RULES = [
|
|
73
|
-
// 항상 실행 (경량 스크립트)
|
|
74
|
-
{
|
|
75
|
-
pattern: null, // always
|
|
76
|
-
script: 'keyword-detector.js',
|
|
77
|
-
args: [prompt],
|
|
78
|
-
label: 'keyword',
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
// 패턴 매칭이 필요한 스크립트
|
|
82
|
-
{
|
|
83
|
-
pattern: /ultrawork|ulw|울트라워크|ralph|ralplan/i,
|
|
84
|
-
script: null, // keyword-detector가 이미 처리
|
|
85
|
-
label: 'skip',
|
|
86
|
-
},
|
|
87
75
|
// echo 전용 (stdout으로 직접 출력)
|
|
88
76
|
{
|
|
89
77
|
pattern: /e2e.*테스트|e2e.*test|playwright|브라우저.*테스트|browser.*test/i,
|
|
@@ -176,7 +164,7 @@ for (const rule of DISPATCH_RULES) {
|
|
|
176
164
|
// 외부 LLM 호출(llm-orchestrate)은 부모/자식 timeout 을 정합시킨다 (B-2):
|
|
177
165
|
// 자식은 hook 모드(primary 45s, fallback 없음, retry 없음)로 단일 시도 후 스스로
|
|
178
166
|
// 정리하고, 부모는 그보다 약간 긴 50s 로 감싸 hard-kill 을 피한다.
|
|
179
|
-
// 경량
|
|
167
|
+
// 경량 스크립트는 기존 30s 유지.
|
|
180
168
|
const isLlm = rule.script === 'llm-orchestrate.js';
|
|
181
169
|
const execTimeout = isLlm ? 50000 : 30000;
|
|
182
170
|
const childEnv = isLlm
|
|
@@ -207,7 +195,6 @@ await Promise.all(execPromises);
|
|
|
207
195
|
|
|
208
196
|
// Evolution: Gap detection — log unmatched prompts for skill gap analysis
|
|
209
197
|
const matched = DISPATCH_RULES.some(r =>
|
|
210
|
-
r.label !== 'skip' && r.label !== 'keyword' &&
|
|
211
198
|
r.pattern !== null && r.pattern.test(prompt) &&
|
|
212
199
|
(r.script || r.echo)
|
|
213
200
|
);
|
|
@@ -218,12 +205,14 @@ if (!matched) {
|
|
|
218
205
|
const utils = await import('./utils.js');
|
|
219
206
|
const projectDir = process.env.CLAUDE_PROJECT_DIR || '.';
|
|
220
207
|
const configPath = utils.projectVibePath(projectDir, 'config.json');
|
|
221
|
-
|
|
208
|
+
// Evolution 은 동결(opt-in) — evolution.enabled === true 로 명시했을 때만
|
|
209
|
+
// gap 로깅을 수행한다 (기본 OFF, P3-3).
|
|
210
|
+
let gapEnabled = false;
|
|
222
211
|
try {
|
|
223
212
|
const fs = await import('fs');
|
|
224
213
|
if (fs.existsSync(configPath)) {
|
|
225
214
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
226
|
-
gapEnabled = config.evolution?.
|
|
215
|
+
gapEnabled = config.evolution?.enabled === true && config.evolution?.gapDetection !== false;
|
|
227
216
|
}
|
|
228
217
|
} catch { /* ignore */ }
|
|
229
218
|
|
|
@@ -86,16 +86,29 @@ async function main() {
|
|
|
86
86
|
import(`${BASE_URL}time/index.js`),
|
|
87
87
|
]);
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
// 인덱스만 주입 (harness-review-2026-07-01 P1-5):
|
|
90
|
+
// 이전에는 startSession 전체 요약(git log·테스트 상태·관찰·리플렉션·RAG
|
|
91
|
+
// goals/constraints/decisions)을 매 세션 덤프했다. 이제는 1줄 인덱스만
|
|
92
|
+
// 주입하고, 전체 컨텍스트 복원은 명시적 `/vibe.utils --continue` 전용.
|
|
93
|
+
const [time, memories, latestVersion] = await Promise.all([
|
|
91
94
|
timeModule.getCurrentTime({ format: 'human', timezone: 'Asia/Seoul' }),
|
|
92
95
|
memoryModule.listMemories({ limit: 5, projectPath: PROJECT_DIR }),
|
|
93
96
|
getLatestVersionCached(),
|
|
94
97
|
]);
|
|
95
98
|
|
|
96
|
-
console.log(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
console.log(time.content[0].text);
|
|
100
|
+
|
|
101
|
+
// Active feature 1줄 인덱스
|
|
102
|
+
try {
|
|
103
|
+
const LIB_BASE_P = (await import('./utils.js')).getLibBaseUrl();
|
|
104
|
+
const iterMod = await import(`${LIB_BASE_P}IterationTracker.js`);
|
|
105
|
+
const progressLine = iterMod.getProgressSummary(PROJECT_DIR);
|
|
106
|
+
if (progressLine) {
|
|
107
|
+
console.log(`\n📋 ${progressLine} — resume full context: /vibe.utils --continue`);
|
|
108
|
+
}
|
|
109
|
+
} catch { /* progress index is best-effort */ }
|
|
110
|
+
|
|
111
|
+
console.log('\n[Memory Index — bodies on demand via recall_memory]');
|
|
99
112
|
console.log(memories.content[0].text);
|
|
100
113
|
|
|
101
114
|
// Phase 3 — Recipes + anti-patterns 인덱스 (post-task curation)
|
|
@@ -126,16 +139,17 @@ async function main() {
|
|
|
126
139
|
}
|
|
127
140
|
}
|
|
128
141
|
|
|
129
|
-
// Scope sync — 기본
|
|
142
|
+
// Scope sync — 기본 OFF (opt-in: scopeGuard.enabled=true). SSOT: scope-from-spec.js.
|
|
130
143
|
try {
|
|
131
144
|
const { syncScopeFile, isScopeGuardEnabled } = await import('./lib/scope-from-spec.js');
|
|
132
|
-
|
|
145
|
+
const guardOn = isScopeGuardEnabled(PROJECT_DIR);
|
|
146
|
+
if (guardOn) {
|
|
133
147
|
const result = syncScopeFile(PROJECT_DIR);
|
|
134
148
|
if (result.action === 'created' || result.action === 'updated' || result.action === 'removed') {
|
|
135
149
|
console.log(`\n🚧 Scope ${result.action} from active SPECs (${path.relative(PROJECT_DIR, path.join(projectVibeRoot(PROJECT_DIR), 'scope.json'))})`);
|
|
136
150
|
}
|
|
137
151
|
}
|
|
138
|
-
// scope 상태 1줄 알림 —
|
|
152
|
+
// scope 상태 1줄 알림 — 활성 scope.json 이 있을 때만 (opt-out 사용자에게 노이즈 금지)
|
|
139
153
|
const scopeJsonPath = path.join(projectVibeRoot(PROJECT_DIR), 'scope.json');
|
|
140
154
|
if (fs.existsSync(scopeJsonPath)) {
|
|
141
155
|
try {
|
|
@@ -144,7 +158,7 @@ async function main() {
|
|
|
144
158
|
const modeLabel = scopeData.mode || 'warn';
|
|
145
159
|
console.log(`\nscope-guard: ${featureLabel} (${modeLabel})`);
|
|
146
160
|
} catch { /* ignore */ }
|
|
147
|
-
} else {
|
|
161
|
+
} else if (guardOn) {
|
|
148
162
|
console.log('\nscope-guard: no active scope (no active SPEC or no derivable paths) — out-of-scope edits are not monitored');
|
|
149
163
|
}
|
|
150
164
|
} catch { /* scope sync is best-effort */ }
|
|
@@ -198,32 +212,8 @@ async function main() {
|
|
|
198
212
|
storageA.close();
|
|
199
213
|
} catch { /* autonomy not yet initialized, skip */ }
|
|
200
214
|
|
|
201
|
-
// Evolution
|
|
202
|
-
|
|
203
|
-
const LIB_BASE = (await import('./utils.js')).getLibBaseUrl();
|
|
204
|
-
const [memMod, regMod, insMod] = await Promise.all([
|
|
205
|
-
import(`${LIB_BASE}memory/MemoryStorage.js`),
|
|
206
|
-
import(`${LIB_BASE}evolution/GenerationRegistry.js`),
|
|
207
|
-
import(`${LIB_BASE}evolution/InsightStore.js`),
|
|
208
|
-
]);
|
|
209
|
-
const storage = new memMod.MemoryStorage(PROJECT_DIR);
|
|
210
|
-
const registry = new regMod.GenerationRegistry(storage);
|
|
211
|
-
const insightStore = new insMod.InsightStore(storage);
|
|
212
|
-
|
|
213
|
-
const genStats = registry.getStats();
|
|
214
|
-
const active = genStats.byStatus?.active || 0;
|
|
215
|
-
const drafts = genStats.byStatus?.draft || 0;
|
|
216
|
-
const gaps = insightStore.getByType('skill_gap').length;
|
|
217
|
-
|
|
218
|
-
if (active > 0 || drafts > 0 || gaps > 0) {
|
|
219
|
-
const parts = [];
|
|
220
|
-
if (active > 0) parts.push(`${active} active skills`);
|
|
221
|
-
if (drafts > 0) parts.push(`${drafts} pending approval`);
|
|
222
|
-
if (gaps > 0) parts.push(`${gaps} gaps detected`);
|
|
223
|
-
console.log(`\n🧬 Evolution: ${parts.join(', ')}`);
|
|
224
|
-
}
|
|
225
|
-
storage.close();
|
|
226
|
-
} catch { /* evolution not yet initialized, skip */ }
|
|
215
|
+
// Evolution 상태 요약은 제거 — evolution 은 동결(opt-in) 상태이며
|
|
216
|
+
// 사용자는 `vibe evolution` CLI 로 명시적으로 조회한다 (P3-3).
|
|
227
217
|
} catch (e) {
|
|
228
218
|
console.log('[Session] Error:', e.message);
|
|
229
219
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@su-record/vibe",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "AI Coding Framework for Claude Code —
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "AI Coding Framework for Claude Code — 10+ agents, 59 skills, multi-LLM orchestration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -9,11 +9,9 @@
|
|
|
9
9
|
"./tools": "./dist/tools/index.js",
|
|
10
10
|
"./tools/memory": "./dist/tools/memory/index.js",
|
|
11
11
|
"./tools/convention": "./dist/tools/convention/index.js",
|
|
12
|
-
"./tools/semantic": "./dist/tools/semantic/index.js",
|
|
13
12
|
"./tools/ui": "./dist/tools/ui/index.js",
|
|
14
13
|
"./tools/time": "./dist/tools/time/index.js",
|
|
15
14
|
"./tools/interaction": "./dist/tools/interaction/index.js",
|
|
16
|
-
"./orchestrator": "./dist/infra/orchestrator/index.js",
|
|
17
15
|
"./memory": {
|
|
18
16
|
"types": "./dist/infra/lib/memory/index.d.ts",
|
|
19
17
|
"default": "./dist/infra/lib/memory/index.js"
|
|
@@ -27,6 +25,8 @@
|
|
|
27
25
|
"dev": "tsc --watch",
|
|
28
26
|
"gen:skill-docs": "npx tsx scripts/gen-skill-docs.ts",
|
|
29
27
|
"gen:skill-docs:check": "npx tsx scripts/gen-skill-docs.ts --check",
|
|
28
|
+
"sync:agent-models": "npx tsx scripts/sync-agent-models.ts",
|
|
29
|
+
"sync:agent-models:check": "npx tsx scripts/sync-agent-models.ts --check",
|
|
30
30
|
"validate:skill-invocation": "npx tsx scripts/validate-skill-invocation.ts",
|
|
31
31
|
"validate:counts": "npx tsx scripts/validate-counts.ts",
|
|
32
32
|
"test": "vitest run",
|