@mstar-harness/cli 0.4.0 → 0.5.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.
Files changed (2) hide show
  1. package/dist/mstar-harness.js +271 -110
  2. package/package.json +1 -1
@@ -2344,8 +2344,8 @@ var require_commander = __commonJS((exports) => {
2344
2344
  });
2345
2345
 
2346
2346
  // src/index.ts
2347
- import fs5 from "fs";
2348
- import path7 from "path";
2347
+ import fs6 from "fs";
2348
+ import path8 from "path";
2349
2349
  import { fileURLToPath } from "url";
2350
2350
 
2351
2351
  // ../../node_modules/@inquirer/core/dist/lib/key.js
@@ -4241,9 +4241,9 @@ function buildModelAssignments(selections) {
4241
4241
  }
4242
4242
 
4243
4243
  // src/adapters/codex.ts
4244
- import fs2 from "node:fs";
4245
- import os from "node:os";
4246
- import path3 from "node:path";
4244
+ import fs3 from "node:fs";
4245
+ import os2 from "node:os";
4246
+ import path4 from "node:path";
4247
4247
 
4248
4248
  // src/utils.ts
4249
4249
  import fs from "node:fs";
@@ -4283,21 +4283,169 @@ function resolveProjectRoot() {
4283
4283
  return process.cwd();
4284
4284
  }
4285
4285
 
