@minimalcorp/tsunagi 0.1.41 → 0.1.43

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 (126) hide show
  1. package/.next/standalone/apps/web/.next/BUILD_ID +1 -1
  2. package/.next/standalone/apps/web/.next/build-manifest.json +3 -3
  3. package/.next/standalone/apps/web/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/apps/web/.next/server/app/_global-error.html +1 -1
  5. package/.next/standalone/apps/web/.next/server/app/_global-error.rsc +1 -1
  6. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  9. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  10. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/.next/standalone/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  12. package/.next/standalone/apps/web/.next/server/app/_not-found.html +1 -1
  13. package/.next/standalone/apps/web/.next/server/app/_not-found.rsc +2 -2
  14. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  15. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  16. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  17. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  18. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  19. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  20. package/.next/standalone/apps/web/.next/server/app/index.html +1 -1
  21. package/.next/standalone/apps/web/.next/server/app/index.rsc +3 -3
  22. package/.next/standalone/apps/web/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  23. package/.next/standalone/apps/web/.next/server/app/index.segments/_full.segment.rsc +3 -3
  24. package/.next/standalone/apps/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  25. package/.next/standalone/apps/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  26. package/.next/standalone/apps/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  27. package/.next/standalone/apps/web/.next/server/app/page.js.nft.json +1 -1
  28. package/.next/standalone/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  29. package/.next/standalone/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
  30. package/.next/standalone/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  31. package/.next/standalone/apps/web/.next/server/app/settings.html +1 -1
  32. package/.next/standalone/apps/web/.next/server/app/settings.rsc +3 -3
  33. package/.next/standalone/apps/web/.next/server/app/settings.segments/_full.segment.rsc +3 -3
  34. package/.next/standalone/apps/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  35. package/.next/standalone/apps/web/.next/server/app/settings.segments/_index.segment.rsc +2 -2
  36. package/.next/standalone/apps/web/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  37. package/.next/standalone/apps/web/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
  38. package/.next/standalone/apps/web/.next/server/app/settings.segments/settings.segment.rsc +1 -1
  39. package/.next/standalone/apps/web/.next/server/app/tasks/[id]/page.js.nft.json +1 -1
  40. package/.next/standalone/apps/web/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  41. package/.next/standalone/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0nhzci6._.js → [root-of-the-server]__0d7excn._.js} +2 -2
  42. package/.next/standalone/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0vi01rl._.js → [root-of-the-server]__0w3_4.k._.js} +2 -2
  43. package/.next/standalone/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0zf09ry._.js → [root-of-the-server]__0ze019p._.js} +2 -2
  44. package/.next/standalone/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0ujaeyr._.js → [root-of-the-server]__13yhig0._.js} +2 -2
  45. package/.next/standalone/apps/web/.next/server/chunks/ssr/{_0soxqr.._.js → _0_2b4wf._.js} +2 -2
  46. package/.next/standalone/apps/web/.next/server/chunks/ssr/_0g.cd6a._.js +1 -1
  47. package/.next/standalone/apps/web/.next/server/chunks/ssr/{_0bt-4.4._.js → _0s18_3n._.js} +2 -2
  48. package/.next/standalone/apps/web/.next/server/chunks/ssr/apps_web_src_app_settings_page_tsx_0i6itlb._.js +1 -1
  49. package/.next/standalone/apps/web/.next/server/chunks/ssr/apps_web_src_app_tasks_[id]_page_tsx_0jshsm2._.js +12 -12
  50. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0uej915._.js → node_modules_00z7dqq._.js} +1 -1
  51. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0k8xmjr._.js → node_modules_00~3~7_._.js} +1 -1
  52. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0bqsyfk._.js → node_modules_06uw9~g._.js} +1 -1
  53. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0smdx~2._.js → node_modules_07f58ia._.js} +1 -1
  54. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0fpqbi2._.js → node_modules_07ve26r._.js} +1 -1
  55. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0b6fjwn._.js → node_modules_0bq-u.l._.js} +1 -1
  56. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0pk6e0.._.js → node_modules_0f3mifg._.js} +1 -1
  57. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0_4__2k._.js → node_modules_0ih0wxv._.js} +1 -1
  58. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0r9vehj._.js → node_modules_0lgu9gx._.js} +1 -1
  59. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0bix9lu._.js → node_modules_0nwi8c2._.js} +1 -1
  60. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0w48uk8._.js → node_modules_0p2g6on._.js} +1 -1
  61. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_07-e6tb._.js → node_modules_0szd5t1._.js} +1 -1
  62. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0yoat72._.js → node_modules_0vtmkdp._.js} +1 -1
  63. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0bzw~9d._.js → node_modules_0z02r3w._.js} +1 -1
  64. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0fsewd2._.js → node_modules_0~pcd4h._.js} +1 -1
  65. package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0l~40og._.js → node_modules_10mbxmu._.js} +1 -1
  66. package/.next/standalone/apps/web/.next/server/middleware-build-manifest.js +3 -3
  67. package/.next/standalone/apps/web/.next/server/pages/404.html +1 -1
  68. package/.next/standalone/apps/web/.next/server/pages/500.html +1 -1
  69. package/.next/standalone/apps/web/.next/server/server-reference-manifest.js +1 -1
  70. package/.next/standalone/apps/web/.next/server/server-reference-manifest.json +1 -1
  71. package/.next/standalone/apps/web/.next/static/chunks/{0nhcl~svhi_26.js → 0.e10jueei8qv.js} +3 -3
  72. package/.next/standalone/apps/web/.next/static/chunks/{0g5hoj5w-z.~6.js → 04ie1rttuhc99.js} +1 -1
  73. package/.next/standalone/apps/web/.next/static/chunks/{0q-u7bl7oa42n.js → 06sk6d3k059.n.js} +1 -1
  74. package/.next/standalone/apps/web/.next/static/chunks/09osn8dd42~wz.css +4 -0
  75. package/.next/standalone/apps/web/.next/static/chunks/0imnlpmbrdgiv.js +5 -0
  76. package/.next/standalone/apps/web/.next/static/chunks/{158q94mo2-6sx.js → 0u_07wgcigiqu.js} +8 -8
  77. package/.next/standalone/apps/web/.next/static/chunks/{02.u6zk77a338.js → 0wvv75.1gvloi.js} +12 -12
  78. package/.next/standalone/apps/web/.next/static/chunks/{0q_j4~6_etj_q.js → 10usg-2-r6y3f.js} +3 -3
  79. package/LICENSE +126 -0
  80. package/README.md +154 -0
  81. package/dist/auto-migrate.js +5 -1
  82. package/dist/cli.js +8 -3
  83. package/dist/server/lib/claude-command.d.ts +2 -0
  84. package/dist/server/lib/claude-command.js +47 -15
  85. package/dist/server/lib/llm-process.js +8 -7
  86. package/dist/server/lib/searxng.d.ts +2 -0
  87. package/dist/server/lib/searxng.js +280 -16
  88. package/dist/server/lib/tab-todos.d.ts +12 -0
  89. package/dist/server/lib/tab-todos.js +139 -0
  90. package/dist/server/lib/whisper-process.js +8 -7
  91. package/dist/server/routes/hooks.js +49 -63
  92. package/dist/server/routes/terminal.js +17 -6
  93. package/npm-shrinkwrap.json +8 -18
  94. package/package.json +1 -8
  95. package/tsunagi-marketplace/plugins/tsunagi-plugin/skills/tsunagi-task/SKILL.md +11 -9
  96. package/.next/standalone/apps/web/.next/static/chunks/09ortx3kdapgz.js +0 -5
  97. package/.next/standalone/apps/web/.next/static/chunks/0p9~lk406udl5.css +0 -4
  98. /package/.next/standalone/apps/web/.next/static/{odzSASj06wo3KbNbC2VNt → oMoJ6ksWSsDkoV7dPvxim}/_buildManifest.js +0 -0
  99. /package/.next/standalone/apps/web/.next/static/{odzSASj06wo3KbNbC2VNt → oMoJ6ksWSsDkoV7dPvxim}/_clientMiddlewareManifest.js +0 -0
  100. /package/.next/standalone/apps/web/.next/static/{odzSASj06wo3KbNbC2VNt → oMoJ6ksWSsDkoV7dPvxim}/_ssgManifest.js +0 -0
  101. /package/{llm-server → dist/llm-server}/README.md +0 -0
  102. /package/{llm-server → dist/llm-server}/download_model.py +0 -0
  103. /package/{llm-server → dist/llm-server}/requirements.txt +0 -0
  104. /package/{llm-server → dist/llm-server}/run.sh +0 -0
  105. /package/{prisma → dist/prisma}/migrations/20260129030443_init/migration.sql +0 -0
  106. /package/{prisma → dist/prisma}/migrations/20260130140855_add_task_planning_fields_and_base_branch/migration.sql +0 -0
  107. /package/{prisma → dist/prisma}/migrations/20260130163529_add_base_branch_commit/migration.sql +0 -0
  108. /package/{prisma → dist/prisma}/migrations/20260131171847_remove_completed_at/migration.sql +0 -0
  109. /package/{prisma → dist/prisma}/migrations/20260328000000_remove_sdk_dead_code/migration.sql +0 -0
  110. /package/{prisma → dist/prisma}/migrations/20260329000000_add_todos_and_planner_tabs/migration.sql +0 -0
  111. /package/{prisma → dist/prisma}/migrations/20260410000000_clear_stale_tab_todos/migration.sql +0 -0
  112. /package/{prisma → dist/prisma}/migrations/20260413000000_make_task_order_non_nullable/migration.sql +0 -0
  113. /package/{prisma → dist/prisma}/migrations/20260629000000_add_tab_mode/migration.sql +0 -0
  114. /package/{prisma → dist/prisma}/migrations/20260915000000_drop_planner_tabs/migration.sql +0 -0
  115. /package/{prisma → dist/prisma}/migrations/20260916000000_add_repository_order/migration.sql +0 -0
  116. /package/{prisma → dist/prisma}/migrations/20260918000000_add_tab_unread/migration.sql +0 -0
  117. /package/{prisma → dist/prisma}/migrations/20260924000000_add_app_settings/migration.sql +0 -0
  118. /package/{prisma → dist/prisma}/migrations/migration_lock.toml +0 -0
  119. /package/{prisma → dist/prisma}/schema.prisma +0 -0
  120. /package/{prisma.config.ts → dist/prisma.config.ts} +0 -0
  121. /package/{scripts → dist/scripts}/monaco-editor.sh +0 -0
  122. /package/{whisper-server → dist/whisper-server}/README.md +0 -0
  123. /package/{whisper-server → dist/whisper-server}/download_model.py +0 -0
  124. /package/{whisper-server → dist/whisper-server}/requirements.txt +0 -0
  125. /package/{whisper-server → dist/whisper-server}/run.sh +0 -0
  126. /package/{whisper-server → dist/whisper-server}/server.py +0 -0
