@harness-engineering/cli 1.2.0 → 1.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.
- package/dist/bin/harness.js +1 -1
- package/dist/{chunk-IXT3KLVN.js → chunk-APYEWOCR.js} +355 -19
- package/dist/index.js +1 -1
- package/package.json +6 -4
- package/dist/agents/commands/claude-code/harness/add-component.md +0 -34
- package/dist/agents/commands/claude-code/harness/align-documentation.md +0 -33
- package/dist/agents/commands/claude-code/harness/architecture-advisor.md +0 -41
- package/dist/agents/commands/claude-code/harness/brainstorming.md +0 -42
- package/dist/agents/commands/claude-code/harness/check-mechanical-constraints.md +0 -32
- package/dist/agents/commands/claude-code/harness/cleanup-dead-code.md +0 -33
- package/dist/agents/commands/claude-code/harness/code-review.md +0 -33
- package/dist/agents/commands/claude-code/harness/debugging.md +0 -43
- package/dist/agents/commands/claude-code/harness/detect-doc-drift.md +0 -32
- package/dist/agents/commands/claude-code/harness/diagnostics.md +0 -43
- package/dist/agents/commands/claude-code/harness/enforce-architecture.md +0 -32
- package/dist/agents/commands/claude-code/harness/execution.md +0 -43
- package/dist/agents/commands/claude-code/harness/git-workflow.md +0 -32
- package/dist/agents/commands/claude-code/harness/initialize-project.md +0 -33
- package/dist/agents/commands/claude-code/harness/onboarding.md +0 -32
- package/dist/agents/commands/claude-code/harness/parallel-agents.md +0 -35
- package/dist/agents/commands/claude-code/harness/planning.md +0 -41
- package/dist/agents/commands/claude-code/harness/pre-commit-review.md +0 -38
- package/dist/agents/commands/claude-code/harness/refactoring.md +0 -35
- package/dist/agents/commands/claude-code/harness/skill-authoring.md +0 -35
- package/dist/agents/commands/claude-code/harness/state-management.md +0 -35
- package/dist/agents/commands/claude-code/harness/tdd.md +0 -42
- package/dist/agents/commands/claude-code/harness/validate-context-engineering.md +0 -32
- package/dist/agents/commands/claude-code/harness/verification.md +0 -38
- package/dist/agents/commands/gemini-cli/harness/add-component.toml +0 -240
- package/dist/agents/commands/gemini-cli/harness/align-documentation.toml +0 -238
- package/dist/agents/commands/gemini-cli/harness/architecture-advisor.toml +0 -469
- package/dist/agents/commands/gemini-cli/harness/brainstorming.toml +0 -326
- package/dist/agents/commands/gemini-cli/harness/check-mechanical-constraints.toml +0 -249
- package/dist/agents/commands/gemini-cli/harness/cleanup-dead-code.toml +0 -258
- package/dist/agents/commands/gemini-cli/harness/code-review.toml +0 -461
- package/dist/agents/commands/gemini-cli/harness/debugging.toml +0 -436
- package/dist/agents/commands/gemini-cli/harness/detect-doc-drift.toml +0 -215
- package/dist/agents/commands/gemini-cli/harness/diagnostics.toml +0 -401
- package/dist/agents/commands/gemini-cli/harness/enforce-architecture.toml +0 -222
- package/dist/agents/commands/gemini-cli/harness/execution.toml +0 -381
- package/dist/agents/commands/gemini-cli/harness/git-workflow.toml +0 -325
- package/dist/agents/commands/gemini-cli/harness/initialize-project.toml +0 -257
- package/dist/agents/commands/gemini-cli/harness/onboarding.toml +0 -316
- package/dist/agents/commands/gemini-cli/harness/parallel-agents.toml +0 -221
- package/dist/agents/commands/gemini-cli/harness/planning.toml +0 -405
- package/dist/agents/commands/gemini-cli/harness/pre-commit-review.toml +0 -294
- package/dist/agents/commands/gemini-cli/harness/refactoring.toml +0 -209
- package/dist/agents/commands/gemini-cli/harness/skill-authoring.toml +0 -350
- package/dist/agents/commands/gemini-cli/harness/state-management.toml +0 -354
- package/dist/agents/commands/gemini-cli/harness/tdd.toml +0 -247
- package/dist/agents/commands/gemini-cli/harness/validate-context-engineering.toml +0 -186
- package/dist/agents/commands/gemini-cli/harness/verification.toml +0 -334
package/dist/bin/harness.js
CHANGED
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
} from "./chunk-EFZOLZFB.js";
|
|
9
9
|
|
|
10
10
|
// src/index.ts
|
|
11
|
-
import { Command as
|
|
12
|
-
import { VERSION } from "@harness-engineering/core";
|
|
11
|
+
import { Command as Command33 } from "commander";
|
|
12
|
+
import { VERSION as VERSION2 } from "@harness-engineering/core";
|
|
13
13
|
|
|
14
14
|
// src/commands/validate.ts
|
|
15
15
|
import { Command } from "commander";
|
|
@@ -1387,8 +1387,8 @@ async function runPersona(persona, executor) {
|
|
|
1387
1387
|
const result = await Promise.race([
|
|
1388
1388
|
executor(command),
|
|
1389
1389
|
new Promise(
|
|
1390
|
-
(
|
|
1391
|
-
() =>
|
|
1390
|
+
(resolve14) => setTimeout(
|
|
1391
|
+
() => resolve14({ ok: false, error: new Error(TIMEOUT_ERROR_MESSAGE) }),
|
|
1392
1392
|
remainingTime
|
|
1393
1393
|
)
|
|
1394
1394
|
)
|
|
@@ -2560,8 +2560,8 @@ function createResetCommand() {
|
|
|
2560
2560
|
}
|
|
2561
2561
|
if (!opts.yes) {
|
|
2562
2562
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
2563
|
-
const answer = await new Promise((
|
|
2564
|
-
rl.question("Reset project state? This cannot be undone. [y/N] ",
|
|
2563
|
+
const answer = await new Promise((resolve14) => {
|
|
2564
|
+
rl.question("Reset project state? This cannot be undone. [y/N] ", resolve14);
|
|
2565
2565
|
});
|
|
2566
2566
|
rl.close();
|
|
2567
2567
|
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
|
|
@@ -2860,8 +2860,7 @@ function buildArgumentHint(args) {
|
|
|
2860
2860
|
|
|
2861
2861
|
// src/slash-commands/render-claude-code.ts
|
|
2862
2862
|
function renderClaudeCode(spec) {
|
|
2863
|
-
const lines = [
|
|
2864
|
-
lines.push("---");
|
|
2863
|
+
const lines = ["---"];
|
|
2865
2864
|
lines.push(`name: ${spec.fullName}`);
|
|
2866
2865
|
lines.push(`description: ${spec.description}`);
|
|
2867
2866
|
const hint = buildArgumentHint(spec.args);
|
|
@@ -2876,6 +2875,8 @@ function renderClaudeCode(spec) {
|
|
|
2876
2875
|
}
|
|
2877
2876
|
lines.push("---");
|
|
2878
2877
|
lines.push("");
|
|
2878
|
+
lines.push(GENERATED_HEADER_CLAUDE);
|
|
2879
|
+
lines.push("");
|
|
2879
2880
|
lines.push("<context>");
|
|
2880
2881
|
lines.push(spec.prompt.context);
|
|
2881
2882
|
lines.push("</context>");
|
|
@@ -2898,14 +2899,14 @@ function renderClaudeCode(spec) {
|
|
|
2898
2899
|
}
|
|
2899
2900
|
|
|
2900
2901
|
// src/slash-commands/render-gemini.ts
|
|
2901
|
-
function
|
|
2902
|
-
return content.replace(/
|
|
2902
|
+
function escapeTomlLiteral(content) {
|
|
2903
|
+
return content.replace(/'''/g, "''\\'''");
|
|
2903
2904
|
}
|
|
2904
2905
|
function renderGemini(spec, skillMdContent, skillYamlContent) {
|
|
2905
2906
|
const lines = [GENERATED_HEADER_GEMINI];
|
|
2906
2907
|
const safeDesc = spec.description.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
2907
2908
|
lines.push(`description = "${safeDesc}"`);
|
|
2908
|
-
lines.push(
|
|
2909
|
+
lines.push("prompt = '''");
|
|
2909
2910
|
lines.push("<context>");
|
|
2910
2911
|
lines.push(spec.prompt.context);
|
|
2911
2912
|
lines.push("</context>");
|
|
@@ -2919,14 +2920,14 @@ function renderGemini(spec, skillMdContent, skillYamlContent) {
|
|
|
2919
2920
|
if (skillMdContent) {
|
|
2920
2921
|
const mdPath = spec.prompt.executionContext.split("\n")[0]?.replace(/^@/, "") ?? "";
|
|
2921
2922
|
lines.push(`--- SKILL.md (${mdPath}) ---`);
|
|
2922
|
-
lines.push(
|
|
2923
|
+
lines.push(escapeTomlLiteral(skillMdContent));
|
|
2923
2924
|
lines.push("");
|
|
2924
2925
|
}
|
|
2925
2926
|
if (skillYamlContent) {
|
|
2926
2927
|
const refs = spec.prompt.executionContext.split("\n");
|
|
2927
2928
|
const yamlPath = (refs[1] ?? refs[0] ?? "").replace(/^@/, "");
|
|
2928
2929
|
lines.push(`--- skill.yaml (${yamlPath}) ---`);
|
|
2929
|
-
lines.push(
|
|
2930
|
+
lines.push(escapeTomlLiteral(skillYamlContent));
|
|
2930
2931
|
}
|
|
2931
2932
|
lines.push("</execution_context>");
|
|
2932
2933
|
lines.push("");
|
|
@@ -2938,7 +2939,7 @@ function renderGemini(spec, skillMdContent, skillYamlContent) {
|
|
|
2938
2939
|
lines.push("<process>");
|
|
2939
2940
|
lines.push(geminiProcess);
|
|
2940
2941
|
lines.push("</process>");
|
|
2941
|
-
lines.push('"
|
|
2942
|
+
lines.push("'''");
|
|
2942
2943
|
lines.push("");
|
|
2943
2944
|
return lines.join("\n");
|
|
2944
2945
|
}
|
|
@@ -2972,7 +2973,7 @@ function computeSyncPlan(outputDir, rendered) {
|
|
|
2972
2973
|
for (const filename of existing) {
|
|
2973
2974
|
if (rendered.has(filename)) continue;
|
|
2974
2975
|
const content = fs16.readFileSync(path24.join(outputDir, filename), "utf-8");
|
|
2975
|
-
if (content.
|
|
2976
|
+
if (content.includes(GENERATED_HEADER_CLAUDE) || content.includes(GENERATED_HEADER_GEMINI)) {
|
|
2976
2977
|
removed.push(filename);
|
|
2977
2978
|
}
|
|
2978
2979
|
}
|
|
@@ -3013,11 +3014,11 @@ function fileExtension(platform) {
|
|
|
3013
3014
|
}
|
|
3014
3015
|
async function confirmDeletion(files) {
|
|
3015
3016
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
3016
|
-
return new Promise((
|
|
3017
|
+
return new Promise((resolve14) => {
|
|
3017
3018
|
rl.question(`
|
|
3018
3019
|
Remove ${files.length} orphaned command(s)? (y/N) `, (answer) => {
|
|
3019
3020
|
rl.close();
|
|
3020
|
-
|
|
3021
|
+
resolve14(answer.toLowerCase() === "y");
|
|
3021
3022
|
});
|
|
3022
3023
|
});
|
|
3023
3024
|
}
|
|
@@ -3139,10 +3140,343 @@ ${result.platform} \u2192 ${result.outputDir}`);
|
|
|
3139
3140
|
});
|
|
3140
3141
|
}
|
|
3141
3142
|
|
|
3143
|
+
// src/commands/ci/index.ts
|
|
3144
|
+
import { Command as Command31 } from "commander";
|
|
3145
|
+
|
|
3146
|
+
// src/commands/ci/check.ts
|
|
3147
|
+
import { Command as Command29 } from "commander";
|
|
3148
|
+
import { runCIChecks } from "@harness-engineering/core";
|
|
3149
|
+
var VALID_CHECKS = ["validate", "deps", "docs", "entropy", "phase-gate"];
|
|
3150
|
+
async function runCICheck(options) {
|
|
3151
|
+
const configResult = resolveConfig(options.configPath);
|
|
3152
|
+
if (!configResult.ok) {
|
|
3153
|
+
return configResult;
|
|
3154
|
+
}
|
|
3155
|
+
const input = {
|
|
3156
|
+
projectRoot: process.cwd(),
|
|
3157
|
+
config: configResult.value
|
|
3158
|
+
};
|
|
3159
|
+
if (options.skip) input.skip = options.skip;
|
|
3160
|
+
if (options.failOn) input.failOn = options.failOn;
|
|
3161
|
+
const result = await runCIChecks(input);
|
|
3162
|
+
if (!result.ok) {
|
|
3163
|
+
return {
|
|
3164
|
+
ok: false,
|
|
3165
|
+
error: new CLIError(result.error.message, ExitCode.ERROR)
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
return { ok: true, value: result.value };
|
|
3169
|
+
}
|
|
3170
|
+
function parseSkip(skip) {
|
|
3171
|
+
if (!skip) return [];
|
|
3172
|
+
return skip.split(",").map((s) => s.trim()).filter((s) => VALID_CHECKS.includes(s));
|
|
3173
|
+
}
|
|
3174
|
+
function parseFailOn(failOn) {
|
|
3175
|
+
if (failOn === "warning") return "warning";
|
|
3176
|
+
return "error";
|
|
3177
|
+
}
|
|
3178
|
+
function createCheckCommand() {
|
|
3179
|
+
return new Command29("check").description("Run all harness checks for CI (validate, deps, docs, entropy, phase-gate)").option("--skip <checks>", "Comma-separated checks to skip (e.g., entropy,docs)").option("--fail-on <severity>", "Fail on severity level: error (default) or warning", "error").action(async (opts, cmd) => {
|
|
3180
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
3181
|
+
const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
|
|
3182
|
+
const skip = parseSkip(opts.skip);
|
|
3183
|
+
const failOn = parseFailOn(opts.failOn);
|
|
3184
|
+
const result = await runCICheck({
|
|
3185
|
+
configPath: globalOpts.config,
|
|
3186
|
+
skip,
|
|
3187
|
+
failOn
|
|
3188
|
+
});
|
|
3189
|
+
if (!result.ok) {
|
|
3190
|
+
if (mode === OutputMode.JSON) {
|
|
3191
|
+
console.log(JSON.stringify({ error: result.error.message }));
|
|
3192
|
+
} else {
|
|
3193
|
+
logger.error(result.error.message);
|
|
3194
|
+
}
|
|
3195
|
+
process.exit(ExitCode.ERROR);
|
|
3196
|
+
}
|
|
3197
|
+
const report = result.value;
|
|
3198
|
+
if (mode === OutputMode.JSON) {
|
|
3199
|
+
console.log(JSON.stringify(report, null, 2));
|
|
3200
|
+
} else if (mode !== OutputMode.QUIET) {
|
|
3201
|
+
for (const check of report.checks) {
|
|
3202
|
+
const logFn = check.status === "pass" ? logger.success : check.status === "fail" ? logger.error : check.status === "warn" ? logger.warn : logger.dim;
|
|
3203
|
+
logFn(`${check.name}: ${check.status} (${check.durationMs}ms)`);
|
|
3204
|
+
for (const issue of check.issues) {
|
|
3205
|
+
const prefix = issue.severity === "error" ? " x" : " !";
|
|
3206
|
+
console.log(`${prefix} ${issue.message}${issue.file ? ` (${issue.file})` : ""}`);
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
console.log("");
|
|
3210
|
+
if (report.exitCode === 0) {
|
|
3211
|
+
logger.success(`All checks passed (${report.summary.passed}/${report.summary.total})`);
|
|
3212
|
+
} else {
|
|
3213
|
+
logger.error(
|
|
3214
|
+
`${report.summary.failed} failed, ${report.summary.warnings} warnings, ${report.summary.passed} passed`
|
|
3215
|
+
);
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
process.exit(report.exitCode);
|
|
3219
|
+
});
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
// src/commands/ci/init.ts
|
|
3223
|
+
import { Command as Command30 } from "commander";
|
|
3224
|
+
import * as fs18 from "fs";
|
|
3225
|
+
import * as path26 from "path";
|
|
3226
|
+
import { Ok as Ok17, Err as Err14 } from "@harness-engineering/core";
|
|
3227
|
+
var ALL_CHECKS = ["validate", "deps", "docs", "entropy", "phase-gate"];
|
|
3228
|
+
function buildSkipFlag(checks) {
|
|
3229
|
+
if (!checks) return "";
|
|
3230
|
+
const skipChecks = ALL_CHECKS.filter((c) => !checks.includes(c));
|
|
3231
|
+
if (skipChecks.length === 0) return "";
|
|
3232
|
+
return ` --skip ${skipChecks.join(",")}`;
|
|
3233
|
+
}
|
|
3234
|
+
function generateGitHubActions(skipFlag) {
|
|
3235
|
+
return `name: Harness Checks
|
|
3236
|
+
|
|
3237
|
+
on:
|
|
3238
|
+
push:
|
|
3239
|
+
branches: [main]
|
|
3240
|
+
pull_request:
|
|
3241
|
+
branches: [main]
|
|
3242
|
+
|
|
3243
|
+
concurrency:
|
|
3244
|
+
group: harness-\${{ github.ref }}
|
|
3245
|
+
cancel-in-progress: true
|
|
3246
|
+
|
|
3247
|
+
jobs:
|
|
3248
|
+
harness:
|
|
3249
|
+
runs-on: ubuntu-latest
|
|
3250
|
+
steps:
|
|
3251
|
+
- uses: actions/checkout@v4
|
|
3252
|
+
- uses: actions/setup-node@v4
|
|
3253
|
+
with:
|
|
3254
|
+
node-version: '22'
|
|
3255
|
+
- run: npm install -g @harness-engineering/cli
|
|
3256
|
+
- name: Run harness checks
|
|
3257
|
+
run: harness ci check --json${skipFlag}
|
|
3258
|
+
`;
|
|
3259
|
+
}
|
|
3260
|
+
function generateGitLabCI(skipFlag) {
|
|
3261
|
+
return `harness:
|
|
3262
|
+
stage: test
|
|
3263
|
+
image: node:22
|
|
3264
|
+
before_script:
|
|
3265
|
+
- npm install -g @harness-engineering/cli
|
|
3266
|
+
script:
|
|
3267
|
+
- harness ci check --json${skipFlag}
|
|
3268
|
+
rules:
|
|
3269
|
+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
3270
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
|
3271
|
+
`;
|
|
3272
|
+
}
|
|
3273
|
+
function generateGenericScript(skipFlag) {
|
|
3274
|
+
return `#!/usr/bin/env bash
|
|
3275
|
+
set -euo pipefail
|
|
3276
|
+
|
|
3277
|
+
# Harness CI Check Script
|
|
3278
|
+
# Generated by: harness ci init --platform generic
|
|
3279
|
+
|
|
3280
|
+
if ! command -v harness &> /dev/null; then
|
|
3281
|
+
echo "Installing @harness-engineering/cli..."
|
|
3282
|
+
npm install -g @harness-engineering/cli
|
|
3283
|
+
fi
|
|
3284
|
+
|
|
3285
|
+
echo "Running harness checks..."
|
|
3286
|
+
harness ci check --json${skipFlag}
|
|
3287
|
+
EXIT_CODE=$?
|
|
3288
|
+
|
|
3289
|
+
if [ $EXIT_CODE -eq 0 ]; then
|
|
3290
|
+
echo "All harness checks passed."
|
|
3291
|
+
elif [ $EXIT_CODE -eq 1 ]; then
|
|
3292
|
+
echo "Harness checks failed. See report above."
|
|
3293
|
+
else
|
|
3294
|
+
echo "Harness internal error."
|
|
3295
|
+
fi
|
|
3296
|
+
|
|
3297
|
+
exit $EXIT_CODE
|
|
3298
|
+
`;
|
|
3299
|
+
}
|
|
3300
|
+
function generateCIConfig(options) {
|
|
3301
|
+
const { platform, checks } = options;
|
|
3302
|
+
const skipFlag = buildSkipFlag(checks);
|
|
3303
|
+
const generators = {
|
|
3304
|
+
github: { filename: ".github/workflows/harness.yml", generate: generateGitHubActions },
|
|
3305
|
+
gitlab: { filename: ".gitlab-ci-harness.yml", generate: generateGitLabCI },
|
|
3306
|
+
generic: { filename: "harness-ci.sh", generate: generateGenericScript }
|
|
3307
|
+
};
|
|
3308
|
+
const entry = generators[platform];
|
|
3309
|
+
if (!entry) {
|
|
3310
|
+
return Err14(new CLIError(`Unknown platform: ${platform}`, ExitCode.ERROR));
|
|
3311
|
+
}
|
|
3312
|
+
return Ok17({
|
|
3313
|
+
filename: entry.filename,
|
|
3314
|
+
content: entry.generate(skipFlag)
|
|
3315
|
+
});
|
|
3316
|
+
}
|
|
3317
|
+
function detectPlatform() {
|
|
3318
|
+
if (fs18.existsSync(".github")) return "github";
|
|
3319
|
+
if (fs18.existsSync(".gitlab-ci.yml")) return "gitlab";
|
|
3320
|
+
return null;
|
|
3321
|
+
}
|
|
3322
|
+
function createInitCommand2() {
|
|
3323
|
+
return new Command30("init").description("Generate CI configuration for harness checks").option("--platform <platform>", "CI platform: github, gitlab, or generic").option("--checks <list>", "Comma-separated list of checks to include").action(async (opts, cmd) => {
|
|
3324
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
3325
|
+
const platform = opts.platform ?? detectPlatform() ?? "generic";
|
|
3326
|
+
const checks = opts.checks ? opts.checks.split(",").map((s) => s.trim()) : void 0;
|
|
3327
|
+
const opts2 = { platform };
|
|
3328
|
+
if (checks) opts2.checks = checks;
|
|
3329
|
+
const result = generateCIConfig(opts2);
|
|
3330
|
+
if (!result.ok) {
|
|
3331
|
+
logger.error(result.error.message);
|
|
3332
|
+
process.exit(result.error.exitCode);
|
|
3333
|
+
}
|
|
3334
|
+
const { filename, content } = result.value;
|
|
3335
|
+
const targetPath = path26.resolve(filename);
|
|
3336
|
+
const dir = path26.dirname(targetPath);
|
|
3337
|
+
fs18.mkdirSync(dir, { recursive: true });
|
|
3338
|
+
fs18.writeFileSync(targetPath, content);
|
|
3339
|
+
if (platform === "generic") {
|
|
3340
|
+
fs18.chmodSync(targetPath, "755");
|
|
3341
|
+
}
|
|
3342
|
+
if (globalOpts.json) {
|
|
3343
|
+
console.log(JSON.stringify({ file: filename, platform }));
|
|
3344
|
+
} else {
|
|
3345
|
+
logger.success(`Generated ${filename} for ${platform}`);
|
|
3346
|
+
logger.dim("Run 'harness ci check' to test locally");
|
|
3347
|
+
}
|
|
3348
|
+
});
|
|
3349
|
+
}
|
|
3350
|
+
|
|
3351
|
+
// src/commands/ci/index.ts
|
|
3352
|
+
function createCICommand() {
|
|
3353
|
+
const command = new Command31("ci").description("CI/CD integration commands");
|
|
3354
|
+
command.addCommand(createCheckCommand());
|
|
3355
|
+
command.addCommand(createInitCommand2());
|
|
3356
|
+
return command;
|
|
3357
|
+
}
|
|
3358
|
+
|
|
3359
|
+
// src/commands/update.ts
|
|
3360
|
+
import { Command as Command32 } from "commander";
|
|
3361
|
+
import { execSync as execSync3 } from "child_process";
|
|
3362
|
+
import { realpathSync } from "fs";
|
|
3363
|
+
import readline3 from "readline";
|
|
3364
|
+
import chalk4 from "chalk";
|
|
3365
|
+
import { VERSION } from "@harness-engineering/core";
|
|
3366
|
+
function detectPackageManager() {
|
|
3367
|
+
try {
|
|
3368
|
+
const argv1 = process.argv[1];
|
|
3369
|
+
if (!argv1) return "npm";
|
|
3370
|
+
const binPath = realpathSync(argv1);
|
|
3371
|
+
if (binPath.includes("pnpm/global/") || binPath.includes("pnpm-global/")) {
|
|
3372
|
+
return "pnpm";
|
|
3373
|
+
}
|
|
3374
|
+
if (binPath.includes(".yarn/")) {
|
|
3375
|
+
return "yarn";
|
|
3376
|
+
}
|
|
3377
|
+
} catch {
|
|
3378
|
+
}
|
|
3379
|
+
return "npm";
|
|
3380
|
+
}
|
|
3381
|
+
function getLatestVersion() {
|
|
3382
|
+
const output = execSync3("npm view @harness-engineering/cli dist-tags.latest", {
|
|
3383
|
+
encoding: "utf-8",
|
|
3384
|
+
timeout: 15e3
|
|
3385
|
+
});
|
|
3386
|
+
return output.trim();
|
|
3387
|
+
}
|
|
3388
|
+
function getInstalledPackages(pm) {
|
|
3389
|
+
try {
|
|
3390
|
+
const output = execSync3(`${pm} list -g --json`, {
|
|
3391
|
+
encoding: "utf-8",
|
|
3392
|
+
timeout: 15e3
|
|
3393
|
+
});
|
|
3394
|
+
const data = JSON.parse(output);
|
|
3395
|
+
const deps = data.dependencies ?? {};
|
|
3396
|
+
return Object.keys(deps).filter((name) => name.startsWith("@harness-engineering/"));
|
|
3397
|
+
} catch {
|
|
3398
|
+
return ["@harness-engineering/cli", "@harness-engineering/core"];
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
function prompt(question) {
|
|
3402
|
+
const rl = readline3.createInterface({
|
|
3403
|
+
input: process.stdin,
|
|
3404
|
+
output: process.stdout
|
|
3405
|
+
});
|
|
3406
|
+
return new Promise((resolve14) => {
|
|
3407
|
+
rl.question(question, (answer) => {
|
|
3408
|
+
rl.close();
|
|
3409
|
+
resolve14(answer.trim().toLowerCase());
|
|
3410
|
+
});
|
|
3411
|
+
});
|
|
3412
|
+
}
|
|
3413
|
+
function createUpdateCommand() {
|
|
3414
|
+
return new Command32("update").description("Update all @harness-engineering packages to the latest version").option("--version <semver>", "Install a specific version instead of latest").action(async (opts, cmd) => {
|
|
3415
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
3416
|
+
const pm = detectPackageManager();
|
|
3417
|
+
if (globalOpts.verbose) {
|
|
3418
|
+
logger.info(`Detected package manager: ${pm}`);
|
|
3419
|
+
}
|
|
3420
|
+
let targetVersion;
|
|
3421
|
+
if (opts.version) {
|
|
3422
|
+
targetVersion = opts.version;
|
|
3423
|
+
} else {
|
|
3424
|
+
logger.info("Checking for updates...");
|
|
3425
|
+
try {
|
|
3426
|
+
targetVersion = getLatestVersion();
|
|
3427
|
+
} catch {
|
|
3428
|
+
logger.error("Failed to fetch latest version from npm registry");
|
|
3429
|
+
return process.exit(ExitCode.ERROR);
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
if (VERSION === targetVersion) {
|
|
3433
|
+
logger.success(`Already up to date (v${VERSION})`);
|
|
3434
|
+
process.exit(ExitCode.SUCCESS);
|
|
3435
|
+
}
|
|
3436
|
+
console.log("");
|
|
3437
|
+
logger.info(`Current version: ${chalk4.dim(`v${VERSION}`)}`);
|
|
3438
|
+
logger.info(`Target version: ${chalk4.green(`v${targetVersion}`)}`);
|
|
3439
|
+
console.log("");
|
|
3440
|
+
const packages = getInstalledPackages(pm);
|
|
3441
|
+
if (globalOpts.verbose) {
|
|
3442
|
+
logger.info(`Installed packages: ${packages.join(", ")}`);
|
|
3443
|
+
}
|
|
3444
|
+
const installArgs = packages.map((pkg) => `${pkg}@${targetVersion}`).join(" ");
|
|
3445
|
+
const installCmd = `${pm} install -g ${installArgs}`;
|
|
3446
|
+
if (globalOpts.verbose) {
|
|
3447
|
+
logger.info(`Running: ${installCmd}`);
|
|
3448
|
+
}
|
|
3449
|
+
try {
|
|
3450
|
+
logger.info("Updating packages...");
|
|
3451
|
+
execSync3(installCmd, { stdio: "inherit", timeout: 12e4 });
|
|
3452
|
+
console.log("");
|
|
3453
|
+
logger.success(`Updated to v${targetVersion}`);
|
|
3454
|
+
} catch {
|
|
3455
|
+
console.log("");
|
|
3456
|
+
logger.error("Update failed. You can try manually:");
|
|
3457
|
+
console.log(` ${chalk4.cyan(installCmd)}`);
|
|
3458
|
+
process.exit(ExitCode.ERROR);
|
|
3459
|
+
}
|
|
3460
|
+
console.log("");
|
|
3461
|
+
const regenAnswer = await prompt("Regenerate slash commands? (y/N) ");
|
|
3462
|
+
if (regenAnswer === "y" || regenAnswer === "yes") {
|
|
3463
|
+
const scopeAnswer = await prompt("Generate for (g)lobal or (l)ocal project? (g/l) ");
|
|
3464
|
+
const globalFlag = scopeAnswer === "g" || scopeAnswer === "global" ? " --global" : "";
|
|
3465
|
+
try {
|
|
3466
|
+
execSync3(`harness generate-slash-commands${globalFlag}`, { stdio: "inherit" });
|
|
3467
|
+
} catch {
|
|
3468
|
+
logger.warn("Slash command generation failed. Run manually:");
|
|
3469
|
+
console.log(` ${chalk4.cyan(`harness generate-slash-commands${globalFlag}`)}`);
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
process.exit(ExitCode.SUCCESS);
|
|
3473
|
+
});
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3142
3476
|
// src/index.ts
|
|
3143
3477
|
function createProgram() {
|
|
3144
|
-
const program = new
|
|
3145
|
-
program.name("harness").description("CLI for Harness Engineering toolkit").version(
|
|
3478
|
+
const program = new Command33();
|
|
3479
|
+
program.name("harness").description("CLI for Harness Engineering toolkit").version(VERSION2).option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--verbose", "Verbose output").option("--quiet", "Minimal output");
|
|
3146
3480
|
program.addCommand(createValidateCommand());
|
|
3147
3481
|
program.addCommand(createCheckDepsCommand());
|
|
3148
3482
|
program.addCommand(createCheckDocsCommand());
|
|
@@ -3159,6 +3493,8 @@ function createProgram() {
|
|
|
3159
3493
|
program.addCommand(createCreateSkillCommand());
|
|
3160
3494
|
program.addCommand(createSetupMcpCommand());
|
|
3161
3495
|
program.addCommand(createGenerateSlashCommandsCommand());
|
|
3496
|
+
program.addCommand(createCICommand());
|
|
3497
|
+
program.addCommand(createUpdateCommand());
|
|
3162
3498
|
return program;
|
|
3163
3499
|
}
|
|
3164
3500
|
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@harness-engineering/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "CLI for Harness Engineering toolkit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"handlebars": "^4.7.0",
|
|
27
27
|
"yaml": "^2.3.0",
|
|
28
28
|
"zod": "^3.22.0",
|
|
29
|
-
"@harness-engineering/core": "0.
|
|
29
|
+
"@harness-engineering/core": "0.7.0",
|
|
30
30
|
"@harness-engineering/linter-gen": "0.1.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
@@ -53,7 +53,9 @@
|
|
|
53
53
|
"dev": "tsup src/index.ts src/bin/harness.ts --format esm --dts --watch",
|
|
54
54
|
"lint": "eslint src",
|
|
55
55
|
"typecheck": "tsc --noEmit",
|
|
56
|
-
"test": "vitest",
|
|
57
|
-
"test:
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:watch": "vitest",
|
|
58
|
+
"test:coverage": "vitest run --coverage",
|
|
59
|
+
"clean": "rm -rf dist"
|
|
58
60
|
}
|
|
59
61
|
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
## <!-- Generated by harness generate-slash-commands. Do not edit. -->
|
|
2
|
-
|
|
3
|
-
name: harness:add-component
|
|
4
|
-
description: Add a component to an existing harness project
|
|
5
|
-
argument-hint: "[--path <path>]"
|
|
6
|
-
allowed-tools:
|
|
7
|
-
|
|
8
|
-
- Bash
|
|
9
|
-
- Read
|
|
10
|
-
- Write
|
|
11
|
-
- Edit
|
|
12
|
-
- Glob
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
<context>
|
|
17
|
-
Cognitive mode: constructive-architect
|
|
18
|
-
Type: flexible
|
|
19
|
-
</context>
|
|
20
|
-
|
|
21
|
-
<objective>
|
|
22
|
-
Add a component to an existing harness project
|
|
23
|
-
</objective>
|
|
24
|
-
|
|
25
|
-
<execution_context>
|
|
26
|
-
@agents/skills/claude-code/add-harness-component/SKILL.md
|
|
27
|
-
@agents/skills/claude-code/add-harness-component/skill.yaml
|
|
28
|
-
</execution_context>
|
|
29
|
-
|
|
30
|
-
<process>
|
|
31
|
-
1. Try: invoke mcp__harness__run_skill with skill: "add-harness-component"
|
|
32
|
-
2. If MCP unavailable: read SKILL.md and follow its workflow directly
|
|
33
|
-
3. Pass through any arguments provided by the user
|
|
34
|
-
</process>
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
## <!-- Generated by harness generate-slash-commands. Do not edit. -->
|
|
2
|
-
|
|
3
|
-
name: harness:align-documentation
|
|
4
|
-
description: Auto-fix documentation drift issues
|
|
5
|
-
argument-hint: "[--path <path>]"
|
|
6
|
-
allowed-tools:
|
|
7
|
-
|
|
8
|
-
- Bash
|
|
9
|
-
- Read
|
|
10
|
-
- Write
|
|
11
|
-
- Edit
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
<context>
|
|
16
|
-
Cognitive mode: meticulous-verifier
|
|
17
|
-
Type: flexible
|
|
18
|
-
</context>
|
|
19
|
-
|
|
20
|
-
<objective>
|
|
21
|
-
Auto-fix documentation drift issues
|
|
22
|
-
</objective>
|
|
23
|
-
|
|
24
|
-
<execution_context>
|
|
25
|
-
@agents/skills/claude-code/align-documentation/SKILL.md
|
|
26
|
-
@agents/skills/claude-code/align-documentation/skill.yaml
|
|
27
|
-
</execution_context>
|
|
28
|
-
|
|
29
|
-
<process>
|
|
30
|
-
1. Try: invoke mcp__harness__run_skill with skill: "align-documentation"
|
|
31
|
-
2. If MCP unavailable: read SKILL.md and follow its workflow directly
|
|
32
|
-
3. Pass through any arguments provided by the user
|
|
33
|
-
</process>
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
## <!-- Generated by harness generate-slash-commands. Do not edit. -->
|
|
2
|
-
|
|
3
|
-
name: harness:architecture-advisor
|
|
4
|
-
description: Interactive architecture advisor that surfaces trade-offs and helps humans choose
|
|
5
|
-
argument-hint: "[--path <path>] [--topic <topic>]"
|
|
6
|
-
allowed-tools:
|
|
7
|
-
|
|
8
|
-
- Read
|
|
9
|
-
- Glob
|
|
10
|
-
- Grep
|
|
11
|
-
- Bash
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
<context>
|
|
16
|
-
Cognitive mode: advisory-guide
|
|
17
|
-
Type: flexible
|
|
18
|
-
State: persistent (files: .harness/architecture/)
|
|
19
|
-
</context>
|
|
20
|
-
|
|
21
|
-
<objective>
|
|
22
|
-
Interactive architecture advisor that surfaces trade-offs and helps humans choose
|
|
23
|
-
|
|
24
|
-
Phases:
|
|
25
|
-
|
|
26
|
-
- discover: Ask questions about the problem space and constraints
|
|
27
|
-
- analyze: Research the codebase and identify relevant patterns
|
|
28
|
-
- propose: Present 2-3 architectural options with trade-offs
|
|
29
|
-
- document: Write an Architecture Decision Record for the chosen option
|
|
30
|
-
</objective>
|
|
31
|
-
|
|
32
|
-
<execution_context>
|
|
33
|
-
@agents/skills/claude-code/harness-architecture-advisor/SKILL.md
|
|
34
|
-
@agents/skills/claude-code/harness-architecture-advisor/skill.yaml
|
|
35
|
-
</execution_context>
|
|
36
|
-
|
|
37
|
-
<process>
|
|
38
|
-
1. Try: invoke mcp__harness__run_skill with skill: "harness-architecture-advisor"
|
|
39
|
-
2. If MCP unavailable: read SKILL.md and follow its workflow directly
|
|
40
|
-
3. Pass through any arguments provided by the user
|
|
41
|
-
</process>
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
## <!-- Generated by harness generate-slash-commands. Do not edit. -->
|
|
2
|
-
|
|
3
|
-
name: harness:brainstorming
|
|
4
|
-
description: Structured ideation and exploration with harness methodology
|
|
5
|
-
argument-hint: "[--path <path>]"
|
|
6
|
-
allowed-tools:
|
|
7
|
-
|
|
8
|
-
- Bash
|
|
9
|
-
- Read
|
|
10
|
-
- Write
|
|
11
|
-
- Edit
|
|
12
|
-
- Glob
|
|
13
|
-
- Grep
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
<context>
|
|
18
|
-
Cognitive mode: constructive-architect
|
|
19
|
-
Type: rigid
|
|
20
|
-
</context>
|
|
21
|
-
|
|
22
|
-
<objective>
|
|
23
|
-
Structured ideation and exploration with harness methodology
|
|
24
|
-
|
|
25
|
-
Phases:
|
|
26
|
-
|
|
27
|
-
- explore: Generate ideas and possibilities
|
|
28
|
-
- evaluate: Assess ideas against constraints
|
|
29
|
-
- prioritize: Select and sequence top ideas
|
|
30
|
-
- validate: Run harness checks on selected approach
|
|
31
|
-
</objective>
|
|
32
|
-
|
|
33
|
-
<execution_context>
|
|
34
|
-
@agents/skills/claude-code/harness-brainstorming/SKILL.md
|
|
35
|
-
@agents/skills/claude-code/harness-brainstorming/skill.yaml
|
|
36
|
-
</execution_context>
|
|
37
|
-
|
|
38
|
-
<process>
|
|
39
|
-
1. Try: invoke mcp__harness__run_skill with skill: "harness-brainstorming"
|
|
40
|
-
2. If MCP unavailable: read SKILL.md and follow its workflow directly
|
|
41
|
-
3. Pass through any arguments provided by the user
|
|
42
|
-
</process>
|