@harness-engineering/cli 1.2.2 → 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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createProgram
4
- } from "../chunk-5RQKSZLA.js";
4
+ } from "../chunk-APYEWOCR.js";
5
5
  import "../chunk-3U5VZYR7.js";
6
6
  import {
7
7
  handleError
@@ -8,8 +8,8 @@ import {
8
8
  } from "./chunk-EFZOLZFB.js";
9
9
 
10
10
  // src/index.ts
11
- import { Command as Command29 } from "commander";
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
- (resolve13) => setTimeout(
1391
- () => resolve13({ ok: false, error: new Error(TIMEOUT_ERROR_MESSAGE) }),
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((resolve13) => {
2564
- rl.question("Reset project state? This cannot be undone. [y/N] ", resolve13);
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") {
@@ -2899,14 +2899,14 @@ function renderClaudeCode(spec) {
2899
2899
  }
2900
2900
 
2901
2901
  // src/slash-commands/render-gemini.ts
2902
- function escapeToml(content) {
2903
- return content.replace(/"""/g, '""\\"');
2902
+ function escapeTomlLiteral(content) {
2903
+ return content.replace(/'''/g, "''\\'''");
2904
2904
  }
2905
2905
  function renderGemini(spec, skillMdContent, skillYamlContent) {
2906
2906
  const lines = [GENERATED_HEADER_GEMINI];
2907
2907
  const safeDesc = spec.description.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
2908
2908
  lines.push(`description = "${safeDesc}"`);
2909
- lines.push('prompt = """');
2909
+ lines.push("prompt = '''");
2910
2910
  lines.push("<context>");
2911
2911
  lines.push(spec.prompt.context);
2912
2912
  lines.push("</context>");
@@ -2920,14 +2920,14 @@ function renderGemini(spec, skillMdContent, skillYamlContent) {
2920
2920
  if (skillMdContent) {
2921
2921
  const mdPath = spec.prompt.executionContext.split("\n")[0]?.replace(/^@/, "") ?? "";
2922
2922
  lines.push(`--- SKILL.md (${mdPath}) ---`);
2923
- lines.push(escapeToml(skillMdContent));
2923
+ lines.push(escapeTomlLiteral(skillMdContent));
2924
2924
  lines.push("");
2925
2925
  }
2926
2926
  if (skillYamlContent) {
2927
2927
  const refs = spec.prompt.executionContext.split("\n");
2928
2928
  const yamlPath = (refs[1] ?? refs[0] ?? "").replace(/^@/, "");
2929
2929
  lines.push(`--- skill.yaml (${yamlPath}) ---`);
2930
- lines.push(escapeToml(skillYamlContent));
2930
+ lines.push(escapeTomlLiteral(skillYamlContent));
2931
2931
  }
2932
2932
  lines.push("</execution_context>");
2933
2933
  lines.push("");
@@ -2939,7 +2939,7 @@ function renderGemini(spec, skillMdContent, skillYamlContent) {
2939
2939
  lines.push("<process>");
2940
2940
  lines.push(geminiProcess);
2941
2941
  lines.push("</process>");
2942
- lines.push('"""');
2942
+ lines.push("'''");
2943
2943
  lines.push("");
2944
2944
  return lines.join("\n");
2945
2945
  }
@@ -3014,11 +3014,11 @@ function fileExtension(platform) {
3014
3014
  }
3015
3015
  async function confirmDeletion(files) {
3016
3016
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
3017
- return new Promise((resolve13) => {
3017
+ return new Promise((resolve14) => {
3018
3018
  rl.question(`
3019
3019
  Remove ${files.length} orphaned command(s)? (y/N) `, (answer) => {
3020
3020
  rl.close();
3021
- resolve13(answer.toLowerCase() === "y");
3021
+ resolve14(answer.toLowerCase() === "y");
3022
3022
  });
3023
3023
  });
3024
3024
  }
@@ -3140,10 +3140,343 @@ ${result.platform} \u2192 ${result.outputDir}`);
3140
3140
  });
3141
3141
  }
3142
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
+
3143
3476
  // src/index.ts
3144
3477
  function createProgram() {
3145
- const program = new Command29();
3146
- program.name("harness").description("CLI for Harness Engineering toolkit").version(VERSION).option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--verbose", "Verbose output").option("--quiet", "Minimal output");
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");
3147
3480
  program.addCommand(createValidateCommand());
3148
3481
  program.addCommand(createCheckDepsCommand());
3149
3482
  program.addCommand(createCheckDocsCommand());
@@ -3160,6 +3493,8 @@ function createProgram() {
3160
3493
  program.addCommand(createCreateSkillCommand());
3161
3494
  program.addCommand(createSetupMcpCommand());
3162
3495
  program.addCommand(createGenerateSlashCommandsCommand());
3496
+ program.addCommand(createCICommand());
3497
+ program.addCommand(createUpdateCommand());
3163
3498
  return program;
3164
3499
  }
3165
3500
 
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  resolveConfig,
16
16
  runCheckPhaseGate,
17
17
  runPersona
18
- } from "./chunk-5RQKSZLA.js";
18
+ } from "./chunk-APYEWOCR.js";
19
19
  import {
20
20
  runCrossCheck
21
21
  } from "./chunk-3U5VZYR7.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harness-engineering/cli",
3
- "version": "1.2.2",
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.6.0",
29
+ "@harness-engineering/core": "0.7.0",
30
30
  "@harness-engineering/linter-gen": "0.1.0"
31
31
  },
32
32
  "devDependencies": {