@cocorograph/hub-agent 0.6.67 → 0.6.69
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.
- package/package.json +1 -1
- package/src/claude-md.mjs +145 -14
package/package.json
CHANGED
package/src/claude-md.mjs
CHANGED
|
@@ -51,7 +51,127 @@ async function fetchDirector({ hubUrl, accessToken, dirName, fetchImpl }) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
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, dirName = "") {
|
|
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
|
+
if (stagingName || productionName) {
|
|
142
|
+
const envExample = productionName ? "production" : "staging"
|
|
143
|
+
lines.push("**デプロイ接続情報の取得(秘匿値を会話に出さない)**:")
|
|
144
|
+
lines.push("")
|
|
145
|
+
lines.push("```bash")
|
|
146
|
+
lines.push(
|
|
147
|
+
"# 秘密鍵を ~/.ssh/hub_deploy/ に書き出し、ssh コマンドだけ受け取る",
|
|
148
|
+
)
|
|
149
|
+
lines.push(
|
|
150
|
+
`python3 ~/.claude/scripts/hub_helper.py deploy_creds ${dirName || "<dir_name>"} --env ${envExample}`,
|
|
151
|
+
)
|
|
152
|
+
lines.push("```")
|
|
153
|
+
lines.push("")
|
|
154
|
+
lines.push(
|
|
155
|
+
"返り値の `ssh_command` をそのまま使って接続します。`key_file` は",
|
|
156
|
+
"chmod 600 で書き出されます。パスワード認証のサーバーは `password_set: true`",
|
|
157
|
+
"だけ返るので、その場合は Hub の「サーバー情報」タブから人手で取得します。",
|
|
158
|
+
"",
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
lines.push("### デプロイ前の必須ガード", "")
|
|
164
|
+
lines.push(
|
|
165
|
+
"- **本番反映の前に必ず現状バックアップを取る**(DB ダンプ / 対象ディレクトリの退避)。",
|
|
166
|
+
"- ステージングで動作確認してから本番へ。",
|
|
167
|
+
"- 破壊的操作(`reset --hard` / `rm -rf` / DB 操作)の前に対象を確認する。",
|
|
168
|
+
"- Git 認証はローカルの `gh` CLI / SSH 鍵を使用します。",
|
|
169
|
+
"",
|
|
170
|
+
)
|
|
171
|
+
return lines.join("\n")
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function renderWithDirector(dirName, director, repositorySection = "") {
|
|
55
175
|
const fm = director?.frontmatter || {}
|
|
56
176
|
const client = (fm.client || "").trim()
|
|
57
177
|
const domain = (fm.domain || "").trim()
|
|
@@ -94,6 +214,10 @@ function renderWithDirector(dirName, director) {
|
|
|
94
214
|
}
|
|
95
215
|
lines.push("")
|
|
96
216
|
|
|
217
|
+
if (repositorySection) {
|
|
218
|
+
lines.push(repositorySection)
|
|
219
|
+
}
|
|
220
|
+
|
|
97
221
|
lines.push("## 初期化")
|
|
98
222
|
lines.push("")
|
|
99
223
|
lines.push(
|
|
@@ -109,8 +233,8 @@ function renderWithDirector(dirName, director) {
|
|
|
109
233
|
return lines.join("\n")
|
|
110
234
|
}
|
|
111
235
|
|
|
112
|
-
function renderPlaceholder(dirName) {
|
|
113
|
-
|
|
236
|
+
function renderPlaceholder(dirName, repositorySection = "") {
|
|
237
|
+
const sections = [
|
|
114
238
|
"# CLAUDE.md",
|
|
115
239
|
"",
|
|
116
240
|
"This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.",
|
|
@@ -119,12 +243,18 @@ function renderPlaceholder(dirName) {
|
|
|
119
243
|
"",
|
|
120
244
|
`- **Hub Director**: \`[[${dirName}/_director]]\` — 案件の進行・意思決定・直近ログ (未作成)`,
|
|
121
245
|
"",
|
|
246
|
+
]
|
|
247
|
+
if (repositorySection) {
|
|
248
|
+
sections.push(repositorySection)
|
|
249
|
+
}
|
|
250
|
+
sections.push(
|
|
122
251
|
"## 初期化",
|
|
123
252
|
"",
|
|
124
253
|
"これは hub-agent の自動生成プレースホルダです。Hub director も未作成です。",
|
|
125
254
|
"詳細を生成するには、このリポジトリで `claude` を起動して `/init-claude-md` スキルを実行してください。",
|
|
126
255
|
"",
|
|
127
|
-
|
|
256
|
+
)
|
|
257
|
+
return sections.join("\n")
|
|
128
258
|
}
|
|
129
259
|
|
|
130
260
|
/**
|
|
@@ -166,18 +296,19 @@ export async function ensureClaudeMd({
|
|
|
166
296
|
const token =
|
|
167
297
|
accessToken === undefined ? await loadAccessToken() : accessToken || null
|
|
168
298
|
const url = hubUrl || DEFAULT_HUB_API
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
dirName,
|
|
174
|
-
fetchImpl,
|
|
175
|
-
|
|
176
|
-
: null
|
|
299
|
+
// director と repositories は独立して取れるので並列フェッチする。
|
|
300
|
+
// どちらも失敗時は null / [] にフォールバックする (token なしの場合も同様)。
|
|
301
|
+
const [director, repositories] = token
|
|
302
|
+
? await Promise.all([
|
|
303
|
+
fetchDirector({ hubUrl: url, accessToken: token, dirName, fetchImpl }),
|
|
304
|
+
fetchRepositories({ hubUrl: url, accessToken: token, dirName, fetchImpl }),
|
|
305
|
+
])
|
|
306
|
+
: [null, []]
|
|
177
307
|
|
|
308
|
+
const repositorySection = renderRepositorySection(repositories, dirName)
|
|
178
309
|
const body = director
|
|
179
|
-
? renderWithDirector(dirName, director)
|
|
180
|
-
: renderPlaceholder(dirName)
|
|
310
|
+
? renderWithDirector(dirName, director, repositorySection)
|
|
311
|
+
: renderPlaceholder(dirName, repositorySection)
|
|
181
312
|
const source = director ? "director" : "placeholder"
|
|
182
313
|
|
|
183
314
|
await fs.writeFile(claudeMdPath, body, "utf-8")
|