@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.
- package/dist/mstar-harness.js +271 -110
- package/package.json +1 -1
package/dist/mstar-harness.js
CHANGED
|
@@ -2344,8 +2344,8 @@ var require_commander = __commonJS((exports) => {
|
|
|
2344
2344
|
});
|
|
2345
2345
|
|
|
2346
2346
|
// src/index.ts
|
|
2347
|
-
import
|
|
2348
|
-
import
|
|
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
|
|
4245
|
-
import
|
|
4246
|
-
import
|
|
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
|
|
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
|
-
|
|
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: "
|
|
4299
|
-
|
|
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 ${
|
|
4488
|
+
errors2.push(`Missing ${PLUGIN_NAME} entry in ${marketplacePath}.`);
|
|
4341
4489
|
return errors2;
|
|
4342
4490
|
}
|
|
4343
4491
|
const source = ensureObject(entry.source);
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
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
|
|
4360
|
-
|
|
4361
|
-
|
|
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(
|
|
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:
|
|
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
|
|
4376
|
-
|
|
4377
|
-
|
|
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(
|
|
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
|
-
|
|
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: (
|
|
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
|
|
4395
|
-
import
|
|
4396
|
-
import
|
|
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
|
|
4591
|
+
return path5.join(os3.homedir(), ".cursor", "plugins", "local", CURSOR_PLUGIN_NAME);
|
|
4403
4592
|
}
|
|
4404
4593
|
function projectInstallPath() {
|
|
4405
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
4435
|
-
|
|
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
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
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
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
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
|
|
4470
|
-
|
|
4471
|
-
|
|
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
|
|
4492
|
-
import
|
|
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
|
|
4682
|
+
return path6.isAbsolute(raw) ? raw : path6.join(resolveProjectRoot(), raw);
|
|
4522
4683
|
}
|
|
4523
4684
|
if (scope === "global")
|
|
4524
|
-
return
|
|
4525
|
-
return
|
|
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
|
|
4634
|
-
import
|
|
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 (!
|
|
4802
|
+
if (!fs5.existsSync(filePath))
|
|
4642
4803
|
return {};
|
|
4643
|
-
const content =
|
|
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 =
|
|
4654
|
-
if (!
|
|
4655
|
-
|
|
4656
|
-
|
|
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 =
|
|
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(
|
|
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";
|