svharness 0.8.0 → 0.13.2
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 +290 -61
- package/dist/adapters/claude-code.js +1 -0
- package/dist/adapters/codechat.js +1 -0
- package/dist/adapters/cursor.js +1 -0
- package/dist/adapters/generic.js +1 -0
- package/dist/adapters/index.js +2 -0
- package/dist/adapters/opencode.js +17 -0
- package/dist/adapters/qoder.js +1 -0
- package/dist/commands/apply.js +456 -71
- package/dist/commands/convert.js +371 -0
- package/dist/commands/init.js +156 -11
- package/dist/commands/references-apply-skills.js +47 -0
- package/dist/commands/wizard.js +442 -0
- package/dist/config/constants.js +7 -0
- package/dist/config/index.js +18 -0
- package/dist/config/load-config.js +54 -0
- package/dist/config/merge-options.js +115 -0
- package/dist/config/normalize.js +165 -0
- package/dist/config/save-config.js +40 -0
- package/dist/config/types.js +2 -0
- package/dist/core/agent-injector.js +58 -9
- package/dist/core/apply-project-entry.js +66 -0
- package/dist/core/build-project-entry.js +98 -0
- package/dist/core/doc-intake-paths.js +155 -0
- package/dist/core/extra-assets-intake.js +254 -0
- package/dist/core/markitdown-client.js +156 -0
- package/dist/core/next-steps.js +33 -22
- package/dist/core/project-ignore.js +53 -0
- package/dist/core/reference-apply-skills.js +35 -0
- package/dist/core/render-meta.js +2 -1
- package/dist/core/repomix-pack.js +3 -3
- package/dist/core/state.js +44 -24
- package/dist/index.js +211 -140
- package/dist/utils/harness-name.js +41 -0
- package/dist/utils/validate-args.js +147 -6
- package/dist/wiki/wikiTasksWriter.js +5 -6
- package/package.json +2 -1
- package/templates/_shared/apply-skills/harness-apply-skills-main.md +19 -78
- package/templates/_shared/build-rules/harness-build-rule-agent-agnostic.md +5 -5
- package/templates/_shared/build-rules/harness-build-rule-chinese-only.md +5 -5
- package/templates/_shared/build-rules/harness-build-rule-convert-check.md +46 -0
- package/templates/_shared/build-rules/harness-build-rule-memory-write.md +1 -1
- package/templates/_shared/build-rules/harness-build-rule-orchestrator-flow.md +36 -10
- package/templates/_shared/build-rules/harness-build-rule-skills-tasks-output.md +3 -2
- package/templates/_shared/build-rules/harness-build-rule-specs-schema.md +3 -3
- package/templates/_shared/build-rules/harness-build-rule-user-interaction.md +36 -16
- package/templates/_shared/build-skills/harness-build-skill-agent-env-merge.md +75 -0
- package/templates/_shared/build-skills/harness-build-skill-knowledge-builder.md +49 -85
- package/templates/_shared/build-skills/harness-build-skill-orchestrator.md +35 -18
- package/templates/_shared/build-skills/harness-build-skill-references-intake.md +91 -0
- package/templates/_shared/build-skills/harness-build-skill-spec-builder.md +19 -9
- package/templates/_shared/build-skills/harness-build-skill-wiki-writer.md +24 -24
- package/templates/_shared/build-skills/harness-build-skills-main.md +83 -0
- package/templates/_shared/meta/AGENTS_APPLY.md.ejs +139 -0
- package/templates/_shared/meta/{AGENTS.md.ejs → AGENTS_BUILD.md.ejs} +7 -5
- package/templates/_shared/meta/CHANGELOG.md.ejs +3 -3
- package/templates/_shared/meta/README.md.ejs +11 -9
- package/templates/_shared/meta/harness.yaml.ejs +28 -7
- package/templates/_shared/skeleton/baseline/code/.gitkeep +1 -0
- package/templates/_shared/skeleton/baseline/wiki/.gitkeep +1 -0
- package/templates/_shared/skeleton/references/apply-skills-registry.example.yaml +11 -0
- package/templates/_shared/skeleton/references/md/.gitkeep +1 -0
- package/templates/_shared/skeleton/references/raw/.gitkeep +1 -0
- package/templates/_shared/skeleton/references/yaml/.gitkeep +1 -0
- package/templates/_shared/skeleton/requirements/md/.gitkeep +1 -0
- package/templates/_shared/skeleton/requirements/raw/.gitkeep +1 -0
- package/templates/_shared/skeleton/requirements/yaml/.gitkeep +1 -0
- package/templates/android-xml/skeleton/agent-env/skills/harness-android-cli/SKILL.md +88 -0
- package/templates/android-xml/skeleton/agent-env/skills/harness-android-service-patterns/SKILL.md +205 -0
- package/templates/android-xml/skeleton/agent-env/skills/harness-android-xml-architecture/SKILL.md +138 -0
- package/templates/android-xml/skeleton/agent-env/skills/harness-lifecycle-management/SKILL.md +158 -0
- package/templates/android-xml/skeleton/agent-env/skills/harness-xml-ui/SKILL.md +112 -0
- package/templates/cpp/skeleton/agent-env/skills/harness-cmake-build/SKILL.md +163 -0
- package/templates/cpp/skeleton/agent-env/skills/harness-cpp-architecture/SKILL.md +157 -0
- package/templates/cpp/skeleton/agent-env/skills/harness-cpp-concurrency/SKILL.md +180 -0
- package/templates/cpp/skeleton/agent-env/skills/harness-memory-safety/SKILL.md +163 -0
- package/templates/cpp/skeleton/agent-env/skills/harness-modern-cpp/SKILL.md +149 -0
- package/templates/python/skeleton/agent-env/skills/harness-async-patterns/SKILL.md +162 -0
- package/templates/python/skeleton/agent-env/skills/harness-python-architecture/SKILL.md +160 -0
- package/templates/python/skeleton/agent-env/skills/harness-python-package-structure/SKILL.md +210 -0
- package/templates/python/skeleton/agent-env/skills/harness-python-performance/SKILL.md +207 -0
- package/templates/python/skeleton/agent-env/skills/harness-python-testing/SKILL.md +198 -0
- package/templates/svharness.config.example.yaml +40 -0
- package/templates/web-react/skeleton/agent-env/skills/harness-react-architecture/SKILL.md +177 -0
- package/templates/web-react/skeleton/agent-env/skills/harness-react-performance/SKILL.md +177 -0
- package/templates/web-react/skeleton/agent-env/skills/harness-react-testing/SKILL.md +193 -0
- package/templates/web-react/skeleton/agent-env/skills/harness-react-ui-patterns/SKILL.md +257 -0
- package/templates/web-react/skeleton/agent-env/skills/harness-state-management/SKILL.md +189 -0
- package/templates/_shared/skeleton/assets/baseline/code/.gitkeep +0 -1
- package/templates/_shared/skeleton/assets/baseline/wiki/.gitkeep +0 -1
- package/templates/_shared/skeleton/assets/raw/.gitkeep +0 -1
- package/templates/_shared/skeleton/assets/requirements/.gitkeep +0 -1
- /package/templates/_shared/skeleton/{assets/baseline/repomix → agent-env/_incoming/skills}/.gitkeep +0 -0
|
@@ -7,8 +7,12 @@ exports.detectBaselineMode = detectBaselineMode;
|
|
|
7
7
|
exports.validateArgs = validateArgs;
|
|
8
8
|
exports.listSupportedArches = listSupportedArches;
|
|
9
9
|
exports.listSupportedAgents = listSupportedAgents;
|
|
10
|
+
exports.isHarnessRoot = isHarnessRoot;
|
|
11
|
+
exports.resolveConvertPaths = resolveConvertPaths;
|
|
12
|
+
exports.validateConvertArgs = validateConvertArgs;
|
|
10
13
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
11
14
|
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
+
const harness_name_1 = require("./harness-name");
|
|
12
16
|
const SUPPORTED_ARCHES = [
|
|
13
17
|
'android-compose',
|
|
14
18
|
'android-xml',
|
|
@@ -21,9 +25,9 @@ const SUPPORTED_AGENTS = [
|
|
|
21
25
|
'qoder',
|
|
22
26
|
'cursor',
|
|
23
27
|
'claude-code',
|
|
28
|
+
'opencode',
|
|
24
29
|
'generic',
|
|
25
30
|
];
|
|
26
|
-
const NAME_RE = /^[a-z][a-z0-9-]{1,39}$/;
|
|
27
31
|
const DEFAULT_BASELINE_MAX_FILE_KB = 1024;
|
|
28
32
|
/**
|
|
29
33
|
* Decide whether the `--baseline` value looks like a git remote or a local path.
|
|
@@ -55,12 +59,19 @@ function detectBaselineMode(input) {
|
|
|
55
59
|
}
|
|
56
60
|
function validateArgs(input) {
|
|
57
61
|
const errors = [];
|
|
58
|
-
// name
|
|
62
|
+
// name (normalized to leading `.`, e.g. my-app → .my-app)
|
|
63
|
+
let normalizedName;
|
|
59
64
|
if (!input.name) {
|
|
60
|
-
errors.push('--name is required');
|
|
65
|
+
errors.push('--harness-name is required');
|
|
61
66
|
}
|
|
62
|
-
else
|
|
63
|
-
|
|
67
|
+
else {
|
|
68
|
+
const nameCheck = (0, harness_name_1.validateHarnessNameInput)(input.name);
|
|
69
|
+
if (!nameCheck.ok) {
|
|
70
|
+
errors.push(nameCheck.message);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
normalizedName = nameCheck.normalized;
|
|
74
|
+
}
|
|
64
75
|
}
|
|
65
76
|
// arch
|
|
66
77
|
if (!input.arch) {
|
|
@@ -115,7 +126,7 @@ function validateArgs(input) {
|
|
|
115
126
|
throw new Error('Argument validation failed:\n - ' + errors.join('\n - '));
|
|
116
127
|
}
|
|
117
128
|
return {
|
|
118
|
-
name: input.name,
|
|
129
|
+
name: normalizedName ?? (0, harness_name_1.normalizeHarnessName)(input.name),
|
|
119
130
|
arch: input.arch,
|
|
120
131
|
agent: input.agent,
|
|
121
132
|
baseline: baselineValue,
|
|
@@ -130,3 +141,133 @@ function listSupportedArches() {
|
|
|
130
141
|
function listSupportedAgents() {
|
|
131
142
|
return SUPPORTED_AGENTS;
|
|
132
143
|
}
|
|
144
|
+
const DEFAULT_CONCURRENCY = 3;
|
|
145
|
+
const DEFAULT_MAX_FILE_MB = 50;
|
|
146
|
+
const DEFAULT_TIMEOUT_SEC = 120;
|
|
147
|
+
const DEFAULT_ENDPOINT = 'http://markitdown.desaysz.site';
|
|
148
|
+
const ENV_ENDPOINT = 'SVHARNESS_MARKITDOWN_ENDPOINT';
|
|
149
|
+
const DEFAULT_CONVERT_TYPE = 'requirements';
|
|
150
|
+
/** True when `dir` exists and contains `harness.yaml`. */
|
|
151
|
+
function isHarnessRoot(dir) {
|
|
152
|
+
return fs_extra_1.default.existsSync(node_path_1.default.join(dir, 'harness.yaml'));
|
|
153
|
+
}
|
|
154
|
+
function harnessMdDir(harnessRoot, type) {
|
|
155
|
+
return node_path_1.default.join(harnessRoot, type, 'md');
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Resolve markdown output directory vs harness layout.
|
|
159
|
+
*
|
|
160
|
+
* - Standalone: `--output` is the final md directory (no `harness.yaml` under it).
|
|
161
|
+
* - Harness: `--output` points at harness root (has `harness.yaml`), or `--harness` is set
|
|
162
|
+
* → writes to `<harness>/<type>/md/` unless both `--harness` and `--output` are given
|
|
163
|
+
* (then `--output` is the final directory).
|
|
164
|
+
*/
|
|
165
|
+
function resolveConvertPaths(input) {
|
|
166
|
+
const outputProvided = input.output !== undefined;
|
|
167
|
+
const outputAbs = node_path_1.default.resolve(input.output ?? '.');
|
|
168
|
+
const harnessExplicit = !!input.harness;
|
|
169
|
+
if (harnessExplicit) {
|
|
170
|
+
const harnessRoot = node_path_1.default.resolve(input.harness);
|
|
171
|
+
if (outputProvided) {
|
|
172
|
+
return {
|
|
173
|
+
outputDir: outputAbs,
|
|
174
|
+
harnessRoot,
|
|
175
|
+
harnessMode: true,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
outputDir: harnessMdDir(harnessRoot, input.type),
|
|
180
|
+
harnessRoot,
|
|
181
|
+
harnessMode: true,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
if (isHarnessRoot(outputAbs)) {
|
|
185
|
+
return {
|
|
186
|
+
outputDir: harnessMdDir(outputAbs, input.type),
|
|
187
|
+
harnessRoot: outputAbs,
|
|
188
|
+
harnessMode: true,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
outputDir: outputAbs,
|
|
193
|
+
harnessMode: false,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Validate and normalise options for `svharness convert`.
|
|
198
|
+
*
|
|
199
|
+
* All parameters have sensible defaults:
|
|
200
|
+
* - --input defaults to `.` (current directory)
|
|
201
|
+
* - --endpoint defaults to http://markitdown.desaysz.site
|
|
202
|
+
* - --output defaults to `.` (current directory); see `resolveConvertPaths` for layout
|
|
203
|
+
*
|
|
204
|
+
* When --harness is explicitly given, it must exist and contain harness.yaml.
|
|
205
|
+
*/
|
|
206
|
+
function validateConvertArgs(input) {
|
|
207
|
+
const errors = [];
|
|
208
|
+
// input: default to ['.']
|
|
209
|
+
const patterns = (input.input ?? ['.']).map((s) => s.trim()).filter(Boolean);
|
|
210
|
+
const harnessExplicit = !!input.harness;
|
|
211
|
+
if (harnessExplicit) {
|
|
212
|
+
const harnessAbs = node_path_1.default.resolve(input.harness);
|
|
213
|
+
if (!fs_extra_1.default.existsSync(harnessAbs)) {
|
|
214
|
+
errors.push(`--harness path does not exist: ${harnessAbs}`);
|
|
215
|
+
}
|
|
216
|
+
else if (!fs_extra_1.default.statSync(harnessAbs).isDirectory()) {
|
|
217
|
+
errors.push(`--harness must be a directory: ${harnessAbs}`);
|
|
218
|
+
}
|
|
219
|
+
else if (!input.allowMissingHarnessYaml && !isHarnessRoot(harnessAbs)) {
|
|
220
|
+
errors.push(`--harness "${harnessAbs}" does not look like a harness root (missing harness.yaml)`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// endpoint: flag > env > default
|
|
224
|
+
let endpoint = (input.endpoint && input.endpoint.trim()) ||
|
|
225
|
+
(process.env[ENV_ENDPOINT] && process.env[ENV_ENDPOINT].trim()) ||
|
|
226
|
+
DEFAULT_ENDPOINT;
|
|
227
|
+
try {
|
|
228
|
+
const u = new URL(endpoint);
|
|
229
|
+
if (!/^https?:$/.test(u.protocol)) {
|
|
230
|
+
errors.push(`--endpoint must use http(s) scheme, got: ${u.protocol}`);
|
|
231
|
+
}
|
|
232
|
+
// strip trailing slash for consistent downstream concatenation
|
|
233
|
+
endpoint = endpoint.replace(/\/+$/, '');
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
errors.push(`--endpoint is not a valid URL: ${endpoint}`);
|
|
237
|
+
}
|
|
238
|
+
const concurrency = positiveInt(input.concurrency, DEFAULT_CONCURRENCY, '--concurrency', errors);
|
|
239
|
+
const maxFileMB = positiveInt(input.maxFileMB, DEFAULT_MAX_FILE_MB, '--max-file-mb', errors);
|
|
240
|
+
const timeoutSec = positiveInt(input.timeoutSec, DEFAULT_TIMEOUT_SEC, '--timeout-sec', errors);
|
|
241
|
+
// type: validate ConvertTargetType
|
|
242
|
+
const convertType = input.type && ['requirements', 'references'].includes(input.type)
|
|
243
|
+
? input.type
|
|
244
|
+
: DEFAULT_CONVERT_TYPE;
|
|
245
|
+
if (errors.length > 0) {
|
|
246
|
+
throw new Error('Argument validation failed:\n - ' + errors.join('\n - '));
|
|
247
|
+
}
|
|
248
|
+
const paths = resolveConvertPaths({
|
|
249
|
+
harness: input.harness,
|
|
250
|
+
output: input.output,
|
|
251
|
+
type: convertType,
|
|
252
|
+
});
|
|
253
|
+
return {
|
|
254
|
+
input: patterns,
|
|
255
|
+
outputDir: paths.outputDir,
|
|
256
|
+
harnessRoot: paths.harnessRoot,
|
|
257
|
+
harnessMode: paths.harnessMode,
|
|
258
|
+
endpoint,
|
|
259
|
+
concurrency,
|
|
260
|
+
maxFileMB,
|
|
261
|
+
timeoutSec,
|
|
262
|
+
type: convertType,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function positiveInt(raw, fallback, flag, errors) {
|
|
266
|
+
if (raw === undefined)
|
|
267
|
+
return fallback;
|
|
268
|
+
if (!Number.isFinite(raw) || raw <= 0) {
|
|
269
|
+
errors.push(`${flag} must be a positive number, got: ${raw}`);
|
|
270
|
+
return fallback;
|
|
271
|
+
}
|
|
272
|
+
return Math.floor(raw);
|
|
273
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Writes a `TASKS.md` inside `<wikiBase>/` based on an outline computed by
|
|
4
4
|
* `runRepowikiOutlineOnly`. The file is consumed by the downstream
|
|
5
|
-
* harness-build-skill-
|
|
5
|
+
* harness-build-skill-wiki-writer skill which fills page content iteratively.
|
|
6
6
|
*/
|
|
7
7
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
8
|
if (k2 === undefined) k2 = k;
|
|
@@ -72,10 +72,10 @@ function renderTasksMarkdown(input) {
|
|
|
72
72
|
lines.push("> 每页应产出 Markdown 文件到对应 `Output` 路径,内容严格基于基线源码。");
|
|
73
73
|
lines.push("");
|
|
74
74
|
lines.push("> **源码引用规范(强制,与 --generate-wiki 产出口径保持一致)**:");
|
|
75
|
-
lines.push("> 所有源码链接必须指向 `
|
|
76
|
-
lines.push("> 1. 行内链接:`[Foo.java:10-30](
|
|
75
|
+
lines.push("> 所有源码链接必须指向 `baseline/code/` 下的路径,允许两种写法(可混用):");
|
|
76
|
+
lines.push("> 1. 行内链接:`[Foo.java:10-30](../../baseline/code/<repo-relative>/Foo.java#L10-L30)`");
|
|
77
77
|
lines.push("> 2. Sources 行:`Sources: <repo-relative>/Foo.java:10-30, <repo-relative>/Bar.kt:5-8`");
|
|
78
|
-
lines.push("> `<repo-relative>` 指**相对 `
|
|
78
|
+
lines.push("> `<repo-relative>` 指**相对 `baseline/code/` 的路径**,既非仓库原始绝对路径,");
|
|
79
79
|
lines.push("> 也非 wiki md 文件的相对路径。行号使用 `start-end`(闭区间)。");
|
|
80
80
|
lines.push("");
|
|
81
81
|
lines.push("## Sections");
|
|
@@ -109,8 +109,7 @@ function renderTasksMarkdown(input) {
|
|
|
109
109
|
lines.push(` - 文件存在且非空`);
|
|
110
110
|
lines.push(` - 首段含页面标题(H1 与 title 一致)`);
|
|
111
111
|
lines.push(` - 至少引用 1 个 Relevant files 中的源码片段或路径`);
|
|
112
|
-
lines.push(` - Sources 行或行内链接的路径必须定位到 \`
|
|
113
|
-
lines.push("");
|
|
112
|
+
lines.push(` - Sources 行或行内链接的路径必须定位到 \`baseline/code/\` 下的真实文件`);
|
|
114
113
|
}
|
|
115
114
|
return lines.join("\n");
|
|
116
115
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svharness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"description": "CLI scaffolder for SDD-Driven Agent-Agnostic Coding Framework (harness)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"svharness": "./bin/cli.js",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"dist/",
|
|
11
11
|
"bin/",
|
|
12
12
|
"templates/",
|
|
13
|
+
"templates/svharness.config.example.yaml",
|
|
13
14
|
"README.md"
|
|
14
15
|
],
|
|
15
16
|
"engines": {
|
|
@@ -1,91 +1,32 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: harness-apply-skills-main
|
|
3
3
|
description: >
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
然后加载 `<harness_root>/agent-env/rules/` 作为强约束、扫描
|
|
7
|
-
`<harness_root>/agent-env/skills/*/SKILL.md` 作为子能力,并参考
|
|
8
|
-
`<harness_root>/assets/baseline/`、`<harness_root>/specs/` 产出代码。
|
|
9
|
-
当用户说"应用 harness-apply-skills-main 完成 xxx 功能开发"、
|
|
10
|
-
"使用 harness 开发 xxx"、"基于 harness 做 xxx"、"apply harness"、
|
|
11
|
-
"按 harness 规范实现 xxx" 时触发。
|
|
4
|
+
harness 应用入口(薄索引模式)。提示用户当前项目已注入 runtime skills/rules,
|
|
5
|
+
优先直接调用已注入的 `harness-apply-skills-*` 子 skill;如无匹配,再按普通开发流程执行。
|
|
12
6
|
---
|
|
13
7
|
|
|
14
|
-
# harness-apply-skills-main(harness
|
|
8
|
+
# harness-apply-skills-main(harness 应用入口)
|
|
15
9
|
|
|
16
|
-
你是一个 **harness
|
|
17
|
-
完成功能开发时,你不直接写业务代码,而是先把 harness 的约束、能力、基线、规格
|
|
18
|
-
装载到上下文,再按语义把任务路由到 harness 内部的子 skill。
|
|
10
|
+
你是一个 **harness 应用入口提示器**。当前项目已经注入运行期 rules 与 skills:
|
|
19
11
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
> 位置,也不要去读取外部 `binding.yaml`——它只是冗余副本,不是权威来源。
|
|
12
|
+
- harness 根目录:`__HARNESS_ROOT_REL__`
|
|
13
|
+
- 运行期技能目录:`__HARNESS_ROOT_REL__/agent-env/skills/`
|
|
14
|
+
- 运行期规则目录:`__HARNESS_ROOT_REL__/agent-env/rules/`
|
|
24
15
|
|
|
25
|
-
##
|
|
16
|
+
## 使用原则
|
|
26
17
|
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- `harness_version` —— harness 构建状态版本(来自 `<harness_root>/VERSION`)
|
|
32
|
-
- `target_project` —— 绑定的目标项目根路径
|
|
33
|
-
- `agent` —— 当前 IDE / agent 标识
|
|
34
|
-
- `applied_at` / `applied_by` —— 绑定时间与工具版本
|
|
35
|
-
- (旁置,不读)`./references/binding.yaml` —— 冗余副本,仅供 `svharnessbuild`
|
|
36
|
-
自身工具链(status / detach 等)解析;skill 运行时**不读它**。
|
|
18
|
+
1. 优先枚举当前项目已注入的 `harness-apply-skills-*` 子 skill,并调用最匹配者完成任务。
|
|
19
|
+
2. 每次任务必须遵守已注入的 rules(always_on 优先)。
|
|
20
|
+
3. 若没有匹配 skill,按普通实现流程完成,但仍需参考 `__HARNESS_ROOT_REL__/specs/`、`__HARNESS_ROOT_REL__/baseline/`。
|
|
21
|
+
4. 不修改 `__HARNESS_ROOT_REL__` 下的任何文件;harness 作为只读知识源。
|
|
37
22
|
|
|
38
|
-
##
|
|
23
|
+
## 典型触发
|
|
39
24
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
25
|
+
- 应用 harness-apply-skills-main 完成 xxx 功能开发
|
|
26
|
+
- 使用 harness 开发 xxx
|
|
27
|
+
- 基于 harness 做 xxx
|
|
43
28
|
|
|
44
|
-
|
|
45
|
-
`<harness_root>/agent-env/rules/*.md`(`always_on` 必须全部遵守;
|
|
46
|
-
`model_decision` 按本次任务语义挑选)。规则冲突时以 `always_on` 为准。
|
|
29
|
+
## 注意
|
|
47
30
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
**子 skill 路由表**。不要整目录朗读内容,按需要再深读。
|
|
51
|
-
|
|
52
|
-
4. **语义路由**:根据用户任务在路由表中选择最匹配的 1 ~ N 个子 skill。
|
|
53
|
-
若无匹配,回到普通实现路径,但仍必须遵守步骤 2 的规则。
|
|
54
|
-
|
|
55
|
-
5. **查阅基线与规格**(按需):
|
|
56
|
-
- `<harness_root>/assets/baseline/wiki/` —— 项目知识摘要,优先用于建立上下文
|
|
57
|
-
- `<harness_root>/assets/baseline/code/` —— 权威参考实现;禁止引用该目录外的路径作为"参考实现"
|
|
58
|
-
- `<harness_root>/specs/signals|ui|behavior|interfaces/` —— 若任务与信号/UI/行为/接口相关,必须先对齐 schema
|
|
59
|
-
- `<harness_root>/tasks/` —— 若存在匹配的任务模板,优先复用
|
|
60
|
-
|
|
61
|
-
6. **执行与交付**:严格在 `target_project` 下写代码,引用 harness 文件时用
|
|
62
|
-
相对路径(`harness_root_rel` + 子路径)。不得把 harness 内部文件复制到
|
|
63
|
-
目标项目;harness 是只读的知识源。
|
|
64
|
-
|
|
65
|
-
7. **记忆沉淀**:若本次产生了可复用经验,按 agent-env 的 memory 规范追加到
|
|
66
|
-
`<harness_root>/agent-env/memory/inbox/`(如果目录存在);不确定时停住询问。
|
|
67
|
-
|
|
68
|
-
## 守则(不得违反)
|
|
69
|
-
|
|
70
|
-
- **不得绕过本 skill 内嵌的 `## 绑定元数据` 小节**去猜测 harness 路径;路径错误必须让用户重绑定。
|
|
71
|
-
- **不得修改 harness 目录下的文件**(rules / skills / specs / baseline / VERSION)。
|
|
72
|
-
harness 是知识源,演进由 `svharnessbuild` 和 harness 内部的 maintainer skill 负责。
|
|
73
|
-
- **不得把 harness 的 always_on rules 当成"建议"**;它们是硬约束。
|
|
74
|
-
- **不得引用 `assets/baseline/code/` 之外的源码**作为"参考实现"。
|
|
75
|
-
- **不得把本 skill 当作业务 skill**:它只做上下文装载 + 路由,业务实现由子 skill 或
|
|
76
|
-
agent 本体完成。
|
|
77
|
-
- 所有面向用户的说明使用**中文**;字段名、类名、路径保留英文。
|
|
78
|
-
- 多个子 skill 有冲突结论时,显式告知用户并请其裁决,不得自行二选一。
|
|
79
|
-
|
|
80
|
-
## 典型触发例
|
|
81
|
-
|
|
82
|
-
- "应用 harness-apply-skills-main 完成无线充电开关功能开发"
|
|
83
|
-
- "基于 harness 新增设置项 Fragment"
|
|
84
|
-
- "按 harness 规范实现新信号 CMDID 0xA3"
|
|
85
|
-
- "apply harness 给我做一个埋点上报"
|
|
86
|
-
|
|
87
|
-
以上触发均需先走算法 1~5,再进入业务实现。
|
|
88
|
-
|
|
89
|
-
## 绑定元数据(权威来源,由 svharnessbuild apply 生成,请勿手工编辑)
|
|
90
|
-
|
|
91
|
-
{{BINDING_YAML_BLOCK}}
|
|
31
|
+
- 本 skill 不负责二级调度与 binding 解析。
|
|
32
|
+
- 如发现路径不一致,请重新执行 `svharness apply --force`。
|
|
@@ -5,14 +5,14 @@ harness 是 SDD-Driven Agent-Agnostic Coding Framework。spec 层与 task 层产
|
|
|
5
5
|
## 绝对禁止
|
|
6
6
|
|
|
7
7
|
- 在 `specs/**`、`tasks/**`、`agent-env/rules/**`、`agent-env/memory/categories/**` 的内容里出现以下任一:
|
|
8
|
-
- 具体 agent 名(如 `qoder`、`codechat`、`cursor`、`claude-code`、`claude`、`chatgpt`、`gpt-4` 等)。
|
|
8
|
+
- 具体 agent 名(如 `qoder`、`codechat`、`cursor`、`claude-code`、`opencode`、`claude`、`chatgpt`、`gpt-4` 等)。
|
|
9
9
|
- 某一 agent 的私有指令语法(如特定 slash command、特定 frontmatter 字段)。
|
|
10
|
-
- 某一 agent 的私有目录路径(`.qoder/`、`.codechat/`、`.cursor/`、`.claude/` 等)。
|
|
10
|
+
- 某一 agent 的私有目录路径(`.qoder/`、`.codechat/`、`.cursor/`、`.claude/`、`.opencode/` 等)。
|
|
11
11
|
|
|
12
12
|
## 允许出现 agent 标识的位置(白名单)
|
|
13
13
|
|
|
14
14
|
- `svharnessbuild` CLI 源码与模板(负责适配注入,天然需要区分)。
|
|
15
|
-
- `
|
|
15
|
+
- `AGENTS_BUILD.md` 中的"已知 agent 适配状态"段落(描述性说明,非约束性规则)。
|
|
16
16
|
- `.harness-build-state.yaml` 的 `agent` 字段(单一记录当前适配的 agent)。
|
|
17
17
|
|
|
18
18
|
## 正反例
|
|
@@ -31,5 +31,5 @@ harness 是 SDD-Driven Agent-Agnostic Coding Framework。spec 层与 task 层产
|
|
|
31
31
|
|
|
32
32
|
## 违规处理
|
|
33
33
|
|
|
34
|
-
-
|
|
35
|
-
- 历史违规条目在
|
|
34
|
+
- S50_generate_specs、S70_runtime_assets 阶段退出前必须 grep 上述黑名单关键词,命中即阶段 FAILED。
|
|
35
|
+
- 历史违规条目在 S90_finalize 前必须清理或抽象化重写。
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
以下产出**必须**使用中文撰写:
|
|
8
8
|
|
|
9
|
-
- `
|
|
9
|
+
- `requirements/yaml/*.requirements.yaml` 中 `description`、`acceptance` 字段
|
|
10
10
|
- `specs/**/*.yaml` 中所有描述性字段(如 `description`、`note`、`summary`、`purpose`)
|
|
11
11
|
- `agent-env/rules/**/*.md` 或 `*.mdc` 中正文段落、正反例注释、检测说明
|
|
12
12
|
- `agent-env/skills/*/SKILL.md` 正文及 description
|
|
13
13
|
- `tasks/templates/**/*.md` 正文
|
|
14
|
-
- `
|
|
14
|
+
- `baseline/wiki/**/*.md` 正文段落
|
|
15
15
|
- `agent-env/memory/**/*.md` 正文
|
|
16
|
-
- `
|
|
16
|
+
- `AGENTS_BUILD.md`、`README.md`、`CHANGELOG.md` 正文
|
|
17
17
|
- `.harness-build-state.yaml` 中 `next_action` 字段
|
|
18
18
|
- Agent 向用户输出的所有状态卡片、提示、确认、错误信息
|
|
19
19
|
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
> `description: "Show or hide the scheduled charging settings entry based on P_TMRAVA signal"`
|
|
39
39
|
|
|
40
40
|
正例:
|
|
41
|
-
> `next_action: '请将原始需求文档放入
|
|
41
|
+
> `next_action: '请将原始需求文档放入 requirements/raw/ 目录后,告诉 Agent 继续构建'`
|
|
42
42
|
|
|
43
43
|
反例(严禁):
|
|
44
44
|
> `next_action: 'Say "begin harness build" in your AI agent session to invoke harness-build-skill-orchestrator.'`
|
|
45
45
|
|
|
46
46
|
## 违规处理
|
|
47
47
|
|
|
48
|
-
-
|
|
48
|
+
- S90_finalize 前必须全量扫描上述文件,发现非中文描述即标记阶段 FAILED。
|
|
49
49
|
- 历史违规条目必须在中文化后才能推进到下一阶段。
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# harness-build-rule-convert-check — 原始文档转换前置检查规则
|
|
2
|
+
|
|
3
|
+
本规则约束 `S30_convert_docs` 阶段中 Agent 对 `requirements/raw/` 和 `references/raw/` 中原始文档的转换行为。
|
|
4
|
+
|
|
5
|
+
## 检查范围
|
|
6
|
+
|
|
7
|
+
Agent 在进入 S30_convert_docs 阶段后必须逐一检查以下两个目录:
|
|
8
|
+
|
|
9
|
+
- `__HARNESS_ROOT__requirements/raw/` —— 正式需求的原始文档
|
|
10
|
+
- `__HARNESS_ROOT__references/raw/` —— 参考资料原始文档
|
|
11
|
+
|
|
12
|
+
## 转换前置检查
|
|
13
|
+
|
|
14
|
+
对上述两个目录执行以下检查:
|
|
15
|
+
|
|
16
|
+
1. **列出原始文件**:扫描目录,识别所有非 `.md`、非 `.gitkeep` 的文件(如 `.pdf`、`.docx`、`.pptx`、`.xlsx`、`.html`、`.txt`、`.csv` 等)。
|
|
17
|
+
2. **检查对应 md 是否存在**:对每个原始文件,检查对应 `md/` 目录中是否存在同基名的 `.md` 文件(如 `requirements/raw/需求文档.pdf` → `requirements/md/需求文档.md`)。
|
|
18
|
+
3. **判断是否需要转换**:仅当 `md/` 中缺少对应文件时,才需要执行转换;已存在的跳过。
|
|
19
|
+
|
|
20
|
+
## 转换方式(按优先级)
|
|
21
|
+
|
|
22
|
+
1. **优先调用 CLI**:若运行环境支持,直接调用 `svharness convert --input <raw文件或目录> --output __HARNESS_ROOT__ --type requirements` 或 `--type references` 完成转换。
|
|
23
|
+
2. **提示用户手动运行**:若 Agent 无法直接调用 CLI,以表单形式提示用户运行上述命令,并提供完整命令示例。
|
|
24
|
+
|
|
25
|
+
## 产物校验
|
|
26
|
+
|
|
27
|
+
转换完成后,Agent 必须校验:
|
|
28
|
+
|
|
29
|
+
- `requirements/md/` 包含 `requirements/raw/` 中每份非 md 源文档对应的 `.md` 文件
|
|
30
|
+
- `references/md/` 包含 `references/raw/` 中每份非 md 源文档对应的 `.md` 文件
|
|
31
|
+
|
|
32
|
+
## 禁止事项
|
|
33
|
+
|
|
34
|
+
- **不得删除原始文件**:`raw/` 中的源文档保留作为原始证据,不得移动或删除。
|
|
35
|
+
- **不得在 raw 下生成 converted_md**:转换产物只能写入 `__HARNESS_ROOT__requirements/md/` 或 `__HARNESS_ROOT__references/md/`;禁止在 `raw/` 内创建 `converted_md/` 或其它中间目录。
|
|
36
|
+
- **不得无中生有**:禁止在 `md/` 中生成无 `raw/` 源文件对应的 `.md` 文件。
|
|
37
|
+
- **不得跳过校验**:即使调用 CLI 成功,也必须确认产物文件存在。
|
|
38
|
+
- **不得修改文件内容**:转换为 Markdown 后,内容应忠实反映源文档,Agent 不得在转换过程中添加、删除或修改信息。
|
|
39
|
+
|
|
40
|
+
## 失败处理
|
|
41
|
+
|
|
42
|
+
若某个文件转换失败(如文件损坏、格式不支持),Agent 应:
|
|
43
|
+
|
|
44
|
+
- 记录失败文件路径和原因
|
|
45
|
+
- 以表单形式告知用户,并提供手动处理建议
|
|
46
|
+
- 其他文件继续完成转换,不因单文件失败阻塞整个阶段
|
|
@@ -28,4 +28,4 @@ agent-env/memory/
|
|
|
28
28
|
|
|
29
29
|
- 阶段退出时自动扫描 inbox。
|
|
30
30
|
- 用户显式请求"记住...""别忘了..."时写入 inbox。
|
|
31
|
-
- `harness-build-skill-
|
|
31
|
+
- `harness-build-skill-references-intake` 或 `harness-build-skill-agent-env-merge` 处理 references/规则候选时同步产生的经验写入 inbox。
|
|
@@ -4,14 +4,36 @@
|
|
|
4
4
|
|
|
5
5
|
## 阶段顺序(不得重排)
|
|
6
6
|
|
|
7
|
-
1. **
|
|
8
|
-
2. **
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
1. **S10_wiki** — 生成 `baseline/wiki/` 与 `baseline/code/SAMPLES.md`。**仅当项目有 baseline 时存在此阶段**;无 baseline 时跳过。退出:wiki 页面或任务清单可回溯到具体源码。Wiki 是项目经验底座,先于需求文档生成,为后续所有阶段提供架构知识参考。
|
|
8
|
+
2. **S20_collect_inputs** — 添加相关文件(四项输入收集)。
|
|
9
|
+
- 2.1 需求文档:提醒用户放入 `requirements/raw/`(也可由用户在 svharness 指令中指定来源路径)。
|
|
10
|
+
- 2.2 参考资料:提醒用户放入 `references/raw/`(也可由用户在 svharness 指令中指定来源路径)。
|
|
11
|
+
- 2.3 额外运行期资源:询问是否通过 `--extra-skills <path...>` 提供(支持文件/目录/glob,可混放 skills/rules,先暂存到 `agent-env/_incoming/skills/`)。
|
|
12
|
+
- 2.4 类型确认约束:若有规则或未知类型资源,必须在 S65 依据 `agent-env/_incoming/manifest.yaml` 完成分流确认后再写入最终目录。
|
|
13
|
+
准入:四类目标目录存在;退出:`requirements/raw/` 至少 1 份真实文档,且其余三项已完成"已提供/无新增"确认。
|
|
14
|
+
3. **S30_convert_docs** — 将 `requirements/raw/` 和 `references/raw/` 中的非 Markdown 原始文档统一转为 Markdown,产物落到对应 `md/` 目录。准入:S20_collect_inputs DONE;退出:每份源文档在对应 `md/` 目录中有同基名的 `.md` 文件。
|
|
15
|
+
4. **S40_extract_requirements** — 由 `harness-build-skill-spec-builder` 将 raw 条目化到 `requirements/yaml/`。退出:每条需求有稳定 id 与归属域。
|
|
16
|
+
5. **S50_generate_specs** — 生成 `specs/{signals,ui,behavior,interfaces}/*.yaml`。退出:全部通过 `specs/*/schema.json` 校验。
|
|
17
|
+
6. **S60_process_references**(阶段叙事:**references 处理**)— 对 S20 中的 references 执行处理与结构化:
|
|
18
|
+
- 必须执行 `svharness convert`,不得跳过转换直接抽取;
|
|
19
|
+
- 必须产出结构化索引(规制约束 / skills 候选 / signals / manuals,缺项需显式标注);
|
|
20
|
+
- references 落地策略需经用户表单确认。
|
|
21
|
+
退出:references 已完成转换、结构化索引与落地确认。
|
|
22
|
+
7. **S61_confirm_baseline_extraction**(阶段叙事:**baseline 自动提取确认**)— 在进入 agent-env 写入前,确认是否启用从 baseline 自动提取 skills/rules。
|
|
23
|
+
- 必须以表单明确选择:启用 / 关闭;
|
|
24
|
+
- 未确认不得默认启用自动提取;
|
|
25
|
+
- 确认结果必须可被后续阶段读取(用于 S65 策略分支),建议写入 `phases.S61_confirm_baseline_extraction.baseline_auto_extract`。
|
|
26
|
+
退出:自动提取开关状态已确认并记录。
|
|
27
|
+
8. **S65_customize_agent_env**(阶段叙事:**agent-env 定制**)— 对 S20 中的 extra-skills / extra-rules 执行合并写入(来源可来自单入口 `--extra-skills` 的 `_incoming/manifest.yaml`;若 S61 启用,可纳入 baseline 自动提取候选)。
|
|
28
|
+
- 任何冲突(同名文件、目录已存在、命名不符合规范、语义重复)必须先给出修改建议;
|
|
29
|
+
- 待用户表单确认后再写入;
|
|
30
|
+
- 未确认不得覆盖。
|
|
31
|
+
退出:extra-skills / extra-rules 完成"无冲突写入"或"冲突已确认后写入/跳过"。
|
|
32
|
+
9. **S70_runtime_assets** — 产出运行期 `agent-env/skills/` 与 `tasks/templates/`。
|
|
33
|
+
语义约束:skills 是执行 task 的能力积木,task 是可复用的流程模板。
|
|
34
|
+
退出:每 skill 有 SKILL.md,tasks 覆盖三类典型场景。
|
|
35
|
+
10. **S80_seed_memory** — 播种 `agent-env/memory/categories/` 初始分类。退出:inbox 流转规则已落地。
|
|
36
|
+
11. **S90_finalize** — 翻转 `specs.bootstrap_mode = false`,归档。
|
|
15
37
|
|
|
16
38
|
## 硬约束
|
|
17
39
|
|
|
@@ -19,7 +41,11 @@
|
|
|
19
41
|
- 遇到 `BLOCKED` 阶段必须显式报出阻塞原因,不得静默跳过。
|
|
20
42
|
- 阶段状态流转仅允许 `PENDING → IN_PROGRESS → DONE/FAILED/BLOCKED`。
|
|
21
43
|
- 任一阶段退出条件未满足,`current_phase` 不得推进。
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
44
|
+
- **S10_wiki 仅在项目有 baseline 时出现**;无 baseline 时该阶段不存在,不在 state 中写入,流程从 S20_collect_inputs 开始。
|
|
45
|
+
- **S20_collect_inputs 退出前必须确认 `requirements/raw/` 中存在用户放入的真实文档**,Agent 不得自行生成占位文件。
|
|
46
|
+
- **S20_collect_inputs 必须对 references/skills/rules 三项完成显式征询**(已提供 / 暂无新增 / 稍后补充)。
|
|
47
|
+
- **S60_process_references 阶段中,references 必须先转换为 Markdown 并完成结构化索引确认**。
|
|
48
|
+
- **S61_confirm_baseline_extraction 阶段中,必须显式确认是否启用 baseline 自动提取 skills/rules,禁止默认启用**。
|
|
49
|
+
- **S65_customize_agent_env 阶段中,任何写入冲突必须先给建议,再经用户确认后写入**。
|
|
24
50
|
- **任何涉及流程变更或内容修改的操作必须经用户表单确认**(见 `harness-build-rule-user-interaction`)。
|
|
25
51
|
- **所有面向用户的输出必须使用中文**(见 `harness-build-rule-chinese-only`)。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# harness-build-rule-skills-tasks-output — skills 与 tasks 产出规范
|
|
2
2
|
|
|
3
|
-
本规则约束
|
|
3
|
+
本规则约束 S70_runtime_assets 阶段产出物的结构与命名。`harness-build-skill-knowledge-builder` 与人工协作时必须遵守(S60/S61/S65 职责不在本规则覆盖范围)。
|
|
4
4
|
|
|
5
5
|
## skills 产出
|
|
6
6
|
|
|
@@ -12,7 +12,7 @@ agent-env/skills/
|
|
|
12
12
|
|
|
13
13
|
- 每个 skill **必须**独占一个子目录,主文件**固定命名** `SKILL.md`(大写,不得用 `skill.md` 或 `<name>.md`)。
|
|
14
14
|
- `SKILL.md` 顶部 yaml frontmatter 必备字段:`name`、`description`。description 中需含中英触发关键词。
|
|
15
|
-
- skill 名使用 `kebab-case
|
|
15
|
+
- skill 名使用 `kebab-case`。运行期 skill 建议使用 `harness-apply-<topic>` 前缀,避免与构建期 `harness-build-*` 混淆。
|
|
16
16
|
|
|
17
17
|
## tasks 产出
|
|
18
18
|
|
|
@@ -33,3 +33,4 @@ tasks/templates/
|
|
|
33
33
|
- skill 与 task 文档中不得硬编码任何具体 agent 的私有指令格式(见 harness-build-rule-agent-agnostic.md)。
|
|
34
34
|
- skill 描述中不得出现"本 agent""本工具"一类自指代,必须写明能力本身。
|
|
35
35
|
- 产出物必须可被任意符合 SKILL.md 规范的 agent 消费。
|
|
36
|
+
- 不得将构建期 skill(`harness-build-*`)误写入运行期 `agent-env/skills/`。
|
|
@@ -18,12 +18,12 @@ specs/
|
|
|
18
18
|
|
|
19
19
|
- 写入任一 `specs/<domain>/*.yaml` 前,必须先读 `specs/<domain>/schema.json`。
|
|
20
20
|
- 字段增减/重命名时,`schema.json` 与对应 yaml 必须**同一提交**更新,不得分两步。
|
|
21
|
-
- yaml 中不得出现任何具体 agent 名(qoder/codechat/cursor/claude-code 等)— 违反 agent-agnostic 铁律。
|
|
21
|
+
- yaml 中不得出现任何具体 agent 名(qoder/codechat/cursor/claude-code/opencode 等)— 违反 agent-agnostic 铁律。
|
|
22
22
|
|
|
23
23
|
## 校验节点
|
|
24
24
|
|
|
25
|
-
-
|
|
26
|
-
- 后续阶段读取 specs 时不做 schema 校验(已由
|
|
25
|
+
- S50_generate_specs 退出前:所有 yaml 逐一过 schema 校验,失败则阶段状态置 FAILED。
|
|
26
|
+
- 后续阶段读取 specs 时不做 schema 校验(已由 S50 把关),但仍不得修改已冻结字段。
|
|
27
27
|
|
|
28
28
|
## 正反例
|
|
29
29
|
|