svharness 0.15.13 → 0.15.14

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.
@@ -36,6 +36,7 @@ async function runAutoPackReq(positionalWorkdir, opts, cmd) {
36
36
  const workCtx = await (0, auto_pack_req_context_1.resolveAutoPackReqWorkContext)(positionalWorkdir, merged.workDir, merged.requirements);
37
37
  const runOpts = {
38
38
  ...merged,
39
+ harnessName: merged.harnessName ?? auto_pack_req_context_1.PACK_REQ_HARNESS_NAME,
39
40
  ...(workCtx.requirementsPath ? { requirements: workCtx.requirementsPath } : {}),
40
41
  };
41
42
  if (workCtx.harnessRoot) {
@@ -44,10 +45,10 @@ async function runAutoPackReq(positionalWorkdir, opts, cmd) {
44
45
  }
45
46
  else if (workCtx.mode === 'requirements-cwd') {
46
47
  logger_1.logger.info(`[auto_pack_req] 当前目录识别为 requirements 输入:${workCtx.requirementsPath}`);
47
- logger_1.logger.info(`[auto_pack_req] 将在上级目录创建 harness:${workCtx.workDir}`);
48
+ logger_1.logger.info(`[auto_pack_req] 将在当前目录创建固定 harness:${node_path_1.default.join(workCtx.workDir, auto_pack_req_context_1.PACK_REQ_HARNESS_DIR)}(等价 --requirements .)`);
48
49
  }
49
50
  else {
50
- logger_1.logger.info('[auto_pack_req] 资料包模式:将创建 harness 并执行 S30+S40');
51
+ logger_1.logger.info(`[auto_pack_req] 资料包模式:将创建 ${auto_pack_req_context_1.PACK_REQ_HARNESS_DIR} 并执行 S30+S40`);
51
52
  }
52
53
  await (0, auto_1.runAuto)(workCtx.workDir, runOpts, cmd);
53
54
  }
@@ -10,6 +10,7 @@ const node_child_process_1 = require("node:child_process");
10
10
  const config_1 = require("../config");
11
11
  const logger_1 = require("../utils/logger");
12
12
  const harness_resolver_1 = require("../lib/harness-resolver");
13
+ const auto_pack_req_context_1 = require("../lib/auto-pack-req-context");
13
14
  const auto_orchestrator_1 = require("../lib/auto-orchestrator");
14
15
  const dashboard_1 = require("../dashboard");
15
16
  const acp_agent_cli_resolver_1 = require("../lib/acp-agent-cli-resolver");
@@ -70,7 +71,7 @@ async function runAuto(positionalWorkdir, opts, cmd) {
70
71
  }
71
72
  async function resolveAutoContext(positionalWorkdir, opts) {
72
73
  const workDir = node_path_1.default.resolve(positionalWorkdir ?? opts.workDir ?? process.cwd());
73
- const harnessRoot = await (0, harness_resolver_1.resolveHarnessRoot)(workDir);
74
+ const harnessRoot = await resolveHarnessForAuto(workDir, normalizeScope(opts.scope));
74
75
  const acpAgentCli = opts.dryRun ? null : (0, acp_agent_cli_resolver_1.ensureAcpAgentCliAvailable)();
75
76
  return {
76
77
  workDir,
@@ -122,3 +123,11 @@ function normalizeBaselineExtract(value) {
122
123
  function normalizeScope(value) {
123
124
  return value === 'requirements' ? 'requirements' : 'full';
124
125
  }
126
+ async function resolveHarnessForAuto(workDir, scope) {
127
+ if (scope === 'requirements') {
128
+ if (await (0, harness_resolver_1.isHarnessRoot)(workDir))
129
+ return workDir;
130
+ return (0, auto_pack_req_context_1.resolvePackReqHarnessRoot)(workDir);
131
+ }
132
+ return (0, harness_resolver_1.resolveHarnessRoot)(workDir);
133
+ }
@@ -20,7 +20,12 @@ exports.RAW_SKIP_DIR_NAMES = new Set([
20
20
  'dist',
21
21
  ]);
22
22
  function shouldSkipRawSubdir(name) {
23
- return exports.RAW_SKIP_DIR_NAMES.has(name);
23
+ if (exports.RAW_SKIP_DIR_NAMES.has(name))
24
+ return true;
25
+ // auto_pack_req:`--requirements .` 时勿把同级的 `.requirements-harness/` 当作源资料。
26
+ if (name.endsWith('-harness'))
27
+ return true;
28
+ return false;
24
29
  }
25
30
  /**
26
31
  * Recursively list source files for seed intake, skipping harness artifact dirs.
@@ -12,6 +12,7 @@ const node_path_1 = __importDefault(require("node:path"));
12
12
  const js_yaml_1 = __importDefault(require("js-yaml"));
13
13
  const init_1 = require("../commands/init");
14
14
  const harness_name_1 = require("../utils/harness-name");
15
+ const doc_intake_paths_1 = require("../core/doc-intake-paths");
15
16
  const logger_1 = require("../utils/logger");
16
17
  async function runMaterialsPrepAndBuild(ctx) {
17
18
  const detected = await detectInputs(ctx.workDir, ctx.buildOverrides);
@@ -60,6 +61,8 @@ async function detectInputs(workDir, overrides) {
60
61
  for (const entry of entries) {
61
62
  if (!entry.isDirectory())
62
63
  continue;
64
+ if ((0, doc_intake_paths_1.shouldSkipRawSubdir)(entry.name))
65
+ continue;
63
66
  const abs = node_path_1.default.join(workDir, entry.name);
64
67
  const lower = entry.name.toLowerCase();
65
68
  const files = await listFiles(abs, 3);
@@ -239,10 +242,15 @@ function deriveHarnessName(goal, workDir, log) {
239
242
  }
240
243
  /** Strip non-ASCII, collapse internal hyphens, trim leading/trailing hyphens. */
241
244
  function sanitizeToAscii(raw) {
242
- return raw
245
+ const ascii = raw
243
246
  .toLowerCase()
244
247
  .replace(/[^a-z0-9]+/g, '-')
245
248
  .replace(/^-+|-+$/g, '');
249
+ // Goal 模板常含「构建 harness」,与 workDir 叠在一起会得到 `xxx-harness` 短名。
250
+ const core = (0, harness_name_1.stripHarnessSuffixFromCore)(ascii);
251
+ if (!core || core === 'harness')
252
+ return '';
253
+ return core;
246
254
  }
247
255
  function detectArchFromDetected(detected) {
248
256
  const baseline = detected.baseline?.path;
@@ -3,12 +3,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PACK_REQ_HARNESS_DIR = exports.PACK_REQ_HARNESS_NAME = void 0;
6
7
  exports.looksLikeMaterialsPackRoot = looksLikeMaterialsPackRoot;
7
8
  exports.looksLikeRequirementsFolder = looksLikeRequirementsFolder;
9
+ exports.resolvePackReqHarnessRoot = resolvePackReqHarnessRoot;
8
10
  exports.resolveAutoPackReqWorkContext = resolveAutoPackReqWorkContext;
9
11
  const node_path_1 = __importDefault(require("node:path"));
10
12
  const fs_extra_1 = __importDefault(require("fs-extra"));
11
13
  const harness_resolver_1 = require("./harness-resolver");
14
+ /** auto_pack_req 固定短名 → 目录 `.requirements-harness/` */
15
+ exports.PACK_REQ_HARNESS_NAME = '.requirements';
16
+ exports.PACK_REQ_HARNESS_DIR = '.requirements-harness';
12
17
  const DOC_FILE_RE = /\.(md|docx|doc|pdf|xlsx|xls|pptx|ppt|txt|csv)$/i;
13
18
  const MATERIALS_PACK_SUBDIR_RE = /^(requirements?|baseline|references?|extra[-_]?skills?|需求|接口|协议|skill)/i;
14
19
  /** 当前目录是否像资料包根(含 requirements/、baseline/ 等子目录) */
@@ -46,15 +51,33 @@ async function looksLikeRequirementsFolder(dir) {
46
51
  }
47
52
  return false;
48
53
  }
54
+ /** 仅识别 workDir 下固定的 `.requirements-harness/`,不扫描其它 `*-harness/`。 */
55
+ async function resolvePackReqHarnessRoot(workDir) {
56
+ const candidate = node_path_1.default.join(node_path_1.default.resolve(workDir), exports.PACK_REQ_HARNESS_DIR);
57
+ return (await (0, harness_resolver_1.isHarnessRoot)(candidate)) ? candidate : null;
58
+ }
49
59
  /**
50
60
  * 解析 auto_pack_req 工作上下文。
51
- * 无显式 workdir 时:若当前目录本身是 requirements 资料夹,则以其父目录为 workDir 并固定 requirements 输入。
61
+ * 无显式 workdir 时:若当前目录本身是 requirements 资料夹,则 workDir 保持为当前目录,
62
+ * requirements 输入等价于 `--requirements .`,harness 固定为 `./.requirements-harness/`。
52
63
  */
53
64
  async function resolveAutoPackReqWorkContext(positionalWorkdir, configWorkDir, requirementsOverride) {
54
65
  const explicit = positionalWorkdir ?? configWorkDir;
55
66
  if (explicit) {
56
67
  const workDir = node_path_1.default.resolve(explicit);
57
- const harnessRoot = await (0, harness_resolver_1.resolveHarnessRoot)(workDir);
68
+ if (await (0, harness_resolver_1.isHarnessRoot)(workDir)) {
69
+ return {
70
+ workDir,
71
+ harnessRoot: workDir,
72
+ requirementsPath: requirementsOverride
73
+ ? node_path_1.default.isAbsolute(requirementsOverride)
74
+ ? requirementsOverride
75
+ : node_path_1.default.resolve(workDir, requirementsOverride)
76
+ : undefined,
77
+ mode: 'harness-rerun',
78
+ };
79
+ }
80
+ const harnessRoot = await resolvePackReqHarnessRoot(workDir);
58
81
  return {
59
82
  workDir,
60
83
  harnessRoot,
@@ -67,22 +90,26 @@ async function resolveAutoPackReqWorkContext(positionalWorkdir, configWorkDir, r
67
90
  };
68
91
  }
69
92
  const cwd = process.cwd();
70
- const harnessFromCwd = await (0, harness_resolver_1.resolveHarnessRoot)(cwd);
71
- if (harnessFromCwd) {
72
- return { workDir: cwd, harnessRoot: harnessFromCwd, mode: 'harness-rerun' };
93
+ if (await (0, harness_resolver_1.isHarnessRoot)(cwd)) {
94
+ return { workDir: cwd, harnessRoot: cwd, mode: 'harness-rerun' };
73
95
  }
74
96
  if (await looksLikeMaterialsPackRoot(cwd)) {
75
- return { workDir: cwd, harnessRoot: null, mode: 'materials-pack' };
97
+ const harnessRoot = await resolvePackReqHarnessRoot(cwd);
98
+ return { workDir: cwd, harnessRoot, mode: harnessRoot ? 'harness-rerun' : 'materials-pack' };
76
99
  }
77
100
  if (await looksLikeRequirementsFolder(cwd)) {
78
- const parent = node_path_1.default.dirname(cwd);
79
- const harnessFromParent = await (0, harness_resolver_1.resolveHarnessRoot)(parent);
101
+ const harnessRoot = await resolvePackReqHarnessRoot(cwd);
80
102
  return {
81
- workDir: parent,
82
- harnessRoot: harnessFromParent,
103
+ workDir: cwd,
104
+ harnessRoot,
83
105
  requirementsPath: node_path_1.default.resolve(cwd),
84
- mode: harnessFromParent ? 'harness-rerun' : 'requirements-cwd',
106
+ mode: harnessRoot ? 'harness-rerun' : 'requirements-cwd',
85
107
  };
86
108
  }
87
- return { workDir: cwd, harnessRoot: null, mode: 'materials-pack' };
109
+ const harnessRoot = await resolvePackReqHarnessRoot(cwd);
110
+ return {
111
+ workDir: cwd,
112
+ harnessRoot,
113
+ mode: harnessRoot ? 'harness-rerun' : 'materials-pack',
114
+ };
88
115
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.normalizeHarnessName = normalizeHarnessName;
4
4
  exports.harnessNameCore = harnessNameCore;
5
+ exports.stripHarnessSuffixFromCore = stripHarnessSuffixFromCore;
5
6
  exports.validateHarnessNameInput = validateHarnessNameInput;
6
7
  exports.harnessDirNameFromName = harnessDirNameFromName;
7
8
  /**
@@ -22,6 +23,17 @@ function harnessNameCore(raw) {
22
23
  const trimmed = raw.trim();
23
24
  return trimmed.startsWith('.') ? trimmed.slice(1) : trimmed;
24
25
  }
26
+ /**
27
+ * Strip redundant `-harness` suffix(es) from an inferred name core.
28
+ * e.g. `requirements-harness` → `requirements`, `foo-harness-harness` → `foo`.
29
+ */
30
+ function stripHarnessSuffixFromCore(core) {
31
+ let result = core;
32
+ while (result.endsWith('-harness')) {
33
+ result = result.slice(0, -'-harness'.length);
34
+ }
35
+ return result;
36
+ }
25
37
  function validateHarnessNameInput(raw) {
26
38
  if (!raw?.trim()) {
27
39
  return { ok: false, message: '--harness-name 为必填参数' };
@@ -37,5 +49,11 @@ function validateHarnessNameInput(raw) {
37
49
  return { ok: true, normalized: normalizeHarnessName(raw) };
38
50
  }
39
51
  function harnessDirNameFromName(harnessName) {
40
- return `${normalizeHarnessName(harnessName)}-harness`;
52
+ const normalized = normalizeHarnessName(harnessName);
53
+ const core = harnessNameCore(normalized);
54
+ // Avoid `.foo-harness-harness` when the short name already ends with `-harness`.
55
+ if (core.endsWith('-harness')) {
56
+ return normalized;
57
+ }
58
+ return `${normalized}-harness`;
41
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svharness",
3
- "version": "0.15.13",
3
+ "version": "0.15.14",
4
4
  "description": "CLI scaffolder for SDD-Driven Agent-Agnostic Coding Framework (harness)",
5
5
  "bin": {
6
6
  "svharness": "./bin/cli.js",
@@ -27,7 +27,7 @@
27
27
  "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
28
28
  "rebuild": "npm run clean && npm run build",
29
29
  "test:requirements": "npm run build && node scripts/test-parse-blocks.js",
30
- "test:auto": "npm run build && node scripts/test-auto-score.js && node scripts/test-auto-prompt.js && node scripts/test-acp-agent-cli-resolver.js && node scripts/test-auto-pack-req-context.js",
30
+ "test:auto": "npm run build && node scripts/test-harness-name.js && node scripts/test-doc-intake-paths.js && node scripts/test-auto-score.js && node scripts/test-auto-prompt.js && node scripts/test-acp-agent-cli-resolver.js && node scripts/test-auto-pack-req-context.js",
31
31
  "test:cli-declarations": "npm run build && node scripts/test-cli-input-declarations.js",
32
32
  "test:agent-launcher": "npm run build && node scripts/test-agent-launcher.js",
33
33
  "test:acp-agent-cli": "npm run build && node scripts/test-acp-agent-cli-resolver.js",