@ripla/godd-mcp 1.0.4-canary.8 → 1.0.4

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 (348) hide show
  1. package/README.md +89 -94
  2. package/dist/godd.cjs +349 -233
  3. package/dist/godd.js +2193 -838
  4. package/dist/index.js +152 -122
  5. package/{notes-api → notes/api}/.env.example +6 -1
  6. package/{notes-api → notes/api}/README.md +6 -5
  7. package/{notes-api → notes/api}/app/main.py +29 -2
  8. package/{notes-api → notes/api}/app/routers/auth.py +6 -1
  9. package/{notes-api → notes/api}/app/routers/issues.py +157 -1
  10. package/{notes-api → notes/api}/app/routers/pr.py +2 -2
  11. package/{notes-api → notes/api}/app/services/github_app_token.py +32 -1
  12. package/{notes-api → notes/api}/app/services/github_issues.py +101 -11
  13. package/{notes-api → notes/api}/app/services/github_pr.py +6 -1
  14. package/{notes-api → notes/api}/tests/test_github_app_token.py +49 -1
  15. package/{notes-api → notes/api}/tests/test_github_service.py +42 -0
  16. package/notes/api/tests/test_health.py +52 -0
  17. package/{notes-api → notes/api}/tests/test_issues.py +216 -0
  18. package/{notes-api → notes/api}/tests/test_pr.py +24 -0
  19. package/{notes-api → notes/api}/tests/test_pr_chain.py +19 -0
  20. package/{notes-api → notes/api}/uv.lock +178 -2
  21. package/notes/app/DESIGN.md +273 -0
  22. package/{notes-app → notes/app}/README.md +4 -1
  23. package/notes/app/docker/Dockerfile.e2e +19 -0
  24. package/notes/app/docker/Dockerfile.prod +15 -0
  25. package/notes/app/docker/Dockerfile.test +17 -0
  26. package/notes/app/e2e/file-tree.spec.ts +26 -0
  27. package/notes/app/e2e/file-view.spec.ts +25 -0
  28. package/notes/app/e2e/fixtures.ts +28 -0
  29. package/{notes-app → notes/app}/e2e/global-setup.ts +5 -7
  30. package/notes/app/e2e/login.spec.ts +25 -0
  31. package/notes/app/e2e/pages/login-page.ts +29 -0
  32. package/notes/app/e2e/pages/main-page.ts +29 -0
  33. package/notes/app/e2e/panel-resize-handle.spec.ts +213 -0
  34. package/{notes-app → notes/app}/eslint.config.js +9 -0
  35. package/{notes-app → notes/app}/package.json +0 -3
  36. package/{notes-app → notes/app}/pnpm-workspace.yaml +1 -0
  37. package/notes/app/src/components/CommentPanel.test.tsx +194 -0
  38. package/{notes-app → notes/app}/src/components/CommentPanel.tsx +119 -10
  39. package/notes/app/src/components/CommentSelectionPopover.tsx +26 -0
  40. package/notes/app/src/components/EditToolbar.test.tsx +102 -0
  41. package/{notes-app → notes/app}/src/components/EditToolbar.tsx +5 -54
  42. package/notes/app/src/components/FileInfoPanel.branch-tab.test.tsx +250 -0
  43. package/notes/app/src/components/FileInfoPanel.history-tab.test.tsx +146 -0
  44. package/notes/app/src/components/FileInfoPanel.test.tsx +101 -0
  45. package/{notes-app → notes/app}/src/components/FileInfoPanel.tsx +135 -20
  46. package/notes/app/src/components/IssueCommentThread.test.tsx +93 -0
  47. package/notes/app/src/components/IssueCommentThread.tsx +156 -0
  48. package/notes/app/src/components/IssueDetailView.test.tsx +1118 -0
  49. package/notes/app/src/components/IssueDetailView.tsx +476 -0
  50. package/notes/app/src/components/IssueList.test.tsx +545 -0
  51. package/notes/app/src/components/IssueList.tsx +374 -0
  52. package/{notes-app → notes/app}/src/components/LabelSelector.test.tsx +94 -8
  53. package/notes/app/src/components/LabelSelector.tsx +452 -0
  54. package/{notes-app → notes/app}/src/components/MarkdownEditor.tsx +140 -2
  55. package/notes/app/src/components/PanelResizeHandle.test.tsx +481 -0
  56. package/notes/app/src/components/PanelResizeHandle.tsx +96 -0
  57. package/{notes-app → notes/app}/src/hooks/useEditSession.test.ts +171 -23
  58. package/{notes-app → notes/app}/src/hooks/useEditSession.ts +49 -62
  59. package/notes/app/src/hooks/useMdUpLayout.test.ts +10 -0
  60. package/notes/app/src/hooks/useMdUpLayout.ts +27 -0
  61. package/notes/app/src/hooks/usePeriodicSync.test.ts +244 -0
  62. package/notes/app/src/hooks/usePeriodicSync.ts +83 -0
  63. package/notes/app/src/hooks/useResizablePanelWidth.test.ts +251 -0
  64. package/notes/app/src/hooks/useResizablePanelWidth.ts +167 -0
  65. package/{notes-app → notes/app}/src/lib/api.test.ts +81 -1
  66. package/{notes-app → notes/app}/src/lib/api.ts +98 -4
  67. package/notes/app/src/lib/commentAnchor.test.ts +22 -0
  68. package/notes/app/src/lib/commentAnchor.ts +28 -0
  69. package/notes/app/src/lib/commentHighlight.test.ts +110 -0
  70. package/notes/app/src/lib/commentHighlight.ts +44 -0
  71. package/notes/app/src/lib/github-sync.test.ts +126 -0
  72. package/notes/app/src/lib/github-sync.ts +89 -0
  73. package/notes/app/src/lib/issue-list-utils.test.ts +275 -0
  74. package/notes/app/src/lib/issue-list-utils.ts +135 -0
  75. package/notes/app/src/lib/issue-mobile-layout.test.ts +17 -0
  76. package/notes/app/src/lib/issue-mobile-layout.ts +20 -0
  77. package/notes/app/src/lib/issue-url-sync.test.ts +73 -0
  78. package/notes/app/src/lib/issue-url-sync.ts +43 -0
  79. package/notes/app/src/lib/main-page-default-view.test.ts +37 -0
  80. package/notes/app/src/lib/main-page-default-view.ts +15 -0
  81. package/notes/app/src/lib/resizable-panel.test.ts +199 -0
  82. package/notes/app/src/lib/resizable-panel.ts +136 -0
  83. package/notes/app/src/pages/LoginPage.test.tsx +17 -0
  84. package/notes/app/src/pages/MainPage.branch-modal.test.tsx +226 -0
  85. package/notes/app/src/pages/MainPage.issue-sync.test.tsx +145 -0
  86. package/notes/app/src/pages/MainPage.sidebar.test.tsx +361 -0
  87. package/{notes-app → notes/app}/src/pages/MainPage.tsx +406 -61
  88. package/{notes-app → notes/app}/src/pages/SettingsPage.test.tsx +32 -23
  89. package/{notes-app → notes/app}/src/pages/UserManagementPage.test.tsx +8 -18
  90. package/notes/app/src/test/mocks.ts +156 -0
  91. package/notes/app/src/test/render.tsx +27 -0
  92. package/package.json +3 -5
  93. package/notes-api/tests/test_health.py +0 -11
  94. package/notes-app/docker/Dockerfile.e2e +0 -14
  95. package/notes-app/docker/Dockerfile.prod +0 -12
  96. package/notes-app/docker/Dockerfile.test +0 -12
  97. package/notes-app/e2e/file-tree.spec.ts +0 -29
  98. package/notes-app/e2e/file-view.spec.ts +0 -24
  99. package/notes-app/e2e/login.spec.ts +0 -32
  100. package/notes-app/src/components/IssueDetailView.test.tsx +0 -201
  101. package/notes-app/src/components/IssueDetailView.tsx +0 -318
  102. package/notes-app/src/components/IssueList.test.tsx +0 -93
  103. package/notes-app/src/components/IssueList.tsx +0 -169
  104. package/notes-app/src/components/LabelSelector.tsx +0 -276
  105. package/notes-app/src/pages/LoginPage.test.tsx +0 -29
  106. package/stacks/django-vue.yaml +0 -33
  107. package/stacks/fastapi-react.yaml +0 -160
  108. package/stacks/flutter-firebase.yaml +0 -58
  109. package/stacks/nextjs-prisma.yaml +0 -33
  110. package/stacks/schema.json +0 -144
  111. package/templates/agents/architect.hbs +0 -183
  112. package/templates/agents/auto-documenter.hbs +0 -36
  113. package/templates/agents/backend-developer.hbs +0 -175
  114. package/templates/agents/code-reviewer.hbs +0 -29
  115. package/templates/agents/database-developer.hbs +0 -52
  116. package/templates/agents/debug-specialist.hbs +0 -39
  117. package/templates/agents/environment-setup.hbs +0 -41
  118. package/templates/agents/frontend-developer.hbs +0 -45
  119. package/templates/agents/integration-qa.hbs +0 -26
  120. package/templates/agents/performance-qa.hbs +0 -24
  121. package/templates/agents/pr-writer.hbs +0 -53
  122. package/templates/agents/quality-lead.hbs +0 -26
  123. package/templates/agents/requirements-analyst.hbs +0 -26
  124. package/templates/agents/security-analyst.hbs +0 -21
  125. package/templates/agents/security-reviewer.hbs +0 -20
  126. package/templates/agents/ssot-updater.hbs +0 -45
  127. package/templates/agents/technical-writer.hbs +0 -28
  128. package/templates/agents/unit-test-engineer.hbs +0 -19
  129. package/templates/agents/user-clone.hbs +0 -43
  130. package/templates/github-actions/aws/deploy-notes.yml.hbs +0 -117
  131. package/templates/github-actions/azure/deploy-notes.yml.hbs +0 -78
  132. package/templates/github-actions/gcp/deploy-notes.yml.hbs +0 -76
  133. package/templates/github-actions/vercel-railway/deploy-notes.yml.hbs +0 -49
  134. package/templates/mindsets/agent-stdio.hbs +0 -51
  135. package/templates/mindsets/dart.hbs +0 -39
  136. package/templates/mindsets/ddd-clean-architecture.hbs +0 -52
  137. package/templates/mindsets/electron.hbs +0 -41
  138. package/templates/mindsets/fastapi.hbs +0 -40
  139. package/templates/mindsets/flutter.hbs +0 -56
  140. package/templates/mindsets/kotlin.hbs +0 -46
  141. package/templates/mindsets/postgresql.hbs +0 -39
  142. package/templates/mindsets/python.hbs +0 -39
  143. package/templates/mindsets/react.hbs +0 -51
  144. package/templates/mindsets/swift.hbs +0 -46
  145. package/templates/mindsets/terraform.hbs +0 -53
  146. package/templates/mindsets/typescript.hbs +0 -34
  147. package/templates/mindsets/vite.hbs +0 -37
  148. package/templates/notes-compose.yml +0 -82
  149. package/templates/partials/cogito-geo.hbs +0 -84
  150. package/templates/partials/cogito-godd.hbs +0 -22
  151. package/templates/partials/cogito-grammar.hbs +0 -105
  152. package/templates/partials/cogito-particle.hbs +0 -76
  153. package/templates/partials/cogito-root-ai.hbs +0 -79
  154. package/templates/partials/cogito-root-arch.hbs +0 -75
  155. package/templates/partials/cogito-root-core.hbs +0 -75
  156. package/templates/partials/cogito-root-daily.hbs +0 -58
  157. package/templates/partials/cogito-root-dev.hbs +0 -71
  158. package/templates/partials/cogito-root-devops.hbs +0 -71
  159. package/templates/partials/cogito-root-framework.hbs +0 -80
  160. package/templates/partials/cogito-root-lang.hbs +0 -74
  161. package/templates/partials/godd-core.hbs +0 -50
  162. package/templates/partials/project-context.hbs +0 -52
  163. package/templates/partials/ssot-header.hbs +0 -40
  164. package/templates/partials/ssot-rules.hbs +0 -30
  165. package/templates/prompts/adr.hbs +0 -39
  166. package/templates/prompts/agent-dev-workflow.hbs +0 -34
  167. package/templates/prompts/analyze-report.hbs +0 -36
  168. package/templates/prompts/auto-documentation.hbs +0 -37
  169. package/templates/prompts/check.hbs +0 -52
  170. package/templates/prompts/commit.hbs +0 -96
  171. package/templates/prompts/dev.hbs +0 -127
  172. package/templates/prompts/docs-init.hbs +0 -101
  173. package/templates/prompts/docs-update.hbs +0 -51
  174. package/templates/prompts/git-workflow.hbs +0 -125
  175. package/templates/prompts/impact-analysis.hbs +0 -43
  176. package/templates/prompts/install.hbs +0 -40
  177. package/templates/prompts/new-branch.hbs +0 -61
  178. package/templates/prompts/notes-deploy.hbs +0 -138
  179. package/templates/prompts/pr-analyze.hbs +0 -44
  180. package/templates/prompts/pr-create.hbs +0 -125
  181. package/templates/prompts/push-execute.hbs +0 -83
  182. package/templates/prompts/push.hbs +0 -24
  183. package/templates/prompts/release-notes.hbs +0 -38
  184. package/templates/prompts/requirements-to-business-flow.hbs +0 -29
  185. package/templates/prompts/requirements-to-tickets.hbs +0 -26
  186. package/templates/prompts/review.hbs +0 -126
  187. package/templates/prompts/setup.hbs +0 -247
  188. package/templates/prompts/spec.hbs +0 -35
  189. package/templates/prompts/specification-to-tickets.hbs +0 -24
  190. package/templates/prompts/submit.hbs +0 -143
  191. package/templates/prompts/sync.hbs +0 -63
  192. package/templates/prompts/test-plan.hbs +0 -56
  193. package/templates/scripts/aws/terraform-backend-setup.sh.hbs +0 -149
  194. package/templates/terraform/aws/alb.tf.hbs +0 -58
  195. package/templates/terraform/aws/backend.tf.hbs +0 -10
  196. package/templates/terraform/aws/cloudfront.tf.hbs +0 -96
  197. package/templates/terraform/aws/ecr.tf.hbs +0 -30
  198. package/templates/terraform/aws/ecs.tf.hbs +0 -142
  199. package/templates/terraform/aws/iam.tf.hbs +0 -88
  200. package/templates/terraform/aws/local.tf.hbs +0 -34
  201. package/templates/terraform/aws/output.tf.hbs +0 -49
  202. package/templates/terraform/aws/provider.tf.hbs +0 -26
  203. package/templates/terraform/aws/rds.tf.hbs +0 -38
  204. package/templates/terraform/aws/s3.tf.hbs +0 -34
  205. package/templates/terraform/aws/secrets.tf.hbs +0 -53
  206. package/templates/terraform/aws/security_groups.tf.hbs +0 -81
  207. package/templates/terraform/aws/vpc.tf.hbs +0 -63
  208. package/templates/terraform/azure/backend.tf.hbs +0 -8
  209. package/templates/terraform/azure/iam.tf.hbs +0 -36
  210. package/templates/terraform/azure/main.tf.hbs +0 -203
  211. package/templates/terraform/azure/output.tf.hbs +0 -19
  212. package/templates/terraform/azure/provider.tf.hbs +0 -19
  213. package/templates/terraform/gcp/backend.tf.hbs +0 -6
  214. package/templates/terraform/gcp/iam.tf.hbs +0 -47
  215. package/templates/terraform/gcp/main.tf.hbs +0 -175
  216. package/templates/terraform/gcp/output.tf.hbs +0 -15
  217. package/templates/terraform/gcp/provider.tf.hbs +0 -19
  218. package/templates/terraform/vercel-railway/main.tf.hbs +0 -64
  219. package/templates/terraform/vercel-railway/output.tf.hbs +0 -11
  220. /package/{notes-api → notes/api}/.dockerignore +0 -0
  221. /package/{notes-api → notes/api}/alembic/README +0 -0
  222. /package/{notes-api → notes/api}/alembic/env.py +0 -0
  223. /package/{notes-api → notes/api}/alembic/script.py.mako +0 -0
  224. /package/{notes-api → notes/api}/alembic/versions/001_initial_notes_tables.py +0 -0
  225. /package/{notes-api → notes/api}/alembic/versions/002_add_performance_indexes.py +0 -0
  226. /package/{notes-api → notes/api}/alembic/versions/003_add_yjs_documents.py +0 -0
  227. /package/{notes-api → notes/api}/alembic/versions/004_add_comments.py +0 -0
  228. /package/{notes-api → notes/api}/alembic/versions/005_add_github_username.py +0 -0
  229. /package/{notes-api → notes/api}/alembic/versions/006_add_check_constraints.py +0 -0
  230. /package/{notes-api → notes/api}/alembic.ini +0 -0
  231. /package/{notes-api → notes/api}/app/__init__.py +0 -0
  232. /package/{notes-api → notes/api}/app/config.py +0 -0
  233. /package/{notes-api → notes/api}/app/database.py +0 -0
  234. /package/{notes-api → notes/api}/app/http_client.py +0 -0
  235. /package/{notes-api → notes/api}/app/models/__init__.py +0 -0
  236. /package/{notes-api → notes/api}/app/models/audit.py +0 -0
  237. /package/{notes-api → notes/api}/app/models/base.py +0 -0
  238. /package/{notes-api → notes/api}/app/models/collab.py +0 -0
  239. /package/{notes-api → notes/api}/app/models/comment.py +0 -0
  240. /package/{notes-api → notes/api}/app/models/session.py +0 -0
  241. /package/{notes-api → notes/api}/app/models/setting.py +0 -0
  242. /package/{notes-api → notes/api}/app/models/user.py +0 -0
  243. /package/{notes-api → notes/api}/app/path_validation.py +0 -0
  244. /package/{notes-api → notes/api}/app/routers/__init__.py +0 -0
  245. /package/{notes-api → notes/api}/app/routers/collab.py +0 -0
  246. /package/{notes-api → notes/api}/app/routers/comments.py +0 -0
  247. /package/{notes-api → notes/api}/app/routers/files.py +0 -0
  248. /package/{notes-api → notes/api}/app/routers/settings.py +0 -0
  249. /package/{notes-api → notes/api}/app/routers/tree.py +0 -0
  250. /package/{notes-api → notes/api}/app/routers/upload.py +0 -0
  251. /package/{notes-api → notes/api}/app/routers/users.py +0 -0
  252. /package/{notes-api → notes/api}/app/schemas/__init__.py +0 -0
  253. /package/{notes-api → notes/api}/app/schemas/auth.py +0 -0
  254. /package/{notes-api → notes/api}/app/schemas/user.py +0 -0
  255. /package/{notes-api → notes/api}/app/security.py +0 -0
  256. /package/{notes-api → notes/api}/app/services/__init__.py +0 -0
  257. /package/{notes-api → notes/api}/app/services/audit.py +0 -0
  258. /package/{notes-api → notes/api}/app/services/business_date.py +0 -0
  259. /package/{notes-api → notes/api}/app/services/collab.py +0 -0
  260. /package/{notes-api → notes/api}/app/services/github.py +0 -0
  261. /package/{notes-api → notes/api}/app/services/pr_chain.py +0 -0
  262. /package/{notes-api → notes/api}/app/startup.py +0 -0
  263. /package/{notes-api → notes/api}/docker/Dockerfile +0 -0
  264. /package/{notes-api → notes/api}/docker/Dockerfile.test +0 -0
  265. /package/{notes-api → notes/api}/pyproject.toml +0 -0
  266. /package/{notes-api → notes/api}/tests/conftest.py +0 -0
  267. /package/{notes-api → notes/api}/tests/test_audit.py +0 -0
  268. /package/{notes-api → notes/api}/tests/test_auth.py +0 -0
  269. /package/{notes-api → notes/api}/tests/test_business_date.py +0 -0
  270. /package/{notes-api → notes/api}/tests/test_collab.py +0 -0
  271. /package/{notes-api → notes/api}/tests/test_comments.py +0 -0
  272. /package/{notes-api → notes/api}/tests/test_config_ssl.py +0 -0
  273. /package/{notes-api → notes/api}/tests/test_config_warnings.py +0 -0
  274. /package/{notes-api → notes/api}/tests/test_files.py +0 -0
  275. /package/{notes-api → notes/api}/tests/test_path_validation.py +0 -0
  276. /package/{notes-api → notes/api}/tests/test_security.py +0 -0
  277. /package/{notes-api → notes/api}/tests/test_settings.py +0 -0
  278. /package/{notes-api → notes/api}/tests/test_tree.py +0 -0
  279. /package/{notes-api → notes/api}/tests/test_upload.py +0 -0
  280. /package/{notes-api → notes/api}/tests/test_users.py +0 -0
  281. /package/{notes-app → notes/app}/.dockerignore +0 -0
  282. /package/{notes-app → notes/app}/index.html +0 -0
  283. /package/{notes-app → notes/app}/nginx.conf +0 -0
  284. /package/{notes-app → notes/app}/package-lock.json +0 -0
  285. /package/{notes-app → notes/app}/playwright.config.ts +0 -0
  286. /package/{notes-app → notes/app}/pnpm-lock.yaml +0 -0
  287. /package/{notes-app → notes/app}/src/App.tsx +0 -0
  288. /package/{notes-app → notes/app}/src/components/BranchSelectorModal.tsx +0 -0
  289. /package/{notes-app → notes/app}/src/components/CodeBlockView.test.tsx +0 -0
  290. /package/{notes-app → notes/app}/src/components/CodeBlockView.tsx +0 -0
  291. /package/{notes-app → notes/app}/src/components/CollabStatus.tsx +0 -0
  292. /package/{notes-app → notes/app}/src/components/DiffPreview.tsx +0 -0
  293. /package/{notes-app → notes/app}/src/components/DrawioEditor.tsx +0 -0
  294. /package/{notes-app → notes/app}/src/components/DrawioViewer.tsx +0 -0
  295. /package/{notes-app → notes/app}/src/components/ErrorBoundary.test.tsx +0 -0
  296. /package/{notes-app → notes/app}/src/components/ErrorBoundary.tsx +0 -0
  297. /package/{notes-app → notes/app}/src/components/FileContentView.tsx +0 -0
  298. /package/{notes-app → notes/app}/src/components/FileContextMenu.test.tsx +0 -0
  299. /package/{notes-app → notes/app}/src/components/FileContextMenu.tsx +0 -0
  300. /package/{notes-app → notes/app}/src/components/FileDialogs.test.tsx +0 -0
  301. /package/{notes-app → notes/app}/src/components/FileDialogs.tsx +0 -0
  302. /package/{notes-app → notes/app}/src/components/SaveStatusIndicator.test.tsx +0 -0
  303. /package/{notes-app → notes/app}/src/components/SaveStatusIndicator.tsx +0 -0
  304. /package/{notes-app → notes/app}/src/components/Skeleton.tsx +0 -0
  305. /package/{notes-app → notes/app}/src/components/SpreadsheetEditor.test.tsx +0 -0
  306. /package/{notes-app → notes/app}/src/components/SpreadsheetEditor.tsx +0 -0
  307. /package/{notes-app → notes/app}/src/components/TextEditor.tsx +0 -0
  308. /package/{notes-app → notes/app}/src/components/ToastContainer.tsx +0 -0
  309. /package/{notes-app → notes/app}/src/components/TreeNode.tsx +0 -0
  310. /package/{notes-app → notes/app}/src/contexts/AuthContext.test.tsx +0 -0
  311. /package/{notes-app → notes/app}/src/contexts/AuthContext.tsx +0 -0
  312. /package/{notes-app → notes/app}/src/contexts/ToastContext.test.ts +0 -0
  313. /package/{notes-app → notes/app}/src/contexts/ToastContext.tsx +0 -0
  314. /package/{notes-app → notes/app}/src/hooks/useAutoSave.test.ts +0 -0
  315. /package/{notes-app → notes/app}/src/hooks/useAutoSave.ts +0 -0
  316. /package/{notes-app → notes/app}/src/hooks/useCollaboration.ts +0 -0
  317. /package/{notes-app → notes/app}/src/hooks/useComments.ts +0 -0
  318. /package/{notes-app → notes/app}/src/hooks/useYText.ts +0 -0
  319. /package/{notes-app → notes/app}/src/lib/branch-diff.test.ts +0 -0
  320. /package/{notes-app → notes/app}/src/lib/branch-diff.ts +0 -0
  321. /package/{notes-app → notes/app}/src/lib/created-file.test.ts +0 -0
  322. /package/{notes-app → notes/app}/src/lib/created-file.ts +0 -0
  323. /package/{notes-app → notes/app}/src/lib/csv-utils.test.ts +0 -0
  324. /package/{notes-app → notes/app}/src/lib/csv-utils.ts +0 -0
  325. /package/{notes-app → notes/app}/src/lib/deepLinkPathToSearch.test.ts +0 -0
  326. /package/{notes-app → notes/app}/src/lib/deepLinkPathToSearch.ts +0 -0
  327. /package/{notes-app → notes/app}/src/lib/draft-storage.test.ts +0 -0
  328. /package/{notes-app → notes/app}/src/lib/draft-storage.ts +0 -0
  329. /package/{notes-app → notes/app}/src/lib/mermaid-render.ts +0 -0
  330. /package/{notes-app → notes/app}/src/lib/postLoginRedirect.test.ts +0 -0
  331. /package/{notes-app → notes/app}/src/lib/postLoginRedirect.ts +0 -0
  332. /package/{notes-app → notes/app}/src/lib/tree-utils.ts +0 -0
  333. /package/{notes-app → notes/app}/src/lib/user-colors.ts +0 -0
  334. /package/{notes-app → notes/app}/src/lib/xlsx-utils.test.ts +0 -0
  335. /package/{notes-app → notes/app}/src/lib/xlsx-utils.ts +0 -0
  336. /package/{notes-app → notes/app}/src/main.tsx +0 -0
  337. /package/{notes-app → notes/app}/src/pages/LoginPage.tsx +0 -0
  338. /package/{notes-app → notes/app}/src/pages/MainPage.insertChildEntry.test.ts +0 -0
  339. /package/{notes-app → notes/app}/src/pages/SettingsPage.tsx +0 -0
  340. /package/{notes-app → notes/app}/src/pages/UserManagementPage.tsx +0 -0
  341. /package/{notes-app → notes/app}/src/styles/globals.css +0 -0
  342. /package/{notes-app → notes/app}/src/types.ts +0 -0
  343. /package/{notes-app → notes/app}/src/vite-env.d.ts +0 -0
  344. /package/{notes-app → notes/app}/tsconfig.json +0 -0
  345. /package/{notes-app → notes/app}/tsconfig.node.json +0 -0
  346. /package/{notes-app → notes/app}/vite.config.js +0 -0
  347. /package/{notes-app → notes/app}/vite.config.ts +0 -0
  348. /package/{notes-app → notes/app}/vitest.config.ts +0 -0