4286
+ // src/adapters/shared-install.ts
4287
+ import fs2 from "node:fs";
4288
+ import os from "node:os";
4289
+ import path3 from "node:path";
4290
+ import { execFileSync } from "node:child_process";
4291
+ var REPO_URL = "https://github.com/btspoony/mstar-harness.git";
4292
+ var PLUGIN_NAME = "morning-star-harness";
4293
+ var HARNESS_REPO_PATH = path3.join(os.homedir(), ".mstar", "harness");
4294
+ var HARNESS_MARKER = ".codex-plugin/plugin.json";
4295
+ function pathOrSymlinkExists(filePath) {
4296
+ try {
4297
+ fs2.lstatSync(filePath);
4298
+ return true;
4299
+ } catch {
4300
+ return false;
4301
+ }
4302
+ }
4303
+ function ensureDir(dirPath, dryRun) {
4304
+ if (dryRun)
4305
+ return;
4306
+ if (!fs2.existsSync(dirPath))
4307
+ fs2.mkdirSync(dirPath, { recursive: true });
4308
+ }
4309
+ function runCommand(command, cwd, dryRun) {
4310
+ if (dryRun)
4311
+ return;
4312
+ execFileSync(command[0], command.slice(1), { cwd, stdio: "pipe", encoding: "utf8" });
4313
+ }
4314
+ function ensureLocalHarnessRepo(dryRun) {
4315
+ const notes = [];
4316
+ if (fs2.existsSync(HARNESS_REPO_PATH)) {
4317
+ const errors2 = validateLocalHarnessRepo();
4318
+ if (errors2.length) {
4319
+ throw new Error(errors2.join(`
4320
+ `));
4321
+ }
4322
+ notes.push(`Using existing local harness repo at ${HARNESS_REPO_PATH}`);
4323
+ return notes;
4324
+ }
4325
+ ensureDir(path3.dirname(HARNESS_REPO_PATH), dryRun);
4326
+ runCommand(["git", "clone", REPO_URL, HARNESS_REPO_PATH], path3.dirname(HARNESS_REPO_PATH), dryRun);
4327
+ notes.push(`Cloned ${REPO_URL} to ${HARNESS_REPO_PATH}`);
4328
+ return notes;
4329
+ }
4330
+ function validateLocalHarnessRepo() {
4331
+ const errors2 = [];
4332
+ if (!fs2.existsSync(HARNESS_REPO_PATH)) {
4333
+ errors2.push(`Missing local harness repo: ${HARNESS_REPO_PATH}`);
4334
+ return errors2;
4335
+ }
4336
+ const marker = path3.join(HARNESS_REPO_PATH, HARNESS_MARKER);
4337
+ if (!fs2.existsSync(marker)) {
4338
+ errors2.push(`Local harness repo is missing marker file: ${marker}`);
4339
+ }
4340
+ return errors2;
4341
+ }
4342
+ function ensureSymlink(target, linkPath, dryRun) {
4343
+ if (pathOrSymlinkExists(linkPath)) {
4344
+ const stat = fs2.lstatSync(linkPath);
4345
+ if (!stat.isSymbolicLink()) {
4346
+ throw new Error(`Path exists and is not a symlink: ${linkPath}`);
4347
+ }
4348
+ if (!fs2.existsSync(target)) {
4349
+ throw new Error(`Symlink target is missing: ${target}`);
4350
+ }
4351
+ const actual = fs2.realpathSync(linkPath);
4352
+ const expected = fs2.realpathSync(target);
4353
+ if (actual !== expected) {
4354
+ throw new Error(`Symlink ${linkPath} points to ${actual}, expected ${expected}`);
4355
+ }
4356
+ return `Symlink already exists: ${linkPath} -> ${target}`;
4357
+ }
4358
+ ensureDir(path3.dirname(linkPath), dryRun);
4359
+ if (!dryRun)
4360
+ fs2.symlinkSync(target, linkPath);
4361
+ return `Linked ${linkPath} -> ${target}`;
4362
+ }
4363
+ function validateSymlink(target, linkPath) {
4364
+ const errors2 = [];
4365
+ if (!pathOrSymlinkExists(linkPath)) {
4366
+ errors2.push(`Missing symlink: ${linkPath}`);
4367
+ return errors2;
4368
+ }
4369
+ const stat = fs2.lstatSync(linkPath);
4370
+ if (!stat.isSymbolicLink()) {
4371
+ errors2.push(`Path exists but is not a symlink: ${linkPath}`);
4372
+ return errors2;
4373
+ }
4374
+ if (!fs2.existsSync(target)) {
4375
+ errors2.push(`Symlink target is missing: ${target}`);
4376
+ return errors2;
4377
+ }
4378
+ const actual = fs2.realpathSync(linkPath);
4379
+ const expected = fs2.realpathSync(target);
4380
+ if (actual !== expected) {
4381
+ errors2.push(`Symlink ${linkPath} points to ${actual}, expected ${expected}`);
4382
+ }
4383
+ return errors2;
4384
+ }
4385
+ function appendGitignore(projectRoot, entries, dryRun) {
4386
+ const gitignorePath = path3.join(projectRoot, ".gitignore");
4387
+ const current = fs2.existsSync(gitignorePath) ? fs2.readFileSync(gitignorePath, "utf8") : "";
4388
+ const lines = new Set(current.split(/\r?\n/).map((line) => line.trim()));
4389
+ const missing = entries.filter((entry) => !lines.has(entry));
4390
+ if (!missing.length)
4391
+ return [];
4392
+ if (!dryRun) {
4393
+ const prefix = current && !current.endsWith(`
4394
+ `) ? `
4395
+ ` : "";
4396
+ fs2.appendFileSync(gitignorePath, `${prefix}${missing.join(`
4397
+ `)}
4398
+ `, "utf8");
4399
+ }
4400
+ return missing.map((entry) => `Added ${entry} to .gitignore`);
4401
+ }
4402
+ function homeRelativeSourcePath(targetPath) {
4403
+ const rel = path3.relative(os.homedir(), targetPath).split(path3.sep).join("/");
4404
+ return rel.startsWith("..") ? targetPath : `./${rel}`;
4405
+ }
4406
+
4286
4407
  // src/adapters/codex.ts
4287
4408
  var MARKETPLACE_NAME = "personal";
4288
4409
  var MARKETPLACE_DISPLAY_NAME = "Personal";
4289
- var MARKETPLACE_PATH = path3.join(os.homedir(), ".agents", "plugins", "marketplace.json");
4290
- var PLUGIN_NAME = "morning-star-harness";
4291
- var PLUGIN_URL = "https://github.com/btspoony/mstar-harness.git";
4292
- var PLUGIN_REF = "main";
4410
+ var GLOBAL_MARKETPLACE_PATH = path4.join(os2.homedir(), ".agents", "plugins", "marketplace.json");
4293
4411
  var PLUGIN_CATEGORY = "Productivity";
