@su-record/vibe 2.13.0 → 2.14.1

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 (69) hide show
  1. package/CLAUDE.md +18 -15
  2. package/README.en.md +7 -5
  3. package/README.md +8 -6
  4. package/dist/cli/detect/matcher.d.ts +15 -0
  5. package/dist/cli/detect/matcher.d.ts.map +1 -0
  6. package/dist/cli/detect/matcher.js +278 -0
  7. package/dist/cli/detect/matcher.js.map +1 -0
  8. package/dist/cli/detect/signatures.d.ts +76 -0
  9. package/dist/cli/detect/signatures.d.ts.map +1 -0
  10. package/dist/cli/detect/signatures.js +175 -0
  11. package/dist/cli/detect/signatures.js.map +1 -0
  12. package/dist/cli/detect/workspace.d.ts +7 -0
  13. package/dist/cli/detect/workspace.d.ts.map +1 -0
  14. package/dist/cli/detect/workspace.js +112 -0
  15. package/dist/cli/detect/workspace.js.map +1 -0
  16. package/dist/cli/detect.characterization.test.d.ts +7 -0
  17. package/dist/cli/detect.characterization.test.d.ts.map +1 -0
  18. package/dist/cli/detect.characterization.test.js +294 -0
  19. package/dist/cli/detect.characterization.test.js.map +1 -0
  20. package/dist/cli/detect.d.ts.map +1 -1
  21. package/dist/cli/detect.js +64 -488
  22. package/dist/cli/detect.js.map +1 -1
  23. package/dist/cli/postinstall/constants.d.ts.map +1 -1
  24. package/dist/cli/postinstall/constants.js +1 -0
  25. package/dist/cli/postinstall/constants.js.map +1 -1
  26. package/dist/cli/setup/ProjectSetup.d.ts.map +1 -1
  27. package/dist/cli/setup/ProjectSetup.js +5 -4
  28. package/dist/cli/setup/ProjectSetup.js.map +1 -1
  29. package/dist/infra/lib/ui-ux/CsvDataLoader.d.ts +10 -1
  30. package/dist/infra/lib/ui-ux/CsvDataLoader.d.ts.map +1 -1
  31. package/dist/infra/lib/ui-ux/CsvDataLoader.js +11 -5
  32. package/dist/infra/lib/ui-ux/CsvDataLoader.js.map +1 -1
  33. package/dist/infra/lib/ui-ux/CsvDataLoader.test.js +8 -8
  34. package/dist/infra/lib/ui-ux/CsvDataLoader.test.js.map +1 -1
  35. package/dist/infra/lib/ui-ux/SearchService.test.js +1 -1
  36. package/dist/infra/lib/ui-ux/SearchService.test.js.map +1 -1
  37. package/dist/tools/index.d.ts +2 -0
  38. package/dist/tools/index.d.ts.map +1 -1
  39. package/dist/tools/index.js +2 -0
  40. package/dist/tools/index.js.map +1 -1
  41. package/dist/tools/loop/index.d.ts +6 -0
  42. package/dist/tools/loop/index.d.ts.map +1 -0
  43. package/dist/tools/loop/index.js +5 -0
  44. package/dist/tools/loop/index.js.map +1 -0
  45. package/dist/tools/loop/validateLoopDefinition.d.ts +38 -0
  46. package/dist/tools/loop/validateLoopDefinition.d.ts.map +1 -0
  47. package/dist/tools/loop/validateLoopDefinition.js +224 -0
  48. package/dist/tools/loop/validateLoopDefinition.js.map +1 -0
  49. package/dist/tools/loop/validateLoopDefinition.test.d.ts +14 -0
  50. package/dist/tools/loop/validateLoopDefinition.test.d.ts.map +1 -0
  51. package/dist/tools/loop/validateLoopDefinition.test.js +229 -0
  52. package/dist/tools/loop/validateLoopDefinition.test.js.map +1 -0
  53. package/hooks/scripts/__tests__/.vibe/command-log.txt +39 -0
  54. package/hooks/scripts/__tests__/.vibe/memories/memories.db-shm +0 -0
  55. package/hooks/scripts/__tests__/.vibe/memories/memories.db-wal +0 -0
  56. package/hooks/scripts/__tests__/keyword-detector.test.js +26 -18
  57. package/hooks/scripts/__tests__/loop-ledger.test.js +321 -0
  58. package/hooks/scripts/keyword-detector.js +22 -22
  59. package/hooks/scripts/lib/hook-context.js +31 -2
  60. package/hooks/scripts/lib/loop-ledger.js +118 -0
  61. package/hooks/scripts/loop-ledger.js +56 -0
  62. package/package.json +3 -2
  63. package/skills/vibe/SKILL.md +40 -23
  64. package/skills/vibe.loop/SKILL.md +116 -0
  65. package/skills/vibe.run/SKILL.md +22 -18
  66. package/skills/vibe.run/references/ralph-loop.md +18 -17
  67. package/skills/vibe.run/references/ultrawork-mode.md +36 -33
  68. package/vibe/rules/loop-contract.md +54 -0
  69. package/vibe/templates/loop-template.md +69 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.characterization.test.js","sourceRoot":"","sources":["../../src/cli/detect.characterization.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,8EAA8E;AAE9E,SAAS,UAAU;IACjB,OAAO,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,SAAS,CAAC,GAAW,EAAE,OAAe,EAAE,OAAe;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACrC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW,EAAE,GAA4B;IACzD,SAAS,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,UAAU,CAAC,MAAuB;IACzC,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC;AAED,8EAA8E;AAE9E,QAAQ,CAAC,qCAAqC,EAAE,GAAG,EAAE;IACnD,IAAI,GAAW,CAAC;IAEhB,UAAU,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvE,4EAA4E;IAC5E,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,QAAQ,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QACtF,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC1D,kDAAkD;QAClD,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,QAAQ,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QACrE,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,QAAQ,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,QAAQ,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QAClF,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QACxD,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,SAAS,CAAC,GAAG,EAAE,kBAAkB,EAAE,qCAAqC,CAAC,CAAC;QAC1E,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,SAAS,CAAC,GAAG,EAAE,gBAAgB,EAAE,qEAAqE,CAAC,CAAC;QACxG,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACvD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,kEAAkE,CAAC,CAAC;QAC9F,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,sFAAsF,CAAC,CAAC;QACjH,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,gFAAgF,CAAC,CAAC;QAC/G,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,SAAS,CAAC,GAAG,EAAE,cAAc,EAAE,wFAAwF,CAAC,CAAC;QACzH,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACrD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,QAAQ,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,oCAAoC;QACpC,QAAQ,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAChE,yBAAyB;QACzB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QAC7F,wBAAwB;QACxB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QAEvG,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,WAAW;QACX,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QACzF,UAAU;QACV,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QAEjG,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC7C,yCAAyC;QACzC,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAC;QAC1E,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC1C,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC;QAC1E,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,sEAAsE,EAAE,GAAG,EAAE;QAC9E,uBAAuB;QACvB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QAC1F,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,QAAQ,EAAE,6BAA6B,CAAC,CAAC;QAEnF,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,QAAQ,CAAC,GAAG,EAAE;YACZ,IAAI,EAAE,OAAO;YACb,YAAY,EAAE;gBACZ,KAAK,EAAE,SAAS;gBAChB,EAAE,EAAE,QAAQ;gBACZ,KAAK,EAAE,QAAQ;gBACf,QAAQ,EAAE,QAAQ;aACnB;SACF,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACtD,gBAAgB;QAChB,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC;QACpE,MAAM,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,QAAQ,CAAC,GAAG,EAAE;YACZ,IAAI,EAAE,OAAO;YACb,YAAY,EAAE;gBACZ,KAAK,EAAE,SAAS;gBAChB,OAAO,EAAE,QAAQ;gBACjB,uBAAuB,EAAE,QAAQ;aAClC;SACF,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAC5D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,QAAQ,CAAC,GAAG,EAAE;YACZ,IAAI,EAAE,MAAM;YACZ,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE;SACtD,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,QAAQ,CAAC,GAAG,EAAE;YACZ,IAAI,EAAE,YAAY;YAClB,YAAY,EAAE,EAAE,eAAe,EAAE,QAAQ,EAAE;SAC5C,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,QAAQ,CAAC,GAAG,EAAE;YACZ,IAAI,EAAE,YAAY;YAClB,YAAY,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,kBAAkB,EAAE,QAAQ,EAAE;SACrE,CAAC,CAAC;QACH,oDAAoD;QACpD,MAAM,YAAY,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC3C,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QAE5E,qDAAqD;QACrD,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1E,SAAS,CAAC,GAAG,EAAE,0BAA0B,EAAE,YAAY,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,gBAAgB,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,QAAQ,CAAC,GAAG,EAAE;YACZ,IAAI,EAAE,SAAS;YACf,YAAY,EAAE,EAAE,iBAAiB,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE;SAChE,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QACzD,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,QAAQ,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,eAAe,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QAChF,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,gGAAgG,CAAC,CAAC;QAC5H,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,SAAS,CAAC,GAAG,EAAE,qBAAqB,EAAE,2BAA2B,CAAC,CAAC;QACnE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,YAAY,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QAEnG,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACvD,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC;QACtE,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,QAAQ,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QAC1E,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,QAAQ,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QAC5E,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,QAAQ,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACnE,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,QAAQ,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QAC/E,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/cli/detect.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAA+B,eAAe,EAAE,MAAM,YAAY,CAAC;AA0H1E;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CA0VrE;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAiCzF,CAAC;AAEF;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAU/F;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAyCjD,CAAC;AAEF;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAsB7F"}
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/cli/detect.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAA+B,eAAe,EAAE,MAAM,YAAY,CAAC;AAoF1E;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CA6BrE;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAiCzF,CAAC;AAEF;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAM/F;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAyCjD,CAAC;AAEF;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAsB7F"}
@@ -3,510 +3,87 @@
3
3
  */