@@ -43,6 +43,15 @@ function mockResponse(body: unknown, status = 200): Response {
43
43
  }
44
44
 
45
45
  const apiFetchMock = vi.fn<(path: string, init?: RequestInit) => Promise<Response>>();
46
+ const triggerPeriodicSync = vi.hoisted(() => vi.fn());
47
+
48
+ vi.mock("@/hooks/usePeriodicSync", () => ({
49
+ usePeriodicSync: ({ onSync, enabled }: { onSync: () => void | Promise<void>; enabled: boolean }) => {
50
+ triggerPeriodicSync.mockImplementation(() => {
51
+ if (enabled) void onSync();
52
+ });
53
+ },
54
+ }));
46
55
 
47
56
  vi.mock("@/lib/api", async (importOriginal) => {
48
57
  const actual = await importOriginal<typeof import("@/lib/api")>();
@@ -138,6 +147,31 @@ describe("useEditSession", () => {
138
147
  expect(result.current.error).toBeNull();
139
148
  });
140
149
 
150
+ it("保存後の fetchSession で prNumber / prUrl が更新される", async () => {
151
+ const saveResult = { branch: "edit/user", sha: "abc123", commitCount: 4 };
152
+ const updatedSession = {
153
+ ...MOCK_SESSION,
154
+ prNumber: 99,
155
+ prUrl: "https://github.com/org/repo/pull/99",
156
+ };
157
+ apiFetchMock
158
+ .mockResolvedValueOnce(mockResponse({ session: MOCK_SESSION }))
159
+ .mockResolvedValueOnce(mockResponse(saveResult))
160
+ .mockResolvedValueOnce(mockResponse({ session: updatedSession }));
161
+
162
+ const { result } = renderHook(() => useEditSession());
163
+ await waitFor(() => expect(result.current.session?.prNumber).toBe(42));
164
+
165
+ await act(async () => {
166
+ await result.current.saveFile("docs/README.md", "new content", "sha123");
167
+ });
168
+
169
+ await waitFor(() => {
170
+ expect(result.current.session?.prNumber).toBe(99);
171
+ expect(result.current.session?.prUrl).toBe(updatedSession.prUrl);
172
+ });
173
+ });
174
+
141
175
  it("sha が null の場合 body に sha を含めない", async () => {
142
176
  const saveResult = { branch: "edit/user", sha: "abc123", commitCount: 1 };
143
177
  apiFetchMock
@@ -231,40 +265,154 @@ describe("useEditSession", () => {
231
265
  });
232
266
  });
233
267
 
234
- describe("discardChanges", () => {
235
- it("破棄成功時に session null にし true を返す", async () => {
268
+ describe("periodic sync", () => {
269
+ it("背景 poll prGithub.state が更新される", async () => {
270
+ apiFetchMock.mockResolvedValueOnce(mockResponse({ session: MOCK_SESSION }));
271
+
272
+ const { result } = renderHook(() => useEditSession({ syncEnabled: true }));
273
+ await waitFor(() => expect(result.current.session?.prGithub?.state).toBe("open"));
274
+
275
+ const mergedSession: EditSession = {
276
+ ...MOCK_SESSION,
277
+ prGithub: {
278
+ ...MOCK_SESSION.prGithub!,
279
+ state: "closed",
280
+ merged: true,
281
+ },
282
+ };
283
+ apiFetchMock.mockResolvedValueOnce(mockResponse({ session: mergedSession }));
284
+
285
+ await act(async () => {
286
+ triggerPeriodicSync();
287
+ });
288
+
289
+ await waitFor(() => {
290
+ expect(result.current.session?.prGithub?.state).toBe("closed");
291
+ expect(result.current.session?.prGithub?.merged).toBe(true);
292
+ });
293
+ });
294
+
295
+ it("背景 poll で 404 のとき session を維持する", async () => {
296
+ apiFetchMock.mockResolvedValueOnce(mockResponse({ session: MOCK_SESSION }));
297
+
298
+ const { result } = renderHook(() => useEditSession({ syncEnabled: true }));
299
+ await waitFor(() => expect(result.current.session).toEqual(MOCK_SESSION));
300
+
301
+ apiFetchMock.mockResolvedValueOnce(mockResponse(null, 404));
302
+
303
+ await act(async () => {
304
+ triggerPeriodicSync();
305
+ });
306
+
307
+ expect(result.current.session).toEqual(MOCK_SESSION);
308
+ });
309
+
310
+ it("背景 poll 失敗時は session を維持する", async () => {
311
+ apiFetchMock.mockResolvedValueOnce(mockResponse({ session: MOCK_SESSION }));
312
+
313
+ const { result } = renderHook(() => useEditSession({ syncEnabled: true }));
314
+ await waitFor(() => expect(result.current.session).toEqual(MOCK_SESSION));
315
+
316
+ apiFetchMock.mockRejectedValueOnce(new Error("Network error"));
317
+
318
+ await act(async () => {
319
+ triggerPeriodicSync();
320
+ });
321
+
322
+ expect(result.current.session).toEqual(MOCK_SESSION);
323
+ });
324
+
325
+ it("mutation 直後の foreground 再取得中に到着した古い background poll 応答が新しい session を上書きしない(#935)", async () => {
326
+ const FRESH: EditSession = { ...MOCK_SESSION, commitCount: 4, prNumber: 99 };
327
+ const saveResult = { branch: "edit/user", sha: "abc123", commitCount: 4 };
328
+
329
+ let resolveStalePoll: ((r: Response) => void) | undefined;
330
+ const stalePoll = new Promise<Response>((resolve) => {
331
+ resolveStalePoll = resolve;
332
+ });
333
+
236
334
  apiFetchMock
237
- .mockResolvedValueOnce(mockResponse({ session: MOCK_SESSION }))
238
- .mockResolvedValueOnce(mockResponse({ success: true }))
239
- .mockResolvedValueOnce(mockResponse({ session: null }));
335
+ .mockResolvedValueOnce(mockResponse({ session: MOCK_SESSION })) // 1: mount foreground
336
+ .mockReturnValueOnce(stalePoll) // 2: background poll(in-flight・古い応答は後で返す)
337
+ .mockResolvedValueOnce(mockResponse(saveResult)) // 3: PUT save
338
+ .mockResolvedValueOnce(mockResponse({ session: FRESH })); // 4: 保存後の foreground 再取得
240
339
 
241
- const { result } = renderHook(() => useEditSession());
242
- await waitFor(() => expect(result.current.session).toBeTruthy());
340
+ const { result } = renderHook(() => useEditSession({ syncEnabled: true }));
341
+ await waitFor(() => expect(result.current.session?.commitCount).toBe(3));
243
342
 
244
- let ok: unknown;
343
+ // background poll を in-flight にする(beginPollRequest で poll seq が進む)
245
344
  await act(async () => {
246
- ok = await result.current.discardChanges();
345
+ triggerPeriodicSync();
346
+ await Promise.resolve();
247
347
  });
248
348
 
249
- expect(ok).toBe(true);
250
- expect(result.current.session).toBeNull();
349
+ // mutation(保存)→ foreground 再取得が新しい session を適用(beginLoadRequest が in-flight poll を無効化)
350
+ await act(async () => {
351
+ await result.current.saveFile("docs/README.md", "new", "sha123");
352
+ });
353
+ await waitFor(() => expect(result.current.session?.commitCount).toBe(4));
354
+ expect(result.current.session?.prNumber).toBe(99);
355
+
356
+ // 後着の古い background poll 応答が到着しても、新しい session を上書きしない
357
+ await act(async () => {
358
+ resolveStalePoll?.(mockResponse({ session: MOCK_SESSION }));
359
+ await Promise.resolve();
360
+ });
361
+
362
+ expect(result.current.session?.commitCount).toBe(4);
363
+ expect(result.current.session?.prNumber).toBe(99);
251
364
  });
252
365
 
253
- it("破棄失敗時に error を設定し false を返す", async () => {
366
+ it("mutation 開始時に in-flight background poll を無効化し、PUT 実行中に到着した古い応答を適用しない(#935)", async () => {
367
+ const STALE = { ...MOCK_SESSION, prNumber: 7 };
368
+ const FRESH = { ...MOCK_SESSION, prNumber: 99, commitCount: 4 };
369
+ const saveResult = { branch: "edit/user", sha: "abc123", commitCount: 4 };
370
+
371
+ let resolveStalePoll: ((r: Response) => void) | undefined;
372
+ const stalePoll = new Promise<Response>((resolve) => {
373
+ resolveStalePoll = resolve;
374
+ });
375
+ let resolvePut: ((r: Response) => void) | undefined;
376
+ const putPromise = new Promise<Response>((resolve) => {
377
+ resolvePut = resolve;
378
+ });
379
+
254
380
  apiFetchMock
255
- .mockResolvedValueOnce(mockResponse({ session: MOCK_SESSION }))
256
- .mockResolvedValueOnce(mockResponse({ detail: "Server error" }, 500));
381
+ .mockResolvedValueOnce(mockResponse({ session: MOCK_SESSION })) // 1: mount foreground
382
+ .mockReturnValueOnce(stalePoll) // 2: background poll(in-flight)
383
+ .mockReturnValueOnce(putPromise) // 3: PUT save(in-flight に保持)
384
+ .mockResolvedValueOnce(mockResponse({ session: FRESH })); // 4: 保存後の foreground 再取得
257
385
 
258
- const { result } = renderHook(() => useEditSession());
259
- await waitFor(() => expect(result.current.session).toBeTruthy());
386
+ const { result } = renderHook(() => useEditSession({ syncEnabled: true }));
387
+ await waitFor(() => expect(result.current.session?.prNumber).toBe(42));
260
388
 
261
- let ok: unknown;
389
+ // background poll を in-flight にする
262
390
  await act(async () => {
263
- ok = await result.current.discardChanges();
391
+ triggerPeriodicSync();
392
+ await Promise.resolve();
264
393
  });
265
394
 
266
- expect(ok).toBe(false);
267
- expect(result.current.error).toBe("Server error");
395
+ // saveFile を開始(PUT は in-flight のまま保持)。開始時の beginPollRequest が in-flight poll を無効化する
396
+ let savePromise: Promise<unknown> | undefined;
397
+ await act(async () => {
398
+ savePromise = result.current.saveFile("docs/README.md", "new", "sha123");
399
+ await Promise.resolve();
400
+ });
401
+
402
+ // PUT 実行中に古い background poll が解決しても、無効化済みのため適用されない
403
+ await act(async () => {
404
+ resolveStalePoll?.(mockResponse({ session: STALE }));
405
+ await Promise.resolve();
406
+ });
407
+ expect(result.current.session?.prNumber).toBe(42); // STALE(7) で上書きされない
408
+
409
+ // PUT 完了 → 保存後 foreground 取得が新しい session を適用
410
+ await act(async () => {
411
+ resolvePut?.(mockResponse(saveResult));
412
+ await savePromise;
413
+ });
414
+ expect(result.current.session?.prNumber).toBe(99);
415
+ expect(result.current.session?.commitCount).toBe(4);
268
416
  });
269
417
  });
270
418
 
@@ -272,15 +420,15 @@ describe("useEditSession", () => {
272
420
  it("error を null にリセットする", async () => {
273
421
  apiFetchMock
274
422
  .mockResolvedValueOnce(mockResponse({ session: MOCK_SESSION }))
275
- .mockResolvedValueOnce(mockResponse({ detail: "fail" }, 500));
423
+ .mockResolvedValueOnce(mockResponse({ detail: "No changes" }, 400));
276
424
 
277
425
  const { result } = renderHook(() => useEditSession());
278
426
  await waitFor(() => expect(result.current.session).toBeTruthy());
279
427
 
280
428
  await act(async () => {
281
- await result.current.discardChanges();
429
+ await result.current.createPr();
282
430
  });
283
- expect(result.current.error).toBeTruthy();
431
+ expect(result.current.error).toBe("No changes");
284
432
 
285
433
  act(() => {
286
434
  result.current.clearError();
@@ -1,5 +1,7 @@
1
1
  import { useState, useCallback, useEffect, useRef } from "react";
2
2
  import { apiFetch, encodePath, getErrorMessage } from "@/lib/api";
3
+ import { usePeriodicSync } from "@/hooks/usePeriodicSync";
4
+ import { GITHUB_SYNC_POLL_INTERVAL_MS } from "@/lib/github-sync";
3
5
 
4
6
  /** `/pr/my-session` が GitHub から付与する PR のライブ状態(DB の prNumber/prUrl は保持) */
5
7
  export type SessionPrGithub = {
@@ -33,43 +35,75 @@ type SaveResult = {
33
35
  prWarning?: string;
34
36
  };
35
37
 
36
- export function useEditSession() {
38
+ export function useEditSession(options?: { syncEnabled?: boolean }) {
39
+ const syncEnabled = options?.syncEnabled ?? false;
37
40
  const [session, setSession] = useState<EditSession | null>(null);
38
41
  const [saving, setSaving] = useState(false);
39
42
  const [error, setError] = useState<string | null>(null);
40
43
  const mountedRef = useRef(true);
44
+ // foreground(初回・mutation 後の再取得)と background poll で seq を分離し、並走時に
45
+ // 先着の古い応答が後着の新しい session を上書きしないようにする(#935、#934 と同型)
46
+ const loadRequestSeq = useRef(0);
47
+ const pollRequestSeq = useRef(0);
41
48
 
42
49
  useEffect(() => {
43
50
  mountedRef.current = true;
44
51
  return () => { mountedRef.current = false; };
45
52
  }, []);
46
53
 
47
- const fetchSession = useCallback(async () => {
54
+ // foreground 取得開始時は poll seq も進め、in-flight の background poll を無効化する
55
+ const beginLoadRequest = useCallback(() => {
56
+ loadRequestSeq.current += 1;
57
+ pollRequestSeq.current += 1;
58
+ return loadRequestSeq.current;
59
+ }, []);
60
+
61
+ // background poll は poll seq のみ進め、in-flight の foreground 取得は無効化しない
62
+ const beginPollRequest = useCallback(() => {
63
+ pollRequestSeq.current += 1;
64
+ return pollRequestSeq.current;
65
+ }, []);
66
+
67
+ const fetchSession = useCallback(async (options?: { background?: boolean }) => {
68
+ const background = options?.background ?? false;
69
+ const requestId = background ? beginPollRequest() : beginLoadRequest();
70
+ const isStale = () =>
71
+ background
72
+ ? requestId !== pollRequestSeq.current
73
+ : requestId !== loadRequestSeq.current;
48
74
  try {
49
75
  const res = await apiFetch("/pr/my-session");
50
- if (!mountedRef.current) return;
76
+ if (!mountedRef.current || isStale()) return;
51
77
  if (res.ok) {
52
78
  const data = await res.json();
53
- if (!mountedRef.current) return;
79
+ if (!mountedRef.current || isStale()) return;
54
80
  const s = data.session;
55
81
  setSession(
56
82
  s
57
83
  ? { ...s, prGithub: s.prGithub ?? null }
58
84
  : null,
59
85
  );
60
- } else {
61
- if (mountedRef.current) setSession(null);
86
+ } else if (!background && mountedRef.current) {
87
+ setSession(null);
62
88
  }
63
89
  } catch {
64
- if (!mountedRef.current) return;
65
- setSession(null);
90
+ if (!mountedRef.current || isStale()) return;
91
+ if (!background) setSession(null);
66
92
  }
67
- }, []);
93
+ }, [beginLoadRequest, beginPollRequest]);
68
94
 
69
95
  useEffect(() => {
70
96
  void fetchSession();
71
97
  }, [fetchSession]);
72
98
 
99
+ usePeriodicSync({
100
+ enabled: syncEnabled,
101
+ intervalMs: GITHUB_SYNC_POLL_INTERVAL_MS,
102
+ onSync: () => fetchSession({ background: true }),
103
+ // マウント時の初回 fetchSession と即時 sync の二重 dispatch を回避(#929)
104
+ skipInitialSync: true,
105
+ });
106
+
73
107
  const saveFile = useCallback(
74
108
  async (
75
109
  path: string,
@@ -81,6 +115,8 @@ export function useEditSession() {
81
115
  ): Promise<SaveResult | null> => {
82
116
  setSaving(true);
83
117
  setError(null);
118
+ // mutation 中に in-flight の background poll が古い session を適用しないよう先行無効化(#935、IssueDetailView と同様)
119
+ beginPollRequest();
84
120
  try {
85
121
  const body: Record<string, unknown> = { content };
86
122
  if (sha) body.sha = sha;
@@ -109,11 +145,13 @@ export function useEditSession() {
109
145
  if (mountedRef.current) setSaving(false);
110
146
  }
111
147
  },
112
- [fetchSession],
148
+ [fetchSession, beginPollRequest],
113
149
  );
114
150
 
115
151
  const createPr = useCallback(async (): Promise<string | null> => {
116
152
  setError(null);
153
+ // mutation 中に in-flight の background poll が古い session を適用しないよう先行無効化(#935、IssueDetailView と同様)
154
+ beginPollRequest();
117
155
  try {
118
156
  const res = await apiFetch("/pr/create", { method: "POST" });
119
157
  if (!res.ok) {
@@ -127,55 +165,7 @@ export function useEditSession() {
127
165
  if (mountedRef.current) setError(getErrorMessage(err, "PR の作成に失敗しました"));
128
166
  return null;
129
167
  }
130
- }, [fetchSession]);
131
-
132
- const submitPr = useCallback(async (): Promise<boolean> => {
133
- setError(null);
134
- try {
135
- const res = await apiFetch("/pr/submit", { method: "POST" });
136
- if (!res.ok) {
137
- const detail = await res.json().catch(() => null);
138
- throw new Error(detail?.detail ?? `Submit failed: ${res.status}`);
139
- }
140
- await fetchSession();
141
- return true;
142
- } catch (err) {
143
- if (mountedRef.current) setError(getErrorMessage(err, "PR の提出に失敗しました"));
144
- return false;
145
- }
146
- }, [fetchSession]);
147
-
148
- const convertToDraft = useCallback(async (): Promise<boolean> => {
149
- setError(null);
150
- try {
151
- const res = await apiFetch("/pr/draft", { method: "POST" });
152
- if (!res.ok) {
153
- const detail = await res.json().catch(() => null);
154
- throw new Error(detail?.detail ?? `Draft conversion failed: ${res.status}`);
155
- }
156
- await fetchSession();
157
- return true;
158
- } catch (err) {
159
- if (mountedRef.current) setError(getErrorMessage(err, "ドラフトへの変更に失敗しました"));
160
- return false;
161
- }
162
- }, [fetchSession]);
163
-
164
- const discardChanges = useCallback(async (): Promise<boolean> => {
165
- setError(null);
166
- try {
167
- const res = await apiFetch("/pr/discard", { method: "POST" });
168
- if (!res.ok) {
169
- const detail = await res.json().catch(() => null);
170
- throw new Error(detail?.detail ?? `Discard failed: ${res.status}`);
171
- }
172
- await fetchSession();
173
- return true;
174
- } catch (err) {
175
- if (mountedRef.current) setError(getErrorMessage(err, "変更の破棄に失敗しました"));
176
- return false;
177
- }
178
- }, [fetchSession]);
168
+ }, [fetchSession, beginPollRequest]);
179
169
 
180
170
  return {
181
171
  session,
@@ -183,9 +173,6 @@ export function useEditSession() {
183
173
  error,
184
174
  saveFile,
185
175
  createPr,
186
- submitPr,
187
- convertToDraft,
188
- discardChanges,
189
176
  clearError: () => setError(null),
190
177
  };
191
178
  }
@@ -0,0 +1,10 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { resolvePanelWidthStyle } from "@/hooks/useMdUpLayout";
3
+
4
+ describe("resolvePanelWidthStyle", () => {
5
+ it("デスクトップレイアウト時のみ幅スタイルを返す", () => {
6
+ expect(resolvePanelWidthStyle(320, true)).toEqual({ width: 320 });
7
+ expect(resolvePanelWidthStyle(320, false)).toBeUndefined();
8
+ expect(resolvePanelWidthStyle(undefined, true)).toBeUndefined();
9
+ });
10
+ });
@@ -0,0 +1,27 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ const MD_UP_QUERY = "(min-width: 768px)";
4
+
5
+ export function useMdUpLayout(): boolean {
6
+ const [mdUp, setMdUp] = useState(() =>
7
+ typeof window !== "undefined" ? window.matchMedia(MD_UP_QUERY).matches : false,
8
+ );
9
+
10
+ useEffect(() => {
11
+ const media = window.matchMedia(MD_UP_QUERY);
12
+ const sync = () => setMdUp(media.matches);
13
+ sync();
14
+ media.addEventListener("change", sync);
15
+ return () => media.removeEventListener("change", sync);
16
+ }, []);
17
+
18
+ return mdUp;
19
+ }
20
+
21
+ export function resolvePanelWidthStyle(
22
+ width: number | undefined,
23
+ applyCustomWidth: boolean,
24
+ ): { width: number } | undefined {
25
+ if (!applyCustomWidth || width == null) return undefined;
26
+ return { width };
27
+ }