4294
- function mstarEntry() {
4412
+ var CODEX_PLUGIN_LINK = ".codex/plugins/mstar-harness";
4413
+ var CODEX_AGENT_NAMES = [
4414
+ "product-manager",
4415
+ "architect",
4416
+ "fullstack-dev",
4417
+ "fullstack-dev-2",
4418
+ "frontend-dev",
4419
+ "qa-engineer",
4420
+ "qc-specialist",
4421
+ "qc-specialist-2",
4422
+ "qc-specialist-3",
4423
+ "ops-engineer",
4424
+ "writing-specialist",
4425
+ "prompt-engineer"
4426
+ ];
4427
+ function globalMarketplacePath() {
4428
+ return GLOBAL_MARKETPLACE_PATH;
4429
+ }
4430
+ function projectMarketplacePath() {
4431
+ return path4.join(resolveProjectRoot(), ".agents", "plugins", "marketplace.json");
4432
+ }
4433
+ function agentSourcePath(agentName) {
4434
+ return path4.join(HARNESS_REPO_PATH, "codex", "agents", `${agentName}.toml`);
4435
+ }
4436
+ function globalAgentLinkPath(agentName) {
4437
+ return path4.join(os2.homedir(), ".codex", "agents", `${agentName}.toml`);
4438
+ }
4439
+ function projectAgentLinkPath(agentName) {
4440
+ return path4.join(resolveProjectRoot(), ".codex", "agents", `${agentName}.toml`);
4441
+ }
4442
+ function mstarEntry(scope) {
4443
+ const sourcePath = scope === "global" ? homeRelativeSourcePath(HARNESS_REPO_PATH) : `./${CODEX_PLUGIN_LINK}`;
4295
4444
  return {
4296
4445
  name: PLUGIN_NAME,
4297
4446
  source: {
4298
- source: "url",
4299
- url: PLUGIN_URL,
4300
- ref: PLUGIN_REF
4447
+ source: "local",
4448
+ path: sourcePath
4301
4449
  },
4302
4450
  policy: {
4303
4451
  installation: "AVAILABLE",
@@ -4319,12 +4467,12 @@ function normalizeMarketplace(raw) {
4319
4467
  }
4320
4468
  return next;
4321
4469
  }
4322
- function upsertEntry(raw) {
4470
+ function upsertEntry(raw, scope) {
4323
4471
  const next = normalizeMarketplace(raw);
4324
4472
  const plugins = next.plugins.filter((entry) => {
4325
4473
  return !(entry && typeof entry === "object" && !Array.isArray(entry) && entry.name === PLUGIN_NAME);
4326
4474
  });
4327
- plugins.push(mstarEntry());
4475
+ plugins.push(mstarEntry(scope));
4328
4476
  next.plugins = plugins;
4329
4477
  return next;
4330
4478
  }
@@ -4334,19 +4482,19 @@ function findEntry(raw) {
4334
4482
  return entry && typeof entry === "object" && !Array.isArray(entry) && entry.name === PLUGIN_NAME;
4335
4483
  });
4336
4484
  }
4337
- function validateEntryShape(entry) {
4485
+ function validateEntryShape(entry, scope, marketplacePath) {
4338
4486
  const errors2 = [];
4339
4487
  if (!entry) {
4340
- errors2.push(`Missing ${PLUGIN_NAME} entry in ${MARKETPLACE_PATH}.`);
4488
+ errors2.push(`Missing ${PLUGIN_NAME} entry in ${marketplacePath}.`);
4341
4489
  return errors2;
4342
4490
  }
4343
4491
  const source = ensureObject(entry.source);
4344
- if (source.source !== "url")
4345
- errors2.push("Codex marketplace entry source.source must be `url`.");
4346
- if (source.url !== PLUGIN_URL)
4347
- errors2.push(`Codex marketplace entry source.url must be ${PLUGIN_URL}.`);
4348
- if (source.ref !== PLUGIN_REF)
4349
- errors2.push(`Codex marketplace entry source.ref must be ${PLUGIN_REF}.`);
4492
+ const expectedSourcePath = mstarEntry(scope).source.path;
4493
+ if (source.source !== "local")
4494
+ errors2.push("Codex marketplace entry source.source must be `local`.");
4495
+ if (source.path !== expectedSourcePath) {
4496
+ errors2.push(`Codex marketplace entry source.path must be ${expectedSourcePath}.`);
4497
+ }
4350
4498
  const policy = ensureObject(entry.policy);
4351
4499
  if (policy.installation !== "AVAILABLE")
4352
4500
  errors2.push("Codex marketplace entry policy.installation must be AVAILABLE.");
@@ -4356,100 +4504,115 @@ function validateEntryShape(entry) {
4356
4504
  errors2.push(`Codex marketplace entry category must be ${PLUGIN_CATEGORY}.`);
4357
4505
  return errors2;
4358
4506
  }
4359
- function runInit(dryRun) {
4360
- const current = readJson(MARKETPLACE_PATH);
4361
- const next = upsertEntry(current);
4507
+ function marketplacePath(scope) {
4508
+ if (scope === "global")
4509
+ return globalMarketplacePath();
4510
+ return projectMarketplacePath();
4511
+ }
4512
+ function ensureAgentLinks(scope, dryRun) {
4513
+ const notes = [];
4514
+ for (const agentName of CODEX_AGENT_NAMES) {
4515
+ const source = agentSourcePath(agentName);
4516
+ const linkPath = scope === "global" ? globalAgentLinkPath(agentName) : projectAgentLinkPath(agentName);
4517
+ notes.push(ensureSymlink(source, linkPath, dryRun));
4518
+ }
4519
+ return notes;
4520
+ }
4521
+ function validateAgentLinks(scope) {
4522
+ const errors2 = [];
4523
+ for (const agentName of CODEX_AGENT_NAMES) {
4524
+ const source = agentSourcePath(agentName);
4525
+ const linkPath = scope === "global" ? globalAgentLinkPath(agentName) : projectAgentLinkPath(agentName);
4526
+ errors2.push(...validateSymlink(source, linkPath));
4527
+ }
4528
+ return errors2;
4529
+ }
4530
+ function runInit(scope, dryRun) {
4531
+ const pathToMarketplace = marketplacePath(scope);
4532
+ const current = readJson(pathToMarketplace);
4533
+ const next = upsertEntry(current, scope);
4362
4534
  const existingEntry = findEntry(current);
4535
+ const notes = ensureLocalHarnessRepo(dryRun);
4536
+ if (scope === "project") {
4537
+ const projectRoot = resolveProjectRoot();
4538
+ notes.push(ensureSymlink(HARNESS_REPO_PATH, path4.join(projectRoot, CODEX_PLUGIN_LINK), dryRun));
4539
+ notes.push(...appendGitignore(projectRoot, [CODEX_PLUGIN_LINK, ".codex/agents/*.toml"], dryRun));
4540
+ }
4541
+ notes.push(...ensureAgentLinks(scope, dryRun));
4363
4542
  if (!dryRun)
4364
- writeJson(MARKETPLACE_PATH, next);
4543
+ writeJson(pathToMarketplace, next);
4544
+ notes.push(existingEntry ? `Updated ${PLUGIN_NAME} local marketplace entry.` : `Added ${PLUGIN_NAME} local marketplace entry.`);
4545
+ notes.push(`Source path: ${mstarEntry(scope).source.path}`);
4546
+ notes.push(`Install after init: codex plugin add ${PLUGIN_NAME} --marketplace ${MARKETPLACE_NAME}`);
4365
4547
  return {
4366
- location: MARKETPLACE_PATH,
4367
- notes: [
4368
- existingEntry ? `Updated ${PLUGIN_NAME} entry in personal Codex marketplace.` : `Added ${PLUGIN_NAME} entry to personal Codex marketplace.`,
4369
- `Source URL: ${PLUGIN_URL}#${PLUGIN_REF}`,
4370
- `Install after init: codex plugin add ${PLUGIN_NAME} --marketplace ${String(next.name)}`
4371
- ]
4548
+ location: pathToMarketplace,
4549
+ notes
4372
4550
  };
4373
4551
  }
4374
- function runDoctor() {
4375
- const errors2 = [];
4376
- if (!fs2.existsSync(MARKETPLACE_PATH)) {
4377
- return { location: MARKETPLACE_PATH, errors: [`Missing Codex personal marketplace: ${MARKETPLACE_PATH}`] };
4552
+ function runDoctor(scope) {
4553
+ const pathToMarketplace = marketplacePath(scope);
4554
+ const errors2 = validateLocalHarnessRepo();
4555
+ if (!fs3.existsSync(pathToMarketplace)) {
4556
+ return { location: pathToMarketplace, errors: [...errors2, `Missing Codex marketplace: ${pathToMarketplace}`] };
4378
4557
  }
4379
- const marketplace = readJson(MARKETPLACE_PATH);
4558
+ const marketplace = readJson(pathToMarketplace);
4380
4559
  if (marketplace.name !== MARKETPLACE_NAME) {
4381
4560
  errors2.push(`Codex personal marketplace name must be ${MARKETPLACE_NAME}.`);
4382
4561
  }
4383
- errors2.push(...validateEntryShape(findEntry(marketplace)));
4384
- return { location: MARKETPLACE_PATH, errors: errors2 };
4562
+ errors2.push(...validateEntryShape(findEntry(marketplace), scope, pathToMarketplace));
4563
+ if (scope === "project") {
4564
+ const projectRoot = resolveProjectRoot();
4565
+ errors2.push(...validateSymlink(HARNESS_REPO_PATH, path4.join(projectRoot, CODEX_PLUGIN_LINK)));
4566
+ const gitignorePath = path4.join(projectRoot, ".gitignore");
4567
+ const gitignore = fs3.existsSync(gitignorePath) ? fs3.readFileSync(gitignorePath, "utf8") : "";
4568
+ const lines = gitignore.split(/\r?\n/);
4569
+ if (!lines.includes(CODEX_PLUGIN_LINK))
4570
+ errors2.push(`Missing .gitignore entry: ${CODEX_PLUGIN_LINK}`);
4571
+ if (!lines.includes(".codex/agents/*.toml"))
4572
+ errors2.push("Missing .gitignore entry: .codex/agents/*.toml");
4573
+ }
4574
+ errors2.push(...validateAgentLinks(scope));
4575
+ return { location: pathToMarketplace, errors: errors2 };
4385
4576
  }
4386
4577
  var codexAdapter = {
4387
4578
  target: "codex",
4388
4579
  mode: "install",
4389
- runInstallInit: (_scope, dryRun) => runInit(dryRun),
4390
- runInstallDoctor: () => runDoctor()
4580
+ runInstallInit: (scope, dryRun) => runInit(scope, dryRun),
4581
+ runInstallDoctor: (scope) => runDoctor(scope)
4391
4582
  };
4392
4583
 
4393
4584
  // src/adapters/cursor.ts
4394
- import fs3 from "node:fs";
4395
- import os2 from "node:os";
4396
- import path4 from "node:path";
4397
- import { execFileSync } from "node:child_process";
4398
- var REPO_URL = "https://github.com/btspoony/mstar-harness.git";
4585
+ import fs4 from "node:fs";
4586
+ import os3 from "node:os";
4587
+ import path5 from "node:path";
4399
4588
  var CURSOR_PLUGIN_NAME = "mstar-harness";
4400
4589
  var CURSOR_PLUGIN_MARKER = ".cursor-plugin/plugin.json";
4401
4590
  function globalInstallPath() {
4402
- return path4.join(os2.homedir(), ".cursor", "plugins", "local", CURSOR_PLUGIN_NAME);
4591
+ return path5.join(os3.homedir(), ".cursor", "plugins", "local", CURSOR_PLUGIN_NAME);
4403
4592
  }
4404
4593
  function projectInstallPath() {
4405
- return path4.join(resolveProjectRoot(), ".cursor", "plugins", CURSOR_PLUGIN_NAME);
4406
- }
4407
- function ensureDir(dirPath, dryRun) {
4408
- if (dryRun)
4409
- return;
4410
- if (!fs3.existsSync(dirPath))
4411
- fs3.mkdirSync(dirPath, { recursive: true });
4412
- }
4413
- function runCommand(command, cwd, dryRun) {
4414
- if (dryRun)
4415
- return;
4416
- execFileSync(command[0], command.slice(1), { cwd, stdio: "pipe", encoding: "utf8" });
4594
+ return path5.join(resolveProjectRoot(), ".cursor", "plugins", CURSOR_PLUGIN_NAME);
4417
4595
  }
4418
4596
  function globalInit(dryRun) {
4419
4597
  const location = globalInstallPath();
4420
- const notes = [];
4421
- if (fs3.existsSync(location)) {
4422
- notes.push(`Plugin already exists at ${location}`);
4423
- return { location, notes };
4424
- }
4425
- ensureDir(path4.dirname(location), dryRun);
4426
- runCommand(["git", "clone", REPO_URL, location], path4.dirname(location), dryRun);
4427
- notes.push(`Cloned ${REPO_URL} to ${location}`);
4598
+ const notes = ensureLocalHarnessRepo(dryRun);
4599
+ notes.push(ensureSymlink(HARNESS_REPO_PATH, location, dryRun));
4428
4600
  return { location, notes };
4429
4601
  }
4430
4602
  function projectInit(dryRun) {
4431
4603
  const projectRoot = resolveProjectRoot();
4432
4604
  const location = projectInstallPath();
4433
- const notes = [];
4434
- if (fs3.existsSync(location)) {
4435
- notes.push(`Submodule path already exists at ${location}`);
4436
- return { location, notes };
4437
- }
4438
- runCommand(["git", "rev-parse", "--is-inside-work-tree"], projectRoot, dryRun);
4439
- ensureDir(path4.join(projectRoot, ".cursor", "plugins"), dryRun);
4440
- runCommand(["git", "submodule", "add", REPO_URL, ".cursor/plugins/mstar-harness"], projectRoot, dryRun);
4441
- notes.push("Added mstar-harness as git submodule at .cursor/plugins/mstar-harness");
4605
+ const notes = ensureLocalHarnessRepo(dryRun);
4606
+ notes.push(ensureSymlink(HARNESS_REPO_PATH, location, dryRun));
4607
+ notes.push(...appendGitignore(projectRoot, [".cursor/plugins/mstar-harness"], dryRun));
4442
4608
  return { location, notes };
4443
4609
  }
4444
4610
  function globalDoctor() {
4445
4611
  const location = globalInstallPath();
4446
- const errors2 = [];
4447
- if (!fs3.existsSync(location)) {
4448
- errors2.push(`Missing plugin directory: ${location}`);
4449
- return { location, errors: errors2 };
4450
- }
4451
- const marker = path4.join(location, CURSOR_PLUGIN_MARKER);
4452
- if (!fs3.existsSync(marker)) {
4612
+ const errors2 = validateLocalHarnessRepo();
4613
+ errors2.push(...validateSymlink(HARNESS_REPO_PATH, location));
4614
+ const marker = path5.join(location, CURSOR_PLUGIN_MARKER);
4615
+ if (!fs4.existsSync(marker)) {
4453
4616
  errors2.push(`Missing Cursor plugin marker file: ${marker}`);
4454
4617
  }
4455
4618
  return { location, errors: errors2 };
@@ -4457,18 +4620,16 @@ function globalDoctor() {
4457
4620
  function projectDoctor() {
4458
4621
  const projectRoot = resolveProjectRoot();
4459
4622
  const location = projectInstallPath();
4460
- const errors2 = [];
4461
- if (!fs3.existsSync(location)) {
4462
- errors2.push(`Missing submodule directory: ${location}`);
4463
- }
4464
- const gitmodulesPath = path4.join(projectRoot, ".gitmodules");
4465
- if (!fs3.existsSync(gitmodulesPath)) {
4466
- errors2.push("Missing .gitmodules (expected cursor plugin submodule entry).");
4467
- return { location, errors: errors2 };
4623
+ const errors2 = validateLocalHarnessRepo();
4624
+ errors2.push(...validateSymlink(HARNESS_REPO_PATH, location));
4625
+ const marker = path5.join(location, CURSOR_PLUGIN_MARKER);
4626
+ if (!fs4.existsSync(marker)) {
4627
+ errors2.push(`Missing Cursor plugin marker file: ${marker}`);
4468
4628
  }
4469
- const gitmodules = fs3.readFileSync(gitmodulesPath, "utf8");
4470
- if (!gitmodules.includes("path = .cursor/plugins/mstar-harness")) {
4471
- errors2.push("Missing .cursor/plugins/mstar-harness entry in .gitmodules.");
4629
+ const gitignorePath = path5.join(projectRoot, ".gitignore");
4630
+ const gitignore = fs4.existsSync(gitignorePath) ? fs4.readFileSync(gitignorePath, "utf8") : "";
4631
+ if (!gitignore.split(/\r?\n/).includes(".cursor/plugins/mstar-harness")) {
4632
+ errors2.push("Missing .gitignore entry: .cursor/plugins/mstar-harness");
4472
4633
  }
4473
4634
  return { location, errors: errors2 };
4474
4635
  }
@@ -4488,8 +4649,8 @@ var cursorAdapter = {
4488
4649
  };
4489
4650
 
4490
4651
  // src/adapters/opencode.ts
4491
- import os3 from "node:os";
4492
- import path5 from "node:path";
4652
+ import os4 from "node:os";
4653
+ import path6 from "node:path";
4493
4654
  import { execFileSync as execFileSync2 } from "node:child_process";
4494
4655
  var OPENCODE_CONFIG_SCHEMA = "https://opencode.ai/config.json";
4495
4656
  var MSTAR_OPENCODE_PLUGIN = "@mstar-harness/opencode@latest";
@@ -4518,11 +4679,11 @@ function getOpencodeModels() {
4518
4679
  function resolveOpencodeConfigPath(scope, outputPath) {
4519
4680
  if (outputPath && outputPath.trim()) {
4520
4681
  const raw = outputPath.trim();
4521
- return path5.isAbsolute(raw) ? raw : path5.join(resolveProjectRoot(), raw);
4682
+ return path6.isAbsolute(raw) ? raw : path6.join(resolveProjectRoot(), raw);
4522
4683
  }
4523
4684
  if (scope === "global")
4524
- return path5.join(os3.homedir(), ".config", "opencode", "opencode.json");
4525
- return path5.join(resolveProjectRoot(), "opencode.json");
4685
+ return path6.join(os4.homedir(), ".config", "opencode", "opencode.json");
4686
+ return path6.join(resolveProjectRoot(), "opencode.json");
4526
4687
  }
4527
4688
  function ensureConfigSchema(config) {
4528
4689
  const next = ensureObject(config);
@@ -4630,17 +4791,17 @@ function getAdapter(target) {
4630
4791
  var SUPPORTED_TARGETS = ["opencode", "cursor", "codex"];
4631
4792
 
4632
4793
  // src/utils.ts
4633
- import fs4 from "node:fs";
4634
- import path6 from "node:path";
4794
+ import fs5 from "node:fs";
4795
+ import path7 from "node:path";
4635
4796
  function parseCsv(raw) {
4636
4797
  if (!raw)
4637
4798
  return;
4638
4799
  return raw.split(",").map((item) => item.trim()).filter(Boolean);
4639
4800
  }
4640
4801
  function readJson2(filePath) {
4641
- if (!fs4.existsSync(filePath))
4802
+ if (!fs5.existsSync(filePath))
4642
4803
  return {};
4643
- const content = fs4.readFileSync(filePath, "utf8").trim();
4804
+ const content = fs5.readFileSync(filePath, "utf8").trim();
4644
4805
  if (!content)
4645
4806
  return {};
4646
4807
  try {
@@ -4650,18 +4811,18 @@ function readJson2(filePath) {
4650
4811
  }
4651
4812
  }
4652
4813
  function writeJson2(filePath, value) {
4653
- const parent = path6.dirname(filePath);
4654
- if (!fs4.existsSync(parent))
4655
- fs4.mkdirSync(parent, { recursive: true });
4656
- fs4.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}
4814
+ const parent = path7.dirname(filePath);
4815
+ if (!fs5.existsSync(parent))
4816
+ fs5.mkdirSync(parent, { recursive: true });
4817
+ fs5.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}
4657
4818
  `, "utf8");
4658
4819
  }
4659
4820
 
4660
4821
  // src/index.ts
4661
- var packageJsonPath = path7.resolve(path7.dirname(fileURLToPath(import.meta.url)), "../package.json");
4822
+ var packageJsonPath = path8.resolve(path8.dirname(fileURLToPath(import.meta.url)), "../package.json");
4662
4823
  var packageVersion = (() => {
4663
4824
  try {
4664
- const parsed = JSON.parse(fs5.readFileSync(packageJsonPath, "utf8"));
4825
+ const parsed = JSON.parse(fs6.readFileSync(packageJsonPath, "utf8"));
4665
4826
  return parsed.version || "0.0.0";
4666
4827
  } catch {
4667
4828
  return "0.0.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mstar-harness/cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Morning Star harness installer CLI (OpenCode, Cursor, Codex).",
5
5
  "license": "MIT",
6
6
  "repository": {