@skillcdn/console 0.1.24 → 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.
Files changed (85) hide show
  1. package/README.md +6 -5
  2. package/dist/api.d.ts +1 -1
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +1 -1
  5. package/dist/api.js.map +1 -1
  6. package/dist/cli/cli.d.ts.map +1 -1
  7. package/dist/cli/cli.js +64 -65
  8. package/dist/cli/cli.js.map +1 -1
  9. package/dist/cli/format.d.ts +4 -28
  10. package/dist/cli/format.d.ts.map +1 -1
  11. package/dist/cli/format.js +7 -261
  12. package/dist/cli/format.js.map +1 -1
  13. package/dist/cli/help.d.ts.map +1 -1
  14. package/dist/cli/help.js +6 -6
  15. package/dist/cli/help.js.map +1 -1
  16. package/dist/cli/pack.d.ts +46 -0
  17. package/dist/cli/pack.d.ts.map +1 -0
  18. package/dist/cli/pack.js +250 -0
  19. package/dist/cli/pack.js.map +1 -0
  20. package/dist/compat.d.ts +1 -1
  21. package/dist/compat.js +1 -1
  22. package/dist/components/decision-list.d.ts +13 -1
  23. package/dist/components/decision-list.d.ts.map +1 -1
  24. package/dist/components/decision-list.js +74 -29
  25. package/dist/components/decision-list.js.map +1 -1
  26. package/dist/components/paged.d.ts +24 -0
  27. package/dist/components/paged.d.ts.map +1 -1
  28. package/dist/components/paged.js +40 -4
  29. package/dist/components/paged.js.map +1 -1
  30. package/dist/components/skills.d.ts.map +1 -1
  31. package/dist/components/skills.js +8 -4
  32. package/dist/components/skills.js.map +1 -1
  33. package/dist/components/ui.d.ts +2 -0
  34. package/dist/components/ui.d.ts.map +1 -1
  35. package/dist/components/ui.js +7 -2
  36. package/dist/components/ui.js.map +1 -1
  37. package/dist/console.css +112 -5
  38. package/dist/console.d.ts.map +1 -1
  39. package/dist/console.js +2 -2
  40. package/dist/console.js.map +1 -1
  41. package/dist/data.d.ts.map +1 -1
  42. package/dist/data.js +27 -2
  43. package/dist/data.js.map +1 -1
  44. package/dist/i18n/en.d.ts +13 -0
  45. package/dist/i18n/en.d.ts.map +1 -1
  46. package/dist/i18n/en.js +15 -0
  47. package/dist/i18n/en.js.map +1 -1
  48. package/dist/i18n/ko.d.ts.map +1 -1
  49. package/dist/i18n/ko.js +15 -0
  50. package/dist/i18n/ko.js.map +1 -1
  51. package/dist/index.d.ts +2 -2
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +2 -2
  54. package/dist/index.js.map +1 -1
  55. package/dist/router.d.ts +6 -1
  56. package/dist/router.d.ts.map +1 -1
  57. package/dist/router.js +13 -7
  58. package/dist/router.js.map +1 -1
  59. package/dist/schemas.d.ts +321 -159
  60. package/dist/schemas.d.ts.map +1 -1
  61. package/dist/schemas.js +61 -154
  62. package/dist/schemas.js.map +1 -1
  63. package/docs/specs/cli.md +3 -3
  64. package/docs/specs/rest.md +5 -13
  65. package/package.json +2 -1
  66. package/skills/deploying-a-console/SKILL.md +2 -2
  67. package/skills/working-the-board/SKILL.md +3 -3
  68. package/src/api.ts +2 -7
  69. package/src/cli/cli.ts +92 -76
  70. package/src/cli/format.ts +10 -328
  71. package/src/cli/help.ts +7 -6
  72. package/src/cli/pack.ts +356 -0
  73. package/src/compat.ts +1 -1
  74. package/src/components/decision-list.tsx +227 -94
  75. package/src/components/paged.tsx +72 -3
  76. package/src/components/skills.tsx +51 -33
  77. package/src/components/ui.tsx +16 -3
  78. package/src/console.tsx +3 -0
  79. package/src/data.ts +32 -2
  80. package/src/i18n/en.ts +18 -0
  81. package/src/i18n/ko.ts +17 -0
  82. package/src/index.ts +9 -1
  83. package/src/router.ts +20 -9
  84. package/src/schemas.ts +76 -180
  85. 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,70 +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
