@su-record/vibe 3.2.18 → 3.2.20

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.
Files changed (80) hide show
  1. package/CLAUDE.md +1 -0
  2. package/dist/cli/commands/remove.d.ts.map +1 -1
  3. package/dist/cli/commands/remove.js +15 -6
  4. package/dist/cli/commands/remove.js.map +1 -1
  5. package/dist/cli/setup/ProjectSetup.d.ts.map +1 -1
  6. package/dist/cli/setup/ProjectSetup.js +13 -1
  7. package/dist/cli/setup/ProjectSetup.js.map +1 -1
  8. package/dist/tools/dispatch/costGate.d.ts +59 -0
  9. package/dist/tools/dispatch/costGate.d.ts.map +1 -0
  10. package/dist/tools/dispatch/costGate.js +85 -0
  11. package/dist/tools/dispatch/costGate.js.map +1 -0
  12. package/dist/tools/dispatch/deterministicSignals.d.ts +61 -0
  13. package/dist/tools/dispatch/deterministicSignals.d.ts.map +1 -0
  14. package/dist/tools/dispatch/deterministicSignals.js +136 -0
  15. package/dist/tools/dispatch/deterministicSignals.js.map +1 -0
  16. package/dist/tools/dispatch/index.d.ts +5 -0
  17. package/dist/tools/dispatch/index.d.ts.map +1 -0
  18. package/dist/tools/dispatch/index.js +3 -0
  19. package/dist/tools/dispatch/index.js.map +1 -0
  20. package/dist/tools/dispatch/vibePaths.d.ts +7 -0
  21. package/dist/tools/dispatch/vibePaths.d.ts.map +1 -0
  22. package/dist/tools/dispatch/vibePaths.js +35 -0
  23. package/dist/tools/dispatch/vibePaths.js.map +1 -0
  24. package/dist/tools/index.d.ts +5 -0
  25. package/dist/tools/index.d.ts.map +1 -1
  26. package/dist/tools/index.js +4 -0
  27. package/dist/tools/index.js.map +1 -1
  28. package/dist/tools/spec/index.d.ts +2 -0
  29. package/dist/tools/spec/index.d.ts.map +1 -1
  30. package/dist/tools/spec/index.js +2 -0
  31. package/dist/tools/spec/index.js.map +1 -1
  32. package/dist/tools/spec/validateSpecDocument.d.ts +30 -0
  33. package/dist/tools/spec/validateSpecDocument.d.ts.map +1 -0
  34. package/dist/tools/spec/validateSpecDocument.js +201 -0
  35. package/dist/tools/spec/validateSpecDocument.js.map +1 -0
  36. package/hooks/scripts/code-check.js +90 -16
  37. package/hooks/scripts/lib/anchor.js +15 -4
  38. package/hooks/scripts/lib/inbox.js +10 -2
  39. package/package.json +1 -1
  40. package/skills/vibe/SKILL.md +18 -5
  41. package/skills/vibe.image/SKILL.md +8 -0
  42. package/skills/vibe.review/SKILL.md +8 -0
  43. package/skills/vibe.spec/SKILL.md +21 -0
  44. package/vibe/rules/loop-contract.md +31 -0
  45. package/dist/infra/lib/DeepInit.d.ts +0 -60
  46. package/dist/infra/lib/DeepInit.d.ts.map +0 -1
  47. package/dist/infra/lib/DeepInit.js +0 -245
  48. package/dist/infra/lib/DeepInit.js.map +0 -1
  49. package/dist/infra/lib/FrameworkDetector.d.ts +0 -56
  50. package/dist/infra/lib/FrameworkDetector.d.ts.map +0 -1
  51. package/dist/infra/lib/FrameworkDetector.js +0 -287
  52. package/dist/infra/lib/FrameworkDetector.js.map +0 -1
  53. package/dist/infra/lib/ModelRouter.d.ts +0 -48
  54. package/dist/infra/lib/ModelRouter.d.ts.map +0 -1
  55. package/dist/infra/lib/ModelRouter.js +0 -216
  56. package/dist/infra/lib/ModelRouter.js.map +0 -1
  57. package/dist/infra/lib/OrchestrateWorkflow.d.ts +0 -88
  58. package/dist/infra/lib/OrchestrateWorkflow.d.ts.map +0 -1
  59. package/dist/infra/lib/OrchestrateWorkflow.js +0 -259
  60. package/dist/infra/lib/OrchestrateWorkflow.js.map +0 -1
  61. package/dist/infra/lib/ReviewRace.d.ts +0 -86
  62. package/dist/infra/lib/ReviewRace.d.ts.map +0 -1
  63. package/dist/infra/lib/ReviewRace.js +0 -454
  64. package/dist/infra/lib/ReviewRace.js.map +0 -1
  65. package/dist/infra/lib/RuleBuildSystem.d.ts +0 -157
  66. package/dist/infra/lib/RuleBuildSystem.d.ts.map +0 -1
  67. package/dist/infra/lib/RuleBuildSystem.js +0 -550
  68. package/dist/infra/lib/RuleBuildSystem.js.map +0 -1
  69. package/dist/infra/lib/SkillQualityGate.d.ts +0 -42
  70. package/dist/infra/lib/SkillQualityGate.d.ts.map +0 -1
  71. package/dist/infra/lib/SkillQualityGate.js +0 -220
  72. package/dist/infra/lib/SkillQualityGate.js.map +0 -1
  73. package/dist/infra/lib/SkillRepository.d.ts +0 -134
  74. package/dist/infra/lib/SkillRepository.d.ts.map +0 -1
  75. package/dist/infra/lib/SkillRepository.js +0 -500
  76. package/dist/infra/lib/SkillRepository.js.map +0 -1
  77. package/dist/infra/lib/UltraQA.d.ts +0 -89
  78. package/dist/infra/lib/UltraQA.d.ts.map +0 -1
  79. package/dist/infra/lib/UltraQA.js +0 -301
  80. package/dist/infra/lib/UltraQA.js.map +0 -1
