@cocorograph/hub-agent 0.6.67 → 0.6.68

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/claude-md.mjs +120 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocorograph/hub-agent",
3
- "version": "0.6.67",
3
+ "version": "0.6.68",
4
4
  "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの tmux/pty を中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
package/src/claude-md.mjs CHANGED
@@ -51,7 +51,102 @@ async function fetchDirector({ hubUrl, accessToken, dirName, fetchImpl }) {
51
51
  }
52
52
  }
53
53
 
54
- function renderWithDirector(dirName, director) {
54
+ /**
55
+ * dir 名から GitRepository 一覧を取得する。
56
+ *
57
+ * 旧 Hub backend(GitRepository モデル未導入)では 404 を返すので、
58
+ * その場合は空配列扱いで後方互換を維持する。
59
+ *
60
+ * @returns {Promise<Array<object>>}
61
+ */
62
+ async function fetchRepositories({ hubUrl, accessToken, dirName, fetchImpl }) {
63
+ const f = fetchImpl || globalThis.fetch
64
+ if (!f) return []
65
+ const url =
66
+ `${hubUrl.replace(/\/+$/, "")}/api/git-repositories/by-dir/` +
67
+ `${encodeURIComponent(dirName)}/`
68
+ try {
69
+ const res = await f(url, {
70
+ headers: { Authorization: `Bearer ${accessToken}` },
71
+ })
72
+ if (!res.ok) return []
73
+ const payload = await res.json()
74
+ return Array.isArray(payload?.repositories) ? payload.repositories : []
75
+ } catch {
76
+ return []
77
+ }
78
+ }
79
+
80
+ /**
81
+ * GitRepository 配列を CLAUDE.md のクローンレス作業手順セクションに描画する。
82
+ *
83
+ * リポジトリが 1 件もない場合は空文字列を返し、CLAUDE.md には何も追記しない。
84
+ */
85
+ function renderRepositorySection(repositories) {
86
+ if (!Array.isArray(repositories) || repositories.length === 0) return ""
87
+
88
+ const lines = ["## Cockpit クローンレス開発フロー", ""]
89
+ lines.push(
90
+ "この workspace ディレクトリには Git リポジトリを常設しません。",
91
+ "コードを修正するときは、毎回一時ディレクトリにチェックアウトして作業します。",
92
+ "",
93
+ )
94
+
95
+ for (const repo of repositories) {
96
+ const repoUrl = (repo?.repo_url || "").trim()
97
+ if (!repoUrl) continue
98
+ const branch = (repo?.default_branch || "main").trim()
99
+ const deployDisplay = (repo?.deploy_method_display || "").trim()
100
+ const deployNotes = (repo?.deploy_notes || "").trim()
101
+ const stagingName = (repo?.staging_server_name || "").trim()
102
+ const productionName = (repo?.production_server_name || "").trim()
103
+ const repoLabel = (repo?.name || "").trim()
104
+ const heading = repoLabel
105
+ ? `### ${repoUrl} — ${repoLabel}`
106
+ : `### ${repoUrl}`
107
+
108
+ lines.push(heading)
109
+ lines.push("")
110
+ lines.push(`- **デフォルトブランチ**: \`${branch}\``)
111
+ if (deployDisplay) lines.push(`- **デプロイ方式**: ${deployDisplay}`)
112
+ if (stagingName) lines.push(`- **ステージング**: ${stagingName}`)
113
+ if (productionName) lines.push(`- **本番**: ${productionName}`)
114
+ lines.push("")
115
+
116
+ lines.push("**作業手順(クローンレス)**:")
117
+ lines.push("")
118
+ lines.push("```bash")
119
+ lines.push("TMPDIR=$(mktemp -d)")
120
+ lines.push(`gh repo clone ${repoUrl} "$TMPDIR" -- --depth 1 --filter=blob:none --no-checkout`)
121
+ lines.push(`cd "$TMPDIR" && git fetch origin ${branch} && git checkout -b feat/<your-change> origin/${branch}`)
122
+ lines.push("# ファイル編集 → 動作確認")
123
+ lines.push('git commit -am "<message>"')
124
+ lines.push("git push -u origin HEAD")
125
+ lines.push("gh pr create --base " + branch + " --fill")
126
+ lines.push("# 作業終了後")
127
+ lines.push('cd / && rm -rf "$TMPDIR"')
128
+ lines.push("```")
129
+ lines.push("")
130
+
131
+ if (deployNotes) {
132
+ lines.push("**デプロイ手順メモ**:")
133
+ lines.push("")
134
+ lines.push("```")
135
+ lines.push(deployNotes)
136
+ lines.push("```")
137
+ lines.push("")
138
+ }
139
+ }
140
+
141
+ lines.push(
142
+ "> Git 認証はローカルの `gh` CLI / SSH 鍵を使用します。",
143
+ "> サーバー接続情報(パスワード・秘密鍵)は Hub の「サーバー情報」タブから取得してください。",
144
+ "",
145
+ )
146
+ return lines.join("\n")
147
+ }
148
+
149
+ function renderWithDirector(dirName, director, repositorySection = "") {
55
150
  const fm = director?.frontmatter || {}
56
151
  const client = (fm.client || "").trim()
57
152
  const domain = (fm.domain || "").trim()
@@ -94,6 +189,10 @@ function renderWithDirector(dirName, director) {
94
189
  }
95
190
  lines.push("")
96
191
 
192
+ if (repositorySection) {
193
+ lines.push(repositorySection)
194
+ }
195
+
97
196
  lines.push("## 初期化")
98
197
  lines.push("")
99
198
  lines.push(
@@ -109,8 +208,8 @@ function renderWithDirector(dirName, director) {
109
208
  return lines.join("\n")
110
209
  }
111
210
 
112
- function renderPlaceholder(dirName) {
113
- return [
211
+ function renderPlaceholder(dirName, repositorySection = "") {
212
+ const sections = [
114
213
  "# CLAUDE.md",
115
214
  "",
116
215
  "This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.",
@@ -119,12 +218,18 @@ function renderPlaceholder(dirName) {
119
218
  "",
120
219
  `- **Hub Director**: \`[[${dirName}/_director]]\` — 案件の進行・意思決定・直近ログ (未作成)`,
121
220
  "",
221
+ ]
222
+ if (repositorySection) {
223
+ sections.push(repositorySection)
224
+ }
225
+ sections.push(
122
226
  "## 初期化",
123
227
  "",
124
228
  "これは hub-agent の自動生成プレースホルダです。Hub director も未作成です。",
125
229
  "詳細を生成するには、このリポジトリで `claude` を起動して `/init-claude-md` スキルを実行してください。",
126
230
  "",
127
- ].join("\n")
231
+ )
232
+ return sections.join("\n")
128
233
  }
129
234
 
130
235
  /**
@@ -166,18 +271,19 @@ export async function ensureClaudeMd({
166
271
  const token =
167
272
  accessToken === undefined ? await loadAccessToken() : accessToken || null
168
273
  const url = hubUrl || DEFAULT_HUB_API
169
- const director = token
170
- ? await fetchDirector({
171
- hubUrl: url,
172
- accessToken: token,
173
- dirName,
174
- fetchImpl,
175
- })
176
- : null
274
+ // director repositories は独立して取れるので並列フェッチする。
275
+ // どちらも失敗時は null / [] にフォールバックする (token なしの場合も同様)。
276
+ const [director, repositories] = token
277
+ ? await Promise.all([
278
+ fetchDirector({ hubUrl: url, accessToken: token, dirName, fetchImpl }),
279
+ fetchRepositories({ hubUrl: url, accessToken: token, dirName, fetchImpl }),
280
+ ])
281
+ : [null, []]
177
282
 
283
+ const repositorySection = renderRepositorySection(repositories)
178
284
  const body = director
179
- ? renderWithDirector(dirName, director)
180
- : renderPlaceholder(dirName)
285
+ ? renderWithDirector(dirName, director, repositorySection)
286
+ : renderPlaceholder(dirName, repositorySection)
181
287
  const source = director ? "director" : "placeholder"
182
288
 
183
289
  await fs.writeFile(claudeMdPath, body, "utf-8")