- // The path, which console skill takes, and the URI beside it, which a host's own connection takes.
300
- for (const skill of skills.items) {
301
- lines.push(
302
- `${skill.name} ${skill.description}`,
303
- ` ${skill.path}${skill.uri === null ? "" : ` ${skill.uri}`}`,
304
- );
305
- }
306
- lines.push(
307
- "",
308
- `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.`,
309
- ...own,
310
- );
311
- return lines.join("\n");
312
- }
313
-
314
243
  /** One half of the rules: where it comes from, and the text as it stands. */
315
244
  function formatRulesHalf(where: "the workspace" | "this project", rules: RestRules): string {
316
245
  const page = rules.page;
@@ -417,19 +346,25 @@ export function formatDecisionLine(decision: RestDecision): string {
417
346
  return `${decision.id} ${state} ${decision.question} (asked by ${asker(decision)})`;
418
347
  }
419
348
 
349
+ /** The options, one a line; the person's own answer, when they wrote one, is the last and says so. */
420
350
  export function formatOptions(decision: RestDecision): string {
421
- return decision.options.map((option) => ` ${option.id}) ${option.label}`).join("\n");
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");
422
357
  }
423
358
 
424
- /** 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. */
425
360
  export function formatAnswer(decision: RestDecision): string {
426
361
  const { answer } = decision;
427
362
  if (answer === null) {
428
363
  return "waits for a person.";
429
364
  }
430
- const label = decision.options.find((option) => option.id === answer.option)?.label;
365
+ const chosen = decision.options.find((option) => option.id === answer.option);
431
366
  const lines = [
432
- `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}`,
433
368
  ];
434
369
  if (answer.note !== null && answer.note.length > 0) {
435
370
  lines.push(`Note: ${answer.note}`);
@@ -531,256 +466,3 @@ export function formatVersion(path: string, version: RestVersion): string {
531
466
  }
532
467
 
533
468
  /** The text of one path across the pages a skill came in: the fragments joined in order. */
534
- function joinTexts(
535
- pages: readonly RestSkillPage[],
536
- pick: (page: RestSkillPage) => readonly { readonly path: string; readonly body: string }[],
537
- ): { readonly path: string; readonly body: string }[] {
538
- const joined = new Map<string, string>();
539
- for (const page of pages) {
540
- for (const text of pick(page)) {
541
- joined.set(text.path, (joined.get(text.path) ?? "") + text.body);
542
- }
543
- }
544
- return [...joined.entries()].map(([path, body]) => ({ path, body }));
545
- }
546
-
547
- const NO_ADDRESS = "No skills address: neither this project nor the console names one.";
548
-
549
- /**
550
- * One skill, as load_skill lays it out for a model (ADR-0028): the rules from the repository's
551
- * root to the nearest manifest, the body, the included files, each under a line that names its
552
- * path; then the files the skill has and what it refers to. A skill described only is named and
553
- * pointed at, and nothing of its body is shown.
554
- */
555
- export function formatSkillLoad(loaded: RestSkillLoad, pages: readonly RestSkillPage[]): string {
556
- if (loaded.status === "none" || loaded.address === null) {
557
- return NO_ADDRESS;
558
- }
559
- const head = `skill at ${loaded.address}, served by ${loaded.source}: ${loaded.status}`;
560
- if (loaded.status !== "ready") {
561
- return [head, SKILLS_WORDS[loaded.status]].join("\n");
562
- }
563
- const [first] = pages;
564
- if (first === undefined) {
565
- return [head, "Nothing was read."].join("\n");
566
- }
567
- const last = pages[pages.length - 1] ?? first;
568
- const lines = [
569
- `${first.name} ${first.description}`,
570
- `skill ${first.path} at ${loaded.address}, served by ${loaded.source}`,
571
- ];
572
- if (first.serving.license !== null) {
573
- lines.push(`license: ${first.serving.license}`);
574
- }
575
- if (!first.serving.full) {
576
- lines.push(
577
- "",
578
- `Described only: the license keeps the skill at its source. Read it there: ${first.serving.sourceUrl ?? loaded.page ?? loaded.source}`,
579
- );
580
- return lines.join("\n");
581
- }
582
- for (const rule of joinTexts(pages, (page) => page.rules)) {
583
- lines.push("", `--- applicable rules: ${rule.path} ---`, rule.body.trimEnd());
584
- }
585
- lines.push(
586
- "",
587
- `--- skill: ${first.path} ---`,
588
- pages
589
- .map((page) => page.body)
590
- .join("")
591
- .trimEnd(),
592
- );
593
- for (const file of joinTexts(pages, (page) => page.included)) {
594
- lines.push("", `--- included file: ${file.path} ---`, file.body.trimEnd());
595
- }
596
- if (!last.complete) {
597
- lines.push("", "The skill did not come whole: the deployment stopped before the end.");
598
- }
599
- if (last.files.length > 0) {
600
- lines.push(
601
- "",
602
- `Files of the skill, read with console skill --file <path>: ${last.files.join(", ")}`,
603
- );
604
- }
605
- if (last.references.length > 0) {
606
- lines.push(
607
- `References: ${last.references
608
- .map((reference) =>
609
- reference.status === "available"
610
- ? reference.path
611
- : `${reference.path} (${reference.status})`,
612
- )
613
- .join(", ")}`,
614
- );
615
- }
616
- if (last.warnings.length > 0) {
617
- lines.push(`Warnings: ${last.warnings.join("; ")}`);
618
- }
619
- lines.push(
620
- "",
621
- "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.",
622
- );
623
- return lines.join("\n");
624
- }
625
-
626
- /** A file of the pack as read: its text whole, or what the folder holds. */
627
- export function formatSkillFile(read: RestSkillFile, content: string): string {
628
- if (read.status === "none" || read.address === null) {
629
- return NO_ADDRESS;
630
- }
631
- const head = `file at ${read.address}, served by ${read.source}: ${read.status}`;
632
- if (read.status !== "ready") {
633
- return [
634
- head,
635
- read.status === "failed"
636
- ? "SkillCDN will not carry this file: it is too large, or not text. Read it at the repository."
637
- : SKILLS_WORDS[read.status],
638
- ].join("\n");
639
- }
640
- if (read.folder !== null) {
641
- return [
642
- `folder ${read.folder.path} at ${read.address}${read.folder.truncated ? " (cut)" : ""}`,
643
- ...read.folder.entries.map(
644
- (entry) =>
645
- `${entry.kind === "directory" ? "d" : "f"} ${entry.path}${entry.size === null ? "" : ` ${entry.size}`}`,
646
- ),
647
- "",
648
- "console skills <folder> shows a folder with its skills marked, what each folder holds and the README that introduces it.",
649
- ].join("\n");
650
- }
651
- if (read.file === null) {
652
- return `${head}: nothing was read.`;
653
- }
654
- return [
655
- `--- file: ${read.file.path} (${read.file.totalLength} characters) ---`,
656
- content.trimEnd(),
657
- ].join("\n");
658
- }
659
-
660
- /** One entry of a browsed folder: what it is, its path, its words, and the facts of its kind. */
661
- function formatBrowseEntry(entry: RestSkillBrowseEntry): string {
662
- const facts: string[] = [];
663
- if (entry.kind === "directory") {
664
- if (entry.skillCount > 0) {
665
- facts.push(counted(entry.skillCount, "skill"));
666
- }
667
- if (entry.documentCount > 0) {
668
- facts.push(counted(entry.documentCount, "document"));
669
- }
670
- if (entry.overviewPath !== null) {
671
- facts.push(`README ${entry.overviewPath}`);
672
- }
673
- }
674
- if (entry.kind === "skill") {
675
- if (entry.license !== null) {
676
- facts.push(entry.license);
677
- }
678
- if (entry.describedOnly) {
679
- facts.push("described only");
680
- }
681
- }
682
- if (entry.kind === "file" && entry.size !== null) {
683
- facts.push(`${entry.size} bytes`);
684
- }
685
- const words = [entry.name, entry.description]
686
- .filter((text): text is string => text !== null && text !== "")
687
- .join(" ");
688
- const mark = entry.kind === "skill" ? "skill" : entry.kind === "directory" ? "d" : "f";
689
- return `${mark} ${entry.path}${words === "" ? "" : ` ${words}`}${facts.length === 0 ? "" : ` (${facts.join("; ")})`}`;
690
- }
691
-
692
- /**
693
- * What a folder of the pack holds, as browse_repo lays it out: the README that introduces the
694
- * folder, then its skills, its folders with what each holds, and its files, across the pages the
695
- * listing came in.
696
- */
697
- export function formatSkillBrowse(
698
- browsed: RestSkillBrowse,
699
- folders: readonly NonNullable<RestSkillBrowse["folder"]>[],
700
- ): string {
701
- if (browsed.status === "none" || browsed.address === null) {
702
- return NO_ADDRESS;
703
- }
704
- const head = `folder at ${browsed.address}, served by ${browsed.source}: ${browsed.status}`;
705
- if (browsed.status !== "ready") {
706
- return [head, SKILLS_WORDS[browsed.status]].join("\n");
707
- }
708
- const [first] = folders;
709
- if (first === undefined) {
710
- return [head, "Nothing was read."].join("\n");
711
- }
712
- const last = folders[folders.length - 1] ?? first;
713
- const lines = [`folder ${first.path === "" ? "/" : first.path} at ${browsed.address}`];
714
- if (first.overview !== null) {
715
- const words = [first.overview.title, first.overview.description]
716
- .filter((text): text is string => text !== null && text !== "")
717
- .join(" ");
718
- lines.push(` ${words === "" ? "" : `${words} `}(README: ${first.overview.path})`);
719
- }
720
- const entries = folders.flatMap((folder) => folder.entries);
721
- if (entries.length === 0) {
722
- lines.push("Nothing in this folder.");
723
- }
724
- for (const entry of entries) {
725
- lines.push(formatBrowseEntry(entry));
726
- }
727
- if (last.nextCursor !== null) {
728
- lines.push("More entries: the listing stopped before the end of the folder.");
729
- }
730
- lines.push(
731
- "",
732
- "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>.",
733
- );
734
- return lines.join("\n");
735
- }
736
-
737
- /**
738
- * What words found in the pack, as search_repo lays it out: skills with the files of theirs that
739
- * matched, documents on their own, in relevance order; words, not meaning.
740
- */
741
- export function formatSkillFind(found: RestSkillFind): string {
742
- if (found.status === "none" || found.address === null) {
743
- return NO_ADDRESS;
744
- }
745
- const head = `search at ${found.address}, served by ${found.source}: ${found.status}`;
746
- if (found.status !== "ready") {
747
- return [head, SKILLS_WORDS[found.status]].join("\n");
748
- }
749
- if (found.found === null) {
750
- return [head, "Nothing was read."].join("\n");
751
- }
752
- const { query, path, items, nextCursor } = found.found;
753
- const where = path === null || path === "" ? "" : ` within ${path}`;
754
- const lines = [
755
- `found for "${query}"${where} at ${found.address}: ${counted(items.length, "result")}`,
756
- ];
757
- for (const item of items) {
758
- if (item.kind === "skill") {
759
- lines.push(
760
- `skill ${item.path} ${item.name} ${item.description}${item.describedOnly ? " (described only)" : ""}`,
761
- );
762
- for (const file of item.files) {
763
- lines.push(` file ${file.path}${file.title === null ? "" : ` ${file.title}`}`);
764
- }
765
- if (item.moreFiles > 0) {
766
- lines.push(` and ${counted(item.moreFiles, "more file")}`);
767
- }
768
- } else {
769
- lines.push(
770
- `document ${item.path}${item.title === null ? "" : ` ${item.title}`}${item.summary === null ? "" : ` ${item.summary}`}`,
771
- );
772
- }
773
- }
774
- if (nextCursor !== null) {
775
- lines.push(
776
- "More matched: narrow the words, or search within a folder (console skills <folder> --search <words>).",
777
- );
778
- }
779
- if (items.length > 0) {
780
- lines.push(
781
- "",
782
- "Read a skill with console skill <path>, a file or a document with console skill --file <path>.",
783
- );
784
- }
785
- return lines.join("\n");
786
- }
package/src/cli/help.ts CHANGED
@@ -285,11 +285,12 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
285
285
  ],
286
286
  },
287
287
  skills: {
288
- usage: "skills | skills <folder> | skills --search <words> [<folder>] [--limit <n>]",
288
+ usage:
289
+ "skills | skills <folder> | skills --search <words> [<folder>] [--limit <n>] [--cursor <cursor>]",
289
290
  synopsis:
290
291
  "The project's pack: its skills; what a folder of it holds; or what words find in it.",
291
292
  about:
292
- "The project's pack, as SkillCDN serves it at the project's address, read by the console with the pack token the project holds where the pack is private (ADR-0028). Alone: the pack by name with how many skills and documents it has, and its skills, each with its description, the path of its SKILL.md, which console skill reads it by, and the URI you load it by through your own SkillCDN connection where your host has one; and where the console's own skills are on this machine. With a folder (. for the root): what it holds, as browse_repo lays it out: its skills, its folders with how many skills and documents each holds and the README that introduces it, its files; whole, page after page. With --search: the skills and the documents the words find, in relevance order, a skill with the files of it that matched; 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 last line says when more matched. --json answers with the console's own JSON, the page of each skill included.",
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.",
293
294
  limits: ["search: 1 to 500 characters; limit: 1 to 25"],
294
295
  refusals: [
295
296
  "status not_found: the pack is not served to the console; private, with no pack token on the project",
@@ -301,11 +302,11 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
301
302
  synopsis:
302
303
  "One skill of the pack whole, its rules first; or with --file a file of the pack, or what a folder holds.",
303
304
  about:
304
- "One skill of the project's pack, whole, as load_skill lays it out for you: the rules of the repository from its root to the nearest manifest, the skill's body, the files it includes, each under a line that names its path; then the files it has and what it refers to. Read by the console from the SkillCDN deployment, 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 it lists beside the path (skill://gh/<owner>/<repo>/<path>). --file <path> reads a file of the pack instead, whole, or what a folder holds, and takes no skill's path beside it; console skills <folder> shows a folder with more.",
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.",
305
306
  refusals: [
306
307
  "status not_found: the pack is not served to the console; private, with no pack token on the project",
307
308
  "status token_invalid: the pack token the project holds no longer works; an owner sets a new one",
308
- "status failed, for a file: SkillCDN will not carry it, too large or not text",
309
+ "status failed, for a file: the pack will not carry it, too large or not text",
309
310
  ],
310
311
  },
311
312
  rules: {
@@ -370,7 +371,7 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
370
371
  'ask "<question>" --option "<label>" --option "<label>" [...] [--body <markdown>|--file <path>] [--wait <seconds>]',
371
372
  synopsis:
372
373
  "Raises a decision a person must make, with options, and waits a while for the answer.",
373
- 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. ${ON_THE_RUN}`,
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}`,
374
375
  limits: [
375
376
  `question: one line, up to ${MAX_QUESTION_LENGTH} characters`,
376
377
  `options: ${MIN_OPTIONS} to ${MAX_OPTIONS}, each one line up to ${MAX_OPTION_LABEL_LENGTH} characters`,
@@ -383,7 +384,7 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
383
384
  synopsis:
384
385
  "One decision in full; waits for its answer with --wait; writes what followed with --outcome.",
385
386
  about:
386
- "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.",
387
388
  limits: [`outcome: ${MARKDOWN}`],
388
389
  refusals: ["decision.not_found: no decision has that id"],
389
390
  },