harnessmith 0.1.0__tar.gz

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 (110) hide show
  1. harnessmith-0.1.0/.claude/settings.local.json +16 -0
  2. harnessmith-0.1.0/.github/workflows/release.yml +22 -0
  3. harnessmith-0.1.0/.gitignore +22 -0
  4. harnessmith-0.1.0/AGENTS.md +100 -0
  5. harnessmith-0.1.0/CLAUDE.md +127 -0
  6. harnessmith-0.1.0/HarnessSmith.bat +177 -0
  7. harnessmith-0.1.0/HarnessSmith.sh +139 -0
  8. harnessmith-0.1.0/LICENSE +21 -0
  9. harnessmith-0.1.0/PKG-INFO +431 -0
  10. harnessmith-0.1.0/README.md +394 -0
  11. harnessmith-0.1.0/docs/02-development/00-overview.md +265 -0
  12. harnessmith-0.1.0/docs/02-development/01-slice-0-scaffold.md +48 -0
  13. harnessmith-0.1.0/docs/02-development/02-slice-1-golden-path.md +61 -0
  14. harnessmith-0.1.0/docs/02-development/03-slice-2-routing-and-context.md +49 -0
  15. harnessmith-0.1.0/docs/02-development/04-slice-3-product-web.md +95 -0
  16. harnessmith-0.1.0/docs/02-development/05-slice-4-mcp-tools.md +72 -0
  17. harnessmith-0.1.0/docs/02-development/06-slice-5-paradigms.md +75 -0
  18. harnessmith-0.1.0/docs/02-development/07-slice-6-tools-and-skills.md +79 -0
  19. harnessmith-0.1.0/docs/02-development/075-slice-6b-global-rules.md +55 -0
  20. harnessmith-0.1.0/docs/02-development/08-slice-7-wizard.md +77 -0
  21. harnessmith-0.1.0/docs/02-development/09-slice-8-sessions.md +59 -0
  22. harnessmith-0.1.0/docs/02-development/095-slice-8b-memory.md +76 -0
  23. harnessmith-0.1.0/docs/02-development/10-slice-9-stop-resume-reask.md +71 -0
  24. harnessmith-0.1.0/docs/02-development/11-slice-10-hitl-and-ask.md +91 -0
  25. harnessmith-0.1.0/docs/02-development/12-slice-11-mcp-management.md +123 -0
  26. harnessmith-0.1.0/docs/02-development/125-slice-12-anthropic-dual-spec.md +101 -0
  27. harnessmith-0.1.0/docs/02-development/13-slice-13-product-tui.md +108 -0
  28. harnessmith-0.1.0/docs/02-development/14-forge-add-incremental-regeneration.md +130 -0
  29. harnessmith-0.1.0/docs/02-development/15-llm-robustness-and-context.md +80 -0
  30. harnessmith-0.1.0/docs/README.md +17 -0
  31. harnessmith-0.1.0/examples/spec.yaml +45 -0
  32. harnessmith-0.1.0/harnessmith/__init__.py +3 -0
  33. harnessmith-0.1.0/harnessmith/catalog/__init__.py +166 -0
  34. harnessmith-0.1.0/harnessmith/catalog/mcp_servers.yaml +185 -0
  35. harnessmith-0.1.0/harnessmith/cli.py +348 -0
  36. harnessmith-0.1.0/harnessmith/cli_wizard.py +174 -0
  37. harnessmith-0.1.0/harnessmith/debuglog.py +59 -0
  38. harnessmith-0.1.0/harnessmith/generator.py +507 -0
  39. harnessmith-0.1.0/harnessmith/node_bootstrap.py +197 -0
  40. harnessmith-0.1.0/harnessmith/presets/__init__.py +50 -0
  41. harnessmith-0.1.0/harnessmith/presets/coding-assistant/mcp_prefill.yaml +13 -0
  42. harnessmith-0.1.0/harnessmith/presets/coding-assistant/spec.yaml +54 -0
  43. harnessmith-0.1.0/harnessmith/scaffold.py +145 -0
  44. harnessmith-0.1.0/harnessmith/spec.py +239 -0
  45. harnessmith-0.1.0/harnessmith/templates/.devcontainer/devcontainer.json.j2 +12 -0
  46. harnessmith-0.1.0/harnessmith/templates/.dockerignore.j2 +11 -0
  47. harnessmith-0.1.0/harnessmith/templates/.env.example.j2 +5 -0
  48. harnessmith-0.1.0/harnessmith/templates/.gitignore.j2 +17 -0
  49. harnessmith-0.1.0/harnessmith/templates/.python-version.j2 +1 -0
  50. harnessmith-0.1.0/harnessmith/templates/AGENTS.md.j2 +914 -0
  51. harnessmith-0.1.0/harnessmith/templates/Dockerfile.j2 +30 -0
  52. harnessmith-0.1.0/harnessmith/templates/LICENSE.j2 +21 -0
  53. harnessmith-0.1.0/harnessmith/templates/README.md.j2 +325 -0
  54. harnessmith-0.1.0/harnessmith/templates/RULES.md.j2 +10 -0
  55. harnessmith-0.1.0/harnessmith/templates/__launch_name__.bat.j2 +173 -0
  56. harnessmith-0.1.0/harnessmith/templates/__launch_name__.sh.j2 +143 -0
  57. harnessmith-0.1.0/harnessmith/templates/config.yaml.j2 +270 -0
  58. harnessmith-0.1.0/harnessmith/templates/pyproject.toml.j2 +50 -0
  59. harnessmith-0.1.0/harnessmith/templates/skills/example-skill/SKILL.md.j2 +30 -0
  60. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/__init__.py.j2 +3 -0
  61. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/__init__.py.j2 +17 -0
  62. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/config.py.j2 +681 -0
  63. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/context.py.j2 +471 -0
  64. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/debuglog.py.j2 +72 -0
  65. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/extensions.py.j2 +188 -0
  66. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/hooks.py.j2 +116 -0
  67. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/interaction.py.j2 +266 -0
  68. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/llm.py.j2 +425 -0
  69. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/llm_anthropic.py.j2 +422 -0
  70. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/loop.py.j2 +85 -0
  71. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/mcp.py.j2 +1251 -0
  72. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/memory.py.j2 +353 -0
  73. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/mock.py.j2 +109 -0
  74. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/paradigms/__init__.py.j2 +359 -0
  75. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/paradigms/agent.py.j2 +236 -0
  76. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/paradigms/ask.py.j2 +236 -0
  77. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/paradigms/plan.py.j2 +240 -0
  78. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/prompts.py.j2 +153 -0
  79. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/session.py.j2 +316 -0
  80. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/skills.py.j2 +143 -0
  81. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/tools.py.j2 +357 -0
  82. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/trace.py.j2 +110 -0
  83. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/harness/usage.py.j2 +207 -0
  84. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/interfaces/__init__.py.j2 +1 -0
  85. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/interfaces/cli.py.j2 +1261 -0
  86. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/interfaces/web.py.j2 +1456 -0
  87. harnessmith-0.1.0/harnessmith/templates/src/__project_slug__/interfaces/web_index.html.j2 +3296 -0
  88. harnessmith-0.1.0/harnessmith/templates/tests/_mcp_dummy_server.py.j2 +36 -0
  89. harnessmith-0.1.0/harnessmith/templates/tests/test_harness.py.j2 +2539 -0
  90. harnessmith-0.1.0/harnessmith/templates/tests/test_llm_anthropic.py.j2 +324 -0
  91. harnessmith-0.1.0/harnessmith/templates/tests/test_mcp.py.j2 +1126 -0
  92. harnessmith-0.1.0/harnessmith/templates/tests/test_memory.py.j2 +251 -0
  93. harnessmith-0.1.0/harnessmith/templates/tests/test_sessions.py.j2 +364 -0
  94. harnessmith-0.1.0/harnessmith/templates/tests/test_skills.py.j2 +112 -0
  95. harnessmith-0.1.0/harnessmith/templates/tests/test_web.py.j2 +1706 -0
  96. harnessmith-0.1.0/harnessmith/wizard/__init__.py +11 -0
  97. harnessmith-0.1.0/harnessmith/wizard/app.py +682 -0
  98. harnessmith-0.1.0/harnessmith/wizard/static/index.html +430 -0
  99. harnessmith-0.1.0/pyproject.toml +84 -0
  100. harnessmith-0.1.0/tests/test_catalog.py +109 -0
  101. harnessmith-0.1.0/tests/test_cli.py +102 -0
  102. harnessmith-0.1.0/tests/test_cli_wizard.py +165 -0
  103. harnessmith-0.1.0/tests/test_debuglog.py +61 -0
  104. harnessmith-0.1.0/tests/test_generator.py +1223 -0
  105. harnessmith-0.1.0/tests/test_golden.py +354 -0
  106. harnessmith-0.1.0/tests/test_node_bootstrap.py +149 -0
  107. harnessmith-0.1.0/tests/test_presets.py +53 -0
  108. harnessmith-0.1.0/tests/test_spec.py +176 -0
  109. harnessmith-0.1.0/tests/test_wizard.py +732 -0
  110. harnessmith-0.1.0/uv.lock +614 -0
