harness-engineer 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 harness-engineer contributors
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.
package/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # harness-engineer
2
+
3
+ `harness-engineer` is a Codex-first scaffolding CLI for repository-owned agent orchestration workflows.
4
+
5
+ It turns the "harness as files" pattern into a reusable npm package: fixed role definitions, durable memory, runbooks, and task lifecycle artifacts can all be generated into a blank repository with one command.
6
+
7
+ 中文说明见 [README.zh-CN.md](./README.zh-CN.md)。
8
+
9
+ > License: [MIT](./LICENSE)
10
+
11
+ ## What It Creates
12
+
13
+ - `AGENTS.md` as the short collaboration entrypoint
14
+ - `.codex/config.toml` and fixed role files under `.codex/agents/`
15
+ - durable memory under `.codex/memory/`
16
+ - `docs/index.md` plus runbooks and source-of-truth placeholders
17
+ - task lifecycle folders under `docs/plans/` and `logs/codex/`
18
+ - a machine-readable `harness-engineer.config.json`
19
+
20
+ ## Quick Start
21
+
22
+ For published package usage:
23
+
24
+ ```bash
25
+ pnpm dlx harness-engineer@latest init . \
26
+ --preset generic-software \
27
+ --project-name "Acme Platform" \
28
+ --yes
29
+
30
+ harness-engineer task new 2026-04-02-auth-debug --class B
31
+ harness-engineer status
32
+ ```
33
+
34
+ If you are running from this source repository:
35
+
36
+ ```bash
37
+ pnpm install
38
+ pnpm build
39
+
40
+ node dist/cli.js init . \
41
+ --preset generic-software \
42
+ --project-name "Acme Platform" \
43
+ --yes
44
+ ```
45
+
46
+ ## Install From npm
47
+
48
+ Without cloning this repository, users can initialize a project directly from npm:
49
+
50
+ ```bash
51
+ pnpm dlx harness-engineer@latest init . \
52
+ --preset generic-software \
53
+ --project-name "Acme Platform" \
54
+ --language bilingual \
55
+ --yes
56
+ ```
57
+
58
+ Or install it into a project first:
59
+
60
+ ```bash
61
+ npm install -D harness-engineer
62
+ npx harness-engineer init . --preset generic-software --project-name "Acme Platform"
63
+ ```
64
+
65
+ ## Presets
66
+
67
+ ### `generic-software`
68
+
69
+ The default preset for Codex-driven software repositories.
70
+
71
+ Fixed roles:
72
+
73
+ - `architect-backend`
74
+ - `architect-frontend`
75
+ - `runtime-integrations`
76
+ - `product-ui`
77
+ - `reviewer`
78
+ - `qa-guard`
79
+
80
+ Truth-source docs live under `docs/source-of-truth/`.
81
+
82
+ ### `agentadmin-codex`
83
+
84
+ A compatibility preset extracted from the AgentAdmin harness structure.
85
+
86
+ It keeps the AgentAdmin-specific document layering (`dev-docs/ + spec/`) and the original fixed role names:
87
+
88
+ - `architect-backend`
89
+ - `architect-frontend`
90
+ - `runtime-executor`
91
+ - `console-ui`
92
+ - `reviewer`
93
+ - `qa-guard`
94
+
95
+ ## CLI
96
+
97
+ ### `init`
98
+
99
+ ```bash
100
+ harness-engineer init [dir] \
101
+ --preset <preset> \
102
+ --project-name <name> \
103
+ [--language en|zh|bilingual] \
104
+ [--dev-command "<cmd>"] \
105
+ [--force] \
106
+ [--yes]
107
+ ```
108
+
109
+ Notes:
110
+
111
+ - Generates files only when missing by default.
112
+ - Use `--force` to overwrite managed templates.
113
+ - `--dev-command` generates `.codex/environments/environment.toml`.
114
+ - `init` also adds `harness-engineer` to `devDependencies`.
115
+ - `--language bilingual` keeps the base `AGENTS.md` canonical and adds a bilingual `AGENTS.override.md` plus localized core harness docs.
116
+
117
+ ### `task new`
118
+
119
+ ```bash
120
+ harness-engineer task new <slug> --class A|B|C
121
+ ```
122
+
123
+ Creates:
124
+
125
+ - `docs/plans/active/<slug>.md`
126
+ - `logs/codex/active/<slug>/run.md`
127
+ - `logs/codex/active/<slug>/handoff.md`
128
+
129
+ ### `task archive`
130
+
131
+ ```bash
132
+ harness-engineer task archive <slug>
133
+ ```
134
+
135
+ Moves the task from active to completed and appends completion sections to the plan if needed.
136
+
137
+ ### `status`
138
+
139
+ ```bash
140
+ harness-engineer status
141
+ ```
142
+
143
+ Reports:
144
+
145
+ - active tasks
146
+ - missing managed files
147
+ - drifted managed files
148
+ - inconsistent task artifacts
149
+
150
+ ## Local Development
151
+
152
+ ```bash
153
+ pnpm install
154
+ pnpm test
155
+ pnpm test:coverage
156
+ pnpm build
157
+ ```
158
+
159
+ The package is intentionally source-first. `dist/`, `coverage/`, and `node_modules/` are generated locally and are not meant to be committed.
160
+
161
+ ## Testing Strategy
162
+
163
+ - unit tests for rendering, config helpers, and preset selection
164
+ - integration tests for init, task lifecycle, CLI smoke flow, and status drift detection
165
+ - a self-contained AgentAdmin compatibility snapshot test under `tests/integration/agentadmin-golden.test.ts`
166
+
167
+ ## Repository Layout
168
+
169
+ ```text
170
+ src/ source code for the CLI and generators
171
+ tests/ unit, integration, and fixture-backed compatibility tests
172
+ ```
173
+
174
+ ## Open-Source Release Notes
175
+
176
+ Before publishing updates, review [OPEN_SOURCE_RELEASE_CHECKLIST.md](./OPEN_SOURCE_RELEASE_CHECKLIST.md).
@@ -0,0 +1,174 @@
1
+ # harness-engineer
2
+
3
+ `harness-engineer` 是一个面向 Codex 的仓库协作脚手架 CLI,用来把“harness 作为文件结构”的工作方式封装成可复用 npm 包。
4
+
5
+ 它可以在一个空白仓库里一次性生成固定角色、长期记忆、runbook、任务计划与 handoff 结构,适合团队把 AI 协作流程沉淀为仓库内真源。
6
+
7
+ > 许可证:[MIT](./LICENSE)
8
+
9
+ ## 它会生成什么
10
+
11
+ - `AGENTS.md` 作为最短协作入口
12
+ - `.codex/config.toml` 和 `.codex/agents/` 下的固定角色文件
13
+ - `.codex/memory/` 下的长期记忆
14
+ - `docs/index.md`、runbook 和真源文档占位
15
+ - `docs/plans/` 与 `logs/codex/` 下的任务闭环目录
16
+ - 机器可读配置 `harness-engineer.config.json`
17
+
18
+ ## 快速开始
19
+
20
+ 发布后推荐这样使用:
21
+
22
+ ```bash
23
+ pnpm dlx harness-engineer@latest init . \
24
+ --preset generic-software \
25
+ --project-name "Acme Platform" \
26
+ --yes
27
+
28
+ harness-engineer task new 2026-04-02-auth-debug --class B
29
+ harness-engineer status
30
+ ```
31
+
32
+ 如果你是在当前源码仓库里本地运行:
33
+
34
+ ```bash
35
+ pnpm install
36
+ pnpm build
37
+
38
+ node dist/cli.js init . \
39
+ --preset generic-software \
40
+ --project-name "Acme Platform" \
41
+ --yes
42
+ ```
43
+
44
+ ## 直接从 npm 使用
45
+
46
+ 用户不需要 clone 当前仓库,也可以直接从 npm 初始化项目:
47
+
48
+ ```bash
49
+ pnpm dlx harness-engineer@latest init . \
50
+ --preset generic-software \
51
+ --project-name "Acme Platform" \
52
+ --language bilingual \
53
+ --yes
54
+ ```
55
+
56
+ 也可以先安装到项目里:
57
+
58
+ ```bash
59
+ npm install -D harness-engineer
60
+ npx harness-engineer init . --preset generic-software --project-name "Acme Platform"
61
+ ```
62
+
63
+ ## 预设
64
+
65
+ ### `generic-software`
66
+
67
+ 默认的通用 Codex 软件仓库预设。
68
+
69
+ 固定角色:
70
+
71
+ - `architect-backend`
72
+ - `architect-frontend`
73
+ - `runtime-integrations`
74
+ - `product-ui`
75
+ - `reviewer`
76
+ - `qa-guard`
77
+
78
+ 真源文档落在 `docs/source-of-truth/`。
79
+
80
+ ### `agentadmin-codex`
81
+
82
+ 从 AgentAdmin harness 抽取出来的兼容预设。
83
+
84
+ 它保留 AgentAdmin 当前的文档分层方式(`dev-docs/ + spec/`)和固定角色命名:
85
+
86
+ - `architect-backend`
87
+ - `architect-frontend`
88
+ - `runtime-executor`
89
+ - `console-ui`
90
+ - `reviewer`
91
+ - `qa-guard`
92
+
93
+ ## CLI 命令
94
+
95
+ ### `init`
96
+
97
+ ```bash
98
+ harness-engineer init [dir] \
99
+ --preset <preset> \
100
+ --project-name <name> \
101
+ [--language en|zh|bilingual] \
102
+ [--dev-command "<cmd>"] \
103
+ [--force] \
104
+ [--yes]
105
+ ```
106
+
107
+ 说明:
108
+
109
+ - 默认只生成缺失文件,不覆盖已有模板。
110
+ - 使用 `--force` 可以覆盖受管理模板。
111
+ - 传入 `--dev-command` 时会生成 `.codex/environments/environment.toml`。
112
+ - `init` 会把 `harness-engineer` 自动加入 `devDependencies`。
113
+ - `--language bilingual` 会保留标准 `AGENTS.md`,并额外生成双语版 `AGENTS.override.md` 与本地化核心 harness 文档。
114
+
115
+ ### `task new`
116
+
117
+ ```bash
118
+ harness-engineer task new <slug> --class A|B|C
119
+ ```
120
+
121
+ 会创建:
122
+
123
+ - `docs/plans/active/<slug>.md`
124
+ - `logs/codex/active/<slug>/run.md`
125
+ - `logs/codex/active/<slug>/handoff.md`
126
+
127
+ ### `task archive`
128
+
129
+ ```bash
130
+ harness-engineer task archive <slug>
131
+ ```
132
+
133
+ 会把任务从 active 移到 completed,并在 plan 中补齐结果段落。
134
+
135
+ ### `status`
136
+
137
+ ```bash
138
+ harness-engineer status
139
+ ```
140
+
141
+ 会报告:
142
+
143
+ - 当前 active 任务
144
+ - 缺失的受管理文件
145
+ - 漂移的受管理模板
146
+ - 不一致的任务产物
147
+
148
+ ## 本地开发
149
+
150
+ ```bash
151
+ pnpm install
152
+ pnpm test
153
+ pnpm test:coverage
154
+ pnpm build
155
+ ```
156
+
157
+ 仓库以源码为主,`dist/`、`coverage/` 和 `node_modules/` 都是本地产物,不应该提交。
158
+
159
+ ## 测试策略
160
+
161
+ - 单元测试:渲染、配置、预设选择
162
+ - 集成测试:init、任务生命周期、CLI smoke、status 漂移检查
163
+ - 兼容性测试:`tests/integration/agentadmin-golden.test.ts`
164
+
165
+ ## 仓库结构
166
+
167
+ ```text
168
+ src/ CLI 与生成器源码
169
+ tests/ 单元、集成与 fixture 兼容性测试
170
+ ```
171
+
172
+ ## 开源发布说明
173
+
174
+ 准备发布更新前,请先阅读 [OPEN_SOURCE_RELEASE_CHECKLIST.md](./OPEN_SOURCE_RELEASE_CHECKLIST.md)。
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export declare function runCli(argv: string[], cwd?: string): Promise<number>;
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAQA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,SAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAkFjF"}
package/dist/cli.js ADDED
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+ import { initProject } from "./init.js";
3
+ import { readOwnPackageVersion } from "./config.js";
4
+ import { getStatus } from "./status.js";
5
+ import { archiveTask, createTask } from "./tasks.js";
6
+ export async function runCli(argv, cwd = process.cwd()) {
7
+ const [command, ...rest] = argv;
8
+ if (!command || command === "help" || command === "--help" || command === "-h") {
9
+ writeStdout(buildUsage());
10
+ return 0;
11
+ }
12
+ if (command === "init") {
13
+ const { positionals, options } = parseArgs(rest);
14
+ const targetDir = positionals[0] ?? ".";
15
+ const projectName = asRequiredString(options["project-name"], "--project-name is required.");
16
+ const preset = asString(options.preset) ?? "generic-software";
17
+ const languageOption = asString(options.language);
18
+ const language = languageOption ? parseHarnessLanguage(languageOption) : undefined;
19
+ const force = Boolean(options.force);
20
+ const devCommand = asString(options["dev-command"]);
21
+ const packageVersion = await readOwnPackageVersion(import.meta.url);
22
+ const result = await initProject({
23
+ cwd,
24
+ targetDir,
25
+ projectName,
26
+ preset,
27
+ language,
28
+ yes: Boolean(options.yes),
29
+ force,
30
+ devCommand,
31
+ packageVersion,
32
+ });
33
+ writeStdout(`Initialized ${result.targetDir}`);
34
+ writeStdout(`Created: ${result.createdFiles.length}`);
35
+ writeStdout(`Skipped: ${result.skippedFiles.length}`);
36
+ writeStdout(`Overwritten: ${result.overwrittenFiles.length}`);
37
+ return 0;
38
+ }
39
+ if (command === "task") {
40
+ const [subcommand, ...taskArgs] = rest;
41
+ if (subcommand === "new") {
42
+ const { positionals, options } = parseArgs(taskArgs);
43
+ const slug = positionals[0];
44
+ if (!slug) {
45
+ throw new Error("task new requires a <slug>.");
46
+ }
47
+ const taskClass = (asString(options.class) ?? "B");
48
+ validateTaskClass(taskClass);
49
+ await createTask({
50
+ cwd,
51
+ slug,
52
+ taskClass,
53
+ });
54
+ writeStdout(`Created task "${slug}".`);
55
+ return 0;
56
+ }
57
+ if (subcommand === "archive") {
58
+ const { positionals } = parseArgs(taskArgs);
59
+ const slug = positionals[0];
60
+ if (!slug) {
61
+ throw new Error("task archive requires a <slug>.");
62
+ }
63
+ await archiveTask({ cwd, slug });
64
+ writeStdout(`Archived task "${slug}".`);
65
+ return 0;
66
+ }
67
+ throw new Error(`Unknown task subcommand "${subcommand ?? ""}".`);
68
+ }
69
+ if (command === "status") {
70
+ const status = await getStatus(cwd);
71
+ writeStdout(`Config: ${status.configPath}`);
72
+ writeStdout(`Active tasks: ${status.activeTasks.length === 0 ? "none" : status.activeTasks.join(", ")}`);
73
+ writeStdout(`Missing managed files: ${status.missingManagedFiles.length}`);
74
+ writeStdout(`Drifted managed files: ${status.driftedManagedFiles.length}`);
75
+ writeStdout(`Inconsistent tasks: ${status.inconsistentTasks.length}`);
76
+ return 0;
77
+ }
78
+ throw new Error(`Unknown command "${command}".`);
79
+ }
80
+ function buildUsage() {
81
+ return [
82
+ "Usage:",
83
+ " harness-engineer init [dir] --preset <preset> --project-name <name> [--yes] [--force] [--dev-command <cmd>] [--language <en|zh|bilingual>]",
84
+ " harness-engineer task new <slug> --class <A|B|C>",
85
+ " harness-engineer task archive <slug>",
86
+ " harness-engineer status",
87
+ ].join("\n");
88
+ }
89
+ function parseArgs(argv) {
90
+ const positionals = [];
91
+ const options = {};
92
+ for (let index = 0; index < argv.length; index += 1) {
93
+ const token = argv[index];
94
+ if (!token) {
95
+ continue;
96
+ }
97
+ if (!token.startsWith("--")) {
98
+ positionals.push(token);
99
+ continue;
100
+ }
101
+ const optionName = token.slice(2);
102
+ const next = argv[index + 1];
103
+ if (!next || next.startsWith("--")) {
104
+ options[optionName] = true;
105
+ continue;
106
+ }
107
+ options[optionName] = next;
108
+ index += 1;
109
+ }
110
+ return { positionals, options };
111
+ }
112
+ function asString(value) {
113
+ return typeof value === "string" ? value : undefined;
114
+ }
115
+ function asRequiredString(value, errorMessage) {
116
+ const stringValue = asString(value);
117
+ if (!stringValue) {
118
+ throw new Error(errorMessage);
119
+ }
120
+ return stringValue;
121
+ }
122
+ function validateTaskClass(taskClass) {
123
+ if (!["A", "B", "C"].includes(taskClass)) {
124
+ throw new Error(`Invalid task class "${taskClass}". Use A, B, or C.`);
125
+ }
126
+ }
127
+ function parseHarnessLanguage(language) {
128
+ if (language === "en" || language === "zh" || language === "bilingual") {
129
+ return language;
130
+ }
131
+ throw new Error(`Invalid language "${language}". Use en, zh, or bilingual.`);
132
+ }
133
+ function writeStdout(message) {
134
+ process.stdout.write(`${message}\n`);
135
+ }
136
+ if (import.meta.url === `file://${process.argv[1]}`) {
137
+ runCli(process.argv.slice(2)).then((exitCode) => {
138
+ process.exitCode = exitCode;
139
+ }, (error) => {
140
+ const message = error instanceof Error ? error.message : String(error);
141
+ process.stderr.write(`${message}\n`);
142
+ process.exitCode = 1;
143
+ });
144
+ }
145
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { HarnessConfig, HarnessRoleConfig } from "./types.js";
2
+ export declare const HARNESS_CONFIG_FILE = "harness-engineer.config.json";
3
+ export interface BuildConfigOptions {
4
+ cwd: string;
5
+ targetDir?: string;
6
+ projectName: string;
7
+ presetKey: string;
8
+ language?: HarnessConfig["language"];
9
+ devCommand?: string;
10
+ }
11
+ export declare function buildHarnessConfig(options: BuildConfigOptions): HarnessConfig;
12
+ export declare function loadHarnessConfig(cwd: string): Promise<HarnessConfig>;
13
+ export declare function readOwnPackageVersion(moduleUrl: string): Promise<string>;
14
+ export declare function buildHarnessDependencyVersion(version: string): string;
15
+ export declare function createDefaultPackageJson(projectName: string): {
16
+ name: string;
17
+ version: string;
18
+ private: true;
19
+ devDependencies: Record<string, string>;
20
+ };
21
+ export declare function serializeHarnessConfig(config: HarnessConfig): string;
22
+ export declare function getRoleByKey(config: HarnessConfig, key: string): HarnessRoleConfig;
23
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAInE,eAAO,MAAM,mBAAmB,iCAAiC,CAAC;AAElE,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,GAAG,aAAa,CAwB7E;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAO3E;AAED,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS9E;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,IAAI,CAAC;IACd,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC,CAOA;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAEpE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAOlF"}
package/dist/config.js ADDED
@@ -0,0 +1,66 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
3
+ import { getPreset } from "./presets.js";
4
+ import { formatJson, pathExists, readJsonFile, sortPaths, toPackageName } from "./utils.js";
5
+ export const HARNESS_CONFIG_FILE = "harness-engineer.config.json";
6
+ export function buildHarnessConfig(options) {
7
+ const preset = getPreset(options.presetKey);
8
+ const targetDir = resolve(options.cwd, options.targetDir ?? ".");
9
+ const language = options.language ?? preset.defaultLanguage;
10
+ const roles = preset.roles.map((role) => ({ ...role }));
11
+ const truthSources = preset.truthSources.map((source) => ({ ...source }));
12
+ const config = {
13
+ projectName: options.projectName,
14
+ preset: preset.key,
15
+ language,
16
+ version: 1,
17
+ devCommand: options.devCommand ?? null,
18
+ paths: { ...preset.paths },
19
+ roles,
20
+ truthSources,
21
+ managedFiles: [],
22
+ };
23
+ config.managedFiles = sortPaths(preset.buildManagedFiles(config).map((entry) => entry.path));
24
+ void targetDir;
25
+ return config;
26
+ }
27
+ export async function loadHarnessConfig(cwd) {
28
+ const configPath = join(cwd, HARNESS_CONFIG_FILE);
29
+ if (!(await pathExists(configPath))) {
30
+ throw new Error(`Missing ${HARNESS_CONFIG_FILE} in ${cwd}. Run "harness-engineer init" first.`);
31
+ }
32
+ return readJsonFile(configPath);
33
+ }
34
+ export async function readOwnPackageVersion(moduleUrl) {
35
+ try {
36
+ const packageJsonUrl = new URL("../package.json", moduleUrl);
37
+ const contents = await readFile(packageJsonUrl, "utf8");
38
+ const parsed = JSON.parse(contents);
39
+ return parsed.version ?? "latest";
40
+ }
41
+ catch {
42
+ return "latest";
43
+ }
44
+ }
45
+ export function buildHarnessDependencyVersion(version) {
46
+ return version === "latest" ? "latest" : `^${version}`;
47
+ }
48
+ export function createDefaultPackageJson(projectName) {
49
+ return {
50
+ name: toPackageName(projectName),
51
+ version: "0.0.0",
52
+ private: true,
53
+ devDependencies: {},
54
+ };
55
+ }
56
+ export function serializeHarnessConfig(config) {
57
+ return formatJson(config);
58
+ }
59
+ export function getRoleByKey(config, key) {
60
+ const role = config.roles.find((candidate) => candidate.key === key);
61
+ if (!role) {
62
+ throw new Error(`Unknown role "${key}" in harness config.`);
63
+ }
64
+ return role;
65
+ }
66
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { runCli } from "./cli.js";
2
+ export { initProject } from "./init.js";
3
+ export { getPreset } from "./presets.js";
4
+ export { renderTemplate } from "./render.js";
5
+ export { getStatus } from "./status.js";
6
+ export { archiveTask, createTask } from "./tasks.js";
7
+ export type { HarnessConfig, HarnessLanguage, HarnessPathsConfig, HarnessRoleConfig, InitOptions, InitResult, StatusResult, TaskArchiveOptions, TaskClass, TaskNewOptions, TruthSourceConfig, } from "./types.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACrD,YAAY,EACV,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,YAAY,EACZ,kBAAkB,EAClB,SAAS,EACT,cAAc,EACd,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { runCli } from "./cli.js";
2
+ export { initProject } from "./init.js";
3
+ export { getPreset } from "./presets.js";
4
+ export { renderTemplate } from "./render.js";
5
+ export { getStatus } from "./status.js";
6
+ export { archiveTask, createTask } from "./tasks.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
package/dist/init.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { InitOptions, InitResult } from "./types.js";
2
+ export declare function initProject(options: InitOptions): Promise<InitResult>;
3
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG1D,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CA4C3E"}