anymorph 0.2.0 → 0.2.1
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/README.md +46 -2
- package/dist/index.js +598 -26
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -27,6 +27,7 @@ anymorph check yourdomain.com
|
|
|
27
27
|
anymorph workspaces
|
|
28
28
|
|
|
29
29
|
# Prepare and validate local GEO strategy artifacts
|
|
30
|
+
anymorph geo init --repo .
|
|
30
31
|
anymorph geo prepare yourdomain.com
|
|
31
32
|
anymorph geo validate 20260517T074200Z --repo .
|
|
32
33
|
```
|
|
@@ -122,12 +123,55 @@ repos under `~/.anymorph/repos/{domain}`. Use `--repo` to target an explicit
|
|
|
122
123
|
local checkout.
|
|
123
124
|
|
|
124
125
|
```bash
|
|
126
|
+
anymorph geo init --repo ~/works/yourdomain.com
|
|
125
127
|
anymorph geo prepare yourdomain.com
|
|
126
128
|
anymorph geo prepare yourdomain.com --repo ~/works/yourdomain.com
|
|
127
129
|
```
|
|
128
130
|
|
|
129
|
-
|
|
130
|
-
`
|
|
131
|
+
`prepare` syncs the GEO scaffold first unless `--skip-scaffold` is passed. It
|
|
132
|
+
then initializes `agent/runs/{runId}/manifest.json`, `context.json`,
|
|
133
|
+
`intents.json`, `crawl_logs.json`, and `status.json`. The agent fills the
|
|
134
|
+
content artifacts separately.
|
|
135
|
+
|
|
136
|
+
### `anymorph geo init`
|
|
137
|
+
|
|
138
|
+
Initialize a local tenant repo for GEO work.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
anymorph geo init --repo .
|
|
142
|
+
anymorph geo init --repo . --skills-source /path/to/anymorph-geo-skills/plugins/anymorph-geo/skills
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
This installs managed skills under `.claude/skills` and `.agents/skills`, writes
|
|
146
|
+
`agent/contracts`, creates `agent/runs` and `agent/archive`, and creates missing
|
|
147
|
+
memory files without overwriting existing `agent/BRAND.md`, `agent/STRATEGY.md`,
|
|
148
|
+
or `agent/LEARNINGS.md`.
|
|
149
|
+
|
|
150
|
+
### `anymorph geo sync`
|
|
151
|
+
|
|
152
|
+
Sync managed GEO files without touching memory or run artifacts.
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
anymorph geo sync --repo .
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### `anymorph geo doctor`
|
|
159
|
+
|
|
160
|
+
Check whether the local scaffold still matches the installed skillpack.
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
anymorph geo doctor --repo .
|
|
164
|
+
anymorph geo doctor --repo . --json
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### `anymorph geo intents <runId>`
|
|
168
|
+
|
|
169
|
+
Show the pre-fetched intent list for a local GEO strategy run.
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
anymorph geo intents 20260517T074200Z --repo .
|
|
173
|
+
anymorph geo intents 20260517T074200Z --repo . --json
|
|
174
|
+
```
|
|
131
175
|
|
|
132
176
|
### `anymorph geo validate <runId>`
|
|
133
177
|
|
package/dist/index.js
CHANGED
|
@@ -5430,14 +5430,14 @@ var baseOpen = async (options) => {
|
|
|
5430
5430
|
}
|
|
5431
5431
|
const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
|
|
5432
5432
|
if (options.wait) {
|
|
5433
|
-
return new Promise((
|
|
5433
|
+
return new Promise((resolve3, reject) => {
|
|
5434
5434
|
subprocess.once("error", reject);
|
|
5435
5435
|
subprocess.once("close", (exitCode) => {
|
|
5436
5436
|
if (!options.allowNonzeroExitCode && exitCode > 0) {
|
|
5437
5437
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
5438
5438
|
return;
|
|
5439
5439
|
}
|
|
5440
|
-
|
|
5440
|
+
resolve3(subprocess);
|
|
5441
5441
|
});
|
|
5442
5442
|
});
|
|
5443
5443
|
}
|
|
@@ -7051,7 +7051,7 @@ async function loginCommand() {
|
|
|
7051
7051
|
process.exit(1);
|
|
7052
7052
|
}
|
|
7053
7053
|
function sleep(ms) {
|
|
7054
|
-
return new Promise((
|
|
7054
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
7055
7055
|
}
|
|
7056
7056
|
|
|
7057
7057
|
// src/lib/display.ts
|
|
@@ -7181,7 +7181,7 @@ async function checkCommand(domain, opts) {
|
|
|
7181
7181
|
process.exit(1);
|
|
7182
7182
|
}
|
|
7183
7183
|
function sleep2(ms) {
|
|
7184
|
-
return new Promise((
|
|
7184
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
7185
7185
|
}
|
|
7186
7186
|
|
|
7187
7187
|
// src/commands/status.ts
|
|
@@ -7246,6 +7246,10 @@ async function workspacesCommand(opts) {
|
|
|
7246
7246
|
console.log();
|
|
7247
7247
|
}
|
|
7248
7248
|
|
|
7249
|
+
// src/commands/geo.ts
|
|
7250
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
7251
|
+
import { join as join6 } from "node:path";
|
|
7252
|
+
|
|
7249
7253
|
// src/geo/package-writer.ts
|
|
7250
7254
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
7251
7255
|
import { join as join2 } from "node:path";
|
|
@@ -7261,16 +7265,57 @@ async function writeGeoRunPackage(input) {
|
|
|
7261
7265
|
branch: input.package.branch,
|
|
7262
7266
|
execution: input.package.execution,
|
|
7263
7267
|
mode: input.package.mode,
|
|
7264
|
-
signals: input.package.signals,
|
|
7268
|
+
signals: buildContextSignals(input.package.signals),
|
|
7265
7269
|
systemContext: input.package.systemContext,
|
|
7266
|
-
schemaCatalog: input.package.schemaCatalog
|
|
7270
|
+
schemaCatalog: input.package.schemaCatalog,
|
|
7271
|
+
intentsPath: `agent/runs/${input.package.runId}/intents.json`
|
|
7267
7272
|
});
|
|
7273
|
+
await writeJson(join2(runDir, "intents.json"), buildIntentList(input.package));
|
|
7274
|
+
await writeJson(
|
|
7275
|
+
join2(runDir, "crawl_logs.json"),
|
|
7276
|
+
buildCrawlLogAggregate(input.package)
|
|
7277
|
+
);
|
|
7268
7278
|
await writeJson(join2(runDir, "status.json"), {
|
|
7269
7279
|
status: "pending",
|
|
7270
7280
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7271
7281
|
});
|
|
7272
7282
|
return { runDir };
|
|
7273
7283
|
}
|
|
7284
|
+
function buildIntentList(pkg) {
|
|
7285
|
+
return {
|
|
7286
|
+
runId: pkg.runId,
|
|
7287
|
+
workspaceId: pkg.workspaceId,
|
|
7288
|
+
workspaceDomain: pkg.workspaceDomain,
|
|
7289
|
+
source: "signals.intents",
|
|
7290
|
+
intents: extractIntents(pkg.signals)
|
|
7291
|
+
};
|
|
7292
|
+
}
|
|
7293
|
+
function buildCrawlLogAggregate(pkg) {
|
|
7294
|
+
return {
|
|
7295
|
+
runId: pkg.runId,
|
|
7296
|
+
workspaceId: pkg.workspaceId,
|
|
7297
|
+
workspaceDomain: pkg.workspaceDomain,
|
|
7298
|
+
source: "signals.crawlLogs",
|
|
7299
|
+
crawlLogs: extractCrawlLogs(pkg.signals)
|
|
7300
|
+
};
|
|
7301
|
+
}
|
|
7302
|
+
function extractIntents(signals2) {
|
|
7303
|
+
if (!signals2 || typeof signals2 !== "object") return [];
|
|
7304
|
+
const intents = signals2.intents;
|
|
7305
|
+
return Array.isArray(intents) ? intents : [];
|
|
7306
|
+
}
|
|
7307
|
+
function extractCrawlLogs(signals2) {
|
|
7308
|
+
if (!signals2 || typeof signals2 !== "object") return null;
|
|
7309
|
+
return signals2.crawlLogs ?? null;
|
|
7310
|
+
}
|
|
7311
|
+
function buildContextSignals(signals2) {
|
|
7312
|
+
if (!signals2 || typeof signals2 !== "object" || Array.isArray(signals2))
|
|
7313
|
+
return signals2;
|
|
7314
|
+
const record = signals2;
|
|
7315
|
+
const { crawlLogs: _crawlLogs, ...rest } = record;
|
|
7316
|
+
const { crawlLogsSummary: _crawlLogsSummary, ...contextSignals } = rest;
|
|
7317
|
+
return contextSignals;
|
|
7318
|
+
}
|
|
7274
7319
|
async function writeJson(path2, value) {
|
|
7275
7320
|
await writeFile(path2, `${JSON.stringify(value, null, 2)}
|
|
7276
7321
|
`);
|
|
@@ -7361,22 +7406,317 @@ async function gitOutput(repoPath, args) {
|
|
|
7361
7406
|
}
|
|
7362
7407
|
}
|
|
7363
7408
|
|
|
7409
|
+
// src/geo/scaffold.ts
|
|
7410
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
7411
|
+
import {
|
|
7412
|
+
cp,
|
|
7413
|
+
mkdir as mkdir3,
|
|
7414
|
+
readdir as readdir2,
|
|
7415
|
+
readFile,
|
|
7416
|
+
rm,
|
|
7417
|
+
stat as stat2,
|
|
7418
|
+
writeFile as writeFile2
|
|
7419
|
+
} from "node:fs/promises";
|
|
7420
|
+
import { createHash } from "node:crypto";
|
|
7421
|
+
import { dirname, join as join4, relative, resolve as resolve2 } from "node:path";
|
|
7422
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7423
|
+
import { promisify as promisify7 } from "node:util";
|
|
7424
|
+
var execFileAsync6 = promisify7(execFile7);
|
|
7425
|
+
var DEFAULT_SKILLPACK_RELATIVE_PATH = join4(
|
|
7426
|
+
"apps",
|
|
7427
|
+
"anymorph-geo-skills",
|
|
7428
|
+
"plugins",
|
|
7429
|
+
"anymorph-geo",
|
|
7430
|
+
"skills"
|
|
7431
|
+
);
|
|
7432
|
+
var MANAGED_PATHS = [".claude/skills", ".agents/skills", "agent/contracts"];
|
|
7433
|
+
async function initGeoScaffold(input) {
|
|
7434
|
+
return applyGeoScaffold(input, { createMemory: true });
|
|
7435
|
+
}
|
|
7436
|
+
async function syncGeoScaffold(input) {
|
|
7437
|
+
return applyGeoScaffold(input, { createMemory: false });
|
|
7438
|
+
}
|
|
7439
|
+
async function doctorGeoScaffold(input) {
|
|
7440
|
+
const repoPath = resolve2(input.repoPath);
|
|
7441
|
+
const skillsSourceDir = await resolveSkillSourceDir(input.skillsSourceDir);
|
|
7442
|
+
const manifestHash = await hashDirectory(skillsSourceDir);
|
|
7443
|
+
const problems = [];
|
|
7444
|
+
for (const target of [".claude/skills", ".agents/skills"]) {
|
|
7445
|
+
await compareDirectories(skillsSourceDir, join4(repoPath, target), target, problems);
|
|
7446
|
+
}
|
|
7447
|
+
for (const path2 of [
|
|
7448
|
+
"agent/BRAND.md",
|
|
7449
|
+
"agent/STRATEGY.md",
|
|
7450
|
+
"agent/LEARNINGS.md",
|
|
7451
|
+
"agent/runs/.gitkeep",
|
|
7452
|
+
"agent/archive/.gitkeep",
|
|
7453
|
+
"agent/skillpack.json"
|
|
7454
|
+
]) {
|
|
7455
|
+
if (!await exists(join4(repoPath, path2))) problems.push(`Missing ${path2}`);
|
|
7456
|
+
}
|
|
7457
|
+
const lock = await readJson(join4(repoPath, "agent", "skillpack.json"));
|
|
7458
|
+
if (lock && typeof lock === "object") {
|
|
7459
|
+
const actual = lock.manifestHash;
|
|
7460
|
+
if (actual !== manifestHash) {
|
|
7461
|
+
problems.push("agent/skillpack.json manifestHash does not match current skillpack");
|
|
7462
|
+
}
|
|
7463
|
+
}
|
|
7464
|
+
return {
|
|
7465
|
+
ok: problems.length === 0,
|
|
7466
|
+
repoPath,
|
|
7467
|
+
skillsSourceDir,
|
|
7468
|
+
changed: [],
|
|
7469
|
+
problems,
|
|
7470
|
+
manifestHash
|
|
7471
|
+
};
|
|
7472
|
+
}
|
|
7473
|
+
async function resolveSkillSourceDir(explicit) {
|
|
7474
|
+
if (explicit) {
|
|
7475
|
+
const resolved = resolve2(explicit);
|
|
7476
|
+
await ensureDirectory2(resolved);
|
|
7477
|
+
return resolved;
|
|
7478
|
+
}
|
|
7479
|
+
for (const root of searchRoots()) {
|
|
7480
|
+
const candidate = join4(root, DEFAULT_SKILLPACK_RELATIVE_PATH);
|
|
7481
|
+
if (await isDirectory(candidate)) return candidate;
|
|
7482
|
+
}
|
|
7483
|
+
throw new Error(
|
|
7484
|
+
`Could not find GEO skillpack. Pass --skills-source <path> pointing to ${DEFAULT_SKILLPACK_RELATIVE_PATH}.`
|
|
7485
|
+
);
|
|
7486
|
+
}
|
|
7487
|
+
async function applyGeoScaffold(input, options) {
|
|
7488
|
+
const repoPath = resolve2(input.repoPath);
|
|
7489
|
+
const skillsSourceDir = await resolveSkillSourceDir(input.skillsSourceDir);
|
|
7490
|
+
await ensureDirectory2(repoPath);
|
|
7491
|
+
const changed = [];
|
|
7492
|
+
const problems = [];
|
|
7493
|
+
const manifestHash = await hashDirectory(skillsSourceDir);
|
|
7494
|
+
await syncManagedDirectory(skillsSourceDir, join4(repoPath, ".claude", "skills"), changed);
|
|
7495
|
+
await syncManagedDirectory(skillsSourceDir, join4(repoPath, ".agents", "skills"), changed);
|
|
7496
|
+
await syncContracts(repoPath, changed);
|
|
7497
|
+
await ensureRunDirs(repoPath, changed);
|
|
7498
|
+
if (options.createMemory) await ensureMemoryFiles(repoPath, changed);
|
|
7499
|
+
await writeSkillpackLock(
|
|
7500
|
+
{
|
|
7501
|
+
repoPath,
|
|
7502
|
+
skillsSourceDir,
|
|
7503
|
+
ref: input.ref ?? "local",
|
|
7504
|
+
manifestHash
|
|
7505
|
+
},
|
|
7506
|
+
changed
|
|
7507
|
+
);
|
|
7508
|
+
return {
|
|
7509
|
+
ok: problems.length === 0,
|
|
7510
|
+
repoPath,
|
|
7511
|
+
skillsSourceDir,
|
|
7512
|
+
changed,
|
|
7513
|
+
problems,
|
|
7514
|
+
manifestHash
|
|
7515
|
+
};
|
|
7516
|
+
}
|
|
7517
|
+
async function syncManagedDirectory(sourceDir, targetDir, changed) {
|
|
7518
|
+
await rm(targetDir, { recursive: true, force: true });
|
|
7519
|
+
await mkdir3(dirname(targetDir), { recursive: true });
|
|
7520
|
+
await cp(sourceDir, targetDir, { recursive: true });
|
|
7521
|
+
changed.push(relative(process.cwd(), targetDir) || targetDir);
|
|
7522
|
+
}
|
|
7523
|
+
async function syncContracts(repoPath, changed) {
|
|
7524
|
+
const contractsDir = join4(repoPath, "agent", "contracts");
|
|
7525
|
+
await mkdir3(contractsDir, { recursive: true });
|
|
7526
|
+
await writeIfChanged(
|
|
7527
|
+
join4(contractsDir, "actions.schema.json"),
|
|
7528
|
+
`${JSON.stringify(ACTIONS_SCHEMA, null, 2)}
|
|
7529
|
+
`,
|
|
7530
|
+
changed
|
|
7531
|
+
);
|
|
7532
|
+
await writeIfChanged(
|
|
7533
|
+
join4(contractsDir, "memory-item.schema.json"),
|
|
7534
|
+
`${JSON.stringify(MEMORY_ITEM_SCHEMA, null, 2)}
|
|
7535
|
+
`,
|
|
7536
|
+
changed
|
|
7537
|
+
);
|
|
7538
|
+
}
|
|
7539
|
+
async function ensureRunDirs(repoPath, changed) {
|
|
7540
|
+
for (const path2 of [join4(repoPath, "agent", "runs"), join4(repoPath, "agent", "archive")]) {
|
|
7541
|
+
await mkdir3(path2, { recursive: true });
|
|
7542
|
+
await writeIfMissing(join4(path2, ".gitkeep"), "", changed);
|
|
7543
|
+
}
|
|
7544
|
+
}
|
|
7545
|
+
async function ensureMemoryFiles(repoPath, changed) {
|
|
7546
|
+
await mkdir3(join4(repoPath, "agent"), { recursive: true });
|
|
7547
|
+
await writeIfMissing(
|
|
7548
|
+
join4(repoPath, "agent", "BRAND.md"),
|
|
7549
|
+
"# Brand\n\nInitial local scaffold. Replace with workspace brand context before serious runs.\n",
|
|
7550
|
+
changed
|
|
7551
|
+
);
|
|
7552
|
+
await writeIfMissing(
|
|
7553
|
+
join4(repoPath, "agent", "STRATEGY.md"),
|
|
7554
|
+
"# GEO Strategy\n\n## Current Priorities\n\n- Initial local scaffold.\n",
|
|
7555
|
+
changed
|
|
7556
|
+
);
|
|
7557
|
+
await writeIfMissing(
|
|
7558
|
+
join4(repoPath, "agent", "LEARNINGS.md"),
|
|
7559
|
+
"# Learnings\n\n## Stable Learnings\n\n- Initial local scaffold.\n",
|
|
7560
|
+
changed
|
|
7561
|
+
);
|
|
7562
|
+
}
|
|
7563
|
+
async function writeSkillpackLock(input, changed) {
|
|
7564
|
+
const sourceCommit = await gitOutput2(input.skillsSourceDir, ["rev-parse", "HEAD"]);
|
|
7565
|
+
const lock = {
|
|
7566
|
+
name: "anymorph-geo",
|
|
7567
|
+
source: input.skillsSourceDir,
|
|
7568
|
+
sourceRepo: "opactor-dev/anymorph-geo-skills",
|
|
7569
|
+
requestedRef: input.ref,
|
|
7570
|
+
resolvedCommit: sourceCommit,
|
|
7571
|
+
manifestHash: input.manifestHash,
|
|
7572
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7573
|
+
managedPaths: MANAGED_PATHS
|
|
7574
|
+
};
|
|
7575
|
+
await writeIfChanged(
|
|
7576
|
+
join4(input.repoPath, "agent", "skillpack.json"),
|
|
7577
|
+
`${JSON.stringify(lock, null, 2)}
|
|
7578
|
+
`,
|
|
7579
|
+
changed
|
|
7580
|
+
);
|
|
7581
|
+
}
|
|
7582
|
+
async function compareDirectories(sourceDir, targetDir, displayPrefix, problems) {
|
|
7583
|
+
const sourceFiles = await listFiles(sourceDir);
|
|
7584
|
+
const targetFiles = await listFiles(targetDir);
|
|
7585
|
+
const targetSet = new Set(targetFiles);
|
|
7586
|
+
for (const file of sourceFiles) {
|
|
7587
|
+
const source = await readFile(join4(sourceDir, file), "utf8");
|
|
7588
|
+
const targetPath = join4(targetDir, file);
|
|
7589
|
+
if (!await exists(targetPath)) {
|
|
7590
|
+
problems.push(`Missing ${displayPrefix}/${file}`);
|
|
7591
|
+
continue;
|
|
7592
|
+
}
|
|
7593
|
+
const target = await readFile(targetPath, "utf8");
|
|
7594
|
+
if (source !== target) problems.push(`Drifted ${displayPrefix}/${file}`);
|
|
7595
|
+
targetSet.delete(file);
|
|
7596
|
+
}
|
|
7597
|
+
for (const extra of targetSet) problems.push(`Extra managed file ${displayPrefix}/${extra}`);
|
|
7598
|
+
}
|
|
7599
|
+
async function hashDirectory(dir) {
|
|
7600
|
+
const files = await listFiles(dir);
|
|
7601
|
+
const hash = createHash("sha256");
|
|
7602
|
+
for (const file of files) {
|
|
7603
|
+
hash.update(file);
|
|
7604
|
+
hash.update("\0");
|
|
7605
|
+
hash.update(await readFile(join4(dir, file)));
|
|
7606
|
+
hash.update("\0");
|
|
7607
|
+
}
|
|
7608
|
+
return `sha256:${hash.digest("hex")}`;
|
|
7609
|
+
}
|
|
7610
|
+
async function listFiles(dir, base = "") {
|
|
7611
|
+
let entries;
|
|
7612
|
+
try {
|
|
7613
|
+
entries = await readdir2(join4(dir, base), { withFileTypes: true });
|
|
7614
|
+
} catch {
|
|
7615
|
+
return [];
|
|
7616
|
+
}
|
|
7617
|
+
const files = [];
|
|
7618
|
+
for (const entry of entries) {
|
|
7619
|
+
const name = String(entry.name);
|
|
7620
|
+
const path2 = base ? `${base}/${name}` : name;
|
|
7621
|
+
if (entry.isDirectory()) files.push(...await listFiles(dir, path2));
|
|
7622
|
+
else if (entry.isFile()) files.push(path2);
|
|
7623
|
+
}
|
|
7624
|
+
return files.sort();
|
|
7625
|
+
}
|
|
7626
|
+
async function writeIfMissing(path2, content, changed) {
|
|
7627
|
+
if (await exists(path2)) return;
|
|
7628
|
+
await mkdir3(dirname(path2), { recursive: true });
|
|
7629
|
+
await writeFile2(path2, content);
|
|
7630
|
+
changed.push(relative(process.cwd(), path2) || path2);
|
|
7631
|
+
}
|
|
7632
|
+
async function writeIfChanged(path2, content, changed) {
|
|
7633
|
+
const current = await readFile(path2, "utf8").catch(() => null);
|
|
7634
|
+
if (current === content) return;
|
|
7635
|
+
await mkdir3(dirname(path2), { recursive: true });
|
|
7636
|
+
await writeFile2(path2, content);
|
|
7637
|
+
changed.push(relative(process.cwd(), path2) || path2);
|
|
7638
|
+
}
|
|
7639
|
+
async function readJson(path2) {
|
|
7640
|
+
const raw = await readFile(path2, "utf8").catch(() => null);
|
|
7641
|
+
if (!raw) return null;
|
|
7642
|
+
try {
|
|
7643
|
+
return JSON.parse(raw);
|
|
7644
|
+
} catch {
|
|
7645
|
+
return null;
|
|
7646
|
+
}
|
|
7647
|
+
}
|
|
7648
|
+
async function gitOutput2(cwd, args) {
|
|
7649
|
+
try {
|
|
7650
|
+
const { stdout } = await execFileAsync6("git", args, { cwd });
|
|
7651
|
+
return stdout.trim() || null;
|
|
7652
|
+
} catch {
|
|
7653
|
+
return null;
|
|
7654
|
+
}
|
|
7655
|
+
}
|
|
7656
|
+
function searchRoots() {
|
|
7657
|
+
const roots = /* @__PURE__ */ new Set();
|
|
7658
|
+
let cwd = resolve2(process.cwd());
|
|
7659
|
+
for (; ; ) {
|
|
7660
|
+
roots.add(cwd);
|
|
7661
|
+
const parent = dirname(cwd);
|
|
7662
|
+
if (parent === cwd) break;
|
|
7663
|
+
cwd = parent;
|
|
7664
|
+
}
|
|
7665
|
+
let here = dirname(fileURLToPath2(import.meta.url));
|
|
7666
|
+
for (; ; ) {
|
|
7667
|
+
roots.add(here);
|
|
7668
|
+
const parent = dirname(here);
|
|
7669
|
+
if (parent === here) break;
|
|
7670
|
+
here = parent;
|
|
7671
|
+
}
|
|
7672
|
+
return [...roots];
|
|
7673
|
+
}
|
|
7674
|
+
async function ensureDirectory2(path2) {
|
|
7675
|
+
const s = await stat2(path2);
|
|
7676
|
+
if (!s.isDirectory()) throw new Error(`${path2} is not a directory`);
|
|
7677
|
+
}
|
|
7678
|
+
async function isDirectory(path2) {
|
|
7679
|
+
return stat2(path2).then((s) => s.isDirectory()).catch(() => false);
|
|
7680
|
+
}
|
|
7681
|
+
async function exists(path2) {
|
|
7682
|
+
return stat2(path2).then(() => true).catch(() => false);
|
|
7683
|
+
}
|
|
7684
|
+
var ACTIONS_SCHEMA = {
|
|
7685
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
7686
|
+
title: "Anymorph GEO actions artifact",
|
|
7687
|
+
type: "object",
|
|
7688
|
+
required: ["runId", "actions"],
|
|
7689
|
+
properties: {
|
|
7690
|
+
runId: { type: "string" },
|
|
7691
|
+
actions: { type: "array", items: { type: "object" } },
|
|
7692
|
+
artifactPaths: { type: "array", items: { type: "string" } }
|
|
7693
|
+
}
|
|
7694
|
+
};
|
|
7695
|
+
var MEMORY_ITEM_SCHEMA = {
|
|
7696
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
7697
|
+
title: "Anymorph GEO memory item",
|
|
7698
|
+
type: "object",
|
|
7699
|
+
required: ["summary"],
|
|
7700
|
+
properties: {
|
|
7701
|
+
summary: { type: "string" },
|
|
7702
|
+
evidence: { type: "array", items: { type: "string" } }
|
|
7703
|
+
}
|
|
7704
|
+
};
|
|
7705
|
+
|
|
7364
7706
|
// src/geo/validate.ts
|
|
7365
|
-
import { access, readFile } from "node:fs/promises";
|
|
7366
|
-
import { join as
|
|
7707
|
+
import { access, readFile as readFile2 } from "node:fs/promises";
|
|
7708
|
+
import { join as join5 } from "node:path";
|
|
7367
7709
|
async function validateGeoRunArtifacts(input) {
|
|
7368
|
-
const runDir =
|
|
7710
|
+
const runDir = join5(input.repoPath, "agent", "runs", input.runId);
|
|
7369
7711
|
const errors = [];
|
|
7370
7712
|
const warnings = [];
|
|
7371
|
-
const manifest = await
|
|
7372
|
-
const actions = await
|
|
7373
|
-
const status = await
|
|
7374
|
-
|
|
7375
|
-
await requireFile(join4(runDir, "rationale.md"), errors);
|
|
7713
|
+
const manifest = await readJson2(join5(runDir, "manifest.json"), errors);
|
|
7714
|
+
const actions = await readJson2(join5(runDir, "actions.json"), errors);
|
|
7715
|
+
const status = await readJson2(join5(runDir, "status.json"), errors);
|
|
7716
|
+
await requireFile(join5(runDir, "rationale.md"), errors);
|
|
7376
7717
|
if (manifest) validateManifest(manifest, input.runId, errors);
|
|
7377
7718
|
if (actions) validateActions(actions, input.runId, errors);
|
|
7378
7719
|
if (status) validateStatus(status, errors);
|
|
7379
|
-
if (skillUsage) validateSkillUsage(skillUsage, errors);
|
|
7380
7720
|
return {
|
|
7381
7721
|
ok: errors.length === 0,
|
|
7382
7722
|
errors,
|
|
@@ -7391,9 +7731,9 @@ async function requireFile(path2, errors) {
|
|
|
7391
7731
|
errors.push(`Missing ${shortPath(path2)}`);
|
|
7392
7732
|
}
|
|
7393
7733
|
}
|
|
7394
|
-
async function
|
|
7734
|
+
async function readJson2(path2, errors) {
|
|
7395
7735
|
try {
|
|
7396
|
-
const raw = await
|
|
7736
|
+
const raw = await readFile2(path2, "utf8");
|
|
7397
7737
|
return JSON.parse(raw);
|
|
7398
7738
|
} catch (err) {
|
|
7399
7739
|
const message = err instanceof SyntaxError ? "Invalid JSON" : "Missing";
|
|
@@ -7436,6 +7776,7 @@ function validateActions(value, runId, errors) {
|
|
|
7436
7776
|
}
|
|
7437
7777
|
if (value.runId !== runId) errors.push(`actions.json runId must be ${runId}`);
|
|
7438
7778
|
if (!Array.isArray(value.actions)) errors.push("actions.json actions must be an array");
|
|
7779
|
+
if (Array.isArray(value.actions)) validateActionItems(value.actions, errors);
|
|
7439
7780
|
if (!Array.isArray(value.artifactPaths)) {
|
|
7440
7781
|
errors.push("actions.json artifactPaths must be an array");
|
|
7441
7782
|
return;
|
|
@@ -7450,19 +7791,100 @@ function validateActions(value, runId, errors) {
|
|
|
7450
7791
|
}
|
|
7451
7792
|
}
|
|
7452
7793
|
}
|
|
7453
|
-
function
|
|
7454
|
-
if (
|
|
7455
|
-
|
|
7794
|
+
function validateActionItems(actions, errors) {
|
|
7795
|
+
if (actions.length > 20) errors.push("actions.json actions must contain at most 20 items");
|
|
7796
|
+
actions.forEach((action, index) => validateActionItem(action, index, errors));
|
|
7797
|
+
}
|
|
7798
|
+
function validateActionItem(action, index, errors) {
|
|
7799
|
+
const prefix = `actions.json actions[${index}]`;
|
|
7800
|
+
if (!isRecord(action)) {
|
|
7801
|
+
errors.push(`${prefix} must be an object`);
|
|
7456
7802
|
return;
|
|
7457
7803
|
}
|
|
7458
|
-
|
|
7804
|
+
requireString(action, "id", prefix, errors);
|
|
7805
|
+
requireEnum(action, "operation", ["create", "update"], prefix, errors);
|
|
7806
|
+
requireEnum(action, "surface", ["on_page", "off_page"], prefix, errors);
|
|
7807
|
+
requireEnum(action, "assetType", ["brand_owned", "geo_page", "third_party"], prefix, errors);
|
|
7808
|
+
validateIntentRef(action, prefix, errors);
|
|
7809
|
+
validateTarget(action, prefix, errors);
|
|
7810
|
+
requireString(action, "objective", prefix, errors);
|
|
7811
|
+
requireString(action, "changeBrief", prefix, errors);
|
|
7812
|
+
requireString(action, "reason", prefix, errors);
|
|
7813
|
+
requireString(action, "expectedOutcome", prefix, errors);
|
|
7814
|
+
validateEvidence(action.evidence, prefix, errors);
|
|
7815
|
+
requireEnum(action, "priority", ["high", "medium", "low"], prefix, errors);
|
|
7816
|
+
requireEnum(action, "confidence", ["high", "medium", "low"], prefix, errors);
|
|
7817
|
+
}
|
|
7818
|
+
function validateIntentRef(action, prefix, errors) {
|
|
7819
|
+
if (action.intentId !== null && typeof action.intentId !== "string") {
|
|
7820
|
+
errors.push(`${prefix}.intentId must be a string or null`);
|
|
7821
|
+
}
|
|
7822
|
+
if (action.intentId === null) {
|
|
7823
|
+
if (!isRecord(action.proposedIntent)) {
|
|
7824
|
+
errors.push(`${prefix}.proposedIntent is required when intentId is null`);
|
|
7825
|
+
return;
|
|
7826
|
+
}
|
|
7827
|
+
requireString(action.proposedIntent, "name", `${prefix}.proposedIntent`, errors);
|
|
7828
|
+
requireString(action.proposedIntent, "reason", `${prefix}.proposedIntent`, errors);
|
|
7829
|
+
}
|
|
7830
|
+
}
|
|
7831
|
+
function validateTarget(action, prefix, errors) {
|
|
7832
|
+
const target = action.target;
|
|
7833
|
+
if (!isRecord(target)) {
|
|
7834
|
+
errors.push(`${prefix}.target must be an object`);
|
|
7835
|
+
return;
|
|
7836
|
+
}
|
|
7837
|
+
if (action.operation === "create") {
|
|
7838
|
+
if (typeof target.url === "string" && target.url.length > 0) {
|
|
7839
|
+
errors.push(`${prefix}.target.url must be omitted for create actions`);
|
|
7840
|
+
}
|
|
7841
|
+
if (typeof target.pageId === "string" && target.pageId.length > 0) {
|
|
7842
|
+
errors.push(`${prefix}.target.pageId must be omitted for create actions`);
|
|
7843
|
+
}
|
|
7844
|
+
if (!Array.isArray(target.queryCluster) && typeof target.description !== "string") {
|
|
7845
|
+
errors.push(`${prefix}.target needs queryCluster or description for create actions`);
|
|
7846
|
+
}
|
|
7847
|
+
}
|
|
7848
|
+
if (action.operation === "update") {
|
|
7849
|
+
const hasUrl = typeof target.url === "string" && target.url.length > 0;
|
|
7850
|
+
const hasPageId = typeof target.pageId === "string" && target.pageId.length > 0;
|
|
7851
|
+
if (!hasUrl && !hasPageId) {
|
|
7852
|
+
errors.push(`${prefix}.target needs pageId or url for update actions`);
|
|
7853
|
+
}
|
|
7854
|
+
}
|
|
7855
|
+
}
|
|
7856
|
+
function validateEvidence(value, prefix, errors) {
|
|
7857
|
+
if (!Array.isArray(value)) {
|
|
7858
|
+
errors.push(`${prefix}.evidence must be an array`);
|
|
7859
|
+
return;
|
|
7860
|
+
}
|
|
7861
|
+
value.forEach((item, index) => {
|
|
7862
|
+
const evidencePrefix = `${prefix}.evidence[${index}]`;
|
|
7863
|
+
if (!isRecord(item)) {
|
|
7864
|
+
errors.push(`${evidencePrefix} must be an object`);
|
|
7865
|
+
return;
|
|
7866
|
+
}
|
|
7867
|
+
requireEnum(item, "type", ["signal", "workspace", "source", "research"], evidencePrefix, errors);
|
|
7868
|
+
requireString(item, "ref", evidencePrefix, errors);
|
|
7869
|
+
requireString(item, "summary", evidencePrefix, errors);
|
|
7870
|
+
});
|
|
7871
|
+
}
|
|
7872
|
+
function requireString(record, key, prefix, errors) {
|
|
7873
|
+
if (typeof record[key] !== "string" || record[key].length === 0) {
|
|
7874
|
+
errors.push(`${prefix}.${key} must be a non-empty string`);
|
|
7875
|
+
}
|
|
7459
7876
|
}
|
|
7460
|
-
function
|
|
7877
|
+
function requireEnum(record, key, allowed, prefix, errors) {
|
|
7878
|
+
if (typeof record[key] !== "string" || !allowed.includes(record[key])) {
|
|
7879
|
+
errors.push(`${prefix}.${key} must be one of ${allowed.join(", ")}`);
|
|
7880
|
+
}
|
|
7881
|
+
}
|
|
7882
|
+
function validateStatus(value, errors) {
|
|
7461
7883
|
if (!isRecord(value)) {
|
|
7462
|
-
errors.push("
|
|
7884
|
+
errors.push("status.json must be an object");
|
|
7463
7885
|
return;
|
|
7464
7886
|
}
|
|
7465
|
-
if (
|
|
7887
|
+
if (value.status !== "proposed") errors.push('status.json status must be "proposed"');
|
|
7466
7888
|
}
|
|
7467
7889
|
function isRecord(value) {
|
|
7468
7890
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -7475,12 +7897,62 @@ function shortPath(path2) {
|
|
|
7475
7897
|
|
|
7476
7898
|
// src/commands/geo.ts
|
|
7477
7899
|
function buildGeoCommand() {
|
|
7478
|
-
const geo = new Command("geo").description("
|
|
7479
|
-
geo.command("
|
|
7900
|
+
const geo = new Command("geo").description("Manage local GEO repo setup and strategy runs");
|
|
7901
|
+
geo.command("init").description("Initialize GEO scaffold and skills in a tenant repo").option("--repo <path>", "Tenant repo path", ".").option("--skills-source <path>", "Use an explicit GEO skillpack source directory").option("--ref <ref>", "Skillpack ref label to write into agent/skillpack.json", "local").option("--json", "Output as JSON").action(geoInitCommand);
|
|
7902
|
+
geo.command("sync").description("Sync managed GEO scaffold files without touching memory or run artifacts").option("--repo <path>", "Tenant repo path", ".").option("--skills-source <path>", "Use an explicit GEO skillpack source directory").option("--ref <ref>", "Skillpack ref label to write into agent/skillpack.json", "local").option("--json", "Output as JSON").action(geoSyncCommand);
|
|
7903
|
+
geo.command("doctor").description("Check whether a tenant repo GEO scaffold matches the installed skillpack").option("--repo <path>", "Tenant repo path", ".").option("--skills-source <path>", "Use an explicit GEO skillpack source directory").option("--json", "Output as JSON").action(geoDoctorCommand);
|
|
7904
|
+
geo.command("prepare <workspace>").description("Prepare a local GEO strategy run package").option("--repo <path>", "Use an explicit tenant repo path").option("--days <days>", "Signal lookback window in days", parsePositiveInt).option("--skills-source <path>", "Use an explicit GEO skillpack source directory").option("--skip-scaffold", "Skip GEO scaffold sync before writing the run package").option("--json", "Output as JSON").action(geoPrepareCommand);
|
|
7480
7905
|
geo.command("validate <runId>").description("Validate local GEO strategy run artifacts before pushing").option("--repo <path>", "Use an explicit tenant repo path").option("--json", "Output as JSON").action(geoValidateCommand);
|
|
7906
|
+
geo.command("intents <runId>").description("Show the pre-fetched intents for a local GEO strategy run").option("--repo <path>", "Use an explicit tenant repo path").option("--json", "Output as JSON").action(geoIntentsCommand);
|
|
7481
7907
|
geo.command("status <runId>").description("Show backend sync status for a GEO strategy run").option("--json", "Output as JSON").action(geoStatusCommand);
|
|
7482
7908
|
return geo;
|
|
7483
7909
|
}
|
|
7910
|
+
async function geoInitCommand(opts) {
|
|
7911
|
+
try {
|
|
7912
|
+
const result = await initGeoScaffold({
|
|
7913
|
+
repoPath: opts.repo,
|
|
7914
|
+
skillsSourceDir: opts.skillsSource,
|
|
7915
|
+
ref: opts.ref
|
|
7916
|
+
});
|
|
7917
|
+
printScaffoldResult("Initialized GEO scaffold", result, opts.json);
|
|
7918
|
+
} catch (err) {
|
|
7919
|
+
printScaffoldError("Couldn't initialize GEO scaffold", err);
|
|
7920
|
+
}
|
|
7921
|
+
}
|
|
7922
|
+
async function geoSyncCommand(opts) {
|
|
7923
|
+
try {
|
|
7924
|
+
const result = await syncGeoScaffold({
|
|
7925
|
+
repoPath: opts.repo,
|
|
7926
|
+
skillsSourceDir: opts.skillsSource,
|
|
7927
|
+
ref: opts.ref
|
|
7928
|
+
});
|
|
7929
|
+
printScaffoldResult("Synced GEO scaffold", result, opts.json);
|
|
7930
|
+
} catch (err) {
|
|
7931
|
+
printScaffoldError("Couldn't sync GEO scaffold", err);
|
|
7932
|
+
}
|
|
7933
|
+
}
|
|
7934
|
+
async function geoDoctorCommand(opts) {
|
|
7935
|
+
try {
|
|
7936
|
+
const result = await doctorGeoScaffold({
|
|
7937
|
+
repoPath: opts.repo,
|
|
7938
|
+
skillsSourceDir: opts.skillsSource
|
|
7939
|
+
});
|
|
7940
|
+
if (opts.json) {
|
|
7941
|
+
console.log(JSON.stringify(result, null, 2));
|
|
7942
|
+
process.exit(result.ok ? 0 : 1);
|
|
7943
|
+
}
|
|
7944
|
+
if (result.ok) {
|
|
7945
|
+
console.log(source_default.green("\n GEO scaffold is healthy.\n"));
|
|
7946
|
+
return;
|
|
7947
|
+
}
|
|
7948
|
+
console.error(source_default.red("\n GEO scaffold has drift\n"));
|
|
7949
|
+
for (const problem of result.problems) console.error(` - ${problem}`);
|
|
7950
|
+
console.error();
|
|
7951
|
+
process.exit(1);
|
|
7952
|
+
} catch (err) {
|
|
7953
|
+
printScaffoldError("Couldn't check GEO scaffold", err);
|
|
7954
|
+
}
|
|
7955
|
+
}
|
|
7484
7956
|
async function geoPrepareCommand(workspace, opts) {
|
|
7485
7957
|
const res = await apiRequest("POST", "/api/cli/geo-strategy/prepare", {
|
|
7486
7958
|
workspace,
|
|
@@ -7495,6 +7967,13 @@ async function geoPrepareCommand(workspace, opts) {
|
|
|
7495
7967
|
let runDir;
|
|
7496
7968
|
try {
|
|
7497
7969
|
repoPath = await prepareTenantRepo({ package: pkg, repo: opts.repo });
|
|
7970
|
+
if (!opts.skipScaffold) {
|
|
7971
|
+
await initGeoScaffold({
|
|
7972
|
+
repoPath,
|
|
7973
|
+
skillsSourceDir: opts.skillsSource,
|
|
7974
|
+
ref: pkg.branch
|
|
7975
|
+
});
|
|
7976
|
+
}
|
|
7498
7977
|
({ runDir } = await writeGeoRunPackage({ repoPath, package: pkg }));
|
|
7499
7978
|
} catch (err) {
|
|
7500
7979
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -7554,6 +8033,45 @@ async function geoValidateCommand(runId, opts) {
|
|
|
7554
8033
|
console.error();
|
|
7555
8034
|
process.exit(1);
|
|
7556
8035
|
}
|
|
8036
|
+
async function geoIntentsCommand(runId, opts) {
|
|
8037
|
+
const repoPath = opts.repo ?? await findRepoForRun(runId);
|
|
8038
|
+
if (!repoPath) {
|
|
8039
|
+
console.error(source_default.red(`Could not find local repo for run ${runId}. Pass --repo <path>.`));
|
|
8040
|
+
process.exit(1);
|
|
8041
|
+
}
|
|
8042
|
+
let payload;
|
|
8043
|
+
try {
|
|
8044
|
+
payload = await readGeoIntentList(join6(repoPath, "agent", "runs", runId));
|
|
8045
|
+
} catch (err) {
|
|
8046
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
8047
|
+
console.error(source_default.red(`Couldn't read intents for run ${runId}: ${message}`));
|
|
8048
|
+
process.exit(1);
|
|
8049
|
+
}
|
|
8050
|
+
if (opts.json) {
|
|
8051
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
8052
|
+
return;
|
|
8053
|
+
}
|
|
8054
|
+
console.log();
|
|
8055
|
+
console.log(source_default.bold(` GEO intents for ${runId}`));
|
|
8056
|
+
console.log(` ${source_default.bold("Workspace:")} ${payload.workspaceDomain ?? payload.workspaceId ?? "unknown"}`);
|
|
8057
|
+
console.log(` ${source_default.bold("Count:")} ${payload.intents.length}`);
|
|
8058
|
+
console.log();
|
|
8059
|
+
for (const intent of payload.intents) {
|
|
8060
|
+
const row = intent && typeof intent === "object" ? intent : {};
|
|
8061
|
+
const id = stringValue(row.id ?? row.intentId ?? row.intent_id) ?? "unknown";
|
|
8062
|
+
const name = stringValue(row.name ?? row.intentName ?? row.intent_name) ?? "(unnamed)";
|
|
8063
|
+
const type = stringValue(row.type ?? row.intentType ?? row.intent_type);
|
|
8064
|
+
const visibility = numberValue(row.currentVisibility ?? row.current_visibility);
|
|
8065
|
+
const competitor = numberValue(row.competitorVisibility ?? row.competitor_visibility);
|
|
8066
|
+
const details = [
|
|
8067
|
+
type ? `type=${type}` : null,
|
|
8068
|
+
visibility !== null ? `visibility=${formatPct(visibility)}` : null,
|
|
8069
|
+
competitor !== null ? `competitor=${formatPct(competitor)}` : null
|
|
8070
|
+
].filter(Boolean).join(", ");
|
|
8071
|
+
console.log(` - ${name} ${source_default.dim(`(${id})`)}${details ? source_default.dim(` - ${details}`) : ""}`);
|
|
8072
|
+
}
|
|
8073
|
+
console.log();
|
|
8074
|
+
}
|
|
7557
8075
|
async function geoStatusCommand(runId, opts) {
|
|
7558
8076
|
const res = await apiRequest("GET", `/api/cli/geo-strategy/runs/${encodeURIComponent(runId)}`);
|
|
7559
8077
|
if (!res.ok) {
|
|
@@ -7579,10 +8097,64 @@ function parsePositiveInt(value) {
|
|
|
7579
8097
|
}
|
|
7580
8098
|
return parsed;
|
|
7581
8099
|
}
|
|
8100
|
+
async function readGeoIntentList(runDir) {
|
|
8101
|
+
try {
|
|
8102
|
+
const raw = await readFile3(join6(runDir, "intents.json"), "utf8");
|
|
8103
|
+
const parsed = JSON.parse(raw);
|
|
8104
|
+
if (parsed && typeof parsed === "object" && Array.isArray(parsed.intents)) {
|
|
8105
|
+
return parsed;
|
|
8106
|
+
}
|
|
8107
|
+
throw new Error("intents.json does not contain an intents array");
|
|
8108
|
+
} catch (err) {
|
|
8109
|
+
const contextRaw = await readFile3(join6(runDir, "context.json"), "utf8").catch(() => null);
|
|
8110
|
+
if (!contextRaw) throw err;
|
|
8111
|
+
const context = JSON.parse(contextRaw);
|
|
8112
|
+
return {
|
|
8113
|
+
runId: context.runId,
|
|
8114
|
+
workspaceId: context.workspaceId,
|
|
8115
|
+
workspaceDomain: context.workspaceDomain,
|
|
8116
|
+
intents: Array.isArray(context.signals?.intents) ? context.signals.intents : []
|
|
8117
|
+
};
|
|
8118
|
+
}
|
|
8119
|
+
}
|
|
8120
|
+
function stringValue(value) {
|
|
8121
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
8122
|
+
}
|
|
8123
|
+
function numberValue(value) {
|
|
8124
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
8125
|
+
}
|
|
8126
|
+
function formatPct(value) {
|
|
8127
|
+
return `${(value * 100).toFixed(1)}%`;
|
|
8128
|
+
}
|
|
7582
8129
|
async function printApiFailure(res, fallback2) {
|
|
7583
8130
|
const body = await res.json().catch(() => null);
|
|
7584
8131
|
console.error(source_default.red(body?.message ?? body?.error ?? fallback2));
|
|
7585
8132
|
}
|
|
8133
|
+
function printScaffoldResult(title, result, json) {
|
|
8134
|
+
if (json) {
|
|
8135
|
+
console.log(JSON.stringify(result, null, 2));
|
|
8136
|
+
process.exit(result.ok ? 0 : 1);
|
|
8137
|
+
}
|
|
8138
|
+
console.log();
|
|
8139
|
+
console.log(source_default.green.bold(` ${title}`));
|
|
8140
|
+
console.log(` ${source_default.bold("Repo:")} ${result.repoPath}`);
|
|
8141
|
+
console.log(` ${source_default.bold("Skillpack:")} ${result.skillsSourceDir}`);
|
|
8142
|
+
console.log(` ${source_default.bold("Hash:")} ${result.manifestHash}`);
|
|
8143
|
+
if (result.changed.length > 0) {
|
|
8144
|
+
console.log();
|
|
8145
|
+
console.log(source_default.bold(" Changed:"));
|
|
8146
|
+
for (const path2 of result.changed) console.log(` - ${path2}`);
|
|
8147
|
+
} else {
|
|
8148
|
+
console.log();
|
|
8149
|
+
console.log(source_default.dim(" No file changes."));
|
|
8150
|
+
}
|
|
8151
|
+
console.log();
|
|
8152
|
+
}
|
|
8153
|
+
function printScaffoldError(prefix, err) {
|
|
8154
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
8155
|
+
console.error(source_default.red(`${prefix}: ${message}`));
|
|
8156
|
+
process.exit(1);
|
|
8157
|
+
}
|
|
7586
8158
|
|
|
7587
8159
|
// src/index.ts
|
|
7588
8160
|
var program2 = new Command();
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anymorph",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Check your brand's AI visibility across ChatGPT, Perplexity, Gemini, and more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
7
7
|
"bin": {
|
|
8
|
-
"anymorph": "
|
|
8
|
+
"anymorph": "dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|