@morphist/aspects 0.1.2 → 0.1.3
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 +222 -122
- package/dist/scripts/postinstall.js +90 -0
- package/dist/{cli.js → src/cli.js} +1505 -226
- package/package.json +5 -3
|
@@ -6311,12 +6311,12 @@ var icons = {
|
|
|
6311
6311
|
var LINE_COLORS = [colors2.cyan, colors2.teal, colors2.orange, colors2.red];
|
|
6312
6312
|
function morphistBanner() {
|
|
6313
6313
|
const lines = [
|
|
6314
|
-
" █████╗
|
|
6315
|
-
"
|
|
6316
|
-
"
|
|
6317
|
-
"
|
|
6318
|
-
" ██║
|
|
6319
|
-
" ╚═╝
|
|
6314
|
+
" █████╗ ███████╗██████╗ ███████╗ ██████╗████████╗███████╗",
|
|
6315
|
+
" ██╔══██╗██╔════╝██╔══██╗██╔════╝██╔════╝╚══██╔══╝██╔════╝",
|
|
6316
|
+
" ███████║███████╗██████╔╝█████╗ ██║ ██║ ███████╗",
|
|
6317
|
+
" ██╔══██║╚════██║██╔═══╝ ██╔══╝ ██║ ██║ ╚════██║",
|
|
6318
|
+
" ██║ ██║███████║██║ ███████╗╚██████╗ ██║ ███████║",
|
|
6319
|
+
" ╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚══════╝"
|
|
6320
6320
|
];
|
|
6321
6321
|
console.log();
|
|
6322
6322
|
console.log(colors2.cyan(lines[0]));
|
|
@@ -6327,9 +6327,87 @@ function morphistBanner() {
|
|
|
6327
6327
|
console.log(colors2.red(lines[5]));
|
|
6328
6328
|
console.log();
|
|
6329
6329
|
}
|
|
6330
|
+
// package.json
|
|
6331
|
+
var package_default = {
|
|
6332
|
+
name: "@morphist/aspects",
|
|
6333
|
+
version: "0.1.3",
|
|
6334
|
+
description: "Package manager for AI personality aspects - like npm for agent personas",
|
|
6335
|
+
author: "Morphist <hello@morphist.ai>",
|
|
6336
|
+
license: "MIT",
|
|
6337
|
+
type: "module",
|
|
6338
|
+
repository: {
|
|
6339
|
+
type: "git",
|
|
6340
|
+
url: "git+https://github.com/aimorphist/aspects.git"
|
|
6341
|
+
},
|
|
6342
|
+
homepage: "https://github.com/aimorphist/aspects#readme",
|
|
6343
|
+
bugs: {
|
|
6344
|
+
url: "https://github.com/aimorphist/aspects/issues"
|
|
6345
|
+
},
|
|
6346
|
+
keywords: [
|
|
6347
|
+
"ai",
|
|
6348
|
+
"personality",
|
|
6349
|
+
"aspects",
|
|
6350
|
+
"agents",
|
|
6351
|
+
"llm",
|
|
6352
|
+
"prompts",
|
|
6353
|
+
"voice",
|
|
6354
|
+
"cli",
|
|
6355
|
+
"package-manager"
|
|
6356
|
+
],
|
|
6357
|
+
bin: {
|
|
6358
|
+
aspects: "dist/cli.js"
|
|
6359
|
+
},
|
|
6360
|
+
files: [
|
|
6361
|
+
"dist"
|
|
6362
|
+
],
|
|
6363
|
+
scripts: {
|
|
6364
|
+
dev: "bun run src/cli.ts",
|
|
6365
|
+
aspects: "bun run src/cli.ts",
|
|
6366
|
+
create: "bun run src/cli.ts create",
|
|
6367
|
+
new: "bun run src/cli.ts create",
|
|
6368
|
+
init: "bun run src/cli.ts create",
|
|
6369
|
+
add: "bun run src/cli.ts add",
|
|
6370
|
+
get: "bun run src/cli.ts add",
|
|
6371
|
+
list: "bun run src/cli.ts list",
|
|
6372
|
+
compile: "bun run src/cli.ts compile",
|
|
6373
|
+
edit: "bun run src/cli.ts edit",
|
|
6374
|
+
build: "bun build src/cli.ts scripts/postinstall.ts --outdir dist --target node --format esm",
|
|
6375
|
+
typecheck: "tsc --noEmit",
|
|
6376
|
+
prepublishOnly: "bun run build",
|
|
6377
|
+
postinstall: "node dist/postinstall.js || true",
|
|
6378
|
+
validate: "bun run scripts/validate-pr.ts",
|
|
6379
|
+
scan: "bun run scripts/security-scan.ts",
|
|
6380
|
+
test: "bun test tests/unit",
|
|
6381
|
+
"test:e2e": "./scripts/test-cli.sh",
|
|
6382
|
+
"test:unit": "bun test tests/unit",
|
|
6383
|
+
"test:integration": "ASPECTS_REGISTRY_URL=http://localhost:5173/api/v1 bun test tests/integration",
|
|
6384
|
+
"test:all": "bun test tests"
|
|
6385
|
+
},
|
|
6386
|
+
dependencies: {
|
|
6387
|
+
"@clack/prompts": "^0.11.0",
|
|
6388
|
+
"@noble/hashes": "^2.0.1",
|
|
6389
|
+
"@scure/base": "^2.0.0",
|
|
6390
|
+
citty: "^0.1.6",
|
|
6391
|
+
consola: "^3.2.3",
|
|
6392
|
+
ofetch: "^1.3.4",
|
|
6393
|
+
picocolors: "^1.1.1",
|
|
6394
|
+
zod: "^4.3.5"
|
|
6395
|
+
},
|
|
6396
|
+
devDependencies: {
|
|
6397
|
+
"@types/bun": "latest",
|
|
6398
|
+
"@types/node": "^22",
|
|
6399
|
+
typescript: "^5"
|
|
6400
|
+
},
|
|
6401
|
+
engines: {
|
|
6402
|
+
node: ">=18"
|
|
6403
|
+
},
|
|
6404
|
+
publishConfig: {
|
|
6405
|
+
access: "public"
|
|
6406
|
+
}
|
|
6407
|
+
};
|
|
6330
6408
|
|
|
6331
6409
|
// src/commands/create.ts
|
|
6332
|
-
import { writeFile as writeFile4, readFile as
|
|
6410
|
+
import { writeFile as writeFile4, readFile as readFile4, stat as stat3, mkdir as mkdir4 } from "node:fs/promises";
|
|
6333
6411
|
import { join as join5 } from "node:path";
|
|
6334
6412
|
import { execSync } from "node:child_process";
|
|
6335
6413
|
|
|
@@ -20607,11 +20685,18 @@ var OFFICIAL_CATEGORIES = [
|
|
|
20607
20685
|
"guide"
|
|
20608
20686
|
];
|
|
20609
20687
|
var FIELD_LIMITS = {
|
|
20688
|
+
nameMin: 2,
|
|
20610
20689
|
name: 50,
|
|
20690
|
+
displayNameMin: 2,
|
|
20611
20691
|
displayName: 100,
|
|
20692
|
+
taglineMin: 10,
|
|
20612
20693
|
tagline: 200,
|
|
20694
|
+
categoryMin: 2,
|
|
20695
|
+
category: 20,
|
|
20696
|
+
tagMin: 2,
|
|
20613
20697
|
tag: 30,
|
|
20614
20698
|
maxTags: 10,
|
|
20699
|
+
promptMin: 10,
|
|
20615
20700
|
prompt: 50000,
|
|
20616
20701
|
author: 100,
|
|
20617
20702
|
publisher: 50,
|
|
@@ -20631,19 +20716,17 @@ var FIELD_LIMITS = {
|
|
|
20631
20716
|
maxInstructions: 25
|
|
20632
20717
|
};
|
|
20633
20718
|
var aspectSchema = exports_external.object({
|
|
20634
|
-
schemaVersion: exports_external.
|
|
20635
|
-
name: exports_external.string().min(
|
|
20636
|
-
publisher: exports_external.string().max(FIELD_LIMITS.publisher, `publisher must be ${FIELD_LIMITS.publisher} chars or less`).
|
|
20719
|
+
schemaVersion: exports_external.literal(1),
|
|
20720
|
+
name: exports_external.string().min(FIELD_LIMITS.nameMin, `name must be at least ${FIELD_LIMITS.nameMin} chars`).max(FIELD_LIMITS.name, `name must be ${FIELD_LIMITS.name} chars or less`),
|
|
20721
|
+
publisher: exports_external.string().min(1, "publisher is required").max(FIELD_LIMITS.publisher, `publisher must be ${FIELD_LIMITS.publisher} chars or less`).default("anon-user"),
|
|
20637
20722
|
version: exports_external.string().default("0.0.0"),
|
|
20638
|
-
displayName: exports_external.string().min(
|
|
20639
|
-
tagline: exports_external.string().min(
|
|
20723
|
+
displayName: exports_external.string().min(FIELD_LIMITS.displayNameMin, `displayName must be at least ${FIELD_LIMITS.displayNameMin} chars`).max(FIELD_LIMITS.displayName, `displayName must be ${FIELD_LIMITS.displayName} chars or less`),
|
|
20724
|
+
tagline: exports_external.string().min(FIELD_LIMITS.taglineMin, `tagline must be at least ${FIELD_LIMITS.taglineMin} chars`).max(FIELD_LIMITS.tagline, `tagline must be ${FIELD_LIMITS.tagline} chars or less`),
|
|
20640
20725
|
icon: exports_external.string().max(FIELD_LIMITS.icon, `icon must be ${FIELD_LIMITS.icon} chars or less`).optional(),
|
|
20641
20726
|
author: exports_external.string().max(FIELD_LIMITS.author, `author must be ${FIELD_LIMITS.author} chars or less`).optional(),
|
|
20642
20727
|
license: exports_external.string().max(FIELD_LIMITS.license, `license must be ${FIELD_LIMITS.license} chars or less`).optional(),
|
|
20643
|
-
category: exports_external.
|
|
20644
|
-
|
|
20645
|
-
}),
|
|
20646
|
-
tags: exports_external.array(exports_external.string().max(FIELD_LIMITS.tag, `each tag must be ${FIELD_LIMITS.tag} chars or less`)).max(FIELD_LIMITS.maxTags, `maximum ${FIELD_LIMITS.maxTags} tags allowed`).optional(),
|
|
20728
|
+
category: exports_external.string().min(FIELD_LIMITS.categoryMin, `Category must be at least ${FIELD_LIMITS.categoryMin} characters`).max(FIELD_LIMITS.category, `Category must be at most ${FIELD_LIMITS.category} characters`).regex(/^[a-zA-Z0-9-]+$/, "Category must be alphanumeric with hyphens only"),
|
|
20729
|
+
tags: exports_external.array(exports_external.string().min(FIELD_LIMITS.tagMin, `each tag must be at least ${FIELD_LIMITS.tagMin} chars`).max(FIELD_LIMITS.tag, `each tag must be ${FIELD_LIMITS.tag} chars or less`)).max(FIELD_LIMITS.maxTags, `maximum ${FIELD_LIMITS.maxTags} tags allowed`).optional(),
|
|
20647
20730
|
voiceHints: exports_external.object({
|
|
20648
20731
|
speed: exports_external.enum(["slow", "normal", "fast"]).optional(),
|
|
20649
20732
|
emotions: exports_external.array(exports_external.string().max(FIELD_LIMITS.emotion, `each emotion must be ${FIELD_LIMITS.emotion} chars or less`)).max(FIELD_LIMITS.maxEmotions, `maximum ${FIELD_LIMITS.maxEmotions} emotions allowed`).optional(),
|
|
@@ -20656,21 +20739,6 @@ var aspectSchema = exports_external.object({
|
|
|
20656
20739
|
})).refine((modes) => Object.keys(modes).length <= FIELD_LIMITS.maxModes, {
|
|
20657
20740
|
message: `maximum ${FIELD_LIMITS.maxModes} modes allowed`
|
|
20658
20741
|
}).optional(),
|
|
20659
|
-
resources: exports_external.object({
|
|
20660
|
-
voice: exports_external.object({
|
|
20661
|
-
recommended: exports_external.object({
|
|
20662
|
-
provider: exports_external.string(),
|
|
20663
|
-
voiceId: exports_external.string()
|
|
20664
|
-
}).optional()
|
|
20665
|
-
}).optional(),
|
|
20666
|
-
model: exports_external.object({
|
|
20667
|
-
recommended: exports_external.object({
|
|
20668
|
-
provider: exports_external.string(),
|
|
20669
|
-
modelId: exports_external.string()
|
|
20670
|
-
}).optional()
|
|
20671
|
-
}).optional(),
|
|
20672
|
-
skills: exports_external.array(exports_external.string()).optional()
|
|
20673
|
-
}).optional(),
|
|
20674
20742
|
directives: exports_external.array(exports_external.object({
|
|
20675
20743
|
id: exports_external.string().max(FIELD_LIMITS.directiveId, `directive id must be ${FIELD_LIMITS.directiveId} chars or less`),
|
|
20676
20744
|
rule: exports_external.string().max(FIELD_LIMITS.directiveRule, `directive rule must be ${FIELD_LIMITS.directiveRule} chars or less`),
|
|
@@ -20680,11 +20748,11 @@ var aspectSchema = exports_external.object({
|
|
|
20680
20748
|
id: exports_external.string().max(FIELD_LIMITS.instructionId, `instruction id must be ${FIELD_LIMITS.instructionId} chars or less`),
|
|
20681
20749
|
rule: exports_external.string().max(FIELD_LIMITS.instructionRule, `instruction rule must be ${FIELD_LIMITS.instructionRule} chars or less`)
|
|
20682
20750
|
})).max(FIELD_LIMITS.maxInstructions, `maximum ${FIELD_LIMITS.maxInstructions} instructions allowed`).optional(),
|
|
20683
|
-
prompt: exports_external.string().min(
|
|
20751
|
+
prompt: exports_external.string().min(FIELD_LIMITS.promptMin, `prompt must be at least ${FIELD_LIMITS.promptMin} chars`).max(FIELD_LIMITS.prompt, `prompt must be ${FIELD_LIMITS.prompt} chars or less`)
|
|
20684
20752
|
});
|
|
20685
20753
|
|
|
20686
20754
|
// src/commands/set.ts
|
|
20687
|
-
import { mkdir as mkdir3, readFile as
|
|
20755
|
+
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3, readdir } from "node:fs/promises";
|
|
20688
20756
|
import { join as join4 } from "node:path";
|
|
20689
20757
|
|
|
20690
20758
|
// src/utils/colors.ts
|
|
@@ -20704,21 +20772,57 @@ var c3 = {
|
|
|
20704
20772
|
value: (text) => import_picocolors3.default.white(text),
|
|
20705
20773
|
highlight: (text) => import_picocolors3.default.bold(import_picocolors3.default.cyan(text)),
|
|
20706
20774
|
muted: (text) => import_picocolors3.default.dim(text),
|
|
20707
|
-
cmd: (text) => import_picocolors3.default.cyan(text)
|
|
20775
|
+
cmd: (text) => import_picocolors3.default.cyan(text),
|
|
20776
|
+
file: (text) => import_picocolors3.default.yellow(text)
|
|
20708
20777
|
};
|
|
20709
20778
|
var icons2 = {
|
|
20710
20779
|
success: import_picocolors3.default.green("✓"),
|
|
20711
20780
|
error: import_picocolors3.default.red("✗"),
|
|
20712
20781
|
warn: import_picocolors3.default.yellow("⚠"),
|
|
20713
20782
|
info: import_picocolors3.default.blue("ℹ"),
|
|
20783
|
+
working: import_picocolors3.default.cyan("◌"),
|
|
20714
20784
|
arrow: import_picocolors3.default.dim("→"),
|
|
20715
20785
|
bullet: import_picocolors3.default.dim("•"),
|
|
20716
20786
|
sparkle: "✨",
|
|
20717
20787
|
package: "\uD83D\uDCE6",
|
|
20718
20788
|
search: "\uD83D\uDD0D",
|
|
20719
|
-
|
|
20720
|
-
share: "\uD83D\uDD17"
|
|
20789
|
+
generator: "\uD83E\uDDD9",
|
|
20790
|
+
share: "\uD83D\uDD17",
|
|
20791
|
+
verified: import_picocolors3.default.green("✓"),
|
|
20792
|
+
community: import_picocolors3.default.blue("◆"),
|
|
20793
|
+
github: import_picocolors3.default.dim("◎"),
|
|
20794
|
+
local: import_picocolors3.default.yellow("▸"),
|
|
20795
|
+
anonymous: import_picocolors3.default.dim("#"),
|
|
20796
|
+
modified: import_picocolors3.default.yellow("*")
|
|
20721
20797
|
};
|
|
20798
|
+
function formatAspectLine(info) {
|
|
20799
|
+
const name = c3.aspect(info.name);
|
|
20800
|
+
const version2 = c3.version(`@${info.version}`);
|
|
20801
|
+
const scopes = c3.dim(`[${info.scopes.join(",")}]`);
|
|
20802
|
+
let source;
|
|
20803
|
+
switch (info.trust) {
|
|
20804
|
+
case "verified":
|
|
20805
|
+
source = icons2.verified + c3.dim(info.publisher ?? "");
|
|
20806
|
+
break;
|
|
20807
|
+
case "community":
|
|
20808
|
+
source = icons2.community + c3.dim(info.publisher ?? "");
|
|
20809
|
+
break;
|
|
20810
|
+
case "github":
|
|
20811
|
+
source = icons2.github + c3.dim("gh");
|
|
20812
|
+
break;
|
|
20813
|
+
case "local":
|
|
20814
|
+
source = icons2.local + c3.dim("local");
|
|
20815
|
+
break;
|
|
20816
|
+
default: {
|
|
20817
|
+
const spec = info.specifier ?? "";
|
|
20818
|
+
const hashPart = spec.startsWith("blake3:") ? spec.slice(7, 13) : spec.startsWith("hash:") ? spec.slice(5, 11) : "";
|
|
20819
|
+
source = hashPart ? icons2.anonymous + c3.dim(hashPart) : c3.dim("registry");
|
|
20820
|
+
}
|
|
20821
|
+
}
|
|
20822
|
+
const modified = info.isModified ? ` ${icons2.modified}` : "";
|
|
20823
|
+
const tagline = info.tagline ? c3.muted(` — ${info.tagline}`) : "";
|
|
20824
|
+
return ` ${name}${version2} ${scopes} ${source}${modified}${tagline}`;
|
|
20825
|
+
}
|
|
20722
20826
|
|
|
20723
20827
|
// src/utils/paths.ts
|
|
20724
20828
|
import { homedir } from "node:os";
|
|
@@ -20735,7 +20839,11 @@ async function findProjectRoot() {
|
|
|
20735
20839
|
return cachedProjectRoot || null;
|
|
20736
20840
|
let dir = process.cwd();
|
|
20737
20841
|
const root = "/";
|
|
20842
|
+
const home = homedir();
|
|
20738
20843
|
while (dir !== root) {
|
|
20844
|
+
if (dir === home) {
|
|
20845
|
+
break;
|
|
20846
|
+
}
|
|
20739
20847
|
try {
|
|
20740
20848
|
await stat(join(dir, PROJECT_ASPECTS_DIR_NAME));
|
|
20741
20849
|
cachedProjectRoot = dir;
|
|
@@ -20850,6 +20958,22 @@ async function listAllInstalledAspects(projectRoot) {
|
|
|
20850
20958
|
return globalAspects;
|
|
20851
20959
|
}
|
|
20852
20960
|
}
|
|
20961
|
+
async function findInstalledAspect(name, projectRoot) {
|
|
20962
|
+
const results = [];
|
|
20963
|
+
if (projectRoot) {
|
|
20964
|
+
try {
|
|
20965
|
+
const projectAspect = await getInstalledAspect(name, "project", projectRoot);
|
|
20966
|
+
if (projectAspect) {
|
|
20967
|
+
results.push({ scope: "project", ...projectAspect });
|
|
20968
|
+
}
|
|
20969
|
+
} catch {}
|
|
20970
|
+
}
|
|
20971
|
+
const globalAspect = await getInstalledAspect(name, "global");
|
|
20972
|
+
if (globalAspect) {
|
|
20973
|
+
results.push({ scope: "global", ...globalAspect });
|
|
20974
|
+
}
|
|
20975
|
+
return results;
|
|
20976
|
+
}
|
|
20853
20977
|
async function getRegistryUrl() {
|
|
20854
20978
|
const config2 = await readConfig();
|
|
20855
20979
|
return resolveRegistryUrl(process.env.ASPECTS_REGISTRY_URL, config2.settings.registryUrl);
|
|
@@ -20880,9 +21004,50 @@ async function clearAuth() {
|
|
|
20880
21004
|
delete config2.auth;
|
|
20881
21005
|
await writeConfig(config2);
|
|
20882
21006
|
}
|
|
21007
|
+
async function getDefaultHandle() {
|
|
21008
|
+
const auth = await getAuth();
|
|
21009
|
+
return auth?.defaultHandle ?? null;
|
|
21010
|
+
}
|
|
21011
|
+
async function getHandles() {
|
|
21012
|
+
const auth = await getAuth();
|
|
21013
|
+
return auth?.handles ?? [];
|
|
21014
|
+
}
|
|
21015
|
+
async function setDefaultHandle(handle) {
|
|
21016
|
+
const config2 = await readConfig();
|
|
21017
|
+
if (!config2.auth) {
|
|
21018
|
+
throw new Error("Not logged in");
|
|
21019
|
+
}
|
|
21020
|
+
const hasHandle = config2.auth.handles.some((h3) => h3.name === handle);
|
|
21021
|
+
if (!hasHandle) {
|
|
21022
|
+
throw new Error(`You don't have access to handle @${handle}`);
|
|
21023
|
+
}
|
|
21024
|
+
config2.auth.handles = config2.auth.handles.map((h3) => ({
|
|
21025
|
+
...h3,
|
|
21026
|
+
default: h3.name === handle
|
|
21027
|
+
}));
|
|
21028
|
+
config2.auth.defaultHandle = handle;
|
|
21029
|
+
await writeConfig(config2);
|
|
21030
|
+
}
|
|
21031
|
+
async function updateHandles(handles) {
|
|
21032
|
+
const config2 = await readConfig();
|
|
21033
|
+
if (!config2.auth) {
|
|
21034
|
+
throw new Error("Not logged in");
|
|
21035
|
+
}
|
|
21036
|
+
config2.auth.handles = handles;
|
|
21037
|
+
const defaultStillExists = handles.some((h3) => h3.name === config2.auth.defaultHandle);
|
|
21038
|
+
if (!defaultStillExists) {
|
|
21039
|
+
const newDefault = handles.find((h3) => h3.default) ?? handles[0];
|
|
21040
|
+
config2.auth.defaultHandle = newDefault?.name ?? "";
|
|
21041
|
+
}
|
|
21042
|
+
await writeConfig(config2);
|
|
21043
|
+
}
|
|
21044
|
+
async function hasHandlePermission(handle) {
|
|
21045
|
+
const handles = await getHandles();
|
|
21046
|
+
return handles.some((h3) => h3.name === handle);
|
|
21047
|
+
}
|
|
20883
21048
|
|
|
20884
21049
|
// src/lib/installer.ts
|
|
20885
|
-
import { mkdir as mkdir2, writeFile as writeFile2,
|
|
21050
|
+
import { mkdir as mkdir2, writeFile as writeFile2, stat as stat2, access } from "node:fs/promises";
|
|
20886
21051
|
import { join as join3, dirname } from "node:path";
|
|
20887
21052
|
|
|
20888
21053
|
// node_modules/ofetch/dist/node.mjs
|
|
@@ -21678,7 +21843,7 @@ async function searchAspects(params) {
|
|
|
21678
21843
|
async function publishAspect(aspect) {
|
|
21679
21844
|
return apiFetch("/aspects", {
|
|
21680
21845
|
method: "POST",
|
|
21681
|
-
body:
|
|
21846
|
+
body: aspect,
|
|
21682
21847
|
auth: true
|
|
21683
21848
|
});
|
|
21684
21849
|
}
|
|
@@ -21712,6 +21877,26 @@ async function pollDeviceAuth(deviceCode, codeVerifier) {
|
|
|
21712
21877
|
parseResponse: JSON.parse
|
|
21713
21878
|
});
|
|
21714
21879
|
}
|
|
21880
|
+
async function getAccount() {
|
|
21881
|
+
return apiFetch("/account", { auth: true });
|
|
21882
|
+
}
|
|
21883
|
+
async function claimHandle(name, displayName) {
|
|
21884
|
+
return apiFetch("/handles", {
|
|
21885
|
+
method: "POST",
|
|
21886
|
+
body: { name, display_name: displayName },
|
|
21887
|
+
auth: true
|
|
21888
|
+
});
|
|
21889
|
+
}
|
|
21890
|
+
async function checkHandleAvailability(name) {
|
|
21891
|
+
return apiFetch(`/handles/${encodeURIComponent(name)}/available`);
|
|
21892
|
+
}
|
|
21893
|
+
async function setDefaultHandleApi(handle) {
|
|
21894
|
+
await apiFetch("/account/default-handle", {
|
|
21895
|
+
method: "PUT",
|
|
21896
|
+
body: { handle },
|
|
21897
|
+
auth: true
|
|
21898
|
+
});
|
|
21899
|
+
}
|
|
21715
21900
|
|
|
21716
21901
|
// src/lib/registry.ts
|
|
21717
21902
|
var FALLBACK_REGISTRY_URL = "https://raw.githubusercontent.com/aimorphist/aspects/main/registry/index.json";
|
|
@@ -23258,12 +23443,17 @@ async function installFromRegistryApi(name, version2, options) {
|
|
|
23258
23443
|
await mkdir2(aspectDir, { recursive: true });
|
|
23259
23444
|
const content = JSON.stringify(aspect, null, 2);
|
|
23260
23445
|
await writeFile2(join3(aspectDir, ASPECT_FILENAME), content);
|
|
23261
|
-
const hash2 = versionData.blake3 ||
|
|
23446
|
+
const hash2 = versionData.blake3 || blake3HashAspect(aspect);
|
|
23447
|
+
const publisher = aspect.publisher;
|
|
23448
|
+
const trust = publisher ? "community" : undefined;
|
|
23262
23449
|
await addInstalledAspect(name, {
|
|
23263
23450
|
version: aspect.version,
|
|
23264
|
-
source: "registry",
|
|
23265
23451
|
installedAt: new Date().toISOString(),
|
|
23266
|
-
blake3: hash2
|
|
23452
|
+
blake3: hash2,
|
|
23453
|
+
source: "registry",
|
|
23454
|
+
trust: trust ?? "community",
|
|
23455
|
+
publisher,
|
|
23456
|
+
specifier: options?.specifier ?? (publisher ? `${publisher}/${name}` : name)
|
|
23267
23457
|
}, scope, projectRoot);
|
|
23268
23458
|
return { success: true, aspect, source: "registry" };
|
|
23269
23459
|
}
|
|
@@ -23328,12 +23518,17 @@ async function installFromRegistryLegacy(name, version2, options) {
|
|
|
23328
23518
|
const aspectDir = getAspectPath(name, scope, projectRoot);
|
|
23329
23519
|
await mkdir2(aspectDir, { recursive: true });
|
|
23330
23520
|
await writeFile2(join3(aspectDir, ASPECT_FILENAME), content);
|
|
23331
|
-
const hash2 =
|
|
23521
|
+
const hash2 = blake3HashAspect(aspect);
|
|
23522
|
+
const publisher = aspect.publisher;
|
|
23523
|
+
const trust = registryAspect.metadata.trust ?? "community";
|
|
23332
23524
|
await addInstalledAspect(name, {
|
|
23333
23525
|
version: aspect.version,
|
|
23334
|
-
source: "registry",
|
|
23335
23526
|
installedAt: new Date().toISOString(),
|
|
23336
|
-
blake3: hash2
|
|
23527
|
+
blake3: hash2,
|
|
23528
|
+
source: "registry",
|
|
23529
|
+
trust,
|
|
23530
|
+
publisher,
|
|
23531
|
+
specifier: options?.specifier ?? (publisher ? `${publisher}/${name}` : name)
|
|
23337
23532
|
}, scope, projectRoot);
|
|
23338
23533
|
return { success: true, aspect, source: "registry" };
|
|
23339
23534
|
}
|
|
@@ -23370,11 +23565,12 @@ async function installFromGitHub(owner, repo, ref, options) {
|
|
|
23370
23565
|
parseResult.warnings.forEach((w4) => log.warn(w4));
|
|
23371
23566
|
}
|
|
23372
23567
|
const aspect = parseResult.aspect;
|
|
23568
|
+
const hash2 = blake3HashAspect(aspect);
|
|
23373
23569
|
if (!options?.force) {
|
|
23374
23570
|
const existing = await getInstalledAspect(aspect.name, scope, projectRoot);
|
|
23375
|
-
if (existing && existing.source === "github" && existing.githubRef === targetRef) {
|
|
23571
|
+
if (existing && existing.source === "github" && existing.githubRef === `${owner}/${repo}@${targetRef}`) {
|
|
23376
23572
|
const existingAspect = await loadAspectFromPath(getAspectPath(aspect.name, scope, projectRoot));
|
|
23377
|
-
if (existingAspect && existing.blake3 ===
|
|
23573
|
+
if (existingAspect && existing.blake3 === hash2) {
|
|
23378
23574
|
return { success: true, aspect: existingAspect, source: "github", alreadyInstalled: true };
|
|
23379
23575
|
}
|
|
23380
23576
|
}
|
|
@@ -23383,13 +23579,15 @@ async function installFromGitHub(owner, repo, ref, options) {
|
|
|
23383
23579
|
const aspectDir = getAspectPath(aspect.name, scope, projectRoot);
|
|
23384
23580
|
await mkdir2(aspectDir, { recursive: true });
|
|
23385
23581
|
await writeFile2(join3(aspectDir, ASPECT_FILENAME), content);
|
|
23386
|
-
const
|
|
23582
|
+
const specifier = options?.specifier ?? `github:${owner}/${repo}@${targetRef}`;
|
|
23387
23583
|
await addInstalledAspect(aspect.name, {
|
|
23388
23584
|
version: aspect.version,
|
|
23389
|
-
source: "github",
|
|
23390
23585
|
installedAt: new Date().toISOString(),
|
|
23391
23586
|
blake3: hash2,
|
|
23392
|
-
|
|
23587
|
+
source: "github",
|
|
23588
|
+
trust: "github",
|
|
23589
|
+
githubRef: `${owner}/${repo}@${targetRef}`,
|
|
23590
|
+
specifier
|
|
23393
23591
|
}, scope, projectRoot);
|
|
23394
23592
|
return { success: true, aspect, source: "github" };
|
|
23395
23593
|
}
|
|
@@ -23423,22 +23621,23 @@ async function installFromLocal(path, options) {
|
|
|
23423
23621
|
parseResult.warnings.forEach((w4) => log.warn(w4));
|
|
23424
23622
|
}
|
|
23425
23623
|
const aspect = parseResult.aspect;
|
|
23426
|
-
const
|
|
23427
|
-
const hash2 = blake3Hash(content);
|
|
23624
|
+
const hash2 = blake3HashAspect(aspect);
|
|
23428
23625
|
const scope = options?.scope ?? "global";
|
|
23429
23626
|
const projectRoot = options?.projectRoot;
|
|
23430
23627
|
if (!options?.force) {
|
|
23431
23628
|
const existing = await getInstalledAspect(aspect.name, scope, projectRoot);
|
|
23432
|
-
if (existing && existing.
|
|
23629
|
+
if (existing && existing.localPath === aspectDir && existing.blake3 === hash2) {
|
|
23433
23630
|
return { success: true, aspect, source: "local", alreadyInstalled: true };
|
|
23434
23631
|
}
|
|
23435
23632
|
}
|
|
23436
23633
|
await addInstalledAspect(aspect.name, {
|
|
23437
23634
|
version: aspect.version,
|
|
23438
|
-
source: "local",
|
|
23439
23635
|
installedAt: new Date().toISOString(),
|
|
23440
23636
|
blake3: hash2,
|
|
23441
|
-
|
|
23637
|
+
source: "local",
|
|
23638
|
+
trust: "local",
|
|
23639
|
+
localPath: aspectDir,
|
|
23640
|
+
specifier: options?.specifier ?? path
|
|
23442
23641
|
}, scope, projectRoot);
|
|
23443
23642
|
return { success: true, aspect, source: "local" };
|
|
23444
23643
|
}
|
|
@@ -23473,9 +23672,11 @@ async function installFromHash(hash2, options) {
|
|
|
23473
23672
|
await writeFile2(join3(aspectDir, ASPECT_FILENAME), content);
|
|
23474
23673
|
await addInstalledAspect(aspect.name, {
|
|
23475
23674
|
version: aspect.version,
|
|
23476
|
-
source: "registry",
|
|
23477
23675
|
installedAt: new Date().toISOString(),
|
|
23478
|
-
blake3: versionData.blake3 ||
|
|
23676
|
+
blake3: versionData.blake3 || blake3HashAspect(aspect),
|
|
23677
|
+
source: "registry",
|
|
23678
|
+
trust: "community",
|
|
23679
|
+
specifier: options?.specifier ?? `blake3:${hash2}`
|
|
23479
23680
|
}, scope, projectRoot);
|
|
23480
23681
|
return { success: true, aspect, source: "registry" };
|
|
23481
23682
|
}
|
|
@@ -23490,8 +23691,8 @@ async function loadAspectFromPath(aspectDir) {
|
|
|
23490
23691
|
// src/lib/resolver.ts
|
|
23491
23692
|
import { resolve, isAbsolute } from "node:path";
|
|
23492
23693
|
function parseInstallSpec(spec) {
|
|
23493
|
-
if (spec.startsWith("hash:")) {
|
|
23494
|
-
const hash2 = spec.slice(5);
|
|
23694
|
+
if (spec.startsWith("blake3:") || spec.startsWith("hash:")) {
|
|
23695
|
+
const hash2 = spec.startsWith("blake3:") ? spec.slice(7) : spec.slice(5);
|
|
23495
23696
|
if (hash2.length < 16) {
|
|
23496
23697
|
throw new Error(`Invalid hash spec: ${spec}. Hash must be at least 16 characters.`);
|
|
23497
23698
|
}
|
|
@@ -23515,33 +23716,49 @@ function parseInstallSpec(spec) {
|
|
|
23515
23716
|
const absolutePath = isAbsolute(spec) ? spec : resolve(process.cwd(), spec);
|
|
23516
23717
|
return { type: "local", path: absolutePath };
|
|
23517
23718
|
}
|
|
23719
|
+
if (spec.includes("aspects.sh/")) {
|
|
23720
|
+
const aspectsMatch = spec.match(/aspects\.sh\/aspects\/([a-z0-9-]+)/i);
|
|
23721
|
+
if (aspectsMatch?.[1]) {
|
|
23722
|
+
return { type: "registry", name: aspectsMatch[1] };
|
|
23723
|
+
}
|
|
23724
|
+
const hashMatch = spec.match(/aspects\.sh\/a\/([A-Za-z0-9]{16,44})/);
|
|
23725
|
+
if (hashMatch?.[1]) {
|
|
23726
|
+
return { type: "hash", hash: hashMatch[1] };
|
|
23727
|
+
}
|
|
23728
|
+
throw new Error(`Invalid aspects.sh URL: ${spec}`);
|
|
23729
|
+
}
|
|
23518
23730
|
let name;
|
|
23731
|
+
let publisher;
|
|
23519
23732
|
let version2;
|
|
23520
|
-
|
|
23521
|
-
|
|
23522
|
-
|
|
23523
|
-
|
|
23524
|
-
|
|
23525
|
-
} else {
|
|
23526
|
-
name = spec;
|
|
23527
|
-
}
|
|
23733
|
+
const atIndex = spec.lastIndexOf("@");
|
|
23734
|
+
let nameWithPublisher;
|
|
23735
|
+
if (atIndex > 0) {
|
|
23736
|
+
nameWithPublisher = spec.slice(0, atIndex);
|
|
23737
|
+
version2 = spec.slice(atIndex + 1);
|
|
23528
23738
|
} else {
|
|
23529
|
-
|
|
23530
|
-
|
|
23531
|
-
|
|
23532
|
-
|
|
23533
|
-
|
|
23534
|
-
|
|
23739
|
+
nameWithPublisher = spec;
|
|
23740
|
+
}
|
|
23741
|
+
const slashIndex = nameWithPublisher.indexOf("/");
|
|
23742
|
+
if (slashIndex > 0) {
|
|
23743
|
+
publisher = nameWithPublisher.slice(0, slashIndex);
|
|
23744
|
+
if (publisher.startsWith("@")) {
|
|
23745
|
+
publisher = publisher.slice(1);
|
|
23535
23746
|
}
|
|
23747
|
+
name = nameWithPublisher.slice(slashIndex + 1);
|
|
23748
|
+
if (!name) {
|
|
23749
|
+
throw new Error(`Invalid spec: ${spec}. Expected publisher/name format.`);
|
|
23750
|
+
}
|
|
23751
|
+
} else {
|
|
23752
|
+
name = nameWithPublisher;
|
|
23536
23753
|
}
|
|
23537
|
-
return { type: "registry", name, version: version2 };
|
|
23754
|
+
return { type: "registry", name, publisher, version: version2 };
|
|
23538
23755
|
}
|
|
23539
23756
|
|
|
23540
23757
|
// src/commands/set.ts
|
|
23541
23758
|
async function loadSet(name) {
|
|
23542
23759
|
try {
|
|
23543
23760
|
const setPath = join4(getSetsDir(), name, "set.json");
|
|
23544
|
-
const content = await
|
|
23761
|
+
const content = await readFile3(setPath, "utf-8");
|
|
23545
23762
|
return JSON.parse(content);
|
|
23546
23763
|
} catch {
|
|
23547
23764
|
return null;
|
|
@@ -23586,7 +23803,7 @@ var createCommand = defineCommand({
|
|
|
23586
23803
|
},
|
|
23587
23804
|
aspects: {
|
|
23588
23805
|
type: "positional",
|
|
23589
|
-
description: "Aspects to include (optional, launches
|
|
23806
|
+
description: "Aspects to include (optional, launches generator if omitted)",
|
|
23590
23807
|
required: false
|
|
23591
23808
|
}
|
|
23592
23809
|
},
|
|
@@ -23991,7 +24208,28 @@ var CATEGORY_OPTIONS = [
|
|
|
23991
24208
|
var create_default = defineCommand({
|
|
23992
24209
|
meta: {
|
|
23993
24210
|
name: "create",
|
|
23994
|
-
description:
|
|
24211
|
+
description: `Create a new aspect interactively.
|
|
24212
|
+
|
|
24213
|
+
The generator guides you through:
|
|
24214
|
+
1. Name & identity (slug, display name, tagline)
|
|
24215
|
+
2. Category selection (assistant, roleplay, creative, etc.)
|
|
24216
|
+
3. Tags for discovery
|
|
24217
|
+
4. Voice hints (speed, emotions)
|
|
24218
|
+
5. Directives & Instructions
|
|
24219
|
+
|
|
24220
|
+
Directives vs Instructions:
|
|
24221
|
+
Directives Strict MUST-follow rules with priority (high/medium/low)
|
|
24222
|
+
Emphasized across all LLM models via XML, bold, repetition
|
|
24223
|
+
Instructions Softer guidance and preferences, not strictly enforced
|
|
24224
|
+
|
|
24225
|
+
Examples:
|
|
24226
|
+
aspects create Create in current directory
|
|
24227
|
+
aspects create my-aspect Create in ./my-aspect/
|
|
24228
|
+
aspects create ~/aspects/new Create at specific path
|
|
24229
|
+
|
|
24230
|
+
If run inside the aspects registry repo, automatically offers to:
|
|
24231
|
+
- Add to registry/index.json
|
|
24232
|
+
- Commit and push changes`
|
|
23995
24233
|
},
|
|
23996
24234
|
args: {
|
|
23997
24235
|
path: {
|
|
@@ -24041,7 +24279,7 @@ var create_default = defineCommand({
|
|
|
24041
24279
|
while (!aspectName) {
|
|
24042
24280
|
const nameInput = await he2({
|
|
24043
24281
|
message: "Aspect name (slug)",
|
|
24044
|
-
placeholder: "my-
|
|
24282
|
+
placeholder: "my-aspect",
|
|
24045
24283
|
validate: (value) => {
|
|
24046
24284
|
if (!value)
|
|
24047
24285
|
return "Name is required";
|
|
@@ -24079,7 +24317,7 @@ var create_default = defineCommand({
|
|
|
24079
24317
|
const answers = await Ce({
|
|
24080
24318
|
displayName: () => he2({
|
|
24081
24319
|
message: "Display name",
|
|
24082
|
-
placeholder: "My
|
|
24320
|
+
placeholder: "My Aspect",
|
|
24083
24321
|
validate: (value) => {
|
|
24084
24322
|
if (!value)
|
|
24085
24323
|
return "Display name is required";
|
|
@@ -24087,7 +24325,7 @@ var create_default = defineCommand({
|
|
|
24087
24325
|
}),
|
|
24088
24326
|
tagline: () => he2({
|
|
24089
24327
|
message: "Tagline (one-liner description)",
|
|
24090
|
-
placeholder: "A
|
|
24328
|
+
placeholder: "A helpful assistant with a unique personality",
|
|
24091
24329
|
validate: (value) => {
|
|
24092
24330
|
if (!value)
|
|
24093
24331
|
return "Tagline is required";
|
|
@@ -24126,17 +24364,17 @@ var create_default = defineCommand({
|
|
|
24126
24364
|
message: "Voice speed",
|
|
24127
24365
|
options: [
|
|
24128
24366
|
{ value: "normal", label: "Normal" },
|
|
24129
|
-
{ value: "slow", label: "Slow
|
|
24130
|
-
{ value: "fast", label: "Fast
|
|
24367
|
+
{ value: "slow", label: "Slow - deliberate, thoughtful" },
|
|
24368
|
+
{ value: "fast", label: "Fast - energetic, excited" }
|
|
24131
24369
|
],
|
|
24132
24370
|
initialValue: "normal"
|
|
24133
24371
|
}),
|
|
24134
24372
|
promptStyle: () => ve2({
|
|
24135
24373
|
message: "Prompt template",
|
|
24136
24374
|
options: [
|
|
24137
|
-
{ value: "character", label: "Character
|
|
24138
|
-
{ value: "assistant", label: "Assistant
|
|
24139
|
-
{ value: "blank", label: "Blank
|
|
24375
|
+
{ value: "character", label: "Character - roleplay a persona" },
|
|
24376
|
+
{ value: "assistant", label: "Assistant - helpful AI style" },
|
|
24377
|
+
{ value: "blank", label: "Blank - start from scratch" }
|
|
24140
24378
|
],
|
|
24141
24379
|
initialValue: "character"
|
|
24142
24380
|
})
|
|
@@ -24151,11 +24389,11 @@ var create_default = defineCommand({
|
|
|
24151
24389
|
M2.message(`
|
|
24152
24390
|
\uD83D\uDCCB Directives & Instructions
|
|
24153
24391
|
|
|
24154
|
-
Directives are MUST-follow rules
|
|
24392
|
+
Directives are MUST-follow rules - they get special emphasis
|
|
24155
24393
|
across all LLM models (bold, XML tags, repetition).
|
|
24156
24394
|
Example: "Never break character under any circumstances"
|
|
24157
24395
|
|
|
24158
|
-
Instructions are general guidance
|
|
24396
|
+
Instructions are general guidance - softer preferences for
|
|
24159
24397
|
how the AI should behave.
|
|
24160
24398
|
Example: "Prefer shorter responses when possible"
|
|
24161
24399
|
|
|
@@ -24188,7 +24426,7 @@ Keep it light! A few well-crafted rules beat many vague ones.
|
|
|
24188
24426
|
},
|
|
24189
24427
|
{
|
|
24190
24428
|
value: "done",
|
|
24191
|
-
label: hasAny ? "Done
|
|
24429
|
+
label: hasAny ? "Done - finish creating aspect" : "Skip - finish without adding any"
|
|
24192
24430
|
}
|
|
24193
24431
|
]
|
|
24194
24432
|
});
|
|
@@ -24267,7 +24505,7 @@ Keep it light! A few well-crafted rules beat many vague ones.
|
|
|
24267
24505
|
const aspect = {
|
|
24268
24506
|
schemaVersion: 1,
|
|
24269
24507
|
name: aspectName,
|
|
24270
|
-
publisher:
|
|
24508
|
+
publisher: "anon-user",
|
|
24271
24509
|
version: "1.0.0",
|
|
24272
24510
|
displayName: answers.displayName,
|
|
24273
24511
|
tagline: answers.tagline,
|
|
@@ -24346,11 +24584,11 @@ Keep it light! A few well-crafted rules beat many vague ones.
|
|
|
24346
24584
|
M2.info(`
|
|
24347
24585
|
Next: Open a PR at https://github.com/${GITHUB_REPO}/compare`);
|
|
24348
24586
|
} catch {
|
|
24349
|
-
M2.warn("Push failed
|
|
24587
|
+
M2.warn("Push failed - you may need to set up your remote");
|
|
24350
24588
|
}
|
|
24351
24589
|
}
|
|
24352
24590
|
} catch {
|
|
24353
|
-
M2.warn("Git commit failed
|
|
24591
|
+
M2.warn("Git commit failed - you may need to commit manually");
|
|
24354
24592
|
}
|
|
24355
24593
|
}
|
|
24356
24594
|
} else {
|
|
@@ -24387,7 +24625,7 @@ To submit to the registry:`);
|
|
|
24387
24625
|
});
|
|
24388
24626
|
async function addToRegistryIndex(repoRoot, name, displayName, tagline, category, publisher) {
|
|
24389
24627
|
const indexPath = join5(repoRoot, INDEX_PATH);
|
|
24390
|
-
const indexContent = await
|
|
24628
|
+
const indexContent = await readFile4(indexPath, "utf-8");
|
|
24391
24629
|
const index = JSON.parse(indexContent);
|
|
24392
24630
|
const now = new Date().toISOString();
|
|
24393
24631
|
index.aspects[name] = {
|
|
@@ -24456,16 +24694,78 @@ ${tagline}
|
|
|
24456
24694
|
}
|
|
24457
24695
|
}
|
|
24458
24696
|
|
|
24697
|
+
// src/commands/init.ts
|
|
24698
|
+
import { existsSync } from "node:fs";
|
|
24699
|
+
import { mkdir as mkdir5 } from "node:fs/promises";
|
|
24700
|
+
import { join as join6 } from "node:path";
|
|
24701
|
+
var init_default = defineCommand({
|
|
24702
|
+
meta: {
|
|
24703
|
+
name: "init",
|
|
24704
|
+
description: "Initialize a project for local aspect installation"
|
|
24705
|
+
},
|
|
24706
|
+
args: {
|
|
24707
|
+
force: {
|
|
24708
|
+
type: "boolean",
|
|
24709
|
+
alias: "f",
|
|
24710
|
+
description: "Overwrite existing .aspects directory"
|
|
24711
|
+
}
|
|
24712
|
+
},
|
|
24713
|
+
async run({ args }) {
|
|
24714
|
+
const cwd = process.cwd();
|
|
24715
|
+
const aspectsDir = join6(cwd, PROJECT_ASPECTS_DIR_NAME);
|
|
24716
|
+
if (existsSync(aspectsDir) && !args.force) {
|
|
24717
|
+
M2.warn(`${c3.file(".aspects/")} already exists in this directory`);
|
|
24718
|
+
const overwrite = await ye2({
|
|
24719
|
+
message: "Reinitialize?"
|
|
24720
|
+
});
|
|
24721
|
+
if (pD2(overwrite) || !overwrite) {
|
|
24722
|
+
xe("Cancelled");
|
|
24723
|
+
process.exit(0);
|
|
24724
|
+
}
|
|
24725
|
+
}
|
|
24726
|
+
await mkdir5(aspectsDir, { recursive: true });
|
|
24727
|
+
console.log();
|
|
24728
|
+
console.log(`${icons2.success} Initialized ${c3.file(".aspects/")} in ${c3.muted(cwd)}`);
|
|
24729
|
+
console.log();
|
|
24730
|
+
console.log(` Now you can run ${c3.cmd("aspects add <name>")} to install locally.`);
|
|
24731
|
+
console.log();
|
|
24732
|
+
}
|
|
24733
|
+
});
|
|
24734
|
+
async function initProjectAspects() {
|
|
24735
|
+
const cwd = process.cwd();
|
|
24736
|
+
const aspectsDir = join6(cwd, PROJECT_ASPECTS_DIR_NAME);
|
|
24737
|
+
await mkdir5(aspectsDir, { recursive: true });
|
|
24738
|
+
return cwd;
|
|
24739
|
+
}
|
|
24740
|
+
|
|
24459
24741
|
// src/commands/add.ts
|
|
24460
24742
|
var add_default = defineCommand({
|
|
24461
24743
|
meta: {
|
|
24462
24744
|
name: "add",
|
|
24463
|
-
description:
|
|
24745
|
+
description: `Install aspect(s) to your local library.
|
|
24746
|
+
|
|
24747
|
+
Sources:
|
|
24748
|
+
aspects add alaric From registry (aspects.sh)
|
|
24749
|
+
aspects add alaric@1.2.0 Specific version
|
|
24750
|
+
aspects add blake3:<hash> By content hash (from 'aspects share')
|
|
24751
|
+
aspects add github:user/repo From GitHub repository
|
|
24752
|
+
aspects add ./my-aspect From local path
|
|
24753
|
+
|
|
24754
|
+
Scope:
|
|
24755
|
+
By default, installs to ./.aspects/ if in a project, else ~/.aspects/
|
|
24756
|
+
Use -g/--global to always install to ~/.aspects/
|
|
24757
|
+
Use -p/--project to install locally (will prompt to init if needed)
|
|
24758
|
+
|
|
24759
|
+
Examples:
|
|
24760
|
+
aspects add alaric meditation-guide Install multiple aspects
|
|
24761
|
+
aspects add -g alaric Install globally
|
|
24762
|
+
aspects add -p alaric Install to project (init if needed)
|
|
24763
|
+
aspects add --force alaric Overwrite existing`
|
|
24464
24764
|
},
|
|
24465
24765
|
args: {
|
|
24466
24766
|
specs: {
|
|
24467
24767
|
type: "positional",
|
|
24468
|
-
description: "Aspect
|
|
24768
|
+
description: "Aspect spec(s): name, name@version, blake3:<hash>, github:user/repo, or ./path",
|
|
24469
24769
|
required: true
|
|
24470
24770
|
},
|
|
24471
24771
|
force: {
|
|
@@ -24476,6 +24776,11 @@ var add_default = defineCommand({
|
|
|
24476
24776
|
type: "boolean",
|
|
24477
24777
|
alias: "g",
|
|
24478
24778
|
description: "Install to global scope (~/.aspects) instead of project"
|
|
24779
|
+
},
|
|
24780
|
+
project: {
|
|
24781
|
+
type: "boolean",
|
|
24782
|
+
alias: "p",
|
|
24783
|
+
description: "Install to project scope (init if needed)"
|
|
24479
24784
|
}
|
|
24480
24785
|
},
|
|
24481
24786
|
async run({ args }) {
|
|
@@ -24484,6 +24789,24 @@ var add_default = defineCommand({
|
|
|
24484
24789
|
let projectRoot;
|
|
24485
24790
|
if (args.global) {
|
|
24486
24791
|
scope = "global";
|
|
24792
|
+
} else if (args.project) {
|
|
24793
|
+
projectRoot = await findProjectRoot() || undefined;
|
|
24794
|
+
if (!projectRoot) {
|
|
24795
|
+
console.log();
|
|
24796
|
+
const init2 = await ye2({
|
|
24797
|
+
message: `No ${c3.file(".aspects/")} found. Initialize project here?`
|
|
24798
|
+
});
|
|
24799
|
+
if (pD2(init2) || !init2) {
|
|
24800
|
+
console.log(c3.muted(" Falling back to global scope"));
|
|
24801
|
+
scope = "global";
|
|
24802
|
+
} else {
|
|
24803
|
+
projectRoot = await initProjectAspects();
|
|
24804
|
+
console.log(`${icons2.success} Initialized ${c3.file(".aspects/")}`);
|
|
24805
|
+
scope = "project";
|
|
24806
|
+
}
|
|
24807
|
+
} else {
|
|
24808
|
+
scope = "project";
|
|
24809
|
+
}
|
|
24487
24810
|
} else {
|
|
24488
24811
|
projectRoot = await findProjectRoot() || undefined;
|
|
24489
24812
|
scope = projectRoot ? "project" : "global";
|
|
@@ -24500,7 +24823,12 @@ var add_default = defineCommand({
|
|
|
24500
24823
|
results.push({ spec: specStr, success: false, error: err.message });
|
|
24501
24824
|
continue;
|
|
24502
24825
|
}
|
|
24503
|
-
const result = await installAspect(spec, {
|
|
24826
|
+
const result = await installAspect(spec, {
|
|
24827
|
+
force: !!args.force,
|
|
24828
|
+
scope,
|
|
24829
|
+
projectRoot,
|
|
24830
|
+
specifier: specStr
|
|
24831
|
+
});
|
|
24504
24832
|
if (!result.success) {
|
|
24505
24833
|
results.push({ spec: specStr, success: false, error: result.error });
|
|
24506
24834
|
continue;
|
|
@@ -24549,18 +24877,33 @@ var add_default = defineCommand({
|
|
|
24549
24877
|
});
|
|
24550
24878
|
|
|
24551
24879
|
// src/lib/aspect-loader.ts
|
|
24552
|
-
import { join as
|
|
24880
|
+
import { join as join7 } from "node:path";
|
|
24553
24881
|
var ASPECT_FILENAME2 = "aspect.json";
|
|
24554
24882
|
var LEGACY_FILENAME2 = "aspect.yaml";
|
|
24883
|
+
async function findAndLoadAspect(name) {
|
|
24884
|
+
const projectRoot = await findProjectRoot() || undefined;
|
|
24885
|
+
const installed = await findInstalledAspect(name, projectRoot);
|
|
24886
|
+
if (installed.length === 0)
|
|
24887
|
+
return null;
|
|
24888
|
+
const match = installed.find((i2) => i2.scope === "project") || installed[0];
|
|
24889
|
+
const aspect = await loadInstalledAspect(name, match.scope, projectRoot);
|
|
24890
|
+
if (!aspect)
|
|
24891
|
+
return null;
|
|
24892
|
+
return {
|
|
24893
|
+
aspect,
|
|
24894
|
+
scope: match.scope,
|
|
24895
|
+
meta: match
|
|
24896
|
+
};
|
|
24897
|
+
}
|
|
24555
24898
|
async function loadInstalledAspect(name, scope = "global", projectRoot) {
|
|
24556
24899
|
const installed = await getInstalledAspect(name, scope, projectRoot);
|
|
24557
24900
|
if (!installed)
|
|
24558
24901
|
return null;
|
|
24559
|
-
const aspectDir = installed.
|
|
24560
|
-
const jsonResult = await parseAspectFile(
|
|
24902
|
+
const aspectDir = installed.localPath ?? getAspectPath(name, scope, projectRoot);
|
|
24903
|
+
const jsonResult = await parseAspectFile(join7(aspectDir, ASPECT_FILENAME2));
|
|
24561
24904
|
if (jsonResult.success)
|
|
24562
24905
|
return jsonResult.aspect;
|
|
24563
|
-
const yamlResult = await parseAspectFile(
|
|
24906
|
+
const yamlResult = await parseAspectFile(join7(aspectDir, LEGACY_FILENAME2));
|
|
24564
24907
|
return yamlResult.success ? yamlResult.aspect : null;
|
|
24565
24908
|
}
|
|
24566
24909
|
|
|
@@ -24595,21 +24938,44 @@ var list_default = defineCommand({
|
|
|
24595
24938
|
if (installed.length === 0) {
|
|
24596
24939
|
console.log();
|
|
24597
24940
|
console.log(c3.muted(" No aspects installed."));
|
|
24598
|
-
console.log(c3.muted(` Run ${c3.highlight("aspects
|
|
24941
|
+
console.log(c3.muted(` Run ${c3.highlight("aspects add <name>")} to get started.`));
|
|
24599
24942
|
console.log();
|
|
24600
24943
|
return;
|
|
24601
24944
|
}
|
|
24945
|
+
const grouped = new Map;
|
|
24946
|
+
for (const item of installed) {
|
|
24947
|
+
const existing = grouped.get(item.blake3);
|
|
24948
|
+
if (existing) {
|
|
24949
|
+
if (!existing.scopes.includes(item.scope)) {
|
|
24950
|
+
existing.scopes.push(item.scope);
|
|
24951
|
+
}
|
|
24952
|
+
} else {
|
|
24953
|
+
const aspect = await loadInstalledAspect(item.name, item.scope, projectRoot);
|
|
24954
|
+
let isModified = false;
|
|
24955
|
+
if (aspect) {
|
|
24956
|
+
const currentHash = blake3HashAspect(aspect);
|
|
24957
|
+
isModified = currentHash !== item.blake3;
|
|
24958
|
+
}
|
|
24959
|
+
const trust = item.trust ?? (item.source === "github" ? "github" : item.source === "local" ? "local" : "community");
|
|
24960
|
+
grouped.set(item.blake3, {
|
|
24961
|
+
name: item.name,
|
|
24962
|
+
version: item.version,
|
|
24963
|
+
blake3: item.blake3,
|
|
24964
|
+
scopes: [item.scope],
|
|
24965
|
+
trust,
|
|
24966
|
+
publisher: item.publisher,
|
|
24967
|
+
specifier: item.specifier,
|
|
24968
|
+
tagline: aspect?.tagline,
|
|
24969
|
+
isModified
|
|
24970
|
+
});
|
|
24971
|
+
}
|
|
24972
|
+
}
|
|
24602
24973
|
console.log();
|
|
24603
24974
|
console.log(c3.bold(`${icons2.package} Installed aspects`));
|
|
24604
24975
|
console.log();
|
|
24605
|
-
for (const item of
|
|
24606
|
-
|
|
24607
|
-
|
|
24608
|
-
const sourceLabel = item.source === "local" ? c3.dim(" (local)") : item.source === "github" ? c3.dim(" (github)") : "";
|
|
24609
|
-
const name = c3.aspect(item.name);
|
|
24610
|
-
const version2 = c3.version(`@${item.version}`);
|
|
24611
|
-
const tagline = aspect?.tagline ? c3.muted(` — ${aspect.tagline}`) : "";
|
|
24612
|
-
console.log(` ${name}${version2}${scopeLabel}${sourceLabel}${tagline}`);
|
|
24976
|
+
for (const item of grouped.values()) {
|
|
24977
|
+
item.scopes.sort((a2, b4) => a2 === "project" ? -1 : 1);
|
|
24978
|
+
console.log(formatAspectLine(item));
|
|
24613
24979
|
}
|
|
24614
24980
|
console.log();
|
|
24615
24981
|
}
|
|
@@ -24619,7 +24985,19 @@ var list_default = defineCommand({
|
|
|
24619
24985
|
var search_default = defineCommand({
|
|
24620
24986
|
meta: {
|
|
24621
24987
|
name: "search",
|
|
24622
|
-
description:
|
|
24988
|
+
description: `Search the aspect registry.
|
|
24989
|
+
|
|
24990
|
+
Searches aspect names, display names, taglines, and tags.
|
|
24991
|
+
|
|
24992
|
+
Examples:
|
|
24993
|
+
aspects search wizard Find aspects matching "wizard"
|
|
24994
|
+
aspects search List all aspects
|
|
24995
|
+
aspects search --category roleplay Filter by category
|
|
24996
|
+
aspects search --trust verified Only verified aspects
|
|
24997
|
+
|
|
24998
|
+
Categories: assistant, roleplay, creative, productivity, education, gaming, spiritual, pundit
|
|
24999
|
+
|
|
25000
|
+
For advanced filtering, use 'aspects find' with boolean operators.`
|
|
24623
25001
|
},
|
|
24624
25002
|
args: {
|
|
24625
25003
|
query: {
|
|
@@ -24952,7 +25330,7 @@ var find_default = defineCommand({
|
|
|
24952
25330
|
if (results.some((r6) => r6.aspect.name === name && r6.source === "registry")) {
|
|
24953
25331
|
continue;
|
|
24954
25332
|
}
|
|
24955
|
-
const aspectPath = aspectInfo.
|
|
25333
|
+
const aspectPath = aspectInfo.localPath || getAspectPath(name);
|
|
24956
25334
|
const parseResult = await parseAspectFile(`${aspectPath}/aspect.json`);
|
|
24957
25335
|
if (!parseResult.success)
|
|
24958
25336
|
continue;
|
|
@@ -25027,28 +25405,24 @@ var info_default = defineCommand({
|
|
|
25027
25405
|
}
|
|
25028
25406
|
},
|
|
25029
25407
|
async run({ args }) {
|
|
25030
|
-
const
|
|
25031
|
-
if (
|
|
25032
|
-
const aspect =
|
|
25033
|
-
if (!aspect) {
|
|
25034
|
-
log.error(`Failed to load aspect "${args.name}" — aspect.json may be corrupted`);
|
|
25035
|
-
process.exit(1);
|
|
25036
|
-
}
|
|
25408
|
+
const found = await findAndLoadAspect(args.name);
|
|
25409
|
+
if (found) {
|
|
25410
|
+
const { aspect, scope, meta: installMeta } = found;
|
|
25037
25411
|
console.log();
|
|
25038
|
-
console.log(`${c3.bold(aspect.displayName)} ${c3.muted("(")}${c3.aspect(aspect.name)}${c3.version(`@${aspect.version}`)}${c3.muted(")")}`);
|
|
25412
|
+
console.log(`${c3.bold(aspect.displayName)} ${c3.muted("(")}${c3.aspect(aspect.name)}${c3.version(`@${aspect.version}`)}${c3.muted(")")} ${c3.dim(`[${scope}]`)}`);
|
|
25039
25413
|
console.log();
|
|
25040
25414
|
console.log(` ${c3.italic(aspect.tagline)}`);
|
|
25041
25415
|
console.log();
|
|
25042
|
-
const
|
|
25416
|
+
const displayMeta = [];
|
|
25043
25417
|
if (aspect.publisher)
|
|
25044
|
-
|
|
25418
|
+
displayMeta.push(["Publisher", aspect.publisher]);
|
|
25045
25419
|
if (aspect.author)
|
|
25046
|
-
|
|
25420
|
+
displayMeta.push(["Author", aspect.author]);
|
|
25047
25421
|
if (aspect.license)
|
|
25048
|
-
|
|
25049
|
-
|
|
25050
|
-
if (
|
|
25051
|
-
for (const [label, value] of
|
|
25422
|
+
displayMeta.push(["License", aspect.license]);
|
|
25423
|
+
displayMeta.push(["Source", installMeta.source]);
|
|
25424
|
+
if (displayMeta.length > 0) {
|
|
25425
|
+
for (const [label, value] of displayMeta) {
|
|
25052
25426
|
console.log(` ${c3.label(label.padEnd(10))} ${c3.value(value)}`);
|
|
25053
25427
|
}
|
|
25054
25428
|
}
|
|
@@ -25162,31 +25536,79 @@ var remove_default = defineCommand({
|
|
|
25162
25536
|
type: "boolean",
|
|
25163
25537
|
alias: "g",
|
|
25164
25538
|
description: "Remove from global scope (~/.aspects) instead of project"
|
|
25539
|
+
},
|
|
25540
|
+
project: {
|
|
25541
|
+
type: "boolean",
|
|
25542
|
+
alias: "p",
|
|
25543
|
+
description: "Remove from project scope only"
|
|
25544
|
+
},
|
|
25545
|
+
force: {
|
|
25546
|
+
type: "boolean",
|
|
25547
|
+
alias: "f",
|
|
25548
|
+
description: "Skip confirmation prompt"
|
|
25165
25549
|
}
|
|
25166
25550
|
},
|
|
25167
25551
|
async run({ args }) {
|
|
25168
|
-
|
|
25169
|
-
|
|
25170
|
-
if (
|
|
25171
|
-
scope = "global";
|
|
25172
|
-
} else {
|
|
25173
|
-
projectRoot = await findProjectRoot() || undefined;
|
|
25174
|
-
scope = projectRoot ? "project" : "global";
|
|
25175
|
-
}
|
|
25176
|
-
const installed = await getInstalledAspect(args.name, scope, projectRoot);
|
|
25177
|
-
if (!installed) {
|
|
25552
|
+
const projectRoot = await findProjectRoot() || undefined;
|
|
25553
|
+
const found = await findInstalledAspect(args.name, projectRoot);
|
|
25554
|
+
if (found.length === 0) {
|
|
25178
25555
|
log.error(`Aspect "${args.name}" is not installed`);
|
|
25179
25556
|
process.exit(1);
|
|
25180
25557
|
}
|
|
25181
|
-
|
|
25182
|
-
if (
|
|
25183
|
-
|
|
25184
|
-
|
|
25185
|
-
|
|
25186
|
-
|
|
25558
|
+
let toRemove = found;
|
|
25559
|
+
if (args.global && !args.project) {
|
|
25560
|
+
toRemove = found.filter((f4) => f4.scope === "global");
|
|
25561
|
+
if (toRemove.length === 0) {
|
|
25562
|
+
log.error(`Aspect "${args.name}" is not installed globally`);
|
|
25563
|
+
process.exit(1);
|
|
25564
|
+
}
|
|
25565
|
+
} else if (args.project && !args.global) {
|
|
25566
|
+
toRemove = found.filter((f4) => f4.scope === "project");
|
|
25567
|
+
if (toRemove.length === 0) {
|
|
25568
|
+
log.error(`Aspect "${args.name}" is not installed in project scope`);
|
|
25569
|
+
process.exit(1);
|
|
25570
|
+
}
|
|
25571
|
+
}
|
|
25572
|
+
if (toRemove.length > 1 && !args.global && !args.project) {
|
|
25573
|
+
const choice = await ve2({
|
|
25574
|
+
message: `"${args.name}" is installed in multiple scopes. Which to remove?`,
|
|
25575
|
+
options: [
|
|
25576
|
+
...toRemove.map((f4) => ({
|
|
25577
|
+
value: f4.scope,
|
|
25578
|
+
label: `${f4.scope} (${f4.version})`
|
|
25579
|
+
})),
|
|
25580
|
+
{ value: "both", label: "Both" }
|
|
25581
|
+
]
|
|
25582
|
+
});
|
|
25583
|
+
if (pD2(choice)) {
|
|
25584
|
+
xe("Cancelled");
|
|
25585
|
+
process.exit(0);
|
|
25586
|
+
}
|
|
25587
|
+
if (choice === "both") {} else {
|
|
25588
|
+
toRemove = toRemove.filter((f4) => f4.scope === choice);
|
|
25589
|
+
}
|
|
25590
|
+
}
|
|
25591
|
+
if (!args.force && toRemove.length > 0) {
|
|
25592
|
+
const first = toRemove[0];
|
|
25593
|
+
const scopeDesc = toRemove.length === 1 ? `from ${c3.dim(first.scope)} scope` : `from ${toRemove.map((f4) => c3.dim(f4.scope)).join(" and ")} scopes`;
|
|
25594
|
+
const confirmed = await ye2({
|
|
25595
|
+
message: `Remove ${c3.aspect(args.name)}${c3.version(`@${first.version}`)} ${scopeDesc}?`
|
|
25596
|
+
});
|
|
25597
|
+
if (pD2(confirmed) || !confirmed) {
|
|
25598
|
+
xe("Cancelled");
|
|
25599
|
+
process.exit(0);
|
|
25600
|
+
}
|
|
25601
|
+
}
|
|
25602
|
+
for (const install of toRemove) {
|
|
25603
|
+
await removeInstalledAspect(args.name, install.scope, projectRoot);
|
|
25604
|
+
if (install.source === "registry" || install.source === "github") {
|
|
25605
|
+
const aspectDir = getAspectPath(args.name, install.scope, projectRoot);
|
|
25606
|
+
try {
|
|
25607
|
+
await rm(aspectDir, { recursive: true });
|
|
25608
|
+
} catch {}
|
|
25609
|
+
}
|
|
25610
|
+
console.log(`${icons2.success} Removed ${c3.aspect(args.name)}${c3.version(`@${install.version}`)} ${c3.dim(`[${install.scope}]`)}`);
|
|
25187
25611
|
}
|
|
25188
|
-
console.log();
|
|
25189
|
-
console.log(`${icons2.success} Removed ${c3.aspect(args.name)}${c3.version(`@${installed.version}`)}`);
|
|
25190
25612
|
console.log();
|
|
25191
25613
|
}
|
|
25192
25614
|
});
|
|
@@ -25234,22 +25656,22 @@ var update_default = defineCommand({
|
|
|
25234
25656
|
let updated = 0;
|
|
25235
25657
|
for (const aspect of toCheck) {
|
|
25236
25658
|
if (aspect.source === "local") {
|
|
25237
|
-
console.log(` ${c3.aspect(aspect.name)} ${c3.muted("
|
|
25659
|
+
console.log(` ${c3.aspect(aspect.name)} ${c3.muted("- local install, skipping")}`);
|
|
25238
25660
|
continue;
|
|
25239
25661
|
}
|
|
25240
25662
|
if (aspect.source === "github") {
|
|
25241
|
-
console.log(` ${c3.aspect(aspect.name)} ${c3.muted("
|
|
25663
|
+
console.log(` ${c3.aspect(aspect.name)} ${c3.muted("- github install, use")} ${c3.highlight("aspects install github:...")} ${c3.muted("to update")}`);
|
|
25242
25664
|
continue;
|
|
25243
25665
|
}
|
|
25244
25666
|
let registryInfo;
|
|
25245
25667
|
try {
|
|
25246
25668
|
registryInfo = await getRegistryAspect(aspect.name);
|
|
25247
25669
|
} catch {
|
|
25248
|
-
console.log(` ${c3.aspect(aspect.name)} ${c3.error("
|
|
25670
|
+
console.log(` ${c3.aspect(aspect.name)} ${c3.error("- failed to check registry")}`);
|
|
25249
25671
|
continue;
|
|
25250
25672
|
}
|
|
25251
25673
|
if (!registryInfo) {
|
|
25252
|
-
console.log(` ${c3.aspect(aspect.name)} ${c3.warn("
|
|
25674
|
+
console.log(` ${c3.aspect(aspect.name)} ${c3.warn("- not found in registry")}`);
|
|
25253
25675
|
continue;
|
|
25254
25676
|
}
|
|
25255
25677
|
const currentVersion = aspect.version;
|
|
@@ -25296,12 +25718,30 @@ var update_default = defineCommand({
|
|
|
25296
25718
|
});
|
|
25297
25719
|
|
|
25298
25720
|
// src/commands/validate.ts
|
|
25299
|
-
import { readFile as
|
|
25300
|
-
import { join as
|
|
25721
|
+
import { readFile as readFile5, stat as stat4 } from "node:fs/promises";
|
|
25722
|
+
import { join as join8 } from "node:path";
|
|
25301
25723
|
var validate_default = defineCommand({
|
|
25302
25724
|
meta: {
|
|
25303
25725
|
name: "validate",
|
|
25304
|
-
description:
|
|
25726
|
+
description: `Validate an aspect.json file against the schema.
|
|
25727
|
+
|
|
25728
|
+
Checks:
|
|
25729
|
+
- Required fields (name, displayName, tagline, category, prompt)
|
|
25730
|
+
- Field length limits
|
|
25731
|
+
- Category is valid
|
|
25732
|
+
- Directive/instruction structure
|
|
25733
|
+
- Mode references valid directives
|
|
25734
|
+
|
|
25735
|
+
Examples:
|
|
25736
|
+
aspects validate Validate in current directory
|
|
25737
|
+
aspects validate ./my-aspect Validate specific path
|
|
25738
|
+
aspects validate --strict Stricter checks
|
|
25739
|
+
aspects validate --security Scan for prompt injection patterns
|
|
25740
|
+
|
|
25741
|
+
Security scan flags patterns like:
|
|
25742
|
+
- "ignore previous instructions"
|
|
25743
|
+
- Requests for passwords or financial info
|
|
25744
|
+
- Known jailbreak attempts`
|
|
25305
25745
|
},
|
|
25306
25746
|
args: {
|
|
25307
25747
|
path: {
|
|
@@ -25326,15 +25766,28 @@ var validate_default = defineCommand({
|
|
|
25326
25766
|
try {
|
|
25327
25767
|
const stats = await stat4(aspectPath);
|
|
25328
25768
|
if (stats.isDirectory()) {
|
|
25329
|
-
aspectPath =
|
|
25769
|
+
aspectPath = join8(aspectPath, "aspect.json");
|
|
25330
25770
|
}
|
|
25331
25771
|
} catch {
|
|
25332
|
-
|
|
25333
|
-
|
|
25772
|
+
if (args.path) {
|
|
25773
|
+
const projectRoot = await findProjectRoot() || undefined;
|
|
25774
|
+
const installed = await findInstalledAspect(args.path, projectRoot);
|
|
25775
|
+
if (installed.length > 0) {
|
|
25776
|
+
const match = installed.find((i2) => i2.scope === "project") || installed[0];
|
|
25777
|
+
aspectPath = join8(getAspectPath(args.path, match.scope, projectRoot), "aspect.json");
|
|
25778
|
+
M2.info(`Found installed: ${c3.aspect(args.path)} ${c3.dim(`[${match.scope}]`)}`);
|
|
25779
|
+
} else {
|
|
25780
|
+
M2.error(`Path not found: ${aspectPath}`);
|
|
25781
|
+
process.exit(1);
|
|
25782
|
+
}
|
|
25783
|
+
} else {
|
|
25784
|
+
M2.error(`Path not found: ${aspectPath}`);
|
|
25785
|
+
process.exit(1);
|
|
25786
|
+
}
|
|
25334
25787
|
}
|
|
25335
25788
|
let content;
|
|
25336
25789
|
try {
|
|
25337
|
-
content = await
|
|
25790
|
+
content = await readFile5(aspectPath, "utf-8");
|
|
25338
25791
|
} catch {
|
|
25339
25792
|
M2.error(`Cannot read file: ${aspectPath}`);
|
|
25340
25793
|
process.exit(1);
|
|
@@ -25420,7 +25873,7 @@ var validate_default = defineCommand({
|
|
|
25420
25873
|
M2.info("Checks:");
|
|
25421
25874
|
for (const check2 of checks3) {
|
|
25422
25875
|
const icon = check2.passed ? "✓" : "✗";
|
|
25423
|
-
const msg = check2.message ? `
|
|
25876
|
+
const msg = check2.message ? ` - ${check2.message}` : "";
|
|
25424
25877
|
if (check2.passed) {
|
|
25425
25878
|
M2.success(` ${icon} ${check2.label}${msg}`);
|
|
25426
25879
|
} else {
|
|
@@ -25437,8 +25890,8 @@ var validate_default = defineCommand({
|
|
|
25437
25890
|
});
|
|
25438
25891
|
|
|
25439
25892
|
// src/commands/compile.ts
|
|
25440
|
-
import { readFile as
|
|
25441
|
-
import { join as
|
|
25893
|
+
import { readFile as readFile6, writeFile as writeFile5, stat as stat5 } from "node:fs/promises";
|
|
25894
|
+
import { join as join9 } from "node:path";
|
|
25442
25895
|
function detectModelFamily(model) {
|
|
25443
25896
|
const lower = model.toLowerCase();
|
|
25444
25897
|
if (lower.includes("claude")) {
|
|
@@ -25455,28 +25908,45 @@ function detectModelFamily(model) {
|
|
|
25455
25908
|
}
|
|
25456
25909
|
return "unknown";
|
|
25457
25910
|
}
|
|
25458
|
-
function formatDirectivesForModel(directives, family) {
|
|
25911
|
+
function formatDirectivesForModel(directives, family, options) {
|
|
25459
25912
|
if (directives.length === 0)
|
|
25460
25913
|
return "";
|
|
25461
25914
|
const isModern = family === "claude-modern" || family === "gpt-modern" || family === "unknown";
|
|
25915
|
+
const isReminder = options?.isReminder ?? false;
|
|
25462
25916
|
if (isModern) {
|
|
25917
|
+
const tagName = isReminder ? "critical-reminders" : "directives";
|
|
25463
25918
|
const rules = directives.map((d4) => ` <rule id="${d4.id}" priority="${d4.priority}">${d4.rule}</rule>`).join(`
|
|
25464
25919
|
`);
|
|
25465
|
-
|
|
25920
|
+
if (isReminder) {
|
|
25921
|
+
return `<!-- Universal Pattern: High-priority directives repeated here for cross-LLM compatibility.
|
|
25922
|
+
Claude weights prompt beginning; GPT weights prompt end. Repetition ensures emphasis on both. -->
|
|
25923
|
+
<${tagName}>
|
|
25466
25924
|
${rules}
|
|
25467
|
-
|
|
25925
|
+
</${tagName}>
|
|
25926
|
+
`;
|
|
25927
|
+
}
|
|
25928
|
+
return `<${tagName}>
|
|
25929
|
+
${rules}
|
|
25930
|
+
</${tagName}>
|
|
25931
|
+
|
|
25932
|
+
`;
|
|
25933
|
+
}
|
|
25934
|
+
let output = "";
|
|
25935
|
+
if (isReminder) {
|
|
25936
|
+
output += `<!-- Note: Critical directives repeated below for cross-model compatibility -->
|
|
25937
|
+
`;
|
|
25938
|
+
output += `## Critical Reminders
|
|
25468
25939
|
|
|
25469
25940
|
`;
|
|
25470
25941
|
}
|
|
25471
25942
|
const highPriority = directives.filter((d4) => d4.priority === "high");
|
|
25472
25943
|
const otherPriority = directives.filter((d4) => d4.priority !== "high");
|
|
25473
|
-
let output = "";
|
|
25474
25944
|
for (const d4 of highPriority) {
|
|
25475
25945
|
output += `**IMPORTANT**: ${d4.rule}
|
|
25476
25946
|
|
|
25477
25947
|
`;
|
|
25478
25948
|
}
|
|
25479
|
-
if (otherPriority.length > 0) {
|
|
25949
|
+
if (!isReminder && otherPriority.length > 0) {
|
|
25480
25950
|
for (const d4 of otherPriority) {
|
|
25481
25951
|
output += `- ${d4.rule}
|
|
25482
25952
|
`;
|
|
@@ -25486,10 +25956,49 @@ ${rules}
|
|
|
25486
25956
|
}
|
|
25487
25957
|
return output;
|
|
25488
25958
|
}
|
|
25959
|
+
function formatInstructionsForModel(instructions, family) {
|
|
25960
|
+
if (instructions.length === 0)
|
|
25961
|
+
return "";
|
|
25962
|
+
const isModern = family === "claude-modern" || family === "gpt-modern" || family === "unknown";
|
|
25963
|
+
if (isModern) {
|
|
25964
|
+
const rules = instructions.map((i2) => ` <guideline id="${i2.id}">${i2.rule}</guideline>`).join(`
|
|
25965
|
+
`);
|
|
25966
|
+
return `<instructions>
|
|
25967
|
+
${rules}
|
|
25968
|
+
</instructions>
|
|
25969
|
+
|
|
25970
|
+
`;
|
|
25971
|
+
}
|
|
25972
|
+
let output = `## Guidelines
|
|
25973
|
+
|
|
25974
|
+
`;
|
|
25975
|
+
for (const i2 of instructions) {
|
|
25976
|
+
output += `- ${i2.rule}
|
|
25977
|
+
`;
|
|
25978
|
+
}
|
|
25979
|
+
output += `
|
|
25980
|
+
`;
|
|
25981
|
+
return output;
|
|
25982
|
+
}
|
|
25489
25983
|
var compile_default = defineCommand({
|
|
25490
25984
|
meta: {
|
|
25491
25985
|
name: "compile",
|
|
25492
|
-
description:
|
|
25986
|
+
description: `Compile an aspect's prompt for a specific model.
|
|
25987
|
+
|
|
25988
|
+
Formats directives and instructions optimally for the target model:
|
|
25989
|
+
- Modern models (Claude 4.x, GPT-4.1+): Clean XML tags
|
|
25990
|
+
- Legacy models (Claude 3, GPT-4): Markdown with emphasis
|
|
25991
|
+
|
|
25992
|
+
Cross-LLM Universal Pattern:
|
|
25993
|
+
High-priority directives are repeated at BOTH beginning and end.
|
|
25994
|
+
Claude weights prompt beginning; GPT weights prompt end.
|
|
25995
|
+
This ensures critical rules work across all models.
|
|
25996
|
+
|
|
25997
|
+
Examples:
|
|
25998
|
+
aspects compile alaric -m claude-haiku-4-5
|
|
25999
|
+
aspects compile alaric -m gpt-4o --mode campaign
|
|
26000
|
+
aspects compile ./my-aspect -m claude-4 -o prompt.txt
|
|
26001
|
+
aspects compile alaric -m claude-4 --verbose`
|
|
25493
26002
|
},
|
|
25494
26003
|
args: {
|
|
25495
26004
|
name: {
|
|
@@ -25525,15 +26034,15 @@ var compile_default = defineCommand({
|
|
|
25525
26034
|
try {
|
|
25526
26035
|
const stats = await stat5(args.name);
|
|
25527
26036
|
if (stats.isDirectory()) {
|
|
25528
|
-
aspectPath =
|
|
26037
|
+
aspectPath = join9(args.name, "aspect.json");
|
|
25529
26038
|
} else {
|
|
25530
26039
|
aspectPath = args.name;
|
|
25531
26040
|
}
|
|
25532
|
-
aspectContent = await
|
|
26041
|
+
aspectContent = await readFile6(aspectPath, "utf-8");
|
|
25533
26042
|
} catch {
|
|
25534
26043
|
try {
|
|
25535
|
-
aspectPath =
|
|
25536
|
-
aspectContent = await
|
|
26044
|
+
aspectPath = join9(ASPECTS_DIR, args.name, "aspect.json");
|
|
26045
|
+
aspectContent = await readFile6(aspectPath, "utf-8");
|
|
25537
26046
|
} catch {
|
|
25538
26047
|
M2.error(`Aspect not found: ${args.name}`);
|
|
25539
26048
|
M2.info("Try: aspects list");
|
|
@@ -25563,7 +26072,8 @@ var compile_default = defineCommand({
|
|
|
25563
26072
|
M2.info("");
|
|
25564
26073
|
M2.info(`Model family: ${family}`);
|
|
25565
26074
|
}
|
|
25566
|
-
const directives = [];
|
|
26075
|
+
const directives = aspect.directives || [];
|
|
26076
|
+
const instructions = aspect.instructions || [];
|
|
25567
26077
|
let modeInfo = null;
|
|
25568
26078
|
if (args.mode && aspect.modes) {
|
|
25569
26079
|
modeInfo = aspect.modes[args.mode] || null;
|
|
@@ -25588,6 +26098,9 @@ var compile_default = defineCommand({
|
|
|
25588
26098
|
if (directives.length > 0) {
|
|
25589
26099
|
compiled += formatDirectivesForModel(directives, family);
|
|
25590
26100
|
}
|
|
26101
|
+
if (instructions.length > 0) {
|
|
26102
|
+
compiled += formatInstructionsForModel(instructions, family);
|
|
26103
|
+
}
|
|
25591
26104
|
if (modeInfo?.critical) {
|
|
25592
26105
|
if (family === "claude-modern" || family === "gpt-modern" || family === "unknown") {
|
|
25593
26106
|
compiled += `<mode name="${args.mode}">
|
|
@@ -25603,6 +26116,13 @@ ${modeInfo.critical}
|
|
|
25603
26116
|
}
|
|
25604
26117
|
}
|
|
25605
26118
|
compiled += aspect.prompt;
|
|
26119
|
+
const highPriorityDirectives = directives.filter((d4) => d4.priority === "high");
|
|
26120
|
+
if (highPriorityDirectives.length > 0) {
|
|
26121
|
+
compiled += `
|
|
26122
|
+
|
|
26123
|
+
`;
|
|
26124
|
+
compiled += formatDirectivesForModel(highPriorityDirectives, family, { isReminder: true });
|
|
26125
|
+
}
|
|
25606
26126
|
if (aspect.voiceHints && args.verbose) {
|
|
25607
26127
|
M2.info("");
|
|
25608
26128
|
M2.info("Voice hints:");
|
|
@@ -25631,13 +26151,30 @@ ${modeInfo.critical}
|
|
|
25631
26151
|
});
|
|
25632
26152
|
|
|
25633
26153
|
// src/commands/publish.ts
|
|
25634
|
-
import { readFile as
|
|
25635
|
-
import { join as
|
|
26154
|
+
import { readFile as readFile7, stat as stat6, readdir as readdir2 } from "node:fs/promises";
|
|
26155
|
+
import { join as join10, dirname as dirname3 } from "node:path";
|
|
25636
26156
|
var MAX_ASPECT_SIZE = 51200;
|
|
25637
26157
|
var publish_default = defineCommand({
|
|
25638
26158
|
meta: {
|
|
25639
26159
|
name: "publish",
|
|
25640
|
-
description:
|
|
26160
|
+
description: `Publish an aspect to the registry (requires login).
|
|
26161
|
+
|
|
26162
|
+
Publishing claims the aspect name under your account. You can then:
|
|
26163
|
+
- Publish new versions (bump version in aspect.json)
|
|
26164
|
+
- Update metadata (tagline, tags, category)
|
|
26165
|
+
- Build a publisher reputation
|
|
26166
|
+
|
|
26167
|
+
The publisher field in aspect.json must match your logged-in username.
|
|
26168
|
+
|
|
26169
|
+
Examples:
|
|
26170
|
+
aspects publish Interactive (scans for aspects)
|
|
26171
|
+
aspects publish ./my-aspect Publish specific aspect
|
|
26172
|
+
aspects publish --dry-run Validate without publishing
|
|
26173
|
+
|
|
26174
|
+
Don't want an account? Use 'aspects share' instead:
|
|
26175
|
+
- No login required
|
|
26176
|
+
- Content-addressed by Blake3 hash
|
|
26177
|
+
- Anyone can install via: aspects add blake3:<hash>`
|
|
25641
26178
|
},
|
|
25642
26179
|
args: {
|
|
25643
26180
|
path: {
|
|
@@ -25666,7 +26203,21 @@ var publish_default = defineCommand({
|
|
|
25666
26203
|
}
|
|
25667
26204
|
let aspectPath;
|
|
25668
26205
|
if (args.path) {
|
|
25669
|
-
|
|
26206
|
+
const inputPath = args.path;
|
|
26207
|
+
try {
|
|
26208
|
+
await stat6(inputPath);
|
|
26209
|
+
aspectPath = inputPath;
|
|
26210
|
+
} catch {
|
|
26211
|
+
const projectRoot = await findProjectRoot() || undefined;
|
|
26212
|
+
const installed = await findInstalledAspect(inputPath, projectRoot);
|
|
26213
|
+
if (installed.length > 0) {
|
|
26214
|
+
const match = installed.find((i2) => i2.scope === "project") || installed[0];
|
|
26215
|
+
aspectPath = getAspectPath(inputPath, match.scope, projectRoot);
|
|
26216
|
+
M2.info(`Found installed: ${c3.aspect(inputPath)} ${c3.dim(`[${match.scope}]`)}`);
|
|
26217
|
+
} else {
|
|
26218
|
+
aspectPath = inputPath;
|
|
26219
|
+
}
|
|
26220
|
+
}
|
|
25670
26221
|
} else {
|
|
25671
26222
|
const spinner = Y3();
|
|
25672
26223
|
spinner.start("Scanning for aspects...");
|
|
@@ -25716,13 +26267,37 @@ var publish_default = defineCommand({
|
|
|
25716
26267
|
M2.error(`Aspect too large: ${sizeBytes} bytes (${MAX_ASPECT_SIZE} byte limit)`);
|
|
25717
26268
|
process.exit(1);
|
|
25718
26269
|
}
|
|
25719
|
-
|
|
25720
|
-
|
|
25721
|
-
|
|
25722
|
-
|
|
25723
|
-
|
|
25724
|
-
|
|
25725
|
-
|
|
26270
|
+
const auth = await getAuth();
|
|
26271
|
+
if (!dryRun && auth) {
|
|
26272
|
+
const defaultHandle = await getDefaultHandle();
|
|
26273
|
+
const handles = await getHandles();
|
|
26274
|
+
if (validation.aspect.publisher) {
|
|
26275
|
+
const hasPermission = await hasHandlePermission(validation.aspect.publisher);
|
|
26276
|
+
if (!hasPermission) {
|
|
26277
|
+
spinner2.stop("Validation failed");
|
|
26278
|
+
console.log();
|
|
26279
|
+
M2.error(`You don't have permission to publish under @${c3.bold(validation.aspect.publisher)}`);
|
|
26280
|
+
console.log();
|
|
26281
|
+
console.log(c3.muted(" Your handles:"));
|
|
26282
|
+
for (const h4 of handles) {
|
|
26283
|
+
const isDefault = h4.name === defaultHandle;
|
|
26284
|
+
console.log(` @${h4.name}${isDefault ? c3.dim(" (default)") : ""}`);
|
|
26285
|
+
}
|
|
26286
|
+
console.log();
|
|
26287
|
+
console.log(c3.muted(" Either:"));
|
|
26288
|
+
console.log(` 1. Change "publisher" in aspect.json to one of your handles`);
|
|
26289
|
+
console.log(` 2. Get added as a member to @${validation.aspect.publisher} (via web UI)`);
|
|
26290
|
+
console.log(` 3. Use ${c3.cmd("aspects share")} for anonymous publishing`);
|
|
26291
|
+
console.log();
|
|
26292
|
+
process.exit(1);
|
|
26293
|
+
}
|
|
26294
|
+
} else {
|
|
26295
|
+
if (!defaultHandle) {
|
|
26296
|
+
spinner2.stop("Validation failed");
|
|
26297
|
+
M2.error('No default handle set. Run "aspects handle claim <name>" first.');
|
|
26298
|
+
process.exit(1);
|
|
26299
|
+
}
|
|
26300
|
+
validation.aspect.publisher = defaultHandle;
|
|
25726
26301
|
}
|
|
25727
26302
|
}
|
|
25728
26303
|
spinner2.stop("Validation passed");
|
|
@@ -25753,6 +26328,10 @@ var publish_default = defineCommand({
|
|
|
25753
26328
|
spinner3.start(`Publishing ${validation.aspect.name}@${validation.aspect.version}...`);
|
|
25754
26329
|
try {
|
|
25755
26330
|
const parsed = JSON.parse(validation.content);
|
|
26331
|
+
const defaultHandle = await getDefaultHandle();
|
|
26332
|
+
if (defaultHandle && !parsed.publisher) {
|
|
26333
|
+
parsed.publisher = defaultHandle;
|
|
26334
|
+
}
|
|
25756
26335
|
const result = await publishAspect(parsed);
|
|
25757
26336
|
spinner3.stop("Published");
|
|
25758
26337
|
console.log();
|
|
@@ -25773,6 +26352,10 @@ var publish_default = defineCommand({
|
|
|
25773
26352
|
} else if (err.errorCode === "unauthorized") {
|
|
25774
26353
|
M2.info("");
|
|
25775
26354
|
M2.info('Run "aspects login" to authenticate');
|
|
26355
|
+
} else if (err.errorCode === "no_permission") {
|
|
26356
|
+
M2.info("");
|
|
26357
|
+
M2.info("You don't have permission to publish under this handle");
|
|
26358
|
+
M2.info('Run "aspects handle list" to see your handles');
|
|
25776
26359
|
}
|
|
25777
26360
|
} else {
|
|
25778
26361
|
M2.error(`Publish failed: ${err.message}`);
|
|
@@ -25786,13 +26369,13 @@ async function loadAspectFromPath2(inputPath) {
|
|
|
25786
26369
|
try {
|
|
25787
26370
|
const stats = await stat6(inputPath);
|
|
25788
26371
|
if (stats.isDirectory()) {
|
|
25789
|
-
aspectPath =
|
|
26372
|
+
aspectPath = join10(inputPath, "aspect.json");
|
|
25790
26373
|
}
|
|
25791
26374
|
} catch {
|
|
25792
26375
|
return null;
|
|
25793
26376
|
}
|
|
25794
26377
|
try {
|
|
25795
|
-
const content = await
|
|
26378
|
+
const content = await readFile7(aspectPath, "utf-8");
|
|
25796
26379
|
const aspect = JSON.parse(content);
|
|
25797
26380
|
return {
|
|
25798
26381
|
path: dirname3(aspectPath),
|
|
@@ -25818,18 +26401,33 @@ async function findLocalAspects() {
|
|
|
25818
26401
|
const entries = await readdir2(process.cwd(), { withFileTypes: true });
|
|
25819
26402
|
for (const entry of entries) {
|
|
25820
26403
|
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
25821
|
-
const subAspect = await loadAspectFromPath2(
|
|
26404
|
+
const subAspect = await loadAspectFromPath2(join10(process.cwd(), entry.name));
|
|
25822
26405
|
if (subAspect && !aspects.find((a2) => a2.path === subAspect.path)) {
|
|
25823
26406
|
aspects.push(subAspect);
|
|
25824
26407
|
}
|
|
25825
26408
|
}
|
|
25826
26409
|
}
|
|
25827
26410
|
} catch {}
|
|
26411
|
+
const projectRoot = await findProjectRoot();
|
|
26412
|
+
if (projectRoot) {
|
|
26413
|
+
const projectAspectsDir = join10(projectRoot, ".aspects", "aspects");
|
|
26414
|
+
try {
|
|
26415
|
+
const entries = await readdir2(projectAspectsDir, { withFileTypes: true });
|
|
26416
|
+
for (const entry of entries) {
|
|
26417
|
+
if (entry.isDirectory()) {
|
|
26418
|
+
const projectAspect = await loadAspectFromPath2(join10(projectAspectsDir, entry.name));
|
|
26419
|
+
if (projectAspect && !aspects.find((a2) => a2.path === projectAspect.path)) {
|
|
26420
|
+
aspects.push(projectAspect);
|
|
26421
|
+
}
|
|
26422
|
+
}
|
|
26423
|
+
}
|
|
26424
|
+
} catch {}
|
|
26425
|
+
}
|
|
25828
26426
|
try {
|
|
25829
26427
|
const entries = await readdir2(ASPECTS_DIR, { withFileTypes: true });
|
|
25830
26428
|
for (const entry of entries) {
|
|
25831
26429
|
if (entry.isDirectory()) {
|
|
25832
|
-
const installedAspect = await loadAspectFromPath2(
|
|
26430
|
+
const installedAspect = await loadAspectFromPath2(join10(ASPECTS_DIR, entry.name));
|
|
25833
26431
|
if (installedAspect && !aspects.find((a2) => a2.path === installedAspect.path)) {
|
|
25834
26432
|
aspects.push(installedAspect);
|
|
25835
26433
|
}
|
|
@@ -25843,14 +26441,14 @@ async function validateAspect(aspectPath) {
|
|
|
25843
26441
|
try {
|
|
25844
26442
|
const stats = await stat6(aspectPath);
|
|
25845
26443
|
if (stats.isDirectory()) {
|
|
25846
|
-
filePath =
|
|
26444
|
+
filePath = join10(aspectPath, "aspect.json");
|
|
25847
26445
|
}
|
|
25848
26446
|
} catch {
|
|
25849
26447
|
return { valid: false, errors: [`Path not found: ${aspectPath}`] };
|
|
25850
26448
|
}
|
|
25851
26449
|
let content;
|
|
25852
26450
|
try {
|
|
25853
|
-
content = await
|
|
26451
|
+
content = await readFile7(filePath, "utf-8");
|
|
25854
26452
|
} catch {
|
|
25855
26453
|
return { valid: false, errors: [`Cannot read file: ${filePath}`] };
|
|
25856
26454
|
}
|
|
@@ -25891,8 +26489,8 @@ async function validateAspect(aspectPath) {
|
|
|
25891
26489
|
}
|
|
25892
26490
|
|
|
25893
26491
|
// src/commands/edit.ts
|
|
25894
|
-
import { readFile as
|
|
25895
|
-
import { join as
|
|
26492
|
+
import { readFile as readFile8, writeFile as writeFile6 } from "node:fs/promises";
|
|
26493
|
+
import { join as join11 } from "node:path";
|
|
25896
26494
|
var CATEGORIES = [
|
|
25897
26495
|
"assistant",
|
|
25898
26496
|
"roleplay",
|
|
@@ -25909,7 +26507,7 @@ async function listLocalAspects() {
|
|
|
25909
26507
|
const results = [];
|
|
25910
26508
|
for (const [name, info] of Object.entries(config2.installed)) {
|
|
25911
26509
|
const aspectPath = info.path || getAspectPath(name);
|
|
25912
|
-
const parseResult = await parseAspectFile(
|
|
26510
|
+
const parseResult = await parseAspectFile(join11(aspectPath, "aspect.json"));
|
|
25913
26511
|
if (parseResult.success) {
|
|
25914
26512
|
results.push({ name, path: aspectPath, aspect: parseResult.aspect });
|
|
25915
26513
|
}
|
|
@@ -26065,8 +26663,8 @@ var edit_default = defineCommand({
|
|
|
26065
26663
|
xe("Cancelled");
|
|
26066
26664
|
return;
|
|
26067
26665
|
}
|
|
26068
|
-
const aspectPath =
|
|
26069
|
-
const content = await
|
|
26666
|
+
const aspectPath = join11(selectedAspect.path, "aspect.json");
|
|
26667
|
+
const content = await readFile8(aspectPath, "utf-8");
|
|
26070
26668
|
let updatedContent = content;
|
|
26071
26669
|
if (changes.includes("displayName")) {
|
|
26072
26670
|
updatedContent = updatedContent.replace(/displayName:.*$/m, `displayName: "${updatedAspect.displayName}"`);
|
|
@@ -26103,8 +26701,8 @@ ${updatedAspect.tags.map((t4) => ` - ${t4}`).join(`
|
|
|
26103
26701
|
});
|
|
26104
26702
|
|
|
26105
26703
|
// src/commands/bundle.ts
|
|
26106
|
-
import { readFile as
|
|
26107
|
-
import { join as
|
|
26704
|
+
import { readFile as readFile9, writeFile as writeFile7 } from "node:fs/promises";
|
|
26705
|
+
import { join as join12 } from "node:path";
|
|
26108
26706
|
var import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
26109
26707
|
function parseQuery2(query) {
|
|
26110
26708
|
const filters = [];
|
|
@@ -26184,8 +26782,8 @@ async function loadLocalAspects() {
|
|
|
26184
26782
|
const aspects = [];
|
|
26185
26783
|
for (const name of Object.keys(config2.installed || {})) {
|
|
26186
26784
|
try {
|
|
26187
|
-
const aspectPath =
|
|
26188
|
-
const content = await
|
|
26785
|
+
const aspectPath = join12(getAspectPath(name), "aspect.json");
|
|
26786
|
+
const content = await readFile9(aspectPath, "utf-8");
|
|
26189
26787
|
aspects.push(JSON.parse(content));
|
|
26190
26788
|
} catch {}
|
|
26191
26789
|
}
|
|
@@ -26268,8 +26866,8 @@ var bundle_default = defineCommand({
|
|
|
26268
26866
|
}
|
|
26269
26867
|
}
|
|
26270
26868
|
} else {
|
|
26271
|
-
const aspectPath =
|
|
26272
|
-
const content = await
|
|
26869
|
+
const aspectPath = join12(getAspectPath(name), "aspect.json");
|
|
26870
|
+
const content = await readFile9(aspectPath, "utf-8");
|
|
26273
26871
|
aspectsToBundle.push(JSON.parse(content));
|
|
26274
26872
|
}
|
|
26275
26873
|
} catch (error48) {
|
|
@@ -26318,8 +26916,8 @@ var bundle_default = defineCommand({
|
|
|
26318
26916
|
}
|
|
26319
26917
|
}
|
|
26320
26918
|
} else {
|
|
26321
|
-
const aspectPath =
|
|
26322
|
-
const content = await
|
|
26919
|
+
const aspectPath = join12(getAspectPath(aspectName), "aspect.json");
|
|
26920
|
+
const content = await readFile9(aspectPath, "utf-8");
|
|
26323
26921
|
const aspect = JSON.parse(content);
|
|
26324
26922
|
if (!aspectsToBundle.some((a2) => a2.name === aspect.name)) {
|
|
26325
26923
|
aspectsToBundle.push(aspect);
|
|
@@ -26364,8 +26962,8 @@ var bundle_default = defineCommand({
|
|
|
26364
26962
|
});
|
|
26365
26963
|
async function loadSet2(name) {
|
|
26366
26964
|
try {
|
|
26367
|
-
const setPath =
|
|
26368
|
-
const content = await
|
|
26965
|
+
const setPath = join12(getSetsDir(), name, "set.json");
|
|
26966
|
+
const content = await readFile9(setPath, "utf-8");
|
|
26369
26967
|
return JSON.parse(content);
|
|
26370
26968
|
} catch {
|
|
26371
26969
|
return null;
|
|
@@ -26373,16 +26971,39 @@ async function loadSet2(name) {
|
|
|
26373
26971
|
}
|
|
26374
26972
|
|
|
26375
26973
|
// src/commands/login.ts
|
|
26974
|
+
import { exec } from "node:child_process";
|
|
26975
|
+
import * as readline from "node:readline/promises";
|
|
26976
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
26376
26977
|
var login_default = defineCommand({
|
|
26377
26978
|
meta: {
|
|
26378
26979
|
name: "login",
|
|
26379
|
-
description:
|
|
26980
|
+
description: `Authenticate with the aspects registry.
|
|
26981
|
+
|
|
26982
|
+
Uses device authorization flow (like GitHub CLI):
|
|
26983
|
+
1. CLI requests a device code
|
|
26984
|
+
2. Browser opens to verification URL
|
|
26985
|
+
3. Enter the code and authorize
|
|
26986
|
+
4. CLI receives and stores access token
|
|
26987
|
+
|
|
26988
|
+
Benefits of logging in:
|
|
26989
|
+
- Name ownership: Claim aspect names under your publisher ID
|
|
26990
|
+
- Versioning: Publish updates to your aspects
|
|
26991
|
+
- Edit metadata: Update tagline, tags, category
|
|
26992
|
+
|
|
26993
|
+
Credentials stored in ~/.aspects/config.json
|
|
26994
|
+
|
|
26995
|
+
Don't want an account? Use 'aspects share' to publish anonymously.`
|
|
26380
26996
|
},
|
|
26381
26997
|
async run() {
|
|
26382
26998
|
const auth = await getAuth();
|
|
26383
26999
|
if (auth && await isLoggedIn()) {
|
|
27000
|
+
const defaultHandle = await getDefaultHandle();
|
|
26384
27001
|
console.log();
|
|
26385
|
-
|
|
27002
|
+
if (defaultHandle) {
|
|
27003
|
+
console.log(`${icons2.info} Already logged in as ${c3.bold(`@${defaultHandle}`)}`);
|
|
27004
|
+
} else {
|
|
27005
|
+
console.log(`${icons2.info} Already logged in`);
|
|
27006
|
+
}
|
|
26386
27007
|
console.log(c3.muted(' Run "aspects logout" to sign out first.'));
|
|
26387
27008
|
console.log();
|
|
26388
27009
|
return;
|
|
@@ -26393,7 +27014,14 @@ var login_default = defineCommand({
|
|
|
26393
27014
|
try {
|
|
26394
27015
|
deviceCode = await initiateDeviceAuth();
|
|
26395
27016
|
} catch (err) {
|
|
26396
|
-
|
|
27017
|
+
const error48 = err;
|
|
27018
|
+
log.error(`Failed to initiate login: ${error48.message}`);
|
|
27019
|
+
if (error48.statusCode) {
|
|
27020
|
+
console.log(c3.muted(` Status: ${error48.statusCode}`));
|
|
27021
|
+
}
|
|
27022
|
+
if (error48.errorCode) {
|
|
27023
|
+
console.log(c3.muted(` Code: ${error48.errorCode}`));
|
|
27024
|
+
}
|
|
26397
27025
|
process.exit(1);
|
|
26398
27026
|
}
|
|
26399
27027
|
console.log();
|
|
@@ -26403,7 +27031,6 @@ var login_default = defineCommand({
|
|
|
26403
27031
|
console.log(` Code: ${c3.bold(deviceCode.user_code)}`);
|
|
26404
27032
|
console.log();
|
|
26405
27033
|
try {
|
|
26406
|
-
const { exec } = await import("node:child_process");
|
|
26407
27034
|
const platform2 = process.platform;
|
|
26408
27035
|
const openCmd = platform2 === "darwin" ? "open" : platform2 === "win32" ? "start" : "xdg-open";
|
|
26409
27036
|
exec(`${openCmd} "${deviceCode.verification_uri_complete}"`);
|
|
@@ -26419,15 +27046,44 @@ var login_default = defineCommand({
|
|
|
26419
27046
|
if (result.ok && result.access_token) {
|
|
26420
27047
|
const expiresIn = result.expires_in ?? 3600;
|
|
26421
27048
|
const expiresAtDate = new Date(Date.now() + expiresIn * 1000).toISOString();
|
|
26422
|
-
const
|
|
27049
|
+
const account = result.account;
|
|
27050
|
+
if (!account) {
|
|
27051
|
+
log.error("API did not return account info. Please update the registry.");
|
|
27052
|
+
process.exit(1);
|
|
27053
|
+
}
|
|
27054
|
+
if (account.needs_handle) {
|
|
27055
|
+
console.log(`${icons2.success} Authenticated!`);
|
|
27056
|
+
console.log();
|
|
27057
|
+
const suggested = extractUsernameFromToken(result.access_token);
|
|
27058
|
+
const handle = await promptForHandle(suggested, result.access_token);
|
|
27059
|
+
await setAuthTokens({
|
|
27060
|
+
accessToken: result.access_token,
|
|
27061
|
+
refreshToken: result.refresh_token,
|
|
27062
|
+
expiresAt: expiresAtDate,
|
|
27063
|
+
accountId: account.id,
|
|
27064
|
+
handles: [{ name: handle, role: "owner", default: true }],
|
|
27065
|
+
defaultHandle: handle
|
|
27066
|
+
});
|
|
27067
|
+
console.log();
|
|
27068
|
+
console.log(`${icons2.success} Logged in as ${c3.bold(`@${handle}`)}`);
|
|
27069
|
+
console.log(c3.muted(" Credentials stored in ~/.aspects/config.json"));
|
|
27070
|
+
console.log();
|
|
27071
|
+
return;
|
|
27072
|
+
}
|
|
27073
|
+
const defaultHandle = account.handles.find((h4) => h4.default)?.name ?? account.handles[0]?.name ?? "";
|
|
26423
27074
|
await setAuthTokens({
|
|
26424
27075
|
accessToken: result.access_token,
|
|
26425
27076
|
refreshToken: result.refresh_token,
|
|
26426
27077
|
expiresAt: expiresAtDate,
|
|
26427
|
-
|
|
27078
|
+
accountId: account.id,
|
|
27079
|
+
handles: account.handles,
|
|
27080
|
+
defaultHandle
|
|
26428
27081
|
});
|
|
26429
|
-
console.log(`${icons2.success}
|
|
26430
|
-
|
|
27082
|
+
console.log(`${icons2.success} Logged in as ${c3.bold(`@${defaultHandle}`)}`);
|
|
27083
|
+
if (account.handles.length > 1) {
|
|
27084
|
+
console.log(c3.muted(` Also: ${account.handles.filter((h4) => h4.name !== defaultHandle).map((h4) => `@${h4.name}`).join(", ")}`));
|
|
27085
|
+
}
|
|
27086
|
+
console.log(c3.muted(" Credentials stored in ~/.aspects/config.json"));
|
|
26431
27087
|
console.log();
|
|
26432
27088
|
return;
|
|
26433
27089
|
}
|
|
@@ -26465,11 +27121,75 @@ function extractUsernameFromToken(token) {
|
|
|
26465
27121
|
if (parts.length !== 3)
|
|
26466
27122
|
return null;
|
|
26467
27123
|
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
|
|
26468
|
-
|
|
27124
|
+
const raw = payload.preferred_username ?? payload.email ?? payload.username ?? payload.name ?? null;
|
|
27125
|
+
if (!raw)
|
|
27126
|
+
return null;
|
|
27127
|
+
let clean2 = raw.split("@")[0] ?? raw;
|
|
27128
|
+
clean2 = clean2.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/--+/g, "-").replace(/^-|-$/g, "");
|
|
27129
|
+
if (clean2.length < 2 || clean2.length > 39)
|
|
27130
|
+
return null;
|
|
27131
|
+
return clean2;
|
|
26469
27132
|
} catch {
|
|
26470
27133
|
return null;
|
|
26471
27134
|
}
|
|
26472
27135
|
}
|
|
27136
|
+
async function promptForHandle(suggested, accessToken) {
|
|
27137
|
+
const rl2 = readline.createInterface({ input, output });
|
|
27138
|
+
const HANDLE_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
|
27139
|
+
console.log(` ${c3.bold("Claim your handle")}`);
|
|
27140
|
+
console.log(` This will be your publisher identity (e.g., @${suggested ?? "yourname"})`);
|
|
27141
|
+
console.log();
|
|
27142
|
+
while (true) {
|
|
27143
|
+
const prompt2 = suggested ? ` Handle [${suggested}]: ` : " Handle: ";
|
|
27144
|
+
let answer = await rl2.question(prompt2);
|
|
27145
|
+
answer = answer.trim().toLowerCase().replace(/^@/, "");
|
|
27146
|
+
if (!answer && suggested) {
|
|
27147
|
+
answer = suggested;
|
|
27148
|
+
}
|
|
27149
|
+
if (!answer) {
|
|
27150
|
+
console.log(c3.warn(" Please enter a handle"));
|
|
27151
|
+
continue;
|
|
27152
|
+
}
|
|
27153
|
+
if (answer.length < 2) {
|
|
27154
|
+
console.log(c3.warn(" Handle must be at least 2 characters"));
|
|
27155
|
+
continue;
|
|
27156
|
+
}
|
|
27157
|
+
if (answer.length > 39) {
|
|
27158
|
+
console.log(c3.warn(" Handle must be at most 39 characters"));
|
|
27159
|
+
continue;
|
|
27160
|
+
}
|
|
27161
|
+
if (!HANDLE_REGEX.test(answer)) {
|
|
27162
|
+
console.log(c3.warn(" Handle must be lowercase alphanumeric with hyphens"));
|
|
27163
|
+
continue;
|
|
27164
|
+
}
|
|
27165
|
+
if (answer.includes("--")) {
|
|
27166
|
+
console.log(c3.warn(" Handle cannot contain consecutive hyphens"));
|
|
27167
|
+
continue;
|
|
27168
|
+
}
|
|
27169
|
+
try {
|
|
27170
|
+
console.log(` ${icons2.working} Claiming @${answer}...`);
|
|
27171
|
+
await claimHandle(answer);
|
|
27172
|
+
rl2.close();
|
|
27173
|
+
return answer;
|
|
27174
|
+
} catch (err) {
|
|
27175
|
+
if (err instanceof ApiClientError) {
|
|
27176
|
+
switch (err.errorCode) {
|
|
27177
|
+
case "handle_taken":
|
|
27178
|
+
console.log(c3.warn(` @${answer} is already taken. Try another.`));
|
|
27179
|
+
break;
|
|
27180
|
+
case "handle_reserved":
|
|
27181
|
+
console.log(c3.warn(` @${answer} is reserved. Try another.`));
|
|
27182
|
+
break;
|
|
27183
|
+
default:
|
|
27184
|
+
console.log(c3.warn(` ${err.message}`));
|
|
27185
|
+
}
|
|
27186
|
+
continue;
|
|
27187
|
+
}
|
|
27188
|
+
rl2.close();
|
|
27189
|
+
throw err;
|
|
27190
|
+
}
|
|
27191
|
+
}
|
|
27192
|
+
}
|
|
26473
27193
|
|
|
26474
27194
|
// src/commands/logout.ts
|
|
26475
27195
|
var logout_default = defineCommand({
|
|
@@ -26485,10 +27205,10 @@ var logout_default = defineCommand({
|
|
|
26485
27205
|
console.log();
|
|
26486
27206
|
return;
|
|
26487
27207
|
}
|
|
26488
|
-
const
|
|
27208
|
+
const handle = auth.defaultHandle;
|
|
26489
27209
|
await clearAuth();
|
|
26490
27210
|
console.log();
|
|
26491
|
-
console.log(`${icons2.success} Logged out${
|
|
27211
|
+
console.log(`${icons2.success} Logged out${handle ? ` from @${handle}` : ""}`);
|
|
26492
27212
|
console.log(c3.muted(" Auth tokens removed from ~/.aspects/config.json"));
|
|
26493
27213
|
console.log();
|
|
26494
27214
|
}
|
|
@@ -26496,12 +27216,29 @@ var logout_default = defineCommand({
|
|
|
26496
27216
|
|
|
26497
27217
|
// src/commands/share.ts
|
|
26498
27218
|
import { stat as stat7 } from "node:fs/promises";
|
|
26499
|
-
import { join as
|
|
27219
|
+
import { join as join13 } from "node:path";
|
|
26500
27220
|
var MAX_ASPECT_SIZE2 = 51200;
|
|
26501
27221
|
var share_default = defineCommand({
|
|
26502
27222
|
meta: {
|
|
26503
27223
|
name: "share",
|
|
26504
|
-
description:
|
|
27224
|
+
description: `Share an aspect anonymously via content hash (no account required).
|
|
27225
|
+
|
|
27226
|
+
How it works:
|
|
27227
|
+
1. Computes Blake3 hash of your aspect content
|
|
27228
|
+
2. Uploads to registry (content-addressed storage)
|
|
27229
|
+
3. Anyone can install via: aspects add blake3:<hash>
|
|
27230
|
+
|
|
27231
|
+
No account needed! Perfect for:
|
|
27232
|
+
- Quick one-off sharing
|
|
27233
|
+
- Testing before claiming a name
|
|
27234
|
+
- Anonymous contributions
|
|
27235
|
+
|
|
27236
|
+
Examples:
|
|
27237
|
+
aspects share my-aspect Share an installed aspect
|
|
27238
|
+
aspects share ./path/to/aspect Share from local path
|
|
27239
|
+
aspects share my-aspect --dry-run Preview hash without uploading
|
|
27240
|
+
|
|
27241
|
+
Want to claim a name instead? Use 'aspects publish' (requires login).`
|
|
26505
27242
|
},
|
|
26506
27243
|
args: {
|
|
26507
27244
|
target: {
|
|
@@ -26543,8 +27280,8 @@ var share_default = defineCommand({
|
|
|
26543
27280
|
try {
|
|
26544
27281
|
const stats = await stat7(target);
|
|
26545
27282
|
if (stats.isDirectory()) {
|
|
26546
|
-
const jsonPath =
|
|
26547
|
-
const yamlPath =
|
|
27283
|
+
const jsonPath = join13(target, "aspect.json");
|
|
27284
|
+
const yamlPath = join13(target, "aspect.yaml");
|
|
26548
27285
|
try {
|
|
26549
27286
|
await stat7(jsonPath);
|
|
26550
27287
|
filePath = jsonPath;
|
|
@@ -26563,13 +27300,14 @@ var share_default = defineCommand({
|
|
|
26563
27300
|
}
|
|
26564
27301
|
aspect = result.aspect;
|
|
26565
27302
|
} else {
|
|
26566
|
-
const
|
|
26567
|
-
if (!
|
|
27303
|
+
const found = await findAndLoadAspect(target);
|
|
27304
|
+
if (!found) {
|
|
26568
27305
|
M2.error(`Aspect "${target}" is not installed or cannot be read`);
|
|
26569
27306
|
M2.info("To share from a path, use: aspects share ./path/to/aspect.json");
|
|
26570
27307
|
process.exit(1);
|
|
26571
27308
|
}
|
|
26572
|
-
aspect
|
|
27309
|
+
M2.info(`Found: ${c3.aspect(target)} ${c3.dim(`[${found.scope}]`)}`);
|
|
27310
|
+
aspect = found.aspect;
|
|
26573
27311
|
}
|
|
26574
27312
|
const content = canonicalizeAspect(aspect);
|
|
26575
27313
|
const sizeBytes = Buffer.byteLength(content);
|
|
@@ -26586,7 +27324,7 @@ var share_default = defineCommand({
|
|
|
26586
27324
|
console.log(` ${c3.label("Hash")} ${hash2}`);
|
|
26587
27325
|
console.log();
|
|
26588
27326
|
if (dryRun) {
|
|
26589
|
-
M2.info("Dry run
|
|
27327
|
+
M2.info("Dry run - not uploading");
|
|
26590
27328
|
console.log();
|
|
26591
27329
|
console.log(` ${c3.label("Install")} aspects add hash:${hash2}`);
|
|
26592
27330
|
console.log();
|
|
@@ -26608,13 +27346,16 @@ var share_default = defineCommand({
|
|
|
26608
27346
|
console.log();
|
|
26609
27347
|
console.log(`${icons2.success} ${c3.bold("Shared successfully!")}`);
|
|
26610
27348
|
console.log();
|
|
26611
|
-
console.log(` ${c3.label("
|
|
26612
|
-
console.log(` ${c3.label("Install")} ${c3.highlight(`aspects add
|
|
27349
|
+
console.log(` ${c3.label("Name")} ${c3.highlight(response.name)}`);
|
|
27350
|
+
console.log(` ${c3.label("Install")} ${c3.highlight(`aspects add ${response.name}`)}`);
|
|
27351
|
+
console.log(` ${c3.label("URL")} ${response.url}`);
|
|
27352
|
+
console.log();
|
|
27353
|
+
console.log(` ${c3.muted("Or by hash:")} aspects add blake3:${response.blake3}`);
|
|
26613
27354
|
if (response.existing) {
|
|
26614
27355
|
console.log(` ${c3.muted("(Already existed on registry)")}`);
|
|
26615
27356
|
}
|
|
26616
27357
|
console.log();
|
|
26617
|
-
Se("Share this
|
|
27358
|
+
Se("Share this name or URL with anyone to let them install your aspect!");
|
|
26618
27359
|
} catch (err) {
|
|
26619
27360
|
spinner.stop("Upload failed");
|
|
26620
27361
|
if (err instanceof ApiClientError) {
|
|
@@ -26699,7 +27440,501 @@ var unpublish_default = defineCommand({
|
|
|
26699
27440
|
}
|
|
26700
27441
|
});
|
|
26701
27442
|
|
|
27443
|
+
// src/commands/config.ts
|
|
27444
|
+
import { homedir as homedir2 } from "node:os";
|
|
27445
|
+
var DEFAULT_REGISTRY_URL = "https://aspects.sh/api/v1";
|
|
27446
|
+
function tildify(path) {
|
|
27447
|
+
const home = homedir2();
|
|
27448
|
+
return path.startsWith(home) ? path.replace(home, "~") : path;
|
|
27449
|
+
}
|
|
27450
|
+
var CONFIG_KEYS = {
|
|
27451
|
+
"registry.url": {
|
|
27452
|
+
path: ["settings", "registryUrl"],
|
|
27453
|
+
description: "API registry URL",
|
|
27454
|
+
default: DEFAULT_REGISTRY_URL
|
|
27455
|
+
}
|
|
27456
|
+
};
|
|
27457
|
+
function getNestedValue(obj, path) {
|
|
27458
|
+
let current = obj;
|
|
27459
|
+
for (const key of path) {
|
|
27460
|
+
if (current && typeof current === "object" && key in current) {
|
|
27461
|
+
current = current[key];
|
|
27462
|
+
} else {
|
|
27463
|
+
return;
|
|
27464
|
+
}
|
|
27465
|
+
}
|
|
27466
|
+
return current;
|
|
27467
|
+
}
|
|
27468
|
+
function setNestedValue(obj, path, value) {
|
|
27469
|
+
let current = obj;
|
|
27470
|
+
for (let i2 = 0;i2 < path.length - 1; i2++) {
|
|
27471
|
+
const key = path[i2];
|
|
27472
|
+
if (!(key in current) || typeof current[key] !== "object") {
|
|
27473
|
+
current[key] = {};
|
|
27474
|
+
}
|
|
27475
|
+
current = current[key];
|
|
27476
|
+
}
|
|
27477
|
+
const lastKey = path[path.length - 1];
|
|
27478
|
+
if (value === undefined) {
|
|
27479
|
+
delete current[lastKey];
|
|
27480
|
+
} else {
|
|
27481
|
+
current[lastKey] = value;
|
|
27482
|
+
}
|
|
27483
|
+
}
|
|
27484
|
+
var listCommand2 = defineCommand({
|
|
27485
|
+
meta: {
|
|
27486
|
+
name: "list",
|
|
27487
|
+
description: "Show all configuration values"
|
|
27488
|
+
},
|
|
27489
|
+
async run() {
|
|
27490
|
+
const config2 = await readConfig();
|
|
27491
|
+
const registryUrl = await getRegistryUrl();
|
|
27492
|
+
const auth = await getAuth();
|
|
27493
|
+
console.log();
|
|
27494
|
+
console.log(`${icons2.info} Configuration`);
|
|
27495
|
+
console.log();
|
|
27496
|
+
console.log(` ${c3.label("Config file")} ${tildify(CONFIG_PATH)}`);
|
|
27497
|
+
console.log();
|
|
27498
|
+
console.log(c3.bold(" Registry"));
|
|
27499
|
+
const envUrl = process.env.ASPECTS_REGISTRY_URL;
|
|
27500
|
+
if (envUrl) {
|
|
27501
|
+
console.log(` ${c3.label("url")} ${registryUrl} ${c3.dim("(from ASPECTS_REGISTRY_URL)")}`);
|
|
27502
|
+
} else if (config2.settings.registryUrl) {
|
|
27503
|
+
console.log(` ${c3.label("url")} ${registryUrl}`);
|
|
27504
|
+
} else {
|
|
27505
|
+
console.log(` ${c3.label("url")} ${registryUrl} ${c3.dim("(default)")}`);
|
|
27506
|
+
}
|
|
27507
|
+
console.log();
|
|
27508
|
+
console.log(c3.bold(" Auth"));
|
|
27509
|
+
if (auth) {
|
|
27510
|
+
console.log(` ${c3.label("logged in as")} @${auth.defaultHandle}`);
|
|
27511
|
+
const expiresAt = new Date(auth.expiresAt);
|
|
27512
|
+
const isExpired = expiresAt < new Date;
|
|
27513
|
+
if (isExpired) {
|
|
27514
|
+
console.log(` ${c3.label("token")} ${c3.warn("expired")} ${c3.dim(`(${expiresAt.toLocaleString()})`)}`);
|
|
27515
|
+
} else {
|
|
27516
|
+
console.log(` ${c3.label("token")} valid until ${c3.dim(expiresAt.toLocaleString())}`);
|
|
27517
|
+
}
|
|
27518
|
+
} else {
|
|
27519
|
+
console.log(` ${c3.muted("Not logged in")}`);
|
|
27520
|
+
}
|
|
27521
|
+
const installedCount = Object.keys(config2.installed).length;
|
|
27522
|
+
console.log();
|
|
27523
|
+
console.log(c3.bold(" Library"));
|
|
27524
|
+
console.log(` ${c3.label("aspects home")} ${tildify(ASPECTS_HOME)}`);
|
|
27525
|
+
console.log(` ${c3.label("installed")} ${installedCount} aspect${installedCount === 1 ? "" : "s"}`);
|
|
27526
|
+
console.log();
|
|
27527
|
+
}
|
|
27528
|
+
});
|
|
27529
|
+
var getCommand = defineCommand({
|
|
27530
|
+
meta: {
|
|
27531
|
+
name: "get",
|
|
27532
|
+
description: "Get a configuration value"
|
|
27533
|
+
},
|
|
27534
|
+
args: {
|
|
27535
|
+
key: {
|
|
27536
|
+
type: "positional",
|
|
27537
|
+
description: "Config key (e.g., registry.url)",
|
|
27538
|
+
required: true
|
|
27539
|
+
}
|
|
27540
|
+
},
|
|
27541
|
+
async run({ args }) {
|
|
27542
|
+
const key = args.key;
|
|
27543
|
+
const config2 = await readConfig();
|
|
27544
|
+
const knownKey = CONFIG_KEYS[key];
|
|
27545
|
+
if (knownKey) {
|
|
27546
|
+
const value = getNestedValue(config2, knownKey.path);
|
|
27547
|
+
const effectiveValue = value ?? knownKey.default;
|
|
27548
|
+
if (key === "registry.url" && process.env.ASPECTS_REGISTRY_URL) {
|
|
27549
|
+
console.log(process.env.ASPECTS_REGISTRY_URL);
|
|
27550
|
+
return;
|
|
27551
|
+
}
|
|
27552
|
+
console.log(effectiveValue);
|
|
27553
|
+
} else {
|
|
27554
|
+
const path = key.split(".");
|
|
27555
|
+
const value = getNestedValue(config2, path);
|
|
27556
|
+
if (value !== undefined) {
|
|
27557
|
+
console.log(typeof value === "object" ? JSON.stringify(value, null, 2) : value);
|
|
27558
|
+
} else {
|
|
27559
|
+
console.error(`Unknown config key: ${key}`);
|
|
27560
|
+
process.exit(1);
|
|
27561
|
+
}
|
|
27562
|
+
}
|
|
27563
|
+
}
|
|
27564
|
+
});
|
|
27565
|
+
var setCommand = defineCommand({
|
|
27566
|
+
meta: {
|
|
27567
|
+
name: "set",
|
|
27568
|
+
description: "Set a configuration value"
|
|
27569
|
+
},
|
|
27570
|
+
args: {
|
|
27571
|
+
key: {
|
|
27572
|
+
type: "positional",
|
|
27573
|
+
description: "Config key (e.g., registry.url)",
|
|
27574
|
+
required: true
|
|
27575
|
+
},
|
|
27576
|
+
value: {
|
|
27577
|
+
type: "positional",
|
|
27578
|
+
description: "Value to set",
|
|
27579
|
+
required: true
|
|
27580
|
+
}
|
|
27581
|
+
},
|
|
27582
|
+
async run({ args }) {
|
|
27583
|
+
const key = args.key;
|
|
27584
|
+
const value = args.value;
|
|
27585
|
+
const config2 = await readConfig();
|
|
27586
|
+
const knownKey = CONFIG_KEYS[key];
|
|
27587
|
+
if (knownKey) {
|
|
27588
|
+
setNestedValue(config2, knownKey.path, value);
|
|
27589
|
+
await writeConfig(config2);
|
|
27590
|
+
console.log(`${icons2.success} Set ${key} = ${value}`);
|
|
27591
|
+
} else {
|
|
27592
|
+
const path = key.split(".");
|
|
27593
|
+
setNestedValue(config2, path, value);
|
|
27594
|
+
await writeConfig(config2);
|
|
27595
|
+
console.log(`${icons2.success} Set ${key} = ${value}`);
|
|
27596
|
+
}
|
|
27597
|
+
}
|
|
27598
|
+
});
|
|
27599
|
+
var unsetCommand = defineCommand({
|
|
27600
|
+
meta: {
|
|
27601
|
+
name: "unset",
|
|
27602
|
+
description: "Remove a configuration value (revert to default)"
|
|
27603
|
+
},
|
|
27604
|
+
args: {
|
|
27605
|
+
key: {
|
|
27606
|
+
type: "positional",
|
|
27607
|
+
description: "Config key to remove",
|
|
27608
|
+
required: true
|
|
27609
|
+
}
|
|
27610
|
+
},
|
|
27611
|
+
async run({ args }) {
|
|
27612
|
+
const key = args.key;
|
|
27613
|
+
const config2 = await readConfig();
|
|
27614
|
+
const knownKey = CONFIG_KEYS[key];
|
|
27615
|
+
const path = knownKey ? knownKey.path : key.split(".");
|
|
27616
|
+
const currentValue = getNestedValue(config2, path);
|
|
27617
|
+
if (currentValue === undefined) {
|
|
27618
|
+
console.log(`${icons2.info} ${key} is not set`);
|
|
27619
|
+
return;
|
|
27620
|
+
}
|
|
27621
|
+
setNestedValue(config2, path, undefined);
|
|
27622
|
+
await writeConfig(config2);
|
|
27623
|
+
if (knownKey) {
|
|
27624
|
+
console.log(`${icons2.success} Unset ${key} (will use default: ${knownKey.default})`);
|
|
27625
|
+
} else {
|
|
27626
|
+
console.log(`${icons2.success} Unset ${key}`);
|
|
27627
|
+
}
|
|
27628
|
+
}
|
|
27629
|
+
});
|
|
27630
|
+
var pathCommand = defineCommand({
|
|
27631
|
+
meta: {
|
|
27632
|
+
name: "path",
|
|
27633
|
+
description: "Show path to config file"
|
|
27634
|
+
},
|
|
27635
|
+
async run() {
|
|
27636
|
+
console.log(CONFIG_PATH);
|
|
27637
|
+
}
|
|
27638
|
+
});
|
|
27639
|
+
var config_default = defineCommand({
|
|
27640
|
+
meta: {
|
|
27641
|
+
name: "config",
|
|
27642
|
+
description: "View and modify configuration"
|
|
27643
|
+
},
|
|
27644
|
+
subCommands: {
|
|
27645
|
+
list: listCommand2,
|
|
27646
|
+
get: getCommand,
|
|
27647
|
+
set: setCommand,
|
|
27648
|
+
unset: unsetCommand,
|
|
27649
|
+
path: pathCommand
|
|
27650
|
+
}
|
|
27651
|
+
});
|
|
27652
|
+
|
|
27653
|
+
// src/commands/handle.ts
|
|
27654
|
+
var HANDLE_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
|
27655
|
+
var MIN_LENGTH = 2;
|
|
27656
|
+
var MAX_LENGTH = 39;
|
|
27657
|
+
function validateHandleFormat(name) {
|
|
27658
|
+
if (name.length < MIN_LENGTH) {
|
|
27659
|
+
return `Handle must be at least ${MIN_LENGTH} characters`;
|
|
27660
|
+
}
|
|
27661
|
+
if (name.length > MAX_LENGTH) {
|
|
27662
|
+
return `Handle must be at most ${MAX_LENGTH} characters`;
|
|
27663
|
+
}
|
|
27664
|
+
if (!HANDLE_REGEX.test(name)) {
|
|
27665
|
+
return "Handle must be lowercase alphanumeric with hyphens, cannot start or end with hyphen";
|
|
27666
|
+
}
|
|
27667
|
+
if (name.includes("--")) {
|
|
27668
|
+
return "Handle cannot contain consecutive hyphens";
|
|
27669
|
+
}
|
|
27670
|
+
return null;
|
|
27671
|
+
}
|
|
27672
|
+
var claimCommand = defineCommand({
|
|
27673
|
+
meta: {
|
|
27674
|
+
name: "claim",
|
|
27675
|
+
description: "Claim a new handle"
|
|
27676
|
+
},
|
|
27677
|
+
args: {
|
|
27678
|
+
name: {
|
|
27679
|
+
type: "positional",
|
|
27680
|
+
description: "Handle name to claim (e.g., myhandle)",
|
|
27681
|
+
required: true
|
|
27682
|
+
},
|
|
27683
|
+
"display-name": {
|
|
27684
|
+
type: "string",
|
|
27685
|
+
description: "Display name with preferred casing"
|
|
27686
|
+
}
|
|
27687
|
+
},
|
|
27688
|
+
async run({ args }) {
|
|
27689
|
+
const auth = await getAuth();
|
|
27690
|
+
if (!auth) {
|
|
27691
|
+
console.error(`${icons2.error} Not logged in. Run ${c3.cmd("aspects login")} first.`);
|
|
27692
|
+
process.exit(1);
|
|
27693
|
+
}
|
|
27694
|
+
const name = args.name.toLowerCase();
|
|
27695
|
+
const displayName = args["display-name"];
|
|
27696
|
+
const formatError2 = validateHandleFormat(name);
|
|
27697
|
+
if (formatError2) {
|
|
27698
|
+
console.error(`${icons2.error} ${formatError2}`);
|
|
27699
|
+
process.exit(1);
|
|
27700
|
+
}
|
|
27701
|
+
try {
|
|
27702
|
+
console.log(`${icons2.working} Claiming @${name}...`);
|
|
27703
|
+
const result = await claimHandle(name, displayName);
|
|
27704
|
+
const account = await getAccount();
|
|
27705
|
+
await updateHandles(account.handles);
|
|
27706
|
+
console.log(`${icons2.success} Claimed @${result.name}`);
|
|
27707
|
+
console.log();
|
|
27708
|
+
console.log(` You can now publish aspects under this handle.`);
|
|
27709
|
+
console.log(` Use ${c3.cmd(`aspects handle default ${name}`)} to make it your default.`);
|
|
27710
|
+
} catch (err) {
|
|
27711
|
+
if (err instanceof ApiClientError) {
|
|
27712
|
+
switch (err.errorCode) {
|
|
27713
|
+
case "invalid_format":
|
|
27714
|
+
console.error(`${icons2.error} Invalid handle format: ${err.message}`);
|
|
27715
|
+
break;
|
|
27716
|
+
case "handle_taken":
|
|
27717
|
+
console.error(`${icons2.error} @${name} is already taken`);
|
|
27718
|
+
break;
|
|
27719
|
+
case "handle_reserved":
|
|
27720
|
+
console.error(`${icons2.error} @${name} is reserved`);
|
|
27721
|
+
break;
|
|
27722
|
+
case "handle_limit":
|
|
27723
|
+
console.error(`${icons2.error} You've reached the maximum of 5 owned handles`);
|
|
27724
|
+
console.log(` You can be a member of unlimited handles owned by others.`);
|
|
27725
|
+
break;
|
|
27726
|
+
case "rate_limit":
|
|
27727
|
+
console.error(`${icons2.error} Rate limit: max 3 handle claims per 30 days`);
|
|
27728
|
+
break;
|
|
27729
|
+
default:
|
|
27730
|
+
console.error(`${icons2.error} ${err.message}`);
|
|
27731
|
+
}
|
|
27732
|
+
process.exit(1);
|
|
27733
|
+
}
|
|
27734
|
+
throw err;
|
|
27735
|
+
}
|
|
27736
|
+
}
|
|
27737
|
+
});
|
|
27738
|
+
var listCommand3 = defineCommand({
|
|
27739
|
+
meta: {
|
|
27740
|
+
name: "list",
|
|
27741
|
+
description: "List your handles"
|
|
27742
|
+
},
|
|
27743
|
+
args: {
|
|
27744
|
+
refresh: {
|
|
27745
|
+
type: "boolean",
|
|
27746
|
+
description: "Refresh from API",
|
|
27747
|
+
default: false
|
|
27748
|
+
}
|
|
27749
|
+
},
|
|
27750
|
+
async run({ args }) {
|
|
27751
|
+
const auth = await getAuth();
|
|
27752
|
+
if (!auth) {
|
|
27753
|
+
console.error(`${icons2.error} Not logged in. Run ${c3.cmd("aspects login")} first.`);
|
|
27754
|
+
process.exit(1);
|
|
27755
|
+
}
|
|
27756
|
+
let handles = await getHandles();
|
|
27757
|
+
if (args.refresh) {
|
|
27758
|
+
try {
|
|
27759
|
+
const account = await getAccount();
|
|
27760
|
+
await updateHandles(account.handles);
|
|
27761
|
+
handles = account.handles;
|
|
27762
|
+
} catch (err) {
|
|
27763
|
+
console.error(`${icons2.warn} Could not refresh from API: ${err.message}`);
|
|
27764
|
+
}
|
|
27765
|
+
}
|
|
27766
|
+
if (handles.length === 0) {
|
|
27767
|
+
console.log(`${icons2.info} You don't have any handles yet.`);
|
|
27768
|
+
console.log(` Run ${c3.cmd("aspects handle claim <name>")} to claim one.`);
|
|
27769
|
+
return;
|
|
27770
|
+
}
|
|
27771
|
+
console.log();
|
|
27772
|
+
console.log(`${icons2.info} Your Handles`);
|
|
27773
|
+
console.log();
|
|
27774
|
+
for (const handle of handles) {
|
|
27775
|
+
const defaultTag = handle.default ? c3.success(" (default)") : "";
|
|
27776
|
+
const roleTag = c3.dim(` ${handle.role}`);
|
|
27777
|
+
console.log(` @${handle.name}${roleTag}${defaultTag}`);
|
|
27778
|
+
}
|
|
27779
|
+
const ownedCount = handles.filter((h4) => h4.role === "owner").length;
|
|
27780
|
+
console.log();
|
|
27781
|
+
console.log(` ${c3.dim(`${ownedCount}/5 owned handles`)}`);
|
|
27782
|
+
console.log();
|
|
27783
|
+
}
|
|
27784
|
+
});
|
|
27785
|
+
var defaultCommand = defineCommand({
|
|
27786
|
+
meta: {
|
|
27787
|
+
name: "default",
|
|
27788
|
+
description: "Set your default publishing handle"
|
|
27789
|
+
},
|
|
27790
|
+
args: {
|
|
27791
|
+
name: {
|
|
27792
|
+
type: "positional",
|
|
27793
|
+
description: "Handle to set as default",
|
|
27794
|
+
required: true
|
|
27795
|
+
}
|
|
27796
|
+
},
|
|
27797
|
+
async run({ args }) {
|
|
27798
|
+
const auth = await getAuth();
|
|
27799
|
+
if (!auth) {
|
|
27800
|
+
console.error(`${icons2.error} Not logged in. Run ${c3.cmd("aspects login")} first.`);
|
|
27801
|
+
process.exit(1);
|
|
27802
|
+
}
|
|
27803
|
+
const name = args.name.toLowerCase().replace(/^@/, "");
|
|
27804
|
+
const handles = await getHandles();
|
|
27805
|
+
const hasHandle = handles.some((h4) => h4.name === name);
|
|
27806
|
+
if (!hasHandle) {
|
|
27807
|
+
console.error(`${icons2.error} You don't have access to @${name}`);
|
|
27808
|
+
console.log();
|
|
27809
|
+
console.log(" Your handles:");
|
|
27810
|
+
for (const h4 of handles) {
|
|
27811
|
+
console.log(` @${h4.name}`);
|
|
27812
|
+
}
|
|
27813
|
+
process.exit(1);
|
|
27814
|
+
}
|
|
27815
|
+
try {
|
|
27816
|
+
await setDefaultHandleApi(name);
|
|
27817
|
+
await setDefaultHandle(name);
|
|
27818
|
+
console.log(`${icons2.success} Default handle set to @${name}`);
|
|
27819
|
+
} catch (err) {
|
|
27820
|
+
if (err instanceof ApiClientError) {
|
|
27821
|
+
console.error(`${icons2.error} ${err.message}`);
|
|
27822
|
+
process.exit(1);
|
|
27823
|
+
}
|
|
27824
|
+
throw err;
|
|
27825
|
+
}
|
|
27826
|
+
}
|
|
27827
|
+
});
|
|
27828
|
+
var checkCommand = defineCommand({
|
|
27829
|
+
meta: {
|
|
27830
|
+
name: "check",
|
|
27831
|
+
description: "Check if a handle is available"
|
|
27832
|
+
},
|
|
27833
|
+
args: {
|
|
27834
|
+
name: {
|
|
27835
|
+
type: "positional",
|
|
27836
|
+
description: "Handle name to check",
|
|
27837
|
+
required: true
|
|
27838
|
+
}
|
|
27839
|
+
},
|
|
27840
|
+
async run({ args }) {
|
|
27841
|
+
const name = args.name.toLowerCase().replace(/^@/, "");
|
|
27842
|
+
const formatError2 = validateHandleFormat(name);
|
|
27843
|
+
if (formatError2) {
|
|
27844
|
+
console.error(`${icons2.error} ${formatError2}`);
|
|
27845
|
+
process.exit(1);
|
|
27846
|
+
}
|
|
27847
|
+
try {
|
|
27848
|
+
const result = await checkHandleAvailability(name);
|
|
27849
|
+
if (result.available) {
|
|
27850
|
+
console.log(`${icons2.success} @${name} is available`);
|
|
27851
|
+
} else {
|
|
27852
|
+
console.log(`${icons2.error} @${name} is not available`);
|
|
27853
|
+
if (result.reason) {
|
|
27854
|
+
switch (result.reason) {
|
|
27855
|
+
case "taken":
|
|
27856
|
+
console.log(` This handle is already claimed.`);
|
|
27857
|
+
break;
|
|
27858
|
+
case "reserved":
|
|
27859
|
+
console.log(` This handle is reserved.`);
|
|
27860
|
+
break;
|
|
27861
|
+
case "invalid":
|
|
27862
|
+
console.log(` This handle format is not allowed.`);
|
|
27863
|
+
break;
|
|
27864
|
+
default:
|
|
27865
|
+
console.log(` Reason: ${result.reason}`);
|
|
27866
|
+
}
|
|
27867
|
+
}
|
|
27868
|
+
}
|
|
27869
|
+
} catch (err) {
|
|
27870
|
+
if (err instanceof ApiClientError) {
|
|
27871
|
+
console.error(`${icons2.error} ${err.message}`);
|
|
27872
|
+
process.exit(1);
|
|
27873
|
+
}
|
|
27874
|
+
throw err;
|
|
27875
|
+
}
|
|
27876
|
+
}
|
|
27877
|
+
});
|
|
27878
|
+
var handle_default = defineCommand({
|
|
27879
|
+
meta: {
|
|
27880
|
+
name: "handle",
|
|
27881
|
+
description: "Manage your handles (namespaces for publishing)"
|
|
27882
|
+
},
|
|
27883
|
+
subCommands: {
|
|
27884
|
+
claim: claimCommand,
|
|
27885
|
+
list: listCommand3,
|
|
27886
|
+
default: defaultCommand,
|
|
27887
|
+
check: checkCommand
|
|
27888
|
+
}
|
|
27889
|
+
});
|
|
27890
|
+
|
|
27891
|
+
// src/commands/whoami.ts
|
|
27892
|
+
var whoami_default = defineCommand({
|
|
27893
|
+
meta: {
|
|
27894
|
+
name: "whoami",
|
|
27895
|
+
description: "Show your current identity"
|
|
27896
|
+
},
|
|
27897
|
+
async run() {
|
|
27898
|
+
const auth = await getAuth();
|
|
27899
|
+
if (!auth) {
|
|
27900
|
+
console.log(`${icons2.info} Not logged in`);
|
|
27901
|
+
console.log(` Run ${c3.cmd("aspects login")} to authenticate.`);
|
|
27902
|
+
return;
|
|
27903
|
+
}
|
|
27904
|
+
const defaultHandle = await getDefaultHandle();
|
|
27905
|
+
const handles = await getHandles();
|
|
27906
|
+
console.log();
|
|
27907
|
+
if (defaultHandle) {
|
|
27908
|
+
console.log(` ${c3.bold(`@${defaultHandle}`)} ${c3.dim("(default)")}`);
|
|
27909
|
+
} else if (handles.length === 0) {
|
|
27910
|
+
console.log(` ${c3.warn("No handles claimed")}`);
|
|
27911
|
+
console.log(` Run ${c3.cmd("aspects handle claim <name>")} to claim a handle.`);
|
|
27912
|
+
return;
|
|
27913
|
+
}
|
|
27914
|
+
if (handles.length > 0) {
|
|
27915
|
+
console.log();
|
|
27916
|
+
console.log(` ${c3.bold("Handles")}`);
|
|
27917
|
+
for (const handle of handles) {
|
|
27918
|
+
const isDefault = handle.name === defaultHandle;
|
|
27919
|
+
const roleColor = handle.role === "owner" ? c3.success : c3.dim;
|
|
27920
|
+
const defaultIndicator = isDefault ? c3.success(" *") : "";
|
|
27921
|
+
console.log(` @${handle.name} ${roleColor(handle.role)}${defaultIndicator}`);
|
|
27922
|
+
}
|
|
27923
|
+
}
|
|
27924
|
+
const ownedCount = handles.filter((h4) => h4.role === "owner").length;
|
|
27925
|
+
console.log();
|
|
27926
|
+
console.log(` ${c3.dim(`${ownedCount}/5 owned handles`)}`);
|
|
27927
|
+
const expiresAt = new Date(auth.expiresAt);
|
|
27928
|
+
const isExpired = expiresAt < new Date;
|
|
27929
|
+
if (isExpired) {
|
|
27930
|
+
console.log(` ${c3.warn("Token expired")} - run ${c3.cmd("aspects login")} to refresh`);
|
|
27931
|
+
}
|
|
27932
|
+
console.log();
|
|
27933
|
+
}
|
|
27934
|
+
});
|
|
27935
|
+
|
|
26702
27936
|
// src/cli.ts
|
|
27937
|
+
var VERSION = package_default.version;
|
|
26703
27938
|
var ALIASES = {
|
|
26704
27939
|
c: "create",
|
|
26705
27940
|
new: "create",
|
|
@@ -26708,7 +27943,8 @@ var ALIASES = {
|
|
|
26708
27943
|
get: "add",
|
|
26709
27944
|
a: "add",
|
|
26710
27945
|
i: "add",
|
|
26711
|
-
g: "add"
|
|
27946
|
+
g: "add",
|
|
27947
|
+
h: "handle"
|
|
26712
27948
|
};
|
|
26713
27949
|
var ALIAS_HINTS = {};
|
|
26714
27950
|
for (const [alias, canonical] of Object.entries(ALIASES)) {
|
|
@@ -26718,7 +27954,14 @@ var cmdArg = process.argv[2];
|
|
|
26718
27954
|
if (cmdArg && ALIASES[cmdArg]) {
|
|
26719
27955
|
process.argv[2] = ALIASES[cmdArg];
|
|
26720
27956
|
}
|
|
27957
|
+
if (process.argv[2] === "config" && !process.argv[3]) {
|
|
27958
|
+
process.argv.splice(3, 0, "list");
|
|
27959
|
+
}
|
|
27960
|
+
if (process.argv[2] === "handle" && !process.argv[3]) {
|
|
27961
|
+
process.argv.splice(3, 0, "list");
|
|
27962
|
+
}
|
|
26721
27963
|
var COMMANDS = [
|
|
27964
|
+
{ name: "init", cmd: init_default, desc: "Initialize project for local aspects" },
|
|
26722
27965
|
{ name: "create", cmd: create_default, desc: "Create a new aspect interactively", aliases: ["c", "new", "n"] },
|
|
26723
27966
|
{ name: "add", cmd: add_default, desc: "Install aspect(s) to your local library", aliases: ["install", "get", "a", "i", "g"] },
|
|
26724
27967
|
{ name: "list", cmd: list_default, desc: "List installed aspects" },
|
|
@@ -26736,14 +27979,42 @@ var COMMANDS = [
|
|
|
26736
27979
|
{ name: "share", cmd: share_default, desc: "Share an aspect via content hash" },
|
|
26737
27980
|
{ name: "unpublish", cmd: unpublish_default, desc: "Unpublish a version from the registry" },
|
|
26738
27981
|
{ name: "login", cmd: login_default, desc: "Authenticate with the registry" },
|
|
26739
|
-
{ name: "logout", cmd: logout_default, desc: "Clear stored authentication tokens" }
|
|
27982
|
+
{ name: "logout", cmd: logout_default, desc: "Clear stored authentication tokens" },
|
|
27983
|
+
{ name: "handle", cmd: handle_default, desc: "Manage your handles (namespaces)", aliases: ["h"] },
|
|
27984
|
+
{ name: "whoami", cmd: whoami_default, desc: "Show your current identity" },
|
|
27985
|
+
{ name: "config", cmd: config_default, desc: "View and modify configuration" }
|
|
26740
27986
|
];
|
|
26741
27987
|
function showCustomHelp() {
|
|
26742
27988
|
morphistBanner();
|
|
26743
|
-
console.log(import_picocolors5.default.dim(`Package manager for AI personality aspects (
|
|
27989
|
+
console.log(import_picocolors5.default.dim(`Package manager for AI personality aspects (v${VERSION})`));
|
|
26744
27990
|
console.log();
|
|
26745
27991
|
console.log(`${import_picocolors5.default.bold(import_picocolors5.default.underline("USAGE"))} ${import_picocolors5.default.cyan("aspects")} ${import_picocolors5.default.dim("<command>")} ${import_picocolors5.default.dim("[options]")}`);
|
|
26746
27992
|
console.log();
|
|
27993
|
+
console.log(import_picocolors5.default.bold(import_picocolors5.default.underline("QUICK START")));
|
|
27994
|
+
console.log();
|
|
27995
|
+
console.log(` ${import_picocolors5.default.cyan("aspects add alaric")} Install an aspect from the registry`);
|
|
27996
|
+
console.log(` ${import_picocolors5.default.cyan("aspects create")} Create a new aspect interactively`);
|
|
27997
|
+
console.log(` ${import_picocolors5.default.cyan("aspects search wizard")} Search for aspects`);
|
|
27998
|
+
console.log();
|
|
27999
|
+
console.log(import_picocolors5.default.bold(import_picocolors5.default.underline("EXAMPLE: CREATE & SHARE (NO ACCOUNT)")));
|
|
28000
|
+
console.log();
|
|
28001
|
+
console.log(` ${import_picocolors5.default.dim("1.")} ${import_picocolors5.default.cyan("aspects create my-aspect")} Create aspect interactively`);
|
|
28002
|
+
console.log(` ${import_picocolors5.default.dim("2.")} ${import_picocolors5.default.dim("Edit")} ./my-aspect/aspect.json ${import_picocolors5.default.dim("(customize prompt, add directives)")}`);
|
|
28003
|
+
console.log(` ${import_picocolors5.default.dim("3.")} ${import_picocolors5.default.cyan("aspects share ./my-aspect")} Share to registry, get hash`);
|
|
28004
|
+
console.log(` ${import_picocolors5.default.dim("4.")} ${import_picocolors5.default.dim("Share hash with others:")} ${import_picocolors5.default.cyan("aspects add blake3:<hash>")}`);
|
|
28005
|
+
console.log();
|
|
28006
|
+
console.log(import_picocolors5.default.bold(import_picocolors5.default.underline("PUBLISHING")));
|
|
28007
|
+
console.log();
|
|
28008
|
+
console.log(` ${import_picocolors5.default.cyan("aspects share")} ${import_picocolors5.default.dim("No account")} - Share via content hash`);
|
|
28009
|
+
console.log(` Anyone installs with: ${import_picocolors5.default.cyan("aspects add blake3:<hash>")}`);
|
|
28010
|
+
console.log(` Quick, anonymous, no versioning`);
|
|
28011
|
+
console.log();
|
|
28012
|
+
console.log(` ${import_picocolors5.default.cyan("aspects publish")} ${import_picocolors5.default.dim("Account required")} - Claim a name`);
|
|
28013
|
+
console.log(` Anyone installs with: ${import_picocolors5.default.cyan("aspects add <name>")}`);
|
|
28014
|
+
console.log(` Versioning, updates, publisher reputation`);
|
|
28015
|
+
console.log();
|
|
28016
|
+
console.log(` ${import_picocolors5.default.cyan("aspects login")} Create account or authenticate`);
|
|
28017
|
+
console.log();
|
|
26747
28018
|
console.log(import_picocolors5.default.bold(import_picocolors5.default.underline("COMMANDS")));
|
|
26748
28019
|
console.log();
|
|
26749
28020
|
const maxNameLen = Math.max(...COMMANDS.map((c5) => c5.name.length));
|
|
@@ -26753,7 +28024,15 @@ function showCustomHelp() {
|
|
|
26753
28024
|
console.log(` ${import_picocolors5.default.cyan(paddedName)} ${desc}${aliasPart}`);
|
|
26754
28025
|
}
|
|
26755
28026
|
console.log();
|
|
26756
|
-
console.log(
|
|
28027
|
+
console.log(import_picocolors5.default.bold(import_picocolors5.default.underline("CONCEPTS")));
|
|
28028
|
+
console.log();
|
|
28029
|
+
console.log(` ${import_picocolors5.default.bold("Directives")} Strict MUST-follow rules with priority levels`);
|
|
28030
|
+
console.log(` Emphasized across all LLM models (XML, bold, repetition)`);
|
|
28031
|
+
console.log();
|
|
28032
|
+
console.log(` ${import_picocolors5.default.bold("Instructions")} Softer guidance and preferences`);
|
|
28033
|
+
console.log(` General behavioral hints, not strictly enforced`);
|
|
28034
|
+
console.log();
|
|
28035
|
+
console.log(`Use ${import_picocolors5.default.cyan("aspects <command> --help")} for detailed command help.`);
|
|
26757
28036
|
console.log();
|
|
26758
28037
|
}
|
|
26759
28038
|
var showingHelp = process.argv.length === 2 || process.argv.length === 3 && (process.argv[2] === "--help" || process.argv[2] === "-h");
|
|
@@ -26768,7 +28047,7 @@ for (const { name, cmd } of COMMANDS) {
|
|
|
26768
28047
|
var main = defineCommand({
|
|
26769
28048
|
meta: {
|
|
26770
28049
|
name: "aspects",
|
|
26771
|
-
version:
|
|
28050
|
+
version: VERSION,
|
|
26772
28051
|
description: "Package manager for AI personality aspects"
|
|
26773
28052
|
},
|
|
26774
28053
|
subCommands
|