package/LICENSE ADDED
@@ -0,0 +1,126 @@
1
+ # PolyForm Shield License 1.0.0
2
+
3
+ <https://polyformproject.org/licenses/shield/1.0.0>
4
+
5
+ ## Acceptance
6
+
7
+ In order to get any license under these terms, you must agree
8
+ to them as both strict obligations and conditions to all
9
+ your licenses.
10
+
11
+ ## Copyright License
12
+
13
+ The licensor grants you a copyright license for the
14
+ software to do everything you might do with the software
15
+ that would otherwise infringe the licensor's copyright
16
+ in it for any permitted purpose. However, you may
17
+ only distribute the software according to [Distribution
18
+ License](#distribution-license) and make changes or new works
19
+ based on the software according to [Changes and New Works
20
+ License](#changes-and-new-works-license).
21
+
22
+ ## Distribution License
23
+
24
+ The licensor grants you an additional copyright license
25
+ to distribute copies of the software. Your license
26
+ to distribute covers distributing the software with
27
+ changes and new works permitted by [Changes and New Works
28
+ License](#changes-and-new-works-license).
29
+
30
+ ## Notices
31
+
32
+ You must ensure that anyone who gets a copy of any part of
33
+ the software from you also gets a copy of these terms, as well
34
+ as the following copyright notice:
35
+
36
+ > Copyright 2026 minimalcorp
37
+
38
+ ## Changes and New Works License
39
+
40
+ The licensor grants you the copyright licenses in
41
+ [Copyright License](#copyright-license) for changes and
42
+ new works based on the software subject to this license.
43
+
44
+ ## Patent License
45
+
46
+ The licensor grants you a patent license for the software that
47
+ covers patent claims the licensor can license, or becomes able
48
+ to license, that you would infringe by using the software.
49
+
50
+ ## Noncompete
51
+
52
+ Any purpose is a permitted purpose, except for providing to others
53
+ any product that competes with the software.
54
+
55
+ If you use this software to market a product as a substitute for
56
+ the functionality or value of the software, it competes with the
57
+ software. A product may use this software for testing, staging,
58
+ or deploying the product, without necessarily competing with the
59
+ software, even if the product provides some of the same features
60
+ as the software.
61
+
62
+ ## Competition Conditions
63
+
64
+ If you use the software to provide a product that competes with
65
+ the software, that use is not allowed under the licenses granted
66
+ by these terms.
67
+
68
+ ## Fair Use
69
+
70
+ You may have "fair use" rights for the software under the
71
+ law. These terms do not limit them.
72
+
73
+ ## No Other Rights
74
+
75
+ These terms do not allow you to sublicense or transfer any of
76
+ your licenses to anyone else, or prevent the licensor from
77
+ granting licenses to anyone else. These terms do not imply
78
+ any other licenses.
79
+
80
+ ## Patent Defense
81
+
82
+ If you make any written claim that the software infringes or
83
+ contributes to infringement of any patent, your patent license
84
+ for the software granted under these terms ends immediately. If
85
+ your company makes such a claim, your patent license ends
86
+ immediately for work on behalf of your company.
87
+
88
+ ## Violations
89
+
90
+ The first time you are notified in writing that you have
91
+ violated any of these terms, or done anything with the software
92
+ not covered by your licenses, your licenses can nonetheless
93
+ continue if you come into full compliance with these terms,
94
+ and take practical steps to correct past violations, within
95
+ 32 days of receiving notice. Otherwise, all your licenses
96
+ end immediately.
97
+
98
+ ## No Liability
99
+
100
+ ***As far as the law allows, the software comes as is, without
101
+ any warranty or condition, and the licensor will not be liable
102
+ to you for any damages arising out of these terms or the use
103
+ or nature of the software, under any kind of legal claim.***
104
+
105
+ ## Definitions
106
+
107
+ The **licensor** is the individual or entity offering these
108
+ terms, and the **software** is the software the licensor makes
109
+ available under these terms.
110
+
111
+ **You** refers to the individual or entity agreeing to these
112
+ terms.
113
+
114
+ **Your company** is any legal entity, sole proprietorship,
115
+ or other kind of organization that you work for, plus all
116
+ organizations that have control over, are under the control of,
117
+ or are under common control with that organization. **Control**
118
+ means ownership of substantially all the assets of an entity,
119
+ or the power to direct its management and policies by vote,
120
+ contract, or otherwise. Control can be direct or indirect.
121
+
122
+ **Your licenses** are all the licenses granted to you for the
123
+ software under these terms.
124
+
125
+ **Use** means anything you do with the software requiring one
126
+ of your licenses.
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # Tsunagi Monorepo
2
+
3
+ **Tsunagi (繋ぎ)** — 複数 GitHub 組織のプロジェクトをローカルで統合管理し、
4
+ Claude AI による AI 駆動開発を可視化・制御する Web UI ツール。
5
+
6
+ このリポジトリは npm workspaces ベースの monorepo です。
7
+
8
+ ## パッケージ
9
+
10
+ | パッケージ | 場所 | npm 公開 | 説明 |
11
+ | ------------------------------------ | ------------ | -------- | ---------------------------------------------------------------- |
12
+ | [`@minimalcorp/tsunagi`](./apps/web) | `apps/web/` | ✅ | Tsunagi 本体 (Next.js + Fastify + Prisma) |
13
+ | [`tsunagi-docs`](./apps/docs) | `apps/docs/` | ❌ | ユーザー向けドキュメントサイト (Nextra, GitHub Pages にデプロイ) |
14
+
15
+ ## クイックスタート(エンドユーザー向け)
16
+
17
+ Tsunagi を使いたいだけの場合は、npm から直接起動できます:
18
+
19
+ ```bash
20
+ npx @minimalcorp/tsunagi
21
+ ```
22
+
23
+ 詳細は [apps/web/README.md](./apps/web/README.md) または
24
+ [ドキュメントサイト](https://minimalcorp.github.io/tsunagi/) を参照してください。
25
+
26
+ ## 開発セットアップ
27
+
28
+ ```bash
29
+ # リポジトリをクローン
30
+ git clone https://github.com/minimalcorp/tsunagi.git
31
+ cd tsunagi
32
+
33
+ # 依存関係インストール(全ワークスペース)
34
+ npm install
35
+
36
+ # Tsunagi 本体を開発モードで起動
37
+ npm run dev
38
+
39
+ # ドキュメントサイトを開発モードで起動
40
+ npm run docs:dev
41
+ ```
42
+
43
+ ## よく使うコマンド(ルートから実行)
44
+
45
+ | コマンド | 説明 |
46
+ | ---------------------- | ------------------------------------------ |
47
+ | `npm run dev` | web + docs を並列起動(Ctrl+C で両方停止) |
48
+ | `npm run web:dev` | Tsunagi 本体のみ開発モードで起動 |
49
+ | `npm run docs:dev` | ドキュメントサイトのみ開発モードで起動 |
50
+ | `npm run build` | 全ワークスペースをビルド |
51
+ | `npm run lint` | 全ワークスペースで ESLint を実行 |
52
+ | `npm run type-check` | 全ワークスペースで TypeScript 型チェック |
53
+ | `npm run format` | Prettier で全ファイルをフォーマット |
54
+ | `npm run format:check` | Prettier でフォーマット差分チェック |
55
+
56
+ ## リリース
57
+
58
+ GitHub Actions の `Release` workflow を手動実行することで、`@minimalcorp/tsunagi`
59
+ の npm 公開、および docs の GitHub Pages デプロイを統合的に行えます:
60
+
61
+ 1. GitHub の Actions タブから `Release` workflow を選択
62
+ 2. 以下を入力して Run:
63
+ - **target**: `all` / `tsunagi` / `docs` のいずれか
64
+ - **version**: `patch` / `minor` / `major` (tsunagi にのみ適用)
65
+
66
+ 詳細は [.github/workflows/release.yml](./.github/workflows/release.yml) を参照。
67
+
68
+ ### 初回実行前の必須セットアップ
69
+
70
+ 初めて `Release` workflow を実行する前に、以下を手動で設定しておく必要があります。
71
+
72
+ 1. **npm Organization 準備**
73
+ - npmjs.com で `@minimalcorp` organization を作成(未作成の場合)
74
+ - `@minimalcorp/tsunagi` パッケージ名が未取得であることを確認
75
+
76
+ 2. **npm Trusted Publisher を事前登録** (npm 側)
77
+ - 本 workflow は **npm Trusted Publishing (OIDC)** を使用するため `NPM_TOKEN` は不要
78
+ - npmjs.com で Trusted Publisher を追加: organization=`minimalcorp`, repository=`tsunagi`, workflow=`release.yml`, environment=`production-release`
79
+ - パッケージがまだ存在しない段階でも "Pending Trusted Publisher" として事前登録可能
80
+
81
+ 3. **GitHub Pages を Actions ソースに設定**
82
+ - Settings → Pages → Source: `GitHub Actions` を選択(`Deploy from a branch` ではない)
83
+
84
+ 4. **`production-release` Environment の作成と承認者設定**(必須)
85
+ - Settings → Environments → `New environment` → 名前を `production-release` として作成
86
+ - **Required reviewers** を有効化し、リリース承認権限を持つメンバー(自分自身でも可)を追加
87
+ - Release workflow の `approve` job はこの Environment 配下で実行されるため、
88
+ workflow 実行時に GitHub UI で明示的な承認操作が必要になる
89
+ - `publish-tsunagi` / `deploy-docs` 両方がこの approval を通過した後でのみ実行される
90
+ - 承認画面で main の最新状態(他 PR の merge 状況等)を目視確認してから承認する
91
+ ことで、意図しない内容のリリースを防ぐ
92
+
93
+ 5. **`main` ブランチ保護の bypass 設定**(保護ルールを設定している場合)
94
+ - `Release` workflow は `npm version` で bump した commit と tag を `main` に直接 push する
95
+ - main に `Require a pull request before merging` 等の保護ルールがある場合、push がブロックされる
96
+ - `github-actions[bot]` は user role を持たないため、Bypass list に**明示的に**以下のいずれかを設定する必要がある:
97
+ - 独立した **Deploy key** を発行し Bypass に追加(推奨)
98
+ - 管理者ユーザーの **PAT** を `PAT_TOKEN` secret として登録し、workflow の `checkout` で使用
99
+ - 管理者ユーザー自身を Bypass に追加(workflow を本人が起動する運用)
100
+ - 保護ルールを main に設定していない場合は不要
101
+
102
+ これらを設定せずに workflow を実行すると、途中で失敗してバージョン番号だけが bump された中途半端な状態になる可能性があるので、**必ず事前に設定してください**。
103
+
104
+ ### リリース中の concurrent push について
105
+
106
+ Release workflow には以下の保護機構が組み込まれています:
107
+
108
+ - **同時実行の排他制御**: `concurrency: release` group により、2つ目の Release workflow は1つ目の完了を待機します
109
+ - **Push retry with rebase**: workflow の checkout から push までの間に別の PR が main に merge された場合でも、自動で rebase してから retry します(最大3回)。rebase により merge 済みの内容を取り込んだ上で version bump commit を再適用するため、npm publish は最新の main 状態で行われます
110
+ - **Environment 承認ゲート**: 人間が承認するタイミングで main の状態を目視確認できるため、意図しない内容のリリースを事前に察知できます
111
+
112
+ ## リポジトリ構成
113
+
114
+ ```
115
+ tsunagi/
116
+ ├── apps/
117
+ │ ├── web/ # @minimalcorp/tsunagi (Tsunagi 本体)
118
+ │ └── docs/ # tsunagi-docs (Nextra ドキュメントサイト)
119
+ ├── .github/workflows/
120
+ │ ├── ci.yml # PR CI (format / lint / type-check / build)
121
+ │ ├── release.yml # 統合リリース (npm publish + Pages deploy)
122
+ │ └── cla.yml # CLA Assistant bot
123
+ ├── .husky/ # Git hooks
124
+ ├── package.json # monorepo root (workspaces)
125
+ ├── .prettierrc.json # monorepo 共通 Prettier 設定
126
+ ├── LICENSE # PolyForm Shield 1.0.0
127
+ ├── CLA.md # Contributor License Agreement
128
+ ├── CONTRIBUTING.md # コントリビューションガイド
129
+ └── CLAUDE.md # Claude AI 向け作業ルール
130
+ ```
131
+
132
+ ## 対応 OS
133
+
134
+ - macOS
135
+ - Linux
136
+ - Windows は**非サポート**(WSL2 経由で動作する可能性はありますが保証されません)
137
+
138
+ ## 必須ランタイム
139
+
140
+ - **Node.js ≥ 20**
141
+ - **Git ≥ 2.42**(空リポジトリを扱うため `git worktree add --orphan` を使用)
142
+ - **Claude Code CLI**(`claude` コマンドが PATH 上に必要)
143
+
144
+ ## ライセンス
145
+
146
+ 本リポジトリは [PolyForm Shield License 1.0.0](./LICENSE) のもとで公開されています。
147
+ これは source-available ライセンスであり、**競合製品化は禁止**されていますが、
148
+ 個人利用・社内利用・通常の商用利用は自由です。詳細は [LICENSE](./LICENSE) を
149
+ 参照してください。
150
+
151
+ ## コントリビューション
152
+
153
+ Pull Request を送信する際は [CONTRIBUTING.md](./CONTRIBUTING.md) を参照し、
154
+ CLA Assistant bot の指示に従って [CLA](./CLA.md) への同意をお願いします。
@@ -3,7 +3,11 @@ import { promisify } from 'node:util';
3
3
  import * as os from 'node:os';
4
4
  import * as path from 'node:path';
5
5
  import * as fs from 'node:fs/promises';
6
+ import { fileURLToPath } from 'node:url';
6
7
  const execAsync = promisify(exec);
8
+ // bundle.mjs が dist/ にコピーする。npx は npm workspace 内だと cwd を workspace
9
+ // ルートに変えるため、cwd 依存にせず --config で明示する(相対パスは config 基準で解決される)。
10
+ const PRISMA_CONFIG = path.join(path.dirname(fileURLToPath(import.meta.url)), 'prisma.config.ts');
7
11
  function getTsunagiDataDir() {
8
12
  return process.env.TSUNAGI_DATA_DIR || path.join(os.homedir(), '.tsunagi');
9
13
  }
@@ -13,7 +17,7 @@ function getStateDir() {
13
17
  async function autoMigrate() {
14
18
  try {
15
19
  await fs.mkdir(getStateDir(), { recursive: true });
16
- const { stdout } = await execAsync('npx prisma migrate deploy');
20
+ const { stdout } = await execAsync(`npx prisma migrate deploy --config ${JSON.stringify(PRISMA_CONFIG)}`);
17
21
  // Extract "N migration(s) applied." line from prisma output
18
22
  const match = stdout.match(/(\d+)\s+migrations?\s+applied/i);
19
23
  if (match) {
package/dist/cli.js CHANGED
@@ -21,8 +21,13 @@ import { acquireSingleInstanceLock } from './single-instance-lock.js';
21
21
  * <pkg>/.next/standalone/apps/web/server.js ← Next.js standalone entry (bundled from apps/web/.next/standalone)
22
22
  * <pkg>/.next/standalone/apps/web/.next/static/
23
23
  * <pkg>/.next/standalone/node_modules/
24
- * <pkg>/prisma/schema.prisma
25
- * <pkg>/prisma.config.ts
24
+ * <pkg>/dist/prisma/schema.prisma ← bundled from apps/server/prisma
25
+ * <pkg>/dist/prisma/migrations/**
26
+ * <pkg>/dist/prisma.config.ts ← passed via --config by dist/auto-migrate.js
27
+ * <pkg>/dist/scripts/monaco-editor.sh ← $EDITOR for Ctrl+G (resolved by dist/server/routes/terminal.js)
28
+ * <pkg>/dist/docs/ ← bundled from apps/docs/out (served on DOCS_PORT)
29
+ * <pkg>/dist/whisper-server/ ← bundled from apps/whisper-server
30
+ * <pkg>/dist/llm-server/ ← bundled from apps/llm-server
26
31
  * <pkg>/tsunagi-marketplace/plugins/tsunagi-plugin/.claude-plugin/plugin.json
27
32
  */
28
33
  // ---------------------------------------------------------------------------
@@ -58,7 +63,7 @@ const PACKAGE_ROOT = path.resolve(DIST_DIR, '..');
58
63
  const AUTO_MIGRATE_JS = path.join(DIST_DIR, 'auto-migrate.js');
59
64
  const FASTIFY_ENTRY_JS = path.join(DIST_DIR, 'server', 'index.js');
60
65
  const NEXT_STANDALONE_ENTRY = path.join(PACKAGE_ROOT, '.next', 'standalone', 'apps', 'web', 'server.js');
61
- const DOCS_DIR = path.join(PACKAGE_ROOT, 'docs');
66
+ const DOCS_DIR = path.join(DIST_DIR, 'docs');
62
67
  const DOCS_PORT = 2793;
63
68
  const isDebug = !!process.env.TSUNAGI_DEBUG;
64
69
  const isDocker = fs.existsSync('/.dockerenv');
@@ -2,5 +2,7 @@ import type { TabMode } from '@minimalcorp/tsunagi-shared';
2
2
  /** ローカルLLMタブの Claude Code の接続先(tsunagi の中継口) */
3
3
  export declare const LOCAL_LLM_PROXY_URL: string;
4
4
  export declare function isLocalLlmMode(mode: TabMode | undefined): boolean;
5
+ /** 起動スクリプトの type 引数。original: Anthropic、ollama / lm-studio: ローカルLLM */
6
+ export type LaunchType = 'original' | 'ollama' | 'lm-studio';
5
7
  /** タブの mode に応じた claude の起動コマンド(既存セッションがあれば resume) */
6
8
  export declare function buildClaudeCommand(sessionId: string, mode: TabMode): Promise<string>;
@@ -19,6 +19,10 @@ export function isLocalLlmMode(mode) {
19
19
  function shellQuote(value) {
20
20
  return `'${value.replace(/'/g, `'\\''`)}'`;
21
21
  }
22
+ const LAUNCH_TYPE_PROVIDER = {
23
+ ollama: 'ollama',
24
+ 'lm-studio': 'lmstudio',
25
+ };
22
26
  /**
23
27
  * ローカルLLMタブの claude に付ける引数。
24
28
  * - モデルは Settings でだけ切り替える: /model の一覧を中継口の名前1件にし、PreModelSwitch フックで
@@ -28,10 +32,9 @@ function shellQuote(value) {
28
32
  * 毎回プロンプトに載る(prefill が遅くなり tool 選択の精度も落ちる)。そのため MCP を tsunagi の
29
33
  * タスク管理と web_search だけに絞る
30
34
  */
31
- async function localLlmArgs() {
32
- const { active, webSearch } = await getLocalLlmSettings();
35
+ function localLlmArgs(provider, webSearch) {
33
36
  // ollama.com による WebSearch の代行は、使用中のモデルが Ollama のときだけ使える
34
- const localWebSearch = webSearch === 'local' || active?.provider !== 'ollama';
37
+ const localWebSearch = webSearch === 'local' || provider !== 'ollama';
35
38
  const settings = {
36
39
  modelPicker: {
37
40
  options: [
@@ -80,27 +83,56 @@ async function localLlmArgs() {
80
83
  return args;
81
84
  }
82
85
  /**
83
- * ローカルLLMタブの claude を起動するスクリプトを書き出し、そのパスを返す。
86
+ * タスク進捗(プログレスバー)の元になる Task 系ツール。
87
+ * Claude Code は旧モデル以外ではこれらを既定で無効にしており、--allowedTools で明示すると有効になる
88
+ */
89
+ const TASK_TOOLS = 'TaskCreate,TaskGet,TaskUpdate,TaskList';
90
+ /**
91
+ * claude の起動スクリプトを書き出し、そのパスを返す。`sh <script> <type> <session-id>` で起動し、
92
+ * 既存セッションがあれば resume、なければ新規作成する。
84
93
  * 起動コマンドは PTY に1行で書き込むため、macOS の端末入力の1行上限(MAX_CANON = 1024 バイト)を
85
94
  * 超えると後ろが切り捨てられる。引数の JSON をコマンドに直接載せると上限を超え、
86
95
  * `|| claude ... --session-id` 側が途中で切れてタブと別のセッションIDで起動してしまう
87
- * (hooks がタブに紐づかず、--settings / --mcp-config も効かない)。そのため引数はスクリプトに閉じ込める
96
+ * (hooks がタブに紐づかず、--settings / --mcp-config も効かない)。そのため引数はスクリプトに閉じ込める。
97
+ * ローカルLLMの引数は Settings に依存するため起動のたびに書き直す
88
98
  */
89
- async function writeLocalLlmLauncher(args) {
90
- const dir = path.join(getStateDir(), 'local-llm');
91
- await mkdir(dir, { recursive: true });
92
- const file = path.join(dir, 'claude.sh');
99
+ async function writeLauncher() {
100
+ const { webSearch } = await getLocalLlmSettings();
101
+ const branches = Object.keys(LAUNCH_TYPE_PROVIDER)
102
+ .map((type) => ` ${type}) set -- ${localLlmArgs(LAUNCH_TYPE_PROVIDER[type], webSearch).join(' ')} ;;`)
103
+ .join('\n');
104
+ const script = `#!/bin/sh
105
+ # usage: launch-claude.sh <original|ollama|lm-studio> <session-id>
106
+ type=$1
107
+ session_id=$2
108
+ case "$type" in
109
+ original) set -- ;;
110
+ ${branches}
111
+ *) echo "launch-claude.sh: unknown type: $type" >&2; exit 2 ;;
112
+ esac
113
+ claude --dangerously-skip-permissions --allowedTools ${TASK_TOOLS} "$@" --resume "$session_id" 2>/dev/null ||
114
+ exec claude --dangerously-skip-permissions --allowedTools ${TASK_TOOLS} "$@" --session-id "$session_id"
115
+ `;
116
+ const file = path.join(getStateDir(), 'launch-claude.sh');
117
+ await mkdir(path.dirname(file), { recursive: true });
93
118
  // 複数タブの同時起動で書きかけのファイルを読まないよう、書き終えてから置き換える
94
119
  const tmp = `${file}.${randomUUID()}.tmp`;
95
- await writeFile(tmp, `#!/bin/sh\nexec claude ${args.join(' ')} "$@"\n`);
120
+ await writeFile(tmp, script);
96
121
  await rename(tmp, file);
97
122
  return file;
98
123
  }
124
+ /** タブの mode から起動スクリプトの type を決める。ローカルLLMは Settings で選んだ使用中のモデルに従う */
125
+ async function resolveLaunchType(mode) {
126
+ if (!isLocalLlmMode(mode))
127
+ return 'original';
128
+ const { active } = await getLocalLlmSettings();
129
+ if (!active) {
130
+ throw new Error('ローカルLLMのモデルが未設定です。Settings で使用するモデルを選んでください');
131
+ }
132
+ return active.provider === 'ollama' ? 'ollama' : 'lm-studio';
133
+ }
99
134
  /** タブの mode に応じた claude の起動コマンド(既存セッションがあれば resume) */
100
135
  export async function buildClaudeCommand(sessionId, mode) {
101
- const args = ['--dangerously-skip-permissions'];
102
- const claude = isLocalLlmMode(mode)
103
- ? `sh ${shellQuote(await writeLocalLlmLauncher([...args, ...(await localLlmArgs())]))}`
104
- : `claude ${args.join(' ')}`;
105
- return `${claude} --resume ${sessionId} 2>/dev/null || ${claude} --session-id ${sessionId}`;
136
+ const type = await resolveLaunchType(mode);
137
+ return `sh ${shellQuote(await writeLauncher())} ${type} ${sessionId}`;
106
138
  }
@@ -28,15 +28,16 @@ const EXPECTED_MODEL_BYTES = 17_200_000_000;
28
28
  const STARTUP_TIMEOUT_MS = 90_000;
29
29
  // クラッシュ時にエラーメッセージへ含めるstderrの上限文字数。
30
30
  const STDERR_TAIL_MAX_CHARS = 4000;
31
- // このファイルは apps/server/src/lib (dev) または apps/cli/dist/server/lib
32
- // (npm配布物) のいずれかにいる。どちらの場合も3階層上に llm-server が
33
- // 兄弟ディレクトリとして存在するようレイアウトを揃えている
34
- // (apps/server/dist/lib → apps/server → apps → apps/llm-server,
35
- // apps/cli/dist/server/lib → apps/cli/dist → apps/cli → apps/cli/llm-server)。
31
+ // このファイルの位置から llm-server を探す:
32
+ // npm配布物: apps/cli/dist/server/lib → 2階層上 → apps/cli/dist/llm-server
33
+ // dev/build: apps/server/{src,dist}/lib → 3階層上 → apps/llm-server
36
34
  export function findLlmServerDir() {
37
35
  const here = path.dirname(fileURLToPath(import.meta.url));
38
- const candidate = path.join(here, '..', '..', '..', 'llm-server');
39
- return fs.existsSync(path.join(candidate, 'run.sh')) ? candidate : null;
36
+ const candidates = [
37
+ path.join(here, '..', '..', 'llm-server'),
38
+ path.join(here, '..', '..', '..', 'llm-server'),
39
+ ];
40
+ return candidates.find((c) => fs.existsSync(path.join(c, 'run.sh'))) ?? null;
40
41
  }
41
42
  function venvPython() {
42
43
  return path.join(VENV_DIR, 'bin', 'python3');
@@ -9,6 +9,8 @@ export declare function parseSearxngSettings(body: unknown): {
9
9
  };
10
10
  /** searxng-run を探す(設定値 → PATH)。見つからなければ null */
11
11
  export declare function findSearxngBin(settings: SearxngSettings): string | null;
12
+ /** docker コマンドを探す(PATH → Docker Desktop / Homebrew の既定の場所)。見つからなければ null */
13
+ export declare function findDockerBin(): string | null;
12
14
  /** Ollama / LM Studio の有効状態に合わせて起動・停止する。失敗しても例外は投げない */
13
15
  export declare function syncSearxng(): Promise<void>;
14
16
  /** 手動での再起動(設定変更後やエラーからの復帰) */