archal 0.9.11 → 0.9.13
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/index.cjs +250 -49
- package/package.json +8 -3
- package/skills/onboard/SKILL.md +26 -4
- package/skills/scenario/SKILL.md +8 -0
- package/twin-assets/google-workspace/tools.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -86957,11 +86957,18 @@ function checksumDir(dir) {
|
|
|
86957
86957
|
for (const entry of entries) {
|
|
86958
86958
|
if (entry.name === MANIFEST_FILE2) continue;
|
|
86959
86959
|
const p = (0, import_node_path51.join)(dir, entry.name);
|
|
86960
|
+
const nameBytes = Buffer.from(entry.name, "utf8");
|
|
86960
86961
|
if (entry.isDirectory()) {
|
|
86961
|
-
|
|
86962
|
+
const sub = checksumDir(p) ?? "";
|
|
86963
|
+
const subBytes = Buffer.from(sub, "utf8");
|
|
86964
|
+
hash2.update(`d:${nameBytes.length}:${sub ? subBytes.length : 0}:`);
|
|
86965
|
+
hash2.update(nameBytes);
|
|
86966
|
+
hash2.update(subBytes);
|
|
86962
86967
|
} else {
|
|
86963
|
-
|
|
86964
|
-
hash2.update(
|
|
86968
|
+
const content = (0, import_node_fs51.readFileSync)(p);
|
|
86969
|
+
hash2.update(`f:${nameBytes.length}:${content.length}:`);
|
|
86970
|
+
hash2.update(nameBytes);
|
|
86971
|
+
hash2.update(content);
|
|
86965
86972
|
}
|
|
86966
86973
|
}
|
|
86967
86974
|
return hash2.digest("hex");
|
|
@@ -86992,6 +86999,16 @@ function parseTargetFlag(value) {
|
|
|
86992
86999
|
}
|
|
86993
87000
|
|
|
86994
87001
|
// src/skills/installer.ts
|
|
87002
|
+
var ARCHAL_SKILL_PREFIX = "archal-";
|
|
87003
|
+
var SAFE_SKILL_NAME_RE = /^archal-[a-z0-9][a-z0-9-]*$/;
|
|
87004
|
+
function isSafeSkillName(destName) {
|
|
87005
|
+
return SAFE_SKILL_NAME_RE.test(destName);
|
|
87006
|
+
}
|
|
87007
|
+
function isValidManifestEntry(value) {
|
|
87008
|
+
if (!value || typeof value !== "object") return false;
|
|
87009
|
+
const entry = value;
|
|
87010
|
+
return typeof entry["checksum"] === "string" && typeof entry["version"] === "string";
|
|
87011
|
+
}
|
|
86995
87012
|
var RESET8 = "\x1B[0m";
|
|
86996
87013
|
var BOLD7 = "\x1B[1m";
|
|
86997
87014
|
var DIM8 = "\x1B[2m";
|
|
@@ -87008,11 +87025,12 @@ async function runInstaller({
|
|
|
87008
87025
|
}) {
|
|
87009
87026
|
const log3 = silent ? () => {
|
|
87010
87027
|
} : (msg) => process.stdout.write(msg);
|
|
87028
|
+
const isNonInteractive = nonInteractive || !process.stdin.isTTY;
|
|
87011
87029
|
const skillDirs = (0, import_node_fs52.readdirSync)(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
87012
87030
|
const selected = await resolveTargets({
|
|
87013
87031
|
cwd,
|
|
87014
87032
|
requested: requestedTargets ?? null,
|
|
87015
|
-
nonInteractive,
|
|
87033
|
+
nonInteractive: isNonInteractive,
|
|
87016
87034
|
log: log3,
|
|
87017
87035
|
version: version3
|
|
87018
87036
|
});
|
|
@@ -87022,7 +87040,9 @@ async function runInstaller({
|
|
|
87022
87040
|
return { installed: 0, targets: [], skipped: [], version: version3 };
|
|
87023
87041
|
}
|
|
87024
87042
|
let installed = 0;
|
|
87043
|
+
let removed = 0;
|
|
87025
87044
|
const skipped = [];
|
|
87045
|
+
const sourceDestNames = new Set(skillDirs.map((s) => `${ARCHAL_SKILL_PREFIX}${s}`));
|
|
87026
87046
|
for (const target of selected) {
|
|
87027
87047
|
const platformDir = (0, import_node_path52.join)(cwd, target.skillsPath);
|
|
87028
87048
|
(0, import_node_fs52.mkdirSync)(platformDir, { recursive: true });
|
|
@@ -87034,35 +87054,44 @@ async function runInstaller({
|
|
|
87034
87054
|
};
|
|
87035
87055
|
for (const skill of skillDirs) {
|
|
87036
87056
|
const src = (0, import_node_path52.join)(skillsDir, skill);
|
|
87037
|
-
const destName =
|
|
87057
|
+
const destName = `${ARCHAL_SKILL_PREFIX}${skill}`;
|
|
87038
87058
|
const dest = (0, import_node_path52.join)(platformDir, destName);
|
|
87039
87059
|
const sourceChecksum = checksumDir(src);
|
|
87040
87060
|
const existingChecksum = checksumDir(dest);
|
|
87041
87061
|
const manifestEntry = manifest?.skills?.[destName];
|
|
87042
|
-
|
|
87062
|
+
let userEdited = false;
|
|
87063
|
+
let migrationCase = false;
|
|
87064
|
+
if (existingChecksum) {
|
|
87065
|
+
if (manifestEntry?.checksum) {
|
|
87066
|
+
userEdited = existingChecksum !== manifestEntry.checksum && existingChecksum !== sourceChecksum;
|
|
87067
|
+
} else if (existingChecksum !== sourceChecksum) {
|
|
87068
|
+
userEdited = true;
|
|
87069
|
+
migrationCase = true;
|
|
87070
|
+
}
|
|
87071
|
+
}
|
|
87043
87072
|
if (userEdited && !yes) {
|
|
87044
|
-
|
|
87045
|
-
|
|
87046
|
-
|
|
87047
|
-
|
|
87048
|
-
|
|
87049
|
-
|
|
87050
|
-
|
|
87073
|
+
const message = migrationCase ? `${target.label}: ${destName} exists from an earlier install and may contain local edits. Overwrite with archal@${version3}?` : `${target.label}: ${destName} has local edits. Overwrite?`;
|
|
87074
|
+
const skipSuffix = migrationCase ? "legacy install, --yes required" : "user-edited, --yes required";
|
|
87075
|
+
const keptSuffix = migrationCase ? "kept legacy copy" : "kept user edits";
|
|
87076
|
+
const preservedEntry = {
|
|
87077
|
+
version: manifestEntry?.version ?? (migrationCase ? "legacy" : "unknown"),
|
|
87078
|
+
checksum: manifestEntry?.checksum ?? sourceChecksum ?? "",
|
|
87079
|
+
userEdited: true
|
|
87080
|
+
};
|
|
87081
|
+
if (isNonInteractive) {
|
|
87082
|
+
skipped.push(`${target.id}/${destName} (${skipSuffix})`);
|
|
87083
|
+
nextManifest.skills[destName] = preservedEntry;
|
|
87051
87084
|
continue;
|
|
87052
87085
|
}
|
|
87053
87086
|
const { overwrite } = await (0, import_prompts.default)({
|
|
87054
87087
|
type: "confirm",
|
|
87055
87088
|
name: "overwrite",
|
|
87056
|
-
message
|
|
87057
|
-
initial: false
|
|
87089
|
+
message,
|
|
87090
|
+
initial: migrationCase ? true : false
|
|
87058
87091
|
});
|
|
87059
87092
|
if (!overwrite) {
|
|
87060
|
-
skipped.push(`${target.id}/${destName} (
|
|
87061
|
-
nextManifest.skills[destName] =
|
|
87062
|
-
version: manifestEntry?.version ?? "unknown",
|
|
87063
|
-
checksum: existingChecksum,
|
|
87064
|
-
userEdited: true
|
|
87065
|
-
};
|
|
87093
|
+
skipped.push(`${target.id}/${destName} (${keptSuffix})`);
|
|
87094
|
+
nextManifest.skills[destName] = preservedEntry;
|
|
87066
87095
|
continue;
|
|
87067
87096
|
}
|
|
87068
87097
|
}
|
|
@@ -87076,6 +87105,49 @@ async function runInstaller({
|
|
|
87076
87105
|
};
|
|
87077
87106
|
installed++;
|
|
87078
87107
|
}
|
|
87108
|
+
if (manifest?.skills) {
|
|
87109
|
+
for (const [destName, rawEntry] of Object.entries(manifest.skills)) {
|
|
87110
|
+
if (!isSafeSkillName(destName)) {
|
|
87111
|
+
continue;
|
|
87112
|
+
}
|
|
87113
|
+
if (!isValidManifestEntry(rawEntry)) {
|
|
87114
|
+
continue;
|
|
87115
|
+
}
|
|
87116
|
+
const entry = rawEntry;
|
|
87117
|
+
if (sourceDestNames.has(destName)) continue;
|
|
87118
|
+
const dest = (0, import_node_path52.join)(platformDir, destName);
|
|
87119
|
+
if (!(0, import_node_fs52.existsSync)(dest)) continue;
|
|
87120
|
+
const existingChecksum = checksumDir(dest);
|
|
87121
|
+
const userEdited = existingChecksum !== entry.checksum;
|
|
87122
|
+
if (userEdited && !yes) {
|
|
87123
|
+
const preservedEntry = {
|
|
87124
|
+
version: entry.version,
|
|
87125
|
+
checksum: entry.checksum,
|
|
87126
|
+
userEdited: true
|
|
87127
|
+
};
|
|
87128
|
+
if (isNonInteractive) {
|
|
87129
|
+
skipped.push(
|
|
87130
|
+
`${target.id}/${destName} (removed upstream, edits detected \u2014 --yes required)`
|
|
87131
|
+
);
|
|
87132
|
+
nextManifest.skills[destName] = preservedEntry;
|
|
87133
|
+
continue;
|
|
87134
|
+
}
|
|
87135
|
+
const { remove } = await (0, import_prompts.default)({
|
|
87136
|
+
type: "confirm",
|
|
87137
|
+
name: "remove",
|
|
87138
|
+
message: `${target.label}: ${destName} was removed upstream but has local edits. Delete it?`,
|
|
87139
|
+
initial: false
|
|
87140
|
+
});
|
|
87141
|
+
if (!remove) {
|
|
87142
|
+
skipped.push(`${target.id}/${destName} (kept local copy)`);
|
|
87143
|
+
nextManifest.skills[destName] = preservedEntry;
|
|
87144
|
+
continue;
|
|
87145
|
+
}
|
|
87146
|
+
}
|
|
87147
|
+
(0, import_node_fs52.rmSync)(dest, { recursive: true, force: true });
|
|
87148
|
+
removed++;
|
|
87149
|
+
}
|
|
87150
|
+
}
|
|
87079
87151
|
writeManifest(platformDir, nextManifest);
|
|
87080
87152
|
}
|
|
87081
87153
|
const targetLabels = selected.map((t) => t.label).join(", ");
|
|
@@ -87084,6 +87156,10 @@ ${GREEN5}${BOLD7}Archal skills installed${RESET8} ${DIM8}(v${version3})${RESET8}
|
|
|
87084
87156
|
`);
|
|
87085
87157
|
log3(`${DIM8}${installed} skill(s) \u2192 ${targetLabels}${RESET8}
|
|
87086
87158
|
`);
|
|
87159
|
+
if (removed > 0) {
|
|
87160
|
+
log3(`${DIM8}${removed} skill(s) removed (no longer shipped)${RESET8}
|
|
87161
|
+
`);
|
|
87162
|
+
}
|
|
87087
87163
|
if (skipped.length) {
|
|
87088
87164
|
log3(`${YELLOW4}Skipped:${RESET8}
|
|
87089
87165
|
`);
|
|
@@ -87143,7 +87219,7 @@ ${BOLD7}Install Archal skills${RESET8} ${DIM8}(v${version3})${RESET8}
|
|
|
87143
87219
|
onCancel: () => {
|
|
87144
87220
|
log3(`${YELLOW4}Cancelled.${RESET8}
|
|
87145
87221
|
`);
|
|
87146
|
-
process.exit(
|
|
87222
|
+
process.exit(130);
|
|
87147
87223
|
}
|
|
87148
87224
|
}
|
|
87149
87225
|
);
|
|
@@ -87155,11 +87231,27 @@ ${BOLD7}Install Archal skills${RESET8} ${DIM8}(v${version3})${RESET8}
|
|
|
87155
87231
|
init_version();
|
|
87156
87232
|
init_errors6();
|
|
87157
87233
|
function detectPackageManager2(cwd) {
|
|
87234
|
+
const fromPackageManager = readPackageManagerField(cwd);
|
|
87235
|
+
if (fromPackageManager) return fromPackageManager;
|
|
87158
87236
|
if ((0, import_node_fs53.existsSync)((0, import_node_path53.join)(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
87159
|
-
if ((0, import_node_fs53.existsSync)((0, import_node_path53.join)(cwd, "bun.lockb"))) return "bun";
|
|
87237
|
+
if ((0, import_node_fs53.existsSync)((0, import_node_path53.join)(cwd, "bun.lockb")) || (0, import_node_fs53.existsSync)((0, import_node_path53.join)(cwd, "bun.lock"))) return "bun";
|
|
87160
87238
|
if ((0, import_node_fs53.existsSync)((0, import_node_path53.join)(cwd, "yarn.lock"))) return "yarn";
|
|
87161
87239
|
return "npm";
|
|
87162
87240
|
}
|
|
87241
|
+
function readPackageManagerField(cwd) {
|
|
87242
|
+
try {
|
|
87243
|
+
const pkg = JSON.parse((0, import_node_fs53.readFileSync)((0, import_node_path53.join)(cwd, "package.json"), "utf8"));
|
|
87244
|
+
const raw = typeof pkg.packageManager === "string" ? pkg.packageManager : "";
|
|
87245
|
+
const name = raw.split("@")[0];
|
|
87246
|
+
if (name === "pnpm" || name === "yarn" || name === "bun" || name === "npm") {
|
|
87247
|
+
return name;
|
|
87248
|
+
}
|
|
87249
|
+
return null;
|
|
87250
|
+
} catch {
|
|
87251
|
+
return null;
|
|
87252
|
+
}
|
|
87253
|
+
}
|
|
87254
|
+
var SEMVER_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
87163
87255
|
function hasPackageJson(cwd) {
|
|
87164
87256
|
return (0, import_node_fs53.existsSync)((0, import_node_path53.join)(cwd, "package.json"));
|
|
87165
87257
|
}
|
|
@@ -87171,26 +87263,78 @@ function alreadyHasArchalDep(cwd) {
|
|
|
87171
87263
|
return false;
|
|
87172
87264
|
}
|
|
87173
87265
|
}
|
|
87266
|
+
function looksLikeSkillsDir(candidate) {
|
|
87267
|
+
if (!(0, import_node_fs53.existsSync)(candidate)) return false;
|
|
87268
|
+
let entries;
|
|
87269
|
+
try {
|
|
87270
|
+
entries = (0, import_node_fs53.readdirSync)(candidate);
|
|
87271
|
+
} catch {
|
|
87272
|
+
return false;
|
|
87273
|
+
}
|
|
87274
|
+
for (const name of entries) {
|
|
87275
|
+
const child = (0, import_node_path53.join)(candidate, name);
|
|
87276
|
+
try {
|
|
87277
|
+
if (!(0, import_node_fs53.statSync)(child).isDirectory()) continue;
|
|
87278
|
+
if ((0, import_node_fs53.existsSync)((0, import_node_path53.join)(child, "SKILL.md"))) return true;
|
|
87279
|
+
} catch {
|
|
87280
|
+
}
|
|
87281
|
+
}
|
|
87282
|
+
return false;
|
|
87283
|
+
}
|
|
87284
|
+
function findPnpmWorkspaceRoot(start) {
|
|
87285
|
+
let dir = start;
|
|
87286
|
+
for (let i = 0; i < 10; i++) {
|
|
87287
|
+
if ((0, import_node_fs53.existsSync)((0, import_node_path53.join)(dir, "pnpm-workspace.yaml"))) return dir;
|
|
87288
|
+
const parent = (0, import_node_path53.dirname)(dir);
|
|
87289
|
+
if (parent === dir) return null;
|
|
87290
|
+
dir = parent;
|
|
87291
|
+
}
|
|
87292
|
+
return null;
|
|
87293
|
+
}
|
|
87294
|
+
function findRepoRoot(start) {
|
|
87295
|
+
let dir = start;
|
|
87296
|
+
for (let i = 0; i < 10; i++) {
|
|
87297
|
+
if ((0, import_node_fs53.existsSync)((0, import_node_path53.join)(dir, "pnpm-workspace.yaml"))) return dir;
|
|
87298
|
+
const parent = (0, import_node_path53.dirname)(dir);
|
|
87299
|
+
if (parent === dir) return null;
|
|
87300
|
+
dir = parent;
|
|
87301
|
+
}
|
|
87302
|
+
return null;
|
|
87303
|
+
}
|
|
87174
87304
|
function resolveSkillsDir() {
|
|
87175
87305
|
const here = typeof __dirname === "string" ? __dirname : process.cwd();
|
|
87306
|
+
const repoRoot = findRepoRoot(here) ?? findRepoRoot(process.cwd());
|
|
87176
87307
|
const candidates = [
|
|
87177
87308
|
(0, import_node_path53.resolve)(here, "..", "skills"),
|
|
87178
|
-
(0, import_node_path53.resolve)(
|
|
87179
|
-
(0, import_node_path53.resolve)(process.cwd(), "packages", "archal
|
|
87309
|
+
...repoRoot ? [(0, import_node_path53.resolve)(repoRoot, "packages", "archal", "skills")] : [],
|
|
87310
|
+
(0, import_node_path53.resolve)(process.cwd(), "packages", "archal", "skills"),
|
|
87311
|
+
(0, import_node_path53.resolve)(process.cwd(), "skills")
|
|
87180
87312
|
];
|
|
87181
87313
|
for (const candidate of candidates) {
|
|
87182
|
-
if ((
|
|
87314
|
+
if (looksLikeSkillsDir(candidate)) return candidate;
|
|
87183
87315
|
}
|
|
87184
87316
|
throw new CliRuntimeError(
|
|
87185
|
-
"Could not locate the Archal skills directory.
|
|
87317
|
+
"Could not locate the Archal skills directory. This looks like a broken archal install \u2014 reinstall with `npm install archal` (or your package manager equivalent) and retry."
|
|
87186
87318
|
);
|
|
87187
87319
|
}
|
|
87188
87320
|
function runPmAdd(pm, cwd, spec) {
|
|
87189
|
-
|
|
87321
|
+
let args;
|
|
87322
|
+
if (pm === "npm") {
|
|
87323
|
+
args = ["install", "--save-dev", "--no-audit", "--no-fund", "--loglevel=error", spec];
|
|
87324
|
+
} else if (pm === "pnpm" && findPnpmWorkspaceRoot(cwd) === cwd) {
|
|
87325
|
+
args = ["add", "-D", "-w", spec];
|
|
87326
|
+
} else {
|
|
87327
|
+
args = ["add", "-D", spec];
|
|
87328
|
+
}
|
|
87190
87329
|
const result = (0, import_node_child_process10.spawnSync)(pm, args, { cwd, stdio: "inherit" });
|
|
87330
|
+
if (result.error) {
|
|
87331
|
+
throw new CliRuntimeError(
|
|
87332
|
+
`Failed to launch ${pm}: ${result.error.message}. Install ${pm} (or install \`archal\` manually) and re-run \`archal init\`.`
|
|
87333
|
+
);
|
|
87334
|
+
}
|
|
87191
87335
|
if (result.status !== 0) {
|
|
87192
87336
|
throw new CliRuntimeError(
|
|
87193
|
-
`Failed to install ${spec} with ${pm}. Install manually and re-run \`archal init\`.`
|
|
87337
|
+
`Failed to install ${spec} with ${pm} (exit ${result.status}). Install manually and re-run \`archal init\`.`
|
|
87194
87338
|
);
|
|
87195
87339
|
}
|
|
87196
87340
|
}
|
|
@@ -87209,29 +87353,71 @@ function createInitCommand() {
|
|
|
87209
87353
|
throw new CliRuntimeError(err instanceof Error ? err.message : String(err));
|
|
87210
87354
|
}
|
|
87211
87355
|
}
|
|
87212
|
-
|
|
87213
|
-
|
|
87214
|
-
|
|
87215
|
-
|
|
87216
|
-
|
|
87217
|
-
|
|
87218
|
-
|
|
87356
|
+
let cliInstalled = true;
|
|
87357
|
+
const hadPackageJson = hasPackageJson(cwd);
|
|
87358
|
+
const startedAt = Date.now();
|
|
87359
|
+
try {
|
|
87360
|
+
if (!opts.skillsOnly) {
|
|
87361
|
+
if (!SEMVER_RE.test(CLI_VERSION)) {
|
|
87362
|
+
throw new CliRuntimeError(
|
|
87363
|
+
`Invalid CLI_VERSION "${CLI_VERSION}" \u2014 refusing to install. Reinstall archal and retry.`
|
|
87364
|
+
);
|
|
87365
|
+
}
|
|
87366
|
+
if (!hadPackageJson) {
|
|
87367
|
+
process.stderr.write(
|
|
87368
|
+
"No package.json in cwd \u2014 skipping devDependency install. Install archal globally (`npm i -g archal`) or initialize a package.json first.\n"
|
|
87369
|
+
);
|
|
87370
|
+
cliInstalled = false;
|
|
87371
|
+
} else if (alreadyHasArchalDep(cwd)) {
|
|
87372
|
+
process.stdout.write(`archal already a dependency \u2014 skipping install.
|
|
87219
87373
|
`);
|
|
87220
|
-
|
|
87221
|
-
|
|
87222
|
-
|
|
87374
|
+
} else {
|
|
87375
|
+
const pm = detectPackageManager2(cwd);
|
|
87376
|
+
process.stdout.write(`Installing archal@${CLI_VERSION} with ${pm}\u2026
|
|
87223
87377
|
`);
|
|
87224
|
-
|
|
87378
|
+
runPmAdd(pm, cwd, `archal@${CLI_VERSION}`);
|
|
87379
|
+
}
|
|
87380
|
+
} else {
|
|
87381
|
+
cliInstalled = alreadyHasArchalDep(cwd);
|
|
87382
|
+
}
|
|
87383
|
+
const result = await runInstaller({
|
|
87384
|
+
cwd,
|
|
87385
|
+
targets: targets ?? void 0,
|
|
87386
|
+
nonInteractive: opts.nonInteractive === true,
|
|
87387
|
+
yes: opts.yes === true,
|
|
87388
|
+
skillsDir: resolveSkillsDir(),
|
|
87389
|
+
version: CLI_VERSION
|
|
87390
|
+
});
|
|
87391
|
+
if (opts.skillsOnly && !cliInstalled) {
|
|
87392
|
+
process.stdout.write(
|
|
87393
|
+
`Note: --skills-only left out the archal runner. Run \`npm install -D archal\` (or pnpm / yarn / bun equivalent) before \`archal run\`.
|
|
87394
|
+
`
|
|
87395
|
+
);
|
|
87225
87396
|
}
|
|
87397
|
+
captureCliEvent("cli_init_completed", {
|
|
87398
|
+
skills_only: Boolean(opts.skillsOnly),
|
|
87399
|
+
non_interactive: opts.nonInteractive === true,
|
|
87400
|
+
yes: opts.yes === true,
|
|
87401
|
+
target_count: result.targets.length,
|
|
87402
|
+
targets: result.targets,
|
|
87403
|
+
installed: result.installed,
|
|
87404
|
+
skipped: result.skipped.length,
|
|
87405
|
+
had_package_json: hadPackageJson,
|
|
87406
|
+
cli_installed: cliInstalled,
|
|
87407
|
+
duration_ms: Date.now() - startedAt
|
|
87408
|
+
});
|
|
87409
|
+
} catch (err) {
|
|
87410
|
+
captureCliEvent("cli_init_failed", {
|
|
87411
|
+
skills_only: Boolean(opts.skillsOnly),
|
|
87412
|
+
non_interactive: opts.nonInteractive === true,
|
|
87413
|
+
yes: opts.yes === true,
|
|
87414
|
+
had_package_json: hadPackageJson,
|
|
87415
|
+
error_name: err instanceof Error ? err.constructor.name : "Unknown",
|
|
87416
|
+
error_message: err instanceof Error ? err.message : String(err),
|
|
87417
|
+
duration_ms: Date.now() - startedAt
|
|
87418
|
+
});
|
|
87419
|
+
throw err;
|
|
87226
87420
|
}
|
|
87227
|
-
await runInstaller({
|
|
87228
|
-
cwd,
|
|
87229
|
-
targets: targets ?? void 0,
|
|
87230
|
-
nonInteractive: opts.nonInteractive === true,
|
|
87231
|
-
yes: opts.yes === true,
|
|
87232
|
-
skillsDir: resolveSkillsDir(),
|
|
87233
|
-
version: CLI_VERSION
|
|
87234
|
-
});
|
|
87235
87421
|
}
|
|
87236
87422
|
);
|
|
87237
87423
|
}
|
|
@@ -87359,6 +87545,20 @@ function readManifestAt(path2) {
|
|
|
87359
87545
|
return null;
|
|
87360
87546
|
}
|
|
87361
87547
|
}
|
|
87548
|
+
function compareSemver(a, b) {
|
|
87549
|
+
const parse3 = (v) => {
|
|
87550
|
+
const m = /^(\d+)\.(\d+)\.(\d+)(?:[-+][0-9A-Za-z.-]+)?$/.exec(v);
|
|
87551
|
+
if (!m) return null;
|
|
87552
|
+
return [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
87553
|
+
};
|
|
87554
|
+
const pa = parse3(a);
|
|
87555
|
+
const pb = parse3(b);
|
|
87556
|
+
if (!pa || !pb) return null;
|
|
87557
|
+
if (pa[0] !== pb[0]) return pa[0] - pb[0];
|
|
87558
|
+
if (pa[1] !== pb[1]) return pa[1] - pb[1];
|
|
87559
|
+
if (pa[2] !== pb[2]) return pa[2] - pb[2];
|
|
87560
|
+
return 0;
|
|
87561
|
+
}
|
|
87362
87562
|
function maybePrintSkillDriftBanner(cwd = process.cwd()) {
|
|
87363
87563
|
try {
|
|
87364
87564
|
const stale = [];
|
|
@@ -87367,7 +87567,8 @@ function maybePrintSkillDriftBanner(cwd = process.cwd()) {
|
|
|
87367
87567
|
if (!(0, import_node_fs55.existsSync)(manifestPath)) continue;
|
|
87368
87568
|
const manifest = readManifestAt(manifestPath);
|
|
87369
87569
|
if (!manifest?.version) continue;
|
|
87370
|
-
|
|
87570
|
+
const cmp = compareSemver(manifest.version, CLI_VERSION);
|
|
87571
|
+
if (cmp !== null && cmp < 0) {
|
|
87371
87572
|
stale.push(`${target.label} (v${manifest.version})`);
|
|
87372
87573
|
}
|
|
87373
87574
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archal",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.13",
|
|
4
4
|
"description": "Test your agents & integrations against digital twins",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -45,8 +45,13 @@
|
|
|
45
45
|
"twin-assets",
|
|
46
46
|
"LICENSE"
|
|
47
47
|
],
|
|
48
|
-
"
|
|
49
|
-
"vitest": "
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"vitest": ">=2.1.0"
|
|
50
|
+
},
|
|
51
|
+
"peerDependenciesMeta": {
|
|
52
|
+
"vitest": {
|
|
53
|
+
"optional": true
|
|
54
|
+
}
|
|
50
55
|
},
|
|
51
56
|
"scripts": {
|
|
52
57
|
"verify:artifacts": "node scripts/assert-artifacts.mjs",
|
package/skills/onboard/SKILL.md
CHANGED
|
@@ -8,6 +8,19 @@ user-invocable: true
|
|
|
8
8
|
|
|
9
9
|
You are setting up Archal in this project. Archal tests AI agents against digital twins of real services (GitHub, Slack, Stripe, etc.). Handle installation and auth yourself; delegate the workflow-specific setup to the matching sub-skill.
|
|
10
10
|
|
|
11
|
+
## If this is a cold-start
|
|
12
|
+
|
|
13
|
+
The user may have landed here without running `npx archal init` first. If the
|
|
14
|
+
CLI is missing (see "Install + auth" below) AND no `.archal-manifest.json`
|
|
15
|
+
exists in `.claude/skills/`, the canonical first command is:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx archal init
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
That adds `archal` as a devDependency and reinstalls these skills at the
|
|
22
|
+
right version. Re-invoke the onboard skill after it completes.
|
|
23
|
+
|
|
11
24
|
## Discover first
|
|
12
25
|
|
|
13
26
|
Before asking anything, read the repo:
|
|
@@ -25,14 +38,23 @@ Before asking anything, read the repo:
|
|
|
25
38
|
|
|
26
39
|
## Install + auth
|
|
27
40
|
|
|
41
|
+
If you're here via `npx archal init`, archal is already a devDependency
|
|
42
|
+
and the skills are already in place. Go straight to login:
|
|
43
|
+
|
|
28
44
|
```bash
|
|
29
|
-
npx archal --version # check if installed
|
|
30
|
-
npm install -D archal # install if not (or -g for global)
|
|
31
|
-
archal usage # check auth
|
|
32
45
|
archal login # OAuth browser flow, or: archal login --token <token>
|
|
46
|
+
archal usage # verify auth + plan
|
|
33
47
|
```
|
|
34
48
|
|
|
35
|
-
In CI,
|
|
49
|
+
In CI, set `ARCHAL_TOKEN` instead of running `archal login`.
|
|
50
|
+
|
|
51
|
+
If something feels wrong (missing CLI, stale skills), these are the
|
|
52
|
+
recovery commands — don't run them otherwise:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx archal --version # CLI reachable? prints e.g. 0.9.12
|
|
56
|
+
npx archal init --skills-only # re-stage skills if they drifted
|
|
57
|
+
```
|
|
36
58
|
|
|
37
59
|
## Pick a workflow
|
|
38
60
|
|
package/skills/scenario/SKILL.md
CHANGED
|
@@ -99,6 +99,8 @@ Aliases for `evaluator-model`: `evaluator`, `evaluatormodel`, `model`.
|
|
|
99
99
|
| `supabase` | `empty`, `small-project`, `saas-starter`, `ecommerce` |
|
|
100
100
|
| `google-workspace` | `empty`, `assistant-baseline`, `gmail-busy-inbox`, `calendar-packed-week` |
|
|
101
101
|
| `ramp` | `empty`, `default` |
|
|
102
|
+
| `discord` | `empty`, `small-server`, `harvested` |
|
|
103
|
+
| `telegram` | `empty`, `harvested` |
|
|
102
104
|
|
|
103
105
|
## Twin auto-detection from content
|
|
104
106
|
|
|
@@ -111,6 +113,12 @@ If no `twins:` config is set, Archal infers twins from keywords in Setup, Expect
|
|
|
111
113
|
- `stripe`, `payment`, `refund`, `subscription`, `invoice` -> `stripe`
|
|
112
114
|
- `supabase`, `database`, `sql query` -> `supabase`
|
|
113
115
|
- `google workspace`, `gmail`, `calendar event`, `inbox` -> `google-workspace`
|
|
116
|
+
- `discord`, `guild`, `text channel` -> `discord`
|
|
117
|
+
|
|
118
|
+
Not every twin has auto-detect keywords — `telegram` in particular has
|
|
119
|
+
none. If your scenario uses `telegram`, set `twins: telegram` in the
|
|
120
|
+
Config block or in `.archal.json`. `ramp` auto-detects on `ramp`,
|
|
121
|
+
`bill`, `expense`, `reimbursement`, `fund`, `card spend`.
|
|
114
122
|
|
|
115
123
|
## Multi-service scenarios
|
|
116
124
|
|