@cyning/harness 0.3.0

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 (125) hide show
  1. package/AGENTS.md +20 -0
  2. package/CHANGELOG.md +127 -0
  3. package/README.md +133 -0
  4. package/bin/harness.js +7 -0
  5. package/ci/samples/README.md +42 -0
  6. package/ci/samples/pytest.yml.example +49 -0
  7. package/ci/samples/quality.yml.example +56 -0
  8. package/coding_wiki/templates/README.md +11 -0
  9. package/coding_wiki/templates/context.md +11 -0
  10. package/coding_wiki/templates/stable.md +19 -0
  11. package/coding_wiki/templates/volatile.md +15 -0
  12. package/docs/ARCHITECTURE.md +71 -0
  13. package/docs/DESIGN_ONTOLOGY_v1_zh.md +9 -0
  14. package/docs/HARNESS_GRAPH_MODEL_design_v0_zh.md +6 -0
  15. package/docs/HARNESS_GRAPH_MODEL_dialogue_archive_v1_zh.md +8 -0
  16. package/docs/ONBOARDING.md +126 -0
  17. package/docs/P0_V0.2_GAP.md +6 -0
  18. package/docs/README.md +38 -0
  19. package/docs/methodology/AUDIT_doc_consistency_2026-06-15_zh.md +69 -0
  20. package/docs/methodology/DOCUMENT_MAP_v1_zh.md +122 -0
  21. package/docs/methodology/README.md +147 -0
  22. package/docs/methodology/ROADMAP_v1_zh.md +136 -0
  23. package/docs/methodology/execution/P0_V0.2_GAP.md +93 -0
  24. package/docs/methodology/execution/PILOT_EVIDENCE_B2_v1_zh.md +132 -0
  25. package/docs/methodology/execution/README.md +12 -0
  26. package/docs/methodology/graph/HARNESS_GRAPH_MODEL_design_v0_zh.md +258 -0
  27. package/docs/methodology/graph/HARNESS_GRAPH_MODEL_dialogue_archive_v1_zh.md +132 -0
  28. package/docs/methodology/graph/README.md +22 -0
  29. package/docs/methodology/pointers/ADOPTION_METHODOLOGY_v1_zh.md +8 -0
  30. package/docs/methodology/pointers/ADOPTION_TARGET_STATE_v1_zh.md +6 -0
  31. package/docs/methodology/pointers/ASSESSMENT_etclovg_v1_zh.md +5 -0
  32. package/docs/methodology/pointers/ASSESSMENT_ink_v1_zh.md +5 -0
  33. package/docs/methodology/pointers/GUIDANCE_distribution_v1_zh.md +6 -0
  34. package/docs/methodology/pointers/HARNESS_V2_PLAN_v1_zh.md +8 -0
  35. package/docs/methodology/pointers/PILOT_kimi_code_fork_v1_zh.md +6 -0
  36. package/docs/methodology/pointers/POINTER_cyning_harness_repo_v1_zh.md +6 -0
  37. package/docs/methodology/pointers/README.md +40 -0
  38. package/docs/methodology/pointers/SDD_HAT_FLOW_v1_zh.md +8 -0
  39. package/docs/methodology/pointers/STRATEGY_MASTER_v1_zh.md +8 -0
  40. package/docs/methodology/pointers/STRATEGY_ONTOLOGY_v1_zh.md +9 -0
  41. package/docs/methodology/product/DESIGN_ONTOLOGY_v1_zh.md +719 -0
  42. package/docs/methodology/product/README.md +25 -0
  43. package/docs/methodology/prompts/PROMPT_article_theory_roundtable_v1_zh.md +127 -0
  44. package/docs/methodology/prompts/PROMPT_doc_consistency_audit_v1_zh.md +131 -0
  45. package/docs/methodology/prompts/PROMPT_feasibility_review_b_evidence_v1_zh.md +86 -0
  46. package/docs/methodology/prompts/README.md +10 -0
  47. package/docs/methodology/reviews/PROCESS_track_b_evidence_audit_2026-06-15_v1_zh.md +101 -0
  48. package/docs/methodology/reviews/README.md +9 -0
  49. package/docs/methodology/reviews/REVIEW_B3_pilot_evidence_and_sdd_compliance_bench_v1_zh.md +154 -0
  50. package/docs/methodology/reviews/REVIEW_B9_agent_shell_parallel_v1_zh.md +158 -0
  51. package/examples/demo_checkout/ACCEPTANCE.md +80 -0
  52. package/examples/demo_checkout/README.md +41 -0
  53. package/examples/demo_checkout/task_demo_p0_golden_v1.md +101 -0
  54. package/examples/oss-fork/README.md +139 -0
  55. package/examples/oss-fork/kimi-code/00_main.ai.md +23 -0
  56. package/examples/oss-fork/kimi-code/00_main.md +38 -0
  57. package/examples/oss-fork/kimi-code/01_struct.md +39 -0
  58. package/examples/oss-fork/kimi-code/02_version.md +6 -0
  59. package/examples/oss-fork/kimi-code/10_flow_cli_session.md +22 -0
  60. package/examples/oss-fork/kimi-code/README.md +15 -0
  61. package/golden/POINTER_gold_epic_serial.md +93 -0
  62. package/golden/README.md +30 -0
  63. package/graph/stubs/README.md +6 -0
  64. package/graph/templates/00_main.ai.md +28 -0
  65. package/graph/templates/00_main.md +52 -0
  66. package/graph/templates/01_struct.md +38 -0
  67. package/graph/templates/10_flow_MAIN.ai.md +43 -0
  68. package/graph/templates/10_flow_MAIN.md +47 -0
  69. package/graph/templates/99_mermaid_protocol.md +100 -0
  70. package/graph/templates/README.md +34 -0
  71. package/harness/README.md +18 -0
  72. package/harness/invokes/README.md +22 -0
  73. package/harness/invokes/TEMPLATE_invoke.md +59 -0
  74. package/harness/prompts/10-requirements.md +42 -0
  75. package/harness/prompts/22-task-audit.md +67 -0
  76. package/harness/prompts/30-execute-code.md +50 -0
  77. package/harness/prompts/FRAGMENT_30_gate_verify_v1_zh.md +30 -0
  78. package/harness/prompts/FRAGMENT_30_invoke_block_v1_zh.md +27 -0
  79. package/harness/prompts/README.md +28 -0
  80. package/harness/prompts/TEMPLATE_30_gate_stop.md +34 -0
  81. package/harness/templates/FRAGMENT_task_domain_infer_v1_zh.md +16 -0
  82. package/harness/templates/README.md +48 -0
  83. package/harness/templates/TASK_TEMPLATE.md +111 -0
  84. package/harness/templates/TASK_done_README.md +75 -0
  85. package/harness/templates/TASK_epic.md +101 -0
  86. package/harness/templates/TASK_graph_bootstrap.md +113 -0
  87. package/harness/templates/VIEW_done_by_domain.md +66 -0
  88. package/harness/templates/VIEW_done_thin_pointer.md +16 -0
  89. package/ide/adapters/AGENTS.md.fragment.example +31 -0
  90. package/ide/adapters/CLAUDE.md.fragment.example +30 -0
  91. package/ide/adapters/README.md +46 -0
  92. package/ide/adapters/cursor-harness-starter.mdc.example +37 -0
  93. package/lib/cli.js +180 -0
  94. package/lib/manifest.js +38 -0
  95. package/lib/paths.js +36 -0
  96. package/lib/run.js +33 -0
  97. package/package.json +37 -0
  98. package/schema/manifest.v1.schema.json +37 -0
  99. package/standards/POINTER_workspace_truth_v1_zh.md +57 -0
  100. package/standards/README.md +35 -0
  101. package/standards/SOURCES_v1_zh.md +49 -0
  102. package/standards/TEMPLATE_CODING_BASELINE_L1_v1_zh.md +88 -0
  103. package/standards/TEMPLATE_CODING_BASELINE_L2_backend_v1_zh.md +75 -0
  104. package/standards/TEMPLATE_CODING_BASELINE_L2_frontend_v1_zh.md +76 -0
  105. package/wizard/ONBOARDING_wizard_v1_zh.md +112 -0
  106. package/wizard/README.md +213 -0
  107. package/wizard/adopt-existing.sh +37 -0
  108. package/wizard/bootstrap-oss-fork-meta.sh +177 -0
  109. package/wizard/gate-check.sh +118 -0
  110. package/wizard/harness-sync.sh +237 -0
  111. package/wizard/install.sh +206 -0
  112. package/wizard/lib/common.sh +247 -0
  113. package/wizard/lib/manifest.sh +110 -0
  114. package/wizard/profiles/fullstack-node-py.json +21 -0
  115. package/wizard/profiles/harness-only.json +19 -0
  116. package/wizard/profiles/ios-cursor.json +19 -0
  117. package/wizard/profiles/issue-scan-presets.json +51 -0
  118. package/wizard/profiles/oss-fork-meta.json +17 -0
  119. package/wizard/scan-upstream-issues.sh +287 -0
  120. package/wizard/templates/FRAGMENT_rethink_backfill_task_v1_zh.md +44 -0
  121. package/wizard/templates/POINTER_PILOT_adoption_workspace.md +23 -0
  122. package/wizard/templates/TASK_TEMPLATE_upstream_pr_v1.md +164 -0
  123. package/wizard/templates/task_graph_bootstrap_v1.md +53 -0
  124. package/wizard/uninstall.sh +218 -0
  125. package/wizard/upgrade.sh +164 -0
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env bash
2
+ # cyning-harness · 将产品包选定轨道同步到已接入业务仓(不覆盖业务 task / reviews / 自定义图谱正文)
3
+ set -euo pipefail
4
+
5
+ MODE="${1:-}"
6
+ TARGET="${TARGET:-$(pwd)}"
7
+ SYNC_FORCE=0
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ # shellcheck source=lib/common.sh
10
+ source "$SCRIPT_DIR/lib/common.sh"
11
+ HARNESS_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
12
+
13
+ MARKER_BEGIN='<!-- cyning-harness:begin -->'
14
+ MARKER_END='<!-- cyning-harness:end -->'
15
+
16
+ usage() {
17
+ cat <<'EOF'
18
+ 用法(从业务仓根,或指定 TARGET):
19
+
20
+ CYNING_HARNESS=/path/to/cyning-harness \
21
+ TARGET=/path/to/ios_buy \
22
+ "$CYNING_HARNESS/wizard/harness-sync.sh" plan
23
+
24
+ "$CYNING_HARNESS/wizard/harness-sync.sh" apply
25
+
26
+ 或:
27
+
28
+ "$CYNING_HARNESS/wizard/harness-sync.sh" plan --target /path/to/project
29
+ "$CYNING_HARNESS/wizard/harness-sync.sh" apply --target /path/to/project
30
+ "$CYNING_HARNESS/wizard/harness-sync.sh" apply --target /path/to/project --force # 跳过 S5 git-clean
31
+
32
+ 依赖:业务仓已有 .cyning-harness/profile.json(由 install.sh 生成)
33
+
34
+ plan — 仅打印将复制的文件
35
+ apply — 执行复制(S5:git 仓须工作区干净,或 --force)
36
+ EOF
37
+ }
38
+
39
+ while [[ $# -gt 0 ]]; do
40
+ case "$1" in
41
+ plan|apply) MODE="$1"; shift ;;
42
+ --target) TARGET="$2"; shift 2 ;;
43
+ --force) SYNC_FORCE=1; shift ;;
44
+ -h|--help) usage; exit 0 ;;
45
+ *) echo "未知参数: $1" >&2; usage; exit 1 ;;
46
+ esac
47
+ done
48
+
49
+ [[ "$MODE" == "plan" || "$MODE" == "apply" ]] || { usage; exit 1; }
50
+
51
+ CYNING_HARNESS="${CYNING_HARNESS:-$HARNESS_ROOT}"
52
+ refuse_if_product_root "$TARGET" "$HARNESS_ROOT"
53
+ PROFILE_FILE="$TARGET/.cyning-harness/profile.json"
54
+
55
+ if [[ ! -f "$PROFILE_FILE" ]]; then
56
+ echo "错误: 未找到 $PROFILE_FILE" >&2
57
+ echo "请先运行: $CYNING_HARNESS/wizard/install.sh --target $TARGET --preset <name>" >&2
58
+ exit 1
59
+ fi
60
+
61
+ # 简易 JSON 布尔/字符串读取(无 jq 依赖)
62
+ json_bool() { grep -q "\"$1\"[[:space:]]*:[[:space:]]*true" "$PROFILE_FILE"; }
63
+
64
+ track_enabled() {
65
+ local k="$1" default="${2:-false}"
66
+ if ! grep -q "\"$k\"" "$PROFILE_FILE"; then
67
+ [[ "$default" == "true" ]]
68
+ return
69
+ fi
70
+ json_bool "$k"
71
+ }
72
+
73
+ json_str() {
74
+ local k="$1" def="$2"
75
+ local line
76
+ line="$(grep "\"$k\"" "$PROFILE_FILE" | head -1)"
77
+ if [[ -z "$line" ]]; then echo "$def"; return; fi
78
+ sed -E 's/.*:[[:space:]]*"([^"]*)".*/\1/' <<<"$line" | head -1
79
+ }
80
+
81
+ IDE_REL=""
82
+ if grep -q '"paths"' "$PROFILE_FILE"; then
83
+ IDE_REL="$(sed -n '/"paths"/,/^ \}/p' "$PROFILE_FILE" | grep '"ide_cursor"' | head -1 | sed -E 's/.*:[[:space:]]*"([^"]+)".*/\1/')"
84
+ fi
85
+ [[ -z "$IDE_REL" ]] && IDE_REL=".cursor/rules/06-harness-pointer.mdc"
86
+ CI_TRACK="$(json_str ci none)"
87
+
88
+ OPS=()
89
+
90
+ add_cp() {
91
+ local src="$1" dst="$2" note="${3:-}"
92
+ OPS+=("cp|$src|$dst|$note")
93
+ }
94
+
95
+ merge_action_label() {
96
+ local dst="$1"
97
+ if [[ ! -f "$dst" ]]; then
98
+ echo "merge(create)"
99
+ elif grep -qF "$MARKER_BEGIN" "$dst"; then
100
+ echo "merge(replace marker)"
101
+ else
102
+ echo "merge(append marker)"
103
+ fi
104
+ }
105
+
106
+ add_merge() {
107
+ local src="$1" dst="$2" note="${3:-}"
108
+ local action
109
+ action="$(merge_action_label "$dst")"
110
+ OPS+=("merge|$src|$dst|$note|$action")
111
+ }
112
+
113
+ merge_fragment_apply() {
114
+ local src="$1" dst="$2"
115
+ local tmp_block tmp_out
116
+
117
+ tmp_block="$(mktemp)"
118
+ {
119
+ printf '%s\n' "$MARKER_BEGIN"
120
+ cat "$src"
121
+ printf '%s\n' "$MARKER_END"
122
+ } > "$tmp_block"
123
+
124
+ if [[ ! -f "$dst" ]]; then
125
+ cp "$tmp_block" "$dst"
126
+ rm -f "$tmp_block"
127
+ return
128
+ fi
129
+
130
+ if grep -qF "$MARKER_BEGIN" "$dst"; then
131
+ tmp_out="$(mktemp)"
132
+ awk -v begin="$MARKER_BEGIN" -v end="$MARKER_END" -v blockfile="$tmp_block" '
133
+ BEGIN {
134
+ while ((getline line < blockfile) > 0) block = block line "\n"
135
+ close(blockfile)
136
+ inblock = 0
137
+ replaced = 0
138
+ }
139
+ index($0, begin) && !replaced {
140
+ printf "%s", block
141
+ inblock = 1
142
+ replaced = 1
143
+ next
144
+ }
145
+ inblock {
146
+ if (index($0, end)) inblock = 0
147
+ next
148
+ }
149
+ { print }
150
+ ' "$dst" > "$tmp_out"
151
+ mv "$tmp_out" "$dst"
152
+ else
153
+ printf '\n' >> "$dst"
154
+ cat "$tmp_block" >> "$dst"
155
+ fi
156
+ rm -f "$tmp_block"
157
+ }
158
+
159
+ if track_enabled harness_prompts false; then
160
+ for f in "$CYNING_HARNESS/harness/prompts/"*.md; do
161
+ [[ -f "$f" ]] || continue
162
+ add_cp "$f" "$TARGET/docs/harness/prompts/$(basename "$f")" "harness prompts"
163
+ done
164
+ fi
165
+
166
+ if track_enabled harness_invoke_template false; then
167
+ add_cp "$CYNING_HARNESS/harness/invokes/TEMPLATE_invoke.md" \
168
+ "$TARGET/docs/harness/invokes/TEMPLATE_invoke.md" "invoke 模板"
169
+ fi
170
+
171
+ if track_enabled ide_cursor true; then
172
+ add_cp "$CYNING_HARNESS/ide/adapters/cursor-harness-starter.mdc.example" \
173
+ "$TARGET/$IDE_REL" "Cursor 规则"
174
+ fi
175
+
176
+ if track_enabled ide_claude false; then
177
+ add_merge "$CYNING_HARNESS/ide/adapters/CLAUDE.md.fragment.example" \
178
+ "$TARGET/CLAUDE.md" "Claude IDE"
179
+ fi
180
+
181
+ if track_enabled ide_agents false; then
182
+ add_merge "$CYNING_HARNESS/ide/adapters/AGENTS.md.fragment.example" \
183
+ "$TARGET/AGENTS.md" "Agents IDE"
184
+ fi
185
+
186
+ # 图谱/wiki/standards:默认 sync 不覆盖(避免洗掉 01_struct);install 时写入
187
+ FORCE_TRACKS="${FORCE_TRACKS:-0}"
188
+ if [[ "$FORCE_TRACKS" == "1" ]]; then
189
+ if json_bool graph || grep -q '"graph"[[:space:]]*:[[:space:]]*true' "$PROFILE_FILE"; then
190
+ for f in "$CYNING_HARNESS/graph/templates/"*; do
191
+ [[ -f "$f" ]] || continue
192
+ add_cp "$f" "$TARGET/docs/_tech_graph/$(basename "$f")" "graph(force)"
193
+ done
194
+ fi
195
+ if json_bool wiki || grep -q '"wiki"[[:space:]]*:[[:space:]]*true' "$PROFILE_FILE"; then
196
+ for f in "$CYNING_HARNESS/coding_wiki/templates/"*; do
197
+ [[ -f "$f" ]] || continue
198
+ add_cp "$f" "$TARGET/docs/coding_wiki/$(basename "$f")" "wiki(force)"
199
+ done
200
+ fi
201
+ fi
202
+
203
+ if [[ ${#OPS[@]} -eq 0 ]]; then
204
+ echo "无同步项(检查 profile tracks)" >&2
205
+ exit 1
206
+ fi
207
+
208
+ if [[ "$MODE" == "apply" ]]; then
209
+ check_git_clean "$TARGET" "$SYNC_FORCE"
210
+ fi
211
+
212
+ echo "=== cyning-harness sync ($MODE) ==="
213
+ echo "产品包: $CYNING_HARNESS"
214
+ echo "目标仓: $TARGET"
215
+ echo "profile: $PROFILE_FILE"
216
+ echo ""
217
+
218
+ for op in "${OPS[@]}"; do
219
+ IFS='|' read -r kind src dst note extra <<<"$op"
220
+ if [[ "$kind" == "merge" ]]; then
221
+ echo "[$note · $extra] $src -> $dst"
222
+ if [[ "$MODE" == "apply" ]]; then
223
+ merge_fragment_apply "$src" "$dst"
224
+ fi
225
+ else
226
+ echo "[$note] $src -> $dst"
227
+ if [[ "$MODE" == "apply" ]]; then
228
+ mkdir -p "$(dirname "$dst")"
229
+ cp "$src" "$dst"
230
+ fi
231
+ fi
232
+ done
233
+
234
+ if [[ "$MODE" == "apply" ]]; then
235
+ echo ""
236
+ echo "完成。建议: $CYNING_HARNESS/wizard/gate-check.sh --target $TARGET"
237
+ fi
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env bash
2
+ # cyning-harness · 首次接入业务仓(生成 profile + 按 preset 复制)
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ # shellcheck source=lib/common.sh
7
+ source "$SCRIPT_DIR/lib/common.sh"
8
+ # shellcheck source=lib/manifest.sh
9
+ source "$SCRIPT_DIR/lib/manifest.sh"
10
+ HARNESS_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
11
+ TARGET=""
12
+ PRESET="harness-only"
13
+ DRY_RUN=0
14
+ IDE_LIST=""
15
+ TARGET_PROVIDED=0
16
+ PRESET_PROVIDED=0
17
+ IDE_PROVIDED=0
18
+
19
+ usage() {
20
+ cat <<'EOF'
21
+ 用法:
22
+
23
+ # 在产品包目录 · 交互引导(推荐)
24
+ cd /path/to/cyning-harness
25
+ ./wizard/install.sh
26
+
27
+ # 在业务仓目录 · 默认 target 为当前目录
28
+ cd /path/to/your-project
29
+ /path/to/cyning-harness/wizard/install.sh --preset harness-only
30
+
31
+ # 全参数非交互
32
+ /path/to/cyning-harness/wizard/install.sh \
33
+ --target /path/to/project --preset harness-only --ide cursor,claude,agents
34
+
35
+ preset(交互向导:1 或 2):
36
+ harness-only — 仅过程轨 + IDE(常用 · kimi-code-meta 等)
37
+ fullstack-node-py — 全栈五轨 + quality + pytest(Ink 类)
38
+ 其他 preset 仍可通过 --preset <name> 使用(如 ios-cursor、oss-fork-meta)
39
+
40
+ 选项:
41
+ --target PATH 业务仓根(未指定时进入引导;在业务仓内可默认当前目录)
42
+ --preset NAME 见 wizard/profiles/
43
+ --ide LIST 逗号分隔:cursor,claude,agents(写入 profile tracks)
44
+ --dry-run 只打印,不写入(profile 仍会写入供 sync plan)
45
+ EOF
46
+ }
47
+
48
+ while [[ $# -gt 0 ]]; do
49
+ case "$1" in
50
+ --target) TARGET="$2"; TARGET_PROVIDED=1; shift 2 ;;
51
+ --preset) PRESET="$2"; PRESET_PROVIDED=1; shift 2 ;;
52
+ --ide) IDE_LIST="$2"; IDE_PROVIDED=1; shift 2 ;;
53
+ --dry-run) DRY_RUN=1; shift ;;
54
+ -h|--help) usage; exit 0 ;;
55
+ *) echo "未知参数: $1" >&2; usage; exit 1 ;;
56
+ esac
57
+ done
58
+
59
+ CYNING_HARNESS="${CYNING_HARNESS:-$HARNESS_ROOT}"
60
+ VERSION="${HARNESS_VERSION:-$(git -C "$HARNESS_ROOT" describe --tags --always 2>/dev/null || echo unknown)}"
61
+
62
+ # --- 交互:仅补全命令行未提供的项 ---
63
+ if [[ "$TARGET_PROVIDED" -eq 0 ]]; then
64
+ echo "=== cyning-harness 安装向导 ==="
65
+ echo "产品包: $HARNESS_ROOT"
66
+ echo "版本: $VERSION"
67
+ echo ""
68
+ cwd="$(pwd)"
69
+ default_target=""
70
+ if ! is_product_root_path "$cwd" "$HARNESS_ROOT"; then
71
+ default_target="$cwd"
72
+ fi
73
+ if [[ -n "$default_target" ]]; then
74
+ read -r -p "业务仓路径 [$default_target]: " TARGET_INPUT
75
+ TARGET="${TARGET_INPUT:-$default_target}"
76
+ else
77
+ echo "请输入要接入 Harness 的业务仓路径"
78
+ read -r -p "业务仓路径: " TARGET_INPUT
79
+ [[ -n "$TARGET_INPUT" ]] || { echo "未输入路径,已取消。" >&2; exit 1; }
80
+ TARGET="$TARGET_INPUT"
81
+ fi
82
+ fi
83
+
84
+ if [[ "$PRESET_PROVIDED" -eq 0 ]]; then
85
+ if [[ "$TARGET_PROVIDED" -eq 1 ]]; then
86
+ echo "=== cyning-harness install(补全参数)==="
87
+ fi
88
+ print_preset_menu
89
+ read -r -p "preset [1]: " PRESET_INPUT
90
+ PRESET="$(resolve_preset_choice "$PRESET_INPUT" "$SCRIPT_DIR/profiles")"
91
+ fi
92
+
93
+ if [[ "$IDE_PROVIDED" -eq 0 ]]; then
94
+ if prompt_yes_no "是否配置 IDE 勾选?" "y"; then
95
+ print_ide_menu
96
+ read -r -p "IDE [1]: " IDE_INPUT
97
+ IDE_LIST="$(resolve_ide_choice "${IDE_INPUT:-1}")"
98
+ fi
99
+ fi
100
+
101
+ [[ -d "$TARGET" ]] || { echo "错误: 目录不存在: $TARGET" >&2; exit 1; }
102
+ TARGET="$(abs_path "$TARGET")"
103
+ refuse_if_product_root "$TARGET" "$HARNESS_ROOT"
104
+
105
+ PRESET_FILE="$SCRIPT_DIR/profiles/${PRESET}.json"
106
+ [[ -f "$PRESET_FILE" ]] || { echo "未知 preset: $PRESET" >&2; exit 1; }
107
+
108
+ run() {
109
+ if [[ "$DRY_RUN" == "1" ]]; then echo "[dry-run] $*"; else eval "$@"; fi
110
+ }
111
+
112
+ echo ""
113
+ echo "=== cyning-harness install ==="
114
+ echo "目标: $TARGET"
115
+ echo "preset: $PRESET"
116
+ [[ -n "$IDE_LIST" ]] && echo "ide: $IDE_LIST"
117
+ echo ""
118
+
119
+ run mkdir -p "$TARGET/.cyning-harness"
120
+ run mkdir -p "$TARGET/docs/tasks/active" "$TARGET/docs/tasks/done" \
121
+ "$TARGET/docs/tasks/_views" \
122
+ "$TARGET/docs/tasks/done/harness" "$TARGET/docs/tasks/done/cyning-harness" \
123
+ "$TARGET/docs/tasks/done/engineering" "$TARGET/docs/tasks/done/governance" \
124
+ "$TARGET/docs/tasks/done/chatbi" "$TARGET/docs/tasks/done/standards" \
125
+ "$TARGET/docs/tasks/done/epics"
126
+ run mkdir -p "$TARGET/docs/harness/prompts" "$TARGET/docs/harness/reviews" "$TARGET/docs/harness/invokes/by-task"
127
+ run mkdir -p "$TARGET/docs/_tech_graph" "$TARGET/docs/coding_wiki" "$TARGET/docs/standards"
128
+
129
+ PROFILE_DST="$TARGET/.cyning-harness/profile.json"
130
+
131
+ # profile 须真实写入,供 harness-sync plan/apply 读取(含 dry-run)
132
+ mkdir -p "$TARGET/.cyning-harness"
133
+ cp "$PRESET_FILE" "$PROFILE_DST"
134
+ if [[ -n "$IDE_LIST" ]]; then
135
+ patch_profile_ide "$PROFILE_DST" "$IDE_LIST"
136
+ fi
137
+
138
+ if [[ "$DRY_RUN" == "1" ]]; then
139
+ echo "[dry-run] profile: $PROFILE_DST"
140
+ else
141
+ printf '%s\n' "{\"cyning_harness_root\":\"$CYNING_HARNESS\"}" > "$TARGET/.cyning-harness/local.json"
142
+ fi
143
+
144
+ # 委托 sync(install 时 force 图谱/wiki)
145
+ export CYNING_HARNESS TARGET FORCE_TRACKS=1
146
+ if [[ "$DRY_RUN" == "1" ]]; then
147
+ "$SCRIPT_DIR/harness-sync.sh" plan --target "$TARGET"
148
+ else
149
+ "$SCRIPT_DIR/harness-sync.sh" apply --target "$TARGET"
150
+ write_manifest_init "$TARGET" "$VERSION" "$PRESET" "$IDE_LIST" "$PROFILE_DST"
151
+ fi
152
+
153
+ # CI
154
+ CI_MODE="$(grep '"ci"' "$PRESET_FILE" | sed -E 's/.*:[[:space:]]*"([^"]*)".*/\1/' | head -1)"
155
+ if [[ "$CI_MODE" == "both" ]]; then
156
+ run mkdir -p "$TARGET/.github/workflows"
157
+ run cp "$CYNING_HARNESS/ci/samples/quality.yml.example" "$TARGET/.github/workflows/quality.yml"
158
+ run cp "$CYNING_HARNESS/ci/samples/pytest.yml.example" "$TARGET/.github/workflows/pytest.yml"
159
+ fi
160
+
161
+ # standards L1/L2
162
+ if grep -q '"standards_l1"[[:space:]]*:[[:space:]]*true' "$PRESET_FILE"; then
163
+ for f in "$CYNING_HARNESS/standards/"TEMPLATE_CODING_BASELINE_L1*.md; do
164
+ [[ -f "$f" ]] || continue
165
+ run cp "$f" "$TARGET/docs/standards/"
166
+ done
167
+ fi
168
+ if grep -q '"standards_l2"[[:space:]]*:[[:space:]]*true' "$PRESET_FILE"; then
169
+ for f in "$CYNING_HARNESS/standards/"TEMPLATE_CODING_BASELINE_L2*.md; do
170
+ [[ -f "$f" ]] || continue
171
+ run cp "$f" "$TARGET/docs/standards/"
172
+ done
173
+ for f in "$CYNING_HARNESS/standards/"TEMPLATE_SOURCES*.md; do
174
+ [[ -f "$f" ]] || continue
175
+ run cp "$f" "$TARGET/docs/standards/"
176
+ done
177
+ fi
178
+
179
+ # done Hub + _views(仅不存在时)
180
+ if [[ ! -f "$TARGET/docs/tasks/done/README.md" ]]; then
181
+ run cp "$CYNING_HARNESS/harness/templates/TASK_done_README.md" \
182
+ "$TARGET/docs/tasks/done/README.md"
183
+ fi
184
+ if [[ ! -f "$TARGET/docs/tasks/_views/done.md" ]]; then
185
+ run cp "$CYNING_HARNESS/harness/templates/VIEW_done_thin_pointer.md" \
186
+ "$TARGET/docs/tasks/_views/done.md"
187
+ fi
188
+ if [[ ! -f "$TARGET/docs/tasks/_views/done_by_domain.md" ]]; then
189
+ run cp "$CYNING_HARNESS/harness/templates/VIEW_done_by_domain.md" \
190
+ "$TARGET/docs/tasks/_views/done_by_domain.md"
191
+ fi
192
+
193
+ # bootstrap task(仅不存在时)
194
+ if grep -q '"harness_task_bootstrap"[[:space:]]*:[[:space:]]*true' "$PRESET_FILE"; then
195
+ if [[ ! -f "$TARGET/docs/tasks/active/task_graph_bootstrap.md" ]] && [[ ! -f "$TARGET/docs/tasks/done/task_graph_bootstrap"*.md ]]; then
196
+ run cp "$CYNING_HARNESS/harness/templates/TASK_graph_bootstrap.md" \
197
+ "$TARGET/docs/tasks/active/task_graph_bootstrap.md"
198
+ fi
199
+ fi
200
+
201
+ echo ""
202
+ echo "安装完成。profile: $PROFILE_DST"
203
+ echo "manifest: $(manifest_file_path "$TARGET")"
204
+ echo "日后升级: npx @cyning/harness upgrade --target $TARGET"
205
+ echo "或维护者: cd $CYNING_HARNESS && ./wizard/upgrade.sh --target $TARGET"
206
+ echo "清空重装: $CYNING_HARNESS/wizard/uninstall.sh --target $TARGET"
@@ -0,0 +1,247 @@
1
+ # cyning-harness · wizard 脚本共享函数(source 用,勿直接执行)
2
+ # shellcheck shell=bash
3
+
4
+ MARKER_BEGIN='<!-- cyning-harness:begin -->'
5
+ MARKER_END='<!-- cyning-harness:end -->'
6
+
7
+ # 规范化绝对路径
8
+ abs_path() {
9
+ cd "$1" && pwd
10
+ }
11
+
12
+ # 判断目标路径是否为产品包根目录
13
+ is_product_root_path() {
14
+ local target="$1" harness_root="$2"
15
+ [[ "$(abs_path "$target")" == "$(abs_path "$harness_root")" ]]
16
+ }
17
+
18
+ # 禁止对产品包自身执行 install/sync(误在 cyning-harness 根目录 apply 时拦截)
19
+ refuse_if_product_root() {
20
+ local target="$1" harness_root="$2"
21
+ local target_abs harness_abs
22
+ target_abs="$(abs_path "$target")"
23
+ harness_abs="$(abs_path "$harness_root")"
24
+ if is_product_root_path "$target" "$harness_root"; then
25
+ echo "错误: 目标路径不能是 cyning-harness 产品包自身: ${harness_abs}" >&2
26
+ echo "提示: 在业务仓升级;或在产品包目录运行 wizard/upgrade.sh 按提示输入业务仓路径。" >&2
27
+ exit 1
28
+ fi
29
+ }
30
+
31
+ # 更新 profile.json tracks 布尔(无 jq)
32
+ set_json_track_bool() {
33
+ local file="$1" key="$2" val="$3"
34
+ local tmp
35
+ tmp="$(mktemp)"
36
+ if grep -q "\"$key\"" "$file"; then
37
+ sed -E "s/\"$key\"[[:space:]]*:[[:space:]]*(true|false)/\"$key\": $val/" "$file" > "$tmp"
38
+ else
39
+ sed "/\"ci\"/i\\
40
+ \"$key\": $val,
41
+ " "$file" > "$tmp"
42
+ fi
43
+ mv "$tmp" "$file"
44
+ }
45
+
46
+ patch_profile_ide() {
47
+ local profile="$1" ide_list="$2"
48
+ local want_cursor=false want_claude=false want_agents=false
49
+
50
+ IFS=',' read -ra parts <<< "$ide_list"
51
+ for raw in "${parts[@]}"; do
52
+ local p="${raw// /}"
53
+ case "$p" in
54
+ cursor) want_cursor=true ;;
55
+ claude) want_claude=true ;;
56
+ agents) want_agents=true ;;
57
+ *) echo "未知 IDE: $p(允许: cursor, claude, agents)" >&2; exit 1 ;;
58
+ esac
59
+ done
60
+
61
+ set_json_track_bool "$profile" ide_cursor "$want_cursor"
62
+ set_json_track_bool "$profile" ide_claude "$want_claude"
63
+ set_json_track_bool "$profile" ide_agents "$want_agents"
64
+ }
65
+
66
+ # 从 profile 读取 preset 名(无 jq)
67
+ profile_preset_name() {
68
+ local file="$1"
69
+ grep '"preset"' "$file" | head -1 | sed -E 's/.*:[[:space:]]*"([^"]+)".*/\1/'
70
+ }
71
+
72
+ # 从 profile 拼 IDE 勾选展示
73
+ profile_ide_summary() {
74
+ local file="$1"
75
+ local parts=()
76
+ if grep -q '"ide_cursor"[[:space:]]*:[[:space:]]*true' "$file"; then parts+=("cursor"); fi
77
+ if grep -q '"ide_claude"[[:space:]]*:[[:space:]]*true' "$file"; then parts+=("claude"); fi
78
+ if grep -q '"ide_agents"[[:space:]]*:[[:space:]]*true' "$file"; then parts+=("agents"); fi
79
+ if [[ ${#parts[@]} -eq 0 ]]; then
80
+ echo "(无 IDE 轨启用)"
81
+ else
82
+ local IFS=,
83
+ echo "${parts[*]}"
84
+ fi
85
+ }
86
+
87
+ prompt_yes_no() {
88
+ local prompt="$1" default="${2:-n}"
89
+ local hint="y/N"
90
+ if [[ "$default" == "y" ]]; then hint="Y/n"; fi
91
+ local ans=""
92
+ read -r -p "$prompt [$hint]: " ans
93
+ ans="${ans:-$default}"
94
+ [[ "$ans" =~ ^[yY] ]]
95
+ }
96
+
97
+ # 安装向导 · preset 代号菜单(交互仅 1/2;CLI --preset 仍可用 profiles 下任意 json)
98
+ print_preset_menu() {
99
+ cat <<'EOF'
100
+ preset 代号(回车=1):
101
+
102
+ 1 harness-only
103
+ · 仅 Harness 过程轨:prompts、invoke 模板、IDE 入口(Cursor / Claude / Agents)
104
+ · 不写入图谱 / wiki / standards / CI;不覆盖已有业务 task / reviews / by-task invoke
105
+ · 适合:kimi-code-meta、任意仓只要 SDD 帽子链与 invoke 纪律
106
+
107
+ 2 fullstack-node-py
108
+ · Ink 类全栈五轨:图谱模板 + coding_wiki + L1/L2 规范 + Harness 过程轨 + Cursor
109
+ · 附带 .github/workflows/quality.yml + pytest.yml(Next 前端 + Python API 后端 CI 样例)
110
+ · 适合:ai-ink-brain + api-python 同类前后端分仓或 monorepo
111
+
112
+ 亦可输入全称:harness-only / fullstack-node-py
113
+ EOF
114
+ }
115
+
116
+ # 将代号或全称解析为 preset 文件名(不含 .json)
117
+ resolve_preset_choice() {
118
+ local input="$1"
119
+ local profiles_dir="${2:-}"
120
+ case "${input:-1}" in
121
+ 1|h|harness|harness-only) echo "harness-only"; return ;;
122
+ 2|fs|full|fullstack|fullstack-node-py) echo "fullstack-node-py"; return ;;
123
+ esac
124
+ if [[ -n "$profiles_dir" && -f "$profiles_dir/${input}.json" ]]; then
125
+ echo "$input"
126
+ return
127
+ fi
128
+ echo "未知 preset 代号: $input(交互可用 1 或 2,或输入全称)" >&2
129
+ exit 1
130
+ }
131
+
132
+ # 安装向导 · IDE 代号菜单
133
+ print_ide_menu() {
134
+ cat <<'EOF'
135
+ IDE 代号(回车=1 全部):
136
+ 1 全部(cursor + claude + agents)
137
+ 2 仅 cursor
138
+ 3 仅 claude
139
+ 4 仅 agents
140
+ 可组合: 2,3 或 c,cl,a
141
+ EOF
142
+ }
143
+
144
+ # 将代号解析为 install --ide 逗号列表
145
+ resolve_ide_choice() {
146
+ local input="${1:-1}"
147
+ local want_cursor=false want_claude=false want_agents=false
148
+
149
+ if [[ -z "$input" || "$input" == "1" || "$input" == "all" ]]; then
150
+ echo "cursor,claude,agents"
151
+ return
152
+ fi
153
+
154
+ IFS=',' read -ra parts <<< "$input"
155
+ for raw in "${parts[@]}"; do
156
+ local p="${raw// /}"
157
+ case "$p" in
158
+ 1|all) want_cursor=true; want_claude=true; want_agents=true ;;
159
+ 2|c|cursor) want_cursor=true ;;
160
+ 3|cl|claude) want_claude=true ;;
161
+ 4|a|agents) want_agents=true ;;
162
+ *) echo "未知 IDE 代号: $p(可用 1–4 或 c/cl/a)" >&2; exit 1 ;;
163
+ esac
164
+ done
165
+
166
+ local out=()
167
+ [[ "$want_cursor" == true ]] && out+=("cursor")
168
+ [[ "$want_claude" == true ]] && out+=("claude")
169
+ [[ "$want_agents" == true ]] && out+=("agents")
170
+ if [[ ${#out[@]} -eq 0 ]]; then
171
+ echo "未选择任何 IDE" >&2
172
+ exit 1
173
+ fi
174
+ local IFS=,
175
+ echo "${out[*]}"
176
+ }
177
+
178
+ profile_json_bool() {
179
+ local file="$1" key="$2"
180
+ grep -q "\"$key\"[[:space:]]*:[[:space:]]*true" "$file"
181
+ }
182
+
183
+ profile_track_enabled() {
184
+ local file="$1" key="$2" default="${3:-false}"
185
+ if ! grep -q "\"$key\"" "$file"; then
186
+ [[ "$default" == "true" ]]
187
+ return
188
+ fi
189
+ profile_json_bool "$file" "$key"
190
+ }
191
+
192
+ profile_ide_cursor_rel() {
193
+ local file="$1" rel=""
194
+ if grep -q '"paths"' "$file"; then
195
+ rel="$(sed -n '/"paths"/,/^ \}/p' "$file" | grep '"ide_cursor"' | head -1 | sed -E 's/.*:[[:space:]]*"([^"]+)".*/\1/')"
196
+ fi
197
+ [[ -z "$rel" ]] && rel=".cursor/rules/06-harness-pointer.mdc"
198
+ echo "$rel"
199
+ }
200
+
201
+ # 从 CLAUDE.md / AGENTS.md 剥离 Harness marker 块;若仅剩空文件则删除
202
+ # S5 · sync/apply 前检测 Git 工作区干净(v0.3+)
203
+ # 非 git 仓跳过;--force 或 HARNESS_SYNC_FORCE=1 跳过
204
+ check_git_clean() {
205
+ local target="$1" force="${2:-0}"
206
+ if [[ "$force" == "1" || "${HARNESS_SYNC_FORCE:-0}" == "1" ]]; then
207
+ return 0
208
+ fi
209
+ if ! git -C "$target" rev-parse --git-dir >/dev/null 2>&1; then
210
+ return 0
211
+ fi
212
+ if git -C "$target" diff --quiet 2>/dev/null && git -C "$target" diff --cached --quiet 2>/dev/null; then
213
+ return 0
214
+ fi
215
+ echo "错误(S5): Git 工作区不干净,sync apply 已中止。" >&2
216
+ echo "请先 commit/stash,或使用 --force / HARNESS_SYNC_FORCE=1" >&2
217
+ git -C "$target" status --short >&2 || true
218
+ exit 1
219
+ }
220
+
221
+ remove_harness_marker_file() {
222
+ local dst="$1"
223
+ if [[ ! -f "$dst" ]]; then
224
+ return 0
225
+ fi
226
+ if ! grep -qF "$MARKER_BEGIN" "$dst"; then
227
+ return 0
228
+ fi
229
+ local tmp_out
230
+ tmp_out="$(mktemp)"
231
+ awk -v begin="$MARKER_BEGIN" -v end="$MARKER_END" '
232
+ BEGIN { inblock = 0 }
233
+ index($0, begin) { inblock = 1; next }
234
+ inblock {
235
+ if (index($0, end)) inblock = 0
236
+ next
237
+ }
238
+ { print }
239
+ ' "$dst" > "$tmp_out"
240
+ if [[ ! -s "$tmp_out" ]]; then
241
+ rm -f "$dst" "$tmp_out"
242
+ echo " 已删除(仅剩 Harness 内容): $dst"
243
+ else
244
+ mv "$tmp_out" "$dst"
245
+ echo " 已剥离 marker 块: $dst"
246
+ fi
247
+ }