@mstar-harness/cli 0.3.1 → 0.4.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 +179 -48
  2. package/package.json +2 -2
@@ -2344,8 +2344,8 @@ var require_commander = __commonJS((exports) => {
2344
2344
  });
2345
2345
 
2346
2346
  // src/index.ts
2347
- import fs3 from "fs";
2348
- import path6 from "path";
2347
+ import fs5 from "fs";
2348
+ import path7 from "path";
2349
2349
  import { fileURLToPath } from "url";
2350
2350
 
2351
2351
  // ../../node_modules/@inquirer/core/dist/lib/key.js
@@ -4240,13 +4240,13 @@ function buildModelAssignments(selections) {
4240
4240
  return { ...assignments, ...pickRandom(others, selections.others) };
4241
4241
  }
4242
4242
 
4243
- // src/adapters/cursor.ts
4244
- import fs from "node:fs";
4243
+ // src/adapters/codex.ts
4244
+ import fs2 from "node:fs";
4245
4245
  import os from "node:os";
4246
4246
  import path3 from "node:path";
4247
- import { execFileSync } from "node:child_process";
4248
4247
 
4249
4248
  // src/utils.ts
4249
+ import fs from "node:fs";
4250
4250
  import path2 from "node:path";
4251
4251
  function normalizeModelList(raw) {
4252
4252
  return raw.split(`
@@ -4257,6 +4257,25 @@ function ensureObject(value) {
4257
4257
  return value;
4258
4258
  return {};
4259
4259
  }
4260
+ function readJson(filePath) {
4261
+ if (!fs.existsSync(filePath))
4262
+ return {};
4263
+ const content = fs.readFileSync(filePath, "utf8").trim();
4264
+ if (!content)
4265
+ return {};
4266
+ try {
4267
+ return JSON.parse(content);
4268
+ } catch (error) {
4269
+ throw new Error(`Invalid JSON in ${filePath}: ${error.message}`);
4270
+ }
4271
+ }
4272
+ function writeJson(filePath, value) {
4273
+ const parent = path2.dirname(filePath);
4274
+ if (!fs.existsSync(parent))
4275
+ fs.mkdirSync(parent, { recursive: true });
4276
+ fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}
4277
+ `, "utf8");
4278
+ }
4260
4279
  function resolveProjectRoot() {
4261
4280
  const candidate = process.env.MSTAR_CLI_PROJECT_ROOT || process.env.INIT_CWD || process.env.PWD;
4262
4281
  if (candidate && candidate.trim())
@@ -4264,21 +4283,132 @@ function resolveProjectRoot() {
4264
4283
  return process.cwd();
4265
4284
  }
4266
4285
 
4286
+ // src/adapters/codex.ts
4287
+ var MARKETPLACE_NAME = "personal";
4288
+ 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";
4293
+ var PLUGIN_CATEGORY = "Productivity";
4294
+ function mstarEntry() {
4295
+ return {
4296
+ name: PLUGIN_NAME,
4297
+ source: {
4298
+ source: "url",
4299
+ url: PLUGIN_URL,
4300
+ ref: PLUGIN_REF
4301
+ },
4302
+ policy: {
4303
+ installation: "AVAILABLE",
4304
+ authentication: "ON_INSTALL"
4305
+ },
4306
+ category: PLUGIN_CATEGORY
4307
+ };
4308
+ }
4309
+ function normalizeMarketplace(raw) {
4310
+ const next = ensureObject(raw);
4311
+ next.name = MARKETPLACE_NAME;
4312
+ const iface = ensureObject(next.interface);
4313
+ if (typeof iface.displayName !== "string" || !iface.displayName.trim()) {
4314
+ iface.displayName = MARKETPLACE_DISPLAY_NAME;
4315
+ }
4316
+ next.interface = iface;
4317
+ if (!Array.isArray(next.plugins)) {
4318
+ next.plugins = [];
4319
+ }
4320
+ return next;
4321
+ }
4322
+ function upsertEntry(raw) {
4323
+ const next = normalizeMarketplace(raw);
4324
+ const plugins = next.plugins.filter((entry) => {
4325
+ return !(entry && typeof entry === "object" && !Array.isArray(entry) && entry.name === PLUGIN_NAME);
4326
+ });
4327
+ plugins.push(mstarEntry());
4328
+ next.plugins = plugins;
4329
+ return next;
4330
+ }
4331
+ function findEntry(raw) {
4332
+ const plugins = Array.isArray(raw.plugins) ? raw.plugins : [];
4333
+ return plugins.find((entry) => {
4334
+ return entry && typeof entry === "object" && !Array.isArray(entry) && entry.name === PLUGIN_NAME;
4335
+ });
4336
+ }
4337
+ function validateEntryShape(entry) {
4338
+ const errors2 = [];
4339
+ if (!entry) {
4340
+ errors2.push(`Missing ${PLUGIN_NAME} entry in ${MARKETPLACE_PATH}.`);
4341
+ return errors2;
4342
+ }
4343
+ 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}.`);
4350
+ const policy = ensureObject(entry.policy);
4351
+ if (policy.installation !== "AVAILABLE")
4352
+ errors2.push("Codex marketplace entry policy.installation must be AVAILABLE.");
4353
+ if (policy.authentication !== "ON_INSTALL")
4354
+ errors2.push("Codex marketplace entry policy.authentication must be ON_INSTALL.");
4355
+ if (entry.category !== PLUGIN_CATEGORY)
4356
+ errors2.push(`Codex marketplace entry category must be ${PLUGIN_CATEGORY}.`);
4357
+ return errors2;
4358
+ }
4359
+ function runInit(dryRun) {
4360
+ const current = readJson(MARKETPLACE_PATH);
4361
+ const next = upsertEntry(current);
4362
+ const existingEntry = findEntry(current);
4363
+ if (!dryRun)
4364
+ writeJson(MARKETPLACE_PATH, next);
4365
+ 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
+ ]
4372
+ };
4373
+ }
4374
+ function runDoctor() {
4375
+ const errors2 = [];
4376
+ if (!fs2.existsSync(MARKETPLACE_PATH)) {
4377
+ return { location: MARKETPLACE_PATH, errors: [`Missing Codex personal marketplace: ${MARKETPLACE_PATH}`] };
4378
+ }
4379
+ const marketplace = readJson(MARKETPLACE_PATH);
4380
+ if (marketplace.name !== MARKETPLACE_NAME) {
4381
+ errors2.push(`Codex personal marketplace name must be ${MARKETPLACE_NAME}.`);
4382
+ }
4383
+ errors2.push(...validateEntryShape(findEntry(marketplace)));
4384
+ return { location: MARKETPLACE_PATH, errors: errors2 };
4385
+ }
4386
+ var codexAdapter = {
4387
+ target: "codex",
4388
+ mode: "install",
4389
+ runInstallInit: (_scope, dryRun) => runInit(dryRun),
4390
+ runInstallDoctor: () => runDoctor()
4391
+ };
4392
+
4267
4393
  // 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";
4268
4398
  var REPO_URL = "https://github.com/btspoony/mstar-harness.git";
4269
4399
  var CURSOR_PLUGIN_NAME = "mstar-harness";
4270
4400
  var CURSOR_PLUGIN_MARKER = ".cursor-plugin/plugin.json";
4271
4401
  function globalInstallPath() {
4272
- return path3.join(os.homedir(), ".cursor", "plugins", "local", CURSOR_PLUGIN_NAME);
4402
+ return path4.join(os2.homedir(), ".cursor", "plugins", "local", CURSOR_PLUGIN_NAME);
4273
4403
  }
4274
4404
  function projectInstallPath() {
4275
- return path3.join(resolveProjectRoot(), ".cursor", "plugins", CURSOR_PLUGIN_NAME);
4405
+ return path4.join(resolveProjectRoot(), ".cursor", "plugins", CURSOR_PLUGIN_NAME);
4276
4406
  }
4277
4407
  function ensureDir(dirPath, dryRun) {
4278
4408
  if (dryRun)
4279
4409
  return;
4280
- if (!fs.existsSync(dirPath))
4281
- fs.mkdirSync(dirPath, { recursive: true });
4410
+ if (!fs3.existsSync(dirPath))
4411
+ fs3.mkdirSync(dirPath, { recursive: true });
4282
4412
  }
4283
4413
  function runCommand(command, cwd, dryRun) {
4284
4414
  if (dryRun)
@@ -4288,12 +4418,12 @@ function runCommand(command, cwd, dryRun) {
4288
4418
  function globalInit(dryRun) {
4289
4419
  const location = globalInstallPath();
4290
4420
  const notes = [];
4291
- if (fs.existsSync(location)) {
4421
+ if (fs3.existsSync(location)) {
4292
4422
  notes.push(`Plugin already exists at ${location}`);
4293
4423
  return { location, notes };
4294
4424
  }
4295
- ensureDir(path3.dirname(location), dryRun);
4296
- runCommand(["git", "clone", REPO_URL, location], path3.dirname(location), dryRun);
4425
+ ensureDir(path4.dirname(location), dryRun);
4426
+ runCommand(["git", "clone", REPO_URL, location], path4.dirname(location), dryRun);
4297
4427
  notes.push(`Cloned ${REPO_URL} to ${location}`);
4298
4428
  return { location, notes };
4299
4429
  }
@@ -4301,12 +4431,12 @@ function projectInit(dryRun) {
4301
4431
  const projectRoot = resolveProjectRoot();
4302
4432
  const location = projectInstallPath();
4303
4433
  const notes = [];
4304
- if (fs.existsSync(location)) {
4434
+ if (fs3.existsSync(location)) {
4305
4435
  notes.push(`Submodule path already exists at ${location}`);
4306
4436
  return { location, notes };
4307
4437
  }
4308
4438
  runCommand(["git", "rev-parse", "--is-inside-work-tree"], projectRoot, dryRun);
4309
- ensureDir(path3.join(projectRoot, ".cursor", "plugins"), dryRun);
4439
+ ensureDir(path4.join(projectRoot, ".cursor", "plugins"), dryRun);
4310
4440
  runCommand(["git", "submodule", "add", REPO_URL, ".cursor/plugins/mstar-harness"], projectRoot, dryRun);
4311
4441
  notes.push("Added mstar-harness as git submodule at .cursor/plugins/mstar-harness");
4312
4442
  return { location, notes };
@@ -4314,12 +4444,12 @@ function projectInit(dryRun) {
4314
4444
  function globalDoctor() {
4315
4445
  const location = globalInstallPath();
4316
4446
  const errors2 = [];
4317
- if (!fs.existsSync(location)) {
4447
+ if (!fs3.existsSync(location)) {
4318
4448
  errors2.push(`Missing plugin directory: ${location}`);
4319
4449
  return { location, errors: errors2 };
4320
4450
  }
4321
- const marker = path3.join(location, CURSOR_PLUGIN_MARKER);
4322
- if (!fs.existsSync(marker)) {
4451
+ const marker = path4.join(location, CURSOR_PLUGIN_MARKER);
4452
+ if (!fs3.existsSync(marker)) {
4323
4453
  errors2.push(`Missing Cursor plugin marker file: ${marker}`);
4324
4454
  }
4325
4455
  return { location, errors: errors2 };
@@ -4328,15 +4458,15 @@ function projectDoctor() {
4328
4458
  const projectRoot = resolveProjectRoot();
4329
4459
  const location = projectInstallPath();
4330
4460
  const errors2 = [];
4331
- if (!fs.existsSync(location)) {
4461
+ if (!fs3.existsSync(location)) {
4332
4462
  errors2.push(`Missing submodule directory: ${location}`);
4333
4463
  }
4334
- const gitmodulesPath = path3.join(projectRoot, ".gitmodules");
4335
- if (!fs.existsSync(gitmodulesPath)) {
4464
+ const gitmodulesPath = path4.join(projectRoot, ".gitmodules");
4465
+ if (!fs3.existsSync(gitmodulesPath)) {
4336
4466
  errors2.push("Missing .gitmodules (expected cursor plugin submodule entry).");
4337
4467
  return { location, errors: errors2 };
4338
4468
  }
4339
- const gitmodules = fs.readFileSync(gitmodulesPath, "utf8");
4469
+ const gitmodules = fs3.readFileSync(gitmodulesPath, "utf8");
4340
4470
  if (!gitmodules.includes("path = .cursor/plugins/mstar-harness")) {
4341
4471
  errors2.push("Missing .cursor/plugins/mstar-harness entry in .gitmodules.");
4342
4472
  }
@@ -4358,8 +4488,8 @@ var cursorAdapter = {
4358
4488
  };
4359
4489
 
4360
4490
  // src/adapters/opencode.ts
4361
- import os2 from "node:os";
4362
- import path4 from "node:path";
4491
+ import os3 from "node:os";
4492
+ import path5 from "node:path";
4363
4493
  import { execFileSync as execFileSync2 } from "node:child_process";
4364
4494
  var OPENCODE_CONFIG_SCHEMA = "https://opencode.ai/config.json";
4365
4495
  var MSTAR_OPENCODE_PLUGIN = "@mstar-harness/opencode@latest";
@@ -4388,11 +4518,11 @@ function getOpencodeModels() {
4388
4518
  function resolveOpencodeConfigPath(scope, outputPath) {
4389
4519
  if (outputPath && outputPath.trim()) {
4390
4520
  const raw = outputPath.trim();
4391
- return path4.isAbsolute(raw) ? raw : path4.join(resolveProjectRoot(), raw);
4521
+ return path5.isAbsolute(raw) ? raw : path5.join(resolveProjectRoot(), raw);
4392
4522
  }
4393
4523
  if (scope === "global")
4394
- return path4.join(os2.homedir(), ".config", "opencode", "opencode.json");
4395
- return path4.join(resolveProjectRoot(), "opencode.json");
4524
+ return path5.join(os3.homedir(), ".config", "opencode", "opencode.json");
4525
+ return path5.join(resolveProjectRoot(), "opencode.json");
4396
4526
  }
4397
4527
  function ensureConfigSchema(config) {
4398
4528
  const next = ensureObject(config);
@@ -4486,7 +4616,8 @@ var opencodeAdapter = {
4486
4616
  // src/adapters/index.ts
4487
4617
  var adapters = {
4488
4618
  opencode: opencodeAdapter,
4489
- cursor: cursorAdapter
4619
+ cursor: cursorAdapter,
4620
+ codex: codexAdapter
4490
4621
  };
4491
4622
  function getAdapter(target) {
4492
4623
  const adapter = adapters[target];
@@ -4496,20 +4627,20 @@ function getAdapter(target) {
4496
4627
  }
4497
4628
 
4498
4629
  // src/types.ts
4499
- var SUPPORTED_TARGETS = ["opencode", "cursor"];
4630
+ var SUPPORTED_TARGETS = ["opencode", "cursor", "codex"];
4500
4631
 
4501
4632
  // src/utils.ts
4502
- import fs2 from "node:fs";
4503
- import path5 from "node:path";
4633
+ import fs4 from "node:fs";
4634
+ import path6 from "node:path";
4504
4635
  function parseCsv(raw) {
4505
4636
  if (!raw)
4506
4637
  return;
4507
4638
  return raw.split(",").map((item) => item.trim()).filter(Boolean);
4508
4639
  }
4509
- function readJson(filePath) {
4510
- if (!fs2.existsSync(filePath))
4640
+ function readJson2(filePath) {
4641
+ if (!fs4.existsSync(filePath))
4511
4642
  return {};
4512
- const content = fs2.readFileSync(filePath, "utf8").trim();
4643
+ const content = fs4.readFileSync(filePath, "utf8").trim();
4513
4644
  if (!content)
4514
4645
  return {};
4515
4646
  try {
@@ -4518,19 +4649,19 @@ function readJson(filePath) {
4518
4649
  throw new Error(`Invalid JSON in ${filePath}: ${error.message}`);
4519
4650
  }
4520
4651
  }
4521
- function writeJson(filePath, value) {
4522
- const parent = path5.dirname(filePath);
4523
- if (!fs2.existsSync(parent))
4524
- fs2.mkdirSync(parent, { recursive: true });
4525
- fs2.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}
4652
+ 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)}
4526
4657
  `, "utf8");
