@skillcdn/console 0.1.21 → 0.1.22

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/src/cli/help.ts CHANGED
@@ -31,15 +31,21 @@ import {
31
31
  import { COLOR_NAMES } from "../vocabulary.js";
32
32
 
33
33
  // What the command says about itself: for an agent that meets it for the first time, and for a
34
- // person. One table for every command, from which the usage and each command's help are made:
35
- // the overview says what each command does, and a command's own help adds the limits the
36
- // console holds it to and the refusals it may meet, so that an agent learns them before it
37
- // runs into them.
34
+ // person. One table for every command, from which two layers are made. The overview is a map:
35
+ // every command in a line, what it does in a sentence, and what holds everywhere, short enough
36
+ // to be read once at the start. A command's own help is the reference: every form and flag, what
37
+ // each does, the limits the console holds it to and the refusals it may meet, read when the
38
+ // command is about to be used. How the work goes is the skill working-the-board, and what the
39
+ // organization asks is the rules of the board: neither is repeated here.
38
40
 
39
41
  export interface CommandHelp {
40
- /** The command and its arguments, after `console`. */
42
+ /** The command and its arguments, after `console`, every form and flag. */
41
43
  readonly usage: string;
42
- /** What it does, in a sentence. */
44
+ /** The usage as the overview shows it, for a command whose forms are many; `usage` has them all. */
45
+ readonly overview?: string;
46
+ /** What it does, in one line, for the overview. */
47
+ readonly synopsis: string;
48
+ /** What it does in full, for the command's own help. */
43
49
  readonly about: string;
44
50
  /** What the console holds the arguments to. */
45
51
  readonly limits?: readonly string[];
@@ -85,6 +91,8 @@ const TASK_FIELD_REFUSALS = [
85
91
  export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
86
92
  login: {
87
93
  usage: "login --url <origin> [--agent <name>] [--token-stdin] [--default]",
94
+ synopsis:
95
+ "Connects this agent to the console with a code a person approves; keeps the token, never shows it.",
88
96
  about:
89
97
  "Connects this agent to the console: shows an address and a code, which a person opens signed in, names the agent and approves; the command then receives a token of its own and keeps it, with the console's address and the account, in your home directory, never showing it. One login per console and account: a second console adds a login beside the first, the first is the default console a directory that names none works at, and --default makes this one that. A login repeated for the same console and account replaces its token, which is given up at the console. An agent running the command relays the address and the code to its person and waits. --agent says what the agent calls itself; left out, the command names the agent it runs under where its environment says so (Claude Code, Codex), else An agent; the machine's name follows, and the pages show the name before 'on' with the whole on hover. --token-stdin reads a token made by hand from standard input instead, for a script or a job.",
90
98
  refusals: [
@@ -94,42 +102,54 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
94
102
  },
95
103
  logins: {
96
104
  usage: "logins",
105
+ synopsis: "The consoles this machine is signed in to, and which is the default.",
97
106
  about:
98
107
  "The consoles this machine is signed in to, each with the account, what the token is called there, since when, and which is the default; never a token.",
99
108
  },
100
109
  logout: {
101
110
  usage: "logout [--url <origin>] [--account <login>]",
111
+ synopsis: "Gives this agent's token up at one console and forgets it here.",
102
112
  about:
103
113
  "Signs this agent out of one console: gives the token up there, so that it is good for nothing from now on, and forgets it here. Which console: --url, else the one this directory is bound to, else the default, else the one console signed in to; --account says which account where the console has several. A console out of reach leaves the token to the Agents page.",
104
114
  },
105
115
  whoami: {
106
116
  usage: "whoami",
117
+ synopsis: "Who the token acts as, at which console, and in which project this directory works.",
107
118
  about:
108
119
  "Who the token acts as, which console the command talks to from here and as which account, and which project this directory works in.",
109
120
  },
110
121
  projects: {
111
122
  usage: "projects",
123
+ synopsis: "The projects you may work in, with what you are in each.",
112
124
  about:
113
125
  "The projects you may work in: each with its key, its name, what you are in it, and what waits in it. Every command on the board works in one project.",
114
126
  },
115
127
  use: {
116
128
  usage: "use <key> [--url <origin>] [--account <login>]",
129
+ synopsis: "Binds this directory to a project: writes .skillcdn-console.json here.",
117
130
  about:
118
131
  "Says which project this directory works in: writes .skillcdn-console.json here, which the commands read from this directory or one above it, and which is meant to be committed in a private repository and left out of a public one. The file names the project; and the console, where that is not the default login here or --url says so; and the account, where the console has several logins or --account says so. Never a token. --project <key> on any command, or CONSOLE_PROJECT in the environment, wins over the project it names.",
119
132
  refusals: ["project.not_found: no project has the key, or it is not yours to see"],
120
133
  },
121
134
  work: {
122
135
  usage: "work [--person <login>]",
136
+ synopsis:
137
+ "What is on your plate across your projects: your open tasks, and the decisions waiting.",
123
138
  about:
124
139
  "What is on your plate across the projects you may see: the open tasks assigned to you, the sooner due first, each with its project; and the decisions waiting for a person. Needs no project. --person <login> shows another person's plate, for an administrator or for an owner of a project they are in: their open tasks within the projects you may see, without the decisions, which wait for anyone.",
125
140
  },
126
141
  tasks: {
127
142
  usage:
128
143
  "tasks [--state <state>] [--open] [--mine] [--category <key>] [--goal <id>] [--due <day|today|week|overdue>] [--unassigned] [--archived] [--limit <n>] [--offset <n>]",
144
+ synopsis:
145
+ "The tasks on the board, newest first, narrowed by state, owner, category, goal or due day.",
129
146
  about: `The tasks on the board, newest first: number, state, priority, title, who owns and works on each, its category and its due day, and what waits on it. --open keeps the ones not done or dropped, --mine those assigned to you, --unassigned those assigned to nobody (not with --mine), --category and --goal those sorted into one, --due those due by a day: today, within the week, or overdue (the open ones past their day). The tasks put away (ADR-0029) are left out unless --archived asks for them alone. A page at a time: ${DEFAULT_PAGE_LIMIT} unless --limit says otherwise, from --offset on; a last line says when there is more. States: idea, ready, in_progress, in_review, done, dropped.`,
130
147
  },
131
148
  task: {
132
149
  usage: `task <number|id> | task new <title> [--body <markdown>|--file <path>] ${TASK_FIELD_FLAGS} | task edit <number|id> [--title <title>] [--body <markdown>|--file <path>] ${TASK_FIELD_FLAGS} | task archive <number|id> | task archive --goal <id> | task restore <number|id>`,
150
+ overview:
151
+ "task <number|id> | task new <title> [flags] | task edit <number|id> [flags] | task archive <number|id>|--goal <id> | task restore <number|id>",
152
+ synopsis: "One task in full; or a task written, changed, put away or brought back.",
133
153
  about:
134
154
  "One task in full, with its plan (category, goal, start and due days), what happened to it (when it was started and done, which the board keeps itself, and when it was put away), the decisions about it, the runs on it and the discussion on it, each comment whole with its files; or a new task written down; or a task changed, only in what the flags say, where none clears a field (priorities: low, normal, high, urgent). archive puts a task away, whatever its state (ADR-0029, ADR-0030): out of the listings, the board and the calendar, open by its number still; with --goal, every done or dropped task of the goal at once, for an owner. restore brings one back.",
135
155
  limits: [
@@ -141,12 +161,16 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
141
161
  },
142
162
  goals: {
143
163
  usage: "goals [--category <key>] [--state <state>]",
164
+ synopsis: "The roadmap: the project's goals, with their state, their days and how far each is.",
144
165
  about:
145
166
  "The roadmap of the project: its goals, the active first, each with its id, its state (planned, active, done, dropped), its category, the days it starts and is meant to be reached by, and how far it is, counted from the tasks that work towards it. --category keeps those sorted into one, --state those in one state.",
146
167
  },
147
168
  goal: {
148
169
  usage:
149
170
  'goal <id> | goal new "<title>" [--body <markdown>|--file <path>] [--state <state>] [--category <key>] [--starts <day>] [--due <day>] | goal edit <id> [--title <title>] [--body <markdown>|--file <path>] [--state <state>] [--category <key|none>] [--starts <day|none>] [--due <day|none>]',
171
+ overview: "goal <id> | goal new <title> [flags] | goal edit <id> [flags]",
172
+ synopsis:
173
+ "One goal with the tasks that work towards it; or a goal set or changed, for an owner.",
150
174
  about:
151
175
  "One goal in full, with the tasks that work towards it; or a goal set on the roadmap, for an owner of the project; or one changed, only in what the flags say, where none clears a category or a day. A goal is planned in days as a task is, a start and a target, and sorted into a category of the workspace (console categories). A task works towards a goal through its --goal.",
152
176
  limits: [
@@ -163,17 +187,21 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
163
187
  },
164
188
  categories: {
165
189
  usage: "categories",
190
+ synopsis: "The categories tasks are sorted into, by key.",
166
191
  about:
167
192
  "The categories tasks are sorted into, the same in every project: each with its key, which --category takes, its name and its colour. An administrator keeps them: console category, or the Categories page.",
168
193
  },
169
194
  people: {
170
195
  usage: "people",
196
+ synopsis: "Everyone of the workspace, by login, with what each is in it.",
171
197
  about:
172
198
  "Everyone of the workspace, by login, with their name where the host gives one and what they are in it: admin, who configures the workspace, or member, who works in the projects they are in; a person taken out is marked deactivated. Then the logins an administrator let in that nobody has signed in with yet. The logins are what --assignee, member and person take.",
173
199
  },
174
200
  person: {
175
201
  usage:
176
202
  "person role <login> <admin|member> | person add <login> [--host gh|google] | person remove <login> | person deactivate <login> | person reactivate <login>",
203
+ synopsis:
204
+ "Says what a person is in the workspace, lets a login in or takes one out; for an administrator.",
177
205
  about:
178
206
  "For an administrator: the person themselves on the People page, or their agent here, named on it. role says what a person is in the workspace; the board keeps at least one administrator in. add lets a login in: whoever signs in with that account at that host is a person from then on; the host is read off the login (an address is Google's, anything else GitHub's) unless --host says. remove takes a login away that nobody has signed in with yet. deactivate takes a person out: refused on every request at once, their agents with them, kept with everything they did, and listed nowhere; reactivate takes them back in. A person the organization let in comes and goes with it, and is neither.",
179
207
  refusals: [
@@ -187,6 +215,9 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
187
215
  project: {
188
216
  usage:
189
217
  'project <key> | project new <key> "<name>" [--description <markdown>|--file <path>] [--visibility workspace|private] [--skills <address>|none] [--color <name>] [--symbol <text>] | project edit <key> [--name "<name>"] [the same flags; none clears] | project icon <key> <file> | project icon <key> --remove | project pack-token <key> --stdin | project pack-token <key> --remove',
218
+ overview:
219
+ "project <key> | project new <key> <name> [flags] | project edit <key> [flags] | project icon <key> <file>|--remove | project pack-token <key> --stdin|--remove",
220
+ synopsis: "One project in full; or a project made, changed, given its mark or its pack token.",
190
221
  about:
191
222
  "One project in full; or a project made, with you as its owner, private unless --visibility workspace says everyone of the workspace works in it; or one changed, only in what the flags say, for an owner; or its image for its mark, set from a file of this machine or taken away; or its pack token (ADR-0028), the token a person made at SkillCDN for a private pack, read from standard input and never an argument, which the console keeps sealed to read the pack with, or taken away. The organization's skills are the project's unless --skills names an address of its own.",
192
223
  limits: [
@@ -206,12 +237,15 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
206
237
  },
207
238
  members: {
208
239
  usage: "members",
240
+ synopsis: "Those listed in the project, with what each is in it.",
209
241
  about:
210
242
  "Those listed in the project, by login, with what each is in it (owner, who configures it; member, who works on it) and since when. A project open to the workspace has everyone of it as a member besides, and an administrator owns every project.",
211
243
  },
212
244
  member: {
213
245
  usage:
214
246
  "member add <login> [--role owner|member] [--project <key>[,<key>...]] | member role <login> <owner|member> | member remove <login>",
247
+ synopsis:
248
+ "Lists a person in the project, changes what they are in it, or takes them off; for an owner.",
215
249
  about:
216
250
  "Lists a person of the workspace in the project, as a member unless --role says owner; changes what they are in it; or takes them off the list. For an owner of the project. With --project naming several keys with commas, member add lists the person in each of them in turn, one line per project; one listed already is said so, and the rest go on.",
217
251
  refusals: [
@@ -224,6 +258,7 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
224
258
  category: {
225
259
  usage:
226
260
  'category new <key> "<name>" [--color <name>] | category edit <key> [--name "<name>"] [--color <name>] | category remove <key>',
261
+ synopsis: "Makes, changes or removes a category; for an administrator.",
227
262
  about:
228
263
  "Makes a category tasks are sorted into, the same in every project; changes its name or its colour; or removes one no task is sorted into. For an administrator.",
229
264
  limits: [
@@ -240,14 +275,23 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
240
275
  ],
241
276
  },
242
277
  skills: {
243
- usage: "skills",
278
+ usage: "skills | skills <folder> | skills --search <words> [<folder>] [--limit <n>]",
279
+ synopsis:
280
+ "The project's pack: its skills; what a folder of it holds; or what words find in it.",
244
281
  about:
245
- "The skills of the project's pack, as SkillCDN serves them at the project's address: 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. A private pack is read with the pack token the project holds (ADR-0028). --json adds the page of each, for a person.",
282
+ "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.",
283
+ limits: ["search: 1 to 500 characters; limit: 1 to 25"],
284
+ refusals: [
285
+ "status not_found: the pack is not served to the console; private, with no pack token on the project",
286
+ "status token_invalid: the pack token the project holds no longer works; an owner sets a new one",
287
+ ],
246
288
  },
247
289
  skill: {
248
- usage: "skill <path> [--file <path>]",
290
+ usage: "skill <path> | skill --file <path>",
291
+ synopsis:
292
+ "One skill of the pack whole, its rules first; or with --file a file of the pack, or what a folder holds.",
249
293
  about:
250
- "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. --file <path> reads a file of the pack instead, whole, or what a folder holds. What you read is the pack's, under its license: what a private pack says stays in your work and is not copied into reports or pages beyond what the work needs.",
294
+ "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. --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.",
251
295
  refusals: [
252
296
  "status not_found: the pack is not served to the console; private, with no pack token on the project",
253
297
  "status token_invalid: the pack token the project holds no longer works; an owner sets a new one",
@@ -257,8 +301,10 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
257
301
  rules: {
258
302
  usage:
259
303
  "rules | rules write --body <markdown>|--file <path>|-|none [--workspace] [--base <version>]",
304
+ synopsis:
305
+ "What this organization and this project ask of agents; rules write writes them, for an owner or an administrator.",
260
306
  about:
261
- "What this organization asks of agents (ADR-0023): the workspace's rules, the console's own until an administrator writes the organization's, and this project's own when it has them. Read them before taking a task; take prints them again. rules write writes this project's rules, for an owner, or with --workspace the workspace's, for an administrator; none puts the written rules away, so that the console's own hold again, or the project adds none. Without a project named, rules shows the workspace's alone.",
307
+ "What this organization asks of agents (ADR-0023): the workspace's rules, the console's own until an administrator writes the organization's, and this project's own when it has them. rules write writes this project's rules, for an owner, or with --workspace the workspace's, for an administrator; none puts the written rules away, so that the console's own hold again, or the project adds none. Without a project named, rules shows the workspace's alone.",
262
308
  limits: [
263
309
  `body: Markdown, up to ${MAX_RULES_LENGTH} characters; the console's own rules, written as they are, write nothing`,
264
310
  ],
@@ -269,8 +315,9 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
269
315
  },
270
316
  take: {
271
317
  usage: "take <number|id> [--agent <name>]",
318
+ synopsis: "Takes a task: a run begins, and the task is in progress and the person's.",
272
319
  about:
273
- "Takes a task: a run begins, and the task is in progress and the person's. A run still open on the task is taken over, since the board cannot know whether its agent still runs: it ends as abandoned, the feed says who took over, and that agent is refused on its next command. Look before taking (console task <number> lists the runs, with when each was last heard from), and take a task another agent was heard from on lately only when told to. --agent is what people see at work; left out, it is what the person called the token.",
320
+ "Takes a task: a run begins, and the task is in progress and the person's; the rules of the board are printed with the task. A run still open on the task is taken over, since the board cannot know whether its agent still runs: it ends as abandoned, the feed says who took over, and that agent is refused on its next command; console task <number> shows the runs on a task, with when each was last heard from, before it is taken. --agent is what people see at work; left out, it is what the person called the token.",
274
321
  limits: [`agent: one line, up to ${MAX_AGENT_LENGTH} characters`],
275
322
  refusals: [
276
323
  "task.not_found: no task has that number or id",
@@ -279,8 +326,9 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
279
326
  },
280
327
  report: {
281
328
  usage: "report <markdown> | report --file <path> | report -",
329
+ synopsis: "Says how the work goes, in Markdown, on the open run.",
282
330
  about:
283
- "Says how the work goes, in Markdown: what was found, what was done, what is next. A milestone at a time, not every step. With - the report is read from standard input.",
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.",
284
332
  limits: [
285
333
  `the report: ${MARKDOWN}, not blank`,
286
334
  `at most ${MAX_REPORTS_PER_RUN} reports on one run`,
@@ -289,6 +337,7 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
289
337
  },
290
338
  "hand-in": {
291
339
  usage: "hand-in <https url | file path> [--label <words>]",
340
+ synopsis: "Hands in what was made: an https link, or a file from this machine.",
292
341
  about:
293
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 ![words](its name) shows it where the words are.",
294
343
  limits: [
@@ -306,8 +355,10 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
306
355
  ask: {
307
356
  usage:
308
357
  'ask "<question>" --option "<label>" --option "<label>" [...] [--body <markdown>|--file <path>] [--wait <seconds>]',
358
+ synopsis:
359
+ "Raises a decision a person must make, with options, and waits a while for the answer.",
309
360
  about:
310
- "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, and go on meanwhile only with work that does not depend on the answer. The decision's id is printed as soon as it is raised.",
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.",
311
362
  limits: [
312
363
  `question: one line, up to ${MAX_QUESTION_LENGTH} characters`,
313
364
  `options: ${MIN_OPTIONS} to ${MAX_OPTIONS}, each one line up to ${MAX_OPTION_LABEL_LENGTH} characters`,
@@ -317,6 +368,8 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
317
368
  },
318
369
  decision: {
319
370
  usage: "decision <id> [--wait <seconds>] | decision <id> --outcome <markdown>|--file <path>",
371
+ synopsis:
372
+ "One decision in full; waits for its answer with --wait; writes what followed with --outcome.",
320
373
  about:
321
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.",
322
375
  limits: [`outcome: ${MARKDOWN}`],
@@ -324,10 +377,12 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
324
377
  },
325
378
  decisions: {
326
379
  usage: "decisions [--open] [--limit <n>] [--offset <n>]",
380
+ synopsis: "The decisions on the board, the waiting first.",
327
381
  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.`,
328
382
  },
329
383
  finish: {
330
384
  usage: 'finish [--summary "<markdown>"|--file <path>]',
385
+ synopsis: "Ends the run: done and handed in; the task goes up for review.",
331
386
  about:
332
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.",
333
388
  limits: [`summary: ${`Markdown, up to ${MAX_SUMMARY_LENGTH} characters`}`],
@@ -335,12 +390,14 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
335
390
  },
336
391
  fail: {
337
392
  usage: 'fail [--summary "<markdown>"|--file <path>]',
393
+ synopsis: "Ends the run: the work could not be done.",
338
394
  about: "Ends the run: the work could not be done. The summary says why, and what is left.",
339
395
  limits: [`summary: Markdown, up to ${MAX_SUMMARY_LENGTH} characters`],
340
396
  refusals: RUN_REFUSALS,
341
397
  },
342
398
  abandon: {
343
399
  usage: 'abandon [<run id>] [--summary "<markdown>"]',
400
+ synopsis: "Leaves a run; the task stays as it is.",
344
401
  about:
345
402
  "Leaves a run; the task stays as it is. A person gives up this way on a run of theirs that will not come back.",
346
403
  limits: [`summary: Markdown, up to ${MAX_SUMMARY_LENGTH} characters`],
@@ -348,16 +405,19 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
348
405
  },
349
406
  runs: {
350
407
  usage: "runs [--task <number|id>] [--open] [--mine] [--limit <n>] [--offset <n>]",
408
+ synopsis: "The runs, newest first, each with when its agent was last heard from.",
351
409
  about: `The runs, newest first, each with when its agent was last heard from: --open keeps the ones not over, --mine those begun with this token. A page at a time: ${DEFAULT_PAGE_LIMIT} unless --limit says otherwise, from --offset on.`,
352
410
  },
353
411
  run: {
354
412
  usage: "run <id>",
413
+ synopsis: "One run in full: its reports, what it handed in, how it ended.",
355
414
  about: "One run in full: its reports, what it handed in, what it waits for, how it ended.",
356
415
  refusals: ["run.not_found: no run has that id"],
357
416
  },
358
417
  comment: {
359
418
  usage:
360
419
  'comment <number|id> "<markdown>" [--attach <path>]... | comment <number|id> --file <path> | comment <number|id> -',
420
+ synopsis: "Says something in the discussion on a task, with files; needs no run.",
361
421
  about:
362
422
  "Says something in the discussion on a task, for its people: an answer to what they asked there, a question that is not a decision, a remark; with files from this machine put in it, which the console keeps and shows on the task, pictures in place. Needs no run. A report is the run's record; a comment is a word to people. Never rewritten: a mistake is corrected in the next one.",
363
423
  limits: [
@@ -375,23 +435,27 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
375
435
  },
376
436
  file: {
377
437
  usage: "file <id> [--out <path>]",
438
+ synopsis: "Reads a file of the project to this machine, by its id.",
378
439
  about:
379
440
  "Reads a file of the project from the console and writes it on this machine: one a run handed in, one put in a comment, or one attached to a page, by the id the task, the run or the page shows. Written under its own name in the working directory, at --out <path>, or into --out <directory>. Then look at it with your own tools.",
380
441
  refusals: ["file.not_found: no file of this project has that id"],
381
442
  },
382
443
  docs: {
383
444
  usage: "docs [<folder>] [--search <words>] [--archived]",
445
+ synopsis: "The pages of the project's documents: a folder, or those found by words.",
384
446
  about:
385
447
  "The pages of the project's documents: those in a folder, with the folders in it (the root when none is named), or those found by words in their title or body. Archived pages are left out unless --archived.",
386
448
  },
387
449
  doc: {
388
450
  usage: "doc <path> [--version <n>|--versions]",
451
+ synopsis: "One page in full, or one of its versions.",
389
452
  about:
390
453
  "One page in full: its title, its body in Markdown, what it links to, what refers to it (pages, tasks, decisions), and its files. --versions lists its versions, who wrote each and when; --version <n> prints one as it was.",
391
454
  refusals: [...DOCUMENT_REFUSALS, "document.version_not_found: the page has no such version"],
392
455
  },
393
456
  write: {
394
457
  usage: 'write <path> [--title "<title>"] --body <markdown>|--file <path> [--base <version>]',
458
+ synopsis: "Writes a page: a new one at a path, or a new version of the page there.",
395
459
  about:
396
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.",
397
461
  limits: [
@@ -409,6 +473,7 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
409
473
  },
410
474
  attach: {
411
475
  usage: "attach <path> <file> [--label <words>]",
476
+ synopsis: "Attaches a file from this machine to a page.",
412
477
  about:
413
478
  "Attaches a file from this machine to a page: the console keeps it, and the page lists it; a picture is shown where the body names it as ![words](its name).",
414
479
  limits: [
@@ -424,17 +489,21 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
424
489
  },
425
490
  archive: {
426
491
  usage: "archive <path>",
492
+ synopsis: "Puts a page away; nothing is deleted.",
427
493
  about:
428
494
  "Puts a page away: out of the folders and the search, still readable at its path, not written to until restored. Nothing is deleted.",
429
495
  refusals: DOCUMENT_REFUSALS,
430
496
  },
431
497
  restore: {
432
498
  usage: "restore <path>",
499
+ synopsis: "Brings an archived page back.",
433
500
  about: "Brings an archived page back.",
434
501
  refusals: DOCUMENT_REFUSALS,
435
502
  },
436
503
  serve: {
437
504
  usage: "serve [<dir>] [--port <n>]",
505
+ synopsis:
506
+ "For a person: serves a console of your own from this machine, its /api/ carried to the console with your token.",
438
507
  about:
439
508
  "For a person: serves a console of your own from this machine, for you. The build in <dir> (its index.html and files) is served at http://127.0.0.1:11197/, and everything under /api/ is carried to the console you are signed in to with your token, which the pages never see. Without a directory only the API is served there, for a development server to send its /api/ requests to. The loopback only; a request from another site's page is refused. Stop it with Ctrl+C.",
440
509
  refusals: ["not a build to serve: the directory holds no index.html"],
@@ -450,6 +519,8 @@ How the board is worked is the skill working-the-board, installed with this comm
450
519
  where it is; read it before the first command of a run.
451
520
 
452
521
  Usage: console <command> [arguments] [--json] [--url <origin>] [--project <key>]
522
+ A command in full, every form and flag ([flags] below), the limits the console holds it to and what
523
+ it may refuse: console help <command>. Read a command there when you are about to use it.
453
524
  `;
454
525
 
455
526
  const CLOSING = `Options everywhere: --json answers with the console's own JSON; --url names another console;
@@ -458,8 +529,7 @@ A listing (tasks, decisions, runs) is a page at a time: --limit <n> and --offset
458
529
  says when there is more, so that nothing is read that is not needed.
459
530
  Commands on a run (report, hand-in, ask, finish, fail) act on your one open run in the project;
460
531
  with several open, say which with --run <id>. A Markdown body given as - is read from standard input.
461
- console help <command> says what each command takes, the limits the console holds it to, and
462
- what it may refuse. console --version says which version of the command this is.
532
+ console --version says which version of the command this is.
463
533
 
464
534
  Exit codes: 0 done; 1 the console refused, or could not be reached (the reason is on stderr);
465
535
  2 the command was not understood; 3 the decision still waits for a person.
@@ -512,7 +582,7 @@ const SECTIONS: readonly { readonly title: string; readonly commands: readonly s
512
582
  },
513
583
  ];
514
584
 
515
- /** The whole usage, as `console help` prints it, naming the version when it is known. */
585
+ /** The overview, as `console help` prints it: every command in a line, naming the version when it is known. */
516
586
  export function usage(version?: string): string {
517
587
  const parts = [preamble(version)];
518
588
  for (const section of SECTIONS) {
@@ -520,7 +590,7 @@ export function usage(version?: string): string {
520
590
  for (const name of section.commands) {
521
591
  const help = COMMAND_HELP[name];
522
592
  if (help !== undefined) {
523
- parts.push(` console ${help.usage}\n ${help.about}`);
593
+ parts.push(` console ${help.overview ?? help.usage}\n ${help.synopsis}`);
524
594
  }
525
595
  }
526
596
  parts.push("");
@@ -529,7 +599,7 @@ export function usage(version?: string): string {
529
599
  return parts.join("\n");
530
600
  }
531
601
 
532
- /** What `console help <command>` prints, or nothing for a command that is not one. */
602
+ /** What `console help <command>` prints, the command in full; or nothing for a command that is not one. */
533
603
  export function helpFor(name: string): string | undefined {
534
604
  const help = COMMAND_HELP[name];
535
605
  if (help === undefined) {
package/src/client.ts CHANGED
@@ -61,7 +61,9 @@ import {
61
61
  type RestRunEndInput,
62
62
  type RestRunInput,
63
63
  type RestRuns,
64
+ type RestSkillBrowse,
64
65
  type RestSkillFile,
66
+ type RestSkillFind,
65
67
  type RestSkillLoad,
66
68
  type RestSkills,
67
69
  type RestTask,
@@ -103,7 +105,9 @@ import {
103
105
  restRulesSchema,
104
106
  restRunSchema,
105
107
  restRunsSchema,
108
+ restSkillBrowseSchema,
106
109
  restSkillFileSchema,
110
+ restSkillFindSchema,
107
111
  restSkillLoadSchema,
108
112
  restSkillsSchema,
109
113
  restTaskSchema,
@@ -318,6 +322,25 @@ export interface ProjectClient {
318
322
  page?: { readonly offset?: number | undefined; readonly limit?: number | undefined },
319
323
  signal?: AbortSignal,
320
324
  ): Promise<RestSkillFile>;
325
+ /** What a folder of the pack holds, a page of it (`browse_repo`): the mounted folder when `path` is left out. */
326
+ skillBrowse(
327
+ page?: {
328
+ readonly path?: string | undefined;
329
+ readonly cursor?: string | undefined;
330
+ readonly limit?: number | undefined;
331
+ },
332
+ signal?: AbortSignal,
333
+ ): Promise<RestSkillBrowse>;
334
+ /** The skills and the documents of the pack that words find, a page of them in relevance order (`search_repo`), within `path` when given. */
335
+ skillFind(
336
+ query: string,
337
+ options?: {
338
+ readonly path?: string | undefined;
339
+ readonly cursor?: string | undefined;
340
+ readonly limit?: number | undefined;
341
+ },
342
+ signal?: AbortSignal,
343
+ ): Promise<RestSkillFind>;
321
344
  /** Sets the pack token the project reads a private pack with; for an owner. The token travels once and is answered never. */
322
345
  setPackToken(input: RestPackTokenInput): Promise<RestProject>;
323
346
  /** Takes the pack token away; for an owner. */
@@ -714,6 +737,31 @@ export function createClient(options: ClientOptions = {}): ConsoleClient {
714
737
  restSkillFileSchema,
715
738
  signal,
716
739
  ),
740
+ skillBrowse: (page = {}, signal) =>
741
+ request(
742
+ "GET",
743
+ withQuery(`${at("skills")}/browse`, {
744
+ path: page.path,
745
+ cursor: page.cursor,
746
+ limit: page.limit === undefined ? undefined : String(page.limit),
747
+ }),
748
+ undefined,
749
+ restSkillBrowseSchema,
750
+ signal,
751
+ ),
752
+ skillFind: (query, options = {}, signal) =>
753
+ request(
754
+ "GET",
755
+ withQuery(`${at("skills")}/find`, {
756
+ query,
757
+ path: options.path,
758
+ cursor: options.cursor,
759
+ limit: options.limit === undefined ? undefined : String(options.limit),
760
+ }),
761
+ undefined,
762
+ restSkillFindSchema,
763
+ signal,
764
+ ),
717
765
  setPackToken: (input) =>
718
766
  request("PUT", `${base}${at()}/pack-token`, input, restProjectSchema),
719
767
  removePackToken: () =>
package/src/rules.ts CHANGED
@@ -29,7 +29,7 @@ export const REFERENCE_RULES = [
29
29
  "",
30
30
  "## The organization's skills",
31
31
  "",
32
- "`console skills` lists the skills of this project's pack: each with its name, what it does, and the path of its SKILL.md. `console skill <path>` reads one whole, its rules first, as the console reads it from the SkillCDN deployment for you, a private pack with the pack token the project holds; `console skill <path> --file <file>` reads a file of the pack. Where your host lists skills through a connection of your own to the address, the URI `skill://gh/<owner>/<repo>/<path>` is the skill there, followed until its context is complete. Either way, never fetch a skill another way. Load the skill the task names before anything is made; with none named, the one whose description fits the work. The rules those skills share arrive with each of them, from their repository's SKILLCDN.md, and hold within that work. What you read of a private pack is the project's: it stays in your work, and is not copied into a report, a page or a comment beyond what the work needs.",
32
+ "`console skills` lists the skills of this project's pack: each with its name, what it does, and the path of its SKILL.md; `console skills <folder>` shows what a folder of the pack holds, and `console skills --search <words>` finds its skills and documents by words. `console skill <path>` reads one whole, its rules first, as the console reads it from the SkillCDN deployment for you, a private pack with the pack token the project holds; `console skill --file <file>` reads a file of the pack. Where your host lists skills through a connection of your own to the address, the URI `skill://gh/<owner>/<repo>/<path>` is the skill there, followed until its context is complete. Either way, never fetch a skill another way. Load the skill the task names before anything is made; with none named, the one whose description fits the work. The rules those skills share arrive with each of them, from their repository's SKILLCDN.md, and hold within that work. What you read of a private pack is the project's: it stays in your work, and is not copied into a report, a page or a comment beyond what the work needs.",
33
33
  "",
34
34
  "## Where the work lives",
35
35
  "",
package/src/schemas.ts CHANGED
@@ -915,6 +915,16 @@ export const restSkillsSchema = z.object({
915
915
  /** Where a person browses the address, or `null` without one. */
916
916
  page: z.nullable(z.string()),
917
917
  status: z.enum(SKILLS_STATUSES),
918
+ /** The pack as its manifest or its README names it, when `ready`: what it is called, what it is for, the language it is written in. */
919
+ pack: z.optional(
920
+ z.object({
921
+ name: z.nullable(z.string()),
922
+ description: z.nullable(z.string()),
923
+ language: z.nullable(z.string()),
924
+ }),
925
+ ),
926
+ /** How many skills and documents the deployment indexed at the address, when `ready`. */
927
+ counts: z.optional(z.object({ skills: count, documents: count })),
918
928
  /** The skills, when `ready`; empty otherwise. */
919
929
  items: z.array(restSkillSchema),
920
930
  });
@@ -1006,6 +1016,106 @@ export const restSkillFileSchema = z.object({
1006
1016
  });
1007
1017
  export type RestSkillFile = z.infer<typeof restSkillFileSchema>;
1008
1018
 
1019
+ /** An entry of a folder of the pack, as the deployment browses it: a skill, a folder with what it holds, or a file. */
1020
+ export const restSkillBrowseEntrySchema = z.object({
1021
+ kind: z.enum(["skill", "directory", "file"]),
1022
+ /** The path from the repository's root: of the `SKILL.md` for a skill, of the folder, of the file. */
1023
+ path: z.string(),
1024
+ name: z.nullable(z.string()),
1025
+ description: z.nullable(z.string()),
1026
+ /** For a folder: the skills and the documents under it. */
1027
+ skillCount: count,
1028
+ documentCount: count,
1029
+ /** For a file: its size in bytes. */
1030
+ size: z.nullable(count),
1031
+ /** For a folder: the README that introduces it, read with the file route. */
1032
+ overviewPath: z.nullable(z.string()),
1033
+ /** For a skill: its license, and whether the address only describes it. */
1034
+ license: z.nullable(z.string()),
1035
+ describedOnly: z.boolean(),
1036
+ });
1037
+ export type RestSkillBrowseEntry = z.infer<typeof restSkillBrowseEntrySchema>;
1038
+
1039
+ /** `GET .../skills/browse?path=&cursor=&limit=`: what a folder of the pack holds, a page of it (`browse_repo`); `folder` is `null` unless `ready`. */
1040
+ export const restSkillBrowseSchema = z.object({
1041
+ address: z.nullable(z.string()),
1042
+ source: z.string(),
1043
+ page: z.nullable(z.string()),
1044
+ status: z.enum(SKILLS_STATUSES),
1045
+ folder: z.nullable(
1046
+ z.object({
1047
+ path: z.string(),
1048
+ /** The README that introduces the folder, or `null`. */
1049
+ overview: z.nullable(
1050
+ z.object({
1051
+ path: z.string(),
1052
+ title: z.nullable(z.string()),
1053
+ description: z.nullable(z.string()),
1054
+ }),
1055
+ ),
1056
+ entries: z.array(restSkillBrowseEntrySchema),
1057
+ /** Where the listing goes on, or `null` at its end. */
1058
+ nextCursor: z.nullable(z.string()),
1059
+ }),
1060
+ ),
1061
+ });
1062
+ export type RestSkillBrowse = z.infer<typeof restSkillBrowseSchema>;
1063
+
1064
+ /** What a search of the pack found: a skill, as the listing has it, with the files of it that matched; or a document on its own. */
1065
+ export const restSkillFindItemSchema = z.discriminatedUnion("kind", [
1066
+ z.object({
1067
+ kind: z.literal("skill"),
1068
+ name: z.string(),
1069
+ description: z.string(),
1070
+ directory: z.string(),
1071
+ path: z.string(),
1072
+ page: z.string(),
1073
+ uri: z.nullable(z.string()),
1074
+ translations: z.record(
1075
+ z.string(),
1076
+ z.object({ title: z.nullable(z.string()), description: z.nullable(z.string()) }),
1077
+ ),
1078
+ /** The files of the skill that matched, a few, and how many more did. */
1079
+ files: z.array(
1080
+ z.object({
1081
+ path: z.string(),
1082
+ title: z.nullable(z.string()),
1083
+ summary: z.nullable(z.string()),
1084
+ }),
1085
+ ),
1086
+ moreFiles: count,
1087
+ license: z.nullable(z.string()),
1088
+ describedOnly: z.boolean(),
1089
+ }),
1090
+ z.object({
1091
+ kind: z.literal("document"),
1092
+ path: z.string(),
1093
+ title: z.nullable(z.string()),
1094
+ summary: z.nullable(z.string()),
1095
+ /** The folder of the skill the document belongs to, or `null` for one on its own. */
1096
+ skillDirectory: z.nullable(z.string()),
1097
+ }),
1098
+ ]);
1099
+ export type RestSkillFindItem = z.infer<typeof restSkillFindItemSchema>;
1100
+
1101
+ /** `GET .../skills/find?query=&path=&cursor=&limit=`: what words find in the pack, a page of it in relevance order (`search_repo`); `found` is `null` unless `ready`. */
1102
+ export const restSkillFindSchema = z.object({
1103
+ address: z.nullable(z.string()),
1104
+ source: z.string(),
1105
+ page: z.nullable(z.string()),
1106
+ status: z.enum(SKILLS_STATUSES),
1107
+ found: z.nullable(
1108
+ z.object({
1109
+ query: z.string(),
1110
+ /** The folder the search was kept to, or `null` for the whole pack. */
1111
+ path: z.nullable(z.string()),
1112
+ items: z.array(restSkillFindItemSchema),
1113
+ nextCursor: z.nullable(z.string()),
1114
+ }),
1115
+ ),
1116
+ });
1117
+ export type RestSkillFind = z.infer<typeof restSkillFindSchema>;
1118
+
1009
1119
  /**
1010
1120
  * A document's path (ADR-0009): segments of lowercase letters, digits and hyphens, separated
1011
1121
  * by slashes, as a repository keeps files; the document's address, which does not change.