@@ -0,0 +1,7 @@
1
+ /** 읽기용 루트 — 존재하는 레거시 경로를 우선 반환, 없으면 `.vibe/` */
2
+ export declare function projectVibeRoot(projectDir: string): string;
3
+ /** 읽기용 경로 — 레거시 인식 */
4
+ export declare function projectVibePath(projectDir: string, ...sub: string[]): string;
5
+ /** 쓰기용 경로 — 항상 신규 레이아웃(`.vibe/`) */
6
+ export declare function projectVibePathPreferred(projectDir: string, ...sub: string[]): string;
7
+ //# sourceMappingURL=vibePaths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vibePaths.d.ts","sourceRoot":"","sources":["../../../src/tools/dispatch/vibePaths.ts"],"names":[],"mappings":"AAcA,gDAAgD;AAChD,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ1D;AAED,sBAAsB;AACtB,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,CAE5E;AAED,oCAAoC;AACpC,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,CAErF"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * vibe 상태 경로 소유자 (TS 측).
3
+ *
4
+ * 훅 측에는 `hooks/scripts/utils.js` 의 `projectVibeRoot`/`projectVibePath` 가 있는데,
5
+ * TS 도구들이 그것을 쓸 수 없어 `.vibe/` 를 각자 하드코딩했다. 그 결과 레거시
6
+ * 프로젝트(`.claude/vibe/`)에서 읽는 쪽만 조용히 빗나갔다 — 실제로 ANCHOR 가
7
+ * `.last-feature` 를 못 찾아 feature·spec·scope 를 전부 missing 으로 보고했다.
8
+ *
9
+ * 두 런타임이 **같은 해석 규칙**을 갖도록 여기에 미러링한다.
10
+ * 규칙: 읽기는 `.vibe/` → `.claude/vibe/` 순으로 존재하는 쪽, 쓰기는 항상 `.vibe/`.
11
+ */
12
+ import fs from 'fs';
13
+ import path from 'path';
14
+ /** 읽기용 루트 — 존재하는 레거시 경로를 우선 반환, 없으면 `.vibe/` */
15
+ export function projectVibeRoot(projectDir) {
16
+ try {
17
+ const modern = path.join(projectDir, '.vibe');
18
+ if (fs.existsSync(modern))
19
+ return modern;
20
+ const legacy = path.join(projectDir, '.claude', 'vibe');
21
+ if (fs.existsSync(legacy))
22
+ return legacy;
23
+ }
24
+ catch { /* 접근 불가 → 신규 레이아웃으로 취급 */ }
25
+ return path.join(projectDir, '.vibe');
26
+ }
27
+ /** 읽기용 경로 — 레거시 인식 */
28
+ export function projectVibePath(projectDir, ...sub) {
29
+ return path.join(projectVibeRoot(projectDir), ...sub);
30
+ }
31
+ /** 쓰기용 경로 — 항상 신규 레이아웃(`.vibe/`) */
32
+ export function projectVibePathPreferred(projectDir, ...sub) {
33
+ return path.join(projectDir, '.vibe', ...sub);
34
+ }
35
+ //# sourceMappingURL=vibePaths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vibePaths.js","sourceRoot":"","sources":["../../../src/tools/dispatch/vibePaths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,gDAAgD;AAChD,MAAM,UAAU,eAAe,CAAC,UAAkB;IAChD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC9C,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QACxD,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC,CAAC,0BAA0B,CAAC,CAAC;IACtC,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AACxC,CAAC;AAED,sBAAsB;AACtB,MAAM,UAAU,eAAe,CAAC,UAAkB,EAAE,GAAG,GAAa;IAClE,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC;AACxD,CAAC;AAED,oCAAoC;AACpC,MAAM,UAAU,wBAAwB,CAAC,UAAkB,EAAE,GAAG,GAAa;IAC3E,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,CAAC;AAChD,CAAC"}
@@ -66,5 +66,10 @@ export type { VibeSpan, SpanType, SpanStatus, } from '../infra/lib/telemetry/Vib
66
66
  export { ComponentRegistry } from '../infra/lib/ComponentRegistry.js';
67
67
  export type { ComponentSpec, ComponentEntry, } from '../infra/lib/ComponentRegistry.js';
68
68
  export { validateLoopDefinition } from './loop/index.js';
