asdm-cli 0.1.3 → 0.3.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/README.md +43 -10
- package/dist/index.mjs +751 -211
- package/package.json +11 -2
- package/registry/latest.json +6 -4
- package/registry/policy.yaml +1 -0
- package/registry/profiles/base/profile.yaml +1 -0
- package/registry/v0.1.4.json +253 -0
- package/registry/v0.2.0.json +255 -0
- package/registry/v0.3.0.json +255 -0
- package/schemas/config.schema.json +1 -1
- package/schemas/manifest.schema.json +1 -1
- package/schemas/profile.schema.json +5 -1
package/dist/index.mjs
CHANGED
|
@@ -10,35 +10,19 @@ var __export = (target, all) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// src/utils/fs.ts
|
|
13
|
-
var fs_exports = {};
|
|
14
|
-
__export(fs_exports, {
|
|
15
|
-
copyFile: () => copyFile,
|
|
16
|
-
ensureDir: () => ensureDir,
|
|
17
|
-
exists: () => exists,
|
|
18
|
-
getAsdmCacheDir: () => getAsdmCacheDir,
|
|
19
|
-
getAsdmConfigDir: () => getAsdmConfigDir,
|
|
20
|
-
getGlobalLockfilePath: () => getGlobalLockfilePath,
|
|
21
|
-
listFiles: () => listFiles,
|
|
22
|
-
normalizePath: () => normalizePath,
|
|
23
|
-
readFile: () => readFile,
|
|
24
|
-
readJson: () => readJson,
|
|
25
|
-
removeFile: () => removeFile,
|
|
26
|
-
resolveGlobalEmitPath: () => resolveGlobalEmitPath,
|
|
27
|
-
writeFile: () => writeFile,
|
|
28
|
-
writeJson: () => writeJson
|
|
29
|
-
});
|
|
30
13
|
import { promises as fs } from "fs";
|
|
31
14
|
import path from "path";
|
|
32
15
|
import os from "os";
|
|
33
|
-
function normalizePath(p) {
|
|
34
|
-
return p.replace(/\\/g, "/");
|
|
35
|
-
}
|
|
36
16
|
async function ensureDir(dirPath) {
|
|
37
17
|
await fs.mkdir(dirPath, { recursive: true });
|
|
38
18
|
}
|
|
39
19
|
async function writeFile(filePath, content) {
|
|
40
20
|
await ensureDir(path.dirname(filePath));
|
|
41
|
-
|
|
21
|
+
if (typeof content === "string") {
|
|
22
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
23
|
+
} else {
|
|
24
|
+
await fs.writeFile(filePath, content);
|
|
25
|
+
}
|
|
42
26
|
}
|
|
43
27
|
async function readFile(filePath) {
|
|
44
28
|
try {
|
|
@@ -88,11 +72,17 @@ function resolveGlobalEmitPath(relativePath, provider) {
|
|
|
88
72
|
if (!prefix || !globalDir) return null;
|
|
89
73
|
if (!relativePath.startsWith(prefix)) return null;
|
|
90
74
|
const stripped = relativePath.slice(prefix.length);
|
|
91
|
-
|
|
75
|
+
const resolved = path.resolve(globalDir, stripped);
|
|
76
|
+
const safeBase = path.resolve(globalDir) + path.sep;
|
|
77
|
+
if (!resolved.startsWith(safeBase)) return null;
|
|
78
|
+
return resolved;
|
|
92
79
|
}
|
|
93
80
|
function getGlobalLockfilePath() {
|
|
94
81
|
return path.join(getAsdmConfigDir(), "global-lock.json");
|
|
95
82
|
}
|
|
83
|
+
function getGlobalConfigPath() {
|
|
84
|
+
return path.join(getAsdmConfigDir(), "config.json");
|
|
85
|
+
}
|
|
96
86
|
function getAsdmCacheDir() {
|
|
97
87
|
const xdgCache = process.env["XDG_CACHE_HOME"];
|
|
98
88
|
if (xdgCache) return path.join(xdgCache, "asdm");
|
|
@@ -106,10 +96,6 @@ async function readJson(filePath) {
|
|
|
106
96
|
async function writeJson(filePath, data) {
|
|
107
97
|
await writeFile(filePath, JSON.stringify(data, null, 2));
|
|
108
98
|
}
|
|
109
|
-
async function copyFile(src, dest) {
|
|
110
|
-
await ensureDir(path.dirname(dest));
|
|
111
|
-
await fs.copyFile(src, dest);
|
|
112
|
-
}
|
|
113
99
|
var PROVIDER_GLOBAL_DIRS, PROVIDER_PATH_PREFIXES;
|
|
114
100
|
var init_fs = __esm({
|
|
115
101
|
"src/utils/fs.ts"() {
|
|
@@ -117,12 +103,15 @@ var init_fs = __esm({
|
|
|
117
103
|
PROVIDER_GLOBAL_DIRS = {
|
|
118
104
|
opencode: process.platform === "win32" ? path.join(os.homedir(), "AppData", "Roaming", "opencode") : path.join(os.homedir(), ".config", "opencode"),
|
|
119
105
|
"claude-code": process.platform === "win32" ? path.join(os.homedir(), "AppData", "Roaming", "Claude") : path.join(os.homedir(), ".claude"),
|
|
120
|
-
copilot: process.platform === "win32" ? path.join(os.homedir(), "AppData", "Roaming", "GitHub Copilot") : path.join(os.homedir(), ".config", "github-copilot")
|
|
106
|
+
copilot: process.platform === "win32" ? path.join(os.homedir(), "AppData", "Roaming", "GitHub Copilot") : path.join(os.homedir(), ".config", "github-copilot"),
|
|
107
|
+
// agents-dir uses %USERPROFILE%\.agents on all platforms (no AppData variant)
|
|
108
|
+
"agents-dir": path.join(os.homedir(), ".agents")
|
|
121
109
|
};
|
|
122
110
|
PROVIDER_PATH_PREFIXES = {
|
|
123
111
|
opencode: ".opencode/",
|
|
124
112
|
"claude-code": ".claude/",
|
|
125
|
-
copilot: ".github/"
|
|
113
|
+
copilot: ".github/",
|
|
114
|
+
"agents-dir": ".agents/"
|
|
126
115
|
};
|
|
127
116
|
}
|
|
128
117
|
});
|
|
@@ -555,8 +544,79 @@ var init_copilot = __esm({
|
|
|
555
544
|
}
|
|
556
545
|
});
|
|
557
546
|
|
|
547
|
+
// src/adapters/agents-dir.ts
|
|
548
|
+
var agents_dir_exports = {};
|
|
549
|
+
__export(agents_dir_exports, {
|
|
550
|
+
AgentsDirAdapter: () => AgentsDirAdapter,
|
|
551
|
+
createAgentsDirAdapter: () => createAgentsDirAdapter
|
|
552
|
+
});
|
|
553
|
+
import path11 from "path";
|
|
554
|
+
function formatAgentContent4(parsed) {
|
|
555
|
+
return [managedFileHeader(ADAPTER_NAME4), "", parsed.body].join("\n");
|
|
556
|
+
}
|
|
557
|
+
function formatSkillContent4(parsed) {
|
|
558
|
+
return [managedFileHeader(ADAPTER_NAME4), "", parsed.body].join("\n");
|
|
559
|
+
}
|
|
560
|
+
function formatCommandContent3(parsed) {
|
|
561
|
+
return [managedFileHeader(ADAPTER_NAME4), "", parsed.body].join("\n");
|
|
562
|
+
}
|
|
563
|
+
function createAgentsDirAdapter() {
|
|
564
|
+
return new AgentsDirAdapter();
|
|
565
|
+
}
|
|
566
|
+
var ADAPTER_NAME4, AgentsDirAdapter;
|
|
567
|
+
var init_agents_dir = __esm({
|
|
568
|
+
"src/adapters/agents-dir.ts"() {
|
|
569
|
+
"use strict";
|
|
570
|
+
init_fs();
|
|
571
|
+
init_base();
|
|
572
|
+
ADAPTER_NAME4 = "agents-dir";
|
|
573
|
+
AgentsDirAdapter = class {
|
|
574
|
+
name = ADAPTER_NAME4;
|
|
575
|
+
emitAgent(parsed, _targetDir) {
|
|
576
|
+
const relativePath = `.agents/agents/${parsed.name}.md`;
|
|
577
|
+
const content = formatAgentContent4(parsed);
|
|
578
|
+
return [createEmittedFile(relativePath, content, ADAPTER_NAME4, parsed.sourcePath)];
|
|
579
|
+
}
|
|
580
|
+
emitSkill(parsed, _targetDir) {
|
|
581
|
+
const relativePath = `.agents/skills/${parsed.name}/SKILL.md`;
|
|
582
|
+
const content = formatSkillContent4(parsed);
|
|
583
|
+
return [createEmittedFile(relativePath, content, ADAPTER_NAME4, parsed.sourcePath)];
|
|
584
|
+
}
|
|
585
|
+
emitCommand(parsed, _targetDir) {
|
|
586
|
+
const relativePath = `.agents/commands/${parsed.name}.md`;
|
|
587
|
+
const content = formatCommandContent3(parsed);
|
|
588
|
+
return [createEmittedFile(relativePath, content, ADAPTER_NAME4, parsed.sourcePath)];
|
|
589
|
+
}
|
|
590
|
+
emitRootInstructions(_profile, _targetDir) {
|
|
591
|
+
return [];
|
|
592
|
+
}
|
|
593
|
+
emitConfig(_profile, _targetDir) {
|
|
594
|
+
return [];
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Remove all ASDM-managed files from .agents/ in the given project root.
|
|
598
|
+
* Returns the list of absolute paths that were removed.
|
|
599
|
+
*/
|
|
600
|
+
async clean(projectRoot) {
|
|
601
|
+
const agentsDir = path11.join(projectRoot, ".agents");
|
|
602
|
+
if (!await exists(agentsDir)) return [];
|
|
603
|
+
const allFiles = await listFiles(agentsDir);
|
|
604
|
+
const removedPaths = [];
|
|
605
|
+
for (const filePath of allFiles) {
|
|
606
|
+
const content = await readFile(filePath);
|
|
607
|
+
if (content && content.includes("ASDM MANAGED FILE")) {
|
|
608
|
+
await removeFile(filePath);
|
|
609
|
+
removedPaths.push(filePath);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return removedPaths;
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
|
|
558
618
|
// src/cli/index.ts
|
|
559
|
-
import { defineCommand as
|
|
619
|
+
import { defineCommand as defineCommand17, runMain } from "citty";
|
|
560
620
|
|
|
561
621
|
// src/cli/commands/init.ts
|
|
562
622
|
init_fs();
|
|
@@ -703,6 +763,26 @@ function parseRegistryUrl(url) {
|
|
|
703
763
|
}
|
|
704
764
|
return { org: match[1], repo: match[2] };
|
|
705
765
|
}
|
|
766
|
+
function validateProjectConfig(config, label) {
|
|
767
|
+
if (!config.registry) {
|
|
768
|
+
throw new ConfigError(`Missing required field 'registry' in ${label}`);
|
|
769
|
+
}
|
|
770
|
+
if (!config.profile) {
|
|
771
|
+
throw new ConfigError(`Missing required field 'profile' in ${label}`);
|
|
772
|
+
}
|
|
773
|
+
parseRegistryUrl(config.registry);
|
|
774
|
+
}
|
|
775
|
+
async function readProjectConfigFromPath(filePath) {
|
|
776
|
+
const config = await readJson(filePath);
|
|
777
|
+
if (!config) {
|
|
778
|
+
throw new ConfigError(
|
|
779
|
+
`No config found at ${filePath}`,
|
|
780
|
+
"Run `asdm init` to initialize"
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
validateProjectConfig(config, path3.basename(filePath));
|
|
784
|
+
return config;
|
|
785
|
+
}
|
|
706
786
|
async function readProjectConfig(cwd) {
|
|
707
787
|
const filePath = path3.join(cwd, PROJECT_CONFIG_FILE);
|
|
708
788
|
const config = await readJson(filePath);
|
|
@@ -712,13 +792,7 @@ async function readProjectConfig(cwd) {
|
|
|
712
792
|
"Run `asdm init --profile <name>` to initialize"
|
|
713
793
|
);
|
|
714
794
|
}
|
|
715
|
-
|
|
716
|
-
throw new ConfigError(`Missing required field 'registry' in ${PROJECT_CONFIG_FILE}`);
|
|
717
|
-
}
|
|
718
|
-
if (!config.profile) {
|
|
719
|
-
throw new ConfigError(`Missing required field 'profile' in ${PROJECT_CONFIG_FILE}`);
|
|
720
|
-
}
|
|
721
|
-
parseRegistryUrl(config.registry);
|
|
795
|
+
validateProjectConfig(config, PROJECT_CONFIG_FILE);
|
|
722
796
|
return config;
|
|
723
797
|
}
|
|
724
798
|
async function readUserConfig(cwd) {
|
|
@@ -753,8 +827,7 @@ Run \`asdm profiles\` to see available profiles`
|
|
|
753
827
|
policy
|
|
754
828
|
};
|
|
755
829
|
}
|
|
756
|
-
async function
|
|
757
|
-
const filePath = path3.join(cwd, PROJECT_CONFIG_FILE);
|
|
830
|
+
async function createProjectConfigAtPath(filePath, registry, profile, providers = ["opencode"]) {
|
|
758
831
|
const config = {
|
|
759
832
|
$schema: "https://asdm.dev/schemas/config.schema.json",
|
|
760
833
|
registry,
|
|
@@ -763,6 +836,10 @@ async function createProjectConfig(cwd, registry, profile, providers = ["opencod
|
|
|
763
836
|
};
|
|
764
837
|
await writeJson(filePath, config);
|
|
765
838
|
}
|
|
839
|
+
async function createProjectConfig(cwd, registry, profile, providers = ["opencode"]) {
|
|
840
|
+
const filePath = path3.join(cwd, PROJECT_CONFIG_FILE);
|
|
841
|
+
await createProjectConfigAtPath(filePath, registry, profile, providers);
|
|
842
|
+
}
|
|
766
843
|
|
|
767
844
|
// src/core/telemetry.ts
|
|
768
845
|
init_hash();
|
|
@@ -779,7 +856,7 @@ var TelemetryWriter = class {
|
|
|
779
856
|
const fullEvent = {
|
|
780
857
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
781
858
|
machineId: machineId(),
|
|
782
|
-
version: "0.
|
|
859
|
+
version: "0.3.0",
|
|
783
860
|
...event
|
|
784
861
|
};
|
|
785
862
|
const line = JSON.stringify(fullEvent) + "\n";
|
|
@@ -948,7 +1025,12 @@ var init_default = defineCommand({
|
|
|
948
1025
|
},
|
|
949
1026
|
force: {
|
|
950
1027
|
type: "boolean",
|
|
951
|
-
description: "Overwrite existing
|
|
1028
|
+
description: "Overwrite existing config",
|
|
1029
|
+
default: false
|
|
1030
|
+
},
|
|
1031
|
+
global: {
|
|
1032
|
+
type: "boolean",
|
|
1033
|
+
description: "Write config to ~/.config/asdm/config.json instead of .asdm.json",
|
|
952
1034
|
default: false
|
|
953
1035
|
},
|
|
954
1036
|
gitignore: {
|
|
@@ -959,15 +1041,36 @@ var init_default = defineCommand({
|
|
|
959
1041
|
},
|
|
960
1042
|
async run(ctx) {
|
|
961
1043
|
const cwd = process.cwd();
|
|
1044
|
+
const profile = ctx.args.profile || "base";
|
|
1045
|
+
const registry = ctx.args.registry || DEFAULT_REGISTRY;
|
|
1046
|
+
const providers = ["opencode"];
|
|
1047
|
+
if (ctx.args.global) {
|
|
1048
|
+
const targetPath = getGlobalConfigPath();
|
|
1049
|
+
const alreadyExists2 = await exists(targetPath);
|
|
1050
|
+
if (alreadyExists2 && !ctx.args.force) {
|
|
1051
|
+
logger.warn(`Global config already exists at ${targetPath}. Use --force to overwrite.`);
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
try {
|
|
1055
|
+
await ensureDir(path5.dirname(targetPath));
|
|
1056
|
+
await createProjectConfigAtPath(targetPath, registry, profile, providers);
|
|
1057
|
+
logger.success(`Global config written to ${targetPath}`);
|
|
1058
|
+
logger.info(`Registry: ${registry}`);
|
|
1059
|
+
logger.info("Next step: run `asdm sync --global` to install agents, skills, and commands");
|
|
1060
|
+
} catch (err) {
|
|
1061
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1062
|
+
logger.error(message);
|
|
1063
|
+
process.exitCode = 1;
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
962
1068
|
const configPath = path5.join(cwd, ".asdm.json");
|
|
963
1069
|
const alreadyExists = await exists(configPath);
|
|
964
1070
|
if (alreadyExists && !ctx.args.force) {
|
|
965
1071
|
logger.warn(".asdm.json already exists. Use --force to overwrite.");
|
|
966
1072
|
return;
|
|
967
1073
|
}
|
|
968
|
-
const profile = ctx.args.profile || "base";
|
|
969
|
-
const registry = ctx.args.registry || DEFAULT_REGISTRY;
|
|
970
|
-
const providers = ["opencode"];
|
|
971
1074
|
try {
|
|
972
1075
|
await createProjectConfig(cwd, registry, profile, providers);
|
|
973
1076
|
logger.success(`Initialized .asdm.json with profile "${profile}"`);
|
|
@@ -997,9 +1100,10 @@ var init_default = defineCommand({
|
|
|
997
1100
|
|
|
998
1101
|
// src/cli/commands/sync.ts
|
|
999
1102
|
import { defineCommand as defineCommand2 } from "citty";
|
|
1103
|
+
import path13 from "path";
|
|
1000
1104
|
|
|
1001
1105
|
// src/core/syncer.ts
|
|
1002
|
-
import
|
|
1106
|
+
import path12 from "path";
|
|
1003
1107
|
|
|
1004
1108
|
// src/core/registry-client.ts
|
|
1005
1109
|
var GITHUB_API_BASE = "https://api.github.com";
|
|
@@ -1268,16 +1372,16 @@ function diffManifest(manifest, localShas, assetPaths) {
|
|
|
1268
1372
|
function getProfileAssetPaths(manifest, agentNames, skillNames, commandNames) {
|
|
1269
1373
|
const paths = [];
|
|
1270
1374
|
for (const name of agentNames) {
|
|
1271
|
-
const
|
|
1272
|
-
if (manifest.assets[
|
|
1375
|
+
const path22 = `agents/${name}.asdm.md`;
|
|
1376
|
+
if (manifest.assets[path22]) paths.push(path22);
|
|
1273
1377
|
}
|
|
1274
1378
|
for (const name of skillNames) {
|
|
1275
|
-
const
|
|
1276
|
-
if (manifest.assets[
|
|
1379
|
+
const path22 = `skills/${name}/SKILL.asdm.md`;
|
|
1380
|
+
if (manifest.assets[path22]) paths.push(path22);
|
|
1277
1381
|
}
|
|
1278
1382
|
for (const name of commandNames) {
|
|
1279
|
-
const
|
|
1280
|
-
if (manifest.assets[
|
|
1383
|
+
const path22 = `commands/${name}.asdm.md`;
|
|
1384
|
+
if (manifest.assets[path22]) paths.push(path22);
|
|
1281
1385
|
}
|
|
1282
1386
|
return paths;
|
|
1283
1387
|
}
|
|
@@ -1397,12 +1501,17 @@ async function loadAdapters(providers) {
|
|
|
1397
1501
|
adapters.push(createCopilotAdapter2());
|
|
1398
1502
|
break;
|
|
1399
1503
|
}
|
|
1504
|
+
case "agents-dir": {
|
|
1505
|
+
const { createAgentsDirAdapter: createAgentsDirAdapter2 } = await Promise.resolve().then(() => (init_agents_dir(), agents_dir_exports));
|
|
1506
|
+
adapters.push(createAgentsDirAdapter2());
|
|
1507
|
+
break;
|
|
1508
|
+
}
|
|
1400
1509
|
}
|
|
1401
1510
|
}
|
|
1402
1511
|
return adapters;
|
|
1403
1512
|
}
|
|
1404
1513
|
async function getCliVersion() {
|
|
1405
|
-
return "0.
|
|
1514
|
+
return "0.3.0";
|
|
1406
1515
|
}
|
|
1407
1516
|
async function sync(options) {
|
|
1408
1517
|
const startTime = Date.now();
|
|
@@ -1410,7 +1519,8 @@ async function sync(options) {
|
|
|
1410
1519
|
options.telemetry?.write({ event: "sync.started" }).catch(() => {
|
|
1411
1520
|
});
|
|
1412
1521
|
try {
|
|
1413
|
-
const
|
|
1522
|
+
const configFilePath = options.configPath ?? path12.join(cwd, ".asdm.json");
|
|
1523
|
+
const projectConfig = await readProjectConfigFromPath(configFilePath);
|
|
1414
1524
|
const userConfig = await readUserConfig(cwd);
|
|
1415
1525
|
const client = new RegistryClient(projectConfig.registry);
|
|
1416
1526
|
const manifest = await client.getLatestManifest();
|
|
@@ -1435,7 +1545,7 @@ async function sync(options) {
|
|
|
1435
1545
|
}
|
|
1436
1546
|
const diff = diffManifest(manifest, localSourceShas, assetPaths);
|
|
1437
1547
|
const toDownload = options.force ? assetPaths : [...diff.added, ...diff.updated];
|
|
1438
|
-
const cacheDir =
|
|
1548
|
+
const cacheDir = path12.join(getAsdmCacheDir(), manifest.version);
|
|
1439
1549
|
await ensureDir(cacheDir);
|
|
1440
1550
|
const downloadedAssets = /* @__PURE__ */ new Map();
|
|
1441
1551
|
for (const assetPath of toDownload) {
|
|
@@ -1449,17 +1559,16 @@ async function sync(options) {
|
|
|
1449
1559
|
"The asset may have been tampered with or the manifest is stale. Run `asdm sync --force`."
|
|
1450
1560
|
);
|
|
1451
1561
|
}
|
|
1452
|
-
const cachedPath =
|
|
1562
|
+
const cachedPath = path12.join(cacheDir, assetPath);
|
|
1453
1563
|
if (!options.dryRun) {
|
|
1454
1564
|
await writeFile(cachedPath, content);
|
|
1455
1565
|
}
|
|
1456
1566
|
downloadedAssets.set(assetPath, content);
|
|
1457
1567
|
}
|
|
1458
1568
|
for (const assetPath of diff.unchanged) {
|
|
1459
|
-
const cachedPath =
|
|
1569
|
+
const cachedPath = path12.join(cacheDir, assetPath);
|
|
1460
1570
|
try {
|
|
1461
|
-
const
|
|
1462
|
-
const cached = await readFile2(cachedPath);
|
|
1571
|
+
const cached = await readFile(cachedPath);
|
|
1463
1572
|
if (cached) downloadedAssets.set(assetPath, cached);
|
|
1464
1573
|
} catch {
|
|
1465
1574
|
}
|
|
@@ -1520,7 +1629,7 @@ async function sync(options) {
|
|
|
1520
1629
|
}
|
|
1521
1630
|
const resolvedPaths = /* @__PURE__ */ new Map();
|
|
1522
1631
|
for (const emittedFile of allEmittedFiles) {
|
|
1523
|
-
const absolutePath = options.global ? resolveGlobalEmitPath(emittedFile.relativePath, emittedFile.adapter) :
|
|
1632
|
+
const absolutePath = options.global ? resolveGlobalEmitPath(emittedFile.relativePath, emittedFile.adapter) : path12.join(cwd, emittedFile.relativePath);
|
|
1524
1633
|
if (absolutePath !== null) {
|
|
1525
1634
|
resolvedPaths.set(emittedFile.relativePath, absolutePath);
|
|
1526
1635
|
}
|
|
@@ -1581,6 +1690,19 @@ async function sync(options) {
|
|
|
1581
1690
|
}
|
|
1582
1691
|
|
|
1583
1692
|
// src/cli/commands/sync.ts
|
|
1693
|
+
init_fs();
|
|
1694
|
+
async function resolveConfigPath(cwd, isGlobal) {
|
|
1695
|
+
const localPath = path13.join(cwd, ".asdm.json");
|
|
1696
|
+
if (await exists(localPath)) return localPath;
|
|
1697
|
+
if (isGlobal) {
|
|
1698
|
+
const globalPath = getGlobalConfigPath();
|
|
1699
|
+
if (await exists(globalPath)) return globalPath;
|
|
1700
|
+
}
|
|
1701
|
+
throw new ConfigError(
|
|
1702
|
+
"No config found.",
|
|
1703
|
+
isGlobal ? "Run `asdm init` (project) or `asdm init --global` (machine-wide setup)." : "Run `asdm init` to initialize this project."
|
|
1704
|
+
);
|
|
1705
|
+
}
|
|
1584
1706
|
var sync_default = defineCommand2({
|
|
1585
1707
|
meta: {
|
|
1586
1708
|
name: "sync",
|
|
@@ -1623,8 +1745,10 @@ var sync_default = defineCommand2({
|
|
|
1623
1745
|
logger.asdm("Starting sync\u2026");
|
|
1624
1746
|
const telemetry = new TelemetryWriter(cwd);
|
|
1625
1747
|
try {
|
|
1748
|
+
const configPath = await resolveConfigPath(cwd, ctx.args.global ?? false);
|
|
1626
1749
|
const result = await sync({
|
|
1627
1750
|
cwd,
|
|
1751
|
+
configPath,
|
|
1628
1752
|
force: ctx.args.force,
|
|
1629
1753
|
dryRun,
|
|
1630
1754
|
verbose,
|
|
@@ -1666,7 +1790,7 @@ var sync_default = defineCommand2({
|
|
|
1666
1790
|
import { defineCommand as defineCommand3 } from "citty";
|
|
1667
1791
|
|
|
1668
1792
|
// src/core/verifier.ts
|
|
1669
|
-
import
|
|
1793
|
+
import path14 from "path";
|
|
1670
1794
|
init_hash();
|
|
1671
1795
|
init_fs();
|
|
1672
1796
|
var VERIFY_EXIT_CODES = {
|
|
@@ -1691,7 +1815,7 @@ async function verify(cwd, latestManifestVersion, onlyManaged = true, telemetry,
|
|
|
1691
1815
|
let checkedFiles = 0;
|
|
1692
1816
|
const filesToCheck = onlyManaged ? Object.entries(lockfile.files).filter(([, entry]) => entry.managed) : Object.entries(lockfile.files);
|
|
1693
1817
|
for (const [relativePath, entry] of filesToCheck) {
|
|
1694
|
-
const absolutePath = isGlobal ? resolveGlobalEmitPath(relativePath, entry.adapter) ??
|
|
1818
|
+
const absolutePath = isGlobal ? resolveGlobalEmitPath(relativePath, entry.adapter) ?? path14.join(cwd, relativePath) : path14.join(cwd, relativePath);
|
|
1695
1819
|
checkedFiles++;
|
|
1696
1820
|
const fileExists = await exists(absolutePath);
|
|
1697
1821
|
if (!fileExists) {
|
|
@@ -1736,6 +1860,16 @@ async function verify(cwd, latestManifestVersion, onlyManaged = true, telemetry,
|
|
|
1736
1860
|
|
|
1737
1861
|
// src/cli/commands/verify.ts
|
|
1738
1862
|
init_fs();
|
|
1863
|
+
async function fetchLatestManifestVersion(cwd) {
|
|
1864
|
+
try {
|
|
1865
|
+
const config = await readProjectConfig(cwd);
|
|
1866
|
+
const client = new RegistryClient(config.registry);
|
|
1867
|
+
const manifest = await client.getLatestManifest();
|
|
1868
|
+
return manifest.version;
|
|
1869
|
+
} catch {
|
|
1870
|
+
return void 0;
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1739
1873
|
var verify_default = defineCommand3({
|
|
1740
1874
|
meta: {
|
|
1741
1875
|
name: "verify",
|
|
@@ -1761,6 +1895,11 @@ var verify_default = defineCommand3({
|
|
|
1761
1895
|
type: "boolean",
|
|
1762
1896
|
description: "Verify files installed to global provider config directories",
|
|
1763
1897
|
default: false
|
|
1898
|
+
},
|
|
1899
|
+
offline: {
|
|
1900
|
+
type: "boolean",
|
|
1901
|
+
description: "Skip remote manifest version check (exit code 3 will never trigger)",
|
|
1902
|
+
default: false
|
|
1764
1903
|
}
|
|
1765
1904
|
},
|
|
1766
1905
|
async run(ctx) {
|
|
@@ -1788,8 +1927,12 @@ var verify_default = defineCommand3({
|
|
|
1788
1927
|
process.exitCode = result.exitCode;
|
|
1789
1928
|
return;
|
|
1790
1929
|
}
|
|
1930
|
+
let latestManifestVersion;
|
|
1931
|
+
if (!ctx.args.offline && !ctx.args.global) {
|
|
1932
|
+
latestManifestVersion = await fetchLatestManifestVersion(cwd);
|
|
1933
|
+
}
|
|
1791
1934
|
try {
|
|
1792
|
-
const result = await verify(cwd,
|
|
1935
|
+
const result = await verify(cwd, latestManifestVersion, true, telemetry, lockfilePath);
|
|
1793
1936
|
if (useJson) {
|
|
1794
1937
|
console.log(JSON.stringify(result, null, 2));
|
|
1795
1938
|
process.exitCode = result.exitCode;
|
|
@@ -2300,7 +2443,7 @@ var version_default = defineCommand10({
|
|
|
2300
2443
|
description: "Print CLI version and environment info"
|
|
2301
2444
|
},
|
|
2302
2445
|
run(_ctx) {
|
|
2303
|
-
console.log(`asdm v${"0.
|
|
2446
|
+
console.log(`asdm v${"0.3.0"}`);
|
|
2304
2447
|
console.log(`node ${process.version}`);
|
|
2305
2448
|
console.log(`os ${os3.type()} ${os3.release()} (${process.platform})`);
|
|
2306
2449
|
}
|
|
@@ -2308,11 +2451,11 @@ var version_default = defineCommand10({
|
|
|
2308
2451
|
|
|
2309
2452
|
// src/cli/commands/doctor.ts
|
|
2310
2453
|
import { defineCommand as defineCommand11 } from "citty";
|
|
2311
|
-
import
|
|
2454
|
+
import path16 from "path";
|
|
2312
2455
|
|
|
2313
2456
|
// src/core/overlay.ts
|
|
2314
2457
|
init_fs();
|
|
2315
|
-
import
|
|
2458
|
+
import path15 from "path";
|
|
2316
2459
|
import { promises as fs4 } from "fs";
|
|
2317
2460
|
var OVERLAY_SEPARATOR = [
|
|
2318
2461
|
"",
|
|
@@ -2323,7 +2466,7 @@ var OVERLAY_SEPARATOR = [
|
|
|
2323
2466
|
""
|
|
2324
2467
|
].join("\n");
|
|
2325
2468
|
async function readOverlays(projectRoot) {
|
|
2326
|
-
const overlaysDir =
|
|
2469
|
+
const overlaysDir = path15.join(projectRoot, "overlays");
|
|
2327
2470
|
const dirExists = await exists(overlaysDir);
|
|
2328
2471
|
if (!dirExists) return /* @__PURE__ */ new Map();
|
|
2329
2472
|
const entries = await fs4.readdir(overlaysDir, { withFileTypes: true });
|
|
@@ -2331,7 +2474,7 @@ async function readOverlays(projectRoot) {
|
|
|
2331
2474
|
for (const entry of entries) {
|
|
2332
2475
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
2333
2476
|
const agentId = entry.name.slice(0, -".md".length);
|
|
2334
|
-
const overlayPath =
|
|
2477
|
+
const overlayPath = path15.join(overlaysDir, entry.name);
|
|
2335
2478
|
const content = await readFile(overlayPath);
|
|
2336
2479
|
if (content === null) continue;
|
|
2337
2480
|
overlayMap.set(agentId, { agentId, content, path: overlayPath });
|
|
@@ -2362,7 +2505,7 @@ var doctor_default = defineCommand11({
|
|
|
2362
2505
|
let anyFailed = false;
|
|
2363
2506
|
let projectConfig = null;
|
|
2364
2507
|
let registryClient = null;
|
|
2365
|
-
const configPath =
|
|
2508
|
+
const configPath = path16.join(cwd, ".asdm.json");
|
|
2366
2509
|
const hasConfig = await exists(configPath);
|
|
2367
2510
|
checks.push({
|
|
2368
2511
|
label: ".asdm.json present",
|
|
@@ -2427,10 +2570,10 @@ var doctor_default = defineCommand11({
|
|
|
2427
2570
|
}
|
|
2428
2571
|
const managedEntries = Object.entries(lockfile.files).filter(([, e]) => e.managed);
|
|
2429
2572
|
const missingFiles = [];
|
|
2430
|
-
const resolvedCwd =
|
|
2573
|
+
const resolvedCwd = path16.resolve(cwd);
|
|
2431
2574
|
for (const [filePath] of managedEntries) {
|
|
2432
|
-
const absPath =
|
|
2433
|
-
if (!absPath.startsWith(resolvedCwd +
|
|
2575
|
+
const absPath = path16.resolve(cwd, filePath);
|
|
2576
|
+
if (!absPath.startsWith(resolvedCwd + path16.sep) && absPath !== resolvedCwd) {
|
|
2434
2577
|
continue;
|
|
2435
2578
|
}
|
|
2436
2579
|
const fileExists = await exists(absPath);
|
|
@@ -2485,7 +2628,7 @@ var doctor_default = defineCommand11({
|
|
|
2485
2628
|
detail: "Skipped \u2014 no lockfile"
|
|
2486
2629
|
});
|
|
2487
2630
|
}
|
|
2488
|
-
const gitignoreContent = await readFile(
|
|
2631
|
+
const gitignoreContent = await readFile(path16.join(cwd, ".gitignore"));
|
|
2489
2632
|
const hasAsdmBlock = gitignoreContent?.includes(ASDM_MARKER_START) ?? false;
|
|
2490
2633
|
checks.push({
|
|
2491
2634
|
label: ".gitignore has ASDM block",
|
|
@@ -2539,7 +2682,7 @@ var doctor_default = defineCommand11({
|
|
|
2539
2682
|
|
|
2540
2683
|
// src/cli/commands/clean.ts
|
|
2541
2684
|
import { defineCommand as defineCommand12 } from "citty";
|
|
2542
|
-
import
|
|
2685
|
+
import path17 from "path";
|
|
2543
2686
|
import { promises as fs5 } from "fs";
|
|
2544
2687
|
import readline from "readline";
|
|
2545
2688
|
init_fs();
|
|
@@ -2581,9 +2724,14 @@ var clean_default = defineCommand12({
|
|
|
2581
2724
|
description: "Preview what would be removed without deleting",
|
|
2582
2725
|
default: false
|
|
2583
2726
|
},
|
|
2727
|
+
global: {
|
|
2728
|
+
type: "boolean",
|
|
2729
|
+
description: "Clean files installed to global provider config directories",
|
|
2730
|
+
default: false
|
|
2731
|
+
},
|
|
2584
2732
|
target: {
|
|
2585
2733
|
type: "string",
|
|
2586
|
-
description: "Only clean files for a specific provider (opencode | claude-code | copilot)",
|
|
2734
|
+
description: "Only clean files for a specific provider (opencode | claude-code | copilot | agents-dir)",
|
|
2587
2735
|
alias: "t"
|
|
2588
2736
|
}
|
|
2589
2737
|
},
|
|
@@ -2591,119 +2739,226 @@ var clean_default = defineCommand12({
|
|
|
2591
2739
|
const cwd = process.cwd();
|
|
2592
2740
|
const dryRun = ctx.args["dry-run"];
|
|
2593
2741
|
const target = ctx.args.target;
|
|
2742
|
+
const isGlobal = ctx.args.global ?? false;
|
|
2594
2743
|
if (dryRun) {
|
|
2595
2744
|
logger.info("Dry run \u2014 no files will be removed");
|
|
2596
2745
|
}
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
logger.warn("No lockfile found \u2014 nothing to clean");
|
|
2746
|
+
if (isGlobal) {
|
|
2747
|
+
await runGlobalClean(dryRun, target);
|
|
2600
2748
|
return;
|
|
2601
2749
|
}
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2750
|
+
await runLocalClean(cwd, dryRun, target);
|
|
2751
|
+
}
|
|
2752
|
+
});
|
|
2753
|
+
async function runGlobalClean(dryRun, target) {
|
|
2754
|
+
const globalLockfilePath = getGlobalLockfilePath();
|
|
2755
|
+
const lockfile = await readLockfile(process.cwd(), globalLockfilePath);
|
|
2756
|
+
if (!lockfile) {
|
|
2757
|
+
logger.warn("No global lockfile found \u2014 nothing to clean");
|
|
2758
|
+
return;
|
|
2759
|
+
}
|
|
2760
|
+
const managedEntries = Object.entries(lockfile.files).filter(([, entry]) => {
|
|
2761
|
+
if (!entry.managed) return false;
|
|
2762
|
+
if (target) return entry.adapter === target;
|
|
2763
|
+
return true;
|
|
2764
|
+
});
|
|
2765
|
+
if (managedEntries.length === 0) {
|
|
2766
|
+
if (target) {
|
|
2767
|
+
logger.warn(`No globally managed files found for provider "${target}"`);
|
|
2768
|
+
} else {
|
|
2769
|
+
logger.warn("No globally managed files found \u2014 nothing to clean");
|
|
2770
|
+
}
|
|
2771
|
+
return;
|
|
2772
|
+
}
|
|
2773
|
+
if (!dryRun && process.stdout.isTTY && process.stdin.isTTY) {
|
|
2774
|
+
const suffix = target ? ` for provider "${target}"` : "";
|
|
2775
|
+
const confirmed = await confirmPrompt(
|
|
2776
|
+
`About to delete ${managedEntries.length} globally managed file(s)${suffix}. Continue? [y/N] `
|
|
2777
|
+
);
|
|
2778
|
+
if (!confirmed) {
|
|
2779
|
+
logger.info("Aborted \u2014 no files were removed");
|
|
2613
2780
|
return;
|
|
2614
2781
|
}
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2782
|
+
}
|
|
2783
|
+
logger.asdm(`Cleaning ${managedEntries.length} globally managed file(s)\u2026`);
|
|
2784
|
+
logger.divider();
|
|
2785
|
+
let removed = 0;
|
|
2786
|
+
let skippedMissing = 0;
|
|
2787
|
+
let totalBytesFreed = 0;
|
|
2788
|
+
for (const [relativePath, entry] of managedEntries) {
|
|
2789
|
+
const absolutePath = resolveGlobalEmitPath(relativePath, entry.adapter);
|
|
2790
|
+
if (absolutePath === null) {
|
|
2791
|
+
logger.dim(` skip ${relativePath} (project-root file, not applicable in global mode)`);
|
|
2792
|
+
skippedMissing++;
|
|
2793
|
+
continue;
|
|
2794
|
+
}
|
|
2795
|
+
const filePresent = await exists(absolutePath);
|
|
2796
|
+
if (!filePresent) {
|
|
2797
|
+
logger.dim(` skip ${absolutePath} (not found)`);
|
|
2798
|
+
skippedMissing++;
|
|
2799
|
+
continue;
|
|
2800
|
+
}
|
|
2801
|
+
if (dryRun) {
|
|
2802
|
+
logger.bullet(`would remove: ${absolutePath}`);
|
|
2803
|
+
removed++;
|
|
2804
|
+
continue;
|
|
2805
|
+
}
|
|
2806
|
+
const fileSize = await getFileSizeBytes(absolutePath);
|
|
2807
|
+
await removeFile(absolutePath);
|
|
2808
|
+
totalBytesFreed += fileSize;
|
|
2809
|
+
logger.bullet(`removed: ${absolutePath}`);
|
|
2810
|
+
removed++;
|
|
2811
|
+
}
|
|
2812
|
+
const lockfilePresent = await exists(globalLockfilePath);
|
|
2813
|
+
if (target) {
|
|
2814
|
+
if (!dryRun && lockfilePresent) {
|
|
2815
|
+
const updatedFiles = Object.fromEntries(
|
|
2816
|
+
Object.entries(lockfile.files).filter(([, entry]) => entry.adapter !== target)
|
|
2629
2817
|
);
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2818
|
+
const hasRemainingEntries = Object.keys(updatedFiles).length > 0;
|
|
2819
|
+
if (hasRemainingEntries) {
|
|
2820
|
+
await writeLockfile(process.cwd(), { ...lockfile, files: updatedFiles }, globalLockfilePath);
|
|
2821
|
+
logger.bullet(`updated: global lockfile (removed ${target} entries)`);
|
|
2822
|
+
} else {
|
|
2823
|
+
await removeFile(globalLockfilePath);
|
|
2824
|
+
logger.bullet(`removed: global lockfile (no entries remaining)`);
|
|
2633
2825
|
}
|
|
2826
|
+
} else if (dryRun) {
|
|
2827
|
+
logger.bullet(`would update: global lockfile (remove ${target} entries)`);
|
|
2634
2828
|
}
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
let removed = 0;
|
|
2638
|
-
let skippedMissing = 0;
|
|
2639
|
-
let totalBytesFreed = 0;
|
|
2640
|
-
for (const relativePath of safePaths) {
|
|
2641
|
-
const absolutePath = path15.resolve(cwd, relativePath);
|
|
2642
|
-
const fileExists = await exists(absolutePath);
|
|
2643
|
-
if (!fileExists) {
|
|
2644
|
-
logger.dim(` skip ${relativePath} (not found)`);
|
|
2645
|
-
skippedMissing++;
|
|
2646
|
-
continue;
|
|
2647
|
-
}
|
|
2829
|
+
} else {
|
|
2830
|
+
if (lockfilePresent) {
|
|
2648
2831
|
if (dryRun) {
|
|
2649
|
-
logger.bullet(`would remove:
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
logger.bullet(`removed: ${relativePath}`);
|
|
2657
|
-
removed++;
|
|
2832
|
+
logger.bullet(`would remove: global lockfile`);
|
|
2833
|
+
} else {
|
|
2834
|
+
const lockfileSize = await getFileSizeBytes(globalLockfilePath);
|
|
2835
|
+
await removeFile(globalLockfilePath);
|
|
2836
|
+
totalBytesFreed += lockfileSize;
|
|
2837
|
+
logger.bullet(`removed: global lockfile`);
|
|
2838
|
+
}
|
|
2658
2839
|
}
|
|
2659
|
-
|
|
2660
|
-
|
|
2840
|
+
}
|
|
2841
|
+
logger.divider();
|
|
2842
|
+
if (dryRun) {
|
|
2843
|
+
const suffix = target ? ` for provider "${target}"` : "";
|
|
2844
|
+
logger.info(`${removed} file(s) would be removed${suffix}, ${skippedMissing} skipped`);
|
|
2845
|
+
logger.info("Run without --dry-run to actually remove them");
|
|
2846
|
+
} else {
|
|
2847
|
+
const suffix = target ? ` (${target})` : "";
|
|
2848
|
+
logger.success(`Cleaned ${removed} globally managed file(s)${suffix} \u2014 ${formatBytes(totalBytesFreed)} freed`);
|
|
2849
|
+
if (skippedMissing > 0) logger.dim(` ${skippedMissing} file(s) were already missing`);
|
|
2850
|
+
logger.info("Run `asdm sync --global` to reinstall");
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
async function runLocalClean(cwd, dryRun, target) {
|
|
2854
|
+
const lockfile = await readLockfile(cwd);
|
|
2855
|
+
if (!lockfile) {
|
|
2856
|
+
logger.warn("No lockfile found \u2014 nothing to clean");
|
|
2857
|
+
return;
|
|
2858
|
+
}
|
|
2859
|
+
const managedEntries = Object.entries(lockfile.files).filter(([, entry]) => {
|
|
2860
|
+
if (!entry.managed) return false;
|
|
2861
|
+
if (target) return entry.adapter === target;
|
|
2862
|
+
return true;
|
|
2863
|
+
});
|
|
2864
|
+
if (managedEntries.length === 0) {
|
|
2661
2865
|
if (target) {
|
|
2662
|
-
|
|
2663
|
-
const updatedFiles = Object.fromEntries(
|
|
2664
|
-
Object.entries(lockfile.files).filter(([, entry]) => entry.adapter !== target)
|
|
2665
|
-
);
|
|
2666
|
-
await writeLockfile(cwd, { ...lockfile, files: updatedFiles });
|
|
2667
|
-
logger.bullet(`updated: ${LOCKFILE_NAME} (removed ${target} entries)`);
|
|
2668
|
-
} else if (dryRun) {
|
|
2669
|
-
logger.bullet(`would update: ${LOCKFILE_NAME} (remove ${target} entries)`);
|
|
2670
|
-
}
|
|
2866
|
+
logger.warn(`No managed files found for provider "${target}"`);
|
|
2671
2867
|
} else {
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2868
|
+
logger.warn("No managed files found \u2014 nothing to clean");
|
|
2869
|
+
}
|
|
2870
|
+
return;
|
|
2871
|
+
}
|
|
2872
|
+
const managedPaths = managedEntries.map(([filePath]) => filePath);
|
|
2873
|
+
const resolvedCwd = path17.resolve(cwd);
|
|
2874
|
+
const safePaths = managedPaths.filter((relativePath) => {
|
|
2875
|
+
const absPath = path17.resolve(cwd, relativePath);
|
|
2876
|
+
return absPath.startsWith(resolvedCwd + path17.sep) || absPath === resolvedCwd;
|
|
2877
|
+
});
|
|
2878
|
+
const skippedSuspicious = managedPaths.length - safePaths.length;
|
|
2879
|
+
if (skippedSuspicious > 0) {
|
|
2880
|
+
logger.warn(`Skipping ${skippedSuspicious} path(s) outside project root`);
|
|
2881
|
+
}
|
|
2882
|
+
if (!dryRun && process.stdout.isTTY && process.stdin.isTTY) {
|
|
2883
|
+
const suffix = target ? ` for provider "${target}"` : "";
|
|
2884
|
+
const confirmed = await confirmPrompt(
|
|
2885
|
+
`About to delete ${safePaths.length} file(s)${suffix}. Continue? [y/N] `
|
|
2886
|
+
);
|
|
2887
|
+
if (!confirmed) {
|
|
2888
|
+
logger.info("Aborted \u2014 no files were removed");
|
|
2889
|
+
return;
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
logger.asdm(`Cleaning ${safePaths.length} managed file(s)\u2026`);
|
|
2893
|
+
logger.divider();
|
|
2894
|
+
let removed = 0;
|
|
2895
|
+
let skippedMissing = 0;
|
|
2896
|
+
let totalBytesFreed = 0;
|
|
2897
|
+
for (const relativePath of safePaths) {
|
|
2898
|
+
const absolutePath = path17.resolve(cwd, relativePath);
|
|
2899
|
+
const filePresent = await exists(absolutePath);
|
|
2900
|
+
if (!filePresent) {
|
|
2901
|
+
logger.dim(` skip ${relativePath} (not found)`);
|
|
2902
|
+
skippedMissing++;
|
|
2903
|
+
continue;
|
|
2682
2904
|
}
|
|
2683
|
-
logger.divider();
|
|
2684
2905
|
if (dryRun) {
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
}
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2906
|
+
logger.bullet(`would remove: ${relativePath}`);
|
|
2907
|
+
removed++;
|
|
2908
|
+
continue;
|
|
2909
|
+
}
|
|
2910
|
+
const fileSize = await getFileSizeBytes(absolutePath);
|
|
2911
|
+
await removeFile(absolutePath);
|
|
2912
|
+
totalBytesFreed += fileSize;
|
|
2913
|
+
logger.bullet(`removed: ${relativePath}`);
|
|
2914
|
+
removed++;
|
|
2915
|
+
}
|
|
2916
|
+
const lockfilePath = path17.join(cwd, LOCKFILE_NAME);
|
|
2917
|
+
const lockfileOnDisk = await exists(lockfilePath);
|
|
2918
|
+
if (target) {
|
|
2919
|
+
if (!dryRun && lockfileOnDisk) {
|
|
2920
|
+
const updatedFiles = Object.fromEntries(
|
|
2921
|
+
Object.entries(lockfile.files).filter(([, entry]) => entry.adapter !== target)
|
|
2922
|
+
);
|
|
2923
|
+
await writeLockfile(cwd, { ...lockfile, files: updatedFiles });
|
|
2924
|
+
logger.bullet(`updated: ${LOCKFILE_NAME} (removed ${target} entries)`);
|
|
2925
|
+
} else if (dryRun) {
|
|
2926
|
+
logger.bullet(`would update: ${LOCKFILE_NAME} (remove ${target} entries)`);
|
|
2927
|
+
}
|
|
2928
|
+
} else {
|
|
2929
|
+
if (lockfileOnDisk) {
|
|
2930
|
+
if (dryRun) {
|
|
2931
|
+
logger.bullet(`would remove: ${LOCKFILE_NAME}`);
|
|
2932
|
+
} else {
|
|
2933
|
+
const lockfileSize = await getFileSizeBytes(lockfilePath);
|
|
2934
|
+
await removeFile(lockfilePath);
|
|
2935
|
+
totalBytesFreed += lockfileSize;
|
|
2936
|
+
logger.bullet(`removed: ${LOCKFILE_NAME}`);
|
|
2937
|
+
}
|
|
2693
2938
|
}
|
|
2694
2939
|
}
|
|
2695
|
-
|
|
2940
|
+
logger.divider();
|
|
2941
|
+
if (dryRun) {
|
|
2942
|
+
const suffix = target ? ` for provider "${target}"` : "";
|
|
2943
|
+
logger.info(`${removed} file(s) would be removed${suffix}, ${skippedMissing} not found`);
|
|
2944
|
+
logger.info("Run without --dry-run to actually remove them");
|
|
2945
|
+
} else {
|
|
2946
|
+
const suffix = target ? ` (${target})` : "";
|
|
2947
|
+
logger.success(`Cleaned ${removed} managed file(s)${suffix} \u2014 ${formatBytes(totalBytesFreed)} freed`);
|
|
2948
|
+
if (skippedMissing > 0) logger.dim(` ${skippedMissing} file(s) were already missing`);
|
|
2949
|
+
logger.info("Run `asdm sync` to reinstall");
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2696
2952
|
|
|
2697
2953
|
// src/cli/commands/hooks.ts
|
|
2698
2954
|
init_fs();
|
|
2699
2955
|
import { defineCommand as defineCommand13 } from "citty";
|
|
2700
|
-
import
|
|
2956
|
+
import path19 from "path";
|
|
2701
2957
|
import { promises as fs6 } from "fs";
|
|
2702
2958
|
|
|
2703
2959
|
// src/utils/post-merge-hook.ts
|
|
2704
|
-
function
|
|
2705
|
-
return
|
|
2706
|
-
# ASDM MANAGED \u2014 post-merge hook
|
|
2960
|
+
function generatePostMergeHookBody() {
|
|
2961
|
+
return `# ASDM MANAGED \u2014 post-merge hook
|
|
2707
2962
|
if [ -f ".asdm.yaml" ] || [ -f ".asdm.json" ]; then
|
|
2708
2963
|
echo "\u{1F504} ASDM: syncing after merge..."
|
|
2709
2964
|
npx asdm sync
|
|
@@ -2711,43 +2966,108 @@ fi
|
|
|
2711
2966
|
`;
|
|
2712
2967
|
}
|
|
2713
2968
|
|
|
2969
|
+
// src/utils/husky-detect.ts
|
|
2970
|
+
init_fs();
|
|
2971
|
+
import path18 from "path";
|
|
2972
|
+
function parseMajorVersion(versionString) {
|
|
2973
|
+
const stripped = versionString.replace(/^[^0-9]*/, "");
|
|
2974
|
+
const majorPart = stripped.split(".")[0] ?? "";
|
|
2975
|
+
const major = parseInt(majorPart, 10);
|
|
2976
|
+
return isNaN(major) ? null : major;
|
|
2977
|
+
}
|
|
2978
|
+
function majorToHuskyVersion(major) {
|
|
2979
|
+
return major >= 9 ? "v9+" : "v8";
|
|
2980
|
+
}
|
|
2981
|
+
async function detectHusky(cwd) {
|
|
2982
|
+
const huskyDirPath = path18.join(cwd, ".husky");
|
|
2983
|
+
const huskyDirExists = await exists(huskyDirPath);
|
|
2984
|
+
const pkg = await readJson(path18.join(cwd, "package.json"));
|
|
2985
|
+
const huskyVersionString = pkg?.devDependencies?.["husky"] ?? pkg?.dependencies?.["husky"];
|
|
2986
|
+
if (huskyVersionString !== void 0) {
|
|
2987
|
+
const major = parseMajorVersion(huskyVersionString);
|
|
2988
|
+
const version2 = major !== null ? majorToHuskyVersion(major) : "v9+";
|
|
2989
|
+
return {
|
|
2990
|
+
detected: true,
|
|
2991
|
+
version: version2,
|
|
2992
|
+
huskyDir: huskyDirExists ? huskyDirPath : null
|
|
2993
|
+
};
|
|
2994
|
+
}
|
|
2995
|
+
if (!huskyDirExists) {
|
|
2996
|
+
return { detected: false, version: null, huskyDir: null };
|
|
2997
|
+
}
|
|
2998
|
+
const huskyShExists = await exists(path18.join(huskyDirPath, "_", "husky.sh"));
|
|
2999
|
+
const version = huskyShExists ? "v8" : "v9+";
|
|
3000
|
+
return {
|
|
3001
|
+
detected: true,
|
|
3002
|
+
version,
|
|
3003
|
+
huskyDir: huskyDirPath
|
|
3004
|
+
};
|
|
3005
|
+
}
|
|
3006
|
+
|
|
2714
3007
|
// src/cli/commands/hooks.ts
|
|
2715
|
-
var
|
|
2716
|
-
"pre-commit":
|
|
2717
|
-
relativePath: ".git/hooks/pre-commit",
|
|
2718
|
-
content: `#!/usr/bin/env sh
|
|
2719
|
-
# ASDM \u2014 managed pre-commit hook
|
|
3008
|
+
var HOOK_BODIES = {
|
|
3009
|
+
"pre-commit": `# ASDM \u2014 managed pre-commit hook
|
|
2720
3010
|
# Verifies integrity of managed files before allowing commits.
|
|
2721
3011
|
npx asdm verify --strict --quiet
|
|
2722
3012
|
`,
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
"
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
3013
|
+
"post-merge": generatePostMergeHookBody()
|
|
3014
|
+
};
|
|
3015
|
+
var HOOK_MARKERS = {
|
|
3016
|
+
"pre-commit": "ASDM \u2014 managed pre-commit hook",
|
|
3017
|
+
"post-merge": "ASDM MANAGED \u2014 post-merge hook"
|
|
3018
|
+
};
|
|
3019
|
+
var HOOK_DESCRIPTIONS = {
|
|
3020
|
+
"pre-commit": "runs `asdm verify --strict --quiet` before every commit",
|
|
3021
|
+
"post-merge": "runs `asdm sync` after git pull/merge"
|
|
2732
3022
|
};
|
|
2733
3023
|
function resolveHookTypes(hookFlag) {
|
|
2734
|
-
if (hookFlag === "pre-commit" || hookFlag === "post-merge")
|
|
2735
|
-
return [hookFlag];
|
|
2736
|
-
}
|
|
3024
|
+
if (hookFlag === "pre-commit" || hookFlag === "post-merge") return [hookFlag];
|
|
2737
3025
|
return ["pre-commit", "post-merge"];
|
|
2738
3026
|
}
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
3027
|
+
function determineHookMode(huskyInfo, noHusky) {
|
|
3028
|
+
if (noHusky || !huskyInfo.detected) return "git";
|
|
3029
|
+
if (huskyInfo.version === "v8") return "husky-v8";
|
|
3030
|
+
return "husky-v9+";
|
|
3031
|
+
}
|
|
3032
|
+
function buildHookContent(body, mode) {
|
|
3033
|
+
if (mode === "git") return `#!/usr/bin/env sh
|
|
3034
|
+
${body}`;
|
|
3035
|
+
if (mode === "husky-v8") return `#!/usr/bin/env sh
|
|
3036
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
3037
|
+
|
|
3038
|
+
${body}`;
|
|
3039
|
+
return body;
|
|
3040
|
+
}
|
|
3041
|
+
function resolveHookDefinition(cwd, hookType, huskyInfo, noHusky) {
|
|
3042
|
+
const mode = determineHookMode(huskyInfo, noHusky);
|
|
3043
|
+
const body = HOOK_BODIES[hookType];
|
|
3044
|
+
const marker = HOOK_MARKERS[hookType];
|
|
3045
|
+
const description = HOOK_DESCRIPTIONS[hookType];
|
|
3046
|
+
const relativePath = mode === "git" ? `.git/hooks/${hookType}` : `.husky/${hookType}`;
|
|
3047
|
+
const content = buildHookContent(body, mode);
|
|
3048
|
+
return {
|
|
3049
|
+
absolutePath: path19.join(cwd, relativePath),
|
|
3050
|
+
relativePath,
|
|
3051
|
+
content,
|
|
3052
|
+
marker,
|
|
3053
|
+
description
|
|
3054
|
+
};
|
|
3055
|
+
}
|
|
3056
|
+
async function installHook(cwd, hookType, huskyInfo, noHusky) {
|
|
3057
|
+
const mode = determineHookMode(huskyInfo, noHusky);
|
|
3058
|
+
const def = resolveHookDefinition(cwd, hookType, huskyInfo, noHusky);
|
|
3059
|
+
if (mode === "git") {
|
|
3060
|
+
const hasGit = await exists(path19.join(cwd, ".git"));
|
|
3061
|
+
if (!hasGit) {
|
|
3062
|
+
logger.error("No .git directory found", "Run `git init` first");
|
|
3063
|
+
process.exit(1);
|
|
3064
|
+
}
|
|
3065
|
+
} else {
|
|
3066
|
+
await ensureDir(path19.join(cwd, ".husky"));
|
|
2747
3067
|
}
|
|
2748
|
-
const hookExists = await exists(
|
|
3068
|
+
const hookExists = await exists(def.absolutePath);
|
|
2749
3069
|
if (hookExists) {
|
|
2750
|
-
const existing = await fs6.readFile(
|
|
3070
|
+
const existing = await fs6.readFile(def.absolutePath, "utf-8");
|
|
2751
3071
|
if (existing.includes(def.marker)) {
|
|
2752
3072
|
logger.info(`ASDM ${hookType} hook is already installed`);
|
|
2753
3073
|
return;
|
|
@@ -2756,30 +3076,42 @@ async function installHook(cwd, hookType) {
|
|
|
2756
3076
|
logger.warn(`Manual action required: add the ASDM logic to ${def.relativePath}`);
|
|
2757
3077
|
process.exit(1);
|
|
2758
3078
|
}
|
|
2759
|
-
await writeFile(
|
|
3079
|
+
await writeFile(def.absolutePath, def.content);
|
|
2760
3080
|
try {
|
|
2761
|
-
await fs6.chmod(
|
|
3081
|
+
await fs6.chmod(def.absolutePath, 493);
|
|
2762
3082
|
} catch {
|
|
2763
3083
|
}
|
|
2764
3084
|
logger.success(`Installed ${hookType} hook at ${def.relativePath}`);
|
|
2765
3085
|
logger.info(`The hook ${def.description}`);
|
|
2766
3086
|
}
|
|
2767
3087
|
async function uninstallHook(cwd, hookType) {
|
|
2768
|
-
const
|
|
2769
|
-
const
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
3088
|
+
const marker = HOOK_MARKERS[hookType];
|
|
3089
|
+
const candidates = [
|
|
3090
|
+
{
|
|
3091
|
+
relativePath: `.git/hooks/${hookType}`,
|
|
3092
|
+
absolutePath: path19.join(cwd, ".git", "hooks", hookType)
|
|
3093
|
+
},
|
|
3094
|
+
{
|
|
3095
|
+
relativePath: `.husky/${hookType}`,
|
|
3096
|
+
absolutePath: path19.join(cwd, ".husky", hookType)
|
|
3097
|
+
}
|
|
3098
|
+
];
|
|
3099
|
+
let removed = false;
|
|
3100
|
+
for (const { relativePath, absolutePath } of candidates) {
|
|
3101
|
+
const hookExists = await exists(absolutePath);
|
|
3102
|
+
if (!hookExists) continue;
|
|
3103
|
+
const content = await fs6.readFile(absolutePath, "utf-8");
|
|
3104
|
+
if (!content.includes(marker)) {
|
|
3105
|
+
logger.warn(`A ${hookType} hook at ${relativePath} was not installed by ASDM \u2014 skipping`);
|
|
3106
|
+
continue;
|
|
3107
|
+
}
|
|
3108
|
+
await removeFile(absolutePath);
|
|
3109
|
+
logger.success(`Removed ASDM ${hookType} hook from ${relativePath}`);
|
|
3110
|
+
removed = true;
|
|
2774
3111
|
}
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
logger.warn(`The ${hookType} hook was not installed by ASDM \u2014 not removing it`);
|
|
2778
|
-
logger.info(`If you want to remove it manually: rm ${def.relativePath}`);
|
|
2779
|
-
process.exit(1);
|
|
3112
|
+
if (!removed) {
|
|
3113
|
+
logger.info(`No ASDM-managed ${hookType} hook found \u2014 nothing to remove`);
|
|
2780
3114
|
}
|
|
2781
|
-
await removeFile(hookPath);
|
|
2782
|
-
logger.success(`Removed ASDM ${hookType} hook from ${def.relativePath}`);
|
|
2783
3115
|
}
|
|
2784
3116
|
var installCommand = defineCommand13({
|
|
2785
3117
|
meta: {
|
|
@@ -2791,13 +3123,25 @@ var installCommand = defineCommand13({
|
|
|
2791
3123
|
type: "string",
|
|
2792
3124
|
description: "Which hook to install: pre-commit | post-merge | all",
|
|
2793
3125
|
default: "all"
|
|
3126
|
+
},
|
|
3127
|
+
"no-husky": {
|
|
3128
|
+
type: "boolean",
|
|
3129
|
+
description: "Force .git/hooks/ mode even when Husky is detected",
|
|
3130
|
+
default: false
|
|
2794
3131
|
}
|
|
2795
3132
|
},
|
|
2796
3133
|
async run(ctx) {
|
|
2797
3134
|
const cwd = process.cwd();
|
|
2798
3135
|
const hookTypes = resolveHookTypes(ctx.args.hook);
|
|
3136
|
+
const noHusky = ctx.args["no-husky"] ?? false;
|
|
3137
|
+
const huskyInfo = noHusky ? { detected: false, version: null, huskyDir: null } : await detectHusky(cwd);
|
|
3138
|
+
if (huskyInfo.detected) {
|
|
3139
|
+
logger.info(`Using Husky hooks in .husky/ (${huskyInfo.version})`);
|
|
3140
|
+
} else {
|
|
3141
|
+
logger.info("Using Git hooks in .git/hooks/");
|
|
3142
|
+
}
|
|
2799
3143
|
for (const hookType of hookTypes) {
|
|
2800
|
-
await installHook(cwd, hookType);
|
|
3144
|
+
await installHook(cwd, hookType, huskyInfo, noHusky);
|
|
2801
3145
|
}
|
|
2802
3146
|
}
|
|
2803
3147
|
});
|
|
@@ -2973,9 +3317,204 @@ var telemetry_default = defineCommand15({
|
|
|
2973
3317
|
}
|
|
2974
3318
|
});
|
|
2975
3319
|
|
|
3320
|
+
// src/cli/commands/templates.ts
|
|
3321
|
+
init_fs();
|
|
3322
|
+
import { defineCommand as defineCommand16 } from "citty";
|
|
3323
|
+
import path20 from "path";
|
|
3324
|
+
function generateAgentTemplate(name) {
|
|
3325
|
+
return `---
|
|
3326
|
+
name: ${name}
|
|
3327
|
+
type: agent
|
|
3328
|
+
description: "Short description"
|
|
3329
|
+
version: "1.0.0"
|
|
3330
|
+
tags:
|
|
3331
|
+
- tag1
|
|
3332
|
+
- tag2
|
|
3333
|
+
providers:
|
|
3334
|
+
opencode:
|
|
3335
|
+
model: claude-sonnet-4-5
|
|
3336
|
+
permissions: {}
|
|
3337
|
+
tools: []
|
|
3338
|
+
claude-code:
|
|
3339
|
+
model: claude-opus-4-5
|
|
3340
|
+
allowedTools: []
|
|
3341
|
+
copilot:
|
|
3342
|
+
on:
|
|
3343
|
+
push:
|
|
3344
|
+
branches: [main]
|
|
3345
|
+
permissions:
|
|
3346
|
+
contents: read
|
|
3347
|
+
---
|
|
3348
|
+
|
|
3349
|
+
# ${name}
|
|
3350
|
+
|
|
3351
|
+
## Role
|
|
3352
|
+
Describe the agent's primary responsibility here.
|
|
3353
|
+
|
|
3354
|
+
## Instructions
|
|
3355
|
+
- Instruction 1
|
|
3356
|
+
- Instruction 2
|
|
3357
|
+
|
|
3358
|
+
## Guidelines
|
|
3359
|
+
- Guideline 1
|
|
3360
|
+
- Guideline 2
|
|
3361
|
+
`;
|
|
3362
|
+
}
|
|
3363
|
+
function generateSkillTemplate(name) {
|
|
3364
|
+
return `---
|
|
3365
|
+
name: ${name}
|
|
3366
|
+
type: skill
|
|
3367
|
+
description: "Short description"
|
|
3368
|
+
version: "1.0.0"
|
|
3369
|
+
tags:
|
|
3370
|
+
- tag1
|
|
3371
|
+
---
|
|
3372
|
+
|
|
3373
|
+
# ${name}
|
|
3374
|
+
|
|
3375
|
+
## Overview
|
|
3376
|
+
Describe the skill purpose.
|
|
3377
|
+
|
|
3378
|
+
## Usage
|
|
3379
|
+
How to use this skill.
|
|
3380
|
+
|
|
3381
|
+
## Examples
|
|
3382
|
+
- Example 1
|
|
3383
|
+
- Example 2
|
|
3384
|
+
`;
|
|
3385
|
+
}
|
|
3386
|
+
function generateCommandTemplate(name) {
|
|
3387
|
+
return `---
|
|
3388
|
+
name: ${name}
|
|
3389
|
+
type: command
|
|
3390
|
+
description: "Short description"
|
|
3391
|
+
version: "1.0.0"
|
|
3392
|
+
tags:
|
|
3393
|
+
- tag1
|
|
3394
|
+
---
|
|
3395
|
+
|
|
3396
|
+
# /${name}
|
|
3397
|
+
|
|
3398
|
+
## Description
|
|
3399
|
+
What this command does.
|
|
3400
|
+
|
|
3401
|
+
## Usage
|
|
3402
|
+
\`/${name} [options]\`
|
|
3403
|
+
|
|
3404
|
+
## Examples
|
|
3405
|
+
- \`/${name}\` \u2014 basic usage
|
|
3406
|
+
`;
|
|
3407
|
+
}
|
|
3408
|
+
async function writeTemplateFile(outputDir, name, content, force) {
|
|
3409
|
+
const filePath = path20.join(outputDir, `${name}.asdm.md`);
|
|
3410
|
+
const alreadyExists = await exists(filePath);
|
|
3411
|
+
if (alreadyExists && !force) {
|
|
3412
|
+
logger.error(
|
|
3413
|
+
`File already exists: ${filePath}`,
|
|
3414
|
+
"Use --force to overwrite"
|
|
3415
|
+
);
|
|
3416
|
+
process.exitCode = 1;
|
|
3417
|
+
return;
|
|
3418
|
+
}
|
|
3419
|
+
await writeFile(filePath, content);
|
|
3420
|
+
logger.success(`Created ${filePath}`);
|
|
3421
|
+
}
|
|
3422
|
+
var agentCommand = defineCommand16({
|
|
3423
|
+
meta: {
|
|
3424
|
+
name: "agent",
|
|
3425
|
+
description: "Scaffold an agent definition (.asdm.md)"
|
|
3426
|
+
},
|
|
3427
|
+
args: {
|
|
3428
|
+
name: {
|
|
3429
|
+
type: "positional",
|
|
3430
|
+
description: "Agent identifier (used as filename and frontmatter name)",
|
|
3431
|
+
required: true
|
|
3432
|
+
},
|
|
3433
|
+
output: {
|
|
3434
|
+
type: "string",
|
|
3435
|
+
description: "Output directory (default: current working directory)"
|
|
3436
|
+
},
|
|
3437
|
+
force: {
|
|
3438
|
+
type: "boolean",
|
|
3439
|
+
description: "Overwrite existing file",
|
|
3440
|
+
default: false
|
|
3441
|
+
}
|
|
3442
|
+
},
|
|
3443
|
+
async run(ctx) {
|
|
3444
|
+
const cwd = process.cwd();
|
|
3445
|
+
const outputDir = ctx.args.output ?? cwd;
|
|
3446
|
+
await writeTemplateFile(outputDir, ctx.args.name, generateAgentTemplate(ctx.args.name), ctx.args.force);
|
|
3447
|
+
}
|
|
3448
|
+
});
|
|
3449
|
+
var skillCommand = defineCommand16({
|
|
3450
|
+
meta: {
|
|
3451
|
+
name: "skill",
|
|
3452
|
+
description: "Scaffold a skill definition (.asdm.md)"
|
|
3453
|
+
},
|
|
3454
|
+
args: {
|
|
3455
|
+
name: {
|
|
3456
|
+
type: "positional",
|
|
3457
|
+
description: "Skill identifier (used as filename and frontmatter name)",
|
|
3458
|
+
required: true
|
|
3459
|
+
},
|
|
3460
|
+
output: {
|
|
3461
|
+
type: "string",
|
|
3462
|
+
description: "Output directory (default: current working directory)"
|
|
3463
|
+
},
|
|
3464
|
+
force: {
|
|
3465
|
+
type: "boolean",
|
|
3466
|
+
description: "Overwrite existing file",
|
|
3467
|
+
default: false
|
|
3468
|
+
}
|
|
3469
|
+
},
|
|
3470
|
+
async run(ctx) {
|
|
3471
|
+
const cwd = process.cwd();
|
|
3472
|
+
const outputDir = ctx.args.output ?? cwd;
|
|
3473
|
+
await writeTemplateFile(outputDir, ctx.args.name, generateSkillTemplate(ctx.args.name), ctx.args.force);
|
|
3474
|
+
}
|
|
3475
|
+
});
|
|
3476
|
+
var commandCommand = defineCommand16({
|
|
3477
|
+
meta: {
|
|
3478
|
+
name: "command",
|
|
3479
|
+
description: "Scaffold a slash-command definition (.asdm.md)"
|
|
3480
|
+
},
|
|
3481
|
+
args: {
|
|
3482
|
+
name: {
|
|
3483
|
+
type: "positional",
|
|
3484
|
+
description: "Command identifier (used as filename and frontmatter name)",
|
|
3485
|
+
required: true
|
|
3486
|
+
},
|
|
3487
|
+
output: {
|
|
3488
|
+
type: "string",
|
|
3489
|
+
description: "Output directory (default: current working directory)"
|
|
3490
|
+
},
|
|
3491
|
+
force: {
|
|
3492
|
+
type: "boolean",
|
|
3493
|
+
description: "Overwrite existing file",
|
|
3494
|
+
default: false
|
|
3495
|
+
}
|
|
3496
|
+
},
|
|
3497
|
+
async run(ctx) {
|
|
3498
|
+
const cwd = process.cwd();
|
|
3499
|
+
const outputDir = ctx.args.output ?? cwd;
|
|
3500
|
+
await writeTemplateFile(outputDir, ctx.args.name, generateCommandTemplate(ctx.args.name), ctx.args.force);
|
|
3501
|
+
}
|
|
3502
|
+
});
|
|
3503
|
+
var templates_default = defineCommand16({
|
|
3504
|
+
meta: {
|
|
3505
|
+
name: "templates",
|
|
3506
|
+
description: "Scaffold new .asdm.md asset files from built-in templates"
|
|
3507
|
+
},
|
|
3508
|
+
subCommands: {
|
|
3509
|
+
agent: agentCommand,
|
|
3510
|
+
skill: skillCommand,
|
|
3511
|
+
command: commandCommand
|
|
3512
|
+
}
|
|
3513
|
+
});
|
|
3514
|
+
|
|
2976
3515
|
// src/core/version-check.ts
|
|
2977
3516
|
init_fs();
|
|
2978
|
-
import
|
|
3517
|
+
import path21 from "path";
|
|
2979
3518
|
var CACHE_FILENAME = "version-check-cache.json";
|
|
2980
3519
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
2981
3520
|
var FETCH_TIMEOUT_MS2 = 3e3;
|
|
@@ -2996,7 +3535,7 @@ function isNewerVersion(current, latest) {
|
|
|
2996
3535
|
}
|
|
2997
3536
|
async function checkForUpdate(currentVersion) {
|
|
2998
3537
|
const cacheDir = getAsdmConfigDir();
|
|
2999
|
-
const cachePath =
|
|
3538
|
+
const cachePath = path21.join(cacheDir, CACHE_FILENAME);
|
|
3000
3539
|
const cache = await readJson(cachePath);
|
|
3001
3540
|
if (cache) {
|
|
3002
3541
|
const ageMs = Date.now() - new Date(cache.checkedAt).getTime();
|
|
@@ -3032,10 +3571,10 @@ async function checkForUpdate(currentVersion) {
|
|
|
3032
3571
|
}
|
|
3033
3572
|
|
|
3034
3573
|
// src/cli/index.ts
|
|
3035
|
-
var rootCommand =
|
|
3574
|
+
var rootCommand = defineCommand17({
|
|
3036
3575
|
meta: {
|
|
3037
3576
|
name: "asdm",
|
|
3038
|
-
version: "0.
|
|
3577
|
+
version: "0.3.0",
|
|
3039
3578
|
description: "Agentic Software Delivery Model \u2014 Write Once, Emit Many"
|
|
3040
3579
|
},
|
|
3041
3580
|
subCommands: {
|
|
@@ -3053,7 +3592,8 @@ var rootCommand = defineCommand16({
|
|
|
3053
3592
|
clean: clean_default,
|
|
3054
3593
|
hooks: hooks_default,
|
|
3055
3594
|
gitignore: gitignore_default,
|
|
3056
|
-
telemetry: telemetry_default
|
|
3595
|
+
telemetry: telemetry_default,
|
|
3596
|
+
templates: templates_default
|
|
3057
3597
|
}
|
|
3058
3598
|
});
|
|
3059
3599
|
function printUpdateBox(currentVersion, latestVersion) {
|
|
@@ -3081,9 +3621,9 @@ async function main() {
|
|
|
3081
3621
|
await runMain(rootCommand);
|
|
3082
3622
|
if (process.exitCode !== void 0 && process.exitCode !== 0) return;
|
|
3083
3623
|
try {
|
|
3084
|
-
const latestVersion = await checkForUpdate("0.
|
|
3624
|
+
const latestVersion = await checkForUpdate("0.3.0");
|
|
3085
3625
|
if (latestVersion) {
|
|
3086
|
-
printUpdateBox("0.
|
|
3626
|
+
printUpdateBox("0.3.0", latestVersion);
|
|
3087
3627
|
}
|
|
3088
3628
|
} catch {
|
|
3089
3629
|
}
|