svharness 0.14.16 → 0.14.18
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/README.md +211 -15
- package/dist/adapters/codex.js +19 -0
- package/dist/adapters/index.js +2 -0
- package/dist/commands/doctor/utils.js +2 -0
- package/dist/commands/init.js +37 -129
- package/dist/commands/shell-integration.js +27 -28
- package/dist/commands/wiki.js +127 -0
- package/dist/config/index.js +2 -1
- package/dist/config/merge-options.js +19 -0
- package/dist/config/normalize.js +18 -0
- package/dist/core/state.js +39 -0
- package/dist/index.js +62 -14
- package/dist/lib/agent-launcher.js +18 -38
- package/dist/lib/codechat-runner-resolver.js +208 -0
- package/dist/lib/launch-script-path.js +24 -0
- package/dist/lib/ps-codechat-alias.js +183 -0
- package/dist/lib/wiki/run-wiki-generation.js +161 -0
- package/dist/utils/validate-args.js +1 -0
- package/dist/wiki/repowikiIndexer.js +19 -150
- package/dist/wiki/repowikiPrompts.js +296 -0
- package/dist/wiki/repowikiStructureNormalize.js +31 -1
- package/dist/wiki/wikiTasksWriter.js +59 -22
- package/docs/agent-launcher-design.md +119 -26
- package/docs/standalone-codechat-ps1.md +127 -0
- package/package.json +1 -1
- package/scripts/postinstall.js +2 -2
- package/scripts/preuninstall.js +90 -5
- package/templates/_shared/build-rules/harness-build-rule-agent-agnostic.md +2 -2
- package/templates/_shared/build-rules/harness-build-rule-specs-schema.md +1 -1
- package/templates/_shared/build-skills/harness-build-skill-orchestrator.md +1 -1
- package/templates/_shared/build-skills/harness-build-skill-wiki-writer.md +15 -6
- package/templates/codechat/Start-CodeChat.ps1 +498 -0
- package/templates/svharness.config.example.yaml +7 -0
- package/dist/commands/references-apply-skills.js +0 -47
package/README.md
CHANGED
|
@@ -133,7 +133,7 @@ Harness 是一个 **项目本地的知识层**,由两大部分组成:
|
|
|
133
133
|
└── tasks/ # 可复用的开发者工作流
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
同时,CLI 会在**项目根**写入 `CLAUDE.md`(`--agent codechat` 或 `claude-code`)或 `AGENTS.md`(`qoder` / `cursor` / `opencode` / `generic`),并向 Agent IDE 的 skill 目录注入 **7 个构建辅助 skill**:
|
|
136
|
+
同时,CLI 会在**项目根**写入 `CLAUDE.md`(`--agent codechat` 或 `claude-code`)或 `AGENTS.md`(`qoder` / `cursor` / `opencode` / `codex` / `generic`),并向 Agent IDE 的 skill 目录注入 **7 个构建辅助 skill**:
|
|
137
137
|
|
|
138
138
|
| Skill | 职责 |
|
|
139
139
|
|-------|------|
|
|
@@ -228,6 +228,7 @@ svharness wizard
|
|
|
228
228
|
| `apply` | 对应 `svharness apply` |
|
|
229
229
|
| `convert` | 对应 `svharness convert` |
|
|
230
230
|
| `doctor` | 对应 `svharness doctor` |
|
|
231
|
+
| `wiki` | 对应 `svharness wiki` |
|
|
231
232
|
|
|
232
233
|
`build` 节除路径外,可写人类可读说明(仅写入配置文件,供团队阅读):
|
|
233
234
|
|
|
@@ -270,7 +271,7 @@ svharness build \
|
|
|
270
271
|
| `--harness-name <name>` | ✅ 必填* | harness 名称;可在 `build.harnessName` 中提供 | — |
|
|
271
272
|
| `--name <name>` | ⚠️ 已废弃别名 | 等同 `--harness-name`,下个 minor 版本移除 | — |
|
|
272
273
|
| `--arch <arch>` | 可选 | 架构模板:`android-compose` / `android-xml` / `cpp` / `web-react` / `python` | `android-compose` |
|
|
273
|
-
| `--agent <agent>` | 可选 | 目标 Agent IDE:`codechat` / `qoder` / `cursor` / `claude-code` / `opencode` / `generic` | `codechat` |
|
|
274
|
+
| `--agent <agent>` | 可选 | 目标 Agent IDE:`codechat` / `qoder` / `cursor` / `claude-code` / `opencode` / `codex` / `generic` | `codechat` |
|
|
274
275
|
| `--baseline <path\|url>` | 可选 | 基线来源:本地目录 **或** git 仓库 URL | — |
|
|
275
276
|
| `--requirements <path>` | 可选 | 需求输入路径(文件或目录);build 阶段**扁平**拷贝到 `requirements/raw/`(不保留源目录里的 `assets/raw/converted_md` 等嵌套),随后自动 convert 到 `requirements/md/`(失败仅告警,不中断 build) | — |
|
|
276
277
|
| `--references <path>` | 可选 | 参考资料输入路径(文件或目录);build 阶段**扁平**拷贝到 `references/raw/`,随后自动 convert 到 `references/md/`(失败仅告警,不中断 build) | — |
|
|
@@ -312,6 +313,8 @@ svharness build \
|
|
|
312
313
|
| `--baseline` + `--generate-wiki` | 逐页调用 LLM 生成完整 wiki | 初始 `DONE`;失败降级为 `PENDING` |
|
|
313
314
|
|
|
314
315
|
> Wiki 生成失败不会回滚 harness 骨架,仅输出警告。
|
|
316
|
+
>
|
|
317
|
+
> build 内置 wiki 的 LLM prompt **面向编码 Agent**(知识路由卡片);独立 `svharness wiki` 默认面向人类开发者,见 [`wiki` 命令](#wiki-standalone) 一节。
|
|
315
318
|
|
|
316
319
|
#### Repomix(`--repomix`){#repomix-repomix}
|
|
317
320
|
|
|
@@ -351,7 +354,9 @@ svharness build --harness-name my-app --baseline ./src --repomix
|
|
|
351
354
|
| `qoder` | `.qoder/skills/` | `.md` | — |
|
|
352
355
|
| `cursor` | `.cursor/rules/` | `.mdc` | 自动注入 frontmatter |
|
|
353
356
|
| `claude-code` | `.claude/skills/` | `.md` | — |
|
|
354
|
-
| `
|
|
357
|
+
| `opencode` | `.opencode/skills/` | `.md` | — |
|
|
358
|
+
| `codex` | `.agents/skills/` | `.md` | — |
|
|
359
|
+
| `generic` | `.harness/skills/` | `.md` | 中立回退 |
|
|
355
360
|
|
|
356
361
|
### `apply` —— 把已构建好的 harness 绑定到目标项目
|
|
357
362
|
|
|
@@ -367,7 +372,7 @@ svharness apply --harness ../my-app-harness --target ./to-apply-project --clone
|
|
|
367
372
|
|
|
368
373
|
**在 Agent IDE 中触发**:对话框输入 `应用 harness-apply-skills-main 完成 xxx 功能开发`,薄入口会提示并优先使用已注入的 `harness-apply-skills-*` 子 skill。
|
|
369
374
|
|
|
370
|
-
同时,CLI 会在**目标项目根**写入 `CLAUDE.md`(`--agent codechat` 或 `claude-code`)或 `AGENTS.md`(`qoder` / `cursor` / `opencode` / `generic`)。该入口由 `<harness>/AGENTS_APPLY.md` 拷贝并重命名生成(与 `build` 写入项目根入口的规则一致,不再单独落地 `AGENTS_APPLY.md`)。
|
|
375
|
+
同时,CLI 会在**目标项目根**写入 `CLAUDE.md`(`--agent codechat` 或 `claude-code`)或 `AGENTS.md`(`qoder` / `cursor` / `opencode` / `codex` / `generic`)。该入口由 `<harness>/AGENTS_APPLY.md` 拷贝并重命名生成(与 `build` 写入项目根入口的规则一致,不再单独落地 `AGENTS_APPLY.md`)。
|
|
371
376
|
|
|
372
377
|
此外,`apply` 会同步注入运行期资产:
|
|
373
378
|
|
|
@@ -399,7 +404,7 @@ S60(`harness-build-skill-references-intake`)属于 **build 阶段**。若某
|
|
|
399
404
|
|------|----------|------|--------|
|
|
400
405
|
| `--harness <path>` | ✅ 必填 | 已构建好的 harness 目录(形如 `./my-app-harness`) | — |
|
|
401
406
|
| `--target <path>` | 可选 | 目标项目根目录 | **当前工作目录(cwd)** |
|
|
402
|
-
| `--agent <agent>` | 可选 | 目标 Agent:`codechat` / `qoder` / `cursor` / `claude-code` / `opencode` / `generic` | `codechat` |
|
|
407
|
+
| `--agent <agent>` | 可选 | 目标 Agent:`codechat` / `qoder` / `cursor` / `claude-code` / `opencode` / `codex` / `generic` | `codechat` |
|
|
403
408
|
| `--clone` | 可选 flag | 兼容参数;当前实现下行为与默认一致(都会拷贝 harness) | `false` |
|
|
404
409
|
| `--include-build-assets` | 可选 flag | 显式拷贝构建期 skills/rules 到 `build-agent-env/`(用于二次改造) | `false` |
|
|
405
410
|
| `--inject-build-main-bridge` | 可选 flag | 向运行期 skills 注入 `harness-build-skills-bridge`(需 `--include-build-assets`) | `false` |
|
|
@@ -574,6 +579,92 @@ convert-failures.json
|
|
|
574
579
|
|
|
575
580
|
完整契约、fallback chain、Docker 部署见 [markitdown_serve/README.md](./markitdown_serve/README.md);模块设计见 [tmp/svharnessbuild-detail-design.md §4.10](../tmp/svharnessbuild-detail-design.md#410-文档转换convert-命令--markitdown_serve-远端服务)。
|
|
576
581
|
|
|
582
|
+
### `wiki` —— 对任意代码工程生成 baseline wiki(v0.15+)
|
|
583
|
+
|
|
584
|
+
独立子命令,**不依赖 harness 生命周期**,直接对任意代码工程目录就地扫描生成 wiki。与 `build` 内置 wiki 共用同一 repowiki 核心,但不拷贝 code、不创建 harness 骨架。
|
|
585
|
+
|
|
586
|
+
```bash
|
|
587
|
+
# 对当前工程生成 wiki 骨架(默认 ./wiki/)
|
|
588
|
+
svharness wiki
|
|
589
|
+
|
|
590
|
+
# 完整生成(LLM 逐页)
|
|
591
|
+
svharness wiki --generate-wiki
|
|
592
|
+
|
|
593
|
+
# 指定源码目录 + 完整生成
|
|
594
|
+
svharness wiki --wiki-source ./src --generate-wiki --output ./docs/wiki
|
|
595
|
+
|
|
596
|
+
# 对外部仓库目录生成(不拷贝,就地扫描)
|
|
597
|
+
svharness wiki --wiki-source /path/to/other-project --output ./other-wiki
|
|
598
|
+
|
|
599
|
+
# 可选:同步到已有 harness 的 S10_wiki
|
|
600
|
+
svharness wiki --wiki-source ./src \
|
|
601
|
+
--output ./my-app-harness/baseline/wiki \
|
|
602
|
+
--harness ./my-app-harness --update-state --generate-wiki
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
#### 全部参数
|
|
606
|
+
|
|
607
|
+
| 参数 | 必填 | 说明 | 默认值 |
|
|
608
|
+
|------|------|------|--------|
|
|
609
|
+
| `--wiki-source <path>` | 否 | 要扫描的代码工程根目录 | `.`(cwd) |
|
|
610
|
+
| `--output <path>` | 否 | wiki 产物输出目录 | `./wiki` |
|
|
611
|
+
| `--generate-wiki` | 否 | 完整生成(LLM 逐页;与 `--wiki-tasks-only` 互斥) | `false` |
|
|
612
|
+
| `--wiki-tasks-only` | 否 | 仅生成 outline + TASKS.md(与默认等价) | `true`(隐式默认) |
|
|
613
|
+
| `--wiki-lang <lang>` | 否 | 输出语言:`zh` \| `en` | `zh` |
|
|
614
|
+
| `--wiki-model <model>` | 否 | LLM 模型名称 | 内置默认 |
|
|
615
|
+
| `--wiki-base-url <url>` | 否 | OpenAI 兼容 API 地址 | 内置默认 |
|
|
616
|
+
| `--wiki-api-key <key>` | 否 | API 密钥 | 环境变量 / `.env` |
|
|
617
|
+
| `--project-name <name>` | 否 | TASKS.md 标题(默认取 `wiki-source` 的目录名) | — |
|
|
618
|
+
| `--force-full` | 否 | full 模式忽略 checkpoint 强制重新生成 | `false` |
|
|
619
|
+
| `--harness <path>` | 否 | 已有 harness 根目录;**提供时 wiki prompt 面向编码 Agent**(配合 `--update-state` 同步 state) | — |
|
|
620
|
+
| `--update-state` | 否 | 同步 S10_wiki 到 harness 的 `.harness-build-state.yaml`(须同时提供 `--harness`) | `false` |
|
|
621
|
+
| `--config <path>` | 否 | 从配置文件读取 wiki 节 | `svharness.config.yaml` |
|
|
622
|
+
| `--verbose` | 否 | 显示详细日志 | `false` |
|
|
623
|
+
|
|
624
|
+
#### 与 `build` 内置 wiki 的区别
|
|
625
|
+
|
|
626
|
+
| 维度 | `svharness build`(内置) | `svharness wiki`(独立) |
|
|
627
|
+
|------|--------------------------|-------------------------|
|
|
628
|
+
| 源码 | 拷贝 `--baseline` → `<harness>/baseline/code/` | **就地扫描**,不拷贝 |
|
|
629
|
+
| 默认源码根 | `baseline/code/` | **cwd(`.`)** |
|
|
630
|
+
| 输出目录 | `<harness>/baseline/wiki/` | **`./wiki/`** |
|
|
631
|
+
| 依赖 harness | 是(创建 harness 骨架) | **否** |
|
|
632
|
+
|
|
633
|
+
**Wiki 受众(prompt 自动选择,无需 CLI 开关)**:
|
|
634
|
+
|
|
635
|
+
| 入口 | 受众 | 产出风格 |
|
|
636
|
+
|------|------|----------|
|
|
637
|
+
| `svharness build --baseline` | 编码 AI Agent | 知识地图:路由式大纲、固定章节正文 |
|
|
638
|
+
| `svharness wiki`(无 `--harness`) | 人类开发者 | 教程式大纲、简介 + TOC + 叙述性文档 |
|
|
639
|
+
| `svharness wiki --harness <path>` | 编码 AI Agent | 与 build 相同(供 harness baseline/wiki 消费) |
|
|
640
|
+
| state 更新 | init 时自动写 S10_wiki | 默认不写;可选 `--update-state --harness` |
|
|
641
|
+
|
|
642
|
+
#### 产出布局
|
|
643
|
+
|
|
644
|
+
```
|
|
645
|
+
./wiki/ # 或 --output 指定的目录
|
|
646
|
+
├── structure.xml # outline XML
|
|
647
|
+
├── TASKS.md # tasks 模式产出(可交给 agent 补页)
|
|
648
|
+
├── metadata.json # full 模式产出
|
|
649
|
+
├── checkpoint.json # full 模式进度(可续跑)
|
|
650
|
+
└── zh/<section>/<page>.md # full 模式各页面
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
#### 配置文件支持
|
|
654
|
+
|
|
655
|
+
在 `svharness.config.yaml` 中可配置 wiki 节:
|
|
656
|
+
|
|
657
|
+
```yaml
|
|
658
|
+
wiki:
|
|
659
|
+
wikiSource: .
|
|
660
|
+
output: ./wiki
|
|
661
|
+
generateWiki: false
|
|
662
|
+
wikiLang: zh
|
|
663
|
+
wikiModel: Qwen3.6-27B
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
优先级:CLI 参数 > `wiki:` 节 > `defaults:` > 内置默认。
|
|
667
|
+
|
|
577
668
|
### `start-agent` —— 启动 Agent 填充 harness(v0.16+)
|
|
578
669
|
|
|
579
670
|
`build` 生成骨架后,用本命令在**项目根(workdir)**启动 CodeChat CLI,驱动 S10–S90 构建阶段;`apply` 之后亦可在目标项目根启动 Agent 做业务开发。
|
|
@@ -608,12 +699,65 @@ svharness start-agent --work-dir . --no-sync-env --no-skip-permissions
|
|
|
608
699
|
| `--no-sync-env` | 不读取项目 `.claude/.env` 同步到用户级(仍可用已有 `~/.claude/.env`;若无则仅写入用户级内置模板) | false |
|
|
609
700
|
| `--no-skip-permissions` | 不传 `--dangerously-skip-permissions` | 默认传 |
|
|
610
701
|
|
|
702
|
+
#### CodeChat runner 解析
|
|
703
|
+
|
|
704
|
+
`start-agent` / `launch_codechat_cli` 按以下优先级查找 `run.bat`(Windows)或 `run.sh`(Linux/macOS):
|
|
705
|
+
|
|
706
|
+
| 优先级 | 来源 | 路径示例 |
|
|
707
|
+
|--------|------|----------|
|
|
708
|
+
| 1 | 环境变量 | `CODECHAT_CLI_RUNNER` 指向绝对路径 |
|
|
709
|
+
| 2 | 独立安装 | `~/.codechat/cli_app/run.bat`(Win)/ `run.sh`(Linux) |
|
|
710
|
+
| 3 | IDE 插件 | `%APPDATA%\Google\AndroidStudio*\plugins\CodeChat\cli\run.bat` 等 |
|
|
711
|
+
|
|
712
|
+
若 CodeChat 仅通过 **Android Studio / IntelliJ IDEA 插件**安装(无 `~/.codechat/cli_app`),会自动扫描 IDE 插件目录,**无需指定 IDE 版本号**。
|
|
713
|
+
|
|
714
|
+
启动成功时日志示例:
|
|
715
|
+
|
|
716
|
+
```text
|
|
717
|
+
CodeChat CLI: C:\Users\...\AndroidStudio2024.2\plugins\CodeChat\cli\run.bat (ide-plugin: Android Studio 2024.2)
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
**手动指定 runner**(PowerShell):
|
|
721
|
+
|
|
722
|
+
```powershell
|
|
723
|
+
$env:CODECHAT_CLI_RUNNER = "$env:APPDATA\Google\AndroidStudio2024.2\plugins\CodeChat\cli\run.bat"
|
|
724
|
+
svharness start-agent --work-dir D:\projects\my-app
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
**bash**:
|
|
728
|
+
|
|
729
|
+
```bash
|
|
730
|
+
export CODECHAT_CLI_RUNNER="$HOME/.local/share/JetBrains/IntelliJIdea2024.2/plugins/CodeChat/cli/run.sh"
|
|
731
|
+
svharness start-agent --work-dir ~/projects/my-app
|
|
732
|
+
```
|
|
733
|
+
|
|
611
734
|
#### 平台说明
|
|
612
735
|
|
|
613
|
-
| 平台 |
|
|
614
|
-
|
|
615
|
-
| Windows |
|
|
616
|
-
| Linux |
|
|
736
|
+
| 平台 | spawn 模式 | 右键菜单 / PowerShell 别名 |
|
|
737
|
+
|------|------------|----------------------------|
|
|
738
|
+
| Windows | `shell: true` | 支持(`svharness shell`) |
|
|
739
|
+
| Linux / macOS | `shell: false` | 不支持 |
|
|
740
|
+
|
|
741
|
+
Windows 下首次 `start-agent` 会**尽力**写入 PowerShell `codechat` 函数(与 `shell install` 相同逻辑);若未全局安装 svharness,可手动执行 `svharness shell install`。
|
|
742
|
+
|
|
743
|
+
### 无需 svharness:独立 PowerShell 启动器
|
|
744
|
+
|
|
745
|
+
未安装 npm / svharness 时,可拷贝 [`templates/codechat/Start-CodeChat.ps1`](templates/codechat/Start-CodeChat.ps1) 到项目 `.claude\`,实现相同的 env 同步与 `run.bat` 解析(含 Android Studio / IDEA 插件路径):
|
|
746
|
+
|
|
747
|
+
```powershell
|
|
748
|
+
Copy-Item .\templates\codechat\Start-CodeChat.ps1 .\.claude\
|
|
749
|
+
.\.claude\Start-CodeChat.ps1 # 启动
|
|
750
|
+
.\.claude\Start-CodeChat.ps1 -Action Install # 可选:独立右键 + 别名
|
|
751
|
+
.\.claude\Start-CodeChat.ps1 -Action InstallMenu # 仅右键
|
|
752
|
+
.\.claude\Start-CodeChat.ps1 -Action InstallAlias # 仅别名
|
|
753
|
+
.\.claude\Start-CodeChat.ps1 -Action UninstallMenu # 反注册独立右键
|
|
754
|
+
.\.claude\Start-CodeChat.ps1 -Action UninstallAlias # 反注册独立别名
|
|
755
|
+
.\.claude\Start-CodeChat.ps1 -Action Status
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
右键菜单使用注册表键 `CodeChatStandaloneAgent`,**不与** `svharness shell install` 冲突;可与 svharness 菜单并存。
|
|
759
|
+
|
|
760
|
+
详见 [`docs/standalone-codechat-ps1.md`](docs/standalone-codechat-ps1.md)。
|
|
617
761
|
|
|
618
762
|
### `launch_codechat_cli` —— 全局快捷命令(v0.16+)
|
|
619
763
|
|
|
@@ -625,22 +769,74 @@ launch_codechat_cli D:\projects\my-app # Windows
|
|
|
625
769
|
launch_codechat_cli ~/projects/my-app # Linux
|
|
626
770
|
```
|
|
627
771
|
|
|
628
|
-
### `shell` —— Windows
|
|
772
|
+
### `shell` —— Windows 右键菜单与 PowerShell 别名(v0.16+,仅 win32)
|
|
773
|
+
|
|
774
|
+
在资源管理器**文件夹空白处**与**文件夹图标**右键增加「在此启动 CodeChat Agent」,并在 PowerShell `$PROFILE` 写入 `codechat` 函数(等价于 `launch_codechat_cli [workdir]`)。**注册与反注册**对右键菜单、本地 stub、PowerShell 别名**一并**处理。
|
|
629
775
|
|
|
630
|
-
|
|
776
|
+
#### 注册
|
|
631
777
|
|
|
632
778
|
```bash
|
|
633
|
-
svharness shell install #
|
|
634
|
-
svharness shell
|
|
635
|
-
svharness shell status # 查看 stub / 注册表状态
|
|
779
|
+
svharness shell install # 注册右键 + 安装 codechat 别名(幂等)
|
|
780
|
+
svharness shell status # 查看是否已完整安装
|
|
636
781
|
```
|
|
637
782
|
|
|
638
|
-
|
|
783
|
+
注册时会写入:
|
|
784
|
+
|
|
785
|
+
| 项 | 路径 / 键 |
|
|
786
|
+
|----|-----------|
|
|
787
|
+
| 空白处右键 | `HKCU\Software\Classes\Directory\Background\shell\SvharnessLaunchCodeChatAgent` |
|
|
788
|
+
| 文件夹右键 | `HKCU\Software\Classes\Directory\shell\SvharnessLaunchCodeChatAgent` |
|
|
789
|
+
| 启动 stub | `%LOCALAPPDATA%\svharness\bin\launch_codechat_cli.ps1` |
|
|
790
|
+
| 菜单图标 | `%LOCALAPPDATA%\svharness\bin\codechat-agent.ico` |
|
|
791
|
+
| PowerShell 别名 | 当前用户 `$PROFILE` 内 `# >>> codechat >>>` … `# <<< codechat <<<` 块 |
|
|
792
|
+
|
|
793
|
+
`npm install -g svharness` 在 Windows 上 **默认自动** 执行上述注册。跳过:
|
|
639
794
|
|
|
640
795
|
```bash
|
|
641
796
|
SVHARNESS_SKIP_SHELL=1 npm install -g svharness
|
|
642
797
|
```
|
|
643
798
|
|
|
799
|
+
**PowerShell 别名用法**(注册后需**新开** PowerShell 窗口才生效):
|
|
800
|
+
|
|
801
|
+
```powershell
|
|
802
|
+
codechat # 当前目录启动
|
|
803
|
+
codechat D:\projects\my-app # 指定项目根
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
#### 反注册
|
|
807
|
+
|
|
808
|
+
```bash
|
|
809
|
+
svharness shell uninstall # 移除右键、stub、图标与 profile 中的 codechat 块
|
|
810
|
+
svharness shell status # 确认「右键菜单完整 / codechat 别名」均为否
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
反注册时会删除:
|
|
814
|
+
|
|
815
|
+
| 项 | 说明 |
|
|
816
|
+
|----|------|
|
|
817
|
+
| 上述两个 HKCU 注册表键 | 资源管理器右键菜单项消失(无需重启 Explorer) |
|
|
818
|
+
| `%LOCALAPPDATA%\svharness\bin\` | 删除 stub `.ps1` 与 `.ico` |
|
|
819
|
+
| `$PROFILE` 中的 codechat 块 | 移除 `# >>> codechat >>>` … `# <<< codechat <<<` 整段;**不**删除 profile 文件本身 |
|
|
820
|
+
|
|
821
|
+
全局卸载 npm 包时,`preuninstall` 会**尽力**自动反注册(与 `shell uninstall` 相同范围):
|
|
822
|
+
|
|
823
|
+
```bash
|
|
824
|
+
npm uninstall -g svharness
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
若自动反注册失败,可手动执行 `svharness shell uninstall`(包仍在时),或自行检查:
|
|
828
|
+
|
|
829
|
+
- 注册表:`reg query HKCU\Software\Classes\Directory\Background\shell\SvharnessLaunchCodeChatAgent` 应报错「找不到」
|
|
830
|
+
- 目录:`%LOCALAPPDATA%\svharness\bin\` 应不存在或为空
|
|
831
|
+
- profile:打开 `$PROFILE`,确认无 `# >>> codechat >>>` 标记块
|
|
832
|
+
|
|
833
|
+
**注意**:`shell uninstall` **仅**移除 svharness 注册的菜单(键名 `SvharnessLaunchCodeChatAgent`)。若曾用独立脚本 [`Start-CodeChat.ps1`](templates/codechat/Start-CodeChat.ps1) 注册过菜单(键名 `CodeChatStandaloneAgent`),需单独反注册:
|
|
834
|
+
|
|
835
|
+
```powershell
|
|
836
|
+
.\.claude\Start-CodeChat.ps1 -Action UninstallMenu # 独立右键
|
|
837
|
+
.\.claude\Start-CodeChat.ps1 -Action UninstallAlias # 独立别名(若曾 Install / InstallAlias)
|
|
838
|
+
```
|
|
839
|
+
|
|
644
840
|
---
|
|
645
841
|
|
|
646
842
|
## 架构模板
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.codexAdapter = void 0;
|
|
4
|
+
const _frontmatter_1 = require("./_frontmatter");
|
|
5
|
+
/**
|
|
6
|
+
* OpenAI Codex: project skills under `.agents/skills/<name>/SKILL.md`,
|
|
7
|
+
* project guidance via `AGENTS.md`.
|
|
8
|
+
* @see https://developers.openai.com/codex/skills
|
|
9
|
+
* @see https://developers.openai.com/codex/guides/agents-md
|
|
10
|
+
*/
|
|
11
|
+
exports.codexAdapter = {
|
|
12
|
+
name: 'codex',
|
|
13
|
+
projectEntryFile: 'AGENTS.md',
|
|
14
|
+
skillsDir: '.agents/skills',
|
|
15
|
+
skillExt: '.md',
|
|
16
|
+
rulesDir: '.agents/rules',
|
|
17
|
+
ruleExt: '.mdc',
|
|
18
|
+
ruleTransform: (0, _frontmatter_1.ensureMdcFrontmatter)('Harness build-time rule'),
|
|
19
|
+
};
|
package/dist/adapters/index.js
CHANGED
|
@@ -8,12 +8,14 @@ const cursor_1 = require("./cursor");
|
|
|
8
8
|
const claude_code_1 = require("./claude-code");
|
|
9
9
|
const generic_1 = require("./generic");
|
|
10
10
|
const opencode_1 = require("./opencode");
|
|
11
|
+
const codex_1 = require("./codex");
|
|
11
12
|
const REGISTRY = {
|
|
12
13
|
codechat: codechat_1.codechatAdapter,
|
|
13
14
|
qoder: qoder_1.qoderAdapter,
|
|
14
15
|
cursor: cursor_1.cursorAdapter,
|
|
15
16
|
'claude-code': claude_code_1.claudeCodeAdapter,
|
|
16
17
|
opencode: opencode_1.opencodeAdapter,
|
|
18
|
+
codex: codex_1.codexAdapter,
|
|
17
19
|
generic: generic_1.genericAdapter,
|
|
18
20
|
};
|
|
19
21
|
function getAdapter(name) {
|
|
@@ -164,6 +164,7 @@ exports.AGENT_BLACKLIST = [
|
|
|
164
164
|
'cursor',
|
|
165
165
|
'claude-code',
|
|
166
166
|
'opencode',
|
|
167
|
+
'codex',
|
|
167
168
|
'claude',
|
|
168
169
|
'chatgpt',
|
|
169
170
|
'gpt-4',
|
|
@@ -172,6 +173,7 @@ exports.AGENT_BLACKLIST = [
|
|
|
172
173
|
'.cursor/',
|
|
173
174
|
'.claude/',
|
|
174
175
|
'.opencode/',
|
|
176
|
+
'.agents/',
|
|
175
177
|
];
|
|
176
178
|
exports.TASK_CATEGORY_HINTS = [
|
|
177
179
|
['新增', '信号', '接口', '数据', 'increment', 'add'],
|
package/dist/commands/init.js
CHANGED
|
@@ -61,6 +61,7 @@ const extra_assets_intake_1 = require("../core/extra-assets-intake");
|
|
|
61
61
|
const baseline_copy_1 = require("../utils/baseline-copy");
|
|
62
62
|
const doc_intake_paths_1 = require("../core/doc-intake-paths");
|
|
63
63
|
const convert_1 = require("./convert");
|
|
64
|
+
const run_wiki_generation_1 = require("../lib/wiki/run-wiki-generation");
|
|
64
65
|
const wiki_1 = require("../wiki");
|
|
65
66
|
/**
|
|
66
67
|
* Resolve the absolute path to the bundled templates/ directory.
|
|
@@ -387,11 +388,17 @@ async function runInit(opts) {
|
|
|
387
388
|
agent: validated.agent,
|
|
388
389
|
force: !!opts.force,
|
|
389
390
|
});
|
|
391
|
+
// Collect adapter-specific root directories for gitignore.
|
|
392
|
+
const ignoreRoots = new Set();
|
|
393
|
+
ignoreRoots.add(node_path_1.default.posix.join('/', node_path_1.default.posix.dirname(adapterEntry.skillsDir), '/'));
|
|
394
|
+
if (adapterEntry.rulesDir) {
|
|
395
|
+
ignoreRoots.add(node_path_1.default.posix.join('/', node_path_1.default.posix.dirname(adapterEntry.rulesDir), '/'));
|
|
396
|
+
}
|
|
390
397
|
const ignoreEntries = [
|
|
391
398
|
`/${dirName}/`,
|
|
392
399
|
'/AGENTS.md',
|
|
393
400
|
'/CLAUDE.md',
|
|
394
|
-
|
|
401
|
+
...ignoreRoots,
|
|
395
402
|
];
|
|
396
403
|
const ignoreAdded = await (0, project_ignore_1.appendProjectIgnoreEntries)({
|
|
397
404
|
projectRoot: cwd,
|
|
@@ -454,141 +461,42 @@ async function runInit(opts) {
|
|
|
454
461
|
// the wiki result.
|
|
455
462
|
let resolvedWikiPhase = wikiMode === 'off' ? 'none' : wikiMode === 'tasks' ? 'pending' : 'done';
|
|
456
463
|
let resolvedWikiSource;
|
|
457
|
-
if (wikiMode === 'full') {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
logger_1.logger.info(` 源码根:${repoRootFs}`);
|
|
484
|
-
logger_1.logger.info(` 输出到:${node_path_1.default.join(wikiOutputRootFs, wikiRelPath)}`);
|
|
485
|
-
logger_1.logger.info(` 语言:${langFolder} · 模型:${cfg.model}`);
|
|
486
|
-
await (0, wiki_1.runRepowikiIndex)({
|
|
487
|
-
repoRootFs,
|
|
488
|
-
wikiOutputRootFs,
|
|
489
|
-
wikiRelPath,
|
|
490
|
-
baseUrl: cfg.baseUrl,
|
|
491
|
-
apiKey: cfg.apiKey,
|
|
492
|
-
model: cfg.model,
|
|
493
|
-
languages: [langRun],
|
|
494
|
-
comprehensiveSections: true,
|
|
495
|
-
onLog: (m) => logger_1.logger.info(m),
|
|
496
|
-
onProgress: (p) => {
|
|
497
|
-
if (p.total > 0) {
|
|
498
|
-
logger_1.logger.info(` [${p.done}/${p.total}] ${p.detail}`);
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
logger_1.logger.info(` ${p.detail}`);
|
|
502
|
-
}
|
|
503
|
-
},
|
|
504
|
-
});
|
|
505
|
-
logger_1.logger.success('baseline wiki 已生成');
|
|
506
|
-
wikiFullOk = true;
|
|
507
|
-
}
|
|
508
|
-
catch (err) {
|
|
509
|
-
if ((0, wiki_1.isAbortError)(err)) {
|
|
510
|
-
logger_1.logger.warn('wiki 生成被中断(已写出部分页可从 checkpoint 恢复)');
|
|
464
|
+
if (wikiMode === 'full' || wikiMode === 'tasks') {
|
|
465
|
+
const stepLabel = wikiMode === 'full'
|
|
466
|
+
? `步骤 ${stepWiki}/${totalSteps} - 生成 baseline wiki`
|
|
467
|
+
: `步骤 ${stepWiki}/${totalSteps} - 生成 baseline wiki 任务清单`;
|
|
468
|
+
logger_1.logger.section(stepLabel);
|
|
469
|
+
const wikiResult = await (0, run_wiki_generation_1.runWikiGeneration)({
|
|
470
|
+
repoRootFs: wikiSourceRoot,
|
|
471
|
+
wikiOutputRootFs: targetRoot,
|
|
472
|
+
wikiRelPath: node_path_1.default.join('baseline', 'wiki'),
|
|
473
|
+
wikiLang: opts.wikiLang,
|
|
474
|
+
wikiModel: opts.wikiModel,
|
|
475
|
+
wikiBaseUrl: opts.wikiBaseUrl,
|
|
476
|
+
wikiApiKey: opts.wikiApiKey,
|
|
477
|
+
projectName: validated.name,
|
|
478
|
+
cliVersion,
|
|
479
|
+
mode: wikiMode === 'full' ? 'full' : 'tasks',
|
|
480
|
+
cwd,
|
|
481
|
+
wikiAudience: 'agent',
|
|
482
|
+
sourceRootRel: node_path_1.default.relative(targetRoot, wikiSourceRoot).replace(/\\/g, '/') || '.',
|
|
483
|
+
onLog: (m) => logger_1.logger.info(m),
|
|
484
|
+
});
|
|
485
|
+
resolvedWikiPhase = wikiResult.wikiPhase;
|
|
486
|
+
resolvedWikiSource = wikiResult.wikiSource;
|
|
487
|
+
if (wikiResult.ok) {
|
|
488
|
+
if (wikiMode === 'full') {
|
|
489
|
+
logger_1.logger.success('baseline wiki 已生成');
|
|
511
490
|
}
|
|
512
491
|
else {
|
|
513
|
-
logger_1.logger.
|
|
492
|
+
logger_1.logger.success(`baseline wiki 任务清单已生成:${node_path_1.default.relative(targetRoot, wikiResult.tasksPath)} (共 ${wikiResult.pageCount} 页任务)`);
|
|
514
493
|
}
|
|
515
494
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
if (!wikiFullOk) {
|
|
519
|
-
resolvedWikiPhase = 'pending';
|
|
520
|
-
resolvedWikiSource = 'cli-full-degraded';
|
|
495
|
+
else if (wikiMode === 'full') {
|
|
496
|
+
// Full mode failure downgrades to PENDING so the agent can pick it up.
|
|
521
497
|
logger_1.logger.warn('S10_wiki 将标记为 PENDING,等待 agent 接力补齐');
|
|
522
498
|
}
|
|
523
499
|
}
|
|
524
|
-
else if (wikiMode === 'tasks') {
|
|
525
|
-
logger_1.logger.section(`步骤 ${stepWiki}/${totalSteps} - 生成 baseline wiki 任务清单`);
|
|
526
|
-
try {
|
|
527
|
-
const repoRootFs = wikiSourceRoot;
|
|
528
|
-
const wikiOutputRootFs = targetRoot;
|
|
529
|
-
const wikiRelPath = node_path_1.default.join('baseline', 'wiki');
|
|
530
|
-
if (hasBaseline) {
|
|
531
|
-
const exists = await fs_extra_1.default.pathExists(repoRootFs);
|
|
532
|
-
const entries = exists ? await fs_extra_1.default.readdir(repoRootFs) : [];
|
|
533
|
-
if (!exists || entries.length === 0) {
|
|
534
|
-
throw new Error(`Wiki 源码目录为空或不存在:${repoRootFs}。` +
|
|
535
|
-
'请检查基线拷贝结果(黑名单/大小上限可能过滤掉了所有文件)。');
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
const cfg = await (0, wiki_1.resolveWikiRunConfig)({
|
|
539
|
-
cwd,
|
|
540
|
-
apiKey: opts.wikiApiKey,
|
|
541
|
-
baseUrl: opts.wikiBaseUrl,
|
|
542
|
-
model: opts.wikiModel,
|
|
543
|
-
});
|
|
544
|
-
const langFolder = opts.wikiLang === 'en' ? 'en' : 'zh';
|
|
545
|
-
const langRun = (0, wiki_1.langRunForRepowikiTarget)(langFolder);
|
|
546
|
-
logger_1.logger.info(` 源码根:${repoRootFs}`);
|
|
547
|
-
logger_1.logger.info(` 输出到:${node_path_1.default.join(wikiOutputRootFs, wikiRelPath)}`);
|
|
548
|
-
logger_1.logger.info(` 语言:${langFolder} · 模型:${cfg.model}`);
|
|
549
|
-
logger_1.logger.info(' 模式:仅任务清单(tasks-only,默认)');
|
|
550
|
-
const outline = await (0, wiki_1.runRepowikiOutlineOnly)({
|
|
551
|
-
repoRootFs,
|
|
552
|
-
wikiOutputRootFs,
|
|
553
|
-
wikiRelPath,
|
|
554
|
-
baseUrl: cfg.baseUrl,
|
|
555
|
-
apiKey: cfg.apiKey,
|
|
556
|
-
model: cfg.model,
|
|
557
|
-
languages: [langRun],
|
|
558
|
-
comprehensiveSections: true,
|
|
559
|
-
onLog: (m) => logger_1.logger.info(m),
|
|
560
|
-
onProgress: (p) => {
|
|
561
|
-
if (p.total > 0) {
|
|
562
|
-
logger_1.logger.info(` [${p.done}/${p.total}] ${p.detail}`);
|
|
563
|
-
}
|
|
564
|
-
else {
|
|
565
|
-
logger_1.logger.info(` ${p.detail}`);
|
|
566
|
-
}
|
|
567
|
-
},
|
|
568
|
-
});
|
|
569
|
-
const tasksPath = await (0, wiki_1.writeWikiTasks)({
|
|
570
|
-
outline,
|
|
571
|
-
targetRoot,
|
|
572
|
-
wikiRelPath,
|
|
573
|
-
projectName: validated.name,
|
|
574
|
-
cliVersion,
|
|
575
|
-
model: cfg.model,
|
|
576
|
-
generatedAtIso: new Date().toISOString(),
|
|
577
|
-
sourceRootRel: node_path_1.default
|
|
578
|
-
.relative(targetRoot, wikiSourceRoot)
|
|
579
|
-
.replace(/\\/g, '/') || '.',
|
|
580
|
-
});
|
|
581
|
-
logger_1.logger.success(`baseline wiki 任务清单已生成:${node_path_1.default.relative(targetRoot, tasksPath)} (共 ${outline.pages.length} 页任务)`);
|
|
582
|
-
}
|
|
583
|
-
catch (err) {
|
|
584
|
-
if ((0, wiki_1.isAbortError)(err)) {
|
|
585
|
-
logger_1.logger.warn('wiki 任务清单生成被中断');
|
|
586
|
-
}
|
|
587
|
-
else {
|
|
588
|
-
logger_1.logger.warn(`wiki 任务清单生成失败(不回滚 harness 骨架):${err.message}`);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
500
|
// 7. Write build state file (after wiki, so wiki result is accurately reflected).
|
|
593
501
|
let declarationSummary;
|
|
594
502
|
try {
|