4527
4658
  }
4528
4659
 
4529
4660
  // src/index.ts
4530
- var packageJsonPath = path6.resolve(path6.dirname(fileURLToPath(import.meta.url)), "../package.json");
4661
+ var packageJsonPath = path7.resolve(path7.dirname(fileURLToPath(import.meta.url)), "../package.json");
4531
4662
  var packageVersion = (() => {
4532
4663
  try {
4533
- const parsed = JSON.parse(fs3.readFileSync(packageJsonPath, "utf8"));
4664
+ const parsed = JSON.parse(fs5.readFileSync(packageJsonPath, "utf8"));
4534
4665
  return parsed.version || "0.0.0";
4535
4666
  } catch {
4536
4667
  return "0.0.0";
@@ -4609,7 +4740,7 @@ async function resolveSelections(options, models) {
4609
4740
  })
4610
4741
  };
4611
4742
  }
4612
- async function runInit(options) {
4743
+ async function runInit2(options) {
4613
4744
  const target = options.target || (options.yes ? "opencode" : await pickTargetInteractive());
4614
4745
  const scope = options.scope || "project";
4615
4746
  const adapter = getAdapter(target);
@@ -4641,7 +4772,7 @@ async function runInit(options) {
4641
4772
  const configPath = adapter.resolveConfigPath?.(scope, options.output);
4642
4773
  if (!configPath)
4643
4774
  throw new Error(`Adapter ${target} does not implement config path resolution.`);
4644
- const current = readJson(configPath);
4775
+ const current = readJson2(configPath);
4645
4776
  const updated = adapter.mutateConfigForInit?.(current, assignments);
4646
4777
  if (!updated)
4647
4778
  throw new Error(`Adapter ${target} does not implement init mutation.`);
@@ -4653,8 +4784,8 @@ async function runInit(options) {
4653
4784
  - `)}`);
4654
4785
  }
4655
4786
  if (!options.dryRun) {
4656
- writeJson(configPath, updated);
4657
- const persistedErrors = adapter.validateConfig?.(readJson(configPath)) || [];
4787
+ writeJson2(configPath, updated);
4788
+ const persistedErrors = adapter.validateConfig?.(readJson2(configPath)) || [];
4658
4789
  if (persistedErrors.length) {
4659
4790
  throw new Error(`Post-write verification failed:
4660
4791
  - ${persistedErrors.join(`
@@ -4671,7 +4802,7 @@ async function runInit(options) {
4671
4802
  console.log(` - ${roleId}: ${modelId}`);
4672
4803
  }
4673
4804
  }
4674
- function runDoctor(options) {
4805
+ function runDoctor2(options) {
4675
4806
  const target = options.target || "opencode";
4676
4807
  const adapter = getAdapter(target);
4677
4808
  const scope = options.scope || "project";
@@ -4696,7 +4827,7 @@ function runDoctor(options) {
4696
4827
  if (!configPath) {
4697
4828
  throw new Error(`Adapter ${target} does not implement config doctor flow.`);
4698
4829
  }
4699
- const config = readJson(configPath);
4830
+ const config = readJson2(configPath);
4700
4831
  const errors2 = adapter.validateConfig?.(config) || [];
4701
4832
  console.log(`Config file: ${configPath}`);
4702
4833
  if (!errors2.length) {
@@ -4716,10 +4847,10 @@ function runDoctor(options) {
4716
4847
  }
4717
4848
  program2.name("mstar-harness").description("Morning Star harness CLI for target-based agent bootstrap").version(packageVersion);
4718
4849
  program2.command("init").description("Interactive/non-interactive setup for target agent bootstrap").option("-y, --yes", "Non-interactive mode").option("--target <target>", "Install target", "opencode").option("--scope <scope>", "Config scope: global|project (default: project)").option("--output <path>", "Config file path override, relative to project root").option("--dry-run", "Preview result without writing config").option("--pm-model <model>", "Model for project-manager").option("--strategic-models <a,b,c>", "Models for architect/product-manager/prompt-engineer").option("--dev-models <a,b,c>", "Models for fullstack-dev/fullstack-dev-2/frontend-dev").option("--qc-models <a,b,c>", "Models for qc trio").option("--other-models <a,b,c>", "Models for random assignment to remaining roles").action(async (options) => {
4719
- await runInit(options);
4850
+ await runInit2(options);
4720
4851
  });
4721
4852
  program2.command("doctor").description("Validate Morning Star setup for a target agent config").option("--target <target>", "Target agent for doctor checks", "opencode").option("--scope <scope>", "Config scope: global|project", "project").option("--output <path>", "Config file path override, relative to project root").action((options) => {
4722
- runDoctor(options);
4853
+ runDoctor2(options);
4723
4854
  });
4724
4855
  program2.parseAsync(process.argv).catch((error) => {
4725
4856
  console.error(import_picocolors.default.red(`Setup failed: ${error.message}`));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mstar-harness/cli",
3
- "version": "0.3.1",
4
- "description": "Morning Star harness installer CLI (OpenCode, Cursor).",
3
+ "version": "0.4.0",
4
+ "description": "Morning Star harness installer CLI (OpenCode, Cursor, Codex).",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",