@su-record/vibe 3.2.13 → 3.2.15
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.md +15 -14
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/dist/cli/setup/ProjectSetup.d.ts +6 -0
- package/dist/cli/setup/ProjectSetup.d.ts.map +1 -1
- package/dist/cli/setup/ProjectSetup.js +19 -13
- package/dist/cli/setup/ProjectSetup.js.map +1 -1
- package/dist/infra/lib/codex-proxy.d.ts +126 -0
- package/dist/infra/lib/codex-proxy.d.ts.map +1 -1
- package/dist/infra/lib/codex-proxy.js +16 -16
- package/dist/infra/lib/codex-proxy.js.map +1 -1
- package/dist/infra/lib/figma/audit.d.ts.map +1 -1
- package/dist/infra/lib/figma/audit.js +0 -2
- package/dist/infra/lib/figma/audit.js.map +1 -1
- package/dist/infra/lib/figma/extract.d.ts +6 -1
- package/dist/infra/lib/figma/extract.d.ts.map +1 -1
- package/dist/infra/lib/figma/extract.js +17 -15
- package/dist/infra/lib/figma/extract.js.map +1 -1
- package/dist/infra/lib/figma/types.d.ts +103 -0
- package/dist/infra/lib/figma/types.d.ts.map +1 -1
- package/dist/infra/lib/figma/types.js +8 -0
- package/dist/infra/lib/figma/types.js.map +1 -1
- package/hooks/hooks.json +1 -1
- package/hooks/scripts/clone-extract.js +16 -1
- package/hooks/scripts/code-check.js +72 -55
- package/hooks/scripts/lib/anchor.js +74 -0
- package/hooks/scripts/lib/console-allow.js +66 -0
- package/hooks/scripts/lib/dispatcher.js +71 -19
- package/hooks/scripts/lib/inbox.js +60 -0
- package/hooks/scripts/loop-ledger.js +24 -1
- package/hooks/scripts/post-edit-dispatcher.js +4 -3
- package/hooks/scripts/post-edit.js +2 -2
- package/package.json +13 -9
- package/skills/vibe/SKILL.md +2 -1
- package/skills/vibe.clone/references/verification-loops.md +6 -3
- package/skills/vibe.loop/SKILL.md +7 -4
- package/skills/vibe.review/SKILL.md +5 -1
- package/skills/vibe.run/references/e2e-and-autofix.md +1 -1
- package/skills/vibe.run/references/process-steps.md +1 -1
- package/vibe/constitution.md +1 -1
- package/vibe/rules/loop-contract.md +40 -3
- package/vibe/rules/quality/checklist.md +3 -3
- package/vibe/templates/constitution-template.md +1 -1
- package/dist/__tests__/agent-model-sync.test.d.ts +0 -2
- package/dist/__tests__/agent-model-sync.test.d.ts.map +0 -1
- package/dist/__tests__/agent-model-sync.test.js +0 -71
- package/dist/__tests__/agent-model-sync.test.js.map +0 -1
- package/dist/__tests__/architecture.test.d.ts +0 -2
- package/dist/__tests__/architecture.test.d.ts.map +0 -1
- package/dist/__tests__/architecture.test.js +0 -207
- package/dist/__tests__/architecture.test.js.map +0 -1
- package/dist/__tests__/skill-namespace.test.d.ts +0 -2
- package/dist/__tests__/skill-namespace.test.d.ts.map +0 -1
- package/dist/__tests__/skill-namespace.test.js +0 -60
- package/dist/__tests__/skill-namespace.test.js.map +0 -1
- package/dist/__tests__/stakes-contract.test.d.ts +0 -2
- package/dist/__tests__/stakes-contract.test.d.ts.map +0 -1
- package/dist/__tests__/stakes-contract.test.js +0 -166
- package/dist/__tests__/stakes-contract.test.js.map +0 -1
- package/dist/__tests__/stuck-semantics.test.d.ts +0 -2
- package/dist/__tests__/stuck-semantics.test.d.ts.map +0 -1
- package/dist/__tests__/stuck-semantics.test.js +0 -109
- package/dist/__tests__/stuck-semantics.test.js.map +0 -1
- package/dist/__tests__/wiring-integrity.test.d.ts +0 -2
- package/dist/__tests__/wiring-integrity.test.d.ts.map +0 -1
- package/dist/__tests__/wiring-integrity.test.js +0 -160
- package/dist/__tests__/wiring-integrity.test.js.map +0 -1
- package/dist/cli/commands/info.test.d.ts +0 -2
- package/dist/cli/commands/info.test.d.ts.map +0 -1
- package/dist/cli/commands/info.test.js +0 -74
- package/dist/cli/commands/info.test.js.map +0 -1
- package/dist/cli/commands/upgrade.test.d.ts +0 -2
- package/dist/cli/commands/upgrade.test.d.ts.map +0 -1
- package/dist/cli/commands/upgrade.test.js +0 -99
- package/dist/cli/commands/upgrade.test.js.map +0 -1
- package/dist/cli/design/design-md-parser.test.d.ts +0 -2
- package/dist/cli/design/design-md-parser.test.d.ts.map +0 -1
- package/dist/cli/design/design-md-parser.test.js +0 -149
- package/dist/cli/design/design-md-parser.test.js.map +0 -1
- package/dist/cli/detect.characterization.test.d.ts +0 -7
- package/dist/cli/detect.characterization.test.d.ts.map +0 -1
- package/dist/cli/detect.characterization.test.js +0 -294
- package/dist/cli/detect.characterization.test.js.map +0 -1
- package/dist/cli/postinstall/fs-utils.test.d.ts +0 -2
- package/dist/cli/postinstall/fs-utils.test.d.ts.map +0 -1
- package/dist/cli/postinstall/fs-utils.test.js +0 -259
- package/dist/cli/postinstall/fs-utils.test.js.map +0 -1
- package/dist/cli/setup/CodexHooks.test.d.ts +0 -2
- package/dist/cli/setup/CodexHooks.test.d.ts.map +0 -1
- package/dist/cli/setup/CodexHooks.test.js +0 -52
- package/dist/cli/setup/CodexHooks.test.js.map +0 -1
- package/dist/cli/setup/GlobalInstaller.test.d.ts +0 -2
- package/dist/cli/setup/GlobalInstaller.test.d.ts.map +0 -1
- package/dist/cli/setup/GlobalInstaller.test.js +0 -16
- package/dist/cli/setup/GlobalInstaller.test.js.map +0 -1
- package/dist/cli/utils/cli-detector.test.d.ts +0 -2
- package/dist/cli/utils/cli-detector.test.d.ts.map +0 -1
- package/dist/cli/utils/cli-detector.test.js +0 -52
- package/dist/cli/utils/cli-detector.test.js.map +0 -1
- package/dist/infra/lib/SkillFrontmatter.test.d.ts +0 -2
- package/dist/infra/lib/SkillFrontmatter.test.d.ts.map +0 -1
- package/dist/infra/lib/SkillFrontmatter.test.js +0 -60
- package/dist/infra/lib/SkillFrontmatter.test.js.map +0 -1
- package/dist/infra/lib/__tests__/AutomationLevel.test.d.ts +0 -2
- package/dist/infra/lib/__tests__/AutomationLevel.test.d.ts.map +0 -1
- package/dist/infra/lib/__tests__/AutomationLevel.test.js +0 -297
- package/dist/infra/lib/__tests__/AutomationLevel.test.js.map +0 -1
- package/dist/infra/lib/__tests__/ComponentRegistry.test.d.ts +0 -2
- package/dist/infra/lib/__tests__/ComponentRegistry.test.d.ts.map +0 -1
- package/dist/infra/lib/__tests__/ComponentRegistry.test.js +0 -99
- package/dist/infra/lib/__tests__/ComponentRegistry.test.js.map +0 -1
- package/dist/infra/lib/__tests__/DecisionTracer.test.d.ts +0 -2
- package/dist/infra/lib/__tests__/DecisionTracer.test.d.ts.map +0 -1
- package/dist/infra/lib/__tests__/DecisionTracer.test.js +0 -274
- package/dist/infra/lib/__tests__/DecisionTracer.test.js.map +0 -1
- package/dist/infra/lib/__tests__/InteractiveCheckpoint.test.d.ts +0 -2
- package/dist/infra/lib/__tests__/InteractiveCheckpoint.test.d.ts.map +0 -1
- package/dist/infra/lib/__tests__/InteractiveCheckpoint.test.js +0 -350
- package/dist/infra/lib/__tests__/InteractiveCheckpoint.test.js.map +0 -1
- package/dist/infra/lib/__tests__/LoopBreaker.test.d.ts +0 -2
- package/dist/infra/lib/__tests__/LoopBreaker.test.d.ts.map +0 -1
- package/dist/infra/lib/__tests__/LoopBreaker.test.js +0 -340
- package/dist/infra/lib/__tests__/LoopBreaker.test.js.map +0 -1
- package/dist/infra/lib/__tests__/VerificationLoop.test.d.ts +0 -2
- package/dist/infra/lib/__tests__/VerificationLoop.test.d.ts.map +0 -1
- package/dist/infra/lib/__tests__/VerificationLoop.test.js +0 -486
- package/dist/infra/lib/__tests__/VerificationLoop.test.js.map +0 -1
- package/dist/infra/lib/config/GlobalConfigManager.test.d.ts +0 -2
- package/dist/infra/lib/config/GlobalConfigManager.test.d.ts.map +0 -1
- package/dist/infra/lib/config/GlobalConfigManager.test.js +0 -9
- package/dist/infra/lib/config/GlobalConfigManager.test.js.map +0 -1
- package/dist/infra/lib/embedding/__tests__/EmbeddingProvider.test.d.ts +0 -2
- package/dist/infra/lib/embedding/__tests__/EmbeddingProvider.test.d.ts.map +0 -1
- package/dist/infra/lib/embedding/__tests__/EmbeddingProvider.test.js +0 -43
- package/dist/infra/lib/embedding/__tests__/EmbeddingProvider.test.js.map +0 -1
- package/dist/infra/lib/embedding/__tests__/VectorStore.test.d.ts +0 -2
- package/dist/infra/lib/embedding/__tests__/VectorStore.test.d.ts.map +0 -1
- package/dist/infra/lib/embedding/__tests__/VectorStore.test.js +0 -94
- package/dist/infra/lib/embedding/__tests__/VectorStore.test.js.map +0 -1
- package/dist/infra/lib/embedding/__tests__/cosine.test.d.ts +0 -2
- package/dist/infra/lib/embedding/__tests__/cosine.test.d.ts.map +0 -1
- package/dist/infra/lib/embedding/__tests__/cosine.test.js +0 -73
- package/dist/infra/lib/embedding/__tests__/cosine.test.js.map +0 -1
- package/dist/infra/lib/evolution/__tests__/deprecation.test.d.ts +0 -2
- package/dist/infra/lib/evolution/__tests__/deprecation.test.d.ts.map +0 -1
- package/dist/infra/lib/evolution/__tests__/deprecation.test.js +0 -251
- package/dist/infra/lib/evolution/__tests__/deprecation.test.js.map +0 -1
- package/dist/infra/lib/evolution/__tests__/eval.test.d.ts +0 -2
- package/dist/infra/lib/evolution/__tests__/eval.test.d.ts.map +0 -1
- package/dist/infra/lib/evolution/__tests__/eval.test.js +0 -603
- package/dist/infra/lib/evolution/__tests__/eval.test.js.map +0 -1
- package/dist/infra/lib/evolution/__tests__/generation.test.d.ts +0 -2
- package/dist/infra/lib/evolution/__tests__/generation.test.d.ts.map +0 -1
- package/dist/infra/lib/evolution/__tests__/generation.test.js +0 -396
- package/dist/infra/lib/evolution/__tests__/generation.test.js.map +0 -1
- package/dist/infra/lib/evolution/__tests__/insight.test.d.ts +0 -2
- package/dist/infra/lib/evolution/__tests__/insight.test.d.ts.map +0 -1
- package/dist/infra/lib/evolution/__tests__/insight.test.js +0 -204
- package/dist/infra/lib/evolution/__tests__/insight.test.js.map +0 -1
- package/dist/infra/lib/evolution/__tests__/integration.test.d.ts +0 -2
- package/dist/infra/lib/evolution/__tests__/integration.test.d.ts.map +0 -1
- package/dist/infra/lib/evolution/__tests__/integration.test.js +0 -206
- package/dist/infra/lib/evolution/__tests__/integration.test.js.map +0 -1
- package/dist/infra/lib/evolution/__tests__/lifecycle.test.d.ts +0 -2
- package/dist/infra/lib/evolution/__tests__/lifecycle.test.d.ts.map +0 -1
- package/dist/infra/lib/evolution/__tests__/lifecycle.test.js +0 -350
- package/dist/infra/lib/evolution/__tests__/lifecycle.test.js.map +0 -1
- package/dist/infra/lib/evolution/__tests__/parity.test.d.ts +0 -2
- package/dist/infra/lib/evolution/__tests__/parity.test.d.ts.map +0 -1
- package/dist/infra/lib/evolution/__tests__/parity.test.js +0 -319
- package/dist/infra/lib/evolution/__tests__/parity.test.js.map +0 -1
- package/dist/infra/lib/llm/auth/AuthProfileManager.test.d.ts +0 -5
- package/dist/infra/lib/llm/auth/AuthProfileManager.test.d.ts.map +0 -1
- package/dist/infra/lib/llm/auth/AuthProfileManager.test.js +0 -168
- package/dist/infra/lib/llm/auth/AuthProfileManager.test.js.map +0 -1
- package/dist/infra/lib/llm/auth/TokenRefresher.test.d.ts +0 -2
- package/dist/infra/lib/llm/auth/TokenRefresher.test.d.ts.map +0 -1
- package/dist/infra/lib/llm/auth/TokenRefresher.test.js +0 -120
- package/dist/infra/lib/llm/auth/TokenRefresher.test.js.map +0 -1
- package/dist/infra/lib/llm/timeout.test.d.ts +0 -2
- package/dist/infra/lib/llm/timeout.test.d.ts.map +0 -1
- package/dist/infra/lib/llm/timeout.test.js +0 -50
- package/dist/infra/lib/llm/timeout.test.js.map +0 -1
- package/dist/infra/lib/llm-availability.test.d.ts +0 -2
- package/dist/infra/lib/llm-availability.test.d.ts.map +0 -1
- package/dist/infra/lib/llm-availability.test.js +0 -37
- package/dist/infra/lib/llm-availability.test.js.map +0 -1
- package/dist/infra/lib/memory/KnowledgeGraph.test.d.ts +0 -2
- package/dist/infra/lib/memory/KnowledgeGraph.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/KnowledgeGraph.test.js +0 -189
- package/dist/infra/lib/memory/KnowledgeGraph.test.js.map +0 -1
- package/dist/infra/lib/memory/MemorySearch.test.d.ts +0 -2
- package/dist/infra/lib/memory/MemorySearch.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/MemorySearch.test.js +0 -149
- package/dist/infra/lib/memory/MemorySearch.test.js.map +0 -1
- package/dist/infra/lib/memory/MemoryStorage.test.d.ts +0 -2
- package/dist/infra/lib/memory/MemoryStorage.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/MemoryStorage.test.js +0 -198
- package/dist/infra/lib/memory/MemoryStorage.test.js.map +0 -1
- package/dist/infra/lib/memory/SessionRAGRetriever.test.d.ts +0 -2
- package/dist/infra/lib/memory/SessionRAGRetriever.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/SessionRAGRetriever.test.js +0 -180
- package/dist/infra/lib/memory/SessionRAGRetriever.test.js.map +0 -1
- package/dist/infra/lib/memory/SessionRAGStore.test.d.ts +0 -2
- package/dist/infra/lib/memory/SessionRAGStore.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/SessionRAGStore.test.js +0 -326
- package/dist/infra/lib/memory/SessionRAGStore.test.js.map +0 -1
- package/dist/infra/lib/memory/__tests__/InMemoryStorage.test.d.ts +0 -2
- package/dist/infra/lib/memory/__tests__/InMemoryStorage.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/__tests__/InMemoryStorage.test.js +0 -114
- package/dist/infra/lib/memory/__tests__/InMemoryStorage.test.js.map +0 -1
- package/dist/infra/lib/memory/__tests__/ReflectionStore.test.d.ts +0 -2
- package/dist/infra/lib/memory/__tests__/ReflectionStore.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/__tests__/ReflectionStore.test.js +0 -196
- package/dist/infra/lib/memory/__tests__/ReflectionStore.test.js.map +0 -1
- package/dist/infra/lib/memory/index.test.d.ts +0 -2
- package/dist/infra/lib/memory/index.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/index.test.js +0 -23
- package/dist/infra/lib/memory/index.test.js.map +0 -1
- package/dist/infra/lib/telemetry/SkillTelemetry.test.d.ts +0 -2
- package/dist/infra/lib/telemetry/SkillTelemetry.test.d.ts.map +0 -1
- package/dist/infra/lib/telemetry/SkillTelemetry.test.js +0 -124
- package/dist/infra/lib/telemetry/SkillTelemetry.test.js.map +0 -1
- package/dist/infra/lib/ui-ux/Bm25Engine.test.d.ts +0 -2
- package/dist/infra/lib/ui-ux/Bm25Engine.test.d.ts.map +0 -1
- package/dist/infra/lib/ui-ux/Bm25Engine.test.js +0 -159
- package/dist/infra/lib/ui-ux/Bm25Engine.test.js.map +0 -1
- package/dist/infra/lib/ui-ux/CsvDataLoader.test.d.ts +0 -2
- package/dist/infra/lib/ui-ux/CsvDataLoader.test.d.ts.map +0 -1
- package/dist/infra/lib/ui-ux/CsvDataLoader.test.js +0 -141
- package/dist/infra/lib/ui-ux/CsvDataLoader.test.js.map +0 -1
- package/dist/infra/lib/ui-ux/DesignSystemGenerator.test.d.ts +0 -2
- package/dist/infra/lib/ui-ux/DesignSystemGenerator.test.d.ts.map +0 -1
- package/dist/infra/lib/ui-ux/DesignSystemGenerator.test.js +0 -370
- package/dist/infra/lib/ui-ux/DesignSystemGenerator.test.js.map +0 -1
- package/dist/infra/lib/ui-ux/SearchService.test.d.ts +0 -2
- package/dist/infra/lib/ui-ux/SearchService.test.d.ts.map +0 -1
- package/dist/infra/lib/ui-ux/SearchService.test.js +0 -215
- package/dist/infra/lib/ui-ux/SearchService.test.js.map +0 -1
- package/dist/test-helpers/index.d.ts +0 -36
- package/dist/test-helpers/index.d.ts.map +0 -1
- package/dist/test-helpers/index.js +0 -85
- package/dist/test-helpers/index.js.map +0 -1
- package/dist/test-helpers/index.test.d.ts +0 -2
- package/dist/test-helpers/index.test.d.ts.map +0 -1
- package/dist/test-helpers/index.test.js +0 -92
- package/dist/test-helpers/index.test.js.map +0 -1
- package/dist/tests/curated-release-notes.test.d.ts +0 -2
- package/dist/tests/curated-release-notes.test.d.ts.map +0 -1
- package/dist/tests/curated-release-notes.test.js +0 -160
- package/dist/tests/curated-release-notes.test.js.map +0 -1
- package/dist/tools/convention/analyzeComplexity.test.d.ts +0 -2
- package/dist/tools/convention/analyzeComplexity.test.d.ts.map +0 -1
- package/dist/tools/convention/analyzeComplexity.test.js +0 -207
- package/dist/tools/convention/analyzeComplexity.test.js.map +0 -1
- package/dist/tools/convention/validateCodeQuality.test.d.ts +0 -2
- package/dist/tools/convention/validateCodeQuality.test.d.ts.map +0 -1
- package/dist/tools/convention/validateCodeQuality.test.js +0 -230
- package/dist/tools/convention/validateCodeQuality.test.js.map +0 -1
- package/dist/tools/loop/validateLoopDefinition.test.d.ts +0 -14
- package/dist/tools/loop/validateLoopDefinition.test.d.ts.map +0 -1
- package/dist/tools/loop/validateLoopDefinition.test.js +0 -229
- package/dist/tools/loop/validateLoopDefinition.test.js.map +0 -1
- package/dist/tools/spec/executionPacket.test.d.ts +0 -2
- package/dist/tools/spec/executionPacket.test.d.ts.map +0 -1
- package/dist/tools/spec/executionPacket.test.js +0 -374
- package/dist/tools/spec/executionPacket.test.js.map +0 -1
- package/dist/tools/spec/prdParser.test.d.ts +0 -6
- package/dist/tools/spec/prdParser.test.d.ts.map +0 -1
- package/dist/tools/spec/prdParser.test.js +0 -332
- package/dist/tools/spec/prdParser.test.js.map +0 -1
- package/dist/tools/spec/specGenerator.test.d.ts +0 -2
- package/dist/tools/spec/specGenerator.test.d.ts.map +0 -1
- package/dist/tools/spec/specGenerator.test.js +0 -130
- package/dist/tools/spec/specGenerator.test.js.map +0 -1
- package/dist/tools/spec/traceabilityMatrix.path-resolution.test.d.ts +0 -10
- package/dist/tools/spec/traceabilityMatrix.path-resolution.test.d.ts.map +0 -1
- package/dist/tools/spec/traceabilityMatrix.path-resolution.test.js +0 -120
- package/dist/tools/spec/traceabilityMatrix.path-resolution.test.js.map +0 -1
- package/dist/tools/spec/traceabilityMatrix.test.d.ts +0 -6
- package/dist/tools/spec/traceabilityMatrix.test.d.ts.map +0 -1
- package/dist/tools/spec/traceabilityMatrix.test.js +0 -359
- package/dist/tools/spec/traceabilityMatrix.test.js.map +0 -1
- package/hooks/scripts/__tests__/.vibe/command-log.txt +0 -3
- package/hooks/scripts/__tests__/auto-test-debounce.test.js +0 -145
- package/hooks/scripts/__tests__/clone-behaviors.test.js +0 -152
- package/hooks/scripts/__tests__/clone-merge-responsive.test.js +0 -106
- package/hooks/scripts/__tests__/code-check-detectors.test.js +0 -213
- package/hooks/scripts/__tests__/code-check-memory-write.test.js +0 -81
- package/hooks/scripts/__tests__/codex-hook-adapter.test.js +0 -64
- package/hooks/scripts/__tests__/curation-index.test.js +0 -157
- package/hooks/scripts/__tests__/dispatcher-inprocess.test.js +0 -99
- package/hooks/scripts/__tests__/figma-extract.test.js +0 -338
- package/hooks/scripts/__tests__/hook-payload.test.js +0 -29
- package/hooks/scripts/__tests__/llm-orchestrate-antigravity.test.js +0 -66
- package/hooks/scripts/__tests__/loop-ledger.test.js +0 -321
- package/hooks/scripts/__tests__/post-edit-dispatcher.test.js +0 -139
- package/hooks/scripts/__tests__/pre-tool-guard.test.js +0 -523
- package/hooks/scripts/__tests__/recipe-extractor.test.js +0 -244
- package/hooks/scripts/__tests__/run-ledger-verify-required.test.js +0 -151
- package/hooks/scripts/__tests__/run-ledger.test.js +0 -481
- package/hooks/scripts/__tests__/scope-from-spec.test.js +0 -215
- package/hooks/scripts/__tests__/sentinel-guard.test.js +0 -263
- package/hooks/scripts/__tests__/step-counter.test.js +0 -459
- package/hooks/scripts/__tests__/utils-npm-root.test.js +0 -99
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* clone active-interaction-sweep helpers
|
|
3
|
-
* - diffStyles: before/after computed-style differ (clone-extract.js)
|
|
4
|
-
* - sectionHasNode / behaviorsBlock: behavior→section attachment (clone-spec.js)
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect } from 'vitest';
|
|
8
|
-
import { collectSubUrls, diffStyles } from '../clone-extract.js';
|
|
9
|
-
import { sectionHasNode, behaviorsBlock } from '../clone-spec.js';
|
|
10
|
-
|
|
11
|
-
describe('diffStyles', () => {
|
|
12
|
-
it('reports only changed props with from/to', () => {
|
|
13
|
-
const a = { 'background-color': 'rgba(0, 0, 0, 0)', height: '80px' };
|
|
14
|
-
const b = { 'background-color': 'rgb(255, 255, 255)', height: '80px' };
|
|
15
|
-
expect(diffStyles(a, b)).toEqual({
|
|
16
|
-
'background-color': { from: 'rgba(0, 0, 0, 0)', to: 'rgb(255, 255, 255)' },
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('captures props that appear or disappear', () => {
|
|
21
|
-
const d = diffStyles({}, { 'box-shadow': '0 4px 20px rgba(0,0,0,0.1)' });
|
|
22
|
-
expect(d['box-shadow']).toEqual({ from: null, to: '0 4px 20px rgba(0,0,0,0.1)' });
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('returns empty when nothing changed or a snapshot is missing', () => {
|
|
26
|
-
expect(diffStyles({ color: 'red' }, { color: 'red' })).toEqual({});
|
|
27
|
-
expect(diffStyles(null, { color: 'red' })).toEqual({});
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe('collectSubUrls', () => {
|
|
32
|
-
it('keeps same-origin locale menu links and drops footer/external links', () => {
|
|
33
|
-
const urls = collectSubUrls('https://www.lgdisplay.com/kor', [
|
|
34
|
-
{ href: '/kor/company/info/outline/greeting', text: '기업개요' },
|
|
35
|
-
{ href: '/kor/product/tv/oled', text: 'TV' },
|
|
36
|
-
{ href: '/eng', text: 'ENG' },
|
|
37
|
-
{ href: '/kor/privacy', text: '개인정보처리방침' },
|
|
38
|
-
{ href: 'https://news.lgdisplay.com/news', text: 'Newsroom' },
|
|
39
|
-
{ href: 'https://www.youtube.com/@lgdisplay', text: 'You Tube' },
|
|
40
|
-
{ href: '#top', text: 'TOP' },
|
|
41
|
-
]);
|
|
42
|
-
|
|
43
|
-
expect(urls).toEqual([
|
|
44
|
-
'https://www.lgdisplay.com/kor',
|
|
45
|
-
'https://www.lgdisplay.com/kor/company/info/outline/greeting',
|
|
46
|
-
'https://www.lgdisplay.com/kor/product/tv/oled',
|
|
47
|
-
]);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('deduplicates links after dropping query strings and hashes', () => {
|
|
51
|
-
const urls = collectSubUrls('https://example.com/kor', [
|
|
52
|
-
{ href: '/kor/company?utm_source=x', text: 'Company' },
|
|
53
|
-
{ href: '/kor/company#ceo', text: 'Company' },
|
|
54
|
-
]);
|
|
55
|
-
|
|
56
|
-
expect(urls).toEqual([
|
|
57
|
-
'https://example.com/kor',
|
|
58
|
-
'https://example.com/kor/company',
|
|
59
|
-
]);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
describe('behavior → section attachment', () => {
|
|
64
|
-
const section = {
|
|
65
|
-
name: 'Header',
|
|
66
|
-
children: [{ tag: 'header', classes: 'site-nav top', children: [{ tag: 'button', classes: 'tab', children: [] }] }],
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
it('matches a node by tag and class', () => {
|
|
70
|
-
expect(sectionHasNode(section, 'header', 'site-nav')).toBe(true);
|
|
71
|
-
expect(sectionHasNode(section, 'header', 'missing')).toBe(false);
|
|
72
|
-
expect(sectionHasNode(section, 'button')).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('attaches a scroll behavior to the matching section only', () => {
|
|
76
|
-
const behaviors = {
|
|
77
|
-
scroll: [{ label: 'header.site-nav', tag: 'header', cls: 'site-nav', triggerScrollY: 700, changed: { height: { from: '80px', to: '56px' } } }],
|
|
78
|
-
interactive: [{ kind: 'tab-group', count: 3, tabLabels: ['A', 'B', 'C'], contentSwapsOnClick: true }],
|
|
79
|
-
};
|
|
80
|
-
const block = behaviorsBlock(section, behaviors);
|
|
81
|
-
expect(block).toContain('Scroll-triggered');
|
|
82
|
-
expect(block).toContain('height');
|
|
83
|
-
expect(block).toContain('content SWAPS on click');
|
|
84
|
-
|
|
85
|
-
const footer = { name: 'Footer', children: [{ tag: 'p', classes: '', children: [] }] };
|
|
86
|
-
expect(behaviorsBlock(footer, behaviors)).toBeNull();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('renders hover / in-view / time-driven / scroll-lib behavior kinds', () => {
|
|
90
|
-
const behaviors = {
|
|
91
|
-
scroll: [],
|
|
92
|
-
interactive: [],
|
|
93
|
-
hover: [{ label: 'a.cta', tag: 'a', cls: 'cta', transition: 'all 0.3s ease 0s', changed: { 'background-color': { from: 'rgb(0, 0, 0)', to: 'rgb(255, 0, 0)' } } }],
|
|
94
|
-
inview: [{ label: 'div.fade', tag: 'div', cls: 'fade', triggerY: 1400, changed: { opacity: { from: '0', to: '1' } } }],
|
|
95
|
-
timeDriven: [{ label: 'div.slide', tag: 'div', cls: 'slide', mutations: 12, kinds: ['class'] }],
|
|
96
|
-
scrollLib: { name: 'lenis', evidence: 'html.lenis' },
|
|
97
|
-
};
|
|
98
|
-
const hero = {
|
|
99
|
-
name: 'Hero',
|
|
100
|
-
children: [{ tag: 'div', classes: 'fade slide', children: [{ tag: 'a', classes: 'cta', children: [] }] }],
|
|
101
|
-
};
|
|
102
|
-
const block = behaviorsBlock(hero, behaviors);
|
|
103
|
-
expect(block).toContain('**Hover** on `a.cta`');
|
|
104
|
-
expect(block).toContain('transition: `all 0.3s ease 0s`');
|
|
105
|
-
expect(block).toContain('**In-view entrance** on `div.fade` (enters at ~1400px)');
|
|
106
|
-
expect(block).toContain('opacity: `0` → `1`');
|
|
107
|
-
expect(block).toContain('**Time-driven** `div.slide` — 12 mutations/3s');
|
|
108
|
-
expect(block).toContain('lenis');
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('renders a page-level scroll-lib note even when no section-scoped behavior matches', () => {
|
|
112
|
-
const onlyLib = { scroll: [], interactive: [], scrollLib: { name: 'lenis', evidence: 'window.Lenis' } };
|
|
113
|
-
const footer = { name: 'Footer', children: [{ tag: 'p', classes: '', children: [] }] };
|
|
114
|
-
expect(behaviorsBlock(footer, onlyLib)).toContain('lenis');
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('suppresses the default transition value on hover entries', () => {
|
|
118
|
-
const behaviors = {
|
|
119
|
-
hover: [{ label: 'button.x', tag: 'button', cls: 'x', transition: 'all 0s ease 0s', changed: { color: { from: 'rgb(0, 0, 0)', to: 'rgb(9, 9, 9)' } } }],
|
|
120
|
-
};
|
|
121
|
-
const sec = { name: 'S', children: [{ tag: 'button', classes: 'x', children: [] }] };
|
|
122
|
-
expect(behaviorsBlock(sec, behaviors)).not.toContain('transition:');
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
describe('clone-spec CLI --real-content', () => {
|
|
127
|
-
const runCli = async (extraArgs) => {
|
|
128
|
-
const [fs, os, path, url, cp] = await Promise.all([
|
|
129
|
-
import('fs'), import('os'), import('path'), import('url'), import('child_process'),
|
|
130
|
-
]);
|
|
131
|
-
const tmp = fs.default.mkdtempSync(path.default.join(os.default.tmpdir(), 'clone-spec-'));
|
|
132
|
-
const sections = path.default.join(tmp, 'sections.json');
|
|
133
|
-
fs.default.writeFileSync(sections, JSON.stringify({
|
|
134
|
-
meta: { feature: 'f', bp: 'mo', url: 'https://x.test' },
|
|
135
|
-
sections: [{ name: 'Hero', nodeRef: '0', tag: 'section', css: {}, children: [] }],
|
|
136
|
-
}));
|
|
137
|
-
const script = url.default.fileURLToPath(new URL('../clone-spec.js', import.meta.url));
|
|
138
|
-
cp.default.execFileSync('node', [script, sections, `--out=${tmp}/specs`, ...extraArgs]);
|
|
139
|
-
return fs.default.readFileSync(path.default.join(tmp, 'specs', 'Hero.spec.md'), 'utf8');
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
it('defaults to placeholder replacement wording', async () => {
|
|
143
|
-
const spec = await runCli([]);
|
|
144
|
-
expect(spec).toContain('(replace copyrighted copy with placeholders)');
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('switches to verbatim wording with --real-content', async () => {
|
|
148
|
-
const spec = await runCli(['--real-content']);
|
|
149
|
-
expect(spec).toContain('(verbatim — user confirmed rights to reuse this copy)');
|
|
150
|
-
expect(spec).not.toContain('(replace copyrighted copy with placeholders)');
|
|
151
|
-
});
|
|
152
|
-
});
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* clone-merge-responsive — MO/PC SCSS → mobile-first 병합
|
|
3
|
-
* - mergeRules/emitMerged: 순수 병합 코어
|
|
4
|
-
* - CLI: fixture 디렉토리 → sections/tokens/index/class-plan 병합 산출
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect } from 'vitest';
|
|
8
|
-
import fs from 'fs';
|
|
9
|
-
import os from 'os';
|
|
10
|
-
import path from 'path';
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
import { execFileSync } from 'child_process';
|
|
13
|
-
import { mergeRules, emitMerged } from '../clone-merge-responsive.js';
|
|
14
|
-
import { parseScss } from '../clone-validate.js';
|
|
15
|
-
|
|
16
|
-
const SCRIPT = fileURLToPath(new URL('../clone-merge-responsive.js', import.meta.url));
|
|
17
|
-
|
|
18
|
-
const MO = `.f__hero { display: flex; flex-direction: column; padding-top: 24px; }
|
|
19
|
-
.f__hero__title { font-size: 28px; color: rgb(0, 0, 0); }`;
|
|
20
|
-
|
|
21
|
-
const PC = `.f__hero { display: flex; flex-direction: row; padding-top: 64px; }
|
|
22
|
-
.f__hero__title { font-size: 48px; color: rgb(0, 0, 0); }
|
|
23
|
-
.f__hero__side { width: 320px; }`;
|
|
24
|
-
|
|
25
|
-
describe('mergeRules', () => {
|
|
26
|
-
const { base, media, stats } = mergeRules(parseScss(MO), parseScss(PC));
|
|
27
|
-
|
|
28
|
-
it('keeps MO declarations as the mobile-first base', () => {
|
|
29
|
-
expect(base.get('.f__hero')).toEqual({
|
|
30
|
-
display: 'flex', 'flex-direction': 'column', 'padding-top': '24px',
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('puts only differing PC declarations into the media bucket', () => {
|
|
35
|
-
expect(media.get('.f__hero')).toEqual({ 'flex-direction': 'row', 'padding-top': '64px' });
|
|
36
|
-
// color는 양쪽 동일 → media에서 제외
|
|
37
|
-
expect(media.get('.f__hero__title')).toEqual({ 'font-size': '48px' });
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('moves PC-only selectors wholly into the media bucket', () => {
|
|
41
|
-
expect(base.has('.f__hero__side')).toBe(false);
|
|
42
|
-
expect(media.get('.f__hero__side')).toEqual({ width: '320px' });
|
|
43
|
-
expect(stats.pcOnly).toBe(1);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe('emitMerged', () => {
|
|
48
|
-
it('emits base + a single min-width block without duplicating shared declarations', () => {
|
|
49
|
-
const { scss } = emitMerged(parseScss(MO), parseScss(PC), 1024, '// merged');
|
|
50
|
-
expect(scss).toContain('@media (min-width: 1024px)');
|
|
51
|
-
expect(scss.match(/@media/g)).toHaveLength(1);
|
|
52
|
-
// 양 BP 동일 선언은 base에 1번만 등장
|
|
53
|
-
expect(scss.match(/display: flex;/g)).toHaveLength(1);
|
|
54
|
-
expect(scss.match(/color: rgb\(0, 0, 0\);/g)).toHaveLength(1);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('omits the media block when PC has no differences', () => {
|
|
58
|
-
const { scss } = emitMerged(parseScss(MO), parseScss(MO), 1024, '// same');
|
|
59
|
-
expect(scss).not.toContain('@media');
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
describe('CLI file-level merge', () => {
|
|
64
|
-
const write = (dir, rel, content) => {
|
|
65
|
-
const f = path.join(dir, rel);
|
|
66
|
-
fs.mkdirSync(path.dirname(f), { recursive: true });
|
|
67
|
-
fs.writeFileSync(f, content);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
it('merges section dirs and writes index/tokens/class-plan', () => {
|
|
71
|
-
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'clone-merge-'));
|
|
72
|
-
const mo = path.join(tmp, 'mo');
|
|
73
|
-
const pc = path.join(tmp, 'pc');
|
|
74
|
-
const out = path.join(tmp, 'merged');
|
|
75
|
-
write(mo, 'sections/_hero.scss', MO);
|
|
76
|
-
write(pc, 'sections/_hero.scss', PC);
|
|
77
|
-
write(pc, 'sections/_footer.scss', '.f__footer { height: 200px; }'); // PC 전용 섹션
|
|
78
|
-
write(mo, '_tokens.scss', ':root { --c-1: #000; --sp-1: 16px; }');
|
|
79
|
-
write(pc, '_tokens.scss', ':root { --c-1: #000; --sp-1: 32px; }');
|
|
80
|
-
write(mo, 'class-plan.json', JSON.stringify({ 0: 'f__hero' }));
|
|
81
|
-
write(pc, 'class-plan.json', JSON.stringify({ 0: 'f__hero', 0.9: 'f__footer' }));
|
|
82
|
-
|
|
83
|
-
execFileSync('node', [SCRIPT, `--mo=${mo}`, `--pc=${pc}`, `--out=${out}`, '--breakpoint=1024']);
|
|
84
|
-
|
|
85
|
-
const hero = fs.readFileSync(path.join(out, 'sections/_hero.scss'), 'utf8');
|
|
86
|
-
expect(hero).toContain('flex-direction: column');
|
|
87
|
-
expect(hero).toContain('@media (min-width: 1024px)');
|
|
88
|
-
|
|
89
|
-
// PC 전용 섹션 파일은 통째로 media 블록 안
|
|
90
|
-
const footer = fs.readFileSync(path.join(out, 'sections/_footer.scss'), 'utf8');
|
|
91
|
-
expect(footer).toContain('@media (min-width: 1024px)');
|
|
92
|
-
expect(footer).toContain('height: 200px');
|
|
93
|
-
|
|
94
|
-
const tokens = fs.readFileSync(path.join(out, '_tokens.scss'), 'utf8');
|
|
95
|
-
expect(tokens).toContain('--sp-1: 16px'); // base = MO
|
|
96
|
-
expect(tokens).toContain('--sp-1: 32px'); // PC diff → media
|
|
97
|
-
|
|
98
|
-
const index = fs.readFileSync(path.join(out, 'index.scss'), 'utf8');
|
|
99
|
-
expect(index).toContain(`@use './sections/hero'`);
|
|
100
|
-
expect(index).toContain(`@use './sections/footer'`);
|
|
101
|
-
|
|
102
|
-
const plan = JSON.parse(fs.readFileSync(path.join(out, 'class-plan.json'), 'utf8'));
|
|
103
|
-
expect(plan['0']).toBe('f__hero');
|
|
104
|
-
expect(plan['0.9']).toBe('f__footer');
|
|
105
|
-
});
|
|
106
|
-
});
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* code-check.js 정밀 탐지기 테스트
|
|
3
|
-
*
|
|
4
|
-
* 검증 대상:
|
|
5
|
-
* - any 탐지: 단어 경계 기반 (false positive: 'company', 'anything' 등)
|
|
6
|
-
* - any 탐지: `: any`, `as any`, `@ts-ignore`, `<any>` (true positive)
|
|
7
|
-
* - console.log 정책: 경로별 허용/차단
|
|
8
|
-
* - run-ledger verifyRequired round-trip + pass 시 clear
|
|
9
|
-
* - auto-commit verifyRequired skip
|
|
10
|
-
*/
|
|
11
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
12
|
-
import { fileURLToPath } from 'url';
|
|
13
|
-
import path from 'path';
|
|
14
|
-
import fs from 'fs';
|
|
15
|
-
import os from 'os';
|
|
16
|
-
|
|
17
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
|
|
19
|
-
// ─── any 탐지 정규식 (code-check.js와 동일) ──────────────────────────────
|
|
20
|
-
const P1_DETECTORS = [
|
|
21
|
-
/:\s*any\b/,
|
|
22
|
-
/\bas\s+any\b/,
|
|
23
|
-
/<any[\s,>]/,
|
|
24
|
-
/@ts-ignore\b/,
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
function detectsAny(line) {
|
|
28
|
-
return P1_DETECTORS.some(re => re.test(line));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
describe('any 탐지: true positive', () => {
|
|
32
|
-
it(': any — 타입 어노테이션', () => {
|
|
33
|
-
expect(detectsAny('function foo(x: any): void {}')).toBe(true);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it(': any — 공백 포함', () => {
|
|
37
|
-
expect(detectsAny('const x: any = 1;')).toBe(true);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('as any — 타입 캐스트', () => {
|
|
41
|
-
expect(detectsAny('const y = x as any;')).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('<any> — 제네릭', () => {
|
|
45
|
-
expect(detectsAny('const z = foo<any>();')).toBe(true);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('<any, — 제네릭 다중 파라미터', () => {
|
|
49
|
-
expect(detectsAny('const m = new Map<any, string>();')).toBe(true);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('@ts-ignore — 라인 직전', () => {
|
|
53
|
-
expect(detectsAny('// @ts-ignore')).toBe(true);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('@ts-ignore — 인라인', () => {
|
|
57
|
-
expect(detectsAny(' @ts-ignore ')).toBe(true);
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe('any 탐지: false negative (탐지 안 됨)', () => {
|
|
62
|
-
it('"company" 단어 포함 — 탐지 안 됨', () => {
|
|
63
|
-
expect(detectsAny('const company = "Acme";')).toBe(false);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('"anything" 식별자 — 탐지 안 됨', () => {
|
|
67
|
-
expect(detectsAny('if (anything) return;')).toBe(false);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('"manyThings" — 탐지 안 됨', () => {
|
|
71
|
-
expect(detectsAny('const manyThings = [];')).toBe(false);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('"fantasy" — 탐지 안 됨', () => {
|
|
75
|
-
expect(detectsAny('const fantasy = "world";')).toBe(false);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('문자열 리터럴 "any" — 정규식은 문자열 내부도 매칭할 수 있음 (알려진 한계, 문서화)', () => {
|
|
79
|
-
// NOTE: 현재 구현은 문자열 내 "any"를 완전히 제외하지 않음.
|
|
80
|
-
// 실제 코드에서 ": any" 패턴이 문자열로 존재하는 경우는 극히 드물어 허용.
|
|
81
|
-
// 이 테스트는 동작을 문서화하는 것이 목적.
|
|
82
|
-
const result = detectsAny('const msg = "type: any is bad";');
|
|
83
|
-
// 문자열 내 ": any" 도 탐지될 수 있음 — false positive지만 드문 케이스
|
|
84
|
-
expect(typeof result).toBe('boolean');
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// ─── console.log 경로 정책 ────────────────────────────────────────────────
|
|
89
|
-
describe('console.log 허용 경로 정책', () => {
|
|
90
|
-
// 허용 glob 패턴 (code-check.js 기본값과 동일)
|
|
91
|
-
const DEFAULT_ALLOW_GLOBS = [
|
|
92
|
-
'hooks/scripts/**',
|
|
93
|
-
'scripts/**',
|
|
94
|
-
'**/cli/**',
|
|
95
|
-
'**/*.test.*',
|
|
96
|
-
'**/*.spec.*',
|
|
97
|
-
'**/__tests__/**',
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
function globToRegExp(glob) {
|
|
101
|
-
const normalized = glob.replace(/\\/g, '/');
|
|
102
|
-
let out = '';
|
|
103
|
-
for (let i = 0; i < normalized.length; i++) {
|
|
104
|
-
const c = normalized[i];
|
|
105
|
-
if (c === '*') {
|
|
106
|
-
if (normalized[i + 1] === '*') {
|
|
107
|
-
out += '.*';
|
|
108
|
-
i++;
|
|
109
|
-
if (normalized[i + 1] === '/') i++;
|
|
110
|
-
} else {
|
|
111
|
-
out += '[^/]*';
|
|
112
|
-
}
|
|
113
|
-
} else if (c === '?') {
|
|
114
|
-
out += '[^/]';
|
|
115
|
-
} else if ('.+^$()|{}[]\\'.includes(c)) {
|
|
116
|
-
out += '\\' + c;
|
|
117
|
-
} else {
|
|
118
|
-
out += c;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return new RegExp('^' + out + '$');
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function isConsoleAllowed(relPath) {
|
|
125
|
-
return DEFAULT_ALLOW_GLOBS.some(g => globToRegExp(g).test(relPath));
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
it('hooks/scripts/auto-test.js → 허용', () => {
|
|
129
|
-
expect(isConsoleAllowed('hooks/scripts/auto-test.js')).toBe(true);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('hooks/scripts/lib/run-ledger.js → 허용', () => {
|
|
133
|
-
expect(isConsoleAllowed('hooks/scripts/lib/run-ledger.js')).toBe(true);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('src/cli/index.ts → 허용 (**/cli/**)', () => {
|
|
137
|
-
expect(isConsoleAllowed('src/cli/index.ts')).toBe(true);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('src/tools/foo.test.ts → 허용 (**/*.test.*)', () => {
|
|
141
|
-
expect(isConsoleAllowed('src/tools/foo.test.ts')).toBe(true);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('src/__tests__/bar.ts → 허용 (**/__tests__/**)', () => {
|
|
145
|
-
expect(isConsoleAllowed('src/__tests__/bar.ts')).toBe(true);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('src/tools/convention/validateCodeQuality.ts → 차단', () => {
|
|
149
|
-
expect(isConsoleAllowed('src/tools/convention/validateCodeQuality.ts')).toBe(false);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('src/infra/lib/MemoryManager.ts → 차단', () => {
|
|
153
|
-
expect(isConsoleAllowed('src/infra/lib/MemoryManager.ts')).toBe(false);
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// ─── 확장자 게이트 (문서 안의 코드 스니펫 오탐 방지) ──────────────────────
|
|
158
|
-
/**
|
|
159
|
-
* SKILL.md 등 문서에 인용된 `console.log(` 는 커밋되면 안 되는 디버그 코드가
|
|
160
|
-
* 아니라 예시다. 확장자 게이트가 없으면 문서를 편집할 때마다 고칠 수도 없는
|
|
161
|
-
* P1 이 주입되고, 반복되는 오탐이 게이트 신뢰도를 깎는다.
|
|
162
|
-
*
|
|
163
|
-
* 재구현이 아니라 실제 code-check.js 의 run() 을 통과시켜 검증한다.
|
|
164
|
-
*/
|
|
165
|
-
describe('console.log 확장자 게이트', () => {
|
|
166
|
-
let tempDir;
|
|
167
|
-
let runCodeCheck;
|
|
168
|
-
|
|
169
|
-
beforeEach(async () => {
|
|
170
|
-
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vibe-codecheck-ext-'));
|
|
171
|
-
({ run: runCodeCheck } = await import('../code-check.js'));
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
afterEach(() => {
|
|
175
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
/** 허용 glob 에 걸리지 않는 경로를 쓰기 위해 src/feature/ 아래에 만든다 */
|
|
179
|
-
function writeFixture(name, body) {
|
|
180
|
-
const dir = path.join(tempDir, 'src', 'feature');
|
|
181
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
182
|
-
const full = path.join(dir, name);
|
|
183
|
-
fs.writeFileSync(full, body);
|
|
184
|
-
return full;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
it('마크다운 안의 console.log 는 findings 를 만들지 않는다', async () => {
|
|
188
|
-
const filePath = writeFixture(
|
|
189
|
-
'guide.md',
|
|
190
|
-
['# Guide', '', '```bash', 'node -e "console.log(1)"', '```', ''].join('\n'),
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
const { findings } = await runCodeCheck({ filePath });
|
|
194
|
-
|
|
195
|
-
expect(findings.filter(f => /console\.log/.test(f))).toEqual([]);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it('JSON 안의 console.log 문자열도 findings 를 만들지 않는다', async () => {
|
|
199
|
-
const filePath = writeFixture('fixture.json', '{ "cmd": "console.log(1)" }\n');
|
|
200
|
-
|
|
201
|
-
const { findings } = await runCodeCheck({ filePath });
|
|
202
|
-
|
|
203
|
-
expect(findings.filter(f => /console\.log/.test(f))).toEqual([]);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it('코드 확장자에서는 여전히 탐지한다 (게이트가 탐지 자체를 죽이지 않음)', async () => {
|
|
207
|
-
const filePath = writeFixture('service.ts', 'export const f = () => { console.log(1); };\n');
|
|
208
|
-
|
|
209
|
-
const { findings } = await runCodeCheck({ filePath });
|
|
210
|
-
|
|
211
|
-
expect(findings.some(f => /console\.log/.test(f))).toBe(true);
|
|
212
|
-
});
|
|
213
|
-
});
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* code-check.js — C1 회귀 테스트: SQLite addObservation 조건화
|
|
3
|
-
*
|
|
4
|
-
* 검증 대상:
|
|
5
|
-
* - 무위반(clean) Edit → memories.db 자체가 생성되지 않음 (SQLite write 없음)
|
|
6
|
-
* - 위반(violation) Edit → memories.db 생성 (addObservation 기록)
|
|
7
|
-
* - 코드 확장자가 아닌 파일 → 동적 import 전 조기 반환 (write 없음)
|
|
8
|
-
*
|
|
9
|
-
* dist 빌드 필요 (BASE_URL = dist/tools) — `npm run build` 이후 실행.
|
|
10
|
-
* 패턴: 훅 스크립트 공통의 spawnSync 방식 (post-edit-dispatcher.test.js 참고).
|
|
11
|
-
*/
|
|
12
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
13
|
-
import { spawnSync } from 'child_process';
|
|
14
|
-
import fs from 'fs';
|
|
15
|
-
import os from 'os';
|
|
16
|
-
import path from 'path';
|
|
17
|
-
import { fileURLToPath } from 'url';
|
|
18
|
-
|
|
19
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
20
|
-
const SCRIPT = path.resolve(__dirname, '..', 'code-check.js');
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 격리된 임시 프로젝트에서 code-check 를 1회 실행.
|
|
24
|
-
* stdin 으로 PostToolUse payload 전달.
|
|
25
|
-
*/
|
|
26
|
-
function runCodeCheck({ filePath, projectDir }) {
|
|
27
|
-
const payload = { tool_name: 'Edit', tool_input: { file_path: filePath } };
|
|
28
|
-
return spawnSync('node', [SCRIPT], {
|
|
29
|
-
input: JSON.stringify(payload),
|
|
30
|
-
encoding: 'utf-8',
|
|
31
|
-
timeout: 15000,
|
|
32
|
-
env: { ...process.env, CLAUDE_PROJECT_DIR: projectDir },
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function memoriesDbPath(projectDir) {
|
|
37
|
-
return path.join(projectDir, '.vibe', 'memories', 'memories.db');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
describe('code-check.js: 위반 시에만 SQLite 기록 (C1)', () => {
|
|
41
|
-
let projectDir;
|
|
42
|
-
|
|
43
|
-
beforeEach(() => {
|
|
44
|
-
projectDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vibe-code-check-'));
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
afterEach(() => {
|
|
48
|
-
try { fs.rmSync(projectDir, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('무위반 TS 파일 편집 → memories.db 미생성 (SQLite write 없음)', () => {
|
|
52
|
-
const filePath = path.join(projectDir, 'clean.ts');
|
|
53
|
-
fs.writeFileSync(filePath, 'export function add(a: number, b: number): number {\n return a + b;\n}\n');
|
|
54
|
-
|
|
55
|
-
const result = runCodeCheck({ filePath, projectDir });
|
|
56
|
-
|
|
57
|
-
expect(result.status).toBe(0);
|
|
58
|
-
expect(fs.existsSync(memoriesDbPath(projectDir))).toBe(false);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('any 타입 위반이 있는 TS 파일 편집 → memories.db 생성 (SQLite write 발생)', () => {
|
|
62
|
-
const filePath = path.join(projectDir, 'dirty.ts');
|
|
63
|
-
fs.writeFileSync(filePath, 'export function foo(x: any): any {\n return x;\n}\n');
|
|
64
|
-
|
|
65
|
-
const result = runCodeCheck({ filePath, projectDir });
|
|
66
|
-
|
|
67
|
-
expect(result.status).toBe(0);
|
|
68
|
-
expect(result.stdout).toContain('any-type');
|
|
69
|
-
expect(fs.existsSync(memoriesDbPath(projectDir))).toBe(true);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('코드 확장자가 아닌 파일(.md) → 동적 import 조기 반환, memories.db 미생성', () => {
|
|
73
|
-
const filePath = path.join(projectDir, 'notes.md');
|
|
74
|
-
fs.writeFileSync(filePath, "console.log('should not trigger memory write')\n");
|
|
75
|
-
|
|
76
|
-
const result = runCodeCheck({ filePath, projectDir });
|
|
77
|
-
|
|
78
|
-
expect(result.status).toBe(0);
|
|
79
|
-
expect(fs.existsSync(memoriesDbPath(projectDir))).toBe(false);
|
|
80
|
-
});
|
|
81
|
-
});
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { spawnSync } from 'child_process';
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import os from 'os';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
import { fileURLToPath } from 'url';
|
|
7
|
-
|
|
8
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
const ADAPTER = path.resolve(__dirname, '..', 'codex-hook-adapter.js');
|
|
10
|
-
|
|
11
|
-
describe('codex-hook-adapter', () => {
|
|
12
|
-
it('converts a PreToolUse denial into Codex permission output', () => {
|
|
13
|
-
const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'vibe-codex-adapter-'));
|
|
14
|
-
const payload = JSON.stringify({
|
|
15
|
-
tool_name: 'Bash',
|
|
16
|
-
tool_input: { command: 'rm -rf /' },
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const result = spawnSync(process.execPath, [ADAPTER, 'PreToolUse'], {
|
|
20
|
-
cwd,
|
|
21
|
-
input: payload,
|
|
22
|
-
encoding: 'utf-8',
|
|
23
|
-
timeout: 5000,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
expect(result.status).toBe(2);
|
|
27
|
-
const output = JSON.parse(result.stdout.trim());
|
|
28
|
-
expect(output.hookSpecificOutput.permissionDecision).toBe('deny');
|
|
29
|
-
expect(output.hookSpecificOutput.permissionDecisionReason).toContain('Deleting root or home directory');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Codex 에는 CC 의 context_window_* Notification 등가물이 없어, PreCompact 를
|
|
34
|
-
* 붙이기 전까지 압축 전 체크포인트가 아예 저장되지 않았다.
|
|
35
|
-
*/
|
|
36
|
-
it('handles PreCompact without failing or injecting context', () => {
|
|
37
|
-
const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'vibe-codex-precompact-'));
|
|
38
|
-
|
|
39
|
-
const result = spawnSync(process.execPath, [ADAPTER, 'PreCompact'], {
|
|
40
|
-
cwd,
|
|
41
|
-
input: JSON.stringify({ cwd }),
|
|
42
|
-
encoding: 'utf-8',
|
|
43
|
-
timeout: 30000,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
expect(result.status).toBe(0);
|
|
47
|
-
// 압축 직전에 컨텍스트를 더 늘리면 역효과 — stdout 주입이 없어야 한다
|
|
48
|
-
expect(result.stdout.trim()).toBe('');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('ignores unknown events instead of erroring', () => {
|
|
52
|
-
const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'vibe-codex-unknown-'));
|
|
53
|
-
|
|
54
|
-
const result = spawnSync(process.execPath, [ADAPTER, 'SubagentStop'], {
|
|
55
|
-
cwd,
|
|
56
|
-
input: JSON.stringify({ cwd }),
|
|
57
|
-
encoding: 'utf-8',
|
|
58
|
-
timeout: 10000,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
expect(result.status).toBe(0);
|
|
62
|
-
expect(result.stdout.trim()).toBe('');
|
|
63
|
-
});
|
|
64
|
-
});
|