@skillcdn/console 0.1.23 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/api.d.ts +1 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +1 -1
- package/dist/api.js.map +1 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +80 -54
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/format.d.ts +4 -28
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +7 -260
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/help.d.ts +6 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +47 -24
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/pack.d.ts +46 -0
- package/dist/cli/pack.d.ts.map +1 -0
- package/dist/cli/pack.js +250 -0
- package/dist/cli/pack.js.map +1 -0
- package/dist/compat.d.ts +1 -1
- package/dist/compat.js +1 -1
- package/dist/components/decision-list.d.ts +13 -1
- package/dist/components/decision-list.d.ts.map +1 -1
- package/dist/components/decision-list.js +74 -29
- package/dist/components/decision-list.js.map +1 -1
- package/dist/components/paged.d.ts +24 -0
- package/dist/components/paged.d.ts.map +1 -1
- package/dist/components/paged.js +40 -4
- package/dist/components/paged.js.map +1 -1
- package/dist/components/skills.d.ts.map +1 -1
- package/dist/components/skills.js +8 -4
- package/dist/components/skills.js.map +1 -1
- package/dist/components/ui.d.ts +2 -0
- package/dist/components/ui.d.ts.map +1 -1
- package/dist/components/ui.js +7 -2
- package/dist/components/ui.js.map +1 -1
- package/dist/components/work.d.ts +2 -0
- package/dist/components/work.d.ts.map +1 -1
- package/dist/components/work.js +3 -2
- package/dist/components/work.js.map +1 -1
- package/dist/console.css +112 -5
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +5 -3
- package/dist/console.js.map +1 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +27 -2
- package/dist/data.js.map +1 -1
- package/dist/i18n/en.d.ts +13 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +15 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/ko.d.ts.map +1 -1
- package/dist/i18n/ko.js +15 -0
- package/dist/i18n/ko.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/router.d.ts +6 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +13 -7
- package/dist/router.js.map +1 -1
- package/dist/rules.js +3 -3
- package/dist/rules.js.map +1 -1
- package/dist/schemas.d.ts +557 -162
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +72 -158
- package/dist/schemas.js.map +1 -1
- package/docs/specs/cli.md +12 -12
- package/docs/specs/rest.md +7 -15
- package/package.json +2 -1
- package/skills/deploying-a-console/SKILL.md +2 -2
- package/skills/working-the-board/SKILL.md +9 -14
- package/skills/working-the-board/references/setting-up.md +3 -0
- package/skills/working-the-board/references/terms.md +9 -0
- package/skills/working-the-board/references/the-sentence.md +5 -0
- package/src/api.ts +2 -7
- package/src/cli/cli.ts +113 -62
- package/src/cli/format.ts +10 -324
- package/src/cli/help.ts +53 -27
- package/src/cli/pack.ts +356 -0
- package/src/compat.ts +1 -1
- package/src/components/decision-list.tsx +227 -94
- package/src/components/paged.tsx +72 -3
- package/src/components/skills.tsx +51 -33
- package/src/components/ui.tsx +16 -3
- package/src/components/work.tsx +9 -5
- package/src/console.tsx +8 -2
- package/src/data.ts +32 -2
- package/src/i18n/en.ts +18 -0
- package/src/i18n/ko.ts +17 -0
- package/src/index.ts +9 -1
- package/src/router.ts +20 -9
- package/src/rules.ts +3 -3
- package/src/schemas.ts +87 -184
- package/src/styles/console.css +112 -5
package/src/cli/format.ts
CHANGED
|
@@ -15,13 +15,6 @@ import type {
|
|
|
15
15
|
RestProjectRules,
|
|
16
16
|
RestRules,
|
|
17
17
|
RestRun,
|
|
18
|
-
RestSkillBrowse,
|
|
19
|
-
RestSkillBrowseEntry,
|
|
20
|
-
RestSkillFile,
|
|
21
|
-
RestSkillFind,
|
|
22
|
-
RestSkillLoad,
|
|
23
|
-
RestSkillPage,
|
|
24
|
-
RestSkills,
|
|
25
18
|
RestTask,
|
|
26
19
|
RestVersion,
|
|
27
20
|
RestVersionSummary,
|
|
@@ -247,66 +240,6 @@ export function formatArtifact(
|
|
|
247
240
|
return artifact.label === null ? what : `${artifact.label}: ${what}`;
|
|
248
241
|
}
|
|
249
242
|
|
|
250
|
-
const SKILLS_WORDS: Readonly<Record<Exclude<RestSkills["status"], "none" | "ready">, string>> = {
|
|
251
|
-
indexing: "SkillCDN is still indexing the repository; ask again in a moment.",
|
|
252
|
-
failed: "SkillCDN could not index the repository.",
|
|
253
|
-
not_found:
|
|
254
|
-
"SkillCDN does not serve this address to the console: nothing is there, or the pack is private and the project holds no pack token for it (console project pack-token <key> --stdin, for an owner).",
|
|
255
|
-
token_invalid:
|
|
256
|
-
"The pack token the project holds no longer works at SkillCDN: an owner sets a new one (console project pack-token <key> --stdin).",
|
|
257
|
-
unavailable: "SkillCDN could not be reached; ask again later.",
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
/** A count with its noun: `1 skill`, `2 skills`. */
|
|
261
|
-
const counted = (n: number, noun: string): string => `${n} ${noun}${n === 1 ? "" : "s"}`;
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* The organization's skills: where they are, the pack by name and by count, each skill with
|
|
265
|
-
* what an agent loads it by, where a person connects an agent that has no connection to the
|
|
266
|
-
* address, and where the console's own skills are on this machine, when the command knows.
|
|
267
|
-
*/
|
|
268
|
-
export function formatSkills(skills: RestSkills, ownSkills?: string): string {
|
|
269
|
-
const own =
|
|
270
|
-
ownSkills === undefined
|
|
271
|
-
? []
|
|
272
|
-
: ["", `The console's own skills, for working the board: ${ownSkills}`];
|
|
273
|
-
if (skills.status === "none" || skills.address === null || skills.page === null) {
|
|
274
|
-
return ["No skills address: neither this project nor the console names one.", ...own].join(
|
|
275
|
-
"\n",
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
|
-
const head = `skills at ${skills.address}, served by ${skills.source}: ${skills.status}`;
|
|
279
|
-
if (skills.status !== "ready") {
|
|
280
|
-
return [head, SKILLS_WORDS[skills.status], ...own].join("\n");
|
|
281
|
-
}
|
|
282
|
-
const lines = [head];
|
|
283
|
-
if (skills.pack !== undefined) {
|
|
284
|
-
const named = [skills.pack.name, skills.pack.description]
|
|
285
|
-
.filter((text): text is string => text !== null)
|
|
286
|
-
.join(" ");
|
|
287
|
-
if (named !== "") {
|
|
288
|
-
lines.push(named);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
if (skills.counts !== undefined) {
|
|
292
|
-
lines.push(
|
|
293
|
-
`${counted(skills.counts.skills, "skill")}, ${counted(skills.counts.documents, "document")}: console skills <folder> shows what a folder holds (. for the root), console skills --search <words> finds skills and documents by words.`,
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
if (skills.items.length === 0) {
|
|
297
|
-
lines.push("No skills at this address.");
|
|
298
|
-
}
|
|
299
|
-
for (const skill of skills.items) {
|
|
300
|
-
lines.push(`${skill.name} ${skill.description}`, ` ${skill.uri ?? skill.path}`);
|
|
301
|
-
}
|
|
302
|
-
lines.push(
|
|
303
|
-
"",
|
|
304
|
-
`Read a skill whole with console skill <path>, its rules first, and a file of the pack with console skill --file <path>; or load it by its URI through your own SkillCDN connection to this address, which a person makes at ${skills.page}. --json adds the page of each, for a person.`,
|
|
305
|
-
...own,
|
|
306
|
-
);
|
|
307
|
-
return lines.join("\n");
|
|
308
|
-
}
|
|
309
|
-
|
|
310
243
|
/** One half of the rules: where it comes from, and the text as it stands. */
|
|
311
244
|
function formatRulesHalf(where: "the workspace" | "this project", rules: RestRules): string {
|
|
312
245
|
const page = rules.page;
|
|
@@ -413,19 +346,25 @@ export function formatDecisionLine(decision: RestDecision): string {
|
|
|
413
346
|
return `${decision.id} ${state} ${decision.question} (asked by ${asker(decision)})`;
|
|
414
347
|
}
|
|
415
348
|
|
|
349
|
+
/** The options, one a line; the person's own answer, when they wrote one, is the last and says so. */
|
|
416
350
|
export function formatOptions(decision: RestDecision): string {
|
|
417
|
-
return decision.options
|
|
351
|
+
return decision.options
|
|
352
|
+
.map(
|
|
353
|
+
(option) =>
|
|
354
|
+
` ${option.id}) ${option.label}${option.own === true ? " (the person's own answer, not one given)" : ""}`,
|
|
355
|
+
)
|
|
356
|
+
.join("\n");
|
|
418
357
|
}
|
|
419
358
|
|
|
420
|
-
/** The answer, with who gave it; or that there is none yet. */
|
|
359
|
+
/** The answer, with who gave it, and that it is in their own words when it is; or that there is none yet. */
|
|
421
360
|
export function formatAnswer(decision: RestDecision): string {
|
|
422
361
|
const { answer } = decision;
|
|
423
362
|
if (answer === null) {
|
|
424
363
|
return "waits for a person.";
|
|
425
364
|
}
|
|
426
|
-
const
|
|
365
|
+
const chosen = decision.options.find((option) => option.id === answer.option);
|
|
427
366
|
const lines = [
|
|
428
|
-
`Answered: ${answer.option}) ${label ?? answer.option}, by ${answer.by.login} at ${answer.at}`,
|
|
367
|
+
`Answered: ${answer.option}) ${chosen?.label ?? answer.option}${chosen?.own === true ? ", in their own words" : ""}, by ${answer.by.login} at ${answer.at}`,
|
|
429
368
|
];
|
|
430
369
|
if (answer.note !== null && answer.note.length > 0) {
|
|
431
370
|
lines.push(`Note: ${answer.note}`);
|
|
@@ -527,256 +466,3 @@ export function formatVersion(path: string, version: RestVersion): string {
|
|
|
527
466
|
}
|
|
528
467
|
|
|
529
468
|
/** The text of one path across the pages a skill came in: the fragments joined in order. */
|
|
530
|
-
function joinTexts(
|
|
531
|
-
pages: readonly RestSkillPage[],
|
|
532
|
-
pick: (page: RestSkillPage) => readonly { readonly path: string; readonly body: string }[],
|
|
533
|
-
): { readonly path: string; readonly body: string }[] {
|
|
534
|
-
const joined = new Map<string, string>();
|
|
535
|
-
for (const page of pages) {
|
|
536
|
-
for (const text of pick(page)) {
|
|
537
|
-
joined.set(text.path, (joined.get(text.path) ?? "") + text.body);
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
return [...joined.entries()].map(([path, body]) => ({ path, body }));
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
const NO_ADDRESS = "No skills address: neither this project nor the console names one.";
|
|
544
|
-
|
|
545
|
-
/**
|
|
546
|
-
* One skill, as load_skill lays it out for a model (ADR-0028): the rules from the repository's
|
|
547
|
-
* root to the nearest manifest, the body, the included files, each under a line that names its
|
|
548
|
-
* path; then the files the skill has and what it refers to. A skill described only is named and
|
|
549
|
-
* pointed at, and nothing of its body is shown.
|
|
550
|
-
*/
|
|
551
|
-
export function formatSkillLoad(loaded: RestSkillLoad, pages: readonly RestSkillPage[]): string {
|
|
552
|
-
if (loaded.status === "none" || loaded.address === null) {
|
|
553
|
-
return NO_ADDRESS;
|
|
554
|
-
}
|
|
555
|
-
const head = `skill at ${loaded.address}, served by ${loaded.source}: ${loaded.status}`;
|
|
556
|
-
if (loaded.status !== "ready") {
|
|
557
|
-
return [head, SKILLS_WORDS[loaded.status]].join("\n");
|
|
558
|
-
}
|
|
559
|
-
const [first] = pages;
|
|
560
|
-
if (first === undefined) {
|
|
561
|
-
return [head, "Nothing was read."].join("\n");
|
|
562
|
-
}
|
|
563
|
-
const last = pages[pages.length - 1] ?? first;
|
|
564
|
-
const lines = [
|
|
565
|
-
`${first.name} ${first.description}`,
|
|
566
|
-
`skill ${first.path} at ${loaded.address}, served by ${loaded.source}`,
|
|
567
|
-
];
|
|
568
|
-
if (first.serving.license !== null) {
|
|
569
|
-
lines.push(`license: ${first.serving.license}`);
|
|
570
|
-
}
|
|
571
|
-
if (!first.serving.full) {
|
|
572
|
-
lines.push(
|
|
573
|
-
"",
|
|
574
|
-
`Described only: the license keeps the skill at its source. Read it there: ${first.serving.sourceUrl ?? loaded.page ?? loaded.source}`,
|
|
575
|
-
);
|
|
576
|
-
return lines.join("\n");
|
|
577
|
-
}
|
|
578
|
-
for (const rule of joinTexts(pages, (page) => page.rules)) {
|
|
579
|
-
lines.push("", `--- applicable rules: ${rule.path} ---`, rule.body.trimEnd());
|
|
580
|
-
}
|
|
581
|
-
lines.push(
|
|
582
|
-
"",
|
|
583
|
-
`--- skill: ${first.path} ---`,
|
|
584
|
-
pages
|
|
585
|
-
.map((page) => page.body)
|
|
586
|
-
.join("")
|
|
587
|
-
.trimEnd(),
|
|
588
|
-
);
|
|
589
|
-
for (const file of joinTexts(pages, (page) => page.included)) {
|
|
590
|
-
lines.push("", `--- included file: ${file.path} ---`, file.body.trimEnd());
|
|
591
|
-
}
|
|
592
|
-
if (!last.complete) {
|
|
593
|
-
lines.push("", "The skill did not come whole: the deployment stopped before the end.");
|
|
594
|
-
}
|
|
595
|
-
if (last.files.length > 0) {
|
|
596
|
-
lines.push(
|
|
597
|
-
"",
|
|
598
|
-
`Files of the skill, read with console skill --file <path>: ${last.files.join(", ")}`,
|
|
599
|
-
);
|
|
600
|
-
}
|
|
601
|
-
if (last.references.length > 0) {
|
|
602
|
-
lines.push(
|
|
603
|
-
`References: ${last.references
|
|
604
|
-
.map((reference) =>
|
|
605
|
-
reference.status === "available"
|
|
606
|
-
? reference.path
|
|
607
|
-
: `${reference.path} (${reference.status})`,
|
|
608
|
-
)
|
|
609
|
-
.join(", ")}`,
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
if (last.warnings.length > 0) {
|
|
613
|
-
lines.push(`Warnings: ${last.warnings.join("; ")}`);
|
|
614
|
-
}
|
|
615
|
-
lines.push(
|
|
616
|
-
"",
|
|
617
|
-
"Paths from the repository's root are the only identifiers. The content is the pack's, under its license; what a private pack says stays in your work.",
|
|
618
|
-
);
|
|
619
|
-
return lines.join("\n");
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
/** A file of the pack as read: its text whole, or what the folder holds. */
|
|
623
|
-
export function formatSkillFile(read: RestSkillFile, content: string): string {
|
|
624
|
-
if (read.status === "none" || read.address === null) {
|
|
625
|
-
return NO_ADDRESS;
|
|
626
|
-
}
|
|
627
|
-
const head = `file at ${read.address}, served by ${read.source}: ${read.status}`;
|
|
628
|
-
if (read.status !== "ready") {
|
|
629
|
-
return [
|
|
630
|
-
head,
|
|
631
|
-
read.status === "failed"
|
|
632
|
-
? "SkillCDN will not carry this file: it is too large, or not text. Read it at the repository."
|
|
633
|
-
: SKILLS_WORDS[read.status],
|
|
634
|
-
].join("\n");
|
|
635
|
-
}
|
|
636
|
-
if (read.folder !== null) {
|
|
637
|
-
return [
|
|
638
|
-
`folder ${read.folder.path} at ${read.address}${read.folder.truncated ? " (cut)" : ""}`,
|
|
639
|
-
...read.folder.entries.map(
|
|
640
|
-
(entry) =>
|
|
641
|
-
`${entry.kind === "directory" ? "d" : "f"} ${entry.path}${entry.size === null ? "" : ` ${entry.size}`}`,
|
|
642
|
-
),
|
|
643
|
-
"",
|
|
644
|
-
"console skills <folder> shows a folder with its skills marked, what each folder holds and the README that introduces it.",
|
|
645
|
-
].join("\n");
|
|
646
|
-
}
|
|
647
|
-
if (read.file === null) {
|
|
648
|
-
return `${head}: nothing was read.`;
|
|
649
|
-
}
|
|
650
|
-
return [
|
|
651
|
-
`--- file: ${read.file.path} (${read.file.totalLength} characters) ---`,
|
|
652
|
-
content.trimEnd(),
|
|
653
|
-
].join("\n");
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
/** One entry of a browsed folder: what it is, its path, its words, and the facts of its kind. */
|
|
657
|
-
function formatBrowseEntry(entry: RestSkillBrowseEntry): string {
|
|
658
|
-
const facts: string[] = [];
|
|
659
|
-
if (entry.kind === "directory") {
|
|
660
|
-
if (entry.skillCount > 0) {
|
|
661
|
-
facts.push(counted(entry.skillCount, "skill"));
|
|
662
|
-
}
|
|
663
|
-
if (entry.documentCount > 0) {
|
|
664
|
-
facts.push(counted(entry.documentCount, "document"));
|
|
665
|
-
}
|
|
666
|
-
if (entry.overviewPath !== null) {
|
|
667
|
-
facts.push(`README ${entry.overviewPath}`);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
if (entry.kind === "skill") {
|
|
671
|
-
if (entry.license !== null) {
|
|
672
|
-
facts.push(entry.license);
|
|
673
|
-
}
|
|
674
|
-
if (entry.describedOnly) {
|
|
675
|
-
facts.push("described only");
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
if (entry.kind === "file" && entry.size !== null) {
|
|
679
|
-
facts.push(`${entry.size} bytes`);
|
|
680
|
-
}
|
|
681
|
-
const words = [entry.name, entry.description]
|
|
682
|
-
.filter((text): text is string => text !== null && text !== "")
|
|
683
|
-
.join(" ");
|
|
684
|
-
const mark = entry.kind === "skill" ? "skill" : entry.kind === "directory" ? "d" : "f";
|
|
685
|
-
return `${mark} ${entry.path}${words === "" ? "" : ` ${words}`}${facts.length === 0 ? "" : ` (${facts.join("; ")})`}`;
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
/**
|
|
689
|
-
* What a folder of the pack holds, as browse_repo lays it out: the README that introduces the
|
|
690
|
-
* folder, then its skills, its folders with what each holds, and its files, across the pages the
|
|
691
|
-
* listing came in.
|
|
692
|
-
*/
|
|
693
|
-
export function formatSkillBrowse(
|
|
694
|
-
browsed: RestSkillBrowse,
|
|
695
|
-
folders: readonly NonNullable<RestSkillBrowse["folder"]>[],
|
|
696
|
-
): string {
|
|
697
|
-
if (browsed.status === "none" || browsed.address === null) {
|
|
698
|
-
return NO_ADDRESS;
|
|
699
|
-
}
|
|
700
|
-
const head = `folder at ${browsed.address}, served by ${browsed.source}: ${browsed.status}`;
|
|
701
|
-
if (browsed.status !== "ready") {
|
|
702
|
-
return [head, SKILLS_WORDS[browsed.status]].join("\n");
|
|
703
|
-
}
|
|
704
|
-
const [first] = folders;
|
|
705
|
-
if (first === undefined) {
|
|
706
|
-
return [head, "Nothing was read."].join("\n");
|
|
707
|
-
}
|
|
708
|
-
const last = folders[folders.length - 1] ?? first;
|
|
709
|
-
const lines = [`folder ${first.path === "" ? "/" : first.path} at ${browsed.address}`];
|
|
710
|
-
if (first.overview !== null) {
|
|
711
|
-
const words = [first.overview.title, first.overview.description]
|
|
712
|
-
.filter((text): text is string => text !== null && text !== "")
|
|
713
|
-
.join(" ");
|
|
714
|
-
lines.push(` ${words === "" ? "" : `${words} `}(README: ${first.overview.path})`);
|
|
715
|
-
}
|
|
716
|
-
const entries = folders.flatMap((folder) => folder.entries);
|
|
717
|
-
if (entries.length === 0) {
|
|
718
|
-
lines.push("Nothing in this folder.");
|
|
719
|
-
}
|
|
720
|
-
for (const entry of entries) {
|
|
721
|
-
lines.push(formatBrowseEntry(entry));
|
|
722
|
-
}
|
|
723
|
-
if (last.nextCursor !== null) {
|
|
724
|
-
lines.push("More entries: the listing stopped before the end of the folder.");
|
|
725
|
-
}
|
|
726
|
-
lines.push(
|
|
727
|
-
"",
|
|
728
|
-
"Read a skill with console skill <path>; a file, or a README named above, with console skill --file <path>; a folder with console skills <folder>.",
|
|
729
|
-
);
|
|
730
|
-
return lines.join("\n");
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
/**
|
|
734
|
-
* What words found in the pack, as search_repo lays it out: skills with the files of theirs that
|
|
735
|
-
* matched, documents on their own, in relevance order; words, not meaning.
|
|
736
|
-
*/
|
|
737
|
-
export function formatSkillFind(found: RestSkillFind): string {
|
|
738
|
-
if (found.status === "none" || found.address === null) {
|
|
739
|
-
return NO_ADDRESS;
|
|
740
|
-
}
|
|
741
|
-
const head = `search at ${found.address}, served by ${found.source}: ${found.status}`;
|
|
742
|
-
if (found.status !== "ready") {
|
|
743
|
-
return [head, SKILLS_WORDS[found.status]].join("\n");
|
|
744
|
-
}
|
|
745
|
-
if (found.found === null) {
|
|
746
|
-
return [head, "Nothing was read."].join("\n");
|
|
747
|
-
}
|
|
748
|
-
const { query, path, items, nextCursor } = found.found;
|
|
749
|
-
const where = path === null || path === "" ? "" : ` within ${path}`;
|
|
750
|
-
const lines = [
|
|
751
|
-
`found for "${query}"${where} at ${found.address}: ${counted(items.length, "result")}`,
|
|
752
|
-
];
|
|
753
|
-
for (const item of items) {
|
|
754
|
-
if (item.kind === "skill") {
|
|
755
|
-
lines.push(
|
|
756
|
-
`skill ${item.path} ${item.name} ${item.description}${item.describedOnly ? " (described only)" : ""}`,
|
|
757
|
-
);
|
|
758
|
-
for (const file of item.files) {
|
|
759
|
-
lines.push(` file ${file.path}${file.title === null ? "" : ` ${file.title}`}`);
|
|
760
|
-
}
|
|
761
|
-
if (item.moreFiles > 0) {
|
|
762
|
-
lines.push(` and ${counted(item.moreFiles, "more file")}`);
|
|
763
|
-
}
|
|
764
|
-
} else {
|
|
765
|
-
lines.push(
|
|
766
|
-
`document ${item.path}${item.title === null ? "" : ` ${item.title}`}${item.summary === null ? "" : ` ${item.summary}`}`,
|
|
767
|
-
);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
if (nextCursor !== null) {
|
|
771
|
-
lines.push(
|
|
772
|
-
"More matched: narrow the words, or search within a folder (console skills <folder> --search <words>).",
|
|
773
|
-
);
|
|
774
|
-
}
|
|
775
|
-
if (items.length > 0) {
|
|
776
|
-
lines.push(
|
|
777
|
-
"",
|
|
778
|
-
"Read a skill with console skill <path>, a file or a document with console skill --file <path>.",
|
|
779
|
-
);
|
|
780
|
-
}
|
|
781
|
-
return lines.join("\n");
|
|
782
|
-
}
|
package/src/cli/help.ts
CHANGED
|
@@ -60,11 +60,21 @@ const COMMON_REFUSALS = [
|
|
|
60
60
|
"network: the console could not be reached at its address",
|
|
61
61
|
];
|
|
62
62
|
|
|
63
|
+
/** How a command on a run finds its run, and what it says when it cannot: the command's own refusals. */
|
|
64
|
+
const RUN_CHOICE = [
|
|
65
|
+
"run.none: no run of yours is open in the project; take a task first, or name a run with --run <id>",
|
|
66
|
+
"run.ambiguous: several runs of yours are open in the project; say which with --run <id>",
|
|
67
|
+
];
|
|
68
|
+
|
|
63
69
|
const RUN_REFUSALS = [
|
|
70
|
+
...RUN_CHOICE,
|
|
64
71
|
"run.not_yours: the run is another person's",
|
|
65
72
|
"run.over: the run has ended already",
|
|
66
73
|
];
|
|
67
74
|
|
|
75
|
+
/** What every command on a run says of the run it acts on. */
|
|
76
|
+
const ON_THE_RUN = "On your one open run in the project, or the one --run <id> names.";
|
|
77
|
+
|
|
68
78
|
const MARKDOWN = `Markdown, up to ${MAX_BODY_LENGTH} characters`;
|
|
69
79
|
|
|
70
80
|
const DOCUMENT_PATH = `a path of lowercase letters, digits and hyphens, with slashes between its folders (guides/onboarding), up to ${MAX_DOCUMENT_PATH_LENGTH} characters; it does not change once written`;
|
|
@@ -275,11 +285,12 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
275
285
|
],
|
|
276
286
|
},
|
|
277
287
|
skills: {
|
|
278
|
-
usage:
|
|
288
|
+
usage:
|
|
289
|
+
"skills | skills <folder> | skills --search <words> [<folder>] [--limit <n>] [--cursor <cursor>]",
|
|
279
290
|
synopsis:
|
|
280
291
|
"The project's pack: its skills; what a folder of it holds; or what words find in it.",
|
|
281
292
|
about:
|
|
282
|
-
"The project's pack,
|
|
293
|
+
"The project's pack, read by the console at the project's address, or the organization's: at a SkillCDN deployment, with the pack token the project holds where the pack is private (ADR-0028), or from a directory on the console's machine for a local address (ADR-0031). Alone: where the pack is and what a client is told of it on connect, then every skill with the path of its SKILL.md and every document, as a connection of your own would list them, and where the console's own skills are on this machine. With a folder (. for the root): what it holds, whole, as browse_repo lays it out for a model: its skills, its folders with what each holds and the README that introduces it, its files. With --search: what the words find, as search_repo does: skills first, with the files of theirs that matched, then documents, in relevance order; words, not meaning, in the language the pack is written in; within the folder when one is named; 10 unless --limit says otherwise, up to 25, and a nextCursor line says when more matched, which --cursor continues. The text is the one an MCP client's model is given, with the reads named as these commands. --json answers with the console's own JSON: what the pack answered, in the shapes of the main repository's REST API, the pages of a folder joined.",
|
|
283
294
|
limits: ["search: 1 to 500 characters; limit: 1 to 25"],
|
|
284
295
|
refusals: [
|
|
285
296
|
"status not_found: the pack is not served to the console; private, with no pack token on the project",
|
|
@@ -291,11 +302,11 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
291
302
|
synopsis:
|
|
292
303
|
"One skill of the pack whole, its rules first; or with --file a file of the pack, or what a folder holds.",
|
|
293
304
|
about:
|
|
294
|
-
"One skill of the project's pack, whole, as load_skill lays it out for
|
|
305
|
+
"One skill of the project's pack, whole, as load_skill lays it out for a model: the rules of the repository from its root to the nearest manifest, the skill's instructions, the files it includes, then its supporting files and what it refers to; the pages it came in are joined. Read by the console from the pack, with the pack token the project holds where the pack is private, so you need no connection of your own. <path> is the path of the SKILL.md from the repository's root, as console skills lists it, or the URI the pack names it by (skill://<host>/<owner>/<name>/<path>). --file <path> reads a file of the pack instead, whole, as read_repo_file does, or what a folder holds, and takes no skill's path beside it; console skills <folder> shows a folder with more. --json answers with what the pack answered, in the shapes of the main repository's REST API, the pages joined.",
|
|
295
306
|
refusals: [
|
|
296
307
|
"status not_found: the pack is not served to the console; private, with no pack token on the project",
|
|
297
308
|
"status token_invalid: the pack token the project holds no longer works; an owner sets a new one",
|
|
298
|
-
"status failed, for a file:
|
|
309
|
+
"status failed, for a file: the pack will not carry it, too large or not text",
|
|
299
310
|
],
|
|
300
311
|
},
|
|
301
312
|
rules: {
|
|
@@ -325,10 +336,11 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
325
336
|
],
|
|
326
337
|
},
|
|
327
338
|
report: {
|
|
328
|
-
usage:
|
|
339
|
+
usage:
|
|
340
|
+
"report <markdown> [--run <id>] | report --file <path> [--run <id>] | report - [--run <id>]",
|
|
341
|
+
overview: "report <markdown> | report --file <path> | report -",
|
|
329
342
|
synopsis: "Says how the work goes, in Markdown, on the open run.",
|
|
330
|
-
about:
|
|
331
|
-
"Says how the work goes, in Markdown: what was found, what was done, what is next. With - the report is read from standard input.",
|
|
343
|
+
about: `Says how the work goes, in Markdown: what was found, what was done, what is next. With - the report is read from standard input. ${ON_THE_RUN}`,
|
|
332
344
|
limits: [
|
|
333
345
|
`the report: ${MARKDOWN}, not blank`,
|
|
334
346
|
`at most ${MAX_REPORTS_PER_RUN} reports on one run`,
|
|
@@ -336,10 +348,10 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
336
348
|
refusals: [...RUN_REFUSALS, "run.too_many_reports: the run carries as many as one may"],
|
|
337
349
|
},
|
|
338
350
|
"hand-in": {
|
|
339
|
-
usage: "hand-in <https url | file path> [--label <words>]",
|
|
351
|
+
usage: "hand-in <https url | file path> [--label <words>] [--run <id>]",
|
|
352
|
+
overview: "hand-in <https url | file path> [--label <words>]",
|
|
340
353
|
synopsis: "Hands in what was made: an https link, or a file from this machine.",
|
|
341
|
-
about:
|
|
342
|
-
"Hands in what was made: a link (a branch, a pull request, a document, a page), or a file from this machine, which the console keeps and shows on the task; a picture is shown there small and large, and a report that names it as  shows it where the words are.",
|
|
354
|
+
about: `Hands in what was made: a link (a branch, a pull request, a document, a page), or a file from this machine, which the console keeps and shows on the task; a picture is shown there small and large, and a report that names it as  shows it where the words are. ${ON_THE_RUN}`,
|
|
343
355
|
limits: [
|
|
344
356
|
"a link: https only",
|
|
345
357
|
`a file: up to ${MAX_FILE_BYTES} bytes, not empty; it is called by its name, without the path`,
|
|
@@ -354,24 +366,25 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
354
366
|
},
|
|
355
367
|
ask: {
|
|
356
368
|
usage:
|
|
369
|
+
'ask "<question>" --option "<label>" --option "<label>" [...] [--body <markdown>|--file <path>] [--wait <seconds>] [--run <id>]',
|
|
370
|
+
overview:
|
|
357
371
|
'ask "<question>" --option "<label>" --option "<label>" [...] [--body <markdown>|--file <path>] [--wait <seconds>]',
|
|
358
372
|
synopsis:
|
|
359
373
|
"Raises a decision a person must make, with options, and waits a while for the answer.",
|
|
360
|
-
about:
|
|
361
|
-
"Raises a decision a person must make: the question, the options to choose from, and what they need to know. The run waits for the answer. The command waits 60 seconds for it unless --wait says otherwise, and exits with 3 if it is still to come: keep waiting with `console decision <id> --wait 100`, within what your shell gives one command. The decision's id is printed as soon as it is raised.",
|
|
374
|
+
about: `Raises a decision a person must make: the question, the options to choose from, and what they need to know. The run waits for the answer. The command waits 60 seconds for it unless --wait says otherwise, and exits with 3 if it is still to come: keep waiting with \`console decision <id> --wait 100\`, within what your shell gives one command. The decision's id is printed as soon as it is raised. The person chooses one of the options, or answers in their own words: the answer then names one more option, their words, marked own; act on it as given. ${ON_THE_RUN}`,
|
|
362
375
|
limits: [
|
|
363
376
|
`question: one line, up to ${MAX_QUESTION_LENGTH} characters`,
|
|
364
377
|
`options: ${MIN_OPTIONS} to ${MAX_OPTIONS}, each one line up to ${MAX_OPTION_LABEL_LENGTH} characters`,
|
|
365
378
|
`body: ${MARKDOWN}`,
|
|
366
379
|
],
|
|
367
|
-
refusals: ["decision.invalid_run: the run is not yours, or is over"],
|
|
380
|
+
refusals: [...RUN_CHOICE, "decision.invalid_run: the run is not yours, or is over"],
|
|
368
381
|
},
|
|
369
382
|
decision: {
|
|
370
383
|
usage: "decision <id> [--wait <seconds>] | decision <id> --outcome <markdown>|--file <path>",
|
|
371
384
|
synopsis:
|
|
372
385
|
"One decision in full; waits for its answer with --wait; writes what followed with --outcome.",
|
|
373
386
|
about:
|
|
374
|
-
"One decision, with its context, its options, its answer and what followed. With --wait, holds on for the answer that long, and exits with 3 if it is still to come. With --outcome, writes down what followed the decision, so that it reads as a record.",
|
|
387
|
+
"One decision, with its context, its options, its answer (one of the options, or the person's own words as one more, marked own) and what followed. With --wait, holds on for the answer that long, and exits with 3 if it is still to come. With --outcome, writes down what followed the decision, so that it reads as a record.",
|
|
375
388
|
limits: [`outcome: ${MARKDOWN}`],
|
|
376
389
|
refusals: ["decision.not_found: no decision has that id"],
|
|
377
390
|
},
|
|
@@ -381,17 +394,18 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
381
394
|
about: `The decisions on the board, the waiting first; --open keeps only those. A page at a time: ${DEFAULT_PAGE_LIMIT} unless --limit says otherwise, from --offset on.`,
|
|
382
395
|
},
|
|
383
396
|
finish: {
|
|
384
|
-
usage: 'finish [--summary "<markdown>"|--file <path>]',
|
|
397
|
+
usage: 'finish [--summary "<markdown>"|--file <path>] [--run <id>]',
|
|
398
|
+
overview: 'finish [--summary "<markdown>"|--file <path>]',
|
|
385
399
|
synopsis: "Ends the run: done and handed in; the task goes up for review.",
|
|
386
|
-
about:
|
|
387
|
-
"Ends the run: the work is done and handed in, and the task goes up for review. The summary says what was done and what is left; the result itself goes in a last report before, which may be much longer.",
|
|
400
|
+
about: `Ends the run: the work is done and handed in, and the task goes up for review. The summary says what was done and what is left; the result itself goes in a last report before, which may be much longer. ${ON_THE_RUN}`,
|
|
388
401
|
limits: [`summary: ${`Markdown, up to ${MAX_SUMMARY_LENGTH} characters`}`],
|
|
389
402
|
refusals: RUN_REFUSALS,
|
|
390
403
|
},
|
|
391
404
|
fail: {
|
|
392
|
-
usage: 'fail [--summary "<markdown>"|--file <path>]',
|
|
405
|
+
usage: 'fail [--summary "<markdown>"|--file <path>] [--run <id>]',
|
|
406
|
+
overview: 'fail [--summary "<markdown>"|--file <path>]',
|
|
393
407
|
synopsis: "Ends the run: the work could not be done.",
|
|
394
|
-
about:
|
|
408
|
+
about: `Ends the run: the work could not be done. The summary says why, and what is left. ${ON_THE_RUN}`,
|
|
395
409
|
limits: [`summary: Markdown, up to ${MAX_SUMMARY_LENGTH} characters`],
|
|
396
410
|
refusals: RUN_REFUSALS,
|
|
397
411
|
},
|
|
@@ -457,7 +471,7 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
457
471
|
usage: 'write <path> [--title "<title>"] --body <markdown>|--file <path> [--base <version>]',
|
|
458
472
|
synopsis: "Writes a page: a new one at a path, or a new version of the page there.",
|
|
459
473
|
about:
|
|
460
|
-
"Writes a page: the first version at a new path, or a new version of the page there. A link in the body whose destination is a page's path (guides/onboarding) refers to that page, and the page says what refers to it. Without --title the page keeps its title. With --base, the write is refused if the page has moved on since that version.",
|
|
474
|
+
"Writes a page: the first version at a new path, or a new version of the page there. The title is shown above the body, on the page and by console doc, so the body does not begin with it as a heading. A link in the body whose destination is a page's path (guides/onboarding) refers to that page, and the page says what refers to it. Without --title the page keeps its title. With --base, the write is refused if the page has moved on since that version.",
|
|
461
475
|
limits: [
|
|
462
476
|
`path: ${DOCUMENT_PATH}`,
|
|
463
477
|
`title: one line, up to ${MAX_TITLE_LENGTH} characters`,
|
|
@@ -510,13 +524,24 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
510
524
|
},
|
|
511
525
|
};
|
|
512
526
|
|
|
513
|
-
/**
|
|
514
|
-
|
|
527
|
+
/**
|
|
528
|
+
* Where the skill working-the-board is, among the console's own skills in `skillsDir`, with the
|
|
529
|
+
* separators the directory came with, so that the path reads as the machine writes paths.
|
|
530
|
+
*/
|
|
531
|
+
export function ownSkillPath(skillsDir: string): string {
|
|
532
|
+
const separator = skillsDir.includes("\\") ? "\\" : "/";
|
|
533
|
+
const base =
|
|
534
|
+
skillsDir.endsWith("/") || skillsDir.endsWith("\\") ? skillsDir : `${skillsDir}${separator}`;
|
|
535
|
+
return `${base}working-the-board${separator}SKILL.md`;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/** The first lines of the usage, with the version of the command and where its skill is, when known. */
|
|
539
|
+
const preamble = (version: string | undefined, skillsDir: string | undefined): string =>
|
|
515
540
|
`console${version === undefined ? "" : ` ${version}`}: the board of your organization's work with AI agents, from the command line.
|
|
516
541
|
You act as the person whose token you hold; everything you send is shown to people as text.
|
|
517
542
|
The rules of this organization come with the task: console take prints them; console rules reads them any time.
|
|
518
|
-
How the board is worked is the skill working-the-board, installed with this command
|
|
519
|
-
|
|
543
|
+
How the board is worked is the skill working-the-board, installed with this command; read it before the
|
|
544
|
+
first command of a run${skillsDir === undefined ? " (console whoami says where it is)" : `:\n${ownSkillPath(skillsDir)}`}
|
|
520
545
|
|
|
521
546
|
Usage: console <command> [arguments] [--json] [--url <origin>] [--project <key>]
|
|
522
547
|
A command in full, every form and flag ([flags] below), the limits the console holds it to and what
|
|
@@ -532,7 +557,8 @@ with several open, say which with --run <id>. A Markdown body given as - is read
|
|
|
532
557
|
console --version says which version of the command this is.
|
|
533
558
|
|
|
534
559
|
Exit codes: 0 done; 1 the console refused, or could not be reached (the reason is on stderr);
|
|
535
|
-
2 the command was not understood; 3 the decision still waits for a person
|
|
560
|
+
2 the command was not understood; 3 the decision still waits for a person; 4 the command and the
|
|
561
|
+
console do not go together, and stderr says which to update and how.
|
|
536
562
|
What the console refuses is printed as its code and its words. Everywhere:
|
|
537
563
|
${COMMON_REFUSALS.map((refusal) => ` ${refusal}`).join("\n")}
|
|
538
564
|
|
|
@@ -583,8 +609,8 @@ const SECTIONS: readonly { readonly title: string; readonly commands: readonly s
|
|
|
583
609
|
];
|
|
584
610
|
|
|
585
611
|
/** The overview, as `console help` prints it: every command in a line, naming the version when it is known. */
|
|
586
|
-
export function usage(version?: string): string {
|
|
587
|
-
const parts = [preamble(version)];
|
|
612
|
+
export function usage(version?: string, skillsDir?: string): string {
|
|
613
|
+
const parts = [preamble(version, skillsDir)];
|
|
588
614
|
for (const section of SECTIONS) {
|
|
589
615
|
parts.push(`${section.title}`);
|
|
590
616
|
for (const name of section.commands) {
|