@@ -0,0 +1,16 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(fuser -k 8001/tcp)",
5
+ "Bash(fuser -k 8841/tcp)",
6
+ "Bash(curl -s --max-time 2 http://127.0.0.1:8001/ -o /dev/null -w \"product :8001 -> %{http_code}\\\\n\")",
7
+ "Bash(curl -s --max-time 2 http://127.0.0.1:8841/ -o /dev/null -w \"wizard :8841 -> %{http_code}\\\\n\")",
8
+ "Bash(rm -rf /home/s1yu/HarnessForge/generate/obs_demo)",
9
+ "Read(//home/s1yu/longhaul-eval/product/**)",
10
+ "Bash(rm -rf .harness/sessions/* traces/* logs/* .harness/usage.json)",
11
+ "Bash(rm -rf /home/s1yu/longhaul-eval/workspace)",
12
+ "Bash(mkdir -p /home/s1yu/longhaul-eval/workspace)",
13
+ "Bash(chmod +x /home/s1yu/longhaul-eval/run_scenario_a.sh)"
14
+ ]
15
+ }
16
+ }
@@ -0,0 +1,22 @@
1
+ name: Release
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ # Must match the environment registered with the PyPI trusted publisher.
11
+ environment: pypi
12
+ permissions:
13
+ id-token: write
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - name: Check tag matches project version
17
+ run: |
18
+ tag="${GITHUB_REF_NAME#v}"
19
+ version=$(sed -n 's/^version = "\(.*\)"/\1/p' pyproject.toml)
20
+ [ "$tag" = "$version" ] || { echo "tag $tag != pyproject version $version"; exit 1; }
21
+ - run: pipx run build
22
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,22 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .pytest_cache/
6
+
7
+ # uv / virtualenv
8
+ .venv/
9
+
10
+ # Build artifacts (sdist/wheel) — produced by `uv build`, never tracked.
11
+ dist/
12
+ build/
13
+ .ruff_cache/
14
+
15
+ # Transient runtime/debug logs (serve, wizard, debuglog) — local-only, never shipped.
16
+ *.log
17
+
18
+ # Real API keys (human-managed, see CLAUDE.md §0.1) — never tracked.
19
+ .env
20
+
21
+ # Products generated by the wizard's one-click "generate" (default target dir).
22
+ generate/
@@ -0,0 +1,100 @@
1
+ # AGENTS.md — HarnessSmith 快速上手
2
+
3
+ > 面向**在本仓库里干活的 Agent** 的导航地图:30 秒认清这是什么、代码在哪、怎么跑、怎么算"做完"。
4
+ > 这里只讲"怎么上手";**硬约束与红线看 [`CLAUDE.md`](./CLAUDE.md),定位 / 范围 / 决策总表看 [`docs/02-development/00-overview.md`](./docs/02-development/00-overview.md)**。两者冲突时以那两份为准。
5
+
6
+ ## 1. 这是什么(一句话)
7
+
8
+ HarnessSmith 是 **agent harness 的代码生成器**(`create-next-app` for agent harnesses):吃一份 `HarnessSpec`(YAML / preset / 向导采集),渲染出一个**完整、独立、无 agent 框架锁定**的 Python 仓库。生成的产物与本生成器**零运行期关系**——生成即脱离。
9
+
10
+ ## 2. 两层心智(最重要,先记住)
11
+
12
+ 任何改动,先问自己**改的是哪一层**:
13
+
14
+ | 层 | 位置 | 你在改什么 |
15
+ |---|---|---|
16
+ | **生成器本体** | `harnessmith/*.py`(`spec` / `generator` / `cli` / `scaffold` / `wizard` …) | spec schema、渲染引擎、CLI、向导、catalog、preset |
17
+ | **产物模板** | `harnessmith/templates/**/*.j2` | **渲染后**才是用户拿到的代码。改这里 = 改所有未来产物 |
18
+
19
+ 推论:**测试必须覆盖"生成产物"本身**,不能只测生成器。黄金路径 = 生成 → `uv sync` → `pytest` → mock LLM 跑通一次 function-calling(见 §5)。
20
+
21
+ ## 3. 仓库地图
22
+
23
+ ```
24
+ harnessmith/ # ← 生成器本体(这个包发布到 PyPI)
25
+ spec.py # HarnessSpec(Pydantic v2 + YAML,extra=forbid)
26
+ generator.py # 渲染模板 → 写仓库 + git init + uv lock + 冒烟自检
27
+ cli.py # Typer 入口:new / wizard / doctor
28
+ scaffold.py # 生成器与 CLI 向导共享的烤默认 / slug 派生(纯 stdlib)
29
+ cli_wizard.py # 终端交互向导(questionary)
30
+ node_bootstrap.py # Node 系 MCP server 的离线预热 / node 直跑
31
+ debuglog.py # 生成器侧 debug 日志
32
+ catalog/mcp_servers.yaml # 精选静态 MCP catalog(向导/CLI 预填数据源)
33
+ presets/coding-assistant/ # 内置 preset(spec.yaml + mcp_prefill.yaml)
34
+ wizard/ # Web 向导(FastAPI 单页,[wizard] extra,绝不进产物)
35
+ templates/ # ← 产物模板(.j2)。渲染出的才是用户的代码
36
+ src/__project_slug__/harness/ # 产物核心:loop/llm/llm_anthropic/tools/context/
37
+ # session/interaction/hooks/usage/trace/prompts/
38
+ # paradigms/ + mcp/skills/memory(opt-in)
39
+ src/__project_slug__/interfaces/ # cli.py(+ web.py / web_index.html opt-in)
40
+ tests/ # 产物自带测试(mock LLM)
41
+ docs/02-development/ # 设计与切片文档(00-overview = 唯一口径)
42
+ tests/ # ← 生成器自身的测试(test_spec / test_generator / test_golden …)
43
+ ```
44
+
45
+ ## 4. 常用命令(已验证)
46
+
47
+ ```bash
48
+ uv sync # 装生成器依赖
49
+ uv sync --extra wizard # 额外装 Web 向导依赖(FastAPI/uvicorn)
50
+
51
+ # 生成产物
52
+ uv run harnessmith new my-agent --preset coding-assistant # 从 preset 非交互生成
53
+ uv run harnessmith new my-agent --spec ./harness.spec.yaml # 从手写 spec 生成
54
+ uv run harnessmith new my-agent --no-verify # 跳过生成后冒烟(离线/快迭代)
55
+ uv run harnessmith wizard # Web 向导
56
+ uv run harnessmith doctor # 本机工具链预检
57
+
58
+ # 测试(golden 默认被 addopts 的 -m 'not golden' 排除)
59
+ uv run pytest -q # 快测(生成器单元 + 产物渲染,~200 例,约 20s)
60
+ uv run pytest -m golden # 黄金快照:真生成 + uv sync + 产物 pytest(慢)
61
+ uv run pytest -m docker # 需要 Docker daemon
62
+
63
+ # 发布前
64
+ uv build # 产 sdist + wheel 到 dist/
65
+ uvx twine check dist/* # 校验元数据 + README 渲染
66
+ ```
67
+
68
+ > 默认开发用 **mock LLM**,不需要真 key(CLI `--mock`)。真 key / 联网 / 花钱属"只能人做",见 `CLAUDE.md §0.1 / §6`。
69
+
70
+ ## 5. "完成"的硬门槛(详见 `CLAUDE.md §5`)
71
+
72
+ 宣称做完前,至少:
73
+
74
+ - [ ] 新增 / 改动的生成器或模板代码**有自动化测试**。
75
+ - [ ] **黄金路径绿**:preset/示例 spec 生成 → `uv sync && pytest` 全绿 → mock 跑通一次 function-calling(含一次工具调用)。
76
+ - [ ] 断言生成的 `pyproject.toml` **不含 `langchain`/`langgraph`/`adk`**。
77
+ - [ ] 生成后冒烟自检绿;`ReadLints` 无新增告警。
78
+ - [ ] 动了 `HarnessSpec` / 模板核心 / 跨 ≥3 文件 → 额外跑 §5.2 回归(全量黄金 + Docker 冒烟 + `uvx harnessmith new` 冒烟)。
79
+
80
+ ## 6. 红线速记(命中即停,详见 `CLAUDE.md §6`)
81
+
82
+ - **绝不**在产物里引入任何 agent 编排框架(LangChain/LangGraph/ADK)——定位红线。
83
+ - **薄优先**:默认产物核心循环 150–300 行;重能力(MCP/Web/skills/memory)只走 **spec 开关**,关闭 = 代码与依赖中均不存在。
84
+ - 改 `HarnessSpec` 字段语义、给**默认产物**加运行期依赖、改 LLM API 面、任何让**密钥进 git/spec/trace/日志**的路径 → 先停下请人审。
85
+ - **密钥**只进 gitignored `.env`;`config.yaml` / spec 只存 env 引用名。
86
+
87
+ ## 7. HarnessSpec 速览(结构旋钮,决定生成什么)
88
+
89
+ `harnessmith/spec.py` 的 `HarnessSpec`(顶层字段):
90
+
91
+ `version` · `project_slug`(产物包名,默认 `agent_harness`)· `display_name`(产物 UI/README 显示名)· `language`(产物 Web 默认语言 `en`/`zh`)· `llms`(每 profile 选 `provider: openai|anthropic`)· `roles` · `prompts`(`system` + `rules_files`)· `tools` · `paradigms`(`agent`/`plan`/`ask` 多选,默认 `["agent"]`)· `interfaces`(`cli`/`web`/`tui`)· `mcp.enabled` · `skills.enabled` · `memory.enabled` · `observability` · `context`(种子)· `rag`/`secrets`(预留)。
92
+
93
+ 口径:**spec = 配方(生成什么 + 初值,结构轴);产物 `config.yaml` = 运行期权威(行为轴)**。结构变更要重新生成,行为变更不用。
94
+
95
+ ## 8. 入场阅读顺序
96
+
97
+ 1. `README.md` —— 速览定位与产物能力。
98
+ 2. `CLAUDE.md` —— 协作硬约束与红线(**必读**)。
99
+ 3. `docs/02-development/00-overview.md` —— 定位 / 范围 / 决策总表 / 切片地图(**唯一口径**)。
100
+ 4. 按当前任务**只读**相关 slice 子文档(`docs/02-development/` 下),别把全部塞进上下文。
@@ -0,0 +1,127 @@
1
+ # CLAUDE.md — HarnessSmith 项目守则
2
+
3
+ > Agent 进入本项目(**vibe coding 模式**)入场必读。本文件是 Agent 工作的**硬约束**。
4
+ > 项目定位 / 范围 / 决策 / 开发节奏与切片门禁见 `docs/02-development/00-overview.md`。
5
+ >
6
+ > **Tradeoff**:偏向"先把事情做对"。显然小到不值得讨论的操作自己判断,别让流程把简单任务搞复杂。
7
+
8
+ ## 0. 协作模式与角色边界
9
+
10
+ 本项目**主要由 Agent 开发**,人只负责决策、验证、以及"只能人做"的环境配置。
11
+
12
+ | 角色 | 负责 | 不做 |
13
+ |------|------|------|
14
+ | **Agent(你)** | 写生成器与模板代码、写/跑测试、调试、维护文档与变更记录、按 slice 自驱推进、**测试全绿后直接 commit + push `main`**、写完成报告 | 不替人拍板产品决策、不动用钱/联网批量/破坏定位的事、不 `--force` push、不在测试未绿时推 `main` |
15
+ | **人** | 方向与验收、slice 关键决策点签字、**环境与密钥配置(见 §0.1)**、对外发布 | 不写代码、不做 Agent 已能自动化的事 |
16
+
17
+ **一句话**:Agent 负责"怎么做",人负责"做不做、装环境、给 key、何时发布"。
18
+ 开发文档里写"开发者要做 X"一律理解为"**你(Agent)要做 X**,由人按切片节奏验收"。
19
+
20
+ ### 0.1 只能由人配置的环境(Agent 不代劳)
21
+
22
+ - 安装本机工具链:`uv`、`docker`(Agent 不改系统级配置、不装系统包)。
23
+ - **API key / `.env` 真实值**:Agent 只维护 `.env.example`,**绝不写入真实 `.env`、绝不把 key 写进任何被 git 跟踪的文件**。日常开发用 **mock LLM**,不需要真 key;只有"真实 LLM 冒烟 / 对外演示"才需要人提供 key。
24
+ - PyPI 发布凭证、GitHub 仓库设置、对外发布。
25
+
26
+ > Agent 需要某个 key / 环境时:在 plan 阶段列出所需 `.env` key 名与用途,**停下来请人配置**,不要自己编造或跳过。
27
+
28
+ ## 1. Think Before Coding
29
+
30
+ **不要假设。不要藏起困惑。把权衡显式说出来。** 动手前:声明假设,不确定就问;多种合理解读时列出来让人选;有更简单的方案先说一声。宁可开工前多问 1 个问题,不要跑了 30 分钟才发现方向错了。
31
+
32
+ ## 2. Thin First(薄优先 —— 本项目核心卖点,硬约束)
33
+
34
+ - 默认产物模板保持**极薄**:核心循环目标 150–300 行,整体远小于一个框架。
35
+ - 不做没要求的功能;一次性代码不先抽象;不为"以后可能要"加灵活性。
36
+ - 高级能力(RAG / MCP / context 策略 / Web)只通过 **spec 开关**生成,不塞进默认产物。
37
+ - 200 行能压到 50 行就重写。
38
+
39
+ ## 3. Surgical Changes
40
+
41
+ 只动该动的,只清理自己制造的烂摊子。不"顺手优化"无关代码或格式;不重构没坏的东西;沿用现有风格;发现 dead code **提一句**,别擅自删。
42
+
43
+ ## 4. 两层心智:生成器 vs 生成产物(本项目特有)
44
+
45
+ 你写的是一个**生成器**,它渲染出**独立的生成产物仓库**。任何时候分清你在改哪一层:
46
+
47
+ - 改 `harnessmith/`(生成器本体):spec / 渲染引擎 / CLI / 向导 / catalog / presets。
48
+ - 改 `harnessmith/templates/`(产物模板):渲染后才是用户拿到的代码。
49
+ - **测试必须覆盖"生成产物"本身**:生成 → `uv sync` → `pytest` → mock LLM 跑通一次工具调用,而不只是测生成器。
50
+
51
+ ## 5. 目标驱动 + 测试硬门槛(按生成器项目定制)
52
+
53
+ "完成"的硬门槛见 §5.1,没达到 = 没完成,别用"逻辑简单不用测"或"先合后补"做借口。
54
+ 例外:纯文档 / 纯注释 / 纯重命名(改名工具已覆盖全部引用)可不加测试,但要在完成报告里点名说明。
55
+
56
+ ### 5.1 黄金测试是"完成"的硬门槛
57
+
58
+ 任何被宣称完成的功能必须:
59
+
60
+ - 新增 / 改动的生成器或模板代码**有自动化测试**(unit 或 integration)。
61
+ - **黄金路径绿**:用示例 / preset spec 生成项目 → `uv sync && pytest` 全绿 → mock LLM 跑通一次 function-calling(含一次工具调用)。
62
+ - 断言生成的 `pyproject.toml` **不含 `langchain`/`langgraph`/`adk`**。
63
+ - **可运行性自检绿**:生成后冒烟(`uv sync` + import + mock 跑一步 + `pytest -q`)。
64
+ - `ReadLints`(IDE 诊断)无新增 error / warning。
65
+
66
+ ### 5.2 "大改动"额外跑回归
67
+
68
+ 判定:动了 `HarnessSpec`、模板核心,或跨 ≥ 3 个文件。额外跑:
69
+
70
+ - 全量黄金快照(示例 spec + 每个 preset 各生成并 `pytest`)。
71
+ - **Docker 冒烟**:生成产物 `docker build` 成功 + `docker run` 跑通 mock 一步。
72
+ - `uvx harnessmith new` 冒烟。
73
+
74
+ 任一项失败 → 该功能**未完成**,先修,不要急着开下一项。
75
+
76
+ ### 5.3 可自主决定(不必请示)
77
+
78
+ 命名;实现细节 / 内部数据结构 / stdlib 选择;已在文档区间内的参数(默认 context 策略、预算默认值等);fixture / mock 风格 / 测试组织;在 `pyproject.toml` 已列家族内加可选依赖(如 `pytest-*`)。执行后在完成报告里一句话提一下。
79
+
80
+ ## 6. 停下来问人 / 上报的触发条件(命中任一即停)
81
+
82
+ 1. 改 `HarnessSpec` schema 字段或其语义(影响生成器与所有模板)。
83
+ 2. 给**默认产物模板**新增运行期依赖,或把 L2/L3 依赖混进默认核心依赖(违反"薄")。
84
+ 3. 在生成产物里引入任何 **agent 编排框架**(LangChain/LangGraph/ADK…)——**定位红线,绝不允许**。
85
+ 4. 改 LLM API 面(Chat Completions ↔ Responses)或底座 SDK 选型。
86
+ 5. 任何可能让**密钥进入 git / spec 快照 / trace / 日志**的路径。
87
+ 6. 跨 slice 的范围调整:把 L3 提前进 MVP,或把 L1 推迟。
88
+ 7. 需要真实 key / 联网 / 花钱:真实 LLM 跑批、发布到 PyPI、对外网开放端口。
89
+ 8. 生成产物核心代码体积明显超"薄"目标(核心循环远超 300 行)。
90
+ 9. 同一问题连续两次尝试都失败——别进"再试一遍"循环,停下说清卡点。
91
+ 10. 多种合理实现且权衡不清,影响 ≥ 2 个模块 / 后续 slice。
92
+
93
+ 上报格式:**[需要人介入]** + 触发第几条 / 在做什么 / 卡在哪 / 已试过什么(尝试 1·2 及结果)/ 可选项 A·B 及代价 / 我的倾向。等回复再继续。
94
+
95
+ ## 7. Agent 标准工作循环:plan → implement → self-verify → handoff
96
+
97
+ - **plan**:读相关 slice 文档顶部"交付物";复述理解 + 实现计划(≤ 10 行);列会改的文件、新增测试、所需 `.env` key 名;命中 §6 就在 plan 阶段先停。
98
+ - **implement**:先用 Read/Grep/SemanticSearch 看现有代码沿用风格;小步走能 commit 就 commit;不碰无关代码。
99
+ - **self-verify**:跑 §5.1(大改动加 §5.2);自查 §1–§4 没违反;fail 先修不要假装看不见。
100
+ - **handoff**:**先按 §9 把受影响文档更新到与实现一致**(门禁勾选、字段 / 行为描述、决策点结论);再按 `docs/02-development/00-overview.md §完成报告模板` 输出报告;**改动构成一个完整功能且 §5.1 门禁全绿后,直接执行 commit(默认行为,无需先问"要不要提交")**,按 Conventional Commits。
101
+
102
+ ## 8. 提交与分支
103
+
104
+ - **Conventional Commits**:`feat: / fix: / refactor: / docs: / chore: / test: / perf: / build: / ci:`。
105
+ - 一个 commit 一件事,不捎带无关改动。
106
+ - **完整功能 + 测试完毕 = 直接 commit(默认行为,不必先问人)**:当本次改动构成一个完整功能(`feat` / `fix` 等)且 §5.1 门禁全绿后,**直接执行 commit**,不要停下来征求"要不要提交"。多个独立功能各自单独 commit(承接"一个 commit 一件事")。纯文档 / 注释 / 重命名等小改动同理可直接提交。
107
+ - `main` **不再受保护**:门禁全绿后,Agent **可直接 commit 并 push `main`**,无需 feature branch / PR。仍 **不 `--force` push、不跳 pre-commit hook**(除非人明确允许);测试未绿不得推 `main`。需要走 PR 时人会显式要求。
108
+
109
+ ## 9. 文档维护
110
+
111
+ - **任务完成即更新文档(硬规则)**:每个任务 / slice 一旦实现完成,必须把受影响的文档更新到**与实现一致**——slice 子文档勾选退出门禁、修正字段枚举 / 行为描述、回填人审决策点结论,并同步 `docs/02-development/00-overview.md` 的相关条目。**文档落后于实现 = 任务未完成**。
112
+ - 改 `HarnessSpec` 字段:同步 `docs/02-development/00-overview.md §5` + 相关 slice 文档。
113
+ - 改全局决策(罕见,走 §6 人审):必须改 `docs/02-development/00-overview.md` 的决策总表。
114
+ - 改 `.env.example` / 默认依赖:同步产物 README / AGENTS.md 与相关 slice 文档。
115
+ - 文档与代码相互引用的地方,**改一处必检另一处**。Agent 是这套文档体系的唯一维护者。
116
+
117
+ ## 10. 语言与文风
118
+
119
+ - 人机对话默认中文;代码注释 / commit / 文档正文沿用各文件原有语言,勿强行翻译。
120
+ - 不写"// 自增计数器"这类废话注释;不在文档里写"我 / 我们"或 LLM 客套话。
121
+
122
+ ## 11. 入场顺序(Agent 第一次进入本项目时)
123
+
124
+ 1. 读 `README.md` 速览定位。
125
+ 2. 读本文件(`CLAUDE.md`)—— 你正在读。
126
+ 3. 读 `docs/02-development/00-overview.md`(定位 / 范围 / 决策 / 切片门禁)。
127
+ 4. 按当前任务**只读**相关 slice 子文档;不要把全部文档塞进上下文。
@@ -0,0 +1,177 @@
1
+ @echo off
2
+ REM One-click launcher for the HarnessSmith setup wizard.
3
+ REM Lets you pick how to configure your harness: the Web form (opens in a browser)
4
+ REM or an interactive CLI wizard (right here in the terminal). Prefers uv (auto-syncs
5
+ REM the [wizard] extra for the web form); offers to install uv if it's missing.
6
+ echo [HarnessSmith] Setup launcher
7
+ cd /d "%~dp0"
8
+ echo [HarnessSmith] Folder: %CD%
9
+ echo.
10
+
11
+ REM --- Reuse the system proxy (corporate networks) so the curl probe below, the
12
+ REM official uv install, and uv's own sync all reach the net the same way. Only when
13
+ REM none is already set; curl/uv don't read the Windows WinINET proxy on their own,
14
+ REM whereas the wizard's urllib probe DOES - so without this the curl probe could
15
+ REM wrongly report PyPI unreachable and pin a mirror the proxy can't even reach.
16
+ if defined HTTP_PROXY goto :proxy_done
17
+ if defined HTTPS_PROXY goto :proxy_done
18
+ for /f "usebackq delims=" %%p in (`powershell -NoProfile -ExecutionPolicy Bypass -Command "$s=Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' -ErrorAction SilentlyContinue; if ($s.ProxyEnable -eq 1 -and $s.ProxyServer) { $p=[string]$s.ProxyServer; if ($p -match 'https?=([^;]+)') { $p=$matches[1] }; if ($p -notmatch '://') { $p='http://'+$p }; $p }"`) do set "HTTP_PROXY=%%p"
19
+ if not defined HTTP_PROXY goto :proxy_done
20
+ set "HTTPS_PROXY=%HTTP_PROXY%"
21
+ echo [HarnessSmith] Using system proxy: %HTTP_PROXY%
22
+ echo.
23
+ :proxy_done
24
+
25
+ REM Web form (browser) vs interactive CLI wizard (this terminal). Web is the
26
+ REM default on Windows (a browser is normally present). Set HARNESSMITH_MODE to
27
+ REM web or cli to skip the prompt (e.g. for automation). Flat goto flow only.
28
+ set "MODE=web"
29
+ if defined HARNESSMITH_MODE goto :mode_env
30
+ echo [HarnessSmith] How do you want to set up your harness?
31
+ echo [1] Web wizard - fill a form in your browser
32
+ echo [2] CLI wizard - interactive setup in this terminal
33
+ set /p "MODE= Choose [1/2] (Enter = web): "
34
+ if "%MODE%"=="2" set "MODE=cli"
35
+ if not "%MODE%"=="cli" set "MODE=web"
36
+ goto :mode_done
37
+ :mode_env
38
+ set "MODE=%HARNESSMITH_MODE%"
39
+ :mode_done
40
+ echo.
41
+
42
+ echo [HarnessSmith] Step 1/4: looking for uv on PATH ...
43
+ where uv >nul 2>nul
44
+ if not errorlevel 1 goto :run
45
+
46
+ echo [HarnessSmith] Step 2/4: checking known uv install locations ...
47
+ if exist "%USERPROFILE%\.local\bin\uv.exe" set "PATH=%USERPROFILE%\.local\bin;%PATH%"
48
+ if exist "%LOCALAPPDATA%\Microsoft\WinGet\Links\uv.exe" set "PATH=%LOCALAPPDATA%\Microsoft\WinGet\Links;%PATH%"
49
+ where uv >nul 2>nul
50
+ if not errorlevel 1 goto :run
51
+
52
+ REM Look for the installed console script by its .exe name on purpose: a bare
53
+ REM `harnessmith` would resolve to THIS file (HarnessSmith.bat) first, since
54
+ REM Windows searches the current dir and is case-insensitive -> infinite relaunch.
55
+ echo [HarnessSmith] Step 3/4: looking for an installed harnessmith command ...
56
+ where harnessmith.exe >nul 2>nul
57
+ if not errorlevel 1 goto :run_cmd
58
+
59
+ echo [HarnessSmith] Step 4/4: uv is not installed yet.
60
+ echo.
61
+ echo The wizard needs uv - a small tool that manages Python and dependencies for
62
+ echo you (user-level install, no admin required). How would you like to install it?
63
+ echo [1] Standard - official installer (downloads uv from GitHub)
64
+ echo [2] China mirror - pip + Tsinghua mirror (needs Python already installed)
65
+ echo [n] Don't install
66
+ set "CHOICE=1"
67
+ set /p "CHOICE= Choose [1/2/n]: "
68
+ if /i "%CHOICE%"=="n" goto :manual
69
+ if "%CHOICE%"=="2" goto :install_cn
70
+
71
+ REM Use the official installer directly (NOT winget): winget first updates its
72
+ REM own source CDN, which hangs for a long time and fails on some networks (e.g.
73
+ REM behind the GFW) even when the GitHub download itself works fine.
74
+ echo [HarnessSmith] Installing uv via the official installer ...
75
+ powershell -ExecutionPolicy Bypass -NoProfile -Command "irm https://astral.sh/uv/install.ps1 | iex"
76
+ if exist "%USERPROFILE%\.local\bin\uv.exe" set "PATH=%USERPROFILE%\.local\bin;%PATH%"
77
+ where uv >nul 2>nul
78
+ if not errorlevel 1 goto :run
79
+ goto :manual
80
+
81
+ :install_cn
82
+ echo [HarnessSmith] China mirror: installing uv from the Tsinghua PyPI mirror ...
83
+ set "PY="
84
+ where py >nul 2>nul && set "PY=py -3"
85
+ if not defined PY (
86
+ where python >nul 2>nul && set "PY=python"
87
+ )
88
+ if not defined PY (
89
+ echo [HarnessSmith] No Python found - the China-mirror path needs Python first.
90
+ goto :manual_cn
91
+ )
92
+ %PY% -m pip install --user uv -i https://pypi.tuna.tsinghua.edu.cn/simple
93
+ %PY% -m uv --version >nul 2>nul
94
+ if errorlevel 1 (
95
+ echo [HarnessSmith] pip did not produce a runnable uv.
96
+ goto :manual_cn
97
+ )
98
+ echo [HarnessSmith] uv installed. Using the Tsinghua mirror + your system Python.
99
+ set "UV_DEFAULT_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple"
100
+ set "UV_PYTHON_PREFERENCE=only-system"
101
+ goto :run_py
102
+
103
+ :manual
104
+ echo.
105
+ echo [HarnessSmith] Could not find or install uv. Install it manually (user-level, no admin):
106
+ echo winget install astral-sh.uv
107
+ echo - or - powershell -c "irm https://astral.sh/uv/install.ps1 ^| iex"
108
+ echo then double-click this again.
109
+ echo.
110
+ pause
111
+ goto :eof
112
+
113
+ :manual_cn
114
+ echo.
115
+ echo [HarnessSmith] The China-mirror path needs Python. Options:
116
+ echo 1) Install Python (https://www.python.org/downloads/ , reachable in China),
117
+ echo then run this again and choose [2].
118
+ echo 2) Or with Python present: pip install uv -i https://pypi.tuna.tsinghua.edu.cn/simple
119
+ echo 3) Or use a VPN/proxy and choose [1].
120
+ echo.
121
+ pause
122
+ goto :eof
123
+
124
+ :run
125
+ REM Auto-pick the package index: when no index is pinned and the official PyPI is
126
+ REM unreachable (e.g. behind the GFW), use the Tsinghua mirror for this run so the
127
+ REM first `uv sync` can still fetch deps. An explicit UV_DEFAULT_INDEX always wins;
128
+ REM we only probe when curl is present (ships with Windows 10 1803+).
129
+ if defined UV_DEFAULT_INDEX goto :run_go
130
+ where curl >nul 2>nul
131
+ if errorlevel 1 goto :run_go
132
+ echo [HarnessSmith] Checking whether PyPI is reachable ...
133
+ curl -sS -m 3 -I https://pypi.org/simple/ >nul 2>nul
134
+ if not errorlevel 1 goto :run_go
135
+ echo [HarnessSmith] PyPI looks unreachable - using the Tsinghua mirror this run.
136
+ set "UV_DEFAULT_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple"
137
+ :run_go
138
+ if "%MODE%"=="cli" goto :run_go_cli
139
+ echo [HarnessSmith] Launching: uv run --extra wizard harnessmith wizard --open
140
+ echo.
141
+ uv run --extra wizard harnessmith wizard --open
142
+ goto :after_run
143
+ :run_go_cli
144
+ echo [HarnessSmith] Launching: uv run harnessmith new
145
+ echo.
146
+ uv run harnessmith new
147
+ goto :after_run
148
+
149
+ :run_cmd
150
+ if "%MODE%"=="cli" goto :run_cmd_cli
151
+ echo [HarnessSmith] Launching: harnessmith.exe wizard --open
152
+ echo.
153
+ harnessmith.exe wizard --open
154
+ goto :after_run
155
+ :run_cmd_cli
156
+ echo [HarnessSmith] Launching: harnessmith.exe new
157
+ echo.
158
+ harnessmith.exe new
159
+ goto :after_run
160
+
161
+ :run_py
162
+ if "%MODE%"=="cli" goto :run_py_cli
163
+ echo [HarnessSmith] Launching: %PY% -m uv run --extra wizard harnessmith wizard --open
164
+ echo.
165
+ %PY% -m uv run --extra wizard harnessmith wizard --open
166
+ goto :after_run
167
+ :run_py_cli
168
+ echo [HarnessSmith] Launching: %PY% -m uv run harnessmith new
169
+ echo.
170
+ %PY% -m uv run harnessmith new
171
+ goto :after_run
172
+
173
+ :after_run
174
+ echo.
175
+ echo [HarnessSmith] Process exited (code %errorlevel%). Press a key to close.
176
+ pause >nul
177
+ goto :eof
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env bash
2
+ # One-click launcher for the HarnessSmith setup wizard.
3
+ #
4
+ # Lets you pick how to configure your harness: the Web form (opens in a browser)
5
+ # or an interactive CLI wizard (right here in the terminal). Prefers `uv` (which
6
+ # auto-syncs the optional [wizard] extra for the web form); if uv is missing it
7
+ # offers to install it (user-level, no root).
8
+ set -e
9
+ echo "[HarnessSmith] Setup launcher"
10
+ cd "$(dirname "$0")"
11
+ echo "[HarnessSmith] Folder: $PWD"
12
+
13
+ find_uv() {
14
+ if command -v uv >/dev/null 2>&1; then echo uv; return 0; fi
15
+ [ -x "$HOME/.local/bin/uv" ] && { echo "$HOME/.local/bin/uv"; return 0; }
16
+ return 1
17
+ }
18
+
19
+ # Web form vs interactive CLI wizard. On a headless Linux box (no display) the
20
+ # terminal wizard is the sensible default — the web form would only be reachable
21
+ # via SSH port-forward; elsewhere the browser form is the default. Echoes "web"
22
+ # or "cli" on stdout (prompts go to stderr). Set HARNESSMITH_MODE=web|cli to
23
+ # skip the prompt (e.g. for automation).
24
+ choose_mode() {
25
+ if [ -n "${HARNESSMITH_MODE:-}" ]; then echo "$HARNESSMITH_MODE"; return 0; fi
26
+ default=web
27
+ if [ "$(uname)" = Linux ] && [ -z "${DISPLAY:-}" ] && [ -z "${WAYLAND_DISPLAY:-}" ]; then
28
+ default=cli
29
+ fi
30
+ {
31
+ echo "[HarnessSmith] How do you want to set up your harness?"
32
+ echo " [1] Web wizard - fill a form in your browser"
33
+ echo " [2] CLI wizard - interactive setup here in the terminal"
34
+ printf " Choose [1/2] (Enter = %s): " "$default"
35
+ } >&2
36
+ read -r reply </dev/tty 2>/dev/null || reply=""
37
+ case "$reply" in
38
+ 1) echo web ;;
39
+ 2) echo cli ;;
40
+ *) echo "$default" ;;
41
+ esac
42
+ }
43
+
44
+ # Auto-pick the package index: when no index is pinned and the official PyPI is
45
+ # unreachable (e.g. behind the GFW), use the Tsinghua mirror for this run so the
46
+ # first `uv sync` can still fetch deps. An explicit UV_DEFAULT_INDEX always wins;
47
+ # we only probe when curl is available.
48
+ pick_index() {
49
+ [ -n "${UV_DEFAULT_INDEX:-}" ] && return 0
50
+ command -v curl >/dev/null 2>&1 || return 0
51
+ echo "[HarnessSmith] Checking whether PyPI is reachable ..."
52
+ if curl -sS -m 3 -I https://pypi.org/simple/ >/dev/null 2>&1; then return 0; fi
53
+ echo "[HarnessSmith] PyPI looks unreachable - using the Tsinghua mirror this run."
54
+ export UV_DEFAULT_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple
55
+ }
56
+
57
+ # Reuse the system proxy so the curl probe above, the uv install, and uv's sync all
58
+ # reach the net the same way. Only when none is already set. macOS's GUI proxy isn't
59
+ # read by curl/uv on their own, so on a Mac we read it via scutil and export it
60
+ # (Linux users set *_PROXY as usual -> this is a no-op there). Best-effort: any parse
61
+ # miss just leaves the env untouched. Mirrors the wizard's proxy-aware urllib probe.
62
+ pick_proxy() {
63
+ [ -n "${HTTP_PROXY:-}${HTTPS_PROXY:-}${http_proxy:-}${https_proxy:-}" ] && return 0
64
+ [ "$(uname)" = Darwin ] || return 0
65
+ command -v scutil >/dev/null 2>&1 || return 0
66
+ local enabled host port px
67
+ enabled="$(scutil --proxy 2>/dev/null | awk '/^[[:space:]]*HTTPSEnable/ {print $3}')"
68
+ [ "$enabled" = 1 ] || return 0
69
+ host="$(scutil --proxy 2>/dev/null | awk '/^[[:space:]]*HTTPSProxy/ {print $3}')"
70
+ [ -n "$host" ] || return 0
71
+ port="$(scutil --proxy 2>/dev/null | awk '/^[[:space:]]*HTTPSPort/ {print $3}')"
72
+ px="http://$host"
73
+ [ -n "$port" ] && px="$px:$port"
74
+ export HTTP_PROXY="$px" HTTPS_PROXY="$px"
75
+ echo "[HarnessSmith] Using system proxy: $px"
76
+ }
77
+
78
+ pick_proxy
79
+
80
+ echo "[HarnessSmith] Looking for uv ..."
81
+ uv_bin="$(find_uv || true)"
82
+ if [ -z "$uv_bin" ]; then
83
+ # No uv yet: an already-installed harnessmith command works too.
84
+ if command -v harnessmith >/dev/null 2>&1; then
85
+ echo "[HarnessSmith] Found the harnessmith command."
86
+ if [ "$(choose_mode)" = cli ]; then
87
+ echo "[HarnessSmith] Launching: harnessmith new"
88
+ exec harnessmith new
89
+ fi
90
+ echo "[HarnessSmith] Launching: harnessmith wizard --open"
91
+ exec harnessmith wizard --open
92
+ fi
93
+ echo "[HarnessSmith] uv is not installed yet. How would you like to install it?"
94
+ echo " [1] Standard - official installer (downloads from GitHub)"
95
+ echo " [2] China mirror - pip + Tsinghua mirror (needs python3 already installed)"
96
+ echo " [n] Don't install"
97
+ printf " Choose [1/2/n]: "
98
+ read -r choice </dev/tty 2>/dev/null || choice=1
99
+ case "$choice" in
100
+ [Nn]*) ;;
101
+ 2)
102
+ if command -v python3 >/dev/null 2>&1; then py=python3
103
+ elif command -v python >/dev/null 2>&1; then py=python
104
+ else py=; fi
105
+ if [ -n "$py" ]; then
106
+ echo "[HarnessSmith] Installing uv via pip + Tsinghua mirror ..."
107
+ "$py" -m pip install --user uv -i https://pypi.tuna.tsinghua.edu.cn/simple || true
108
+ if "$py" -m uv --version >/dev/null 2>&1; then
109
+ export UV_DEFAULT_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple
110
+ export UV_PYTHON_PREFERENCE=only-system
111
+ echo "[HarnessSmith] Launching: $py -m uv run --extra wizard harnessmith wizard --open"
112
+ exec "$py" -m uv run --extra wizard harnessmith wizard --open
113
+ fi
114
+ fi
115
+ echo "[HarnessSmith] China path needs python3 first (then: pip install uv -i <Tsinghua>)." >&2
116
+ ;;
117
+ *)
118
+ echo "[HarnessSmith] Installing uv via the official installer ..."
119
+ curl -LsSf https://astral.sh/uv/install.sh | sh \
120
+ || echo "[HarnessSmith] uv install failed; see https://docs.astral.sh/uv/" >&2 ;;
121
+ esac
122
+ uv_bin="$(find_uv || true)"
123
+ fi
124
+
125
+ if [ -n "$uv_bin" ]; then
126
+ pick_index
127
+ if [ "$(choose_mode)" = cli ]; then
128
+ echo "[HarnessSmith] Launching: $uv_bin run harnessmith new"
129
+ exec "$uv_bin" run harnessmith new
130
+ fi
131
+ echo "[HarnessSmith] Launching: $uv_bin run --extra wizard harnessmith wizard --open"
132
+ exec "$uv_bin" run --extra wizard harnessmith wizard --open
133
+ fi
134
+
135
+ echo "[HarnessSmith] Could not find or install uv. Install it (user-level, no root):" >&2
136
+ echo " curl -LsSf https://astral.sh/uv/install.sh | sh" >&2
137
+ echo " - or (China) - pip install uv -i https://pypi.tuna.tsinghua.edu.cn/simple" >&2
138
+ echo " then re-run this script." >&2
139
+ exit 1
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 EpisodeYu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.