4
4
  import path from 'path';
5
5
  import fs from 'fs';
6
- /**
7
- * 모노레포 워크스페이스 경로 감지
8
- * 지원: pnpm-workspace.yaml, package.json workspaces, lerna.json, nx.json, turbo.json
9
- */
10
- function detectWorkspacePaths(projectRoot) {
11
- const workspacePaths = new Set();
12
- // 1. pnpm-workspace.yaml
13
- const pnpmWorkspacePath = path.join(projectRoot, 'pnpm-workspace.yaml');
14
- if (fs.existsSync(pnpmWorkspacePath)) {
15
- try {
16
- const content = fs.readFileSync(pnpmWorkspacePath, 'utf-8');
17
- // 간단한 YAML 파싱 (packages: 배열)
18
- const packagesMatch = content.match(/packages:\s*\n((?:\s*-\s*.+\n?)+)/);
19
- if (packagesMatch) {
20
- const lines = packagesMatch[1].split('\n');
21
- for (const line of lines) {
22
- const match = line.match(/^\s*-\s*['"]?([^'"#\n]+)['"]?\s*$/);
23
- if (match) {
24
- const pattern = match[1].trim();
25
- // glob 패턴 확장 (예: packages/*)
26
- expandGlobPattern(projectRoot, pattern, workspacePaths);
27
- }
28
- }
29
- }
6
+ import { detectInDir, detectHosting, detectCicd } from './detect/matcher.js';
7
+ import { detectWorkspacePaths } from './detect/workspace.js';
8
+ // ── conventional sub-directory names ──────────────────────────────────────
9
+ const CONVENTIONAL_SUBDIRS = ['backend', 'frontend', 'server', 'client', 'api', 'web', 'mobile', 'app'];
10
+ const MONOREPO_FALLBACK_DIRS = ['packages', 'apps', 'libs'];
11
+ // ── helpers ────────────────────────────────────────────────────────────────
12
+ function dedupeDetails(details) {
13
+ details.databases = [...new Set(details.databases)];
14
+ details.stateManagement = [...new Set(details.stateManagement)];
15
+ details.hosting = [...new Set(details.hosting)];
16
+ details.cicd = [...new Set(details.cicd)];
17
+ details.capabilities = [...new Set(details.capabilities)];
18
+ }
19
+ function scanDir(dir, prefix, details, stacks) {
20
+ stacks.push(...detectInDir(dir, prefix, details));
21
+ }
22
+ function scanConventionalSubdirs(projectRoot, details, stacks) {
23
+ for (const sub of CONVENTIONAL_SUBDIRS) {
24
+ const full = path.join(projectRoot, sub);
25
+ if (fs.existsSync(full) && fs.statSync(full).isDirectory()) {
26
+ scanDir(full, sub, details, stacks);
30
27
  }
31
- catch { /* ignore */ }
32
28
  }
33
- // 2. package.json workspaces
34
- const packageJsonPath = path.join(projectRoot, 'package.json');
35
- if (fs.existsSync(packageJsonPath)) {
36
- try {
37
- const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
38
- const workspaces = pkg.workspaces;
39
- if (Array.isArray(workspaces)) {
40
- for (const pattern of workspaces) {
41
- expandGlobPattern(projectRoot, pattern, workspacePaths);
42
- }
43
- }
44
- else if (workspaces?.packages && Array.isArray(workspaces.packages)) {
45
- // yarn workspaces 형식: { packages: [...] }
46
- for (const pattern of workspaces.packages) {
47
- expandGlobPattern(projectRoot, pattern, workspacePaths);
48
- }
49
- }
29
+ }
30
+ function scanWorkspacePaths(projectRoot, workspacePaths, scanned, details, stacks) {
31
+ for (const ws of workspacePaths) {
32
+ if (scanned.has(ws))
33
+ continue;
34
+ scanned.add(ws);
35
+ const full = path.join(projectRoot, ws);
36
+ if (fs.existsSync(full) && fs.statSync(full).isDirectory()) {
37
+ scanDir(full, ws, details, stacks);
50
38
  }
51
- catch { /* ignore */ }
52
39
  }
53
- // 3. lerna.json
54
- const lernaPath = path.join(projectRoot, 'lerna.json');
55
- if (fs.existsSync(lernaPath)) {
40
+ }
41
+ function scanMonorepoFallback(projectRoot, scanned, details, stacks) {
42
+ for (const monoDir of MONOREPO_FALLBACK_DIRS) {
43
+ const monoPath = path.join(projectRoot, monoDir);
44
+ if (!fs.existsSync(monoPath) || !fs.statSync(monoPath).isDirectory())
45
+ continue;
56
46
  try {
57
- const lerna = JSON.parse(fs.readFileSync(lernaPath, 'utf-8'));
58
- const packages = lerna.packages || ['packages/*'];
59
- for (const pattern of packages) {
60
- expandGlobPattern(projectRoot, pattern, workspacePaths);
47
+ const entries = fs.readdirSync(monoPath).filter(f => {
48
+ const fp = path.join(monoPath, f);
49
+ return fs.statSync(fp).isDirectory() && !f.startsWith('.');
50
+ });
51
+ for (const entry of entries) {
52
+ const rel = `${monoDir}/${entry}`;
53
+ if (scanned.has(rel))
54
+ continue;
55
+ scanned.add(rel);
56
+ scanDir(path.join(monoPath, entry), rel, details, stacks);
61
57
  }
62
58
  }
63
59
  catch { /* ignore */ }
64
60
  }
65
- // 4. nx.json (projects 경로 확인)
66
- const nxPath = path.join(projectRoot, 'nx.json');
67
- if (fs.existsSync(nxPath)) {
68
- // nx는 보통 apps/, libs/, packages/ 구조
69
- for (const dir of ['apps', 'libs', 'packages']) {
70
- expandGlobPattern(projectRoot, `${dir}/*`, workspacePaths);
71
- }
72
- }
73
- // 5. turbo.json (pipeline이 있으면 모노레포)
74
- const turboPath = path.join(projectRoot, 'turbo.json');
75
- if (fs.existsSync(turboPath)) {
76
- // turbo는 package.json workspaces를 따르므로 추가 처리 불필요
77
- // 기본 폴더 검사
78
- for (const dir of ['apps', 'packages']) {
79
- expandGlobPattern(projectRoot, `${dir}/*`, workspacePaths);
80
- }
81
- }
82
- return [...workspacePaths];
83
- }
84
- /**
85
- * glob 패턴을 실제 디렉토리 경로로 확장
86
- */
87
- function expandGlobPattern(projectRoot, pattern, paths) {
88
- // 패턴 정규화
89
- const cleanPattern = pattern.replace(/['"]/g, '').trim();
90
- // ! 로 시작하면 제외 패턴 - 무시
91
- if (cleanPattern.startsWith('!'))
92
- return;
93
- // * 없으면 직접 경로
94
- if (!cleanPattern.includes('*')) {
95
- const fullPath = path.join(projectRoot, cleanPattern);
96
- if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {
97
- paths.add(cleanPattern);
98
- }
99
- return;
100
- }
101
- // 단일 레벨 glob 처리 (예: packages/*, apps/*)
102
- if (cleanPattern.endsWith('/*') || cleanPattern.endsWith('/**/')) {
103
- const baseDir = cleanPattern.replace(/\/\*+\/?$/, '');
104
- const basePath = path.join(projectRoot, baseDir);
105
- if (fs.existsSync(basePath) && fs.statSync(basePath).isDirectory()) {
106
- try {
107
- const entries = fs.readdirSync(basePath);
108
- for (const entry of entries) {
109
- if (entry.startsWith('.'))
110
- continue;
111
- const entryPath = path.join(basePath, entry);
112
- if (fs.statSync(entryPath).isDirectory()) {
113
- paths.add(`${baseDir}/${entry}`);
114
- }
115
- }
116
- }
117
- catch { /* ignore */ }
118
- }
119
- }
120
61
  }
62
+ // ── public API ─────────────────────────────────────────────────────────────
121
63
  /**
122
64
  * 프로젝트 기술 스택 감지
123
65
  */
124
66
  export function detectTechStacks(projectRoot) {
125
67
  const stacks = [];
126
- const details = { databases: [], stateManagement: [], hosting: [], cicd: [], capabilities: [] };
127
- const detectInDir = (dir, prefix = '') => {
128
- const detected = [];
129
- // Node.js / TypeScript
130
- if (fs.existsSync(path.join(dir, 'package.json'))) {
131
- try {
132
- const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8'));
133
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
134
- // 프레임워크 감지 (우선순위: 특수 프레임워크 → 범용 프레임워크)
135
- // Desktop/Mobile 프레임워크 (최우선)
136
- if (deps['@tauri-apps/cli'] || deps['@tauri-apps/api']) {
137
- detected.push({ type: 'typescript-tauri', path: prefix });
138
- }
139
- else if (deps['electron']) {
140
- detected.push({ type: 'typescript-electron', path: prefix });
141
- }
142
- else if (deps['react-native']) {
143
- detected.push({ type: 'typescript-react-native', path: prefix });
144
- }
145
- // 풀스택/SSR 프레임워크
146
- else if (deps['next']) {
147
- detected.push({ type: 'typescript-nextjs', path: prefix });
148
- }
149
- else if (deps['nuxt'] || deps['nuxt3']) {
150
- detected.push({ type: 'typescript-nuxt', path: prefix });
151
- }
152
- else if (deps['astro']) {
153
- detected.push({ type: 'typescript-astro', path: prefix });
154
- }
155
- // 프론트엔드 프레임워크
156
- else if (deps['@angular/core']) {
157
- detected.push({ type: 'typescript-angular', path: prefix });
158
- }
159
- else if (deps['svelte']) {
160
- detected.push({ type: 'typescript-svelte', path: prefix });
161
- }
162
- else if (deps['vue']) {
163
- detected.push({ type: 'typescript-vue', path: prefix });
164
- }
165
- else if (deps['react']) {
166
- detected.push({ type: 'typescript-react', path: prefix });
167
- }
168
- // 백엔드 프레임워크
169
- else if (deps['@nestjs/core']) {
170
- detected.push({ type: 'typescript-nestjs', path: prefix });
171
- }
172
- else if (deps['express'] || deps['fastify'] || deps['koa'] || deps['hono']) {
173
- detected.push({ type: 'typescript-node', path: prefix });
174
- }
175
- // 기본 Node.js
176
- else if (pkg.name) {
177
- detected.push({ type: 'typescript-node', path: prefix });
178
- }
179
- // DB 감지
180
- if (deps['pg'] || deps['postgres'] || deps['@prisma/client'])
181
- details.databases.push('PostgreSQL');
182
- if (deps['mysql'] || deps['mysql2'])
183
- details.databases.push('MySQL');
184
- if (deps['mongodb'] || deps['mongoose'])
185
- details.databases.push('MongoDB');
186
- if (deps['redis'] || deps['ioredis'])
187
- details.databases.push('Redis');
188
- if (deps['sqlite3'] || deps['better-sqlite3'])
189
- details.databases.push('SQLite');
190
- if (deps['typeorm'])
191
- details.databases.push('TypeORM');
192
- if (deps['prisma'] || deps['@prisma/client'])
193
- details.databases.push('Prisma');
194
- if (deps['drizzle-orm'])
195
- details.databases.push('Drizzle');
196
- if (deps['sequelize'])
197
- details.databases.push('Sequelize');
198
- // 상태관리 감지
199
- if (deps['redux'] || deps['@reduxjs/toolkit'])
200
- details.stateManagement.push('Redux');
201
- if (deps['zustand'])
202
- details.stateManagement.push('Zustand');
203
- if (deps['jotai'])
204
- details.stateManagement.push('Jotai');
205
- if (deps['recoil'])
206
- details.stateManagement.push('Recoil');
207
- if (deps['mobx'])
208
- details.stateManagement.push('MobX');
209
- if (deps['@tanstack/react-query'] || deps['react-query'])
210
- details.stateManagement.push('React Query');
211
- if (deps['swr'])
212
- details.stateManagement.push('SWR');
213
- if (deps['pinia'])
214
- details.stateManagement.push('Pinia');
215
- if (deps['vuex'])
216
- details.stateManagement.push('Vuex');
217
- // Capability 감지: Commerce
218
- if (deps['stripe'] || deps['@stripe/stripe-js'] || deps['@stripe/react-stripe-js']
219
- || deps['@shopify/shopify-api'] || deps['shopify-api-node']
220
- || deps['@medusajs/medusa'] || deps['@paypal/checkout-server-sdk']
221
- || deps['toss-payments'] || deps['iamport-rest-client']) {
222
- details.capabilities.push('commerce');
223
- }
224
- // Capability 감지: Video
225
- if (deps['fluent-ffmpeg'] || deps['@ffmpeg/ffmpeg'] || deps['ffmpeg-static']
226
- || deps['remotion'] || deps['video.js'] || deps['@mux/mux-node']) {
227
- details.capabilities.push('video');
228
- }
229
- // Capability 감지: Event Automation
230
- if (deps['@notionhq/client'] || deps['aligo-smartsms'] || deps['nodemailer']
231
- || deps['python-pptx'] || deps['google-generativeai']) {
232
- // Check for event-specific directory structures
233
- if (fs.existsSync(path.join(dir, 'agents')) || fs.existsSync(path.join(dir, 'schedules'))
234
- || fs.existsSync(path.join(dir, '.event_state.json')) || fs.existsSync(path.join(dir, 'prompts'))) {
235
- details.capabilities.push('event-automation');
236
- }
237
- }
238
- }
239
- catch { /* ignore: optional operation */ }
240
- }
241
- // Python
242
- if (fs.existsSync(path.join(dir, 'pyproject.toml'))) {
243
- try {
244
- const content = fs.readFileSync(path.join(dir, 'pyproject.toml'), 'utf-8');
245
- if (content.includes('fastapi'))
246
- detected.push({ type: 'python-fastapi', path: prefix });
247
- else if (content.includes('django'))
248
- detected.push({ type: 'python-django', path: prefix });
249
- else
250
- detected.push({ type: 'python', path: prefix });
251
- if (content.includes('psycopg') || content.includes('asyncpg'))
252
- details.databases.push('PostgreSQL');
253
- if (content.includes('pymongo'))
254
- details.databases.push('MongoDB');
255
- if (content.includes('sqlalchemy'))
256
- details.databases.push('SQLAlchemy');
257
- if (content.includes('prisma'))
258
- details.databases.push('Prisma');
259
- // Python capability 감지
260
- if (content.includes('stripe') || content.includes('shopify') || content.includes('saleor')) {
261
- details.capabilities.push('commerce');
262
- }
263
- if (content.includes('moviepy') || content.includes('ffmpeg') || content.includes('opencv') || content.includes('vidgear')) {
264
- details.capabilities.push('video');
265
- }
266
- if (content.includes('notion-client') || content.includes('python-pptx') || content.includes('google-generativeai')
267
- || content.includes('aligo')) {
268
- if (fs.existsSync(path.join(dir, 'agents')) || fs.existsSync(path.join(dir, 'schedules'))
269
- || fs.existsSync(path.join(dir, '.event_state.json')) || fs.existsSync(path.join(dir, 'prompts'))) {
270
- details.capabilities.push('event-automation');
271
- }
272
- }
273
- }
274
- catch { /* ignore: optional operation */ }
275
- }
276
- else if (fs.existsSync(path.join(dir, 'requirements.txt'))) {
277
- try {
278
- const content = fs.readFileSync(path.join(dir, 'requirements.txt'), 'utf-8');
279
- if (content.includes('fastapi'))
280
- detected.push({ type: 'python-fastapi', path: prefix });
281
- else if (content.includes('django'))
282
- detected.push({ type: 'python-django', path: prefix });
283
- else
284
- detected.push({ type: 'python', path: prefix });
285
- if (content.includes('psycopg') || content.includes('asyncpg'))
286
- details.databases.push('PostgreSQL');
287
- if (content.includes('pymongo'))
288
- details.databases.push('MongoDB');
289
- if (content.includes('sqlalchemy'))
290
- details.databases.push('SQLAlchemy');
291
- // Python capability 감지
292
- if (content.includes('stripe') || content.includes('shopify') || content.includes('saleor')) {
293
- details.capabilities.push('commerce');
294
- }
295
- if (content.includes('moviepy') || content.includes('ffmpeg') || content.includes('opencv') || content.includes('vidgear')) {
296
- details.capabilities.push('video');
297
- }
298
- if (content.includes('notion-client') || content.includes('python-pptx') || content.includes('google-generativeai')
299
- || content.includes('aligo')) {
300
- if (fs.existsSync(path.join(dir, 'agents')) || fs.existsSync(path.join(dir, 'schedules'))
301
- || fs.existsSync(path.join(dir, '.event_state.json')) || fs.existsSync(path.join(dir, 'prompts'))) {
302
- details.capabilities.push('event-automation');
303
- }
304
- }
305
- }
306
- catch { /* ignore: optional operation */ }
307
- }
308
- // Flutter / Dart
309
- if (fs.existsSync(path.join(dir, 'pubspec.yaml'))) {
310
- detected.push({ type: 'dart-flutter', path: prefix });
311
- try {
312
- const content = fs.readFileSync(path.join(dir, 'pubspec.yaml'), 'utf-8');
313
- if (content.includes('flutter_riverpod') || content.includes('riverpod'))
314
- details.stateManagement.push('Riverpod');
315
- else if (content.includes('provider'))
316
- details.stateManagement.push('Provider');
317
- if (content.includes('bloc'))
318
- details.stateManagement.push('BLoC');
319
- if (content.includes('getx') || content.includes('get:'))
320
- details.stateManagement.push('GetX');
321
- }
322
- catch { /* ignore: optional operation */ }
323
- }
324
- // Go
325
- if (fs.existsSync(path.join(dir, 'go.mod'))) {
326
- detected.push({ type: 'go', path: prefix });
327
- try {
328
- const content = fs.readFileSync(path.join(dir, 'go.mod'), 'utf-8');
329
- if (content.includes('pgx') || content.includes('pq'))
330
- details.databases.push('PostgreSQL');
331
- if (content.includes('go-redis'))
332
- details.databases.push('Redis');
333
- if (content.includes('mongo-driver'))
334
- details.databases.push('MongoDB');
335
- }
336
- catch { /* ignore: optional operation */ }
337
- }
338
- // Rust
339
- if (fs.existsSync(path.join(dir, 'Cargo.toml'))) {
340
- detected.push({ type: 'rust', path: prefix });
341
- try {
342
- const content = fs.readFileSync(path.join(dir, 'Cargo.toml'), 'utf-8');
343
- if (content.includes('sqlx') || content.includes('diesel'))
344
- details.databases.push('PostgreSQL');
345
- if (content.includes('mongodb'))
346
- details.databases.push('MongoDB');
347
- }
348
- catch { /* ignore: optional operation */ }
349
- }
350
- // Java / Kotlin
351
- if (fs.existsSync(path.join(dir, 'build.gradle')) || fs.existsSync(path.join(dir, 'build.gradle.kts'))) {
352
- try {
353
- const gradleFile = fs.existsSync(path.join(dir, 'build.gradle.kts'))
354
- ? path.join(dir, 'build.gradle.kts')
355
- : path.join(dir, 'build.gradle');
356
- const content = fs.readFileSync(gradleFile, 'utf-8');
357
- if (content.includes('com.android'))
358
- detected.push({ type: 'kotlin-android', path: prefix });
359
- else if (content.includes('kotlin'))
360
- detected.push({ type: 'kotlin', path: prefix });
361
- else if (content.includes('spring'))
362
- detected.push({ type: 'java-spring', path: prefix });
363
- else
364
- detected.push({ type: 'java', path: prefix });
365
- if (content.includes('postgresql'))
366
- details.databases.push('PostgreSQL');
367
- if (content.includes('mysql'))
368
- details.databases.push('MySQL');
369
- if (content.includes('jpa') || content.includes('hibernate'))
370
- details.databases.push('JPA/Hibernate');
371
- }
372
- catch { /* ignore: optional operation */ }
373
- }
374
- else if (fs.existsSync(path.join(dir, 'pom.xml'))) {
375
- try {
376
- const content = fs.readFileSync(path.join(dir, 'pom.xml'), 'utf-8');
377
- if (content.includes('spring'))
378
- detected.push({ type: 'java-spring', path: prefix });
379
- else
380
- detected.push({ type: 'java', path: prefix });
381
- if (content.includes('postgresql'))
382
- details.databases.push('PostgreSQL');
383
- if (content.includes('mysql'))
384
- details.databases.push('MySQL');
385
- }
386
- catch { /* ignore: optional operation */ }
387
- }
388
- // Swift / iOS
389
- if (fs.existsSync(path.join(dir, 'Package.swift')) ||
390
- fs.readdirSync(dir).some(f => f.endsWith('.xcodeproj') || f.endsWith('.xcworkspace'))) {
391
- detected.push({ type: 'swift-ios', path: prefix });
392
- }
393
- // Ruby / Rails
394
- if (fs.existsSync(path.join(dir, 'Gemfile'))) {
395
- try {
396
- const content = fs.readFileSync(path.join(dir, 'Gemfile'), 'utf-8');
397
- if (content.includes('rails')) {
398
- detected.push({ type: 'ruby-rails', path: prefix });
399
- }
400
- if (content.includes('pg'))
401
- details.databases.push('PostgreSQL');
402
- if (content.includes('mysql2'))
403
- details.databases.push('MySQL');
404
- if (content.includes('sqlite3'))
405
- details.databases.push('SQLite');
406
- }
407
- catch { /* ignore: optional operation */ }
408
- }
409
- // C# / Unity
410
- if (fs.readdirSync(dir).some(f => f.endsWith('.csproj') || f.endsWith('.sln'))) {
411
- // Unity 프로젝트 판별: ProjectSettings/ProjectVersion.txt 존재 여부
412
- if (fs.existsSync(path.join(dir, 'ProjectSettings', 'ProjectVersion.txt')) ||
413
- fs.existsSync(path.join(dir, 'Assets'))) {
414
- detected.push({ type: 'csharp-unity', path: prefix });
415
- }
416
- }
417
- // GDScript / Godot
418
- if (fs.existsSync(path.join(dir, 'project.godot')) ||
419
- fs.readdirSync(dir).some(f => f.endsWith('.gd'))) {
420
- detected.push({ type: 'gdscript-godot', path: prefix });
421
- }
422
- return detected;
68
+ const details = {
69
+ databases: [], stateManagement: [], hosting: [], cicd: [], capabilities: [],
423
70
  };
424
- // CI/CD 감지
425
- if (fs.existsSync(path.join(projectRoot, '.github', 'workflows'))) {
426
- details.cicd.push('GitHub Actions');
427
- }
428
- if (fs.existsSync(path.join(projectRoot, '.gitlab-ci.yml'))) {
429
- details.cicd.push('GitLab CI');
430
- }
431
- if (fs.existsSync(path.join(projectRoot, 'Jenkinsfile'))) {
432
- details.cicd.push('Jenkins');
433
- }
434
- if (fs.existsSync(path.join(projectRoot, '.circleci'))) {
435
- details.cicd.push('CircleCI');
436
- }
437
- // Hosting 감지
438
- if (fs.existsSync(path.join(projectRoot, 'vercel.json')) ||
439
- fs.existsSync(path.join(projectRoot, '.vercel'))) {
440
- details.hosting.push('Vercel');
441
- }
442
- if (fs.existsSync(path.join(projectRoot, 'netlify.toml'))) {
443
- details.hosting.push('Netlify');
444
- }
445
- if (fs.existsSync(path.join(projectRoot, 'app.yaml')) ||
446
- fs.existsSync(path.join(projectRoot, 'cloudbuild.yaml'))) {
447
- details.hosting.push('Google Cloud');
448
- }
449
- if (fs.existsSync(path.join(projectRoot, 'Dockerfile')) ||
450
- fs.existsSync(path.join(projectRoot, 'docker-compose.yml'))) {
451
- details.hosting.push('Docker');
452
- }
453
- if (fs.existsSync(path.join(projectRoot, 'fly.toml'))) {
454
- details.hosting.push('Fly.io');
455
- }
456
- if (fs.existsSync(path.join(projectRoot, 'railway.json'))) {
457
- details.hosting.push('Railway');
458
- }
459
- // 루트 디렉토리 검사
460
- stacks.push(...detectInDir(projectRoot));
461
- // 1레벨 하위 폴더 검사 (전통적인 폴더 구조)
462
- const subDirs = ['backend', 'frontend', 'server', 'client', 'api', 'web', 'mobile', 'app'];
463
- for (const subDir of subDirs) {
464
- const subPath = path.join(projectRoot, subDir);
465
- if (fs.existsSync(subPath) && fs.statSync(subPath).isDirectory()) {
466
- stacks.push(...detectInDir(subPath, subDir));
467
- }
468
- }
469
- // 모노레포 워크스페이스 감지 및 검사
71
+ // Root
72
+ scanDir(projectRoot, '', details, stacks);
73
+ // Conventional sub-directories (backend/, frontend/, …)
74
+ scanConventionalSubdirs(projectRoot, details, stacks);
75
+ // Monorepo workspace paths
470
76
  const workspacePaths = detectWorkspacePaths(projectRoot);
471
- const scannedPaths = new Set();
472
- for (const workspacePath of workspacePaths) {
473
- // 이미 검사한 경로는 건너뛰기
474
- if (scannedPaths.has(workspacePath))
475
- continue;
476
- scannedPaths.add(workspacePath);
477
- const fullPath = path.join(projectRoot, workspacePath);
478
- if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {
479
- stacks.push(...detectInDir(fullPath, workspacePath));
480
- }
481
- }
482
- // 워크스페이스 설정이 없으면 기본 폴더 검사 (fallback)
77
+ const scanned = new Set();
78
+ scanWorkspacePaths(projectRoot, workspacePaths, scanned, details, stacks);
79
+ // Fallback: scan packages/, apps/, libs/ if no workspace config found
483
80
  if (workspacePaths.length === 0) {
484
- for (const monoDir of ['packages', 'apps', 'libs']) {
485
- const monoPath = path.join(projectRoot, monoDir);
486
- if (fs.existsSync(monoPath) && fs.statSync(monoPath).isDirectory()) {
487
- try {
488
- const subPackages = fs.readdirSync(monoPath).filter(f => {
489
- const fullPath = path.join(monoPath, f);
490
- return fs.statSync(fullPath).isDirectory() && !f.startsWith('.');
491
- });
492
- for (const pkg of subPackages) {
493
- const pkgPath = `${monoDir}/${pkg}`;
494
- if (!scannedPaths.has(pkgPath)) {
495
- scannedPaths.add(pkgPath);
496
- stacks.push(...detectInDir(path.join(monoPath, pkg), pkgPath));
497
- }
498
- }
499
- }
500
- catch { /* ignore */ }
501
- }
502
- }
81
+ scanMonorepoFallback(projectRoot, scanned, details, stacks);
503
82
  }
504
- // 중복 제거
505
- details.databases = [...new Set(details.databases)];
506
- details.stateManagement = [...new Set(details.stateManagement)];
507
- details.hosting = [...new Set(details.hosting)];
508
- details.cicd = [...new Set(details.cicd)];
509
- details.capabilities = [...new Set(details.capabilities)];
83
+ // Project-root-level details (hosting, CI/CD)
84
+ details.hosting.push(...detectHosting(projectRoot));
85
+ details.cicd.push(...detectCicd(projectRoot));
86
+ dedupeDetails(details);
510
87
  return { stacks, details };
511
88
  }
512
89
  /**
@@ -552,9 +129,8 @@ export const STACK_NAMES = {
552
129
  export function getLanguageRulesForStacks(stacks) {
553
130
  const ruleFiles = [];
554
131
  for (const stack of stacks) {
555
- if (STACK_NAMES[stack.type]) {
132
+ if (STACK_NAMES[stack.type])
556
133
  ruleFiles.push(`${stack.type}.md`);
557
- }
558
134
  }
559
135
  return [...new Set(ruleFiles)].join(', ');
560
136
  }
@@ -601,7 +177,7 @@ export const LANGUAGE_RULES = {
601
177
  - No force unwrapping → use guard let / if let
602
178
  - Prefer protocol-oriented programming
603
179
  - Prefer value types (struct)
604
- - Watch memory management with @escaping closures`
180
+ - Watch memory management with @escaping closures`,
605
181
  };
606
182
  /**
607
183
  * 스택에 맞는 언어 규칙 내용 반환