helloagents 3.0.29 → 3.0.30
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +50 -37
- package/README_CN.md +50 -35
- package/bootstrap-lite.md +6 -6
- package/bootstrap.md +10 -8
- package/gemini-extension.json +1 -1
- package/install.ps1 +5 -5
- package/install.sh +5 -5
- package/package.json +1 -1
- package/scripts/advisor-state.mjs +1 -1
- package/scripts/cli-branch.mjs +7 -3
- package/scripts/cli-codex.mjs +6 -5
- package/scripts/cli-deepseek.mjs +131 -0
- package/scripts/cli-doctor-codex.mjs +10 -5
- package/scripts/cli-doctor-render.mjs +16 -0
- package/scripts/cli-doctor.mjs +78 -5
- package/scripts/cli-host-detect.mjs +29 -0
- package/scripts/cli-hosts.mjs +8 -0
- package/scripts/cli-lifecycle-hosts.mjs +28 -2
- package/scripts/cli-lifecycle.mjs +1 -1
- package/scripts/cli-messages.mjs +28 -15
- package/scripts/cli-runtime-carrier.mjs +3 -3
- package/scripts/cli-utils.mjs +9 -0
- package/scripts/notify-events.mjs +1 -0
- package/scripts/notify-route.mjs +3 -4
- package/scripts/notify-source.mjs +1 -0
- package/scripts/notify.mjs +6 -1
- package/scripts/plan-contract.mjs +1 -1
- package/scripts/project-storage.mjs +4 -4
- package/scripts/replay-state.mjs +22 -4
- package/scripts/runtime-context.mjs +14 -2
- package/scripts/runtime-scope.mjs +144 -2
- package/scripts/session-capsule.mjs +14 -0
- package/scripts/turn-state.mjs +7 -0
- package/skills/commands/build/SKILL.md +1 -1
- package/skills/commands/commit/SKILL.md +2 -2
- package/skills/commands/global/SKILL.md +71 -0
- package/skills/commands/help/SKILL.md +16 -15
- package/skills/commands/init/SKILL.md +14 -31
- package/skills/commands/wiki/SKILL.md +1 -1
- package/skills/hello-review/SKILL.md +1 -1
- package/skills/hello-ui/SKILL.md +5 -5
- package/skills/helloagents/SKILL.md +5 -4
package/bootstrap.md
CHANGED
|
@@ -209,7 +209,7 @@
|
|
|
209
209
|
- 审查 / 执行验证 → `~verify`
|
|
210
210
|
- 不确定或希望端到端自动推进时使用 `~auto`
|
|
211
211
|
|
|
212
|
-
|
|
212
|
+
当前项目只要已初始化(例如执行过 `~global`,或当前项目级规则文件已包含 `<!-- HELLOAGENTS_PROFILE: full -->`),就按项目级完整流程执行。
|
|
213
213
|
|
|
214
214
|
#### 2. SPEC — 澄清目标与验收
|
|
215
215
|
根据任务需要,按需读取项目上下文(知识库文件和项目文件),明确:
|
|
@@ -225,9 +225,9 @@
|
|
|
225
225
|
先确定当前技能根目录:
|
|
226
226
|
- 优先使用当前上下文中已注入的“本轮 HelloAGENTS 读取根目录”
|
|
227
227
|
- 若当前上下文未注入,则使用稳定运行根目录 `~/.helloagents/helloagents`
|
|
228
|
-
- 宿主固定链接(Codex `~/.codex/helloagents`、Claude `~/.claude/helloagents`、Gemini `~/.gemini/helloagents`)只作为兼容别名,不作为优先探测路径
|
|
228
|
+
- 宿主固定链接(Codex `~/.codex/helloagents`、Claude `~/.claude/helloagents`、Gemini `~/.gemini/helloagents`、DeepSeek `~/.deepseek/helloagents`)只作为兼容别名,不作为优先探测路径
|
|
229
229
|
- 仍无法确定时,明确说明缺少 HelloAGENTS 读取根目录;不要递归扫描 `$HOME`、`Downloads`、项目目录或旧版本目录
|
|
230
|
-
-
|
|
230
|
+
- 全局模式或已初始化项目时,技能是否需要使用由当前已加载 AGENTS 规则决定;不要因此额外探测项目目录里的 HelloAGENTS skills 路径
|
|
231
231
|
路径确定一次即可,不预读、不扫描整个目录,也不重复探测同一路径。
|
|
232
232
|
hello-* 技能读取路径:`{HELLOAGENTS_READ_ROOT}/skills/{技能名}/SKILL.md`
|
|
233
233
|
包内脚本优先使用稳定命令入口;涉及 turn-state 时按“收尾状态信号”执行。
|
|
@@ -265,8 +265,10 @@ hello-* 技能读取路径:`{HELLOAGENTS_READ_ROOT}/skills/{技能名}/SKILL.m
|
|
|
265
265
|
- 有方案包且准备报告完成 → 优先调用 `scripts/closeout-state.mjs write` 写当前会话 `artifacts/closeout.json`,记录“需求覆盖”和“交付清单”;每项写明 `PASS` / `BLOCKED` 与简要摘要,再进入最终交付
|
|
266
266
|
- 状态文件维护:按上文“流程状态”中的适用范围执行。属于“强制创建并持续更新”范围时,重写 `state_path` 指向的文件(“正在做什么”更新为已完成,清空关键上下文 / 下一步 / 阻塞项);属于“已有则更新”范围时,仅在文件已存在时重写;属于“不创建”范围时不生成此文件
|
|
267
267
|
- 有方案包且任务已完成 → 将整个 `plans/{feature}/` 目录归档到 `.helloagents/archive/YYYY-MM/`,并更新 `archive/_index.md`。清理当前会话临时文件(`artifacts/loop-results.tsv`、`capsule.json`、`events.jsonl`、`artifacts/loop-breaker.json`、`artifacts/verify.json`、`artifacts/review.json`、`artifacts/closeout.json`)
|
|
268
|
-
- 按 `kb_create_mode` 同步知识库(0=关闭 / 1
|
|
269
|
-
-
|
|
268
|
+
- 按 `kb_create_mode` 同步知识库(0=关闭 / 1=知识库已存在时自动同步,未创建则不自动补建 / 2=编码任务在知识库已存在或全局模式下自动创建或同步):
|
|
269
|
+
- `0` → 跳过
|
|
270
|
+
- `1` → 仅在知识库已存在时按模板增量同步;未创建则不自动补建
|
|
271
|
+
- `2` → 仅在编码任务中生效;知识库已存在时按模板增量同步;若知识库不存在但当前项目已处于全局模式,则按 templates/ 创建或补全 `context.md`、`guidelines.md`、`verify.yaml`、`CHANGELOG.md`、`modules/`
|
|
270
272
|
- 已存在但不完整(缺少上述核心文件)→ 按 templates/ 补全缺失文件,不覆盖已有文件
|
|
271
273
|
- 已存在且完整则按模板格式更新 `CHANGELOG.md`、相关 `modules/*.md`、增量经验 delta 追加
|
|
272
274
|
- 符合条件时触发 `hello-reflect`(详见 `hello-reflect` SKILL.md)
|
|
@@ -287,7 +289,7 @@ hello-* 技能读取路径:`{HELLOAGENTS_READ_ROOT}/skills/{技能名}/SKILL.m
|
|
|
287
289
|
### .helloagents/ 目录
|
|
288
290
|
路径: {CWD}/.helloagents/
|
|
289
291
|
所有文件的创建和更新必须按 templates/ 目录中对应模板的格式执行,不可自由发挥格式。
|
|
290
|
-
- `.helloagents/`
|
|
292
|
+
- `.helloagents/` 表示项目本地存储路径,负责知识、方案、状态与运行态;它不再作为项目是否已初始化的判定信号
|
|
291
293
|
- `state_path` 指向的状态文件、当前会话 `capsule.json`、`events.jsonl`、`artifacts/*.json`、`artifacts/loop-results.tsv` 等运行态文件始终保留在项目本地 `.helloagents/sessions/{workspace}/{session}/`
|
|
292
294
|
- `state_path` 是状态文件的唯一位置。宿主提供会话标识时,写入 `.helloagents/sessions/{workspace}/{session}/STATE.md`;没有稳定会话标识时,写入 `.helloagents/sessions/{workspace}/default/STATE.md`
|
|
293
295
|
- `{workspace}` 为当前 Git 分支、`detached-{sha}` 或非 Git 项目的 `workspace`;`.helloagents/sessions/active.json` 只记录当前活跃会话索引,避免同一会话被拆成多个目录
|
|
@@ -299,7 +301,7 @@ templates/ 查找路径(按优先级;首次确定模板根目录后,本轮
|
|
|
299
301
|
- 状态文件(`state_path`)— ≤70 行,用来记录“上次做到哪里”。判断当前任务时,当前用户消息、显式命令、活跃方案包 / PRD、代码与验证证据优先于状态文件
|
|
300
302
|
内容:主线目标、正在做什么、关键上下文(决策/变更/假设)、下一步(具体可执行动作含文件路径)、阻塞项
|
|
301
303
|
适用边界:
|
|
302
|
-
- 强制创建并持续更新:`~wiki`、`~init`、`~plan`、`~build`、`~auto`、`~prd`、`~loop
|
|
304
|
+
- 强制创建并持续更新:`~wiki`、`~init`、`~global`、`~plan`、`~build`、`~auto`、`~prd`、`~loop`,以及进入工作流阶段、已初始化项目的连续任务,或任何会创建/修改本地文件、会在当前工作区留下实际输出或操作记录的非只读任务
|
|
303
305
|
- 强制更新,不要求首次创建:`~clean`,主代理汇总子代理结果后
|
|
304
306
|
- 已有则更新:`~verify`、`~review`(兼容别名)、`~test`、`~commit`
|
|
305
307
|
- 不创建:`~help`、`~idea`、普通问答、一次性只读任务、子代理自身执行过程、压缩/恢复钩子
|
|
@@ -320,7 +322,7 @@ templates/ 查找路径(按优先级;首次确定模板根目录后,本轮
|
|
|
320
322
|
- archive/_index.md — 归档索引
|
|
321
323
|
|
|
322
324
|
### 知识记录(受 `kb_create_mode` 控制)
|
|
323
|
-
- 0=关闭;1
|
|
325
|
+
- 0=关闭;1=知识库已存在时自动同步;2=编码任务在知识库已存在或全局模式下自动创建或同步
|
|
324
326
|
- context.md — 项目架构、技术栈、目录结构、模块索引
|
|
325
327
|
- guidelines.md — 编码约定(仅含非显而易见的约定)
|
|
326
328
|
- CHANGELOG.md — 变更历史
|
package/gemini-extension.json
CHANGED
package/install.ps1
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# HelloAGENTS one-shot installer.
|
|
2
2
|
#
|
|
3
3
|
# Environment:
|
|
4
|
-
# HELLOAGENTS=all|claude|gemini|codex[:standby|global]
|
|
4
|
+
# HELLOAGENTS=all|claude|gemini|codex|deepseek[:standby|global]
|
|
5
5
|
# HELLOAGENTS_ACTION=install|update|cleanup|uninstall|switch-branch|branch
|
|
6
|
-
# HELLOAGENTS_TARGET=all|claude|gemini|codex
|
|
6
|
+
# HELLOAGENTS_TARGET=all|claude|gemini|codex|deepseek
|
|
7
7
|
# HELLOAGENTS_MODE=standby|global
|
|
8
8
|
# HELLOAGENTS_BRANCH=main|beta|...
|
|
9
|
-
# HELLOAGENTS_PACKAGE=helloagents|github
|
|
9
|
+
# HELLOAGENTS_PACKAGE=helloagents|https://github.com/owner/repo/archive/refs/heads/ref.tar.gz|...
|
|
10
10
|
|
|
11
11
|
$ErrorActionPreference = "Stop"
|
|
12
12
|
|
|
@@ -29,7 +29,7 @@ if (-not $Target) { $Target = "all" }
|
|
|
29
29
|
$Target = $Target.ToLowerInvariant()
|
|
30
30
|
if ($Mode) { $Mode = $Mode.ToLowerInvariant() }
|
|
31
31
|
|
|
32
|
-
if (@("all", "claude", "gemini", "codex") -notcontains $Target) {
|
|
32
|
+
if (@("all", "claude", "gemini", "codex", "deepseek") -notcontains $Target) {
|
|
33
33
|
throw "Unsupported HELLOAGENTS target: $Target"
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -39,7 +39,7 @@ if ($Mode -and @("standby", "global") -notcontains $Mode) {
|
|
|
39
39
|
|
|
40
40
|
if (-not $Package) {
|
|
41
41
|
if ($Branch) {
|
|
42
|
-
$Package = "github
|
|
42
|
+
$Package = "https://github.com/hellowind777/helloagents/archive/refs/heads/$Branch.tar.gz"
|
|
43
43
|
} else {
|
|
44
44
|
$Package = "helloagents"
|
|
45
45
|
}
|
package/install.sh
CHANGED
|
@@ -4,12 +4,12 @@ set -eu
|
|
|
4
4
|
# HelloAGENTS one-shot installer.
|
|
5
5
|
#
|
|
6
6
|
# Environment:
|
|
7
|
-
# HELLOAGENTS=all|claude|gemini|codex[:standby|global]
|
|
7
|
+
# HELLOAGENTS=all|claude|gemini|codex|deepseek[:standby|global]
|
|
8
8
|
# HELLOAGENTS_ACTION=install|update|cleanup|uninstall|switch-branch|branch
|
|
9
|
-
# HELLOAGENTS_TARGET=all|claude|gemini|codex
|
|
9
|
+
# HELLOAGENTS_TARGET=all|claude|gemini|codex|deepseek
|
|
10
10
|
# HELLOAGENTS_MODE=standby|global
|
|
11
11
|
# HELLOAGENTS_BRANCH=main|beta|...
|
|
12
|
-
# HELLOAGENTS_PACKAGE=helloagents|github
|
|
12
|
+
# HELLOAGENTS_PACKAGE=helloagents|https://github.com/owner/repo/archive/refs/heads/ref.tar.gz|...
|
|
13
13
|
|
|
14
14
|
ACTION="${HELLOAGENTS_ACTION:-install}"
|
|
15
15
|
TARGET="${HELLOAGENTS_TARGET:-}"
|
|
@@ -36,7 +36,7 @@ TARGET="$(printf '%s' "$TARGET" | tr '[:upper:]' '[:lower:]')"
|
|
|
36
36
|
MODE="$(printf '%s' "$MODE" | tr '[:upper:]' '[:lower:]')"
|
|
37
37
|
|
|
38
38
|
case "$TARGET" in
|
|
39
|
-
all|claude|gemini|codex) ;;
|
|
39
|
+
all|claude|gemini|codex|deepseek) ;;
|
|
40
40
|
*) echo "Unsupported HELLOAGENTS target: $TARGET" >&2; exit 1 ;;
|
|
41
41
|
esac
|
|
42
42
|
|
|
@@ -49,7 +49,7 @@ fi
|
|
|
49
49
|
|
|
50
50
|
if [ -z "$PACKAGE" ]; then
|
|
51
51
|
if [ -n "$BRANCH" ]; then
|
|
52
|
-
PACKAGE="github
|
|
52
|
+
PACKAGE="https://github.com/hellowind777/helloagents/archive/refs/heads/$BRANCH.tar.gz"
|
|
53
53
|
else
|
|
54
54
|
PACKAGE="helloagents"
|
|
55
55
|
fi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "helloagents",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.30",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "HelloAGENTS — The orchestration kernel that makes any AI CLI smarter. Adds intelligent routing, quality verification (Ralph Loop), safety guards, and notifications.",
|
|
6
6
|
"author": "HelloWind",
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
|
|
16
16
|
export const ADVISOR_EVIDENCE_FILE_NAME = 'advisor.json'
|
|
17
17
|
const VALID_ADVISOR_OUTCOMES = new Set(['clean', 'findings'])
|
|
18
|
-
const VALID_SOURCES = new Set(['claude', 'codex', 'gemini'])
|
|
18
|
+
const VALID_SOURCES = new Set(['claude', 'codex', 'gemini', 'deepseek'])
|
|
19
19
|
|
|
20
20
|
function normalizeStringArray(values) {
|
|
21
21
|
if (!Array.isArray(values)) return []
|
package/scripts/cli-branch.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { spawnSync } from 'node:child_process'
|
|
|
2
2
|
|
|
3
3
|
import { normalizeHost } from './cli-lifecycle.mjs'
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const DEFAULT_REPO_ARCHIVE_BASE = 'https://github.com/hellowind777/helloagents/archive/refs/heads'
|
|
6
6
|
|
|
7
7
|
function runCommand(command, args) {
|
|
8
8
|
const needsShell = process.platform === 'win32' && /\.cmd$/i.test(command)
|
|
@@ -19,6 +19,10 @@ function runCommand(command, args) {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function getDefaultNpmCommand() {
|
|
23
|
+
return process.platform === 'win32' ? 'npm.cmd' : 'npm'
|
|
24
|
+
}
|
|
25
|
+
|
|
22
26
|
function parseModeFlag(args) {
|
|
23
27
|
const hasGlobal = args.includes('--global')
|
|
24
28
|
const hasStandby = args.includes('--standby')
|
|
@@ -56,7 +60,7 @@ function parseBranchArgs(args) {
|
|
|
56
60
|
|
|
57
61
|
function buildPackageSpec(ref) {
|
|
58
62
|
if (/^(github:|git\+|https?:|file:)/i.test(ref)) return ref
|
|
59
|
-
return `${
|
|
63
|
+
return `${DEFAULT_REPO_ARCHIVE_BASE}/${ref}.tar.gz`
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
function buildSyncArgs({ host, mode }) {
|
|
@@ -76,7 +80,7 @@ function buildSyncArgs({ host, mode }) {
|
|
|
76
80
|
|
|
77
81
|
export function runBranchSwitch(args, options = {}) {
|
|
78
82
|
const parsed = parseBranchArgs(args)
|
|
79
|
-
const npmCommand = options.npmCommand || process.env.HELLOAGENTS_NPM_CMD ||
|
|
83
|
+
const npmCommand = options.npmCommand || process.env.HELLOAGENTS_NPM_CMD || getDefaultNpmCommand()
|
|
80
84
|
|
|
81
85
|
const packageSpec = buildPackageSpec(parsed.branch)
|
|
82
86
|
runCommand(npmCommand, ['install', '-g', packageSpec])
|
package/scripts/cli-codex.mjs
CHANGED
|
@@ -121,17 +121,17 @@ function removeCodexMarketplaceEntry(marketplaceFile) {
|
|
|
121
121
|
return true;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
function injectCodexRuntimeCarrier(filePath, bootstrapPath, settings) {
|
|
124
|
+
function injectCodexRuntimeCarrier(filePath, bootstrapPath, settings, options = {}) {
|
|
125
125
|
const bootstrapContent = safeRead(bootstrapPath);
|
|
126
126
|
if (!bootstrapContent) return false;
|
|
127
|
-
injectMarkedContent(filePath, buildRuntimeCarrier(bootstrapContent, settings).trimEnd());
|
|
127
|
+
injectMarkedContent(filePath, buildRuntimeCarrier(bootstrapContent, settings, options).trimEnd());
|
|
128
128
|
return true;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
function writeCodexRuntimeCarrier(filePath, bootstrapPath, settings) {
|
|
131
|
+
function writeCodexRuntimeCarrier(filePath, bootstrapPath, settings, options = {}) {
|
|
132
132
|
const bootstrapContent = safeRead(bootstrapPath);
|
|
133
133
|
if (!bootstrapContent) return false;
|
|
134
|
-
safeWrite(filePath, buildRuntimeCarrier(bootstrapContent, settings));
|
|
134
|
+
safeWrite(filePath, buildRuntimeCarrier(bootstrapContent, settings, options));
|
|
135
135
|
return true;
|
|
136
136
|
}
|
|
137
137
|
|
|
@@ -298,9 +298,10 @@ export function installCodexGlobal(home, pkgRoot) {
|
|
|
298
298
|
join(pkgRoot, CODEX_RUNTIME_CARRIER),
|
|
299
299
|
join(pkgRoot, 'bootstrap.md'),
|
|
300
300
|
settings,
|
|
301
|
+
{ profile: 'full' },
|
|
301
302
|
);
|
|
302
303
|
const homeCarrierPath = join(codexDir, CODEX_RUNTIME_CARRIER);
|
|
303
|
-
injectCodexRuntimeCarrier(homeCarrierPath, join(pkgRoot, 'bootstrap.md'), settings);
|
|
304
|
+
injectCodexRuntimeCarrier(homeCarrierPath, join(pkgRoot, 'bootstrap.md'), settings, { profile: 'full' });
|
|
304
305
|
|
|
305
306
|
ensureDir(join(home, '.agents', 'plugins'));
|
|
306
307
|
updateCodexMarketplace(marketplaceFile);
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process'
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
|
|
5
|
+
import { buildRuntimeCarrier, readCarrierSettings } from './cli-runtime-carrier.mjs'
|
|
6
|
+
import { createLink, ensureDir, injectMarkedContent, removeLink, removeMarkedContent, safeRead } from './cli-utils.mjs'
|
|
7
|
+
|
|
8
|
+
export const DEEPSEEK_COMMAND = process.env.HELLOAGENTS_DEEPSEEK_CMD || 'deepseek'
|
|
9
|
+
|
|
10
|
+
function looksLikeCommandPath(command = '') {
|
|
11
|
+
return /[\\/]/.test(command) || /^[A-Za-z]:/.test(command)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function injectDeepseekCarrier(home, pkgRoot, bootstrapFile, options = {}) {
|
|
15
|
+
const deepseekDir = join(home, '.deepseek')
|
|
16
|
+
ensureDir(deepseekDir)
|
|
17
|
+
|
|
18
|
+
const bootstrapContent = safeRead(join(pkgRoot, bootstrapFile))
|
|
19
|
+
if (!bootstrapContent) return false
|
|
20
|
+
|
|
21
|
+
injectMarkedContent(
|
|
22
|
+
join(deepseekDir, 'AGENTS.md'),
|
|
23
|
+
buildRuntimeCarrier(bootstrapContent, readCarrierSettings(home), options).trimEnd(),
|
|
24
|
+
)
|
|
25
|
+
createLink(pkgRoot, join(deepseekDir, 'helloagents'))
|
|
26
|
+
return true
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function installDeepseekStandby(home, pkgRoot) {
|
|
30
|
+
return injectDeepseekCarrier(home, pkgRoot, 'bootstrap-lite.md')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function installDeepseekGlobal(home, pkgRoot) {
|
|
34
|
+
return injectDeepseekCarrier(home, pkgRoot, 'bootstrap.md', { profile: 'full' })
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function uninstallDeepseekStandby(home) {
|
|
38
|
+
const deepseekDir = join(home, '.deepseek')
|
|
39
|
+
if (!existsSync(deepseekDir)) return false
|
|
40
|
+
|
|
41
|
+
removeMarkedContent(join(deepseekDir, 'AGENTS.md'))
|
|
42
|
+
removeLink(join(deepseekDir, 'helloagents'))
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function uninstallDeepseekGlobal(home) {
|
|
47
|
+
return uninstallDeepseekStandby(home)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function runDeepseekCommand(command, args = []) {
|
|
51
|
+
if (looksLikeCommandPath(command) && !existsSync(command)) {
|
|
52
|
+
return {
|
|
53
|
+
ok: false,
|
|
54
|
+
missing: true,
|
|
55
|
+
exitCode: null,
|
|
56
|
+
stdout: '',
|
|
57
|
+
stderr: '',
|
|
58
|
+
errorMessage: '',
|
|
59
|
+
combinedOutput: '',
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const needsShell = process.platform === 'win32' && /\.cmd$/i.test(command)
|
|
63
|
+
const result = spawnSync(command, args, {
|
|
64
|
+
encoding: 'utf-8',
|
|
65
|
+
errors: 'replace',
|
|
66
|
+
shell: needsShell,
|
|
67
|
+
windowsHide: true,
|
|
68
|
+
})
|
|
69
|
+
const stdout = String(result.stdout || '').trim()
|
|
70
|
+
const stderr = String(result.stderr || '').trim()
|
|
71
|
+
const errorMessage = result.error?.message || ''
|
|
72
|
+
return {
|
|
73
|
+
ok: result.status === 0,
|
|
74
|
+
missing: result.error?.code === 'ENOENT',
|
|
75
|
+
exitCode: typeof result.status === 'number' ? result.status : null,
|
|
76
|
+
stdout,
|
|
77
|
+
stderr,
|
|
78
|
+
errorMessage,
|
|
79
|
+
combinedOutput: [stdout, stderr, errorMessage].filter(Boolean).join('\n').trim(),
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function tryParseJson(text = '') {
|
|
84
|
+
if (!text) return null
|
|
85
|
+
try {
|
|
86
|
+
return JSON.parse(text)
|
|
87
|
+
} catch {
|
|
88
|
+
return null
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function buildDoctorSummary(report = {}) {
|
|
93
|
+
const skills = report.skills || {}
|
|
94
|
+
const capability = report.capability || {}
|
|
95
|
+
const sandbox = report.sandbox || {}
|
|
96
|
+
const mcp = report.mcp || {}
|
|
97
|
+
return {
|
|
98
|
+
version: typeof report.version === 'string' ? report.version : '',
|
|
99
|
+
configPath: typeof report.config_path === 'string' ? report.config_path : '',
|
|
100
|
+
skillsSelected: Array.isArray(skills.selected) ? skills.selected : [],
|
|
101
|
+
mcpPresent: Boolean(mcp.present),
|
|
102
|
+
sandboxAvailable: typeof sandbox.available === 'boolean' ? sandbox.available : null,
|
|
103
|
+
resolvedProvider: typeof capability.resolved_provider === 'string' ? capability.resolved_provider : '',
|
|
104
|
+
resolvedModel: typeof capability.resolved_model === 'string' ? capability.resolved_model : '',
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function inspectNativeDeepseekDoctor() {
|
|
109
|
+
const result = runDeepseekCommand(DEEPSEEK_COMMAND, ['doctor', '--json'])
|
|
110
|
+
if (result.missing) {
|
|
111
|
+
return {
|
|
112
|
+
available: false,
|
|
113
|
+
command: DEEPSEEK_COMMAND,
|
|
114
|
+
exitCode: null,
|
|
115
|
+
ok: false,
|
|
116
|
+
summary: null,
|
|
117
|
+
note: 'command-not-found',
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const parsed = tryParseJson(result.stdout) || tryParseJson(result.combinedOutput)
|
|
122
|
+
return {
|
|
123
|
+
available: true,
|
|
124
|
+
command: DEEPSEEK_COMMAND,
|
|
125
|
+
exitCode: result.exitCode,
|
|
126
|
+
ok: result.ok,
|
|
127
|
+
summary: parsed ? buildDoctorSummary(parsed) : null,
|
|
128
|
+
parseError: parsed ? '' : (result.combinedOutput ? 'invalid-json' : ''),
|
|
129
|
+
output: parsed ? '' : result.combinedOutput,
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -64,9 +64,9 @@ function readExpectedHooks(runtime, hooksFile, pathVar) {
|
|
|
64
64
|
return pickManagedHooks(loadHooksWithCliEntry(runtime.pkgRoot, hooksFile, pathVar)?.hooks || {})
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
function readExpectedCarrierContent(runtime, fileName, settings) {
|
|
67
|
+
function readExpectedCarrierContent(runtime, fileName, settings, options = {}) {
|
|
68
68
|
const bootstrap = safeRead(join(runtime.pkgRoot, fileName)) || ''
|
|
69
|
-
return normalizeText(buildRuntimeCarrier(bootstrap, settings))
|
|
69
|
+
return normalizeText(buildRuntimeCarrier(bootstrap, settings, options))
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function buildDoctorIssue(runtime, code, cn, en) {
|
|
@@ -168,7 +168,12 @@ function buildCodexChecks(runtime, settings, trackedMode, detectedMode) {
|
|
|
168
168
|
checks: {
|
|
169
169
|
carrierMarker: (safeRead(join(codexDir, 'AGENTS.md')) || '').includes('HELLOAGENTS_START'),
|
|
170
170
|
carrierContentMatch: normalizeText((safeRead(join(codexDir, 'AGENTS.md')) || '').match(/<!-- HELLOAGENTS_START -->([\s\S]*?)<!-- HELLOAGENTS_END -->/)?.[1] || '')
|
|
171
|
-
=== readExpectedCarrierContent(
|
|
171
|
+
=== readExpectedCarrierContent(
|
|
172
|
+
runtime,
|
|
173
|
+
expectedHomeCarrier,
|
|
174
|
+
settings,
|
|
175
|
+
expectedHomeCarrier === 'bootstrap.md' ? { profile: 'full' } : {},
|
|
176
|
+
),
|
|
172
177
|
homeLink: homeLinkTarget === (safeRealTarget(runtime.pkgRoot) || normalizePath(runtime.pkgRoot)),
|
|
173
178
|
globalHomeLink: homeLinkTarget === runtimeRoot,
|
|
174
179
|
modelInstructionsFile: !!modelInstructionsLine,
|
|
@@ -187,8 +192,8 @@ function buildCodexChecks(runtime, settings, trackedMode, detectedMode) {
|
|
|
187
192
|
pluginCache: existsSync(pluginCacheRoot),
|
|
188
193
|
pluginRootLink: pluginRootTarget === runtimeRoot,
|
|
189
194
|
pluginCacheLink: pluginCacheTarget === runtimeRoot,
|
|
190
|
-
pluginCarrierMatch: normalizeText(safeRead(join(pluginRoot, 'AGENTS.md')) || '') === readExpectedCarrierContent(runtime, 'bootstrap.md', settings),
|
|
191
|
-
pluginCacheCarrierMatch: normalizeText(safeRead(join(pluginCacheRoot, 'AGENTS.md')) || '') === readExpectedCarrierContent(runtime, 'bootstrap.md', settings),
|
|
195
|
+
pluginCarrierMatch: normalizeText(safeRead(join(pluginRoot, 'AGENTS.md')) || '') === readExpectedCarrierContent(runtime, 'bootstrap.md', settings, { profile: 'full' }),
|
|
196
|
+
pluginCacheCarrierMatch: normalizeText(safeRead(join(pluginCacheRoot, 'AGENTS.md')) || '') === readExpectedCarrierContent(runtime, 'bootstrap.md', settings, { profile: 'full' }),
|
|
192
197
|
marketplaceEntry: Array.isArray(marketplace.plugins) && marketplace.plugins.some((plugin) => plugin?.name === CODEX_PLUGIN_NAME),
|
|
193
198
|
pluginEnabled: codexConfig.includes(CODEX_PLUGIN_CONFIG_HEADER) && codexConfig.includes('enabled = true'),
|
|
194
199
|
globalNotifyPathMatch: codexConfig.includes(CODEX_MANAGED_NOTIFY_VALUE),
|
|
@@ -17,6 +17,22 @@ export function printDoctorText(runtime, report) {
|
|
|
17
17
|
for (const [key, value] of Object.entries(entry.checks)) {
|
|
18
18
|
console.log(` ${key}: ${value ? 'ok' : 'missing'}`)
|
|
19
19
|
}
|
|
20
|
+
if (entry.nativeDoctor) {
|
|
21
|
+
console.log(` native_doctor.available: ${entry.nativeDoctor.available ? 'ok' : 'missing'}`)
|
|
22
|
+
if (entry.nativeDoctor.available) {
|
|
23
|
+
console.log(` native_doctor.ok: ${entry.nativeDoctor.ok ? 'ok' : 'missing'}`)
|
|
24
|
+
}
|
|
25
|
+
if (entry.nativeDoctor.summary) {
|
|
26
|
+
if (entry.nativeDoctor.summary.version) console.log(` native_doctor.version: ${entry.nativeDoctor.summary.version}`)
|
|
27
|
+
if (entry.nativeDoctor.summary.configPath) console.log(` native_doctor.config_path: ${entry.nativeDoctor.summary.configPath}`)
|
|
28
|
+
if (entry.nativeDoctor.summary.resolvedProvider) console.log(` native_doctor.resolved_provider: ${entry.nativeDoctor.summary.resolvedProvider}`)
|
|
29
|
+
if (entry.nativeDoctor.summary.resolvedModel) console.log(` native_doctor.resolved_model: ${entry.nativeDoctor.summary.resolvedModel}`)
|
|
30
|
+
if (entry.nativeDoctor.summary.sandboxAvailable !== null) console.log(` native_doctor.sandbox_available: ${entry.nativeDoctor.summary.sandboxAvailable}`)
|
|
31
|
+
console.log(` native_doctor.mcp_present: ${entry.nativeDoctor.summary.mcpPresent ? 'ok' : 'missing'}`)
|
|
32
|
+
console.log(` native_doctor.skills_selected: ${entry.nativeDoctor.summary.skillsSelected.join(', ') || '(none)'}`)
|
|
33
|
+
}
|
|
34
|
+
if (entry.nativeDoctor.output) console.log(` native_doctor.output: ${entry.nativeDoctor.output}`)
|
|
35
|
+
}
|
|
20
36
|
for (const note of entry.notes) {
|
|
21
37
|
console.log(` note: ${note}`)
|
|
22
38
|
}
|
package/scripts/cli-doctor.mjs
CHANGED
|
@@ -3,10 +3,10 @@ import { join } from 'node:path'
|
|
|
3
3
|
|
|
4
4
|
import { DEFAULTS } from './cli-config.mjs'
|
|
5
5
|
import { inspectCodexDoctor as inspectCodexDoctorImpl } from './cli-doctor-codex.mjs'
|
|
6
|
+
import { inspectNativeDeepseekDoctor } from './cli-deepseek.mjs'
|
|
6
7
|
import { printDoctorText } from './cli-doctor-render.mjs'
|
|
7
8
|
import { buildRuntimeCarrier } from './cli-runtime-carrier.mjs'
|
|
8
|
-
import {
|
|
9
|
-
import { loadHooksWithCliEntry, safeJson, safeRead } from './cli-utils.mjs'
|
|
9
|
+
import { FULL_CARRIER_PROFILE_MARKER, loadHooksWithCliEntry, safeJson, safeRead } from './cli-utils.mjs'
|
|
10
10
|
|
|
11
11
|
const runtime = {
|
|
12
12
|
home: '',
|
|
@@ -78,9 +78,9 @@ function managedHooksMatch(actualHooks, expectedHooks) {
|
|
|
78
78
|
return stringifySorted(pickManagedHooks(actualHooks || {})) === stringifySorted(expectedHooks || {})
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
function readExpectedCarrierContent(fileName, settings) {
|
|
81
|
+
function readExpectedCarrierContent(fileName, settings, options = {}) {
|
|
82
82
|
const bootstrap = safeRead(join(runtime.pkgRoot, fileName)) || ''
|
|
83
|
-
return normalizeText(buildRuntimeCarrier(bootstrap, settings))
|
|
83
|
+
return normalizeText(buildRuntimeCarrier(bootstrap, settings, options))
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
function buildDoctorIssue(code, cn, en) {
|
|
@@ -219,6 +219,78 @@ function inspectGeminiDoctor(settings) {
|
|
|
219
219
|
return { host, label: runtime.getHostLabel(host), trackedMode, detectedMode, status, checks, issues, notes, suggestedFix: suggestDoctorFix(host, status, trackedMode) }
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
function inspectDeepseekDoctor(settings) {
|
|
223
|
+
const host = 'deepseek'
|
|
224
|
+
const trackedMode = normalizeDoctorMode(runtime.getTrackedHostMode(settings, host))
|
|
225
|
+
const detectedMode = normalizeDoctorMode(runtime.detectHostMode(host))
|
|
226
|
+
const deepseekDir = join(runtime.home, '.deepseek')
|
|
227
|
+
const carrierPath = join(deepseekDir, 'AGENTS.md')
|
|
228
|
+
const expectedCarrierFile = (detectedMode === 'global' || (detectedMode === 'none' && trackedMode === 'global'))
|
|
229
|
+
? 'bootstrap.md'
|
|
230
|
+
: 'bootstrap-lite.md'
|
|
231
|
+
const checks = {
|
|
232
|
+
carrierMarker: (safeRead(carrierPath) || '').includes('HELLOAGENTS_START'),
|
|
233
|
+
carrierContentMatch: extractManagedCarrierContent(carrierPath)
|
|
234
|
+
=== readExpectedCarrierContent(
|
|
235
|
+
expectedCarrierFile,
|
|
236
|
+
settings,
|
|
237
|
+
expectedCarrierFile === 'bootstrap.md' ? { profile: 'full' } : {},
|
|
238
|
+
),
|
|
239
|
+
homeLink: safeRealTarget(join(deepseekDir, 'helloagents')) === runtime.pkgRoot,
|
|
240
|
+
fullProfile: (safeRead(carrierPath) || '').includes(FULL_CARRIER_PROFILE_MARKER),
|
|
241
|
+
}
|
|
242
|
+
const nativeDoctor = inspectNativeDeepseekDoctor()
|
|
243
|
+
const issues = []
|
|
244
|
+
const notes = []
|
|
245
|
+
|
|
246
|
+
if (trackedMode !== 'none' && detectedMode !== 'none' && trackedMode !== detectedMode) {
|
|
247
|
+
issues.push(buildDoctorIssue('tracked-mode-mismatch', '记录模式与检测模式不一致', 'Tracked mode does not match detected mode'))
|
|
248
|
+
}
|
|
249
|
+
if (detectedMode === 'standby') {
|
|
250
|
+
if (!checks.carrierMarker) issues.push(buildDoctorIssue('standby-carrier-missing', 'standby `~/.deepseek/AGENTS.md` 缺少 HelloAGENTS 标记', 'Standby `~/.deepseek/AGENTS.md` is missing the HELLOAGENTS marker'))
|
|
251
|
+
if (checks.carrierMarker && !checks.carrierContentMatch) issues.push(buildDoctorIssue('standby-carrier-drift', 'standby `~/.deepseek/AGENTS.md` 与当前标准模式规则不一致', 'Standby `~/.deepseek/AGENTS.md` differs from the current standby rules'))
|
|
252
|
+
if (!checks.homeLink) issues.push(buildDoctorIssue('standby-link-missing', 'standby `~/.deepseek/helloagents` 链接缺失或未指向稳定运行根目录', 'Standby `~/.deepseek/helloagents` link is missing or points to a different runtime root'))
|
|
253
|
+
if (checks.fullProfile) issues.push(buildDoctorIssue('standby-full-profile-residue', 'standby 模式下不应保留 full 标记', 'Standby mode should not keep the full profile marker'))
|
|
254
|
+
}
|
|
255
|
+
if (detectedMode === 'global') {
|
|
256
|
+
if (!checks.carrierMarker) issues.push(buildDoctorIssue('global-carrier-missing', 'global `~/.deepseek/AGENTS.md` 缺少 HelloAGENTS 标记', 'Global `~/.deepseek/AGENTS.md` is missing the HELLOAGENTS marker'))
|
|
257
|
+
if (checks.carrierMarker && !checks.carrierContentMatch) issues.push(buildDoctorIssue('global-carrier-drift', 'global `~/.deepseek/AGENTS.md` 与当前全局模式规则不一致', 'Global `~/.deepseek/AGENTS.md` differs from the current global rules'))
|
|
258
|
+
if (!checks.homeLink) issues.push(buildDoctorIssue('global-link-missing', 'global `~/.deepseek/helloagents` 链接缺失或未指向稳定运行根目录', 'Global `~/.deepseek/helloagents` link is missing or points to a different runtime root'))
|
|
259
|
+
if (!checks.fullProfile) issues.push(buildDoctorIssue('global-full-profile-missing', 'global 模式缺少 full 标记', 'Global mode is missing the full profile marker'))
|
|
260
|
+
}
|
|
261
|
+
if (trackedMode === 'none' && detectedMode !== 'none') {
|
|
262
|
+
issues.push(buildDoctorIssue('untracked-managed-state', '检测到受管状态,但配置中未记录该 CLI 模式', 'Managed state detected but this CLI mode is not tracked in config'))
|
|
263
|
+
}
|
|
264
|
+
if (trackedMode !== 'none' && detectedMode === 'none') {
|
|
265
|
+
issues.push(buildDoctorIssue('tracked-state-missing', '配置记录该 CLI 已安装,但未检测到对应的受管文件或配置', 'Config says this CLI is installed, but no managed artifacts were detected'))
|
|
266
|
+
}
|
|
267
|
+
if (!nativeDoctor.available) {
|
|
268
|
+
notes.push(runtime.msg(
|
|
269
|
+
'未找到 deepseek 命令;已跳过 DeepSeek 原生 doctor。',
|
|
270
|
+
'The deepseek command was not found; DeepSeek native doctor was skipped.',
|
|
271
|
+
))
|
|
272
|
+
} else if (!nativeDoctor.ok) {
|
|
273
|
+
issues.push(buildDoctorIssue('native-doctor-failed', 'DeepSeek 原生 doctor 返回非零状态', 'DeepSeek native doctor returned a non-zero status'))
|
|
274
|
+
}
|
|
275
|
+
if (nativeDoctor.available && nativeDoctor.parseError) {
|
|
276
|
+
issues.push(buildDoctorIssue('native-doctor-invalid-json', 'DeepSeek 原生 doctor 输出不是可解析的 JSON', 'DeepSeek native doctor output was not valid JSON'))
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const status = summarizeDoctorStatus(issues, { host, trackedMode, detectedMode })
|
|
280
|
+
return {
|
|
281
|
+
host,
|
|
282
|
+
label: runtime.getHostLabel(host),
|
|
283
|
+
trackedMode,
|
|
284
|
+
detectedMode,
|
|
285
|
+
status,
|
|
286
|
+
checks,
|
|
287
|
+
issues,
|
|
288
|
+
notes,
|
|
289
|
+
nativeDoctor,
|
|
290
|
+
suggestedFix: suggestDoctorFix(host, status, trackedMode),
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
222
294
|
function parseDoctorArgs(args) {
|
|
223
295
|
const wantsJson = args.includes('--json')
|
|
224
296
|
const unknownFlags = args.filter((arg) => arg.startsWith('--') && arg !== '--json' && arg !== '--all')
|
|
@@ -239,12 +311,13 @@ function parseDoctorArgs(args) {
|
|
|
239
311
|
function inspectDoctorHost(host, settings) {
|
|
240
312
|
if (host === 'claude') return inspectClaudeDoctor(settings)
|
|
241
313
|
if (host === 'gemini') return inspectGeminiDoctor(settings)
|
|
314
|
+
if (host === 'deepseek') return inspectDeepseekDoctor(settings)
|
|
242
315
|
return inspectCodexDoctorImpl(runtime, settings)
|
|
243
316
|
}
|
|
244
317
|
|
|
245
318
|
function buildDoctorReport(host) {
|
|
246
319
|
const settings = runtime.readSettings(true)
|
|
247
|
-
const hosts = host === 'all' ? ['claude', 'gemini', 'codex'] : [host]
|
|
320
|
+
const hosts = host === 'all' ? ['claude', 'gemini', 'codex', 'deepseek'] : [host]
|
|
248
321
|
const reports = hosts.map((target) => inspectDoctorHost(target, settings))
|
|
249
322
|
const summary = reports.reduce((acc, report) => {
|
|
250
323
|
acc[report.status] = (acc[report.status] || 0) + 1
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
CODEX_PLUGIN_NAME,
|
|
8
8
|
} from './cli-codex.mjs'
|
|
9
9
|
import { getStableRuntimeRoot } from './cli-runtime-root.mjs'
|
|
10
|
+
import { FULL_CARRIER_PROFILE_MARKER } from './cli-utils.mjs'
|
|
10
11
|
import { safeJson, safeRead } from './cli-utils.mjs'
|
|
11
12
|
|
|
12
13
|
const HOST_ALIASES = new Map([
|
|
@@ -18,6 +19,8 @@ const HOST_ALIASES = new Map([
|
|
|
18
19
|
['gemini-cli', 'gemini'],
|
|
19
20
|
['codex', 'codex'],
|
|
20
21
|
['codex-cli', 'codex'],
|
|
22
|
+
['deepseek', 'deepseek'],
|
|
23
|
+
['deepseek-tui', 'deepseek'],
|
|
21
24
|
])
|
|
22
25
|
|
|
23
26
|
function hasHelloagentsMarker(filePath) {
|
|
@@ -28,6 +31,10 @@ function hasHelloagentsSettings(filePath) {
|
|
|
28
31
|
return JSON.stringify(safeJson(filePath) || {}).includes('helloagents')
|
|
29
32
|
}
|
|
30
33
|
|
|
34
|
+
function hasFullCarrierProfile(filePath) {
|
|
35
|
+
return (safeRead(filePath) || '').includes(FULL_CARRIER_PROFILE_MARKER)
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
function normalizePath(value = '') {
|
|
32
39
|
return String(value || '').replace(/\\/g, '/').toLowerCase()
|
|
33
40
|
}
|
|
@@ -90,6 +97,26 @@ function detectCodexMode(home) {
|
|
|
90
97
|
return ''
|
|
91
98
|
}
|
|
92
99
|
|
|
100
|
+
function detectDeepseekMode(home) {
|
|
101
|
+
const deepseekDir = join(home, '.deepseek')
|
|
102
|
+
const carrierPath = join(deepseekDir, 'AGENTS.md')
|
|
103
|
+
const runtimeRoot = normalizePath(getStableRuntimeRoot(home))
|
|
104
|
+
const homeLinkTarget = safeRealTarget(join(deepseekDir, 'helloagents'))
|
|
105
|
+
if (
|
|
106
|
+
(hasHelloagentsMarker(carrierPath) || (existsSync(join(deepseekDir, 'helloagents')) && homeLinkTarget === runtimeRoot))
|
|
107
|
+
&& hasFullCarrierProfile(carrierPath)
|
|
108
|
+
) {
|
|
109
|
+
return 'global'
|
|
110
|
+
}
|
|
111
|
+
if (
|
|
112
|
+
(existsSync(join(deepseekDir, 'helloagents')) && homeLinkTarget === runtimeRoot)
|
|
113
|
+
|| hasHelloagentsMarker(carrierPath)
|
|
114
|
+
) {
|
|
115
|
+
return 'standby'
|
|
116
|
+
}
|
|
117
|
+
return ''
|
|
118
|
+
}
|
|
119
|
+
|
|
93
120
|
export function normalizeHost(value = '') {
|
|
94
121
|
return HOST_ALIASES.get(String(value || '').toLowerCase()) || ''
|
|
95
122
|
}
|
|
@@ -98,6 +125,7 @@ export function getHostLabel(host) {
|
|
|
98
125
|
if (host === 'claude') return 'Claude Code'
|
|
99
126
|
if (host === 'gemini') return 'Gemini CLI'
|
|
100
127
|
if (host === 'codex') return 'Codex CLI'
|
|
128
|
+
if (host === 'deepseek') return 'DeepSeek TUI'
|
|
101
129
|
return 'All CLIs'
|
|
102
130
|
}
|
|
103
131
|
|
|
@@ -105,5 +133,6 @@ export function detectHostMode(host, runtime) {
|
|
|
105
133
|
if (host === 'claude') return detectClaudeMode(runtime.home)
|
|
106
134
|
if (host === 'gemini') return detectGeminiMode(runtime.home)
|
|
107
135
|
if (host === 'codex') return detectCodexMode(runtime.home)
|
|
136
|
+
if (host === 'deepseek') return detectDeepseekMode(runtime.home)
|
|
108
137
|
return ''
|
|
109
138
|
}
|
package/scripts/cli-hosts.mjs
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
loadHooksWithCliEntry,
|
|
13
13
|
} from './cli-utils.mjs';
|
|
14
14
|
import { buildRuntimeCarrier, readCarrierSettings } from './cli-runtime-carrier.mjs';
|
|
15
|
+
import { installDeepseekGlobal, installDeepseekStandby, uninstallDeepseekGlobal, uninstallDeepseekStandby } from './cli-deepseek.mjs'
|
|
15
16
|
|
|
16
17
|
export function installClaudeStandby(home, pkgRoot) {
|
|
17
18
|
const claudeDir = join(home, '.claude');
|
|
@@ -81,3 +82,10 @@ export function uninstallGeminiStandby(home) {
|
|
|
81
82
|
|
|
82
83
|
return true;
|
|
83
84
|
}
|
|
85
|
+
|
|
86
|
+
export {
|
|
87
|
+
installDeepseekGlobal,
|
|
88
|
+
installDeepseekStandby,
|
|
89
|
+
uninstallDeepseekGlobal,
|
|
90
|
+
uninstallDeepseekStandby,
|
|
91
|
+
}
|