agentwheel 0.15.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/dist/index.js +758 -50
- 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
|
|
|
@@ -4623,9 +4623,10 @@ function formatPlan(plan) {
|
|
|
4623
4623
|
);
|
|
4624
4624
|
return lines.join("\n");
|
|
4625
4625
|
}
|
|
4626
|
-
function planReport(targets, warnings = []) {
|
|
4626
|
+
function planReport(targets, warnings = [], applied = false) {
|
|
4627
4627
|
return {
|
|
4628
4628
|
schemaVersion: 1,
|
|
4629
|
+
applied,
|
|
4629
4630
|
targets: [...targets].sort(comparePlanReportTargets),
|
|
4630
4631
|
warnings: [...warnings].sort((a, b) => a.localeCompare(b))
|
|
4631
4632
|
};
|
|
@@ -7759,13 +7760,6 @@ var RegistryClient = class {
|
|
|
7759
7760
|
const index = await this.getIndex(options);
|
|
7760
7761
|
return index.entries.find((entry) => entry.name === name);
|
|
7761
7762
|
}
|
|
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
7763
|
async clearCache() {
|
|
7770
7764
|
await rm8(this.cachePath, { force: true });
|
|
7771
7765
|
}
|
|
@@ -11318,6 +11312,628 @@ function valueAfter(lines, prefix) {
|
|
|
11318
11312
|
return lines.find((line) => line.startsWith(prefix))?.slice(prefix.length).trim() ?? null;
|
|
11319
11313
|
}
|
|
11320
11314
|
|
|
11315
|
+
// src/catalogue/client.ts
|
|
11316
|
+
import { createHash as createHash11 } from "crypto";
|
|
11317
|
+
import { readFile as readFile32, rm as rm11 } from "fs/promises";
|
|
11318
|
+
import { homedir as homedir9 } from "os";
|
|
11319
|
+
import { join as join44 } from "path";
|
|
11320
|
+
|
|
11321
|
+
// src/model/catalogue.ts
|
|
11322
|
+
import { z as z13 } from "zod";
|
|
11323
|
+
var searchScopeSchema = z13.enum(["all", "registry", "enriched", "vercel"]);
|
|
11324
|
+
var searchTypeSchema = z13.enum(["package", "skill", "plugin", "mcp", "adapter"]);
|
|
11325
|
+
var searchEcosystemSchema = z13.enum([
|
|
11326
|
+
"official",
|
|
11327
|
+
"openpack",
|
|
11328
|
+
"mcp-registry",
|
|
11329
|
+
"clawhub",
|
|
11330
|
+
"skillkit",
|
|
11331
|
+
"vercel"
|
|
11332
|
+
]);
|
|
11333
|
+
var catalogueProvenanceSchema = z13.enum(["registry", "enriched", "vercel"]);
|
|
11334
|
+
var installabilitySchema = z13.enum(["registry", "source", "informational"]);
|
|
11335
|
+
var nullableString = z13.string().nullable();
|
|
11336
|
+
var nullableStringArray = z13.array(z13.string()).nullable();
|
|
11337
|
+
var enrichedCatalogueEntrySchema = z13.object({
|
|
11338
|
+
id: z13.string().min(1),
|
|
11339
|
+
name: z13.string().min(1),
|
|
11340
|
+
ecosystem: searchEcosystemSchema.nullable(),
|
|
11341
|
+
type: searchTypeSchema.nullable(),
|
|
11342
|
+
description: nullableString,
|
|
11343
|
+
tags: nullableStringArray,
|
|
11344
|
+
source: nullableString,
|
|
11345
|
+
installCommand: nullableString,
|
|
11346
|
+
repoUrl: nullableString,
|
|
11347
|
+
homepageUrl: nullableString.optional(),
|
|
11348
|
+
homepageLinkLabel: nullableString.optional(),
|
|
11349
|
+
stars: z13.number().finite().nullable().optional(),
|
|
11350
|
+
lastPush: nullableString.optional(),
|
|
11351
|
+
archived: z13.boolean().nullable(),
|
|
11352
|
+
provides: nullableStringArray,
|
|
11353
|
+
version: nullableString,
|
|
11354
|
+
featured: z13.boolean().nullable().optional()
|
|
11355
|
+
});
|
|
11356
|
+
var enrichedCatalogueSchema = z13.object({
|
|
11357
|
+
schemaVersion: z13.literal(1),
|
|
11358
|
+
generatedAt: z13.string().datetime(),
|
|
11359
|
+
entries: z13.array(enrichedCatalogueEntrySchema)
|
|
11360
|
+
}).superRefine((value, context) => {
|
|
11361
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11362
|
+
value.entries.forEach((entry, index) => {
|
|
11363
|
+
if (seen.has(entry.id)) {
|
|
11364
|
+
context.addIssue({
|
|
11365
|
+
code: "custom",
|
|
11366
|
+
path: ["entries", index, "id"],
|
|
11367
|
+
message: `duplicate catalogue id: ${entry.id}`
|
|
11368
|
+
});
|
|
11369
|
+
}
|
|
11370
|
+
seen.add(entry.id);
|
|
11371
|
+
});
|
|
11372
|
+
});
|
|
11373
|
+
var vercelCatalogueEntrySchema = z13.object({
|
|
11374
|
+
o: z13.string().min(1),
|
|
11375
|
+
r: z13.string().min(1),
|
|
11376
|
+
s: z13.string().min(1),
|
|
11377
|
+
d: z13.string().nullable().optional()
|
|
11378
|
+
});
|
|
11379
|
+
var vercelCatalogueSchema = z13.object({
|
|
11380
|
+
schemaVersion: z13.literal(1),
|
|
11381
|
+
generatedAt: z13.string().datetime(),
|
|
11382
|
+
count: z13.number().int().nonnegative(),
|
|
11383
|
+
entries: z13.array(vercelCatalogueEntrySchema)
|
|
11384
|
+
}).superRefine((value, context) => {
|
|
11385
|
+
if (value.count !== value.entries.length) {
|
|
11386
|
+
context.addIssue({
|
|
11387
|
+
code: "custom",
|
|
11388
|
+
path: ["count"],
|
|
11389
|
+
message: `count must equal entries length (${value.entries.length})`
|
|
11390
|
+
});
|
|
11391
|
+
}
|
|
11392
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11393
|
+
value.entries.forEach((entry, index) => {
|
|
11394
|
+
const id = `${entry.o}/${entry.r}/${entry.s}`;
|
|
11395
|
+
if (seen.has(id)) {
|
|
11396
|
+
context.addIssue({
|
|
11397
|
+
code: "custom",
|
|
11398
|
+
path: ["entries", index],
|
|
11399
|
+
message: `duplicate Vercel catalogue id: ${id}`
|
|
11400
|
+
});
|
|
11401
|
+
}
|
|
11402
|
+
seen.add(id);
|
|
11403
|
+
});
|
|
11404
|
+
});
|
|
11405
|
+
var catalogueCacheSchema = z13.object({
|
|
11406
|
+
version: z13.literal(1),
|
|
11407
|
+
fetchedAt: z13.string().datetime(),
|
|
11408
|
+
sources: z13.tuple([z13.string().url(), z13.string().url()]),
|
|
11409
|
+
enriched: enrichedCatalogueSchema,
|
|
11410
|
+
vercel: vercelCatalogueSchema
|
|
11411
|
+
});
|
|
11412
|
+
var catalogueCacheEnvelopeSchema = z13.object({
|
|
11413
|
+
version: z13.literal(1),
|
|
11414
|
+
fetchedAt: z13.string().datetime(),
|
|
11415
|
+
sources: z13.tuple([z13.string().url(), z13.string().url()]),
|
|
11416
|
+
contentHash: z13.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
11417
|
+
enriched: z13.unknown(),
|
|
11418
|
+
vercel: z13.unknown()
|
|
11419
|
+
});
|
|
11420
|
+
var searchResultSchema = z13.object({
|
|
11421
|
+
id: z13.string().min(1),
|
|
11422
|
+
name: z13.string().min(1),
|
|
11423
|
+
description: z13.string(),
|
|
11424
|
+
type: searchTypeSchema,
|
|
11425
|
+
ecosystem: searchEcosystemSchema.optional(),
|
|
11426
|
+
tags: z13.array(z13.string()),
|
|
11427
|
+
provides: z13.array(z13.string()),
|
|
11428
|
+
source: z13.string().min(1).optional(),
|
|
11429
|
+
repoUrl: z13.string().min(1).optional(),
|
|
11430
|
+
installCommand: z13.string().min(1).optional(),
|
|
11431
|
+
installability: installabilitySchema,
|
|
11432
|
+
provenances: z13.array(catalogueProvenanceSchema).min(1),
|
|
11433
|
+
score: z13.number().int().nonnegative(),
|
|
11434
|
+
matchedFields: z13.array(z13.string())
|
|
11435
|
+
});
|
|
11436
|
+
var searchResponseSchema = z13.object({
|
|
11437
|
+
schemaVersion: z13.literal(1),
|
|
11438
|
+
query: z13.string(),
|
|
11439
|
+
scope: searchScopeSchema,
|
|
11440
|
+
fromCache: z13.boolean(),
|
|
11441
|
+
results: z13.array(searchResultSchema)
|
|
11442
|
+
});
|
|
11443
|
+
|
|
11444
|
+
// src/catalogue/client.ts
|
|
11445
|
+
var DEFAULT_ENRICHED_CATALOGUE_URL = "https://raw.githubusercontent.com/NestDevLab/agentwheel-registry/main/catalogue-data.json";
|
|
11446
|
+
var DEFAULT_VERCEL_CATALOGUE_URL = "https://raw.githubusercontent.com/NestDevLab/agentwheel-registry/main/catalogue-vercel-index.json";
|
|
11447
|
+
var DEFAULT_CATALOGUE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
11448
|
+
var MAX_CATALOGUE_PAYLOAD_BYTES = 32 * 1024 * 1024;
|
|
11449
|
+
var CatalogueClient = class {
|
|
11450
|
+
constructor(options = {}) {
|
|
11451
|
+
this.options = options;
|
|
11452
|
+
this.cachePath = options.cachePath ?? defaultCatalogueCachePath();
|
|
11453
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
11454
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
11455
|
+
this.sources = [
|
|
11456
|
+
options.enrichedUrl ?? DEFAULT_ENRICHED_CATALOGUE_URL,
|
|
11457
|
+
options.vercelUrl ?? DEFAULT_VERCEL_CATALOGUE_URL
|
|
11458
|
+
];
|
|
11459
|
+
}
|
|
11460
|
+
options;
|
|
11461
|
+
cachePath;
|
|
11462
|
+
now;
|
|
11463
|
+
fetchImpl;
|
|
11464
|
+
sources;
|
|
11465
|
+
async getIndex(options = {}) {
|
|
11466
|
+
const cached = await this.readCache();
|
|
11467
|
+
const usableCache = cached && sameSources2(cached.sources, this.sources) ? cached : void 0;
|
|
11468
|
+
const expired = usableCache ? this.isExpired(usableCache) : false;
|
|
11469
|
+
if (this.options.offline) {
|
|
11470
|
+
if (!usableCache) {
|
|
11471
|
+
throw new Error("Offline catalogue cache is missing. Run without --offline first.");
|
|
11472
|
+
}
|
|
11473
|
+
const stale = expired;
|
|
11474
|
+
this.options.warn?.(
|
|
11475
|
+
stale ? "Offline: using stale catalogue cache because refresh is disabled." : "Offline: using cached catalogue data."
|
|
11476
|
+
);
|
|
11477
|
+
return this.fromCache(usableCache, stale);
|
|
11478
|
+
}
|
|
11479
|
+
if (!options.refresh && usableCache && !expired) {
|
|
11480
|
+
return this.fromCache(usableCache, false);
|
|
11481
|
+
}
|
|
11482
|
+
try {
|
|
11483
|
+
const [enriched, vercel] = await Promise.all([
|
|
11484
|
+
this.fetchJson(this.sources[0], enrichedCatalogueSchema),
|
|
11485
|
+
this.fetchJson(this.sources[1], vercelCatalogueSchema)
|
|
11486
|
+
]);
|
|
11487
|
+
const fetchedAt = this.now().toISOString();
|
|
11488
|
+
const cache = {
|
|
11489
|
+
version: 1,
|
|
11490
|
+
fetchedAt,
|
|
11491
|
+
sources: this.sources,
|
|
11492
|
+
enriched,
|
|
11493
|
+
vercel
|
|
11494
|
+
};
|
|
11495
|
+
const cacheFile = {
|
|
11496
|
+
...cache,
|
|
11497
|
+
contentHash: catalogueContentHash(enriched, vercel)
|
|
11498
|
+
};
|
|
11499
|
+
await writeJsonAtomic(this.cachePath, cacheFile);
|
|
11500
|
+
return { enriched, vercel, sources: this.sources, fetchedAt, fromCache: false, stale: false };
|
|
11501
|
+
} catch (error) {
|
|
11502
|
+
if (!usableCache) throw error;
|
|
11503
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
11504
|
+
this.options.warn?.(`Catalogue refresh failed; using stale catalogue cache: ${reason}`);
|
|
11505
|
+
return this.fromCache(usableCache, true);
|
|
11506
|
+
}
|
|
11507
|
+
}
|
|
11508
|
+
async clearCache() {
|
|
11509
|
+
await rm11(this.cachePath, { force: true });
|
|
11510
|
+
}
|
|
11511
|
+
async readCache() {
|
|
11512
|
+
if (!await pathExists(this.cachePath)) return void 0;
|
|
11513
|
+
try {
|
|
11514
|
+
const value = JSON.parse(await readFile32(this.cachePath, "utf8"));
|
|
11515
|
+
const envelope = catalogueCacheEnvelopeSchema.parse(value);
|
|
11516
|
+
if (envelope.contentHash) {
|
|
11517
|
+
const contentHash = catalogueContentHash(envelope.enriched, envelope.vercel);
|
|
11518
|
+
if (contentHash !== envelope.contentHash) {
|
|
11519
|
+
throw new Error("catalogue cache integrity check failed");
|
|
11520
|
+
}
|
|
11521
|
+
return envelope;
|
|
11522
|
+
}
|
|
11523
|
+
return catalogueCacheSchema.parse(value);
|
|
11524
|
+
} catch (error) {
|
|
11525
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
11526
|
+
this.options.warn?.(`Ignoring invalid catalogue cache: ${reason}`);
|
|
11527
|
+
return void 0;
|
|
11528
|
+
}
|
|
11529
|
+
}
|
|
11530
|
+
isExpired(cache) {
|
|
11531
|
+
const ttlMs = this.options.ttlMs ?? DEFAULT_CATALOGUE_TTL_MS;
|
|
11532
|
+
return this.now().getTime() - new Date(cache.fetchedAt).getTime() > ttlMs;
|
|
11533
|
+
}
|
|
11534
|
+
fromCache(cache, stale) {
|
|
11535
|
+
return {
|
|
11536
|
+
enriched: cache.enriched,
|
|
11537
|
+
vercel: cache.vercel,
|
|
11538
|
+
sources: cache.sources,
|
|
11539
|
+
fetchedAt: cache.fetchedAt,
|
|
11540
|
+
fromCache: true,
|
|
11541
|
+
stale
|
|
11542
|
+
};
|
|
11543
|
+
}
|
|
11544
|
+
async fetchJson(source, schema) {
|
|
11545
|
+
const response = await this.fetchImpl(source);
|
|
11546
|
+
if (!response.ok) {
|
|
11547
|
+
throw new Error(`Catalogue source failed (${response.status}): ${source}`);
|
|
11548
|
+
}
|
|
11549
|
+
const declaredLength = response.headers.get("content-length");
|
|
11550
|
+
if (declaredLength !== null) {
|
|
11551
|
+
const bytes = Number(declaredLength);
|
|
11552
|
+
if (Number.isFinite(bytes) && bytes > MAX_CATALOGUE_PAYLOAD_BYTES) {
|
|
11553
|
+
throw new Error(`Catalogue payload exceeds 32 MiB limit: ${source}`);
|
|
11554
|
+
}
|
|
11555
|
+
}
|
|
11556
|
+
const payload = await response.arrayBuffer();
|
|
11557
|
+
if (payload.byteLength > MAX_CATALOGUE_PAYLOAD_BYTES) {
|
|
11558
|
+
throw new Error(`Catalogue payload exceeds 32 MiB limit: ${source}`);
|
|
11559
|
+
}
|
|
11560
|
+
let value;
|
|
11561
|
+
try {
|
|
11562
|
+
value = JSON.parse(new TextDecoder().decode(payload));
|
|
11563
|
+
} catch {
|
|
11564
|
+
throw new Error(`Catalogue source returned invalid JSON: ${source}`);
|
|
11565
|
+
}
|
|
11566
|
+
return schema.parse(value);
|
|
11567
|
+
}
|
|
11568
|
+
};
|
|
11569
|
+
function defaultCatalogueCachePath() {
|
|
11570
|
+
return join44(homedir9(), ".agentwheel", "catalogue-cache.json");
|
|
11571
|
+
}
|
|
11572
|
+
function sameSources2(a, b) {
|
|
11573
|
+
return a.length === b.length && a.every((source, index) => source === b[index]);
|
|
11574
|
+
}
|
|
11575
|
+
function catalogueContentHash(enriched, vercel) {
|
|
11576
|
+
return createHash11("sha256").update(JSON.stringify({ enriched, vercel })).digest("hex");
|
|
11577
|
+
}
|
|
11578
|
+
|
|
11579
|
+
// src/search/index.ts
|
|
11580
|
+
var SCORE = {
|
|
11581
|
+
exactName: 1e4,
|
|
11582
|
+
namePrefix: 5e3,
|
|
11583
|
+
namePhrase: 3e3,
|
|
11584
|
+
tagProvidesPhrase: 2e3,
|
|
11585
|
+
descriptionPhrase: 1e3,
|
|
11586
|
+
typeEcosystemPhrase: 800,
|
|
11587
|
+
repositoryPhrase: 400,
|
|
11588
|
+
nameToken: 300,
|
|
11589
|
+
nameTokenPrefix: 200,
|
|
11590
|
+
tagProvidesToken: 180,
|
|
11591
|
+
descriptionToken: 80,
|
|
11592
|
+
typeEcosystemToken: 60,
|
|
11593
|
+
repositoryToken: 40,
|
|
11594
|
+
allTerms: 500
|
|
11595
|
+
};
|
|
11596
|
+
var PROVENANCE_ORDER = ["registry", "enriched", "vercel"];
|
|
11597
|
+
var MATCHED_FIELD_ORDER = ["name", "tags", "provides", "description", "type", "ecosystem", "repository"];
|
|
11598
|
+
function buildSearchEntries(input) {
|
|
11599
|
+
const enriched = catalogueEntries(input.enriched);
|
|
11600
|
+
const vercel = catalogueEntries(input.vercel);
|
|
11601
|
+
assertUniqueIdentities(enriched.map((entry) => entry.id), "enriched catalogue");
|
|
11602
|
+
assertUniqueIdentities(vercel.map((entry) => `${entry.o}/${entry.r}/${entry.s}`), "Vercel catalogue");
|
|
11603
|
+
const records = [
|
|
11604
|
+
...(input.registry ?? []).map(normalizeRegistryEntry),
|
|
11605
|
+
...enriched.map(normalizeEnrichedEntry),
|
|
11606
|
+
...vercel.map(normalizeVercelEntry)
|
|
11607
|
+
];
|
|
11608
|
+
const byId = /* @__PURE__ */ new Map();
|
|
11609
|
+
for (const record of records) {
|
|
11610
|
+
const existing = byId.get(record.id);
|
|
11611
|
+
byId.set(record.id, existing ? mergeEntry(existing, record) : record);
|
|
11612
|
+
}
|
|
11613
|
+
for (const [registryId, registryEntry] of [...byId]) {
|
|
11614
|
+
if (registryEntry.provenances.length !== 1 || registryEntry.provenances[0] !== "registry" || registryEntry.hasRegistrySelectors || !registryEntry.source) {
|
|
11615
|
+
continue;
|
|
11616
|
+
}
|
|
11617
|
+
const canonicalSource = canonicalizeSource(registryEntry.source);
|
|
11618
|
+
const candidates = [...byId.entries()].filter(
|
|
11619
|
+
([candidateId2, candidate2]) => candidateId2 !== registryId && !candidate2.provenances.includes("registry") && candidate2.name === registryEntry.name && candidate2.source !== void 0 && canonicalizeSource(candidate2.source) === canonicalSource
|
|
11620
|
+
);
|
|
11621
|
+
if (candidates.length !== 1) continue;
|
|
11622
|
+
const [candidateId, candidate] = candidates[0];
|
|
11623
|
+
byId.set(candidateId, mergeEntry(registryEntry, candidate, candidateId));
|
|
11624
|
+
byId.delete(registryId);
|
|
11625
|
+
}
|
|
11626
|
+
return [...byId.values()].sort(compareStableEntries);
|
|
11627
|
+
}
|
|
11628
|
+
function searchEntries(entries, query, options = {}) {
|
|
11629
|
+
const normalizedQuery = normalizeSearchText(query);
|
|
11630
|
+
const queryTokens = tokenizeSearchText(query);
|
|
11631
|
+
if (!normalizedQuery || queryTokens.length === 0) return [];
|
|
11632
|
+
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(
|
|
11633
|
+
(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)
|
|
11634
|
+
).map(({ result }) => result);
|
|
11635
|
+
const limit = options.limit === void 0 ? 20 : Math.min(100, Math.max(0, Math.trunc(options.limit)));
|
|
11636
|
+
return results.slice(0, limit);
|
|
11637
|
+
}
|
|
11638
|
+
function normalizeSearchText(value) {
|
|
11639
|
+
return value.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim().replace(/\s+/g, " ");
|
|
11640
|
+
}
|
|
11641
|
+
function tokenizeSearchText(value) {
|
|
11642
|
+
const normalized = normalizeSearchText(value);
|
|
11643
|
+
return normalized ? [...new Set(normalized.split(" "))] : [];
|
|
11644
|
+
}
|
|
11645
|
+
function normalizeRegistryEntry(entry) {
|
|
11646
|
+
return {
|
|
11647
|
+
id: `registry:${entry.name}`,
|
|
11648
|
+
name: entry.name,
|
|
11649
|
+
description: entry.description,
|
|
11650
|
+
type: entry.type,
|
|
11651
|
+
ecosystem: inferEcosystem(entry.source),
|
|
11652
|
+
tags: sortedUniqueStrings(entry.tags),
|
|
11653
|
+
provides: [],
|
|
11654
|
+
source: entry.source,
|
|
11655
|
+
installCommand: `npx agentwheel install ${shellQuote2(entry.name)}`,
|
|
11656
|
+
installability: "registry",
|
|
11657
|
+
provenances: ["registry"],
|
|
11658
|
+
archived: false,
|
|
11659
|
+
featured: false,
|
|
11660
|
+
alternateDescriptions: [],
|
|
11661
|
+
descriptionRank: 2,
|
|
11662
|
+
hasRegistrySelectors: Boolean(entry.select?.length || entry.skills?.length)
|
|
11663
|
+
};
|
|
11664
|
+
}
|
|
11665
|
+
function normalizeEnrichedEntry(entry) {
|
|
11666
|
+
const source = nonEmpty(entry.source);
|
|
11667
|
+
const installCommand = enrichedInstallCommand(entry, source);
|
|
11668
|
+
return {
|
|
11669
|
+
id: entry.id,
|
|
11670
|
+
name: entry.name,
|
|
11671
|
+
description: entry.description ?? "",
|
|
11672
|
+
type: entry.type ?? inferType(entry.ecosystem),
|
|
11673
|
+
ecosystem: entry.ecosystem ?? void 0,
|
|
11674
|
+
tags: sortedUniqueStrings(entry.tags ?? []),
|
|
11675
|
+
provides: sortedUniqueStrings(entry.provides ?? []),
|
|
11676
|
+
source,
|
|
11677
|
+
repoUrl: nonEmpty(entry.repoUrl),
|
|
11678
|
+
installCommand,
|
|
11679
|
+
installability: source || installCommand ? "source" : "informational",
|
|
11680
|
+
provenances: ["enriched"],
|
|
11681
|
+
archived: entry.archived ?? false,
|
|
11682
|
+
featured: entry.featured ?? false,
|
|
11683
|
+
stars: entry.stars ?? void 0,
|
|
11684
|
+
lastPush: nonEmpty(entry.lastPush),
|
|
11685
|
+
alternateDescriptions: [],
|
|
11686
|
+
descriptionRank: 3,
|
|
11687
|
+
hasRegistrySelectors: false
|
|
11688
|
+
};
|
|
11689
|
+
}
|
|
11690
|
+
function normalizeVercelEntry(entry) {
|
|
11691
|
+
const path = `${entry.o}/${entry.r}/${entry.s}`;
|
|
11692
|
+
const source = `vercel:skills.sh/${path}`;
|
|
11693
|
+
return {
|
|
11694
|
+
id: `vercel:${path}`,
|
|
11695
|
+
name: entry.s,
|
|
11696
|
+
description: entry.d ?? "",
|
|
11697
|
+
type: "skill",
|
|
11698
|
+
ecosystem: "vercel",
|
|
11699
|
+
tags: [],
|
|
11700
|
+
provides: ["skills"],
|
|
11701
|
+
source,
|
|
11702
|
+
repoUrl: `https://github.com/${entry.o}/${entry.r}`,
|
|
11703
|
+
installCommand: `npx agentwheel install ${shellQuote2(source)}`,
|
|
11704
|
+
installability: "source",
|
|
11705
|
+
provenances: ["vercel"],
|
|
11706
|
+
archived: false,
|
|
11707
|
+
featured: false,
|
|
11708
|
+
alternateDescriptions: [],
|
|
11709
|
+
descriptionRank: 1,
|
|
11710
|
+
hasRegistrySelectors: false
|
|
11711
|
+
};
|
|
11712
|
+
}
|
|
11713
|
+
function mergeEntry(first, second, id = first.id) {
|
|
11714
|
+
const primary = second.description && second.descriptionRank > first.descriptionRank ? second : first;
|
|
11715
|
+
const secondary = primary === first ? second : first;
|
|
11716
|
+
const descriptions = uniqueStrings([
|
|
11717
|
+
primary.description,
|
|
11718
|
+
...primary.alternateDescriptions,
|
|
11719
|
+
secondary.description,
|
|
11720
|
+
...secondary.alternateDescriptions
|
|
11721
|
+
]).filter(Boolean);
|
|
11722
|
+
const description = descriptions[0] ?? "";
|
|
11723
|
+
return {
|
|
11724
|
+
id,
|
|
11725
|
+
name: first.name || second.name,
|
|
11726
|
+
description,
|
|
11727
|
+
type: first.type ?? second.type,
|
|
11728
|
+
ecosystem: first.ecosystem ?? second.ecosystem,
|
|
11729
|
+
tags: sortedUniqueStrings([...first.tags, ...second.tags]),
|
|
11730
|
+
provides: sortedUniqueStrings([...first.provides, ...second.provides]),
|
|
11731
|
+
source: first.source ?? second.source,
|
|
11732
|
+
repoUrl: first.repoUrl ?? second.repoUrl,
|
|
11733
|
+
installCommand: first.installCommand ?? second.installCommand,
|
|
11734
|
+
installability: betterInstallability(first.installability, second.installability),
|
|
11735
|
+
provenances: PROVENANCE_ORDER.filter(
|
|
11736
|
+
(provenance) => first.provenances.includes(provenance) || second.provenances.includes(provenance)
|
|
11737
|
+
),
|
|
11738
|
+
archived: first.archived || second.archived,
|
|
11739
|
+
featured: first.featured || second.featured,
|
|
11740
|
+
stars: maxDefined(first.stars, second.stars),
|
|
11741
|
+
lastPush: maxText(first.lastPush, second.lastPush),
|
|
11742
|
+
alternateDescriptions: descriptions.slice(1),
|
|
11743
|
+
descriptionRank: Math.max(first.descriptionRank, second.descriptionRank),
|
|
11744
|
+
hasRegistrySelectors: first.hasRegistrySelectors || second.hasRegistrySelectors
|
|
11745
|
+
};
|
|
11746
|
+
}
|
|
11747
|
+
function scoreEntry(entry, query, queryTokens) {
|
|
11748
|
+
let score = 0;
|
|
11749
|
+
const matched = /* @__PURE__ */ new Set();
|
|
11750
|
+
const name = normalizeSearchText(entry.name);
|
|
11751
|
+
const descriptions = [entry.description, ...entry.alternateDescriptions].map(normalizeSearchText);
|
|
11752
|
+
const tags = entry.tags.map(normalizeSearchText);
|
|
11753
|
+
const provides = entry.provides.map(normalizeSearchText);
|
|
11754
|
+
const type = normalizeSearchText(entry.type);
|
|
11755
|
+
const ecosystem = normalizeSearchText(entry.ecosystem ?? "");
|
|
11756
|
+
const repositories = [entry.source ?? "", entry.repoUrl ?? ""].map(normalizeSearchText);
|
|
11757
|
+
if (name === query) {
|
|
11758
|
+
score += SCORE.exactName;
|
|
11759
|
+
matched.add("name");
|
|
11760
|
+
} else if (name.startsWith(query)) {
|
|
11761
|
+
score += SCORE.namePrefix;
|
|
11762
|
+
matched.add("name");
|
|
11763
|
+
} else if (name.includes(query)) {
|
|
11764
|
+
score += SCORE.namePhrase;
|
|
11765
|
+
matched.add("name");
|
|
11766
|
+
}
|
|
11767
|
+
const tagsPhraseMatch = matchesPhrase(tags, query);
|
|
11768
|
+
const providesPhraseMatch = matchesPhrase(provides, query);
|
|
11769
|
+
if (tagsPhraseMatch || providesPhraseMatch) {
|
|
11770
|
+
score += SCORE.tagProvidesPhrase;
|
|
11771
|
+
if (tagsPhraseMatch) matched.add("tags");
|
|
11772
|
+
if (providesPhraseMatch) matched.add("provides");
|
|
11773
|
+
}
|
|
11774
|
+
if (matchesPhrase(descriptions, query)) {
|
|
11775
|
+
score += SCORE.descriptionPhrase;
|
|
11776
|
+
matched.add("description");
|
|
11777
|
+
}
|
|
11778
|
+
const typePhraseMatch = type.includes(query);
|
|
11779
|
+
const ecosystemPhraseMatch = ecosystem.includes(query);
|
|
11780
|
+
if (typePhraseMatch || ecosystemPhraseMatch) {
|
|
11781
|
+
score += SCORE.typeEcosystemPhrase;
|
|
11782
|
+
if (typePhraseMatch) matched.add("type");
|
|
11783
|
+
if (ecosystemPhraseMatch) matched.add("ecosystem");
|
|
11784
|
+
}
|
|
11785
|
+
if (matchesPhrase(repositories, query)) {
|
|
11786
|
+
score += SCORE.repositoryPhrase;
|
|
11787
|
+
matched.add("repository");
|
|
11788
|
+
}
|
|
11789
|
+
const nameTokens = name.split(" ");
|
|
11790
|
+
const tagTokenText = tags.join(" ");
|
|
11791
|
+
const provideTokenText = provides.join(" ");
|
|
11792
|
+
const descriptionTokenText = descriptions.join(" ");
|
|
11793
|
+
const repositoryTokenText = repositories.join(" ");
|
|
11794
|
+
let allTermsCovered = true;
|
|
11795
|
+
for (const token of queryTokens) {
|
|
11796
|
+
const nameTokenMatch = includesToken(name, token);
|
|
11797
|
+
if (nameTokenMatch) {
|
|
11798
|
+
score += SCORE.nameToken;
|
|
11799
|
+
matched.add("name");
|
|
11800
|
+
} else if (nameTokens.some((candidate) => candidate.startsWith(token))) {
|
|
11801
|
+
score += SCORE.nameTokenPrefix;
|
|
11802
|
+
matched.add("name");
|
|
11803
|
+
}
|
|
11804
|
+
const tagTokenMatch = includesToken(tagTokenText, token);
|
|
11805
|
+
const provideTokenMatch = includesToken(provideTokenText, token);
|
|
11806
|
+
if (tagTokenMatch || provideTokenMatch) {
|
|
11807
|
+
score += SCORE.tagProvidesToken;
|
|
11808
|
+
if (tagTokenMatch) matched.add("tags");
|
|
11809
|
+
if (provideTokenMatch) matched.add("provides");
|
|
11810
|
+
}
|
|
11811
|
+
const descriptionTokenMatch = includesToken(descriptionTokenText, token);
|
|
11812
|
+
if (descriptionTokenMatch) {
|
|
11813
|
+
score += SCORE.descriptionToken;
|
|
11814
|
+
matched.add("description");
|
|
11815
|
+
}
|
|
11816
|
+
const typeTokenMatch = includesToken(type, token);
|
|
11817
|
+
const ecosystemTokenMatch = includesToken(ecosystem, token);
|
|
11818
|
+
if (typeTokenMatch || ecosystemTokenMatch) {
|
|
11819
|
+
score += SCORE.typeEcosystemToken;
|
|
11820
|
+
if (typeTokenMatch) matched.add("type");
|
|
11821
|
+
if (ecosystemTokenMatch) matched.add("ecosystem");
|
|
11822
|
+
}
|
|
11823
|
+
const repositoryTokenMatch = includesToken(repositoryTokenText, token);
|
|
11824
|
+
if (repositoryTokenMatch) {
|
|
11825
|
+
score += SCORE.repositoryToken;
|
|
11826
|
+
matched.add("repository");
|
|
11827
|
+
}
|
|
11828
|
+
if (!nameTokenMatch && !tagTokenMatch && !provideTokenMatch && !descriptionTokenMatch && !typeTokenMatch && !ecosystemTokenMatch && !repositoryTokenMatch) {
|
|
11829
|
+
allTermsCovered = false;
|
|
11830
|
+
}
|
|
11831
|
+
}
|
|
11832
|
+
if (allTermsCovered) {
|
|
11833
|
+
score += SCORE.allTerms;
|
|
11834
|
+
}
|
|
11835
|
+
return {
|
|
11836
|
+
id: entry.id,
|
|
11837
|
+
name: entry.name,
|
|
11838
|
+
description: entry.description,
|
|
11839
|
+
type: entry.type,
|
|
11840
|
+
...entry.ecosystem ? { ecosystem: entry.ecosystem } : {},
|
|
11841
|
+
tags: entry.tags,
|
|
11842
|
+
provides: entry.provides,
|
|
11843
|
+
...entry.source ? { source: entry.source } : {},
|
|
11844
|
+
...entry.repoUrl ? { repoUrl: entry.repoUrl } : {},
|
|
11845
|
+
...entry.installCommand ? { installCommand: entry.installCommand } : {},
|
|
11846
|
+
installability: entry.installability,
|
|
11847
|
+
provenances: entry.provenances,
|
|
11848
|
+
score,
|
|
11849
|
+
matchedFields: MATCHED_FIELD_ORDER.filter((field) => matched.has(field))
|
|
11850
|
+
};
|
|
11851
|
+
}
|
|
11852
|
+
function catalogueEntries(catalogue) {
|
|
11853
|
+
if (!catalogue) return [];
|
|
11854
|
+
return Array.isArray(catalogue) ? catalogue : catalogue.entries;
|
|
11855
|
+
}
|
|
11856
|
+
function canonicalizeSource(source) {
|
|
11857
|
+
const value = source.normalize("NFKC").trim();
|
|
11858
|
+
const github = value.match(
|
|
11859
|
+
/^(?:github:|git:(?:git\+)?https?:\/\/github\.com\/|(?:git\+)?https?:\/\/github\.com\/)([^/#]+)\/([^#]+?)(?:#(.*))?$/i
|
|
11860
|
+
);
|
|
11861
|
+
if (github) {
|
|
11862
|
+
const owner = github[1].toLowerCase();
|
|
11863
|
+
const repository = github[2].replace(/\.git$/i, "").replace(/\/+$/, "").toLowerCase();
|
|
11864
|
+
const ref = github[3];
|
|
11865
|
+
return `github:${owner}/${repository}${ref === void 0 ? "" : `#${ref}`}`;
|
|
11866
|
+
}
|
|
11867
|
+
return value.replace(/\/+$/, "");
|
|
11868
|
+
}
|
|
11869
|
+
function inferEcosystem(source) {
|
|
11870
|
+
const canonical = canonicalizeSource(source);
|
|
11871
|
+
if (canonical.startsWith("vercel:")) return "vercel";
|
|
11872
|
+
if (canonical.startsWith("mcp-registry:")) return "mcp-registry";
|
|
11873
|
+
if (canonical.startsWith("clawhub:")) return "clawhub";
|
|
11874
|
+
if (canonical.startsWith("skillkit:")) return "skillkit";
|
|
11875
|
+
return void 0;
|
|
11876
|
+
}
|
|
11877
|
+
function inferType(ecosystem) {
|
|
11878
|
+
if (ecosystem === "vercel" || ecosystem === "skillkit") return "skill";
|
|
11879
|
+
if (ecosystem === "mcp-registry") return "mcp";
|
|
11880
|
+
if (ecosystem === "clawhub") return "plugin";
|
|
11881
|
+
return "package";
|
|
11882
|
+
}
|
|
11883
|
+
function betterInstallability(a, b) {
|
|
11884
|
+
const rank = { registry: 3, source: 2, informational: 1 };
|
|
11885
|
+
return rank[a] >= rank[b] ? a : b;
|
|
11886
|
+
}
|
|
11887
|
+
function nonEmpty(value) {
|
|
11888
|
+
return value?.trim() ? value : void 0;
|
|
11889
|
+
}
|
|
11890
|
+
function uniqueStrings(values) {
|
|
11891
|
+
return [...new Set(values)];
|
|
11892
|
+
}
|
|
11893
|
+
function sortedUniqueStrings(values) {
|
|
11894
|
+
return uniqueStrings(values).sort(compareText);
|
|
11895
|
+
}
|
|
11896
|
+
function enrichedInstallCommand(entry, source) {
|
|
11897
|
+
const catalogueCommand = nonEmpty(entry.installCommand);
|
|
11898
|
+
if (!source) return catalogueCommand;
|
|
11899
|
+
if (entry.ecosystem === "mcp-registry" || entry.ecosystem === "clawhub") {
|
|
11900
|
+
return catalogueCommand ?? `npx agentwheel install ${shellQuote2(source)}`;
|
|
11901
|
+
}
|
|
11902
|
+
return `npx agentwheel install ${shellQuote2(source)}`;
|
|
11903
|
+
}
|
|
11904
|
+
function shellQuote2(value) {
|
|
11905
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
11906
|
+
}
|
|
11907
|
+
function matchesPhrase(fields, query) {
|
|
11908
|
+
return fields.some((field) => field.includes(query));
|
|
11909
|
+
}
|
|
11910
|
+
function compareStableEntries(a, b) {
|
|
11911
|
+
return compareText(normalizeSearchText(a.name), normalizeSearchText(b.name)) || compareText(a.id, b.id);
|
|
11912
|
+
}
|
|
11913
|
+
function maxDefined(a, b) {
|
|
11914
|
+
if (a === void 0) return b;
|
|
11915
|
+
if (b === void 0) return a;
|
|
11916
|
+
return Math.max(a, b);
|
|
11917
|
+
}
|
|
11918
|
+
function maxText(a, b) {
|
|
11919
|
+
if (a === void 0) return b;
|
|
11920
|
+
if (b === void 0) return a;
|
|
11921
|
+
return a >= b ? a : b;
|
|
11922
|
+
}
|
|
11923
|
+
function assertUniqueIdentities(ids, label) {
|
|
11924
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11925
|
+
for (const id of ids) {
|
|
11926
|
+
if (seen.has(id)) throw new Error(`Duplicate ${label} id: ${id}`);
|
|
11927
|
+
seen.add(id);
|
|
11928
|
+
}
|
|
11929
|
+
}
|
|
11930
|
+
function compareText(a, b) {
|
|
11931
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
11932
|
+
}
|
|
11933
|
+
function includesToken(normalizedText, token) {
|
|
11934
|
+
return normalizedText === token || normalizedText.startsWith(`${token} `) || normalizedText.endsWith(` ${token}`) || normalizedText.includes(` ${token} `);
|
|
11935
|
+
}
|
|
11936
|
+
|
|
11321
11937
|
// src/cli/index.ts
|
|
11322
11938
|
var CLI_VERSION = resolveCliVersion();
|
|
11323
11939
|
var COMPANION_SKILL_SOURCE = "github:NestDevLab/agentwheel";
|
|
@@ -11361,17 +11977,59 @@ program.command("list").description("list artifacts exposed by a package source"
|
|
|
11361
11977
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
11362
11978
|
const selectedArtifacts = selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry);
|
|
11363
11979
|
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:
|
|
11980
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join45(targetRoot, ".agentwheel", "cache") }))));
|
|
11365
11981
|
const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
|
|
11366
11982
|
for (const artifact of artifacts) {
|
|
11367
11983
|
console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
|
|
11368
11984
|
}
|
|
11369
11985
|
});
|
|
11986
|
+
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) => {
|
|
11987
|
+
const trimmedQuery = query.trim();
|
|
11988
|
+
if (!trimmedQuery) {
|
|
11989
|
+
throw new Error("Search query must not be empty.");
|
|
11990
|
+
}
|
|
11991
|
+
const scope = parseSearchScope(options.scope);
|
|
11992
|
+
const type = options.type === void 0 ? void 0 : parseSearchType(options.type);
|
|
11993
|
+
const ecosystem = options.ecosystem === void 0 ? void 0 : parseSearchEcosystem(options.ecosystem);
|
|
11994
|
+
const limit = parseSearchLimit(options.limit);
|
|
11995
|
+
if (options.refresh && options.offline) {
|
|
11996
|
+
throw new Error("--refresh cannot be used with --offline.");
|
|
11997
|
+
}
|
|
11998
|
+
const warning = (message) => console.error(message);
|
|
11999
|
+
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
12000
|
+
const registryRequest = scope === "all" || scope === "registry" ? new RegistryClient({ workspaceRoot: targetRoot, offline: options.offline, warn: warning }).getIndex({ refresh: options.refresh }) : void 0;
|
|
12001
|
+
const catalogueRequest = scope === "all" || scope === "enriched" || scope === "vercel" ? new CatalogueClient({ offline: options.offline, warn: warning }).getIndex({ refresh: options.refresh }) : void 0;
|
|
12002
|
+
const [registryIndex, catalogueIndex] = await Promise.all([registryRequest, catalogueRequest]);
|
|
12003
|
+
const entries = buildSearchEntries({
|
|
12004
|
+
registry: registryIndex?.entries,
|
|
12005
|
+
enriched: scope === "all" || scope === "enriched" ? catalogueIndex?.enriched : void 0,
|
|
12006
|
+
vercel: scope === "all" || scope === "vercel" ? catalogueIndex?.vercel : void 0
|
|
12007
|
+
});
|
|
12008
|
+
const results = searchEntries(entries, trimmedQuery, {
|
|
12009
|
+
type,
|
|
12010
|
+
ecosystem,
|
|
12011
|
+
limit,
|
|
12012
|
+
includeArchived: options.includeArchived
|
|
12013
|
+
});
|
|
12014
|
+
const loadedIndexes = [registryIndex, catalogueIndex].filter((index) => index !== void 0);
|
|
12015
|
+
const response = {
|
|
12016
|
+
schemaVersion: 1,
|
|
12017
|
+
query: trimmedQuery,
|
|
12018
|
+
scope,
|
|
12019
|
+
fromCache: loadedIndexes.every((index) => index.fromCache),
|
|
12020
|
+
results
|
|
12021
|
+
};
|
|
12022
|
+
if (options.json) {
|
|
12023
|
+
console.log(JSON.stringify(response, null, 2));
|
|
12024
|
+
return;
|
|
12025
|
+
}
|
|
12026
|
+
printSearchResults(trimmedQuery, results);
|
|
12027
|
+
});
|
|
11370
12028
|
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
12029
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
11372
12030
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
11373
12031
|
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:
|
|
12032
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join45(targetRoot, ".agentwheel", "cache") }))));
|
|
11375
12033
|
const result = await driver.scan(resolved);
|
|
11376
12034
|
if (result.findings.length === 0) {
|
|
11377
12035
|
console.log("Scan ok: no findings");
|
|
@@ -11434,7 +12092,7 @@ program.command("deps").description("inspect the OpenPack dependency graph").add
|
|
|
11434
12092
|
for (const decision of result.bundle.graphLock.canonical.overrides) {
|
|
11435
12093
|
console.log(`OVERRIDE ${decision.graphNodeId}:${decision.type}/${decision.name} replaces ${decision.overriddenGraphNodeId}:${decision.type}/${decision.name} via ${decision.rootId} (${decision.selector})`);
|
|
11436
12094
|
}
|
|
11437
|
-
await
|
|
12095
|
+
await rm12(result.bundle.root, { recursive: true, force: true });
|
|
11438
12096
|
}
|
|
11439
12097
|
continue;
|
|
11440
12098
|
}
|
|
@@ -11466,11 +12124,6 @@ program.command("registry").description("manage optional registry indexes").addC
|
|
|
11466
12124
|
const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
|
|
11467
12125
|
printRegistryEntries((await client.getIndex()).entries);
|
|
11468
12126
|
})
|
|
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
12127
|
).addCommand(
|
|
11475
12128
|
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
12129
|
const draft = createRegistryPublishDraft(source, {
|
|
@@ -11644,7 +12297,7 @@ journalCommand.command("list").description("show pending apply journals for reso
|
|
|
11644
12297
|
if (!journal) continue;
|
|
11645
12298
|
pending += 1;
|
|
11646
12299
|
console.log(`PENDING ${state.adapter.name}/${state.installationType} at ${state.installRoot}`);
|
|
11647
|
-
console.log(` journal: ${
|
|
12300
|
+
console.log(` journal: ${join45(state.installRoot, ".agentwheel", `${state.state.stateKey}.apply-journal.json`)}`);
|
|
11648
12301
|
console.log(` stateKey: ${state.state.stateKey}`);
|
|
11649
12302
|
console.log(` createdAt: ${journal.createdAt}`);
|
|
11650
12303
|
console.log(` updatedAt: ${journal.updatedAt}`);
|
|
@@ -11696,6 +12349,14 @@ async function runInstallCommand(nameOrSource, options, behavior) {
|
|
|
11696
12349
|
if (outputFormat !== "human") {
|
|
11697
12350
|
const report = await buildPlanReport(nameOrSource, normalizedOptions);
|
|
11698
12351
|
if (report.targets.some((target) => target.hasBlockingChanges)) process.exitCode = 1;
|
|
12352
|
+
if (behavior.apply) {
|
|
12353
|
+
await runInstallCommand(
|
|
12354
|
+
nameOrSource,
|
|
12355
|
+
{ ...normalizedOptions, format: "human", json: false },
|
|
12356
|
+
{ apply: true, quiet: true }
|
|
12357
|
+
);
|
|
12358
|
+
report.applied = true;
|
|
12359
|
+
}
|
|
11699
12360
|
process.stdout.write(`${renderReport(report, outputFormat)}
|
|
11700
12361
|
`);
|
|
11701
12362
|
return;
|
|
@@ -11735,12 +12396,14 @@ async function runInstallCommand(nameOrSource, options, behavior) {
|
|
|
11735
12396
|
warn: (message) => console.warn(message)
|
|
11736
12397
|
});
|
|
11737
12398
|
for (const result of results) {
|
|
11738
|
-
|
|
11739
|
-
|
|
11740
|
-
|
|
12399
|
+
if (!behavior.quiet) {
|
|
12400
|
+
console.log(`Profile ${normalizedOptions.profile} / ${result.runtime} / ${result.packageName} at ${result.targetRoot} (${result.transport}):`);
|
|
12401
|
+
console.log(formatPlan(result.plan));
|
|
12402
|
+
if (result.reloaded) console.log(`Reloaded runtime via ${result.reloadCommandSummary}.`);
|
|
12403
|
+
}
|
|
11741
12404
|
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
11742
12405
|
}
|
|
11743
|
-
if (behavior.apply) {
|
|
12406
|
+
if (behavior.apply && !behavior.quiet) {
|
|
11744
12407
|
console.log("Applied.");
|
|
11745
12408
|
}
|
|
11746
12409
|
return;
|
|
@@ -11767,7 +12430,7 @@ async function runInstallCommand(nameOrSource, options, behavior) {
|
|
|
11767
12430
|
}
|
|
11768
12431
|
}
|
|
11769
12432
|
for (const result of await buildGraphPlansForTarget(target, source, { ...targetOptions, scope, extraPackage, reportFormat: outputFormat }, { mode: "install" })) {
|
|
11770
|
-
console.log(formatGraphPlan(result));
|
|
12433
|
+
if (!behavior.quiet) console.log(formatGraphPlan(result));
|
|
11771
12434
|
if (behavior.apply) {
|
|
11772
12435
|
const transport = transportForTarget(target);
|
|
11773
12436
|
const executePlugins = target.executePlugins ?? targetOptions.executePlugins;
|
|
@@ -11781,10 +12444,12 @@ async function runInstallCommand(nameOrSource, options, behavior) {
|
|
|
11781
12444
|
enabled: target.reloadRuntimes ?? shouldReloadRuntimes(targetOptions),
|
|
11782
12445
|
executePlugins
|
|
11783
12446
|
});
|
|
11784
|
-
|
|
11785
|
-
|
|
12447
|
+
if (!behavior.quiet) {
|
|
12448
|
+
console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
|
|
12449
|
+
if (reloaded) console.log(`Reloaded runtime via ${formatReloadCommands(target.reloadCommands)}.`);
|
|
12450
|
+
}
|
|
11786
12451
|
}
|
|
11787
|
-
await
|
|
12452
|
+
await rm12(result.bundle.root, { recursive: true, force: true });
|
|
11788
12453
|
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
11789
12454
|
}
|
|
11790
12455
|
if (behavior.apply && extraPackage && !targetOptions.onlySource) {
|
|
@@ -11879,7 +12544,7 @@ async function buildPlanReport(nameOrSource, options) {
|
|
|
11879
12544
|
for (const result of results) {
|
|
11880
12545
|
reportTargets.push(installPlanReportTarget(result.plan, result.graphLockDigest));
|
|
11881
12546
|
reportWarnings.push(...result.warnings);
|
|
11882
|
-
await
|
|
12547
|
+
await rm12(result.bundle.root, { recursive: true, force: true });
|
|
11883
12548
|
}
|
|
11884
12549
|
}
|
|
11885
12550
|
return planReport(reportTargets, reportWarnings);
|
|
@@ -11935,7 +12600,7 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
11935
12600
|
const bundle = await stageSource(driver, resolvedSource, {
|
|
11936
12601
|
workspaceRoot: targetRoot,
|
|
11937
12602
|
adapter,
|
|
11938
|
-
cacheRoot:
|
|
12603
|
+
cacheRoot: join45(targetRoot, ".agentwheel", "cache"),
|
|
11939
12604
|
mode: options.mode,
|
|
11940
12605
|
ref: initialVersion?.ref,
|
|
11941
12606
|
frozenLock: lockMode,
|
|
@@ -11961,7 +12626,7 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
11961
12626
|
overrides: overrideArtifactsFromOptions(options)
|
|
11962
12627
|
};
|
|
11963
12628
|
} finally {
|
|
11964
|
-
await
|
|
12629
|
+
await rm12(bundle.root, { recursive: true, force: true });
|
|
11965
12630
|
}
|
|
11966
12631
|
}
|
|
11967
12632
|
function findConfiguredPackage(packages, value) {
|
|
@@ -12112,7 +12777,7 @@ async function runConfiguredGraphPackages(target, options, behavior) {
|
|
|
12112
12777
|
console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
|
|
12113
12778
|
if (reloaded) console.log(`Reloaded runtime via ${formatReloadCommands(target.reloadCommands)}.`);
|
|
12114
12779
|
}
|
|
12115
|
-
await
|
|
12780
|
+
await rm12(result.bundle.root, { recursive: true, force: true });
|
|
12116
12781
|
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
12117
12782
|
}
|
|
12118
12783
|
}
|
|
@@ -12329,7 +12994,7 @@ function scopeUpdatePlanToDependencies(result, selectors, previousLock, manifest
|
|
|
12329
12994
|
selectedPreviousNodeIds,
|
|
12330
12995
|
selectedRootIds
|
|
12331
12996
|
);
|
|
12332
|
-
const graphLockDigest =
|
|
12997
|
+
const graphLockDigest = createHash12("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
12333
12998
|
return {
|
|
12334
12999
|
...result,
|
|
12335
13000
|
bundle: { ...result.bundle, graphLock },
|
|
@@ -12497,7 +13162,7 @@ function scopeUpdatePlanToRoot(result, rootId, previousLock, manifest) {
|
|
|
12497
13162
|
selectedPreviousNodeIds,
|
|
12498
13163
|
/* @__PURE__ */ new Set([rootId])
|
|
12499
13164
|
);
|
|
12500
|
-
const graphLockDigest =
|
|
13165
|
+
const graphLockDigest = createHash12("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
|
|
12501
13166
|
return {
|
|
12502
13167
|
...scoped,
|
|
12503
13168
|
bundle: { ...scoped.bundle, graphLock },
|
|
@@ -12569,7 +13234,7 @@ function keepManifestEntryOperation(entry, targetRoot, scopeDescription, operati
|
|
|
12569
13234
|
artifactType: entry.artifactType,
|
|
12570
13235
|
artifactName: entry.artifactName,
|
|
12571
13236
|
kind: entry.kind,
|
|
12572
|
-
destPath: operation?.destPath ??
|
|
13237
|
+
destPath: operation?.destPath ?? join45(targetRoot, entry.path),
|
|
12573
13238
|
relativeDestPath: entry.path,
|
|
12574
13239
|
desiredHash: entry.sourceHash,
|
|
12575
13240
|
currentHash: operation?.currentHash ?? entry.hash,
|
|
@@ -12686,7 +13351,7 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
12686
13351
|
if (!options.dryRun) {
|
|
12687
13352
|
console.log(formatUninstallResult(result));
|
|
12688
13353
|
}
|
|
12689
|
-
if (renderedRoot) await
|
|
13354
|
+
if (renderedRoot) await rm12(renderedRoot, { recursive: true, force: true });
|
|
12690
13355
|
if (plan.hasBlockingChanges) process.exitCode = 1;
|
|
12691
13356
|
}
|
|
12692
13357
|
}
|
|
@@ -13137,7 +13802,7 @@ async function collectPendingInstallWork(target, options) {
|
|
|
13137
13802
|
const message = error instanceof Error ? error.message : String(error);
|
|
13138
13803
|
return { pendingCount: 0, driftCount: 0, conflictCount: 0, counts: {}, error: message };
|
|
13139
13804
|
} finally {
|
|
13140
|
-
await Promise.all(results.map((result) =>
|
|
13805
|
+
await Promise.all(results.map((result) => rm12(result.bundle.root, { recursive: true, force: true })));
|
|
13141
13806
|
}
|
|
13142
13807
|
}
|
|
13143
13808
|
async function printDoctor(target, options) {
|
|
@@ -13154,12 +13819,12 @@ async function printDoctor(target, options) {
|
|
|
13154
13819
|
const requestedSkills = doctorSkillRequests(target, options);
|
|
13155
13820
|
const skills = [];
|
|
13156
13821
|
for (const request of requestedSkills) {
|
|
13157
|
-
const skillPath =
|
|
13822
|
+
const skillPath = join45(state.installRoot, targetMapping.dest, request.name);
|
|
13158
13823
|
const exists = await pathExists(skillPath);
|
|
13159
13824
|
const manifestEntry = manifest?.entries.find((entry) => {
|
|
13160
13825
|
if (entry.artifactType !== "skills") return false;
|
|
13161
13826
|
const legacyInstallName = "installName" in entry && typeof entry.installName === "string" ? entry.installName : void 0;
|
|
13162
|
-
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path ===
|
|
13827
|
+
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path === join45(targetMapping.dest, request.name);
|
|
13163
13828
|
});
|
|
13164
13829
|
const status = manifestEntry ? "managed" : exists ? "present-unmanaged" : "missing";
|
|
13165
13830
|
skills.push({
|
|
@@ -13239,7 +13904,7 @@ function doctorSkillLabel(name) {
|
|
|
13239
13904
|
return `${name} skill`;
|
|
13240
13905
|
}
|
|
13241
13906
|
function isSyncwheelWorkspace(targetRoot) {
|
|
13242
|
-
return existsSync(
|
|
13907
|
+
return existsSync(join45(targetRoot, ".syncwheel", "manifest.json"));
|
|
13243
13908
|
}
|
|
13244
13909
|
function skillInstallCommand(adapter, installationType, options, skill, behavior = {}) {
|
|
13245
13910
|
const args = [
|
|
@@ -13307,7 +13972,7 @@ function normalizeRuntimeScopeOptions(options, behavior = {}) {
|
|
|
13307
13972
|
}
|
|
13308
13973
|
const canDefaultTargetRoot = !options.agent && !options.all && !options.allDetected && !options.profile;
|
|
13309
13974
|
if (!targetRoot && canDefaultTargetRoot && (options.user || installationType === "user" || behavior.defaultUser)) {
|
|
13310
|
-
targetRoot =
|
|
13975
|
+
targetRoot = homedir10();
|
|
13311
13976
|
}
|
|
13312
13977
|
if (!installationType && behavior.defaultUser) {
|
|
13313
13978
|
installationType = "user";
|
|
@@ -13327,12 +13992,12 @@ function looksLikeSourceSpecifier(value) {
|
|
|
13327
13992
|
return value.includes(":") || value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value === "~" || value.startsWith("~/");
|
|
13328
13993
|
}
|
|
13329
13994
|
function normalizeCliPath(value) {
|
|
13330
|
-
if (value === "~") return
|
|
13331
|
-
if (value.startsWith("~/")) return resolve22(
|
|
13995
|
+
if (value === "~") return homedir10();
|
|
13996
|
+
if (value.startsWith("~/")) return resolve22(homedir10(), value.slice(2));
|
|
13332
13997
|
return resolve22(value);
|
|
13333
13998
|
}
|
|
13334
13999
|
function isHomePath(path) {
|
|
13335
|
-
return resolve22(path) === resolve22(
|
|
14000
|
+
return resolve22(path) === resolve22(homedir10());
|
|
13336
14001
|
}
|
|
13337
14002
|
function adapterListFromOption(adapter) {
|
|
13338
14003
|
if (!adapter) return [];
|
|
@@ -13387,10 +14052,10 @@ function filterUninstallPlanBySelection(plan, selected) {
|
|
|
13387
14052
|
};
|
|
13388
14053
|
}
|
|
13389
14054
|
async function initPackage(root) {
|
|
13390
|
-
await mkdir23(
|
|
13391
|
-
await mkdir23(
|
|
13392
|
-
await mkdir23(
|
|
13393
|
-
const manifestPath =
|
|
14055
|
+
await mkdir23(join45(root, "instructions"), { recursive: true });
|
|
14056
|
+
await mkdir23(join45(root, "rules"), { recursive: true });
|
|
14057
|
+
await mkdir23(join45(root, "skills"), { recursive: true });
|
|
14058
|
+
const manifestPath = join45(root, "openpack.json");
|
|
13394
14059
|
const manifest = {
|
|
13395
14060
|
schemaVersion: 2,
|
|
13396
14061
|
name: "example/agentwheel-package",
|
|
@@ -13403,7 +14068,7 @@ async function initPackage(root) {
|
|
|
13403
14068
|
};
|
|
13404
14069
|
await writeFile22(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
13405
14070
|
`, "utf8");
|
|
13406
|
-
await writeFile22(
|
|
14071
|
+
await writeFile22(join45(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
|
|
13407
14072
|
}
|
|
13408
14073
|
async function defaultBootstrapPackage(_root) {
|
|
13409
14074
|
const packageRoot = await findAgentwheelPackageRoot(dirname32(fileURLToPath3(import.meta.url)));
|
|
@@ -13474,6 +14139,49 @@ function printRegistryEntries(entries) {
|
|
|
13474
14139
|
console.log(`${entry.name} ${entry.type} ${entry.source} ${entry.description}${tags}`);
|
|
13475
14140
|
}
|
|
13476
14141
|
}
|
|
14142
|
+
function parseSearchScope(value) {
|
|
14143
|
+
const parsed = searchScopeSchema.safeParse(value);
|
|
14144
|
+
if (parsed.success) return parsed.data;
|
|
14145
|
+
throw new Error(`Invalid search scope: ${value}. Expected one of: ${searchScopeSchema.options.join(", ")}.`);
|
|
14146
|
+
}
|
|
14147
|
+
function parseSearchType(value) {
|
|
14148
|
+
const parsed = searchTypeSchema.safeParse(value);
|
|
14149
|
+
if (parsed.success) return parsed.data;
|
|
14150
|
+
throw new Error(`Invalid artifact type: ${value}. Expected one of: ${searchTypeSchema.options.join(", ")}.`);
|
|
14151
|
+
}
|
|
14152
|
+
function parseSearchEcosystem(value) {
|
|
14153
|
+
const parsed = searchEcosystemSchema.safeParse(value);
|
|
14154
|
+
if (parsed.success) return parsed.data;
|
|
14155
|
+
throw new Error(`Invalid ecosystem: ${value}. Expected one of: ${searchEcosystemSchema.options.join(", ")}.`);
|
|
14156
|
+
}
|
|
14157
|
+
function parseSearchLimit(value) {
|
|
14158
|
+
const limit = Number(value);
|
|
14159
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
14160
|
+
throw new Error(`Invalid search limit: ${value}. Expected an integer from 1 to 100.`);
|
|
14161
|
+
}
|
|
14162
|
+
return limit;
|
|
14163
|
+
}
|
|
14164
|
+
function printSearchResults(query, results) {
|
|
14165
|
+
if (results.length === 0) {
|
|
14166
|
+
console.log(`No artifacts found for ${JSON.stringify(query)}.`);
|
|
14167
|
+
return;
|
|
14168
|
+
}
|
|
14169
|
+
for (const [index, result] of results.entries()) {
|
|
14170
|
+
const ecosystem = result.ecosystem ?? "unknown";
|
|
14171
|
+
const provenances = result.provenances.join("+");
|
|
14172
|
+
console.log(
|
|
14173
|
+
`${index + 1}. ${result.name} [type=${result.type}; ecosystem=${ecosystem}; installability=${result.installability}; provenance=${provenances}]`
|
|
14174
|
+
);
|
|
14175
|
+
console.log(` ${result.description || "(no description)"}`);
|
|
14176
|
+
if (result.installCommand) {
|
|
14177
|
+
console.log(` Install: ${result.installCommand}`);
|
|
14178
|
+
} else if (result.source) {
|
|
14179
|
+
console.log(` Source: ${result.source}`);
|
|
14180
|
+
} else {
|
|
14181
|
+
console.log(" Install: unavailable");
|
|
14182
|
+
}
|
|
14183
|
+
}
|
|
14184
|
+
}
|
|
13477
14185
|
async function main() {
|
|
13478
14186
|
await maybeCheckForUpdate({
|
|
13479
14187
|
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.1"
|
|
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:
|