agentwheel 0.15.0 → 0.16.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 +23 -0
- package/dist/index.js +737 -42
- package/openpack.json +1 -1
- package/package.json +1 -1
- package/skills/agentwheel/SKILL.md +31 -10
package/README.md
CHANGED
|
@@ -98,6 +98,7 @@ Fragments are Agentwheel composition inputs, not runtime file-drop targets.
|
|
|
98
98
|
|
|
99
99
|
| Command | Meaning |
|
|
100
100
|
|---|---|
|
|
101
|
+
| `agentwheel search <query>` | Search configured registries and the public enriched/Vercel catalogue; supports stable JSON output for agent reranking. |
|
|
101
102
|
| `agentwheel add <source>` | Validate and save a package entry in `.agentwheel/config.json`; does not touch runtimes. |
|
|
102
103
|
| `agentwheel plan [name-or-source]` | Preview what `install` would reconcile without writing; supports `--profile <name>` and `--json`. |
|
|
103
104
|
| `agentwheel install` | Reconcile configured packages into the current target or selected fleet. Uses the graph lock as input by default. |
|
|
@@ -152,6 +153,28 @@ agentwheel plan
|
|
|
152
153
|
agentwheel install
|
|
153
154
|
```
|
|
154
155
|
|
|
156
|
+
## Artifact Discovery
|
|
157
|
+
|
|
158
|
+
Search configured registries and the complete public catalogue with one command:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
agentwheel search "conversation memory"
|
|
162
|
+
agentwheel search "telegram integration" --type skill
|
|
163
|
+
agentwheel search "message recall" --json --limit 10
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Use `--scope registry`, `--scope enriched`, or `--scope vercel` to restrict a query. The default
|
|
167
|
+
`--scope all` combines every source, deduplicates equivalent artifacts, and reports every
|
|
168
|
+
provenance plus the safe installation route.
|
|
169
|
+
|
|
170
|
+
Search is deterministic and lexical. The companion skill adds semantic behavior at the agent
|
|
171
|
+
layer: it can generate a small set of related queries, merge and rerank the JSON results against
|
|
172
|
+
the original request, and suggest at most three artifacts. Search never installs or changes
|
|
173
|
+
configuration by itself.
|
|
174
|
+
|
|
175
|
+
Registry maintenance remains available through `agentwheel registry update` and
|
|
176
|
+
`agentwheel registry list`. Registry short names continue to resolve during add/install.
|
|
177
|
+
|
|
155
178
|
## Source Inputs
|
|
156
179
|
|
|
157
180
|
Agentwheel can install from explicit local paths, Git sources, catalogue short names, provider
|
package/dist/index.js
CHANGED
|
@@ -9,11 +9,11 @@ import {
|
|
|
9
9
|
} from "./chunk-PKAPR55N.js";
|
|
10
10
|
|
|
11
11
|
// src/cli/index.ts
|
|
12
|
-
import { createHash as
|
|
12
|
+
import { createHash as createHash12 } from "crypto";
|
|
13
13
|
import { existsSync } from "fs";
|
|
14
|
-
import { mkdir as mkdir23, rm as
|
|
15
|
-
import { homedir as
|
|
16
|
-
import { dirname as dirname32, join as
|
|
14
|
+
import { mkdir as mkdir23, rm as rm12, writeFile as writeFile22 } from "fs/promises";
|
|
15
|
+
import { homedir as homedir10 } from "os";
|
|
16
|
+
import { dirname as dirname32, join as join45, resolve as resolve22 } from "path";
|
|
17
17
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
18
18
|
import { Command } from "commander";
|
|
19
19
|
|
|
@@ -7759,13 +7759,6 @@ var RegistryClient = class {
|
|
|
7759
7759
|
const index = await this.getIndex(options);
|
|
7760
7760
|
return index.entries.find((entry) => entry.name === name);
|
|
7761
7761
|
}
|
|
7762
|
-
async search(query, options = {}) {
|
|
7763
|
-
const q = query.toLowerCase();
|
|
7764
|
-
const index = await this.getIndex(options);
|
|
7765
|
-
return index.entries.filter(
|
|
7766
|
-
(entry) => entry.name.toLowerCase().includes(q) || entry.description.toLowerCase().includes(q) || entry.tags.some((tag) => tag.toLowerCase().includes(q))
|
|
7767
|
-
);
|
|
7768
|
-
}
|
|
7769
7762
|
async clearCache() {
|
|
7770
7763
|
await rm8(this.cachePath, { force: true });
|
|
7771
7764
|
}
|
|
@@ -11318,6 +11311,628 @@ function valueAfter(lines, prefix) {
|
|
|
11318
11311
|
return lines.find((line) => line.startsWith(prefix))?.slice(prefix.length).trim() ?? null;
|
|
11319
11312
|
}
|
|
11320
11313
|
|
|
11314
|
+
// src/catalogue/client.ts
|
|
11315
|
+
import { createHash as createHash11 } from "crypto";
|
|
11316
|
+
import { readFile as readFile32, rm as rm11 } from "fs/promises";
|
|
11317
|
+
import { homedir as homedir9 } from "os";
|
|
11318
|
+
import { join as join44 } from "path";
|
|
11319
|
+
|
|
11320
|
+
// src/model/catalogue.ts
|
|
11321
|
+
import { z as z13 } from "zod";
|
|
11322
|
+
var searchScopeSchema = z13.enum(["all", "registry", "enriched", "vercel"]);
|
|
11323
|
+
var searchTypeSchema = z13.enum(["package", "skill", "plugin", "mcp", "adapter"]);
|
|
11324
|
+
var searchEcosystemSchema = z13.enum([
|
|
11325
|
+
"official",
|
|
11326
|
+
"openpack",
|
|
11327
|
+
"mcp-registry",
|
|
11328
|
+
"clawhub",
|
|
11329
|
+
"skillkit",
|
|
11330
|
+
"vercel"
|
|
11331
|
+
]);
|
|
11332
|
+
var catalogueProvenanceSchema = z13.enum(["registry", "enriched", "vercel"]);
|
|
11333
|
+
var installabilitySchema = z13.enum(["registry", "source", "informational"]);
|
|
11334
|
+
var nullableString = z13.string().nullable();
|
|
11335
|
+
var nullableStringArray = z13.array(z13.string()).nullable();
|
|
11336
|
+
var enrichedCatalogueEntrySchema = z13.object({
|
|
11337
|
+
id: z13.string().min(1),
|
|
11338
|
+
name: z13.string().min(1),
|
|
11339
|
+
ecosystem: searchEcosystemSchema.nullable(),
|
|
11340
|
+
type: searchTypeSchema.nullable(),
|
|
11341
|
+
description: nullableString,
|
|
11342
|
+
tags: nullableStringArray,
|
|
11343
|
+
source: nullableString,
|
|
11344
|
+
installCommand: nullableString,
|
|
11345
|
+
repoUrl: nullableString,
|
|
11346
|
+
homepageUrl: nullableString.optional(),
|
|
11347
|
+
homepageLinkLabel: nullableString.optional(),
|
|
11348
|
+
stars: z13.number().finite().nullable().optional(),
|
|
11349
|
+
lastPush: nullableString.optional(),
|
|
11350
|
+
archived: z13.boolean().nullable(),
|
|
11351
|
+
provides: nullableStringArray,
|
|
11352
|
+
version: nullableString,
|
|
11353
|
+
featured: z13.boolean().nullable().optional()
|
|
11354
|
+
});
|
|
11355
|
+
var enrichedCatalogueSchema = z13.object({
|
|
11356
|
+
schemaVersion: z13.literal(1),
|
|
11357
|
+
generatedAt: z13.string().datetime(),
|
|
11358
|
+
entries: z13.array(enrichedCatalogueEntrySchema)
|
|
11359
|
+
}).superRefine((value, context) => {
|
|
11360
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11361
|
+
value.entries.forEach((entry, index) => {
|
|
11362
|
+
if (seen.has(entry.id)) {
|
|
11363
|
+
context.addIssue({
|
|
11364
|
+
code: "custom",
|
|
11365
|
+
path: ["entries", index, "id"],
|
|
11366
|
+
message: `duplicate catalogue id: ${entry.id}`
|
|
11367
|
+
});
|
|
11368
|
+
}
|
|
11369
|
+
seen.add(entry.id);
|
|
11370
|
+
});
|
|
11371
|
+
});
|
|
11372
|
+
var vercelCatalogueEntrySchema = z13.object({
|
|
11373
|
+
o: z13.string().min(1),
|
|
11374
|
+
r: z13.string().min(1),
|
|
11375
|
+
s: z13.string().min(1),
|
|
11376
|
+
d: z13.string().nullable().optional()
|
|
11377
|
+
});
|
|
11378
|
+
var vercelCatalogueSchema = z13.object({
|
|
11379
|
+
schemaVersion: z13.literal(1),
|
|
11380
|
+
generatedAt: z13.string().datetime(),
|
|
11381
|
+
count: z13.number().int().nonnegative(),
|
|
11382
|
+
entries: z13.array(vercelCatalogueEntrySchema)
|
|
11383
|
+
}).superRefine((value, context) => {
|
|
11384
|
+
if (value.count !== value.entries.length) {
|
|
11385
|
+
context.addIssue({
|
|
11386
|
+
code: "custom",
|
|
11387
|
+
path: ["count"],
|
|
11388
|
+
message: `count must equal entries length (${value.entries.length})`
|
|
11389
|
+
});
|
|
11390
|
+
}
|
|
11391
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11392
|
+
value.entries.forEach((entry, index) => {
|
|
11393
|
+
const id = `${entry.o}/${entry.r}/${entry.s}`;
|
|
11394
|
+
if (seen.has(id)) {
|
|
11395
|
+
context.addIssue({
|
|
11396
|
+
code: "custom",
|
|
11397
|
+
path: ["entries", index],
|
|
11398
|
+
message: `duplicate Vercel catalogue id: ${id}`
|
|
11399
|
+
});
|
|
11400
|
+
}
|
|
11401
|
+
seen.add(id);
|
|
11402
|
+
});
|
|
11403
|
+
});
|
|
11404
|
+
var catalogueCacheSchema = z13.object({
|
|
11405
|
+
version: z13.literal(1),
|
|
11406
|
+
fetchedAt: z13.string().datetime(),
|
|
11407
|
+
sources: z13.tuple([z13.string().url(), z13.string().url()]),
|
|
11408
|
+
enriched: enrichedCatalogueSchema,
|
|
11409
|
+
vercel: vercelCatalogueSchema
|
|
11410
|
+
});
|
|
11411
|
+
var catalogueCacheEnvelopeSchema = z13.object({
|
|
11412
|
+
version: z13.literal(1),
|
|
11413
|
+
fetchedAt: z13.string().datetime(),
|
|
11414
|
+
sources: z13.tuple([z13.string().url(), z13.string().url()]),
|
|
11415
|
+
contentHash: z13.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
11416
|
+
enriched: z13.unknown(),
|
|
11417
|
+
vercel: z13.unknown()
|
|
11418
|
+
});
|
|
11419
|
+
var searchResultSchema = z13.object({
|
|
11420
|
+
id: z13.string().min(1),
|
|
11421
|
+
name: z13.string().min(1),
|
|
11422
|
+
description: z13.string(),
|
|
11423
|
+
type: searchTypeSchema,
|
|
11424
|
+
ecosystem: searchEcosystemSchema.optional(),
|
|
11425
|
+
tags: z13.array(z13.string()),
|
|
11426
|
+
provides: z13.array(z13.string()),
|
|
11427
|
+
source: z13.string().min(1).optional(),
|
|
11428
|
+
repoUrl: z13.string().min(1).optional(),
|
|
11429
|
+
installCommand: z13.string().min(1).optional(),
|
|
11430
|
+
installability: installabilitySchema,
|
|
11431
|
+
provenances: z13.array(catalogueProvenanceSchema).min(1),
|
|
11432
|
+
score: z13.number().int().nonnegative(),
|
|
11433
|
+
matchedFields: z13.array(z13.string())
|
|
11434
|
+
});
|
|
11435
|
+
var searchResponseSchema = z13.object({
|
|
11436
|
+
schemaVersion: z13.literal(1),
|
|
11437
|
+
query: z13.string(),
|
|
11438
|
+
scope: searchScopeSchema,
|
|
11439
|
+
fromCache: z13.boolean(),
|
|
11440
|
+
results: z13.array(searchResultSchema)
|
|
11441
|
+
});
|
|
11442
|
+
|
|
11443
|
+
// src/catalogue/client.ts
|
|
11444
|
+
var DEFAULT_ENRICHED_CATALOGUE_URL = "https://raw.githubusercontent.com/NestDevLab/agentwheel-registry/main/catalogue-data.json";
|
|
11445
|
+
var DEFAULT_VERCEL_CATALOGUE_URL = "https://raw.githubusercontent.com/NestDevLab/agentwheel-registry/main/catalogue-vercel-index.json";
|
|
11446
|
+
var DEFAULT_CATALOGUE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
11447
|
+
var MAX_CATALOGUE_PAYLOAD_BYTES = 32 * 1024 * 1024;
|
|
11448
|
+
var CatalogueClient = class {
|
|
11449
|
+
constructor(options = {}) {
|
|
11450
|
+
this.options = options;
|
|
11451
|
+
this.cachePath = options.cachePath ?? defaultCatalogueCachePath();
|
|
11452
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
11453
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
11454
|
+
this.sources = [
|
|
11455
|
+
options.enrichedUrl ?? DEFAULT_ENRICHED_CATALOGUE_URL,
|
|
11456
|
+
options.vercelUrl ?? DEFAULT_VERCEL_CATALOGUE_URL
|
|
11457
|
+
];
|
|
11458
|
+
}
|
|
11459
|
+
options;
|
|
11460
|
+
cachePath;
|
|
11461
|
+
now;
|
|
11462
|
+
fetchImpl;
|
|
11463
|
+
sources;
|
|
11464
|
+
async getIndex(options = {}) {
|
|
11465
|
+
const cached = await this.readCache();
|
|
11466
|
+
const usableCache = cached && sameSources2(cached.sources, this.sources) ? cached : void 0;
|
|
11467
|
+
const expired = usableCache ? this.isExpired(usableCache) : false;
|
|
11468
|
+
if (this.options.offline) {
|
|
11469
|
+
if (!usableCache) {
|
|
11470
|
+
throw new Error("Offline catalogue cache is missing. Run without --offline first.");
|
|
11471
|
+
}
|
|
11472
|
+
const stale = expired;
|
|
11473
|
+
this.options.warn?.(
|
|
11474
|
+
stale ? "Offline: using stale catalogue cache because refresh is disabled." : "Offline: using cached catalogue data."
|
|
11475
|
+
);
|
|
11476
|
+
return this.fromCache(usableCache, stale);
|
|
11477
|
+
}
|
|
11478
|
+
if (!options.refresh && usableCache && !expired) {
|
|
11479
|
+
return this.fromCache(usableCache, false);
|
|
11480
|
+
}
|
|
11481
|
+
try {
|
|
11482
|
+
const [enriched, vercel] = await Promise.all([
|
|
11483
|
+
this.fetchJson(this.sources[0], enrichedCatalogueSchema),
|
|
11484
|
+
this.fetchJson(this.sources[1], vercelCatalogueSchema)
|
|
11485
|
+
]);
|
|
11486
|
+
const fetchedAt = this.now().toISOString();
|
|
11487
|
+
const cache = {
|
|
11488
|
+
version: 1,
|
|
11489
|
+
fetchedAt,
|
|
11490
|
+
sources: this.sources,
|
|
11491
|
+
enriched,
|
|
11492
|
+
vercel
|
|
11493
|
+
};
|
|
11494
|
+
const cacheFile = {
|
|
11495
|
+
...cache,
|
|
11496
|
+
contentHash: catalogueContentHash(enriched, vercel)
|
|
11497
|
+
};
|
|
11498
|
+
await writeJsonAtomic(this.cachePath, cacheFile);
|
|
11499
|
+
return { enriched, vercel, sources: this.sources, fetchedAt, fromCache: false, stale: false };
|
|
11500
|
+
} catch (error) {
|
|
11501
|
+
if (!usableCache) throw error;
|
|
11502
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
11503
|
+
this.options.warn?.(`Catalogue refresh failed; using stale catalogue cache: ${reason}`);
|
|
11504
|
+
return this.fromCache(usableCache, true);
|
|
11505
|
+
}
|
|
11506
|
+
}
|
|
11507
|
+
async clearCache() {
|
|
11508
|
+
await rm11(this.cachePath, { force: true });
|
|
11509
|
+
}
|
|
11510
|
+
async readCache() {
|
|
11511
|
+
if (!await pathExists(this.cachePath)) return void 0;
|
|
11512
|
+
try {
|
|
11513
|
+
const value = JSON.parse(await readFile32(this.cachePath, "utf8"));
|
|
11514
|
+
const envelope = catalogueCacheEnvelopeSchema.parse(value);
|
|
11515
|
+
if (envelope.contentHash) {
|
|
11516
|
+
const contentHash = catalogueContentHash(envelope.enriched, envelope.vercel);
|
|
11517
|
+
if (contentHash !== envelope.contentHash) {
|
|
11518
|
+
throw new Error("catalogue cache integrity check failed");
|
|
11519
|
+
}
|
|
11520
|
+
return envelope;
|
|
11521
|
+
}
|
|
11522
|
+
return catalogueCacheSchema.parse(value);
|
|
11523
|
+
} catch (error) {
|
|
11524
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
11525
|
+
this.options.warn?.(`Ignoring invalid catalogue cache: ${reason}`);
|
|
11526
|
+
return void 0;
|
|
11527
|
+
}
|
|
11528
|
+
}
|
|
11529
|
+
isExpired(cache) {
|
|
11530
|
+
const ttlMs = this.options.ttlMs ?? DEFAULT_CATALOGUE_TTL_MS;
|
|
11531
|
+
return this.now().getTime() - new Date(cache.fetchedAt).getTime() > ttlMs;
|
|
11532
|
+
}
|
|
11533
|
+
fromCache(cache, stale) {
|
|
11534
|
+
return {
|
|
11535
|
+
enriched: cache.enriched,
|
|
11536
|
+
vercel: cache.vercel,
|
|
11537
|
+
sources: cache.sources,
|
|
11538
|
+
fetchedAt: cache.fetchedAt,
|
|
11539
|
+
fromCache: true,
|
|
11540
|
+
stale
|
|
11541
|
+
};
|
|
11542
|
+
}
|
|
11543
|
+
async fetchJson(source, schema) {
|
|
11544
|
+
const response = await this.fetchImpl(source);
|
|
11545
|
+
if (!response.ok) {
|
|
11546
|
+
throw new Error(`Catalogue source failed (${response.status}): ${source}`);
|
|
11547
|
+
}
|
|
11548
|
+
const declaredLength = response.headers.get("content-length");
|
|
11549
|
+
if (declaredLength !== null) {
|
|
11550
|
+
const bytes = Number(declaredLength);
|
|
11551
|
+
if (Number.isFinite(bytes) && bytes > MAX_CATALOGUE_PAYLOAD_BYTES) {
|
|
11552
|
+
throw new Error(`Catalogue payload exceeds 32 MiB limit: ${source}`);
|
|
11553
|
+
}
|
|
11554
|
+
}
|
|
11555
|
+
const payload = await response.arrayBuffer();
|
|
11556
|
+
if (payload.byteLength > MAX_CATALOGUE_PAYLOAD_BYTES) {
|
|
11557
|
+
throw new Error(`Catalogue payload exceeds 32 MiB limit: ${source}`);
|
|
11558
|
+
}
|
|
11559
|
+
let value;
|
|
11560
|
+
try {
|
|
11561
|
+
value = JSON.parse(new TextDecoder().decode(payload));
|
|
11562
|
+
} catch {
|
|
11563
|
+
throw new Error(`Catalogue source returned invalid JSON: ${source}`);
|
|
11564
|
+
}
|
|
11565
|
+
return schema.parse(value);
|
|
11566
|
+
}
|
|
11567
|
+
};
|
|
11568
|
+
function defaultCatalogueCachePath() {
|
|
11569
|
+
return join44(homedir9(), ".agentwheel", "catalogue-cache.json");
|
|
11570
|
+
}
|
|
11571
|
+
function sameSources2(a, b) {
|
|
11572
|
+
return a.length === b.length && a.every((source, index) => source === b[index]);
|
|
11573
|
+
}
|
|
11574
|
+
function catalogueContentHash(enriched, vercel) {
|
|
11575
|
+
return createHash11("sha256").update(JSON.stringify({ enriched, vercel })).digest("hex");
|
|
11576
|
+
}
|
|
11577
|
+
|
|
11578
|
+
// src/search/index.ts
|
|
11579
|
+
var SCORE = {
|
|
11580
|
+
exactName: 1e4,
|
|
11581
|
+
namePrefix: 5e3,
|
|
11582
|
+
namePhrase: 3e3,
|
|
11583
|
+
tagProvidesPhrase: 2e3,
|
|
11584
|
+
descriptionPhrase: 1e3,
|
|
11585
|
+
typeEcosystemPhrase: 800,
|
|
11586
|
+
repositoryPhrase: 400,
|
|
11587
|
+
nameToken: 300,
|
|
11588
|
+
nameTokenPrefix: 200,
|
|
11589
|
+
tagProvidesToken: 180,
|
|
11590
|
+
descriptionToken: 80,
|
|
11591
|
+
typeEcosystemToken: 60,
|
|
11592
|
+
repositoryToken: 40,
|
|
11593
|
+
allTerms: 500
|
|
11594
|
+
};
|
|
11595
|
+
var PROVENANCE_ORDER = ["registry", "enriched", "vercel"];
|
|
11596
|
+
var MATCHED_FIELD_ORDER = ["name", "tags", "provides", "description", "type", "ecosystem", "repository"];
|
|
11597
|
+
function buildSearchEntries(input) {
|
|
11598
|
+
const enriched = catalogueEntries(input.enriched);
|
|
11599
|
+
const vercel = catalogueEntries(input.vercel);
|
|
11600
|
+
assertUniqueIdentities(enriched.map((entry) => entry.id), "enriched catalogue");
|
|
11601
|
+
assertUniqueIdentities(vercel.map((entry) => `${entry.o}/${entry.r}/${entry.s}`), "Vercel catalogue");
|
|
11602
|
+
const records = [
|
|
11603
|
+
...(input.registry ?? []).map(normalizeRegistryEntry),
|
|
11604
|
+
...enriched.map(normalizeEnrichedEntry),
|
|
11605
|
+
...vercel.map(normalizeVercelEntry)
|
|
11606
|
+
];
|
|
11607
|
+
const byId = /* @__PURE__ */ new Map();
|
|
11608
|
+
for (const record of records) {
|
|
11609
|
+
const existing = byId.get(record.id);
|
|
11610
|
+
byId.set(record.id, existing ? mergeEntry(existing, record) : record);
|
|
11611
|
+
}
|
|
11612
|
+
for (const [registryId, registryEntry] of [...byId]) {
|
|
11613
|
+
if (registryEntry.provenances.length !== 1 || registryEntry.provenances[0] !== "registry" || registryEntry.hasRegistrySelectors || !registryEntry.source) {
|
|
11614
|
+
continue;
|
|
11615
|
+
}
|
|
11616
|
+
const canonicalSource = canonicalizeSource(registryEntry.source);
|
|
11617
|
+
const candidates = [...byId.entries()].filter(
|
|
11618
|
+
([candidateId2, candidate2]) => candidateId2 !== registryId && !candidate2.provenances.includes("registry") && candidate2.name === registryEntry.name && candidate2.source !== void 0 && canonicalizeSource(candidate2.source) === canonicalSource
|
|
11619
|
+
);
|
|
11620
|
+
if (candidates.length !== 1) continue;
|
|
11621
|
+
const [candidateId, candidate] = candidates[0];
|
|
11622
|
+
byId.set(candidateId, mergeEntry(registryEntry, candidate, candidateId));
|
|
11623
|
+
byId.delete(registryId);
|
|
11624
|
+
}
|
|
11625
|
+
return [...byId.values()].sort(compareStableEntries);
|
|
11626
|
+
}
|
|
11627
|
+
function searchEntries(entries, query, options = {}) {
|
|
11628
|
+
const normalizedQuery = normalizeSearchText(query);
|
|
11629
|
+
const queryTokens = tokenizeSearchText(query);
|
|
11630
|
+
if (!normalizedQuery || queryTokens.length === 0) return [];
|
|
11631
|
+
const results = entries.filter((entry) => options.includeArchived || !entry.archived).filter((entry) => options.type === void 0 || entry.type === options.type).filter((entry) => options.ecosystem === void 0 || entry.ecosystem === options.ecosystem).map((entry) => ({ entry, result: scoreEntry(entry, normalizedQuery, queryTokens) })).filter(({ result }) => result.score > 0).sort(
|
|
11632
|
+
(a, b) => b.result.score - a.result.score || Number(b.entry.featured) - Number(a.entry.featured) || (b.entry.stars ?? Number.NEGATIVE_INFINITY) - (a.entry.stars ?? Number.NEGATIVE_INFINITY) || compareText(b.entry.lastPush ?? "", a.entry.lastPush ?? "") || compareText(normalizeSearchText(a.result.name), normalizeSearchText(b.result.name)) || compareText(a.result.id, b.result.id)
|
|
11633
|
+
).map(({ result }) => result);
|
|
11634
|
+
const limit = options.limit === void 0 ? 20 : Math.min(100, Math.max(0, Math.trunc(options.limit)));
|
|
11635
|
+
return results.slice(0, limit);
|
|
11636
|
+
}
|
|
11637
|
+
function normalizeSearchText(value) {
|
|
11638
|
+
return value.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim().replace(/\s+/g, " ");
|
|
11639
|
+
}
|
|
11640
|
+
function tokenizeSearchText(value) {
|
|
11641
|
+
const normalized = normalizeSearchText(value);
|
|
11642
|
+
return normalized ? [...new Set(normalized.split(" "))] : [];
|
|
11643
|
+
}
|
|
11644
|
+
function normalizeRegistryEntry(entry) {
|
|
11645
|
+
return {
|
|
11646
|
+
id: `registry:${entry.name}`,
|
|
11647
|
+
name: entry.name,
|
|
11648
|
+
description: entry.description,
|
|
11649
|
+
type: entry.type,
|
|
11650
|
+
ecosystem: inferEcosystem(entry.source),
|
|
11651
|
+
tags: sortedUniqueStrings(entry.tags),
|
|
11652
|
+
provides: [],
|
|
11653
|
+
source: entry.source,
|
|
11654
|
+
installCommand: `npx agentwheel install ${shellQuote2(entry.name)}`,
|
|
11655
|
+
installability: "registry",
|
|
11656
|
+
provenances: ["registry"],
|
|
11657
|
+
archived: false,
|
|
11658
|
+
featured: false,
|
|
11659
|
+
alternateDescriptions: [],
|
|
11660
|
+
descriptionRank: 2,
|
|
11661
|
+
hasRegistrySelectors: Boolean(entry.select?.length || entry.skills?.length)
|
|
11662
|
+
};
|
|
11663
|
+
}
|
|
11664
|
+
function normalizeEnrichedEntry(entry) {
|
|
11665
|
+
const source = nonEmpty(entry.source);
|
|
11666
|
+
const installCommand = enrichedInstallCommand(entry, source);
|
|
11667
|
+
return {
|
|
11668
|
+
id: entry.id,
|
|
11669
|
+
name: entry.name,
|
|
11670
|
+
description: entry.description ?? "",
|
|
11671
|
+
type: entry.type ?? inferType(entry.ecosystem),
|
|
11672
|
+
ecosystem: entry.ecosystem ?? void 0,
|
|
11673
|
+
tags: sortedUniqueStrings(entry.tags ?? []),
|
|
11674
|
+
provides: sortedUniqueStrings(entry.provides ?? []),
|
|
11675
|
+
source,
|
|
11676
|
+
repoUrl: nonEmpty(entry.repoUrl),
|
|
11677
|
+
installCommand,
|
|
11678
|
+
installability: source || installCommand ? "source" : "informational",
|
|
11679
|
+
provenances: ["enriched"],
|
|
11680
|
+
archived: entry.archived ?? false,
|
|
11681
|
+
featured: entry.featured ?? false,
|
|
11682
|
+
stars: entry.stars ?? void 0,
|
|
11683
|
+
lastPush: nonEmpty(entry.lastPush),
|
|
11684
|
+
alternateDescriptions: [],
|
|
11685
|
+
descriptionRank: 3,
|
|
11686
|
+
hasRegistrySelectors: false
|
|
11687
|
+
};
|
|
11688
|
+
}
|
|
11689
|
+
function normalizeVercelEntry(entry) {
|
|
11690
|
+
const path = `${entry.o}/${entry.r}/${entry.s}`;
|
|
11691
|
+
const source = `vercel:skills.sh/${path}`;
|
|
11692
|
+
return {
|
|
11693
|
+
id: `vercel:${path}`,
|
|
11694
|
+
name: entry.s,
|
|
11695
|
+
description: entry.d ?? "",
|
|
11696
|
+
type: "skill",
|
|
11697
|
+
ecosystem: "vercel",
|
|
11698
|
+
tags: [],
|
|
11699
|
+
provides: ["skills"],
|
|
11700
|
+
source,
|
|
11701
|
+
repoUrl: `https://github.com/${entry.o}/${entry.r}`,
|
|
11702
|
+
installCommand: `npx agentwheel install ${shellQuote2(source)}`,
|
|
11703
|
+
installability: "source",
|
|
11704
|
+
provenances: ["vercel"],
|
|
11705
|
+
archived: false,
|
|
11706
|
+
featured: false,
|
|
11707
|
+
alternateDescriptions: [],
|
|
11708
|
+
descriptionRank: 1,
|
|
11709
|
+
hasRegistrySelectors: false
|
|
11710
|
+
};
|
|
11711
|
+
}
|
|
11712
|
+
function mergeEntry(first, second, id = first.id) {
|
|
11713
|
+
const primary = second.description && second.descriptionRank > first.descriptionRank ? second : first;
|
|
11714
|
+
const secondary = primary === first ? second : first;
|
|
11715
|
+
const descriptions = uniqueStrings([
|
|
11716
|
+
primary.description,
|
|
11717
|
+
...primary.alternateDescriptions,
|
|
11718
|
+
secondary.description,
|
|
11719
|
+
...secondary.alternateDescriptions
|
|
11720
|
+
]).filter(Boolean);
|
|
11721
|
+
const description = descriptions[0] ?? "";
|
|
11722
|
+
return {
|
|
11723
|
+
id,
|
|
11724
|
+
name: first.name || second.name,
|
|
11725
|
+
description,
|
|
11726
|
+
type: first.type ?? second.type,
|
|
11727
|
+
ecosystem: first.ecosystem ?? second.ecosystem,
|
|
11728
|
+
tags: sortedUniqueStrings([...first.tags, ...second.tags]),
|
|
11729
|
+
provides: sortedUniqueStrings([...first.provides, ...second.provides]),
|
|
11730
|
+
source: first.source ?? second.source,
|
|
11731
|
+
repoUrl: first.repoUrl ?? second.repoUrl,
|
|
11732
|
+
installCommand: first.installCommand ?? second.installCommand,
|
|
11733
|
+
installability: betterInstallability(first.installability, second.installability),
|
|
11734
|
+
provenances: PROVENANCE_ORDER.filter(
|
|
11735
|
+
(provenance) => first.provenances.includes(provenance) || second.provenances.includes(provenance)
|
|
11736
|
+
),
|
|
11737
|
+
archived: first.archived || second.archived,
|
|
11738
|
+
featured: first.featured || second.featured,
|
|
11739
|
+
stars: maxDefined(first.stars, second.stars),
|
|
11740
|
+
lastPush: maxText(first.lastPush, second.lastPush),
|
|
11741
|
+
alternateDescriptions: descriptions.slice(1),
|
|
11742
|
+
descriptionRank: Math.max(first.descriptionRank, second.descriptionRank),
|
|
11743
|
+
hasRegistrySelectors: first.hasRegistrySelectors || second.hasRegistrySelectors
|
|
11744
|
+
};
|
|
11745
|
+
}
|
|
11746
|
+
function scoreEntry(entry, query, queryTokens) {
|
|
11747
|
+
let score = 0;
|
|
11748
|
+
const matched = /* @__PURE__ */ new Set();
|
|
11749
|
+
const name = normalizeSearchText(entry.name);
|
|
11750
|
+
const descriptions = [entry.description, ...entry.alternateDescriptions].map(normalizeSearchText);
|
|
11751
|
+
const tags = entry.tags.map(normalizeSearchText);
|
|
11752
|
+
const provides = entry.provides.map(normalizeSearchText);
|
|
11753
|
+
const type = normalizeSearchText(entry.type);
|
|
11754
|
+
const ecosystem = normalizeSearchText(entry.ecosystem ?? "");
|
|
11755
|
+
const repositories = [entry.source ?? "", entry.repoUrl ?? ""].map(normalizeSearchText);
|
|
11756
|
+
if (name === query) {
|
|
11757
|
+
score += SCORE.exactName;
|
|
11758
|
+
matched.add("name");
|
|
11759
|
+
} else if (name.startsWith(query)) {
|
|
11760
|
+
score += SCORE.namePrefix;
|
|
11761
|
+
matched.add("name");
|
|
11762
|
+
} else if (name.includes(query)) {
|
|
11763
|
+
score += SCORE.namePhrase;
|
|
11764
|
+
matched.add("name");
|
|
11765
|
+
}
|
|
11766
|
+
const tagsPhraseMatch = matchesPhrase(tags, query);
|
|
11767
|
+
const providesPhraseMatch = matchesPhrase(provides, query);
|
|
11768
|
+
if (tagsPhraseMatch || providesPhraseMatch) {
|
|
11769
|
+
score += SCORE.tagProvidesPhrase;
|
|
11770
|
+
if (tagsPhraseMatch) matched.add("tags");
|
|
11771
|
+
if (providesPhraseMatch) matched.add("provides");
|
|
11772
|
+
}
|
|
11773
|
+
if (matchesPhrase(descriptions, query)) {
|
|
11774
|
+
score += SCORE.descriptionPhrase;
|
|
11775
|
+
matched.add("description");
|
|
11776
|
+
}
|
|
11777
|
+
const typePhraseMatch = type.includes(query);
|
|
11778
|
+
const ecosystemPhraseMatch = ecosystem.includes(query);
|
|
11779
|
+
if (typePhraseMatch || ecosystemPhraseMatch) {
|
|
11780
|
+
score += SCORE.typeEcosystemPhrase;
|
|
11781
|
+
if (typePhraseMatch) matched.add("type");
|
|
11782
|
+
if (ecosystemPhraseMatch) matched.add("ecosystem");
|
|
11783
|
+
}
|
|
11784
|
+
if (matchesPhrase(repositories, query)) {
|
|
11785
|
+
score += SCORE.repositoryPhrase;
|
|
11786
|
+
matched.add("repository");
|
|
11787
|
+
}
|
|
11788
|
+
const nameTokens = name.split(" ");
|
|
11789
|
+
const tagTokenText = tags.join(" ");
|
|
11790
|
+
const provideTokenText = provides.join(" ");
|
|
11791
|
+
const descriptionTokenText = descriptions.join(" ");
|
|
11792
|
+
const repositoryTokenText = repositories.join(" ");
|
|
11793
|
+
let allTermsCovered = true;
|
|
11794
|
+
for (const token of queryTokens) {
|
|
11795
|
+
const nameTokenMatch = includesToken(name, token);
|
|
11796
|
+
if (nameTokenMatch) {
|
|
11797
|
+
score += SCORE.nameToken;
|
|
11798
|
+
matched.add("name");
|
|
11799
|
+
} else if (nameTokens.some((candidate) => candidate.startsWith(token))) {
|
|
11800
|
+
score += SCORE.nameTokenPrefix;
|
|
11801
|
+
matched.add("name");
|
|
11802
|
+
}
|
|
11803
|
+
const tagTokenMatch = includesToken(tagTokenText, token);
|
|
11804
|
+
const provideTokenMatch = includesToken(provideTokenText, token);
|
|
11805
|
+
if (tagTokenMatch || provideTokenMatch) {
|
|
11806
|
+
score += SCORE.tagProvidesToken;
|
|
11807
|
+
if (tagTokenMatch) matched.add("tags");
|
|
11808
|
+
if (provideTokenMatch) matched.add("provides");
|
|
11809
|
+
}
|
|
11810
|
+
const descriptionTokenMatch = includesToken(descriptionTokenText, token);
|
|
11811
|
+
if (descriptionTokenMatch) {
|
|
11812
|
+
score += SCORE.descriptionToken;
|
|
11813
|
+
matched.add("description");
|
|
11814
|
+
}
|
|
11815
|
+
const typeTokenMatch = includesToken(type, token);
|
|
11816
|
+
const ecosystemTokenMatch = includesToken(ecosystem, token);
|
|
11817
|
+
if (typeTokenMatch || ecosystemTokenMatch) {
|
|
11818
|
+
score += SCORE.typeEcosystemToken;
|
|
11819
|
+
if (typeTokenMatch) matched.add("type");
|
|
11820
|
+
if (ecosystemTokenMatch) matched.add("ecosystem");
|
|
11821
|
+
}
|
|
11822
|
+
const repositoryTokenMatch = includesToken(repositoryTokenText, token);
|
|
11823
|
+
if (repositoryTokenMatch) {
|
|
11824
|
+
score += SCORE.repositoryToken;
|
|
11825
|
+
matched.add("repository");
|
|
11826
|
+
}
|
|
11827
|
+
if (!nameTokenMatch && !tagTokenMatch && !provideTokenMatch && !descriptionTokenMatch && !typeTokenMatch && !ecosystemTokenMatch && !repositoryTokenMatch) {
|
|
11828
|
+
allTermsCovered = false;
|
|
11829
|
+
}
|
|
11830
|
+
}
|
|
11831
|
+
if (allTermsCovered) {
|
|
11832
|
+
score += SCORE.allTerms;
|
|
11833
|
+
}
|
|
11834
|
+
return {
|
|
11835
|
+
id: entry.id,
|
|
11836
|
+
name: entry.name,
|
|
11837
|
+
description: entry.description,
|
|
11838
|
+
type: entry.type,
|
|
11839
|
+
...entry.ecosystem ? { ecosystem: entry.ecosystem } : {},
|
|
11840
|
+
tags: entry.tags,
|
|
11841
|
+
provides: entry.provides,
|
|
11842
|
+
...entry.source ? { source: entry.source } : {},
|
|
11843
|
+
...entry.repoUrl ? { repoUrl: entry.repoUrl } : {},
|
|
11844
|
+
...entry.installCommand ? { installCommand: entry.installCommand } : {},
|
|
11845
|
+
installability: entry.installability,
|
|
11846
|
+
provenances: entry.provenances,
|
|
11847
|
+
score,
|
|
11848
|
+
matchedFields: MATCHED_FIELD_ORDER.filter((field) => matched.has(field))
|
|
11849
|
+
};
|
|
11850
|
+
}
|
|
11851
|
+
function catalogueEntries(catalogue) {
|
|
11852
|
+
if (!catalogue) return [];
|
|
11853
|
+
return Array.isArray(catalogue) ? catalogue : catalogue.entries;
|
|
11854
|
+
}
|
|
11855
|
+
function canonicalizeSource(source) {
|
|
11856
|
+
const value = source.normalize("NFKC").trim();
|
|
11857
|
+
const github = value.match(
|
|
11858
|
+
/^(?:github:|git:(?:git\+)?https?:\/\/github\.com\/|(?:git\+)?https?:\/\/github\.com\/)([^/#]+)\/([^#]+?)(?:#(.*))?$/i
|
|
11859
|
+
);
|
|
11860
|
+
if (github) {
|
|
11861
|
+
const owner = github[1].toLowerCase();
|
|
11862
|
+
const repository = github[2].replace(/\.git$/i, "").replace(/\/+$/, "").toLowerCase();
|
|
11863
|
+
const ref = github[3];
|
|
11864
|
+
return `github:${owner}/${repository}${ref === void 0 ? "" : `#${ref}`}`;
|
|
11865
|
+
}
|
|
11866
|
+
return value.replace(/\/+$/, "");
|
|
11867
|
+
}
|
|
11868
|
+
function inferEcosystem(source) {
|
|
11869
|
+
const canonical = canonicalizeSource(source);
|
|
11870
|
+
if (canonical.startsWith("vercel:")) return "vercel";
|
|
11871
|
+
if (canonical.startsWith("mcp-registry:")) return "mcp-registry";
|
|
11872
|
+
if (canonical.startsWith("clawhub:")) return "clawhub";
|
|
11873
|
+
if (canonical.startsWith("skillkit:")) return "skillkit";
|
|
11874
|
+
return void 0;
|
|
11875
|
+
}
|
|
11876
|
+
function inferType(ecosystem) {
|
|
11877
|
+
if (ecosystem === "vercel" || ecosystem === "skillkit") return "skill";
|
|
11878
|
+
if (ecosystem === "mcp-registry") return "mcp";
|
|
11879
|
+
if (ecosystem === "clawhub") return "plugin";
|
|
11880
|
+
return "package";
|
|
11881
|
+
}
|
|
11882
|
+
function betterInstallability(a, b) {
|
|
11883
|
+
const rank = { registry: 3, source: 2, informational: 1 };
|
|
11884
|
+
return rank[a] >= rank[b] ? a : b;
|
|
11885
|
+
}
|
|
11886
|
+
function nonEmpty(value) {
|
|
11887
|
+
return value?.trim() ? value : void 0;
|
|
11888
|
+
}
|
|
11889
|
+
function uniqueStrings(values) {
|
|
11890
|
+
return [...new Set(values)];
|
|
11891
|
+
}
|
|
11892
|
+
function sortedUniqueStrings(values) {
|
|
11893
|
+
return uniqueStrings(values).sort(compareText);
|
|
11894
|
+
}
|
|
11895
|
+
function enrichedInstallCommand(entry, source) {
|
|
11896
|
+
const catalogueCommand = nonEmpty(entry.installCommand);
|
|
11897
|
+
if (!source) return catalogueCommand;
|
|
11898
|
+
if (entry.ecosystem === "mcp-registry" || entry.ecosystem === "clawhub") {
|
|
11899
|
+
return catalogueCommand ?? `npx agentwheel install ${shellQuote2(source)}`;
|
|
11900
|
+
}
|
|
11901
|
+
return `npx agentwheel install ${shellQuote2(source)}`;
|
|
11902
|
+
}
|
|
11903
|
+
function shellQuote2(value) {
|
|
11904
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
11905
|
+
}
|
|
11906
|
+
function matchesPhrase(fields, query) {
|
|
11907
|
+
return fields.some((field) => field.includes(query));
|
|
11908
|
+
}
|
|
11909
|
+
function compareStableEntries(a, b) {
|
|
11910
|
+
return compareText(normalizeSearchText(a.name), normalizeSearchText(b.name)) || compareText(a.id, b.id);
|
|
11911
|
+
}
|
|
11912
|
+
function maxDefined(a, b) {
|
|
11913
|
+
if (a === void 0) return b;
|
|
11914
|
+
if (b === void 0) return a;
|
|
11915
|
+
return Math.max(a, b);
|
|
11916
|
+
}
|
|
11917
|
+
function maxText(a, b) {
|
|
11918
|
+
if (a === void 0) return b;
|
|
11919
|
+
if (b === void 0) return a;
|
|
11920
|
+
return a >= b ? a : b;
|
|
11921
|
+
}
|
|
11922
|
+
function assertUniqueIdentities(ids, label) {
|
|
11923
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11924
|
+
for (const id of ids) {
|
|
11925
|
+
if (seen.has(id)) throw new Error(`Duplicate ${label} id: ${id}`);
|
|
11926
|
+
seen.add(id);
|
|
11927
|
+
}
|
|
11928
|
+
}
|
|
11929
|
+
function compareText(a, b) {
|
|
11930
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
11931
|
+
}
|
|
11932
|
+
function includesToken(normalizedText, token) {
|
|
11933
|
+
return normalizedText === token || normalizedText.startsWith(`${token} `) || normalizedText.endsWith(` ${token}`) || normalizedText.includes(` ${token} `);
|
|
11934
|
+
}
|
|
11935
|
+
|
|
11321
11936
|
// src/cli/index.ts
|
|
11322
11937
|
var CLI_VERSION = resolveCliVersion();
|
|
11323
11938
|
var COMPANION_SKILL_SOURCE = "github:NestDevLab/agentwheel";
|
|
@@ -11361,17 +11976,59 @@ program.command("list").description("list artifacts exposed by a package source"
|
|
|
11361
11976
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
11362
11977
|
const selectedArtifacts = selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry);
|
|
11363
11978
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
11364
|
-
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot:
|
|
11979
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join45(targetRoot, ".agentwheel", "cache") }))));
|
|
11365
11980
|
const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
|
|
11366
11981
|
for (const artifact of artifacts) {
|
|
11367
11982
|
console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
|
|
11368
11983
|
}
|
|
11369
11984
|
});
|
|
11985
|
+
program.command("search").description("search registry and public catalogue artifacts").argument("<query>", "search query").option("--json", "print the versioned search response as JSON", false).option("--scope <scope>", "search scope: all, registry, enriched, or vercel", "all").option("--type <type>", "artifact type: package, skill, plugin, mcp, or adapter").option("--ecosystem <ecosystem>", "ecosystem: official, openpack, mcp-registry, clawhub, skillkit, or vercel").option("--limit <n>", "maximum number of results (1-100)", "20").option("--include-archived", "include archived catalogue entries", false).option("--refresh", "refresh registry and catalogue caches", false).option("--offline", "use compatible local caches without network access", false).option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (query, options) => {
|
|
11986
|
+
const trimmedQuery = query.trim();
|
|
11987
|
+
if (!trimmedQuery) {
|
|
11988
|
+
throw new Error("Search query must not be empty.");
|
|
11989
|
+
}
|
|
11990
|
+
const scope = parseSearchScope(options.scope);
|
|
11991
|
+
const type = options.type === void 0 ? void 0 : parseSearchType(options.type);
|
|
11992
|
+
const ecosystem = options.ecosystem === void 0 ? void 0 : parseSearchEcosystem(options.ecosystem);
|
|
11993
|
+
const limit = parseSearchLimit(options.limit);
|
|
11994
|
+
if (options.refresh && options.offline) {
|
|
11995
|
+
throw new Error("--refresh cannot be used with --offline.");
|
|
11996
|
+
}
|
|
11997
|
+
const warning = (message) => console.error(message);
|
|
11998
|
+
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
11999
|
+
const registryRequest = scope === "all" || scope === "registry" ? new RegistryClient({ workspaceRoot: targetRoot, offline: options.offline, warn: warning }).getIndex({ refresh: options.refresh }) : void 0;
|
|
12000
|
+
const catalogueRequest = scope === "all" || scope === "enriched" || scope === "vercel" ? new CatalogueClient({ offline: options.offline, warn: warning }).getIndex({ refresh: options.refresh }) : void 0;
|
|
12001
|
+
const [registryIndex, catalogueIndex] = await Promise.all([registryRequest, catalogueRequest]);
|
|
12002
|
+
const entries = buildSearchEntries({
|
|
12003
|
+
registry: registryIndex?.entries,
|
|
12004
|
+
enriched: scope === "all" || scope === "enriched" ? catalogueIndex?.enriched : void 0,
|
|
12005
|
+
vercel: scope === "all" || scope === "vercel" ? catalogueIndex?.vercel : void 0
|
|
12006
|
+
});
|
|
12007
|
+
const results = searchEntries(entries, trimmedQuery, {
|
|
12008
|
+
type,
|
|
12009
|
+
ecosystem,
|
|
12010
|
+
limit,
|
|
12011
|
+
includeArchived: options.includeArchived
|
|
12012
|
+
});
|
|
12013
|
+
const loadedIndexes = [registryIndex, catalogueIndex].filter((index) => index !== void 0);
|
|
12014
|
+
const response = {
|
|
12015
|
+
schemaVersion: 1,
|
|
12016
|
+
query: trimmedQuery,
|
|
12017
|
+
scope,
|
|
12018
|
+
fromCache: loadedIndexes.every((index) => index.fromCache),
|
|
12019
|
+
results
|
|
12020
|
+
};
|
|
12021
|
+
if (options.json) {
|
|
12022
|
+
console.log(JSON.stringify(response, null, 2));
|
|
12023
|
+
return;
|
|
12024
|
+
}
|
|
12025
|
+
printSearchResults(trimmedQuery, results);
|
|
12026
|
+
});
|
|
11370
12027
|
program.command("scan").description("scan a package source for validation findings").argument("<source>", "package source").option("--driver <driver>", "source driver").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (source, options) => {
|
|
11371
12028
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
11372
12029
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
11373
12030
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
11374
|
-
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot:
|
|
12031
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join45(targetRoot, ".agentwheel", "cache") }))));
|
|
11375
12032
|
const result = await driver.scan(resolved);
|
|
11376
12033
|
if (result.findings.length === 0) {
|
|
11377
12034
|
console.log("Scan ok: no findings");
|
|
@@ -11434,7 +12091,7 @@ program.command("deps").description("inspect the OpenPack dependency graph").add
|
|
|
11434
12091
|
for (const decision of result.bundle.graphLock.canonical.overrides) {
|
|
11435
12092
|
console.log(`OVERRIDE ${decision.graphNodeId}:${decision.type}/${decision.name} replaces ${decision.overriddenGraphNodeId}:${decision.type}/${decision.name} via ${decision.rootId} (${decision.selector})`);
|
|
11436
12093
|
}
|
|
11437
|
-
await
|
|
12094
|
+
await rm12(result.bundle.root, { recursive: true, force: true });
|
|
11438
12095
|
}
|
|
11439
12096
|
continue;
|
|
11440
12097
|
}
|
|
@@ -11466,11 +12123,6 @@ program.command("registry").description("manage optional registry indexes").addC
|
|
|
11466
12123
|
const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
|
|
11467
12124
|
printRegistryEntries((await client.getIndex()).entries);
|
|
11468
12125
|
})
|
|
11469
|
-
).addCommand(
|
|
11470
|
-
new Command("search").description("search registry entries").argument("<query>", "search query").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (query, options) => {
|
|
11471
|
-
const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
|
|
11472
|
-
printRegistryEntries(await client.search(query));
|
|
11473
|
-
})
|
|
11474
12126
|
).addCommand(
|
|
11475
12127
|
new Command("publish").description("draft a catalogue submission for a public source").argument("<source>", "public resource source or GitHub URL").option("--name <name>", "registry short name").option("--type <type>", "entry type (package, skill, plugin, mcp, or adapter)").option("--description <text>", "short catalogue description").option("--tag <tag>", "search tag (repeatable or comma-separated)", collectTagOption, []).option("--select <type/name>", "selected artifact inside a larger package (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "selected skill inside a larger package (repeatable or comma-separated)", collectSkillOption, []).option("--json", "print only the registry entry JSON", false).action(async (source, options) => {
|
|
11476
12128
|
const draft = createRegistryPublishDraft(source, {
|
|
@@ -11644,7 +12296,7 @@ journalCommand.command("list").description("show pending apply journals for reso
|
|
|
11644
12296
|
if (!journal) continue;
|
|
11645
12297
|
pending += 1;
|
|
11646
12298
|
console.log(`PENDING ${state.adapter.name}/${state.installationType} at ${state.installRoot}`);
|
|
11647
|
-
console.log(` journal: ${
|
|
12299
|
+
console.log(` journal: ${join45(state.installRoot, ".agentwheel", `${state.state.stateKey}.apply-journal.json`)}`);
|
|
11648
12300
|
console.log(` stateKey: ${state.state.stateKey}`);
|
|
11649
12301
|
console.log(` createdAt: ${journal.createdAt}`);
|
|
11650
12302
|
console.log(` updatedAt: ${journal.updatedAt}`);
|
|
@@ -11784,7 +12436,7 @@ async function runInstallCommand(nameOrSource, options, behavior) {
|
|
|
11784
12436
|
console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
|
|
11785
12437
|
if (reloaded) console.log(`Reloaded runtime via ${formatReloadCommands(target.reloadCommands)}.`);
|
|
11786
12438
|
}
|
|
11787
|
-
await
|
|
12439
|
+
await rm12(result.bundle.root, { recursive: true, force: true });
|
|
11788
12440
|
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
11789
12441
|
}
|
|
11790
12442
|
if (behavior.apply && extraPackage && !targetOptions.onlySource) {
|
|
@@ -11879,7 +12531,7 @@ async function buildPlanReport(nameOrSource, options) {
|
|
|
11879
12531
|
for (const result of results) {
|
|
11880
12532
|
reportTargets.push(installPlanReportTarget(result.plan, result.graphLockDigest));
|
|
11881
12533
|
reportWarnings.push(...result.warnings);
|
|
11882
|
-
await
|
|
12534
|
+
await rm12(result.bundle.root, { recursive: true, force: true });
|
|
11883
12535
|
}
|
|
11884
12536
|
}
|
|
11885
12537
|
return planReport(reportTargets, reportWarnings);
|
|
@@ -11935,7 +12587,7 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
11935
12587
|
const bundle = await stageSource(driver, resolvedSource, {
|
|
11936
12588
|
workspaceRoot: targetRoot,
|
|
11937
12589
|
adapter,
|
|
11938
|
-
cacheRoot:
|
|
12590
|
+
cacheRoot: join45(targetRoot, ".agentwheel", "cache"),
|
|
11939
12591
|
mode: options.mode,
|
|
11940
12592
|
ref: initialVersion?.ref,
|
|
11941
12593
|
frozenLock: lockMode,
|
|
@@ -11961,7 +12613,7 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
11961
12613
|
overrides: overrideArtifactsFromOptions(options)
|
|
11962
12614
|
};
|
|
11963
12615
|
} finally {
|
|
11964
|
-
await
|
|
12616
|
+
await rm12(bundle.root, { recursive: true, force: true });
|
|
11965
12617
|
}
|
|
11966
12618
|
}
|
|
11967
12619
|
function findConfiguredPackage(packages, value) {
|
|
@@ -12112,7 +12764,7 @@ async function runConfiguredGraphPackages(target, options, behavior) {
|
|
|
12112
12764
|
console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
|
|
12113
12765
|
if (reloaded) console.log(`Reloaded runtime via ${formatReloadCommands(target.reloadCommands)}.`);
|
|
12114
12766
|
}
|
|
12115
|
-
await
|
|
12767
|
+
await rm12(result.bundle.root, { recursive: true, force: true });
|
|
12116
12768
|
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
12117
12769
|
}
|
|
12118
12770
|
}
|
|
@@ -12329,7 +12981,7 @@ function scopeUpdatePlanToDependencies(result, selectors, previousLock, manifest
|
|
|
12329
12981
|
selectedPreviousNodeIds,
|
|
12330
12982
|
selectedRootIds
|
|
12331
12983
|
);
|
|
12332
|
-
const graphLockDigest =
|
|
12984
|
+
const graphLockDigest = createHash12("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
12333
12985
|
return {
|
|
12334
12986
|
...result,
|
|
12335
12987
|
bundle: { ...result.bundle, graphLock },
|
|
@@ -12497,7 +13149,7 @@ function scopeUpdatePlanToRoot(result, rootId, previousLock, manifest) {
|
|
|
12497
13149
|
selectedPreviousNodeIds,
|
|
12498
13150
|
/* @__PURE__ */ new Set([rootId])
|
|
12499
13151
|
);
|
|
12500
|
-
const graphLockDigest =
|
|
13152
|
+
const graphLockDigest = createHash12("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
12501
13153
|
return {
|
|
12502
13154
|
...scoped,
|
|
12503
13155
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -12569,7 +13221,7 @@ function keepManifestEntryOperation(entry, targetRoot, scopeDescription, operati
|
|
|
12569
13221
|
artifactType: entry.artifactType,
|
|
12570
13222
|
artifactName: entry.artifactName,
|
|
12571
13223
|
kind: entry.kind,
|
|
12572
|
-
destPath: operation?.destPath ??
|
|
13224
|
+
destPath: operation?.destPath ?? join45(targetRoot, entry.path),
|
|
12573
13225
|
relativeDestPath: entry.path,
|
|
12574
13226
|
desiredHash: entry.sourceHash,
|
|
12575
13227
|
currentHash: operation?.currentHash ?? entry.hash,
|
|
@@ -12686,7 +13338,7 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
12686
13338
|
if (!options.dryRun) {
|
|
12687
13339
|
console.log(formatUninstallResult(result));
|
|
12688
13340
|
}
|
|
12689
|
-
if (renderedRoot) await
|
|
13341
|
+
if (renderedRoot) await rm12(renderedRoot, { recursive: true, force: true });
|
|
12690
13342
|
if (plan.hasBlockingChanges) process.exitCode = 1;
|
|
12691
13343
|
}
|
|
12692
13344
|
}
|
|
@@ -13137,7 +13789,7 @@ async function collectPendingInstallWork(target, options) {
|
|
|
13137
13789
|
const message = error instanceof Error ? error.message : String(error);
|
|
13138
13790
|
return { pendingCount: 0, driftCount: 0, conflictCount: 0, counts: {}, error: message };
|
|
13139
13791
|
} finally {
|
|
13140
|
-
await Promise.all(results.map((result) =>
|
|
13792
|
+
await Promise.all(results.map((result) => rm12(result.bundle.root, { recursive: true, force: true })));
|
|
13141
13793
|
}
|
|
13142
13794
|
}
|
|
13143
13795
|
async function printDoctor(target, options) {
|
|
@@ -13154,12 +13806,12 @@ async function printDoctor(target, options) {
|
|
|
13154
13806
|
const requestedSkills = doctorSkillRequests(target, options);
|
|
13155
13807
|
const skills = [];
|
|
13156
13808
|
for (const request of requestedSkills) {
|
|
13157
|
-
const skillPath =
|
|
13809
|
+
const skillPath = join45(state.installRoot, targetMapping.dest, request.name);
|
|
13158
13810
|
const exists = await pathExists(skillPath);
|
|
13159
13811
|
const manifestEntry = manifest?.entries.find((entry) => {
|
|
13160
13812
|
if (entry.artifactType !== "skills") return false;
|
|
13161
13813
|
const legacyInstallName = "installName" in entry && typeof entry.installName === "string" ? entry.installName : void 0;
|
|
13162
|
-
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path ===
|
|
13814
|
+
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path === join45(targetMapping.dest, request.name);
|
|
13163
13815
|
});
|
|
13164
13816
|
const status = manifestEntry ? "managed" : exists ? "present-unmanaged" : "missing";
|
|
13165
13817
|
skills.push({
|
|
@@ -13239,7 +13891,7 @@ function doctorSkillLabel(name) {
|
|
|
13239
13891
|
return `${name} skill`;
|
|
13240
13892
|
}
|
|
13241
13893
|
function isSyncwheelWorkspace(targetRoot) {
|
|
13242
|
-
return existsSync(
|
|
13894
|
+
return existsSync(join45(targetRoot, ".syncwheel", "manifest.json"));
|
|
13243
13895
|
}
|
|
13244
13896
|
function skillInstallCommand(adapter, installationType, options, skill, behavior = {}) {
|
|
13245
13897
|
const args = [
|
|
@@ -13307,7 +13959,7 @@ function normalizeRuntimeScopeOptions(options, behavior = {}) {
|
|
|
13307
13959
|
}
|
|
13308
13960
|
const canDefaultTargetRoot = !options.agent && !options.all && !options.allDetected && !options.profile;
|
|
13309
13961
|
if (!targetRoot && canDefaultTargetRoot && (options.user || installationType === "user" || behavior.defaultUser)) {
|
|
13310
|
-
targetRoot =
|
|
13962
|
+
targetRoot = homedir10();
|
|
13311
13963
|
}
|
|
13312
13964
|
if (!installationType && behavior.defaultUser) {
|
|
13313
13965
|
installationType = "user";
|
|
@@ -13327,12 +13979,12 @@ function looksLikeSourceSpecifier(value) {
|
|
|
13327
13979
|
return value.includes(":") || value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value === "~" || value.startsWith("~/");
|
|
13328
13980
|
}
|
|
13329
13981
|
function normalizeCliPath(value) {
|
|
13330
|
-
if (value === "~") return
|
|
13331
|
-
if (value.startsWith("~/")) return resolve22(
|
|
13982
|
+
if (value === "~") return homedir10();
|
|
13983
|
+
if (value.startsWith("~/")) return resolve22(homedir10(), value.slice(2));
|
|
13332
13984
|
return resolve22(value);
|
|
13333
13985
|
}
|
|
13334
13986
|
function isHomePath(path) {
|
|
13335
|
-
return resolve22(path) === resolve22(
|
|
13987
|
+
return resolve22(path) === resolve22(homedir10());
|
|
13336
13988
|
}
|
|
13337
13989
|
function adapterListFromOption(adapter) {
|
|
13338
13990
|
if (!adapter) return [];
|
|
@@ -13387,10 +14039,10 @@ function filterUninstallPlanBySelection(plan, selected) {
|
|
|
13387
14039
|
};
|
|
13388
14040
|
}
|
|
13389
14041
|
async function initPackage(root) {
|
|
13390
|
-
await mkdir23(
|
|
13391
|
-
await mkdir23(
|
|
13392
|
-
await mkdir23(
|
|
13393
|
-
const manifestPath =
|
|
14042
|
+
await mkdir23(join45(root, "instructions"), { recursive: true });
|
|
14043
|
+
await mkdir23(join45(root, "rules"), { recursive: true });
|
|
14044
|
+
await mkdir23(join45(root, "skills"), { recursive: true });
|
|
14045
|
+
const manifestPath = join45(root, "openpack.json");
|
|
13394
14046
|
const manifest = {
|
|
13395
14047
|
schemaVersion: 2,
|
|
13396
14048
|
name: "example/agentwheel-package",
|
|
@@ -13403,7 +14055,7 @@ async function initPackage(root) {
|
|
|
13403
14055
|
};
|
|
13404
14056
|
await writeFile22(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
13405
14057
|
`, "utf8");
|
|
13406
|
-
await writeFile22(
|
|
14058
|
+
await writeFile22(join45(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
|
|
13407
14059
|
}
|
|
13408
14060
|
async function defaultBootstrapPackage(_root) {
|
|
13409
14061
|
const packageRoot = await findAgentwheelPackageRoot(dirname32(fileURLToPath3(import.meta.url)));
|
|
@@ -13474,6 +14126,49 @@ function printRegistryEntries(entries) {
|
|
|
13474
14126
|
console.log(`${entry.name} ${entry.type} ${entry.source} ${entry.description}${tags}`);
|
|
13475
14127
|
}
|
|
13476
14128
|
}
|
|
14129
|
+
function parseSearchScope(value) {
|
|
14130
|
+
const parsed = searchScopeSchema.safeParse(value);
|
|
14131
|
+
if (parsed.success) return parsed.data;
|
|
14132
|
+
throw new Error(`Invalid search scope: ${value}. Expected one of: ${searchScopeSchema.options.join(", ")}.`);
|
|
14133
|
+
}
|
|
14134
|
+
function parseSearchType(value) {
|
|
14135
|
+
const parsed = searchTypeSchema.safeParse(value);
|
|
14136
|
+
if (parsed.success) return parsed.data;
|
|
14137
|
+
throw new Error(`Invalid artifact type: ${value}. Expected one of: ${searchTypeSchema.options.join(", ")}.`);
|
|
14138
|
+
}
|
|
14139
|
+
function parseSearchEcosystem(value) {
|
|
14140
|
+
const parsed = searchEcosystemSchema.safeParse(value);
|
|
14141
|
+
if (parsed.success) return parsed.data;
|
|
14142
|
+
throw new Error(`Invalid ecosystem: ${value}. Expected one of: ${searchEcosystemSchema.options.join(", ")}.`);
|
|
14143
|
+
}
|
|
14144
|
+
function parseSearchLimit(value) {
|
|
14145
|
+
const limit = Number(value);
|
|
14146
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
14147
|
+
throw new Error(`Invalid search limit: ${value}. Expected an integer from 1 to 100.`);
|
|
14148
|
+
}
|
|
14149
|
+
return limit;
|
|
14150
|
+
}
|
|
14151
|
+
function printSearchResults(query, results) {
|
|
14152
|
+
if (results.length === 0) {
|
|
14153
|
+
console.log(`No artifacts found for ${JSON.stringify(query)}.`);
|
|
14154
|
+
return;
|
|
14155
|
+
}
|
|
14156
|
+
for (const [index, result] of results.entries()) {
|
|
14157
|
+
const ecosystem = result.ecosystem ?? "unknown";
|
|
14158
|
+
const provenances = result.provenances.join("+");
|
|
14159
|
+
console.log(
|
|
14160
|
+
`${index + 1}. ${result.name} [type=${result.type}; ecosystem=${ecosystem}; installability=${result.installability}; provenance=${provenances}]`
|
|
14161
|
+
);
|
|
14162
|
+
console.log(` ${result.description || "(no description)"}`);
|
|
14163
|
+
if (result.installCommand) {
|
|
14164
|
+
console.log(` Install: ${result.installCommand}`);
|
|
14165
|
+
} else if (result.source) {
|
|
14166
|
+
console.log(` Source: ${result.source}`);
|
|
14167
|
+
} else {
|
|
14168
|
+
console.log(" Install: unavailable");
|
|
14169
|
+
}
|
|
14170
|
+
}
|
|
14171
|
+
}
|
|
13477
14172
|
async function main() {
|
|
13478
14173
|
await maybeCheckForUpdate({
|
|
13479
14174
|
currentVersion: CLI_VERSION,
|
package/openpack.json
CHANGED
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agentwheel
|
|
3
|
-
description: Use
|
|
3
|
+
description: Discover or manage reusable agent artifacts with Agentwheel. Use when a requested agent capability, integration, workflow, policy, or tool may already exist, and when adding, installing, updating, customizing, ejecting, or uninstalling skills, rules, instructions, commands, MCP, hooks, settings, plugins, or subagents across runtimes.
|
|
4
4
|
allowed-tools: [Bash]
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
7
|
author: NestDevLab
|
|
8
|
-
version: "0.
|
|
8
|
+
version: "0.16.0"
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# agentwheel
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Agentwheel discovers reusable artifacts and manages their desired state across runtimes.
|
|
14
14
|
|
|
15
15
|
agentwheel is the control plane. It reads packages from sources, stores desired state in `.agentwheel/config.json`, plans runtime changes, and writes only through `install`. Treat runtime output directories as generated files.
|
|
16
16
|
|
|
@@ -29,11 +29,12 @@ Mental model:
|
|
|
29
29
|
- Gmail, Drive, registry publishing, git commits, pushes, and runtime reloads/restarts are separate external side effects. Get explicit approval for them.
|
|
30
30
|
- Programmatic adapters execute local code. Use `--adapter-module` only with `--allow-adapter-code` after the user approves that local code execution.
|
|
31
31
|
- OpenClaw plugin artifacts are only planned by default. Use `--execute-plugins` only after explicit approval.
|
|
32
|
+
- Search results are proposals, not approval. Never add, install, enable, or change configuration until the user confirms the artifact and target scope.
|
|
32
33
|
|
|
33
34
|
## Core Flow
|
|
34
35
|
|
|
35
36
|
```bash
|
|
36
|
-
agentwheel
|
|
37
|
+
agentwheel search tmux
|
|
37
38
|
agentwheel add github:NestDevLab/agent-mesh --skill codex-tmux --adapter codex --installation-type local --mode tracking
|
|
38
39
|
agentwheel plan
|
|
39
40
|
agentwheel install
|
|
@@ -72,14 +73,34 @@ agentwheel init package
|
|
|
72
73
|
|
|
73
74
|
`agentwheel init package` creates `openpack.json`, `instructions/`, `rules/`, `skills/`, and `instructions/AGENTS.md`.
|
|
74
75
|
|
|
75
|
-
## Discovery
|
|
76
|
+
## Discovery And Recommendations
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
Search configured registries and public catalogue sources:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
agentwheel search "<query>"
|
|
82
|
+
agentwheel search "<query>" --json --limit 10
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
When a reusable artifact could satisfy the request:
|
|
86
|
+
|
|
87
|
+
1. Extract the capability and constraints from the complete request.
|
|
88
|
+
2. Generate one to four short lexical queries using capability terms, synonyms, runtime names, and artifact types. Prefer English catalogue terms when the request uses another language.
|
|
89
|
+
3. Run one `agentwheel search "<query>" --json --limit 10` per variant. Stop after four calls; do not recursively refine without new user requirements.
|
|
90
|
+
4. Merge results by stable `id`. Treat CLI scores as retrieval signals, not semantic confidence.
|
|
91
|
+
5. Rerank against the original request using capabilities, runtime or ecosystem, artifact type, description, tags, `provides`, and installability. Do not infer capabilities absent from result metadata.
|
|
92
|
+
6. Suggest zero to three distinct artifacts. For each, give its name or source, one evidence-based match reason, installability, and a safe next command.
|
|
93
|
+
7. Wait for explicit approval before `add`, `install`, plugin execution, or configuration changes.
|
|
94
|
+
|
|
95
|
+
For automatic suggestions, skip discovery when the user explicitly wants custom implementation, has already selected an artifact, an installed artifact clearly satisfies the request, candidates are only weak lexical matches, or the same suggestion was already shown without new evidence.
|
|
96
|
+
|
|
97
|
+
Search recommendations are conversational only: they do not select OpenPack `suggests`, mutate desired state, or imply installation approval.
|
|
98
|
+
|
|
99
|
+
Registry maintenance remains explicit:
|
|
78
100
|
|
|
79
101
|
```bash
|
|
80
102
|
agentwheel registry update
|
|
81
103
|
agentwheel registry list
|
|
82
|
-
agentwheel registry search <query>
|
|
83
104
|
```
|
|
84
105
|
|
|
85
106
|
Inspect an explicit source before adding it:
|
|
@@ -91,7 +112,7 @@ agentwheel list ./local-agent-pack
|
|
|
91
112
|
agentwheel scan ./local-agent-pack
|
|
92
113
|
```
|
|
93
114
|
|
|
94
|
-
Filter
|
|
115
|
+
Filter source inspection to specific artifacts:
|
|
95
116
|
|
|
96
117
|
```bash
|
|
97
118
|
agentwheel list github:owner/repo --select skills/review --select rules/core.md
|
|
@@ -498,11 +519,11 @@ If a selected artifact is missing:
|
|
|
498
519
|
agentwheel list <source>
|
|
499
520
|
```
|
|
500
521
|
|
|
501
|
-
If registry short
|
|
522
|
+
If a registry short name fails:
|
|
502
523
|
|
|
503
524
|
```bash
|
|
504
525
|
agentwheel registry update
|
|
505
|
-
agentwheel
|
|
526
|
+
agentwheel search "<query>" --scope registry
|
|
506
527
|
```
|
|
507
528
|
|
|
508
529
|
If npm update checks are noisy:
|