akm-cli 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +332 -7
- package/dist/common.js +5 -0
- package/dist/config-cli.js +87 -0
- package/dist/config.js +197 -25
- package/dist/init.js +2 -2
- package/dist/install-audit.js +324 -0
- package/dist/installed-kits.js +2 -2
- package/dist/matchers.js +25 -22
- package/dist/registry-install.js +46 -7
- package/dist/setup.js +2 -2
- package/dist/stash-add.js +4 -3
- package/dist/stash-source-manage.js +3 -3
- package/package.json +1 -1
package/dist/matchers.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* - `extensionMatcher` (3) -- classifies any file by extension alone.
|
|
9
9
|
* Ensures every known file type is discoverable regardless of directory.
|
|
10
|
-
* - `directoryMatcher` (10) -- boosts specificity when
|
|
10
|
+
* - `directoryMatcher` (10) -- boosts specificity when an ancestor
|
|
11
11
|
* directory matches a known type name (e.g. `scripts/`, `agents/`).
|
|
12
12
|
* - `parentDirHintMatcher` (15) -- boosts specificity based on the
|
|
13
13
|
* immediate parent directory name.
|
|
@@ -43,31 +43,34 @@ export function extensionMatcher(ctx) {
|
|
|
43
43
|
}
|
|
44
44
|
// ── directoryMatcher (specificity: 10) ──────────────────────────────────────
|
|
45
45
|
/**
|
|
46
|
-
* Directory-based matcher that boosts specificity when
|
|
46
|
+
* Directory-based matcher that boosts specificity when an ancestor
|
|
47
47
|
* directory segment from the stash root matches a known type name.
|
|
48
|
+
*
|
|
49
|
+
* The first matching type-like ancestor wins. This preserves intuitive
|
|
50
|
+
* behavior for nested kit layouts such as `agent-stash/agents/blog/foo.md`
|
|
51
|
+
* while still honoring earlier type roots like `commands/agents/foo.md`.
|
|
48
52
|
*/
|
|
49
53
|
export function directoryMatcher(ctx) {
|
|
50
|
-
const topDir = ctx.ancestorDirs[0];
|
|
51
|
-
if (!topDir)
|
|
52
|
-
return null;
|
|
53
54
|
const ext = ctx.ext;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
55
|
+
for (const dir of ctx.ancestorDirs) {
|
|
56
|
+
if (dir === "scripts" && SCRIPT_EXTENSIONS.has(ext)) {
|
|
57
|
+
return { type: "script", specificity: 10, renderer: "script-source" };
|
|
58
|
+
}
|
|
59
|
+
if (dir === "skills" && ctx.fileName === "SKILL.md") {
|
|
60
|
+
return { type: "skill", specificity: 10, renderer: "skill-md" };
|
|
61
|
+
}
|
|
62
|
+
if (dir === "commands" && ext === ".md") {
|
|
63
|
+
return { type: "command", specificity: 10, renderer: "command-md" };
|
|
64
|
+
}
|
|
65
|
+
if (dir === "agents" && ext === ".md") {
|
|
66
|
+
return { type: "agent", specificity: 10, renderer: "agent-md" };
|
|
67
|
+
}
|
|
68
|
+
if (dir === "knowledge" && ext === ".md") {
|
|
69
|
+
return { type: "knowledge", specificity: 10, renderer: "knowledge-md" };
|
|
70
|
+
}
|
|
71
|
+
if (dir === "memories" && ext === ".md") {
|
|
72
|
+
return { type: "memory", specificity: 10, renderer: "memory-md" };
|
|
73
|
+
}
|
|
71
74
|
}
|
|
72
75
|
return null;
|
|
73
76
|
}
|
package/dist/registry-install.js
CHANGED
|
@@ -4,7 +4,8 @@ import fs from "node:fs";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { TYPE_DIRS } from "./asset-spec";
|
|
6
6
|
import { fetchWithRetry, isWithin } from "./common";
|
|
7
|
-
import { loadConfig, saveConfig } from "./config";
|
|
7
|
+
import { loadConfig, loadUserConfig, saveConfig } from "./config";
|
|
8
|
+
import { auditInstallCandidate, deriveRegistryLabels, enforceRegistryInstallPolicy, formatInstallAuditFailure, } from "./install-audit";
|
|
8
9
|
import { copyIncludedPaths, findNearestIncludeConfig } from "./kit-include";
|
|
9
10
|
import { getRegistryCacheDir as _getRegistryCacheDir } from "./paths";
|
|
10
11
|
import { parseRegistryRef, resolveRegistryArtifact, validateGitRef, validateGitUrl } from "./registry-resolve";
|
|
@@ -12,13 +13,20 @@ import { warn } from "./warn";
|
|
|
12
13
|
const REGISTRY_STASH_DIR_NAMES = new Set(Object.values(TYPE_DIRS));
|
|
13
14
|
export async function installRegistryRef(ref, options) {
|
|
14
15
|
const parsed = parseRegistryRef(ref);
|
|
16
|
+
const config = loadConfig();
|
|
15
17
|
if (parsed.source === "local") {
|
|
16
|
-
return installLocalRegistryRef(parsed, options);
|
|
18
|
+
return installLocalRegistryRef(parsed, config, options);
|
|
17
19
|
}
|
|
18
20
|
if (parsed.source === "git") {
|
|
19
|
-
return installGitRegistryRef(parsed, options);
|
|
21
|
+
return installGitRegistryRef(parsed, config, options);
|
|
20
22
|
}
|
|
21
23
|
const resolved = await resolveRegistryArtifact(parsed);
|
|
24
|
+
const registryLabels = deriveRegistryLabels({
|
|
25
|
+
source: resolved.source,
|
|
26
|
+
ref: resolved.ref,
|
|
27
|
+
artifactUrl: resolved.artifactUrl,
|
|
28
|
+
});
|
|
29
|
+
enforceRegistryInstallPolicy(registryLabels, config, ref);
|
|
22
30
|
const installedAt = (options?.now ?? new Date()).toISOString();
|
|
23
31
|
const cacheRootDir = options?.cacheRootDir ?? getRegistryCacheRootDir();
|
|
24
32
|
const cacheDir = buildInstallCacheDir(cacheRootDir, resolved.source, resolved.id, resolved.resolvedVersion ?? resolved.resolvedRevision);
|
|
@@ -30,6 +38,7 @@ export async function installRegistryRef(ref, options) {
|
|
|
30
38
|
const cachedStashRoot = detectStashRoot(extractedDir);
|
|
31
39
|
if (cachedStashRoot) {
|
|
32
40
|
const integrity = fs.existsSync(archivePath) ? await computeFileHash(archivePath) : undefined;
|
|
41
|
+
const audit = runInstallAuditOrThrow(extractedDir, resolved.source, resolved.ref, registryLabels, config);
|
|
33
42
|
return {
|
|
34
43
|
id: resolved.id,
|
|
35
44
|
source: resolved.source,
|
|
@@ -42,6 +51,7 @@ export async function installRegistryRef(ref, options) {
|
|
|
42
51
|
extractedDir,
|
|
43
52
|
stashRoot: cachedStashRoot,
|
|
44
53
|
integrity,
|
|
54
|
+
audit,
|
|
45
55
|
};
|
|
46
56
|
}
|
|
47
57
|
}
|
|
@@ -54,11 +64,13 @@ export async function installRegistryRef(ref, options) {
|
|
|
54
64
|
let provisionalKitRoot;
|
|
55
65
|
let installRoot;
|
|
56
66
|
let stashRoot;
|
|
67
|
+
let audit;
|
|
57
68
|
try {
|
|
58
69
|
await downloadArchive(resolved.artifactUrl, archivePath);
|
|
59
70
|
verifyArchiveIntegrity(archivePath, resolved.resolvedRevision, resolved.source);
|
|
60
71
|
integrity = await computeFileHash(archivePath);
|
|
61
72
|
extractTarGzSecure(archivePath, extractedDir);
|
|
73
|
+
audit = runInstallAuditOrThrow(extractedDir, resolved.source, resolved.ref, registryLabels, config);
|
|
62
74
|
provisionalKitRoot = detectStashRoot(extractedDir);
|
|
63
75
|
installRoot = applyAkmIncludeConfig(provisionalKitRoot, cacheDir, extractedDir) ?? provisionalKitRoot;
|
|
64
76
|
stashRoot = detectStashRoot(installRoot);
|
|
@@ -86,11 +98,18 @@ export async function installRegistryRef(ref, options) {
|
|
|
86
98
|
extractedDir,
|
|
87
99
|
stashRoot,
|
|
88
100
|
integrity,
|
|
101
|
+
audit,
|
|
89
102
|
};
|
|
90
103
|
}
|
|
91
|
-
async function installLocalRegistryRef(parsed, options) {
|
|
104
|
+
async function installLocalRegistryRef(parsed, config, options) {
|
|
92
105
|
const resolved = await resolveRegistryArtifact(parsed);
|
|
93
106
|
const installedAt = (options?.now ?? new Date()).toISOString();
|
|
107
|
+
const registryLabels = deriveRegistryLabels({
|
|
108
|
+
source: resolved.source,
|
|
109
|
+
ref: resolved.ref,
|
|
110
|
+
artifactUrl: resolved.artifactUrl,
|
|
111
|
+
});
|
|
112
|
+
const audit = runInstallAuditOrThrow(parsed.sourcePath, resolved.source, resolved.ref, registryLabels, config);
|
|
94
113
|
// For local directories, detect the stash root within the source path.
|
|
95
114
|
// If no nested stash is found, the source path itself is used.
|
|
96
115
|
const stashRoot = detectStashRoot(parsed.sourcePath);
|
|
@@ -105,10 +124,18 @@ async function installLocalRegistryRef(parsed, options) {
|
|
|
105
124
|
cacheDir: parsed.sourcePath,
|
|
106
125
|
extractedDir: parsed.sourcePath,
|
|
107
126
|
stashRoot,
|
|
127
|
+
audit,
|
|
108
128
|
};
|
|
109
129
|
}
|
|
110
|
-
async function installGitRegistryRef(parsed, options) {
|
|
130
|
+
async function installGitRegistryRef(parsed, config, options) {
|
|
111
131
|
const resolved = await resolveRegistryArtifact(parsed);
|
|
132
|
+
const registryLabels = deriveRegistryLabels({
|
|
133
|
+
source: resolved.source,
|
|
134
|
+
ref: resolved.ref,
|
|
135
|
+
artifactUrl: resolved.artifactUrl,
|
|
136
|
+
gitUrl: parsed.url,
|
|
137
|
+
});
|
|
138
|
+
enforceRegistryInstallPolicy(registryLabels, config, parsed.ref);
|
|
112
139
|
const installedAt = (options?.now ?? new Date()).toISOString();
|
|
113
140
|
const cacheRootDir = options?.cacheRootDir ?? getRegistryCacheRootDir();
|
|
114
141
|
const cacheDir = buildInstallCacheDir(cacheRootDir, parsed.source, parsed.id, resolved.resolvedRevision);
|
|
@@ -121,6 +148,7 @@ async function installGitRegistryRef(parsed, options) {
|
|
|
121
148
|
const installRoot = applyAkmIncludeConfig(provisionalKitRoot, cacheDir, extractedDir) ?? provisionalKitRoot;
|
|
122
149
|
const stashRoot = detectStashRoot(installRoot);
|
|
123
150
|
if (stashRoot) {
|
|
151
|
+
const audit = runInstallAuditOrThrow(extractedDir, resolved.source, resolved.ref, registryLabels, config);
|
|
124
152
|
return {
|
|
125
153
|
id: resolved.id,
|
|
126
154
|
source: resolved.source,
|
|
@@ -132,6 +160,7 @@ async function installGitRegistryRef(parsed, options) {
|
|
|
132
160
|
cacheDir,
|
|
133
161
|
extractedDir,
|
|
134
162
|
stashRoot,
|
|
163
|
+
audit,
|
|
135
164
|
};
|
|
136
165
|
}
|
|
137
166
|
}
|
|
@@ -147,6 +176,7 @@ async function installGitRegistryRef(parsed, options) {
|
|
|
147
176
|
let provisionalKitRoot;
|
|
148
177
|
let installRoot;
|
|
149
178
|
let stashRoot;
|
|
179
|
+
let audit;
|
|
150
180
|
try {
|
|
151
181
|
const cloneArgs = ["clone", "--depth", "1"];
|
|
152
182
|
if (parsed.requestedRef) {
|
|
@@ -163,6 +193,7 @@ async function installGitRegistryRef(parsed, options) {
|
|
|
163
193
|
copyDirectoryContents(cloneDir, extractedDir);
|
|
164
194
|
// Clean up the clone dir
|
|
165
195
|
fs.rmSync(cloneDir, { recursive: true, force: true });
|
|
196
|
+
audit = runInstallAuditOrThrow(extractedDir, resolved.source, resolved.ref, registryLabels, config);
|
|
166
197
|
provisionalKitRoot = detectStashRoot(extractedDir);
|
|
167
198
|
installRoot = applyAkmIncludeConfig(provisionalKitRoot, cacheDir, extractedDir) ?? provisionalKitRoot;
|
|
168
199
|
stashRoot = detectStashRoot(installRoot);
|
|
@@ -189,10 +220,11 @@ async function installGitRegistryRef(parsed, options) {
|
|
|
189
220
|
cacheDir,
|
|
190
221
|
extractedDir,
|
|
191
222
|
stashRoot,
|
|
223
|
+
audit,
|
|
192
224
|
};
|
|
193
225
|
}
|
|
194
226
|
export function upsertInstalledRegistryEntry(entry) {
|
|
195
|
-
const current =
|
|
227
|
+
const current = loadUserConfig();
|
|
196
228
|
const currentInstalled = current.installed ?? [];
|
|
197
229
|
const withoutExisting = currentInstalled.filter((item) => item.id !== entry.id);
|
|
198
230
|
const nextInstalled = [...withoutExisting, normalizeInstalledEntry(entry)];
|
|
@@ -204,7 +236,7 @@ export function upsertInstalledRegistryEntry(entry) {
|
|
|
204
236
|
return nextConfig;
|
|
205
237
|
}
|
|
206
238
|
export function removeInstalledRegistryEntry(id) {
|
|
207
|
-
const current =
|
|
239
|
+
const current = loadUserConfig();
|
|
208
240
|
const currentInstalled = current.installed ?? [];
|
|
209
241
|
const nextInstalled = currentInstalled.filter((item) => item.id !== id);
|
|
210
242
|
const nextConfig = {
|
|
@@ -462,3 +494,10 @@ async function computeFileHash(filePath) {
|
|
|
462
494
|
const hash = createHash("sha256").update(data).digest("hex");
|
|
463
495
|
return `sha256:${hash}`;
|
|
464
496
|
}
|
|
497
|
+
function runInstallAuditOrThrow(rootDir, source, ref, registryLabels, config) {
|
|
498
|
+
const audit = auditInstallCandidate({ rootDir, source, ref, registryLabels, config });
|
|
499
|
+
if (audit.blocked) {
|
|
500
|
+
throw new Error(formatInstallAuditFailure(ref, audit));
|
|
501
|
+
}
|
|
502
|
+
return audit;
|
|
503
|
+
}
|
package/dist/setup.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import * as p from "@clack/prompts";
|
|
10
10
|
import { isHttpUrl } from "./common";
|
|
11
|
-
import { DEFAULT_CONFIG, getConfigPath,
|
|
11
|
+
import { DEFAULT_CONFIG, getConfigPath, loadUserConfig, saveConfig } from "./config";
|
|
12
12
|
import { closeDatabase, isVecAvailable, openDatabase } from "./db";
|
|
13
13
|
import { detectAgentPlatforms, detectOllama, detectOpenViking } from "./detect";
|
|
14
14
|
import { checkEmbeddingAvailability, DEFAULT_LOCAL_MODEL, isTransformersAvailable } from "./embedder";
|
|
@@ -568,7 +568,7 @@ async function stepAgentPlatforms(current) {
|
|
|
568
568
|
// ── Main Wizard ─────────────────────────────────────────────────────────────
|
|
569
569
|
export async function runSetupWizard() {
|
|
570
570
|
p.intro("akm setup");
|
|
571
|
-
const current =
|
|
571
|
+
const current = loadUserConfig();
|
|
572
572
|
const configPath = getConfigPath();
|
|
573
573
|
// Step 1: Stash directory
|
|
574
574
|
p.log.step("Step 1: Stash Directory");
|
package/dist/stash-add.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { isHttpUrl, resolveStashDir } from "./common";
|
|
4
|
-
import { loadConfig, saveConfig } from "./config";
|
|
4
|
+
import { loadConfig, loadUserConfig, saveConfig } from "./config";
|
|
5
5
|
import { UsageError } from "./errors";
|
|
6
6
|
import { akmIndex } from "./indexer";
|
|
7
7
|
import { upsertLockEntry } from "./lockfile";
|
|
@@ -36,7 +36,7 @@ export async function akmAdd(input) {
|
|
|
36
36
|
async function addLocalStashSource(ref, sourcePath, stashDir) {
|
|
37
37
|
const stashRoot = detectStashRoot(sourcePath);
|
|
38
38
|
const resolvedPath = path.resolve(stashRoot);
|
|
39
|
-
const config =
|
|
39
|
+
const config = loadUserConfig();
|
|
40
40
|
// Check for duplicates in stashes[]
|
|
41
41
|
const stashes = [...(config.stashes ?? [])];
|
|
42
42
|
const existing = stashes.find((s) => s.type === "filesystem" && s.path && path.resolve(s.path) === resolvedPath);
|
|
@@ -75,7 +75,7 @@ async function addLocalStashSource(ref, sourcePath, stashDir) {
|
|
|
75
75
|
}
|
|
76
76
|
async function addWebsiteStashSource(ref, stashDir, name, options) {
|
|
77
77
|
const normalizedUrl = validateWebsiteInputUrl(ref);
|
|
78
|
-
const config =
|
|
78
|
+
const config = loadUserConfig();
|
|
79
79
|
const stashes = [...(config.stashes ?? [])];
|
|
80
80
|
let entry = stashes.find((stash) => stash.type === "website" && stash.url === normalizedUrl);
|
|
81
81
|
if (!entry) {
|
|
@@ -167,6 +167,7 @@ async function addRegistryKit(ref, stashDir) {
|
|
|
167
167
|
cacheDir: installed.cacheDir,
|
|
168
168
|
extractedDir: installed.extractedDir,
|
|
169
169
|
installedAt: installed.installedAt,
|
|
170
|
+
audit: installed.audit,
|
|
170
171
|
},
|
|
171
172
|
config: {
|
|
172
173
|
stashCount: config.stashes?.length ?? 0,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { loadConfig, saveConfig } from "./config";
|
|
2
|
+
import { loadConfig, loadUserConfig, saveConfig } from "./config";
|
|
3
3
|
import { UsageError } from "./errors";
|
|
4
4
|
import { resolveStashSources } from "./search-source";
|
|
5
5
|
// ── Operations ──────────────────────────────────────────────────────────────
|
|
@@ -12,7 +12,7 @@ import { resolveStashSources } from "./search-source";
|
|
|
12
12
|
*/
|
|
13
13
|
export function addStash(opts) {
|
|
14
14
|
const { target, name, providerType, options: providerOptions } = opts;
|
|
15
|
-
const config =
|
|
15
|
+
const config = loadUserConfig();
|
|
16
16
|
const stashes = [...(config.stashes ?? [])];
|
|
17
17
|
const isUrl = target.startsWith("http://") || target.startsWith("https://");
|
|
18
18
|
let entry;
|
|
@@ -49,7 +49,7 @@ export function addStash(opts) {
|
|
|
49
49
|
* Match priority: URL > path > name (most specific first).
|
|
50
50
|
*/
|
|
51
51
|
export function removeStash(target) {
|
|
52
|
-
const config =
|
|
52
|
+
const config = loadUserConfig();
|
|
53
53
|
const stashes = [...(config.stashes ?? [])];
|
|
54
54
|
const isUrl = target.startsWith("http://") || target.startsWith("https://");
|
|
55
55
|
const resolvedPath = !isUrl ? path.resolve(target) : undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "akm (Agent Kit Manager) — A package manager for AI agent skills, commands, tools, and knowledge. Works with Claude Code, OpenCode, Cursor, and any AI coding assistant.",
|
|
6
6
|
"keywords": [
|