69
+ export { validateSpecDocument, formatSpecValidation, featureSlugFromPath, } from './spec/index.js';
70
+ export { collectDispatchSignals, detectResumeState, detectStakesSignals, classifyUrl, classifyAttachment, } from './dispatch/index.js';
71
+ export type { DispatchSignals, ResumeState, StakesSignals, UrlKind, AttachmentKind, } from './dispatch/index.js';
72
+ export { evaluateCostGate, formatCostGate } from './dispatch/index.js';
73
+ export type { CostOperation, CostGateConfig, CostGateDecision, } from './dispatch/index.js';
69
74
  export type { ParsedLoopDefinition, LoopValidationResult, LoopTrigger, LoopVerify, LoopIsolation, LoopStatus, } from './loop/index.js';
70
75
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
@@ -81,4 +81,8 @@ export { createSpan, completeSpan } from '../infra/lib/telemetry/VibeSpan.js';
81
81
  export { ComponentRegistry } from '../infra/lib/ComponentRegistry.js';
82
82
  // ─── Loop Tools ───
83
83
  export { validateLoopDefinition } from './loop/index.js';
84
+ export { validateSpecDocument, formatSpecValidation, featureSlugFromPath, } from './spec/index.js';
85
+ // 디스패처 결정론 신호 — 파일 존재·URL 도메인·첨부 확장자는 모델이 아니라 코드가 판정한다
86
+ export { collectDispatchSignals, detectResumeState, detectStakesSignals, classifyUrl, classifyAttachment, } from './dispatch/index.js';
87
+ export { evaluateCostGate, formatCostGate } from './dispatch/index.js';
84
88
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
@@ -11,4 +11,6 @@ export { compileExecutionPacket, getHarnessProfile, validateExecutionPacket, wri
11
11
  export type { CompileExecutionPacketInput, CompileExecutionPacketResult, ExecutionPacket, ExecutionPacketFileInput, HarnessProfile, HarnessProfileName, PacketError, PacketErrorCode, ValidateExecutionPacketInput, ValidateExecutionPacketResult, WriteExecutionPacketResult, } from './executionPacket.js';
12
12
  export { generateTraceabilityMatrix, formatMatrixAsMarkdown, formatMatrixAsHtml } from './traceabilityMatrix.js';
13
13
  export type { TraceItem, TraceabilityMatrix, TraceSummary, TraceMatrixOptions } from './traceabilityMatrix.js';
14
+ export { validateSpecDocument, formatSpecValidation, featureSlugFromPath, } from './validateSpecDocument.js';
15
+ export type { SpecValidationResult, SpecFinding, SpecFindingSeverity, } from './validateSpecDocument.js';
14
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/spec/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,QAAQ,EACR,YAAY,EACb,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,WAAW,EACX,SAAS,EACT,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,oBAAoB,EACpB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,2BAA2B,EAC3B,4BAA4B,EAC5B,eAAe,EACf,wBAAwB,EACxB,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,4BAA4B,EAC5B,6BAA6B,EAC7B,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EACnB,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/spec/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,QAAQ,EACR,YAAY,EACb,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,WAAW,EACX,SAAS,EACT,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,oBAAoB,EACpB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,2BAA2B,EAC3B,4BAA4B,EAC5B,eAAe,EACf,wBAAwB,EACxB,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,4BAA4B,EAC5B,6BAA6B,EAC7B,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,mBAAmB,GACpB,MAAM,2BAA2B,CAAC"}
@@ -12,4 +12,6 @@ export { generateSpec } from './specGenerator.js';
12
12
  export { compileExecutionPacket, getHarnessProfile, validateExecutionPacket, writeExecutionPacket, } from './executionPacket.js';
13
13
  // Traceability Matrix
14
14
  export { generateTraceabilityMatrix, formatMatrixAsMarkdown, formatMatrixAsHtml } from './traceabilityMatrix.js';
15
+ // SPEC Code Guard — 산출물이 하류(run/verify)로 넘어가기 전 노드 단위 검사
16
+ export { validateSpecDocument, formatSpecValidation, featureSlugFromPath, } from './validateSpecDocument.js';
15
17
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/spec/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,iBAAiB;AACjB,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAE5B,aAAa;AACb,OAAO,EACL,QAAQ,EACR,YAAY,EACb,MAAM,gBAAgB,CAAC;AAOxB,iBAAiB;AACjB,OAAO,EACL,YAAY,EACb,MAAM,oBAAoB,CAAC;AAM5B,4BAA4B;AAC5B,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAe9B,sBAAsB;AACtB,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/spec/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,iBAAiB;AACjB,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAE5B,aAAa;AACb,OAAO,EACL,QAAQ,EACR,YAAY,EACb,MAAM,gBAAgB,CAAC;AAOxB,iBAAiB;AACjB,OAAO,EACL,YAAY,EACb,MAAM,oBAAoB,CAAC;AAM5B,4BAA4B;AAC5B,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAe9B,sBAAsB;AACtB,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAQjC,yDAAyD;AACzD,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,30 @@
1
+ export type SpecFindingSeverity = 'P1' | 'P2';
2
+ export interface SpecFinding {
3
+ severity: SpecFindingSeverity;
4
+ code: string;
5
+ message: string;
6
+ /** 1-indexed. 문서 전체에 걸린 문제면 생략 */
7
+ line?: number;
8
+ }
9
+ export interface SpecValidationResult {
10
+ /** P1 이 하나도 없으면 통과 — P2 는 통과를 막지 않는다 */
11
+ valid: boolean;
12
+ findings: SpecFinding[];
13
+ /** 문서에서 발견한 REQ ID 목록 (중복 제거) */
14
+ requirementIds: string[];
15
+ }
16
+ /** 파일명에서 feature 슬러그를 뽑는다 (분할 SPEC 은 디렉토리명) */
17
+ export declare function featureSlugFromPath(specPath: string): string;
18
+ /**
19
+ * SPEC 문서를 검사한다.
20
+ *
21
+ * @param content SPEC 마크다운 원문
22
+ * @param options.specPath 파일 경로 — 주면 REQ 슬러그와 파일명 일치까지 검사한다
23
+ * @returns P1 이 없으면 valid
24
+ */
25
+ export declare function validateSpecDocument(content: string, options?: {
26
+ specPath?: string;
27
+ }): SpecValidationResult;
28
+ /** 사람이 읽는 한 줄 요약 — 스킬이 그대로 출력한다 */
29
+ export declare function formatSpecValidation(result: SpecValidationResult): string;
30
+ //# sourceMappingURL=validateSpecDocument.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateSpecDocument.d.ts","sourceRoot":"","sources":["../../../src/tools/spec/validateSpecDocument.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,mBAAmB,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,wCAAwC;IACxC,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,iCAAiC;IACjC,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAwBD,+CAA+C;AAC/C,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG5D;AAqID;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,oBAAoB,CAwBtB;AAED,mCAAmC;AACnC,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAUzE"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * SPEC 문서 Code Guard — `vibe.spec` 산출물이 하류로 넘어가기 전에 검사한다.
3
+ *
4
+ * 배경: vibe 의 결정론 게이트는 전부 파이프라인 **끝**(JUDGE — run-ledger·테스트·RTM)에
5
+ * 몰려 있었다. 중간 노드에는 가드가 없어, 게이트로 쓸 수 없는 SPEC 이 run 까지
6
+ * 흘러간 뒤 verify 단계에서야 걸렸다. 실패를 늦게 발견할수록 비싸다.
7
+ *
8
+ * 여기서 검사하는 것은 취향이 아니라 **하류가 실제로 요구하는 것**이다:
9
+ * - RTM 은 `REQ-{feature}-NNN` 이 없으면 `status: 'empty'` 를 내고, 그건 통과가 아니라
10
+ * 판정불가다 (traceabilityMatrix.ts). 커버리지 게이트가 통째로 죽는다.
11
+ * - `Stakes:` 는 디스패처가 파이프라인 깊이를 정하는 입력이다.
12
+ * - Done Criteria 는 JUDGE 의 입력이다 — 없으면 판정할 것이 없다.
13
+ * - 채워지지 않은 placeholder 는 직역 하네스가 실데이터로 넣는다
14
+ * (dual-harness-doctrine 운영 규칙 2).
15
+ */
16
+ import path from 'path';
17
+ const REQ_ID = /\bREQ-([a-z0-9-]+)-(\d{3})\b/g;
18
+ const STAKES_LINE = /^\s*[-*]?\s*\*{0,2}Stakes\*{0,2}\s*[::]\s*(.+)$/im;
19
+ const VALID_STAKES = ['demo', 'prototype', 'production'];
20
+ /**
21
+ * 코드 구간(펜스 + 인라인 코드)을 공백으로 지운다 — 줄 번호는 보존한다.
22
+ *
23
+ * SPEC 은 경로 패턴(`styles/{feature}/`), 데이터 모양(`{r,g,b,a}`), 셸 brace
24
+ * expansion(`hooks/{a,b}.js`)을 인라인 코드로 정상적으로 쓴다. 원문을 그대로 훑으면
25
+ * 그것들이 전부 미치환 placeholder 로 잡힌다 (실측: 오탐 28건).
26
+ */
27
+ function stripCodeSpans(content) {
28
+ const blank = (block) => block.replace(/[^\n]/g, ' ');
29
+ return content
30
+ .replace(/```[\s\S]*?```/g, blank)
31
+ .replace(/`[^`\n]*`/g, blank);
32
+ }
33
+ function lineOf(content, index) {
34
+ return content.slice(0, index).split('\n').length;
35
+ }
36
+ /** 파일명에서 feature 슬러그를 뽑는다 (분할 SPEC 은 디렉토리명) */
37
+ export function featureSlugFromPath(specPath) {
38
+ const base = path.basename(specPath, '.md');
39
+ return base === '_index' ? path.basename(path.dirname(specPath)) : base;
40
+ }
41
+ function collectRequirementIds(content) {
42
+ const out = [];
43
+ for (const m of content.matchAll(REQ_ID)) {
44
+ out.push({ id: m[0], slug: m[1], index: m.index ?? 0 });
45
+ }
46
+ return out;
47
+ }
48
+ function checkRequirements(content, featureSlug, findings) {
49
+ const found = collectRequirementIds(content);
50
+ if (found.length === 0) {
51
+ findings.push({
52
+ severity: 'P1',
53
+ code: 'no-requirement-ids',
54
+ message: 'REQ-* ID 가 하나도 없다 — RTM 이 status:"empty" 를 내고 커버리지 게이트가 판정불가가 된다. '
55
+ + '모든 기능 요구사항에 REQ-{feature}-NNN 을 붙여라.',
56
+ });
57
+ return [];
58
+ }
59
+ // 파일명과 다른 슬러그는 게이트를 깨지는 않는다 — RTM 은 featureName 을 파일 탐색에만
60
+ // 쓰고 REQ 는 슬러그와 무관하게 전부 파싱한다(traceabilityMatrix.ts extractRequirements).
61
+ // 다만 ID 규약은 REQ-{feature}-NNN 이고(requirementId.ts), 어긋나면 여러 feature 의
62
+ // ID 가 한 문서에서 섞여 추적이 흐려진다 — 그래서 P2 로 알린다.
63
+ if (featureSlug) {
64
+ const mismatched = found.filter(f => f.slug !== featureSlug);
65
+ if (mismatched.length > 0) {
66
+ const slugs = [...new Set(mismatched.map(f => f.slug))];
67
+ findings.push({
68
+ severity: 'P2',
69
+ code: 'requirement-id-slug-mismatch',
70
+ message: `REQ 슬러그(${slugs.join(', ')})가 파일명(${featureSlug})과 다르다 — ${mismatched.length}건. `
71
+ + 'RTM 집계에는 포함되지만 ID 규약(REQ-{feature}-NNN)에서 벗어나 추적이 흐려진다.',
72
+ line: lineOf(content, mismatched[0].index),
73
+ });
74
+ }
75
+ }
76
+ return [...new Set(found.map(f => f.id))];
77
+ }
78
+ function checkStakes(content, findings) {
79
+ const m = content.match(STAKES_LINE);
80
+ if (!m) {
81
+ findings.push({
82
+ severity: 'P1',
83
+ code: 'no-stakes',
84
+ message: 'Stakes 필드가 없다 — 디스패처가 파이프라인 깊이를 정할 입력이 없다 (demo | prototype | production).',
85
+ });
86
+ return;
87
+ }
88
+ const value = m[1].toLowerCase();
89
+ if (!VALID_STAKES.some(s => value.includes(s))) {
90
+ findings.push({
91
+ severity: 'P1',
92
+ code: 'invalid-stakes',
93
+ message: `Stakes 값을 알 수 없다: "${m[1].trim()}" — demo | prototype | production 중 하나여야 한다.`,
94
+ });
95
+ }
96
+ }
97
+ function checkDoneCriteria(content, findings) {
98
+ const heading = content.match(/^#{1,3}\s*\d*\.?\s*Done Criteria.*$/im);
99
+ if (!heading) {
100
+ findings.push({
101
+ severity: 'P1',
102
+ code: 'no-done-criteria',
103
+ message: 'Done Criteria 섹션이 없다 — JUDGE 가 판정할 입력이 없다.',
104
+ });
105
+ return;
106
+ }
107
+ // 섹션 본문에 D1 같은 기준 항목이 실제로 있는지
108
+ const after = content.slice((heading.index ?? 0) + heading[0].length);
109
+ const body = after.split(/^#{1,3}\s/m)[0];
110
+ if (!/\bD\d+\b/.test(body)) {
111
+ findings.push({
112
+ severity: 'P1',
113
+ code: 'empty-done-criteria',
114
+ message: 'Done Criteria 섹션에 기준 항목(D1, D2 …)이 없다 — 제목만 있고 판정 기준이 비었다.',
115
+ line: lineOf(content, heading.index ?? 0),
116
+ });
117
+ }
118
+ }
119
+ /**
120
+ * 템플릿에서 온 미치환 placeholder — 직역 하네스가 실데이터로 넣는다.
121
+ *
122
+ * 좁게 잡는다. 중괄호 하나만으로는 판단할 수 없다 — 경로 패턴·데이터 모양·brace
123
+ * expansion 이 전부 중괄호를 쓴다. 템플릿 placeholder 는 **산문**이라는 점이
124
+ * 구분점이다: `{Observable functional requirement}` 처럼 공백을 포함한다.
125
+ * 반면 `{feature}` · `{token}` · `{r,g,b,a}` 는 공백이 없다.
126
+ *
127
+ * `<예시>` 는 검사하지 않는다 — dual-harness-doctrine 이 **권장하는** 예시 표기이지
128
+ * 채워야 할 자리가 아니다. `<채워넣을 값>` 만 미치환으로 본다.
129
+ */
130
+ function checkPlaceholders(content, findings) {
131
+ const scannable = stripCodeSpans(content);
132
+ const patterns = [
133
+ { re: /\{\{[^}\n]+\}\}/g, code: 'unresolved-template-var', label: '템플릿 변수' },
134
+ { re: /\{[^}\n]*\s[^}\n]*\}/g, code: 'unfilled-placeholder', label: 'placeholder' },
135
+ { re: /<채워넣을 값>/g, code: 'unfilled-placeholder', label: 'placeholder' },
136
+ ];
137
+ const seen = new Set();
138
+ for (const { re, code, label } of patterns) {
139
+ for (const m of scannable.matchAll(re)) {
140
+ const line = lineOf(scannable, m.index ?? 0);
141
+ if (seen.has(line))
142
+ continue;
143
+ seen.add(line);
144
+ findings.push({
145
+ severity: 'P1',
146
+ code,
147
+ message: `미치환 ${label}: ${m[0].slice(0, 60)} — 직역 하네스는 이 텍스트를 실데이터로 넣는다.`,
148
+ line,
149
+ });
150
+ }
151
+ }
152
+ }
153
+ function checkScenarios(content, findings) {
154
+ if (!/^#{1,3}\s*\d*\.?\s*Scenarios/im.test(content)) {
155
+ findings.push({
156
+ severity: 'P2',
157
+ code: 'no-scenarios',
158
+ message: 'Scenarios 섹션이 없다 — vibe.run 의 시나리오 루프가 분해할 단위가 없다.',
159
+ });
160
+ }
161
+ }
162
+ /**
163
+ * SPEC 문서를 검사한다.
164
+ *
165
+ * @param content SPEC 마크다운 원문
166
+ * @param options.specPath 파일 경로 — 주면 REQ 슬러그와 파일명 일치까지 검사한다
167
+ * @returns P1 이 없으면 valid
168
+ */
169
+ export function validateSpecDocument(content, options = {}) {
170
+ const findings = [];
171
+ if (content.trim().length === 0) {
172
+ return {
173
+ valid: false,
174
+ findings: [{ severity: 'P1', code: 'empty-spec', message: 'SPEC 이 비어 있다.' }],
175
+ requirementIds: [],
176
+ };
177
+ }
178
+ const featureSlug = options.specPath ? featureSlugFromPath(options.specPath) : undefined;
179
+ const requirementIds = checkRequirements(content, featureSlug, findings);
180
+ checkStakes(content, findings);
181
+ checkDoneCriteria(content, findings);
182
+ checkPlaceholders(content, findings);
183
+ checkScenarios(content, findings);
184
+ return {
185
+ valid: !findings.some(f => f.severity === 'P1'),
186
+ findings,
187
+ requirementIds,
188
+ };
189
+ }
190
+ /** 사람이 읽는 한 줄 요약 — 스킬이 그대로 출력한다 */
191
+ export function formatSpecValidation(result) {
192
+ if (result.valid && result.findings.length === 0) {
193
+ return `✅ SPEC guard 통과 — REQ ${result.requirementIds.length}건`;
194
+ }
195
+ const lines = result.findings.map(f => ` ${f.severity} ${f.code}${f.line ? ` (line ${f.line})` : ''}: ${f.message}`);
196
+ const head = result.valid
197
+ ? `⚠️ SPEC guard 통과 (P2 ${result.findings.length}건) — REQ ${result.requirementIds.length}건`
198
+ : `❌ SPEC guard 실패 — P1 ${result.findings.filter(f => f.severity === 'P1').length}건`;
199
+ return [head, ...lines].join('\n');
200
+ }
201
+ //# sourceMappingURL=validateSpecDocument.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateSpecDocument.js","sourceRoot":"","sources":["../../../src/tools/spec/validateSpecDocument.ts"],"names":[],"mappings":"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"}
@@ -69,36 +69,110 @@ function findUnescaped(str, token) {
69
69
  return -1;
70
70
  }
71
71
 
72
+ /** 정규식 리터럴이 시작될 수 있는 위치인지 — 직전 코드로 나눗셈과 구분한다 */
73
+ function canStartRegex(codeSoFar) {
74
+ const prev = codeSoFar.replace(/\s+$/, '');
75
+ if (prev === '') return true;
76
+ if (/\b(return|typeof|case|in|of|new|delete|void|throw)$/.test(prev)) return true;
77
+ return /[=(,:[!&|?{};+\-*%~^<>]$/.test(prev);
78
+ }
79
+
80
+ /** 템플릿 리터럴 안에서 종료 백틱과 `${` 중 먼저 오는 것을 찾는다 */
81
+ function nextTemplateStop(rest) {
82
+ for (let i = 0; i < rest.length; i++) {
83
+ if (rest[i] === '\\') { i++; continue; }
84
+ if (rest[i] === '`') return { kind: 'end', index: i };
85
+ if (rest[i] === '$' && rest[i + 1] === '{') return { kind: 'expr', index: i };
86
+ }
87
+ return null;
88
+ }
89
+
90
+ /**
91
+ * 템플릿 리터럴 본문을 소비한다. `${` 를 만나면 **코드 구간**으로 넘긴다 —
92
+ * 보간 안은 문자열이 아니라 실행되는 코드다 (`` `${x as any}` `` 를 놓치던 원인).
93
+ */
94
+ function consumeTemplate(rest, state) {
95
+ const stop = nextTemplateStop(rest);
96
+ if (!stop) return null; // 줄 끝까지 템플릿 — 다음 줄로 상태를 이어간다
97
+ if (stop.kind === 'end') {
98
+ state.stack.pop();
99
+ return rest.slice(stop.index + 1);
100
+ }
101
+ // depth 는 보간 안에서 열린 `{` 수 — 0 일 때의 `}` 만 보간을 닫는다
102
+ state.stack.push({ kind: 'expr', depth: 0 });
103
+ return rest.slice(stop.index + 2);
104
+ }
105
+
72
106
  /**
73
107
  * 한 줄에서 코드가 아닌 구간(주석·문자열·템플릿 리터럴)을 지운다.
74
- * 여러 줄에 걸친 블록 주석·템플릿 리터럴을 잇기 위해 state를 받고 갱신해 돌려준다.
108
+ *
109
+ * 상태는 스택이다 — 템플릿 안의 `${}` 안에 또 템플릿이 올 수 있다.
110
+ * 여러 줄에 걸친 블록 주석·템플릿 리터럴은 스택으로 이어진다.
111
+ *
75
112
  * @param {string} line
76
- * @param {{ inBlock: boolean, inTemplate: boolean }} state - 제자리 갱신
113
+ * @param {{ stack: Array<'block'|'template'|'expr'> }} state - 제자리 갱신
77
114
  * @returns {string} 코드 구간만 남은 문자열
78
115
  */
79
116
  function stripNonCodeLine(line, state) {
80
117
  let rest = line;
81
118
  let code = '';
119
+
82
120
  while (rest.length > 0) {
83
- if (state.inBlock || state.inTemplate) {
84
- const closer = state.inBlock ? '*/' : '`';
85
- const end = findUnescaped(rest, closer);
121
+ const frame = state.stack[state.stack.length - 1];
122
+ const top = typeof frame === 'object' ? frame.kind : frame;
123
+
124
+ if (top === 'block') {
125
+ const end = findUnescaped(rest, '*/');
86
126
  if (end === -1) break;
87
- if (state.inBlock) state.inBlock = false;
88
- else state.inTemplate = false;
89
- rest = rest.slice(end + closer.length);
127
+ state.stack.pop();
128
+ rest = rest.slice(end + 2);
129
+ continue;
130
+ }
131
+ if (top === 'template') {
132
+ const next = consumeTemplate(rest, state);
133
+ if (next === null) break;
134
+ rest = next;
90
135
  continue;
91
136
  }
92
- const opener = rest.match(/\/\*|\/\/|`|'|"/);
137
+
138
+ // 코드 구간 (최상위 또는 `${}` 안)
139
+ const opener = rest.match(/\/\*|\/\/|`|'|"|\/|\{|\}/);
93
140
  if (!opener) return code + rest;
94
141
  code += rest.slice(0, opener.index);
95
142
  const token = opener[0];
96
- rest = rest.slice(opener.index + token.length);
143
+ const consumed = rest.slice(opener.index + token.length);
144
+
97
145
  if (token === '//') break;
98
- if (token === '/*') { state.inBlock = true; continue; }
99
- if (token === '`') { state.inTemplate = true; continue; }
100
- const close = findUnescaped(rest, token); // 홑/겹따옴표는 줄을 넘지 않는다
101
- rest = close === -1 ? '' : rest.slice(close + 1);
146
+ if (token === '{') {
147
+ if (top === 'expr') frame.depth++;
148
+ code += '{';
149
+ rest = consumed;
150
+ continue;
151
+ }
152
+ if (token === '}') {
153
+ // 보간 안에서 열린 `{` 가 남아 있으면 그걸 닫는 것이지 보간의 끝이 아니다
154
+ // (`${ {a:1}.x as any }` 를 조기 종료하던 원인)
155
+ if (top === 'expr' && frame.depth === 0) state.stack.pop();
156
+ else if (top === 'expr') { frame.depth--; code += '}'; }
157
+ else code += '}';
158
+ rest = consumed;
159
+ continue;
160
+ }
161
+ if (token === '/*') { state.stack.push('block'); rest = consumed; continue; }
162
+ if (token === '`') { state.stack.push('template'); rest = consumed; continue; }
163
+ if (token === '/') {
164
+ // 정규식 리터럴이면 통째로 건너뛴다 — 안의 백틱/따옴표가 상태를 오염시킨다
165
+ if (canStartRegex(code)) {
166
+ const end = findUnescaped(consumed, '/');
167
+ rest = end === -1 ? '' : consumed.slice(end + 1);
168
+ } else {
169
+ code += '/';
170
+ rest = consumed;
171
+ }
172
+ continue;
173
+ }
174
+ const close = findUnescaped(consumed, token); // 홑/겹따옴표는 줄을 넘지 않는다
175
+ rest = close === -1 ? '' : consumed.slice(close + 1);
102
176
  }
103
177
  return code;
104
178
  }
@@ -115,7 +189,7 @@ function stripNonCodeLine(line, state) {
115
189
  */
116
190
  function detectAnyType(lines) {
117
191
  const findings = [];
118
- const state = { inBlock: false, inTemplate: false };
192
+ const state = { stack: [] };
119
193
  lines.forEach((line, i) => {
120
194
  const code = stripNonCodeLine(line, state);
121
195
  for (const re of P1_DETECTORS) {
@@ -153,7 +227,7 @@ function detectAnyType(lines) {
153
227
  function detectConsoleLogs(lines, filePath) {
154
228
  if (!shouldCheckConsole(filePath)) return [];
155
229
  const findings = [];
156
- const state = { inBlock: false, inTemplate: false };
230
+ const state = { stack: [] };
157
231
  lines.forEach((line, i) => {
158
232
  const code = stripNonCodeLine(line, state);
159
233
  if (/console\.log\(/.test(code)) {
@@ -8,6 +8,8 @@
8
8
  import fs from 'fs';
9
9
  import path from 'path';
10
10
  import { readLedger } from './run-ledger.js';
11
+ import { projectVibePath } from '../utils.js';
12
+ import { inboxPath } from './inbox.js';
11
13
 
12
14
  /** 우선순위대로 첫 번째로 존재하는 경로를 고른다 */
13
15
  function firstExisting(projectDir, candidates) {
@@ -17,10 +19,16 @@ function firstExisting(projectDir, candidates) {
17
19
  return null;
18
20
  }
19
21
 
20
- /** `.vibe/.last-feature` 에 기록된 직전 feature 이름 */
22
+ /**
23
+ * `.last-feature` 에 기록된 직전 feature 이름.
24
+ *
25
+ * 경로는 `projectVibePath` 가 소유한다 — `.vibe/` 를 하드코딩하면 레거시
26
+ * 프로젝트(`.claude/vibe/`)에서 못 찾고, feature 가 null 이 되면 spec 탐색까지
27
+ * 연쇄로 무너져 ANCHOR 가 전부 missing 을 보고한다 (감사 2026-08-08 G-D 재현).
28
+ */
21
29
  function readLastFeature(projectDir) {
22
30
  try {
23
- const raw = fs.readFileSync(path.join(projectDir, '.vibe', '.last-feature'), 'utf-8').trim();
31
+ const raw = fs.readFileSync(projectVibePath(projectDir, '.last-feature'), 'utf-8').trim();
24
32
  return raw.length > 0 ? raw : null;
25
33
  } catch {
26
34
  return null;
@@ -41,7 +49,7 @@ function findSpec(projectDir, feature) {
41
49
  /** 인박스에서 가장 최근 블록(다음 `## ` 직전까지) */
42
50
  function readLatestInboxBlock(projectDir) {
43
51
  try {
44
- const raw = fs.readFileSync(path.join(projectDir, '.vibe', 'loops', 'inbox.md'), 'utf-8');
52
+ const raw = fs.readFileSync(inboxPath(projectDir), 'utf-8');
45
53
  const start = raw.indexOf('## ');
46
54
  if (start === -1) return null;
47
55
  const next = raw.indexOf('\n## ', start + 3);
@@ -62,7 +70,10 @@ function readLatestInboxBlock(projectDir) {
62
70
  export function buildAnchor(projectDir, feature) {
63
71
  const resolved = feature || readLastFeature(projectDir);
64
72
  const spec = findSpec(projectDir, resolved);
65
- const scope = firstExisting(projectDir, [path.join('.vibe', 'scope.json')]);
73
+ // scope 경로 소유권을 utils 에 맡긴다 — 쓰는 쪽(scope-from-spec)과 다른 루트를
74
+ // 보면 ANCHOR 만 scope 를 놓친다
75
+ const scopeAbs = projectVibePath(projectDir, 'scope.json');
76
+ const scope = fs.existsSync(scopeAbs) ? path.relative(projectDir, scopeAbs) : null;
66
77
  const ledger = readLedger(projectDir);
67
78
 
68
79
  const missing = [];
@@ -8,11 +8,19 @@
8
8
  */
9
9
  import fs from 'fs';
10
10
  import path from 'path';
11
+ import { projectVibePath } from '../utils.js';
11
12
 
12
13
  const HEADER = '# Loop Inbox\n\n> 루프가 남긴 사람 리뷰 큐. 최신 항목이 위에 온다.\n';
13
14
 
14
- function inboxPath(projectDir) {
15
- return path.join(projectDir, '.vibe', 'loops', 'inbox.md');
15
+ /**
16
+ * 인박스 경로 — 이 모듈이 소유한다.
17
+ *
18
+ * 쓰기는 항상 `.vibe/` 로 한다(신규 레이아웃). 읽기는 레거시(`.claude/vibe/`)도
19
+ * 봐야 하므로 `projectVibePath` 를 쓴다 — 두 쪽이 다른 파일을 보면 기록은 되는데
20
+ * ANCHOR 는 못 읽는 상태가 된다.
21
+ */
22
+ export function inboxPath(projectDir) {
23
+ return projectVibePath(projectDir, 'loops', 'inbox.md');
16
24
  }
17
25
 
18
26
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@su-record/vibe",
3
- "version": "3.2.18",
3
+ "version": "3.2.20",
4
4
  "description": "AI Coding Framework for Claude Code — 7+ agents, 52 skills, multi-LLM orchestration",
5
5
  "type": "module",
6
6
  "main": "dist/cli/index.js",