@skillcdn/console 0.3.2 → 0.4.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 +3 -3
- 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 +96 -7
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +6 -4
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +48 -17
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/pack.d.ts +7 -2
- package/dist/cli/pack.d.ts.map +1 -1
- package/dist/cli/pack.js +16 -2
- package/dist/cli/pack.js.map +1 -1
- package/dist/client.d.ts +7 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +7 -1
- package/dist/client.js.map +1 -1
- package/dist/compat.d.ts +1 -1
- package/dist/compat.js +2 -2
- package/dist/components/board.d.ts +12 -1
- package/dist/components/board.d.ts.map +1 -1
- package/dist/components/board.js +52 -4
- package/dist/components/board.js.map +1 -1
- package/dist/components/calendar.d.ts +4 -0
- package/dist/components/calendar.d.ts.map +1 -1
- package/dist/components/calendar.js +61 -22
- package/dist/components/calendar.js.map +1 -1
- package/dist/components/decision-form.d.ts +12 -4
- package/dist/components/decision-form.d.ts.map +1 -1
- package/dist/components/decision-form.js +24 -6
- package/dist/components/decision-form.js.map +1 -1
- package/dist/components/decision-list.d.ts +4 -0
- package/dist/components/decision-list.d.ts.map +1 -1
- package/dist/components/decision-list.js +9 -6
- package/dist/components/decision-list.js.map +1 -1
- package/dist/components/documents.d.ts.map +1 -1
- package/dist/components/documents.js +3 -3
- package/dist/components/documents.js.map +1 -1
- package/dist/components/event-feed.d.ts.map +1 -1
- package/dist/components/event-feed.js +63 -8
- package/dist/components/event-feed.js.map +1 -1
- package/dist/components/my-decisions.d.ts +20 -0
- package/dist/components/my-decisions.d.ts.map +1 -0
- package/dist/components/my-decisions.js +36 -0
- package/dist/components/my-decisions.js.map +1 -0
- package/dist/components/people.d.ts.map +1 -1
- package/dist/components/people.js +1 -1
- package/dist/components/people.js.map +1 -1
- package/dist/components/projects.d.ts.map +1 -1
- package/dist/components/projects.js +3 -3
- package/dist/components/projects.js.map +1 -1
- package/dist/components/roadmap.js +1 -1
- package/dist/components/roadmap.js.map +1 -1
- package/dist/components/runs.d.ts.map +1 -1
- package/dist/components/runs.js +1 -1
- package/dist/components/runs.js.map +1 -1
- package/dist/components/shell.d.ts.map +1 -1
- package/dist/components/shell.js +36 -1
- package/dist/components/shell.js.map +1 -1
- package/dist/components/task-list.js +1 -1
- package/dist/components/task-list.js.map +1 -1
- package/dist/components/task-view.d.ts.map +1 -1
- package/dist/components/task-view.js +2 -2
- package/dist/components/task-view.js.map +1 -1
- package/dist/components/ui.d.ts +10 -0
- package/dist/components/ui.d.ts.map +1 -1
- package/dist/components/ui.js +15 -11
- package/dist/components/ui.js.map +1 -1
- package/dist/console.css +1642 -433
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +70 -7
- package/dist/console.js.map +1 -1
- package/dist/data.d.ts +11 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +2 -0
- package/dist/data.js.map +1 -1
- package/dist/i18n/en.d.ts +34 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +36 -2
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/ko.d.ts.map +1 -1
- package/dist/i18n/ko.js +37 -3
- package/dist/i18n/ko.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/router.d.ts +9 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +12 -0
- package/dist/router.js.map +1 -1
- package/dist/routes.d.ts +3 -1
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +3 -1
- package/dist/routes.js.map +1 -1
- package/dist/rules.js +1 -1
- package/dist/rules.js.map +1 -1
- package/dist/schemas.d.ts +242 -3
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +18 -4
- package/dist/schemas.js.map +1 -1
- package/docs/specs/cli.md +6 -5
- package/docs/specs/rest.md +11 -6
- package/package.json +2 -2
- package/skills/building-a-console/SKILL.md +3 -3
- package/skills/building-a-console/references/the-default-console.md +4 -4
- package/skills/building-a-console/references/the-look-from-a-website.md +5 -5
- package/skills/working-the-board/SKILL.md +6 -4
- package/skills/working-the-board/references/terms.md +1 -1
- package/src/api.ts +2 -0
- package/src/cli/cli.ts +123 -7
- package/src/cli/format.ts +11 -4
- package/src/cli/help.ts +49 -16
- package/src/cli/pack.ts +17 -1
- package/src/client.ts +21 -1
- package/src/compat.ts +2 -2
- package/src/components/board.tsx +114 -21
- package/src/components/calendar.tsx +207 -102
- package/src/components/decision-form.tsx +71 -23
- package/src/components/decision-list.tsx +30 -3
- package/src/components/documents.tsx +27 -11
- package/src/components/event-feed.tsx +116 -33
- package/src/components/my-decisions.tsx +138 -0
- package/src/components/people.tsx +3 -1
- package/src/components/projects.tsx +7 -5
- package/src/components/roadmap.tsx +1 -1
- package/src/components/runs.tsx +5 -1
- package/src/components/shell.tsx +36 -1
- package/src/components/task-list.tsx +1 -1
- package/src/components/task-view.tsx +48 -47
- package/src/components/ui.tsx +64 -11
- package/src/console.tsx +129 -27
- package/src/data.ts +17 -0
- package/src/i18n/en.ts +38 -2
- package/src/i18n/ko.ts +39 -3
- package/src/index.ts +3 -0
- package/src/router.ts +18 -1
- package/src/routes.ts +3 -1
- package/src/rules.ts +1 -1
- package/src/schemas.ts +20 -4
- package/src/styles/console.css +1642 -433
package/docs/specs/cli.md
CHANGED
|
@@ -27,7 +27,7 @@ The board is a project's ([ADR-0008](../adr/0008-a-workspace-holds-projects-and-
|
|
|
27
27
|
| `whoami` | `GET /api/v1/me` | the login, the role, the workspace, the address, and the project this directory works in; with a project, where the organization's rules are read and where the skill working-the-board is on this machine; then the command's version and where it is installed, the console's version and the range it takes, and the update to make when they differ. Keeps the workspace's time zone on the login it used, so that the moments the command prints are in it from then on ([ADR-0035](../adr/0035-who-may-see-whom-is-decided-by-one-policy-and-the-workspace-keeps-its-settings.md)) |
|
|
28
28
|
| `projects` | `GET /api/v1/projects` | one line per project: key, name, what the person is in it, what waits in it |
|
|
29
29
|
| `use <key> [--url <origin>] [--account <login>]` | `GET /api/v1/projects/<key>` | that the directory works in the project from now on, at which console and as which account where it binds them, and that `console rules` says what its people ask; then the sentence for the checkout's agreement (AGENTS.md, or CLAUDE.md), to be copied as it is; writes `.skillcdn-console.json` |
|
|
30
|
-
| `work [--person <login>]` | `GET /api/v1/work`, `GET /api/v1/people/<id>/work` | what is on the person's plate across their projects: the open tasks they work on, the sooner due first, each with its project; and the decisions waiting. `--person` shows another person's plate, for an administrator or an owner of a project they are in: the open tasks they work on within the projects the asker may see, without the decisions. The tasks done or dropped lately, which the answer carries as `closed` for the week on the plate's page, are in `--json` only. Needs no project |
|
|
30
|
+
| `work [--person <login>]` | `GET /api/v1/work`, `GET /api/v1/people/<id>/work` | what is on the person's plate across their projects: the open tasks they work on, the sooner due first, each with its project; and the decisions waiting for their answer ([ADR-0042](../adr/0042-a-decision-is-addressed-to-a-person-its-raiser-unless-another-is-named.md)). `--person` shows another person's plate, for an administrator or an owner of a project they are in: the open tasks they work on within the projects the asker may see, without the decisions. The tasks done or dropped lately, which the answer carries as `closed` for the week on the plate's page, are in `--json` only. Needs no project |
|
|
31
31
|
| `tasks [--state <state>] [--open] [--mine] [--category <key>] [--goal <id>] [--due <day\|today\|week\|overdue>] [--unassigned] [--search <words>] [--archived] [--limit <n>] [--offset <n>]` | `GET .../tasks` with the filters and the page (`--mine` and `--unassigned` not together; `--search` is `q=`, the words of a title or a number, how a task is found as a parent, [ADR-0037](../adr/0037-a-task-has-one-owner-and-any-number-of-workers-and-a-parent-or-a-goal-is-found-by-search.md)) | one line per task: number, state, priority, title, owner, workers, category, due day, what waits on it, and whether it is put away; the tasks put away are left out unless `--archived` asks for them alone; a page at a time |
|
|
32
32
|
| `task <number\|id>` | `GET .../tasks/<ref>`, the decisions about it, the runs on it, `GET .../tasks/<id>/comments` | the task in full, with its plan and its moments, and the discussion on it: each comment whole, with its files and the command that reads each |
|
|
33
33
|
| `task new <title> [--body <markdown>\|--file <path>] [--state <state>] [--priority <priority>] [--category <key>] [--goal <id>] [--starts <day>] [--due <day>] [--worker <login\|me\|none>]... [--parent <number\|id>]` | `POST .../tasks` | the number and the id; `--worker` given again, or with commas between, names several, in place of the workers there were, and `none` alone nobody |
|
|
@@ -57,8 +57,8 @@ The board is a project's ([ADR-0008](../adr/0008-a-workspace-holds-projects-and-
|
|
|
57
57
|
| `members` | `GET .../members` | one line per member: login, role, since when; in a project open to the workspace, everyone of it ([ADR-0035](../adr/0035-who-may-see-whom-is-decided-by-one-policy-and-the-workspace-keeps-its-settings.md)) |
|
|
58
58
|
| `member add <login> [--role owner\|member] [--project <key>[,<key>...]]`, `member role <login> <owner\|member>`, `member remove <login>` | `GET /api/v1/people?login=` for the id, then `POST .../members` (once per project when `--project` names several with commas), `PATCH .../members/<id>`, `DELETE .../members/<id>` | the member as they are now, or that they are no longer listed; with several projects, one line per project, a person listed already said so; for an owner. In a project open to the workspace everyone is a member already: `add` lists an owner with `--role owner` and refuses a member (`member.open_project`), `role <login> owner` lists one and `role <login> member` takes the listing away, `remove` takes a listing away and the person stays a member |
|
|
59
59
|
| `category new <key> <name> [--color <name>]`, `category edit <key> [--name <name>] [--color <name>]`, `category remove <key>` | `POST /api/v1/categories`, `PATCH .../categories/<key>`, `DELETE .../categories/<key>` | the category as it is now, or that it is gone; for an administrator |
|
|
60
|
-
| `skills`, `skills <folder>`, `skills --search <words> [<folder>] [--limit <n>] [--cursor <cursor>]` | `GET .../skills`; `GET .../skills/browse?path=&cursor=`, page after page to the end; `GET .../skills/find?query=&path=&cursor=&limit=`, one page | the project's pack, or the organization's ([ADR-0031](../adr/0031-the-console-reads-a-pack-as-the-skillcdn-command-does-and-a-local-one-from-a-directory.md)): where it is, at a deployment or on the console's machine, and how the read went; what a client is told of it on connect, as the deployment tells one, with the reads named as the console's commands; every skill with the path of its `SKILL.md` and every document, as a search without words lists them; where a person connects an agent of its own, and where the console's own skills are on this machine. With a folder (`.` for the root; `/` is taken too, but a Windows shell rewrites it into a path before the command sees it), what it holds, whole, as `browse_repo` lays it out for a model. With `--search`, one page of what the words find, as `search_repo` does: skills first with the files of theirs that matched, then documents, in relevance order, within the folder when one is named, ten unless `--limit` says otherwise (up to 25), and a `nextCursor` line when more matched, which `--cursor` continues. `--json` is the console's answer: what the pack answered, in the shapes of the main repository's REST API, the pages of a folder joined |
|
|
61
|
-
| `skill <path>`, `skill --file <path
|
|
60
|
+
| `skills`, `skills <folder\|file>`, `skills --search <words> [<folder>] [--limit <n>] [--cursor <cursor>]` | `GET .../skills`; `GET .../skills/browse?path=&cursor=`, page after page to the end; `GET .../skills/find?query=&path=&cursor=&limit=`, one page | the project's pack, or the organization's ([ADR-0031](../adr/0031-the-console-reads-a-pack-as-the-skillcdn-command-does-and-a-local-one-from-a-directory.md)): where it is, at a deployment or on the console's machine, and how the read went; what a client is told of it on connect, as the deployment tells one, with the reads named as the console's commands; every skill with the path of its `SKILL.md` and every document, as a search without words lists them; where a person connects an agent of its own, and where the console's own skills are on this machine. With a folder (`.` for the root; `/` is taken too, but a Windows shell rewrites it into a path before the command sees it), what it holds, whole, as `browse_repo` lays it out for a model. With a file's path, the file's outline, whole: every heading with its level and the offset and limit that read its section with `skill --file`; a file that is not Markdown has none, and the line says so (SkillCDN 0.4). With `--search`, one page of what the words find, as `search_repo` does: skills first with the files of theirs that matched, then documents, each with the sections of it that matched and the offset and limit that read each, in relevance order, within the folder when one is named, ten unless `--limit` says otherwise (up to 25), a line on how the page was ranked where the deployment searches by meaning too, and a `nextCursor` line when more matched, which `--cursor` continues. `--json` is the console's answer: what the pack answered, in the shapes of the main repository's REST API, the pages of a folder or an outline joined |
|
|
61
|
+
| `skill <path>`, `skill --file <path> [--offset <n>] [--limit <n>]` | `GET .../skills`, then `GET .../skills/one?path=&cursor=`, page after page until `complete`; with `--file`, `GET .../skills/file?path=&offset=` until the end, or, with `--offset` or `--limit`, `GET .../skills/file?path=&offset=&limit=` once | the skill whole, as `load_skill` lays it out for a model: the rules of the repository from its root to the nearest manifest, the instructions, the included files, then its supporting files and what it refers to, rendered by the main repository's own renderers with the reads named as the console's commands; or the file whole, or what a folder holds; or, with `--offset` (0 or more) or `--limit` (1 to 100,000), one page of the file as asked, the section a search or an outline named, with where the file continues; a private pack through the pack token the project holds ([ADR-0028](../adr/0028-the-console-reads-a-projects-pack-through-the-deployments-rest-api-with-a-pack-token-for-a-private-one.md)). `<path>` is the path of the SKILL.md from the repository's root, or the URI the pack names it by (`skill://<host>/<owner>/<name>/<path>`); a URI of another pack is not understood. `--file` takes no skill's path beside it, and `--offset` and `--limit` go with `--file` only: otherwise not understood. A read the pack refused prints its status and the pack's own words, and exits with `0` |
|
|
62
62
|
| `rules` | `GET .../rules`, or `GET /api/v1/rules` without a project named, and `GET /api/v1/me` | the rules that hold, as they read for the person (`{language}` is the language they chose, or the task's) ([ADR-0023](../adr/0023-the-rules-of-a-board-are-pages-the-workspace-and-its-projects-keep-read-first-by-every-agent.md)): the workspace's, the console's own until an administrator writes the organization's, with its version and who wrote it when written; then this project's own, or that it adds none |
|
|
63
63
|
| `rules write --body <markdown>\|--file <path>\|-\|none [--workspace] [--base <version>]` | `PUT .../docs/rules` or `POST .../docs/rules/archive`; with `--workspace`, `PUT /api/v1/rules` or `POST /api/v1/rules/archive` | the version written, that the console's own rules sent as they are wrote nothing, or that the rules are put away; for an owner of the project, or an administrator for the workspace's |
|
|
64
64
|
| `take <number\|id> [--agent <name>]` | `GET .../tasks/<ref>`, then `POST .../runs`, then `GET .../rules`, `GET /api/v1/me`, `GET .../tasks/<ref>` again, `GET .../tasks/<id>/comments` and `GET .../runs?open=true&mine=true` | the run that began, with the run's id and the task's id on a line of their own, the task in full as it stands taken with the discussion on it, the rules that hold as they read for the person, and what to do next; a run open on the task is taken over, and that is said when it happened ([ADR-0020](../adr/0020-the-console-does-not-claim-an-agent-is-at-work-a-run-says-when-it-was-last-heard-from-and-a-new-run-takes-over.md)); another run of the token open in the project is named, with the `--run <id>` to say on every command on a run from here |
|
|
@@ -66,9 +66,10 @@ The board is a project's ([ADR-0008](../adr/0008-a-workspace-holds-projects-and-
|
|
|
66
66
|
| `hand-in <https url \| file path> [--label <words>] [--run <id>]` | `POST .../runs/<id>/artifacts` for a link; `POST .../runs/<id>/files` for a file, sent as a form with its name and its media type by extension | how many artifacts; a picture handed in is shown on the task, and where a report names it as `` |
|
|
67
67
|
| `comment <number\|id> "<markdown>" [--attach <path>]...`, `comment <number\|id> --file <path>`, `comment <number\|id> -` | `GET .../tasks/<ref>`, then `POST .../tasks/<id>/comments`: as JSON, or as one form with the part `body` and a part `file` per file attached, its media type by extension | the comment as written, with its files and the command that reads each; a word to the task's people, which needs no run ([ADR-0024](../adr/0024-a-task-carries-a-discussion-of-comments-with-their-files-read-as-one-thread-with-the-runs-and-the-decisions.md)) |
|
|
68
68
|
| `file <id> [--out <path>]` | `GET .../files/<id>` | that the file is saved, where, with its size and type: under its own name in the working directory, at the path, or into the directory named; a file a run handed in, one put in a comment, or one attached to a page, so that an agent looks at a picture a person put on the board |
|
|
69
|
-
| `ask "<question>" --option "<label>" ... [--body <markdown>\|--file <path>] [--wait <seconds>] [--run <id>]` | `POST .../decisions` with `runId`, then `GET .../decisions/<id>?wait=` | the decision raised, its id on a line of its own, its options; then the answer, or that it still waits |
|
|
69
|
+
| `ask "<question>" --option "<label>" ... [--body <markdown>\|--file <path>] [--to <login\|me>] [--wait <seconds>] [--run <id>]` | `POST .../decisions` with `runId` and, for `--to`, `deciderId`, then `GET .../decisions/<id>?wait=` | the decision raised, its id on a line of its own, whom it waits for, its options; then the answer, or that it still waits. It is for the person the agent works for unless `--to` names another of the project ([ADR-0042](../adr/0042-a-decision-is-addressed-to-a-person-its-raiser-unless-another-is-named.md)) |
|
|
70
70
|
| `decision <id> [--wait <seconds>]` | `GET .../decisions/<id>`, with `?wait=` while waiting | the decision; with a wait, the answer or that it still waits |
|
|
71
|
-
| `
|
|
71
|
+
| `answer <id> --option <n>\|--own "<answer>" [--note <markdown>\|--file <path>]` | `POST .../decisions/<id>/answer` | the decision with its answer: a person's answer written in their name through the agent, as they gave it in chat or in passing, with where and when in the note, so that the record is whole and a run that waited goes on ([ADR-0042](../adr/0042-a-decision-is-addressed-to-a-person-its-raiser-unless-another-is-named.md)) |
|
|
72
|
+
| `decisions [--open] [--limit <n>] [--offset <n>]`, `decisions --mine\|--raised [--open] [--limit <n>] [--offset <n>]` | `GET .../decisions`; `GET /api/v1/decisions` for `--mine`, with `raised=true` for `--raised`, neither needing a project | one line per decision, the waiting first; with `--mine`, the decisions addressed to the person across the projects they may see, with `--raised` those they asked of someone else, each with its project's key; a page at a time |
|
|
72
73
|
| `finish [--summary <markdown>\|--file <path>] [--run <id>]`, `fail [...]` | `POST .../runs/<id>/end` | how the run ended |
|
|
73
74
|
| `abandon [<run id>] [--summary <markdown>]` | `POST .../runs/<id>/end`, as `abandoned` | the same |
|
|
74
75
|
| `runs [--task <number\|id>] [--open] [--mine] [--limit <n>] [--offset <n>]` | `GET .../runs` | one line per run, with when an open one was last heard from; a page at a time |
|
package/docs/specs/rest.md
CHANGED
|
@@ -38,6 +38,7 @@ The API is the board as its members see it: the people, the projects, and in eac
|
|
|
38
38
|
| 400 | `decision.invalid_task` | The decision is about a task that is not one of the project's. |
|
|
39
39
|
| 400 | `decision.no_such_option` | The answer names an option the decision does not have. |
|
|
40
40
|
| 400 | `decision.invalid_run` | A decision raised from a run that is not the asker's, or is over. |
|
|
41
|
+
| 400 | `decision.invalid_decider` | The person named to decide may not work in the project ([ADR-0042](../adr/0042-a-decision-is-addressed-to-a-person-its-raiser-unless-another-is-named.md)). |
|
|
41
42
|
| 400 | `token.expiry_at_most` | A token that would not expire, or would outlast `TOKEN_DAYS_AT_MOST`, where the organization requires an expiry. |
|
|
42
43
|
| 400 | `document.invalid_path` | The path a page is written at is not one: segments of lowercase letters, digits and hyphens, separated by slashes. |
|
|
43
44
|
| 400 | `run.too_many_reports`, `run.too_many_artifacts` | The run carries as many as one may (`MAX_REPORTS_PER_RUN`, `MAX_ARTIFACTS_PER_RUN`). |
|
|
@@ -140,7 +141,11 @@ The rules of the board ([ADR-0023](../adr/0023-the-rules-of-a-board-are-pages-th
|
|
|
140
141
|
|
|
141
142
|
### `GET /api/v1/work`
|
|
142
143
|
|
|
143
|
-
`{ "tasks": [{ "project": { "key", "name", "color", "symbol", "icon" }, "task" }, ...], "closed": [{ "project", "task" }, ...], "decisions": [{ "project", "decision" }, ...] }`: what is on the asker's plate across the projects they may see ([ADR-0019](../adr/0019-a-task-is-planned-in-days-the-board-keeps-its-moments-and-goals-and-categories-give-the-work-its-shape.md)): the open tasks they are at work on, the sooner due first and those with no day last; `closed`, the tasks they are at work on that are done or dropped and not yet put away, the last closed first, so that the week on the plate's page shows them on their days as a project's calendar does; and the decisions that wait for a
|
|
144
|
+
`{ "tasks": [{ "project": { "key", "name", "color", "symbol", "icon" }, "task" }, ...], "closed": [{ "project", "task" }, ...], "decisions": [{ "project", "decision" }, ...] }`: what is on the asker's plate across the projects they may see ([ADR-0019](../adr/0019-a-task-is-planned-in-days-the-board-keeps-its-moments-and-goals-and-categories-give-the-work-its-shape.md)): the open tasks they are at work on, the sooner due first and those with no day last; `closed`, the tasks they are at work on that are done or dropped and not yet put away, the last closed first, so that the week on the plate's page shows them on their days as a project's calendar does; and the decisions that wait for the asker's answer ([ADR-0042](../adr/0042-a-decision-is-addressed-to-a-person-its-raiser-unless-another-is-named.md)), the longest waiting first; each with the project it belongs to, so that a page says and links it. A task put away is in neither list ([ADR-0030](../adr/0030-a-task-is-put-away-from-any-state.md)). At most `LIST_LIMIT` of each.
|
|
145
|
+
|
|
146
|
+
### `GET /api/v1/decisions?open=&raised=&limit=&offset=`
|
|
147
|
+
|
|
148
|
+
`{ "items": [{ "project": { "key", "name", "color", "symbol", "icon" }, "decision" }, ...], "more": boolean }`: the decisions addressed to the asker ([ADR-0042](../adr/0042-a-decision-is-addressed-to-a-person-its-raiser-unless-another-is-named.md)) across the projects they may see, a page at a time as the tasks are (`limit`, `offset`, `more`): the ones that wait first, the longest waiting first among them, then the answered, newest first, as the record; `open=true` keeps only those that wait, and `raised=true` answers instead the decisions the asker raised, through their agent or themselves, for someone else. Each decision comes with the project it belongs to, as the plate's things do, so that a page says and links it. What a person's own page of decisions reads, in both kinds, and `console decisions --mine` and `--raised`.
|
|
144
149
|
|
|
145
150
|
### `GET /api/v1/tokens` and `POST /api/v1/tokens`
|
|
146
151
|
|
|
@@ -216,11 +221,11 @@ Writing one, for whoever may work in the project, with a session or a token, is
|
|
|
216
221
|
|
|
217
222
|
### `GET /api/v1/projects/<key>/decisions?open=&task=&limit=&offset=`
|
|
218
223
|
|
|
219
|
-
`{ "items": [decision, ...], "more": boolean }`: the project's, the ones that wait first, newest first within each, a page at a time as the tasks are (`limit`, `offset`, `more`); `open=true` keeps only those that wait, `task=<id>` only those about one task. A decision carries its `question`, `body` (Markdown: the context it rests on), `options` (each an `id` and a `label`; the last carries `own: true` when it is the person's own answer, written instead of choosing, and the options given carry no `own`), `taskId` and `taskNumber` (or `null`), `raisedBy`, `run` (the `id` and `agent` of the run that raised it, when an agent asked, or `null`), `answer`: `null` while it waits, else the chosen `option`, a `note` or `null` (the rationale given with it), `by` and `at`; and `outcome`, what followed, in Markdown, or `null` until someone writes it. Context, rationale and outcome make the decision a record ([ADR-0009](../adr/0009-documents-are-pages-of-markdown-in-a-project-addressed-by-path-versioned-and-linked-both-ways.md)); a link in any of them to a document's path is kept both ways, so that the document says the decision refers to it.
|
|
224
|
+
`{ "items": [decision, ...], "more": boolean }`: the project's, the ones that wait first, newest first within each, a page at a time as the tasks are (`limit`, `offset`, `more`); `open=true` keeps only those that wait, `task=<id>` only those about one task. A decision carries its `question`, `body` (Markdown: the context it rests on), `options` (each an `id` and a `label`; the last carries `own: true` when it is the person's own answer, written instead of choosing, and the options given carry no `own`), `taskId` and `taskNumber` (or `null`), `raisedBy`, `decider` (whom it is for, [ADR-0042](../adr/0042-a-decision-is-addressed-to-a-person-its-raiser-unless-another-is-named.md): the raiser, unless another person of the project was named), `run` (the `id` and `agent` of the run that raised it, when an agent asked, or `null`), `answer`: `null` while it waits, else the chosen `option`, a `note` or `null` (the rationale given with it), `by` and `at`; and `outcome`, what followed, in Markdown, or `null` until someone writes it. Context, rationale and outcome make the decision a record ([ADR-0009](../adr/0009-documents-are-pages-of-markdown-in-a-project-addressed-by-path-versioned-and-linked-both-ways.md)); a link in any of them to a document's path is kept both ways, so that the document says the decision refers to it.
|
|
220
225
|
|
|
221
226
|
### `POST /api/v1/projects/<key>/decisions`
|
|
222
227
|
|
|
223
|
-
Takes `question`, `options` (two to `MAX_OPTIONS` labels), and optionally `body`, and either `taskId` or `runId`. Options are numbered from `1`. From a run, which must be the asker's and open (`decision.invalid_run`), the decision is about the run's task, `taskId` is not looked at, and the run waits (`status: waiting`) until the answer. Answers `201` with the decision and writes a `decision.raised` event, with the `agent` when a run asked.
|
|
228
|
+
Takes `question`, `options` (two to `MAX_OPTIONS` labels), and optionally `body`, and either `taskId` or `runId`. `deciderId` names whom the decision is for ([ADR-0042](../adr/0042-a-decision-is-addressed-to-a-person-its-raiser-unless-another-is-named.md)): a person who may work in the project, as a worker of a task must (`decision.invalid_decider` otherwise); left out, the asker, which from a run is the person the agent works for. Anyone of the project may still answer. Options are numbered from `1`. From a run, which must be the asker's and open (`decision.invalid_run`), the decision is about the run's task, `taskId` is not looked at, and the run waits (`status: waiting`) until the answer. Answers `201` with the decision and writes a `decision.raised` event, with the `agent` when a run asked.
|
|
224
229
|
|
|
225
230
|
### `GET .../decisions/<id>?wait=` and `POST .../decisions/<id>/answer`
|
|
226
231
|
|
|
@@ -242,7 +247,7 @@ One goal, for whoever may see the project. The patch, for an owner, takes any of
|
|
|
242
247
|
|
|
243
248
|
### `GET /api/v1/projects/<key>/runs?task=&open=&mine=&limit=&offset=` and `GET .../runs/<id>`
|
|
244
249
|
|
|
245
|
-
`{ "items": [run, ...], "more": boolean }`: the project's runs, newest first, a page at a time as the tasks are (`limit`, `offset`, `more`); `task=<id>` keeps those on one task, `open=true` those not over (`running` or `waiting`) and `open=false` those over, `mine=true` the asker's own: with a token, the runs begun with that token, which is one agent's; on a session, the runs for the person. A run is one agent's work on one task for one person: `taskId` and `taskNumber` (the one people say), `person` (whom the agent acts for), `agent` (what it calls itself), `status` (`running`, that is open, `waiting` for a decision, `finished`, `failed`, `abandoned`), `startedAt`, `endedAt` or `null`, `lastHeardAt` (when the agent was last heard from through the run: its start, then its latest report, hand-in or question; the console knows nothing of an agent but what it sends, and claims nothing more, [ADR-0020](../adr/0020-the-console-does-not-claim-an-agent-is-at-work-a-run-says-when-it-was-last-heard-from-and-a-new-run-takes-over.md)), `summary` (Markdown, what the agent said at the end, or `null`), `reports` (each `id`, `body` in Markdown, `createdAt`; oldest first), `artifacts` (each `id`, `kind`, `url` for a link or `null`, `label` or `null`, `file` for a file or `null`, and `createdAt`; a file is its `name`, `size` in bytes, `contentType` and `sha256`), and `waitingFor`, the id of the decision the run waits for, or `null`.
|
|
250
|
+
`{ "items": [run, ...], "more": boolean }`: the project's runs, newest first, a page at a time as the tasks are (`limit`, `offset`, `more`); `task=<id>` keeps those on one task, `open=true` those not over (`running` or `waiting`) and `open=false` those over, `mine=true` the asker's own: with a token, the runs begun with that token, which is one agent's; on a session, the runs for the person. A run is one agent's work on one task for one person: `taskId` and `taskNumber` (the one people say), `person` (whom the agent acts for), `agent` (what it calls itself), `status` (`running`, that is open, `waiting` for a decision, `finished`, `failed`, `abandoned`), `startedAt`, `endedAt` or `null`, `lastHeardAt` (when the agent was last heard from through the run: its start, then its latest report, hand-in or question; the console knows nothing of an agent but what it sends, and claims nothing more, [ADR-0020](../adr/0020-the-console-does-not-claim-an-agent-is-at-work-a-run-says-when-it-was-last-heard-from-and-a-new-run-takes-over.md); a run `running` and not heard from for twelve hours, or the hours the deployment sets, is ended by the console as `abandoned`, its `summary` saying so and `run.ended` naming no actor and `reason: "unheard"`, while a run `waiting` for an answer waits on, [ADR-0041](../adr/0041-a-run-not-heard-from-for-twelve-hours-is-ended-by-the-console.md)), `summary` (Markdown, what the agent said at the end, or `null`), `reports` (each `id`, `body` in Markdown, `createdAt`; oldest first), `artifacts` (each `id`, `kind`, `url` for a link or `null`, `label` or `null`, `file` for a file or `null`, and `createdAt`; a file is its `name`, `size` in bytes, `contentType` and `sha256`), and `waitingFor`, the id of the decision the run waits for, or `null`.
|
|
246
251
|
|
|
247
252
|
### `POST /api/v1/projects/<key>/runs`
|
|
248
253
|
|
|
@@ -260,7 +265,7 @@ The bytes of any file of the project, handed in by a run, put in a comment or at
|
|
|
260
265
|
|
|
261
266
|
### `POST .../runs/<id>/end`
|
|
262
267
|
|
|
263
|
-
Takes `status` (`finished`, `failed` or `abandoned`) and optionally a `summary` (Markdown, `MAX_SUMMARY_LENGTH`: what was done and what is left), ends the run (`run.ended`), and answers it. A finished run puts a task that was `in_progress` up for review (`in_review`); the rest leave the task as it is. The run's person ends it, as `finished` or `failed` from their agent; `abandoned` is a person giving up on a run that will not come back: their own, or anyone's as an owner of the project, signed in or through their agent (`run.not_yours` otherwise). A run that is over is `run.over`.
|
|
268
|
+
Takes `status` (`finished`, `failed` or `abandoned`) and optionally a `summary` (Markdown, `MAX_SUMMARY_LENGTH`: what was done and what is left), ends the run (`run.ended`), and answers it. A finished run puts a task that was `in_progress` up for review (`in_review`); the rest leave the task as it is. The run's person ends it, as `finished` or `failed` from their agent; `abandoned` is a person giving up on a run that will not come back: their own, or anyone's as an owner of the project, signed in or through their agent (`run.not_yours` otherwise); the console gives up on a run itself when it has not heard from it for twelve hours ([ADR-0041](../adr/0041-a-run-not-heard-from-for-twelve-hours-is-ended-by-the-console.md)). A run that is over is `run.over`.
|
|
264
269
|
|
|
265
270
|
### `GET /api/v1/projects/<key>/skills`
|
|
266
271
|
|
|
@@ -268,7 +273,7 @@ The project's pack, read by address ([ADR-0028](../adr/0028-the-console-reads-a-
|
|
|
268
273
|
|
|
269
274
|
### `GET /api/v1/projects/<key>/skills/one?path=&cursor=`, `.../skills/file?path=&offset=&limit=`, `.../skills/browse?path=&cursor=&limit=` and `.../skills/find?query=&path=&cursor=&limit=`
|
|
270
275
|
|
|
271
|
-
One skill of the pack, a file of it, what a folder holds and what words find, read by the console from the pack for whoever may see the project, with the SkillCDN token the pack is read with, the project's own or the workspace's ([ADR-0028](../adr/0028-the-console-reads-a-projects-pack-through-the-deployments-rest-api-with-a-pack-token-for-a-private-one.md)), and answered as the pack answered them ([ADR-0031](../adr/0031-the-console-reads-a-pack-as-the-skillcdn-command-does-and-a-local-one-from-a-directory.md)): `{ "address", "source", "page", "status", "refusal", "read" }`, the envelope as above, and `read` the body of the matching route of the main repository's REST API, as `@skillcdn/core` spells it, or `null` unless `ready`. `skills/one` is `load_skill`: `read` is a `RestSkill`, a page of the skill's context, the rules from the repository's root to the nearest manifest, the body and the included files, which `complete` and `nextCursor` say to continue, and `commit`, the commit it was read from. `skills/file` is `read_repo_file`: `read` is a `RestFile`, a page of the file in characters (`offset`, `nextOffset`, `totalLength`; `limit` 1 to 100,000) or, when the path names a folder, its entries. `skills/browse` is `browse_repo`: `read` is a `RestBrowse`, a page of what the folder holds (`limit` 1 to 200, 50 unless given; `cursor` continues). `skills/find` is `search_repo`: `read` is a `RestFind`, a page of what the words find in relevance order (`query` 1 to 500 characters, required; `limit` 1 to 25, 10 unless given). `path` is a path from the repository's root, up to 500 characters, the mounted folder when `browse` and `find` leave it out; a query out of bounds is `request.invalid`. Each read is logged with who read what of which pack, never with what was read or the words searched for. What the pack refuses is the status with the pack's own `refusal`: a skill or a file that is not there is `not_found`, a file the pack will not carry, too large or not text, is `failed` with where it is at its source. `@skillcdn/core` reads every `read` back as the result it was made from (`skillResultOf`, `fileResultOf`, `directoryResultOf`, `browseResultOf`, `findResultOf`, at the `mountSummaryOf` of the listing's `mount`) and renders what a model is given; the command does exactly that.
|
|
276
|
+
One skill of the pack, a file of it, what a folder holds and what words find, read by the console from the pack for whoever may see the project, with the SkillCDN token the pack is read with, the project's own or the workspace's ([ADR-0028](../adr/0028-the-console-reads-a-projects-pack-through-the-deployments-rest-api-with-a-pack-token-for-a-private-one.md)), and answered as the pack answered them ([ADR-0031](../adr/0031-the-console-reads-a-pack-as-the-skillcdn-command-does-and-a-local-one-from-a-directory.md)): `{ "address", "source", "page", "status", "refusal", "read" }`, the envelope as above, and `read` the body of the matching route of the main repository's REST API, as `@skillcdn/core` spells it, or `null` unless `ready`. `skills/one` is `load_skill`: `read` is a `RestSkill`, a page of the skill's context, the rules from the repository's root to the nearest manifest, the body and the included files, which `complete` and `nextCursor` say to continue, and `commit`, the commit it was read from. `skills/file` is `read_repo_file`: `read` is a `RestFile`, a page of the file in characters (`offset`, `nextOffset`, `totalLength`; `limit` 1 to 100,000) or, when the path names a folder, its entries. `skills/browse` is `browse_repo`: `read` is a `RestBrowse`, a page of what the folder holds (`limit` 1 to 200, 50 unless given; `cursor` continues); given a file's path, the file's `outline`, every heading with its level and the `offset` and `length` that read its section through `skills/file`, paged the same way, with `entries` empty (SkillCDN 0.4). `skills/find` is `search_repo`: `read` is a `RestFind`, a page of what the words find in relevance order (`query` 1 to 500 characters, required; `limit` 1 to 25, 10 unless given); a document among the results names the `sections` of it that matched, each with its heading path, the `offset` and `limit` that read it and an excerpt, and `ranking` says how the page was ranked where the deployment searches by meaning as well as by words. `path` is a path from the repository's root, up to 500 characters, the mounted folder when `browse` and `find` leave it out; a query out of bounds is `request.invalid`. Each read is logged with who read what of which pack, never with what was read or the words searched for. What the pack refuses is the status with the pack's own `refusal`: a skill or a file that is not there is `not_found`, a file the pack will not carry, too large or not text, is `failed` with where it is at its source. `@skillcdn/core` reads every `read` back as the result it was made from (`skillResultOf`, `fileResultOf`, `directoryResultOf`, `browseResultOf`, `findResultOf`, at the `mountSummaryOf` of the listing's `mount`) and renders what a model is given; the command does exactly that.
|
|
272
277
|
|
|
273
278
|
### `PUT /api/v1/projects/<key>/pack-token` and `DELETE .../pack-token`
|
|
274
279
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skillcdn/console",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "What a custom SkillCDN console is built from: the API client and schemas, the components, the composition of the default console, and the console command agents work the board with.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/skillcdn/console#readme",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"vitest": "^5.0.3"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@skillcdn/core": "^0.
|
|
71
|
+
"@skillcdn/core": "^0.4.0",
|
|
72
72
|
"react-markdown": "^10.1.0",
|
|
73
73
|
"remark-gfm": "^4.0.1",
|
|
74
74
|
"zod": "^4.6.5"
|
|
@@ -5,7 +5,7 @@ license: MIT
|
|
|
5
5
|
compatibility: A shell with Node.js 24 or newer and npm or pnpm, and an agent that reads and writes files. For looking at the pages against a board, the console command signed in by a person. A browser the agent can drive is optional. No key of the agent's own is needed, and it never sees a token.
|
|
6
6
|
metadata:
|
|
7
7
|
author: skillcdn
|
|
8
|
-
version: "1.
|
|
8
|
+
version: "1.7"
|
|
9
9
|
skillcdn:
|
|
10
10
|
translations:
|
|
11
11
|
ko:
|
|
@@ -50,8 +50,8 @@ Nothing here restates them: they are the contract, current at the version instal
|
|
|
50
50
|
| Which kind | Asked when the request does not say: "Is this console for you alone, served from your machine with your token; for the whole organization, in place of the default pages in the image; or a script against the API?" A member usually wants their own. |
|
|
51
51
|
| Where the organization's build goes | Asked for the organization's: "How is the console run: with the deployment repository that deploying-a-console made, with an image you build yourselves, or from a directory mounted into it?" The answer places the result: the source in `console-ui/` of that repository, which its workflow builds on every push; a repository of its own whose build goes beside the Dockerfile of [deploy/README.md](/deploy/README.md) "A custom console in the image"; or the build in the directory. |
|
|
52
52
|
| What it must show or do | From the request: a page, a component, a language, a brand, a column, a filter, a report. Asked for its missing half: "What should it show or do that the default console does not?" Everything else is the default console's and stays. |
|
|
53
|
-
| The marks | From the organization's website when the request, or deploying-a-console, names one: its symbol and wordmark read from the site as [the-look-from-a-website.md](references/the-look-from-a-website.md) says, the
|
|
54
|
-
| The colour | From the website's own colours when one is named, as the reference says, its primary colour read from the theme colour, the stylesheet's properties or the primary button. Else asked when the request names none and marks are wanted: "Which colour, or which mood?" A colour comes as words or a value; the value is the mark's own fill when it has one, else chosen and shown. A
|
|
53
|
+
| The marks | From the organization's website when the request, or deploying-a-console, names one: its symbol and wordmark read from the site as [the-look-from-a-website.md](references/the-look-from-a-website.md) says, the dark version or, with the person's word, the one-colour mark recoloured, shown at the plan. Else asked for a console that shows marks of its own, for the half the request leaves open: "Which picture should the pages show beside the board's name: a small square mark, the name as lettering, or both? Where is the file? The pages are light, so it should be a dark one." `brand` takes either or both. The symbol's slot is a square of one line's height (`.sc-brand-symbol`, 1rem): a wide mark, a lettermark of three or four times its height, is a wordmark and goes in as one, alone or beside a square symbol. A mark drawn in a light colour vanishes on the light pages: ask for a dark version, or, with the person's word, recolour it when it is one colour. Default without a site: the board's name alone and no marks, which is the honest console. |
|
|
54
|
+
| The colour | From the website's own colours when one is named, as the reference says, its primary colour read from the theme colour, the stylesheet's properties or the primary button. Else asked when the request names none and marks are wanted: "Which colour, or which mood?" A colour comes as words or a value; the value is the mark's own fill when it has one, else chosen and shown. A dark or saturated colour becomes the accent (`--sc-color-accent` and its family); a pale one tints the field (`--sc-field-gradient`), with a darker shade of it as the accent, since an accent must read on the light field. The mapping is shown at the plan, with the contrast checked. |
|
|
55
55
|
| The languages | Asked when the organization's language is not among the packs: "Which languages should the pages speak?" Default: the packs the package ships, chosen by the browser; a Korean organization gets Pretendard loaded, as the default console does. |
|
|
56
56
|
| The name | Not asked: the server names the board (`WORKSPACE_NAME`, conventionally the organization or the person it is for, then Console: `Acme Console`), and `title` is only what the page says until the server answers. `index.html` gets the organization's `<title>` and `lang`. |
|
|
57
57
|
| The console's address | For running one's own, what `console whoami` says. A script takes `CONSOLE_URL` and `CONSOLE_TOKEN` from the environment, never from a file in the repository. |
|
|
@@ -10,7 +10,7 @@ The four files of the default UI, `apps/console/web/` in the console's repositor
|
|
|
10
10
|
<head>
|
|
11
11
|
<meta charset="utf-8" />
|
|
12
12
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
13
|
-
<meta name="color-scheme" content="
|
|
13
|
+
<meta name="color-scheme" content="light" />
|
|
14
14
|
<title>Console</title>
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
@@ -24,9 +24,9 @@ The four files of the default UI, `apps/console/web/` in the console's repositor
|
|
|
24
24
|
|
|
25
25
|
```tsx
|
|
26
26
|
// The marks of the brand come from the main repository's package, never from a copy here
|
|
27
|
-
// (ADR-0013); the build gives back their addresses. The pages are
|
|
27
|
+
// (ADR-0013); the build gives back their addresses. The pages are light, so the wordmark is black.
|
|
28
28
|
import symbol from "@skillcdn/brand/symbol.svg";
|
|
29
|
-
import wordmark from "@skillcdn/brand/wordmark-
|
|
29
|
+
import wordmark from "@skillcdn/brand/wordmark-black.svg";
|
|
30
30
|
import { createConsole } from "@skillcdn/console";
|
|
31
31
|
// The styles ship with the package; the build bundles them next to the script.
|
|
32
32
|
import "@skillcdn/console/console.css";
|
|
@@ -137,4 +137,4 @@ export default defineConfig({
|
|
|
137
137
|
}
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
-
What a console of one's own changes. In the page: its `<title>` and `lang`, and its icons, linked directly in the page or through the plugin, for its own. In the script: the `title` it passes; the two `@skillcdn/brand` imports, for its own symbol and wordmark (a
|
|
140
|
+
What a console of one's own changes. In the page: its `<title>` and `lang`, and its icons, linked directly in the page or through the plugin, for its own. In the script: the `title` it passes; the two `@skillcdn/brand` imports, for its own symbol and wordmark (a dark one, since the pages are light), or none; the face it loads, for its languages (`npm install pretendard` for Korean). In the build: the proxy's target, `http://127.0.0.1:11197` with `changeOrigin: true` for `console serve`; the default console's own proxy points at the `api` role's development port, which a console of one's own does not have, and its plugin reads the icons from the brand package, which a console of one's own replaces with files of its own. In the compiler's settings: `jsx`, `moduleResolution` and the `vite/client` types stay, which make an imported picture a module; `extends` and `references` are the repository's and go, with `"strict": true` of the console's own in their place. One thing to add in the build: `build: { assetsInlineLimit: 0 }`, so that every picture and font stays a file; Vite inlines an asset under 4 KB as a `data:` address, which the policy allows for a picture and refuses for a font (`font-src 'self'`), and the default console gets by only because none of its assets is that small.
|
|
@@ -12,17 +12,17 @@ Fetch the homepage as HTML, with the agent's own tools, and read, in this order:
|
|
|
12
12
|
| The wordmark | The same header: the name as lettering, wide, often an SVG beside or instead of the symbol | `brand.wordmark`, alone or beside the symbol. A mark three or four times wider than high is a wordmark, not a symbol. |
|
|
13
13
|
| The colours | `<meta name="theme-color">`; custom properties on `:root` in the site's stylesheet (`--color-primary`, `--brand`, `--accent` and their kin); the fill of the primary button and the colour of links | One primary colour, as a value; a second where the site plainly has two. |
|
|
14
14
|
| The type | `font-family` of the body and of the headings, in the stylesheet | The names, to tell the person; a face is loaded only as the rules below allow. |
|
|
15
|
-
| The mood | The site itself: dark or light, flat or glassy, round or square corners | Whether the field stays
|
|
15
|
+
| The mood | The site itself: dark or light, flat or glassy, round or square corners | Whether the field stays light (the pages are light by default, ADR-0040; a dark site is said at the plan, and darkening the pages is the person's word, not the default), and whether the radii follow the site (`--sc-radius-*`). |
|
|
16
16
|
|
|
17
17
|
A site that cannot be fetched (a login wall, a block, no site after all) ends the reading: say so in one line and ask the person for the files, or keep the honest default.
|
|
18
18
|
|
|
19
19
|
## Mapping onto the pages
|
|
20
20
|
|
|
21
|
-
The pages are
|
|
21
|
+
The pages are light: a pale field, and panes of frosted glass over it. The mapping is shown at the plan as a table, value by value, before a file is written.
|
|
22
22
|
|
|
23
|
-
- **A mark drawn in a
|
|
24
|
-
- **A
|
|
25
|
-
- **A
|
|
23
|
+
- **A mark drawn in a light colour vanishes** on the light pages. Take the site's dark or black variant first (its header on a light page, a light-mode asset, a `prefers-color-scheme` rule). Found none, and the mark is one colour: recolour it to the pages' text colour, with the person's word, since the mark is theirs. A mark of several colours on a light fill: keep its fill as a rounded square behind it.
|
|
24
|
+
- **A dark or saturated primary colour** becomes the accent: `--sc-color-accent` and its family (`-solid`, `-contrast`, `-subtle`, the point), as the package's README lists them.
|
|
25
|
+
- **A pale primary colour** tints the field: `--sc-field-gradient`, with a darker shade of it as the accent, since an accent must read on the light field.
|
|
26
26
|
- **Contrast is checked**, not assumed: the accent on the field, and text on the panes, at the 4.5:1 of WCAG AA for text; a shade is moved until it passes, and the plan says which.
|
|
27
27
|
- **The type follows the rules of the skill**: scripts, styles and fonts are bundled, never loaded from a CDN or written inline. A face the organization owns, or one under a license that allows bundling, is put in the repository and named in `--sc-font-sans`; otherwise the system faces stay, and the plan says that the site's face was not taken and why. Pretendard stays for Korean, as the default console has it.
|
|
28
28
|
- **The name** stays the server's (`WORKSPACE_NAME`): the look is the marks, the colours and the type, never the words.
|
|
@@ -5,7 +5,7 @@ license: MIT
|
|
|
5
5
|
compatibility: Needs a shell with the console command on the PATH (npm install -g @skillcdn/console, Node.js 24 or newer), signed in by a person. Nothing else; the command needs no key of its own and the agent never sees a token.
|
|
6
6
|
metadata:
|
|
7
7
|
author: skillcdn
|
|
8
|
-
version: "1.
|
|
8
|
+
version: "1.25"
|
|
9
9
|
skillcdn:
|
|
10
10
|
translations:
|
|
11
11
|
ko:
|
|
@@ -22,6 +22,8 @@ A task of the board goes in, named by its number; a run comes out: the task take
|
|
|
22
22
|
- **Checkpoints:** on the board, as decisions (phase 4), which a person answers from wherever they are; the reports say what was done at each milestone, and need no answer.
|
|
23
23
|
- **Go-ahead:** the task's body is the go-ahead. A task that says what the agent may decide alone is followed within that; everything else that would change scope, cost or a trade-off is a decision.
|
|
24
24
|
- **Changes mid-run:** a person changes the task or answers a decision on the board; the run takes it from there and says so in its next report. A report is never rewritten.
|
|
25
|
+
- **A choice made elsewhere:** a decision is the record of a choice that is the person's to make, not the place it has to be made. An answer they give in chat, in a meeting or by changing the task is written on the decision in their name, `console answer <id> --option <n>|--own "<their words>" --note "<where and when>"`, before the work goes on: the run waits on the record, and the board shows as waiting only what is still open. What was decided is not asked again.
|
|
26
|
+
- **A briefing:** asked what is on their plate, what is next, or for a briefing, the agent starts from `console work`: the decisions waiting for the person's answer come first, each with what waits on it and since when, since a waiting decision holds a run; then the tasks by their days. `console decisions --mine` and `--raised` read the rest of their decisions, the answered ones as the record.
|
|
25
27
|
- **The discussion:** what people say about the task is in its comments, which `console task <number>` and `console take` print with the task; a picture they put in is saved with `console file <id>` and looked at with the agent's own tools. An answer to what they asked there, a question that is not a decision, a remark on what was handed in, is a comment of the agent's own: `console comment <number> "<markdown>" [--attach <file>]`, which needs no run. A report is the run's record; a comment is a word to people.
|
|
26
28
|
- **Work given in chat:** when a person gives work in chat, in a directory that works in a project, the agent writes it on the board first (`console task new`), tells the person the number the command printed, never one assumed, since other agents write on the same board and the next number is nobody's to guess, takes it by that number and works it as below, so that the board holds the work and its record, and the person decides on the board.
|
|
27
29
|
- **Language:** what people read, the reports, the questions, the pages and the summaries, is in the language the rules name for the person (`console take` prints them with the task), else the task's, so that the people of the board read as they wrote.
|
|
@@ -41,7 +43,7 @@ The full contract is the [specification of the command line](/docs/specs/cli.md)
|
|
|
41
43
|
| Input | Source |
|
|
42
44
|
|---|---|
|
|
43
45
|
| The task | Its number (`#7`) or its id, from what the person said. Told to pick one, `console work` lists what is on the person's plate across their projects, the sooner due first (`--person <login>` another person's, for an administrator or an owner of a project they are in), and `console tasks --state ready --mine` what is ready and theirs in this one (`--due overdue`, `--due week`, `--category <key>` and `--goal <id>` narrow it further; `--search <words>` finds a task by the words of its title, or by its number); a task is never taken without being told which, or that any ready one may be. |
|
|
44
|
-
| The work | The task's body, read with `console task <number>`: what to make, where, the milestones, what to ask; the comments on it, which are the discussion so far, printed with the task, their pictures saved with `console file <id>`; with its plan, the category, the goal it works towards (`console goal <id>` says what reaching it means, its category and the days it starts and is meant to be reached by) and the days it starts and is due, which the reports and the summary keep to. The pages it links to (`console doc <path>`) and the project's pack (`console skills` says where the pack is, what a client is told of it on connect, and every skill and document; `console skills <folder>` shows what a folder of it holds, `console skills --search <words>` what the words find in it; `console skill <path>` reads one whole through the console, its rules first, as an MCP client's model is given it, a private pack through the pack token the project holds, [ADR-0028](/docs/adr/0028-the-console-reads-a-projects-pack-through-the-deployments-rest-api-with-a-pack-token-for-a-private-one.md), and a pack on the console's own machine from its directory, [ADR-0031](/docs/adr/0031-the-console-reads-a-pack-as-the-skillcdn-command-does-and-a-local-one-from-a-directory.md); `console skill --file <file>` a file of it; a host that lists skills through a connection of its own loads them by their URI, which means nothing elsewhere for a pack on the console's machine) say the rest. |
|
|
46
|
+
| The work | The task's body, read with `console task <number>`: what to make, where, the milestones, what to ask; the comments on it, which are the discussion so far, printed with the task, their pictures saved with `console file <id>`; with its plan, the category, the goal it works towards (`console goal <id>` says what reaching it means, its category and the days it starts and is meant to be reached by) and the days it starts and is due, which the reports and the summary keep to. The pages it links to (`console doc <path>`) and the project's pack (`console skills` says where the pack is, what a client is told of it on connect, and every skill and document; `console skills <folder>` shows what a folder of it holds, `console skills --search <words>` what the words find in it; `console skill <path>` reads one whole through the console, its rules first, as an MCP client's model is given it, a private pack through the pack token the project holds, [ADR-0028](/docs/adr/0028-the-console-reads-a-projects-pack-through-the-deployments-rest-api-with-a-pack-token-for-a-private-one.md), and a pack on the console's own machine from its directory, [ADR-0031](/docs/adr/0031-the-console-reads-a-pack-as-the-skillcdn-command-does-and-a-local-one-from-a-directory.md); `console skill --file <file>` a file of it, `console skills <file>` the outline of a document and `console skill --file <file> --offset <n> --limit <n>` one section of it, as a search or the outline names them; a host that lists skills through a connection of its own loads them by their URI, which means nothing elsewhere for a pack on the console's machine) say the rest. |
|
|
45
47
|
| The rules | The workspace's, the console's own until an administrator writes the organization's, and this project's own when it has them: which skills to load first, where the work lives, the pages, what may be decided alone, how to report ([ADR-0023](/docs/adr/0023-the-rules-of-a-board-are-pages-the-workspace-and-its-projects-keep-read-first-by-every-agent.md)). `console take` prints them with the task, so they are read once, there; `console rules` reads them without taking, as when a task is still to be chosen. They hold over this skill's defaults where they differ. |
|
|
46
48
|
|
|
47
49
|
## Workflow
|
|
@@ -54,7 +56,7 @@ Told which task, go to phase 2: `console take` prints the rules of the board wit
|
|
|
54
56
|
|
|
55
57
|
### Phase 2: Take
|
|
56
58
|
|
|
57
|
-
`console take <number>`. A run begins, and the command says its id and the task's on a line of their own (`run id:`, `task id:`; a task has a number for people and an id for the API, a run, a decision and a goal have ids alone, and every command that makes one prints it so, `--json` the whole answer; an id is read from there, never assumed): the task is in progress and the person's, and every report, hand-in, question and ending from here acts on this run. With another run of the person's open in the project, which `console take` says, each of those commands names the run: `--run <id>`. Taken by mistake, `console abandon` leaves it and the task stays as it was.
|
|
59
|
+
`console take <number>`. A run begins, and the command says its id and the task's on a line of their own (`run id:`, `task id:`; a task has a number for people and an id for the API, a run, a decision and a goal have ids alone, and every command that makes one prints it so, `--json` the whole answer; an id is read from there, never assumed): the task is in progress and the person's, and every report, hand-in, question and ending from here acts on this run. With another run of the person's open in the project, which `console take` says, each of those commands names the run: `--run <id>`. Taken by mistake, `console abandon` leaves it and the task stays as it was. A run the board has not heard from for twelve hours (what the deployment sets), and that is not waiting for an answer, is ended by the board itself as abandoned (ADR-0041): report at the milestones, wait for a decision with `--wait` or come back for it, and, refused with `run.over` on a run ended that way, take the task again.
|
|
58
60
|
|
|
59
61
|
### Phase 3: Work, and report at the milestones
|
|
60
62
|
|
|
@@ -62,7 +64,7 @@ The work happens where it happens: in the repository, the document, the tool. At
|
|
|
62
64
|
|
|
63
65
|
### Phase 4: Ask when a person must decide
|
|
64
66
|
|
|
65
|
-
A choice the body does not settle, a trade-off, a cost, a change of scope: `console ask "<question>" --option "<one>" --option "<another>" --body "<what they need to know>"`. The run waits for the answer; the command waits a minute and exits with `3` while it is still to come. Keep waiting with `console decision <id> --wait 100`, as often as needed, and meanwhile do only what does not depend on the answer. Act on the answer as given, which may be the person's own words rather than one of the options, marked as theirs; what was answered is not asked again. Once acted on, `console decision <id> --outcome "<what followed>"` makes the decision read as a record.
|
|
67
|
+
A choice the body does not settle, a trade-off, a cost, a change of scope: `console ask "<question>" --option "<one>" --option "<another>" --body "<what they need to know>"`. The decision is for the person you work for, who finds it among their decisions; `--to <login>` addresses it to another person of the project when the task or the rules name who decides such things (ADR-0042). The run waits for the answer; the command waits a minute and exits with `3` while it is still to come. Keep waiting with `console decision <id> --wait 100`, as often as needed, and meanwhile do only what does not depend on the answer. Act on the answer as given, which may be the person's own words rather than one of the options, marked as theirs; what was answered is not asked again. An answer that reaches you anywhere but the board, in chat, in a comment, in the task changed, is written on the decision in the person's name first, `console answer <id> --option <n>|--own "<their words>" --note "<where and when they said it>"`, and acted on from there; a question the work overtook is answered the same way, with the person's word on it, however short. Once acted on, `console decision <id> --outcome "<what followed>"` makes the decision read as a record.
|
|
66
68
|
|
|
67
69
|
### Phase 5: Hand in
|
|
68
70
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
- **Goal**: what the project works towards, on its roadmap, with the day it is meant to be reached by; its progress is its tasks.
|
|
5
5
|
- **Category**: a kind of work, the workspace's word for it, by key.
|
|
6
6
|
- **Run**: one agent at work on one task for one person, with its reports, what it handed in, and what it waits for.
|
|
7
|
-
- **Decision**: a question
|
|
7
|
+
- **Decision**: a choice that is a person's to make, kept as a record on the board: the question, the options, whom it is for ([ADR-0042](/docs/adr/0042-a-decision-is-addressed-to-a-person-its-raiser-unless-another-is-named.md)), the answer with who gave it and when, and what followed. It is answered on the board whoever decided it where: a person who answers in chat, in a meeting or by changing the task has decided, and the agent writes that answer down for them (`console answer`), so that the board shows as waiting only what is still open.
|
|
8
8
|
- **Page**: a document of the project, Markdown at a path that does not change.
|
|
9
9
|
- **Project**: a board of its own, with its members and its skills address.
|
|
10
10
|
- **SkillCDN token**: what the console reads a private pack with, `pack-token` in the API and the command: a project's own, or the workspace's for the organization's pack, which a project reads when it names no address of its own; connected with a code a person approves at SkillCDN, or made there by hand, and shown to nobody.
|
package/src/api.ts
CHANGED
|
@@ -191,6 +191,7 @@ export {
|
|
|
191
191
|
type RestMemberPatch,
|
|
192
192
|
type RestMembers,
|
|
193
193
|
type RestMePatch,
|
|
194
|
+
type RestMyDecisions,
|
|
194
195
|
type RestOrganization,
|
|
195
196
|
type RestPackConnect,
|
|
196
197
|
type RestPackConnectStarted,
|
|
@@ -286,6 +287,7 @@ export {
|
|
|
286
287
|
restMembersSchema,
|
|
287
288
|
restMePatchSchema,
|
|
288
289
|
restMeSchema,
|
|
290
|
+
restMyDecisionsSchema,
|
|
289
291
|
restOrganizationSchema,
|
|
290
292
|
restPackConnectSchema,
|
|
291
293
|
restPackConnectStartedSchema,
|
package/src/cli/cli.ts
CHANGED
|
@@ -1172,8 +1172,37 @@ const skills: Command = async (args, io) => {
|
|
|
1172
1172
|
return EXIT.ok;
|
|
1173
1173
|
};
|
|
1174
1174
|
|
|
1175
|
+
/** The most characters one read of a file asks for: what the console's API allows on one page. */
|
|
1176
|
+
const MAX_FILE_LIMIT = 100_000;
|
|
1177
|
+
|
|
1178
|
+
/** A count given as a flag, within its bounds, or nothing; anything else is not understood. */
|
|
1179
|
+
function countOf(
|
|
1180
|
+
given: string | undefined,
|
|
1181
|
+
name: string,
|
|
1182
|
+
bounds: { readonly least: number; readonly most?: number },
|
|
1183
|
+
): number | undefined {
|
|
1184
|
+
if (given === undefined) {
|
|
1185
|
+
return undefined;
|
|
1186
|
+
}
|
|
1187
|
+
const count = Number(given);
|
|
1188
|
+
if (
|
|
1189
|
+
!Number.isInteger(count) ||
|
|
1190
|
+
count < bounds.least ||
|
|
1191
|
+
(bounds.most !== undefined && count > bounds.most)
|
|
1192
|
+
) {
|
|
1193
|
+
const range =
|
|
1194
|
+
bounds.most === undefined ? `${bounds.least} or more` : `${bounds.least} to ${bounds.most}`;
|
|
1195
|
+
throw misuse(`Not a number for --${name}, ${range}: ${given}`);
|
|
1196
|
+
}
|
|
1197
|
+
return count;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1175
1200
|
const skill: Command = async (args, io) => {
|
|
1176
|
-
const { values, positionals } = parse(args, {
|
|
1201
|
+
const { values, positionals } = parse(args, {
|
|
1202
|
+
file: { type: "string" },
|
|
1203
|
+
offset: { type: "string" },
|
|
1204
|
+
limit: { type: "string" },
|
|
1205
|
+
});
|
|
1177
1206
|
const file = text(values, "file");
|
|
1178
1207
|
// A file is read by its own path from the repository's root; a skill's path beside it would be
|
|
1179
1208
|
// ignored, which reads as if it mattered, so the two together are not understood.
|
|
@@ -1182,6 +1211,17 @@ const skill: Command = async (args, io) => {
|
|
|
1182
1211
|
"Say one: console skill <path of its SKILL.md> for a skill, or console skill --file <path> for a file of the pack.",
|
|
1183
1212
|
);
|
|
1184
1213
|
}
|
|
1214
|
+
// A section is a page of a file read as asked (SkillCDN 0.4): the flags mean nothing without one.
|
|
1215
|
+
if (
|
|
1216
|
+
file === undefined &&
|
|
1217
|
+
(text(values, "offset") !== undefined || text(values, "limit") !== undefined)
|
|
1218
|
+
) {
|
|
1219
|
+
throw misuse(
|
|
1220
|
+
"--offset and --limit go with --file: console skill --file <path> [--offset <n>] [--limit <n>]",
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
const offset = countOf(text(values, "offset"), "offset", { least: 0 });
|
|
1224
|
+
const limit = countOf(text(values, "limit"), "limit", { least: 1, most: MAX_FILE_LIMIT });
|
|
1185
1225
|
const session = await open(io, values);
|
|
1186
1226
|
const project = await session.project();
|
|
1187
1227
|
const { listing, summary } = await packOf(project);
|
|
@@ -1189,6 +1229,16 @@ const skill: Command = async (args, io) => {
|
|
|
1189
1229
|
answer(io, session, listing, () => formatSkills(listing, io.skillsDir));
|
|
1190
1230
|
return EXIT.ok;
|
|
1191
1231
|
}
|
|
1232
|
+
if (file !== undefined && (offset !== undefined || limit !== undefined)) {
|
|
1233
|
+
// One page as asked, the section a search or an outline named; the result says where the
|
|
1234
|
+
// file continues, and asks for nothing.
|
|
1235
|
+
const page = await project.skillFile(file, { offset, limit });
|
|
1236
|
+
const section = page.read === null ? undefined : joinFile([page], summary, limit);
|
|
1237
|
+
answer(io, session, { ...page, read: section === undefined ? null : bodyOfFile(section) }, () =>
|
|
1238
|
+
formatSkillFile(page, section),
|
|
1239
|
+
);
|
|
1240
|
+
return EXIT.ok;
|
|
1241
|
+
}
|
|
1192
1242
|
if (file !== undefined) {
|
|
1193
1243
|
// The file whole, page after page, as the console reads it from the pack.
|
|
1194
1244
|
const pages: RestSkillFile[] = [];
|
|
@@ -1800,11 +1850,16 @@ const ask: Command = async (args, io) => {
|
|
|
1800
1850
|
file: { type: "string" },
|
|
1801
1851
|
run: { type: "string" },
|
|
1802
1852
|
wait: { type: "string" },
|
|
1853
|
+
to: { type: "string" },
|
|
1803
1854
|
});
|
|
1804
1855
|
const question = theOne(
|
|
1805
1856
|
positionals,
|
|
1806
1857
|
'Say the question: console ask "<question>" --option "<label>" --option "<label>"',
|
|
1807
1858
|
);
|
|
1859
|
+
const to = text(values, "to");
|
|
1860
|
+
if (to === "none") {
|
|
1861
|
+
throw misuse("Say whom the decision is for: --to <login|me>.");
|
|
1862
|
+
}
|
|
1808
1863
|
const options = texts(values, "option");
|
|
1809
1864
|
if (options.length < MIN_OPTIONS || options.length > MAX_OPTIONS) {
|
|
1810
1865
|
throw misuse(`Give ${MIN_OPTIONS} to ${MAX_OPTIONS} options, one --option each.`);
|
|
@@ -1814,21 +1869,58 @@ const ask: Command = async (args, io) => {
|
|
|
1814
1869
|
const session = await open(io, values);
|
|
1815
1870
|
const project = await session.project();
|
|
1816
1871
|
const runId = await currentRun(project, values);
|
|
1872
|
+
// Whom the decision is for (ADR-0042): the person the agent works for, unless --to names another.
|
|
1873
|
+
const deciderId = await personIdOf(session, to);
|
|
1817
1874
|
const raised = await project.raiseDecision({
|
|
1818
1875
|
question,
|
|
1819
1876
|
options: options.map((label) => ({ label })),
|
|
1820
1877
|
runId,
|
|
1821
1878
|
...(body === undefined ? {} : { body }),
|
|
1879
|
+
...(typeof deciderId === "string" ? { deciderId } : {}),
|
|
1822
1880
|
});
|
|
1823
1881
|
// The id is printed before any waiting, so that a wait cut short loses nothing.
|
|
1824
1882
|
io.stdout(
|
|
1825
1883
|
session.json
|
|
1826
1884
|
? `${JSON.stringify(raised, null, 2)}\n`
|
|
1827
|
-
: `Decision ${raised.id} raised on run ${runId}; the run waits for
|
|
1885
|
+
: `Decision ${raised.id} raised on run ${runId}; the run waits for ${raised.decider.login}.\ndecision id: ${raised.id}\n${formatOptions(raised)}\n`,
|
|
1828
1886
|
);
|
|
1829
1887
|
return settle(io, session, project, raised, seconds, true);
|
|
1830
1888
|
};
|
|
1831
1889
|
|
|
1890
|
+
/**
|
|
1891
|
+
* Writes a person's answer on a decision, in their name through the agent (ADR-0021): the
|
|
1892
|
+
* option they chose, or their own words, and a note about it, so that a choice made in chat
|
|
1893
|
+
* or in passing reaches the record the run waits on.
|
|
1894
|
+
*/
|
|
1895
|
+
const answering: Command = async (args, io) => {
|
|
1896
|
+
const { values, positionals } = parse(args, {
|
|
1897
|
+
option: { type: "string" },
|
|
1898
|
+
own: { type: "string" },
|
|
1899
|
+
note: { type: "string" },
|
|
1900
|
+
file: { type: "string" },
|
|
1901
|
+
});
|
|
1902
|
+
const id = theOne(
|
|
1903
|
+
positionals,
|
|
1904
|
+
'Say which decision: console answer <id> --option <n>|--own "<answer>" [--note <markdown>|--file <path>]',
|
|
1905
|
+
);
|
|
1906
|
+
const option = text(values, "option");
|
|
1907
|
+
const own = text(values, "own");
|
|
1908
|
+
if ((option === undefined) === (own === undefined)) {
|
|
1909
|
+
throw misuse(
|
|
1910
|
+
'Say the option chosen, --option <n>, or the person\'s own answer, --own "<words>": one of the two.',
|
|
1911
|
+
);
|
|
1912
|
+
}
|
|
1913
|
+
const note = await bodyOf(io, values, text(values, "note"));
|
|
1914
|
+
const session = await open(io, values);
|
|
1915
|
+
const project = await session.project();
|
|
1916
|
+
const answered = await project.answerDecision(id, {
|
|
1917
|
+
...(own === undefined ? { option } : { own }),
|
|
1918
|
+
...(note === undefined ? {} : { note }),
|
|
1919
|
+
});
|
|
1920
|
+
answer(io, session, answered, () => formatDecision(answered));
|
|
1921
|
+
return EXIT.ok;
|
|
1922
|
+
};
|
|
1923
|
+
|
|
1832
1924
|
const decision: Command = async (args, io) => {
|
|
1833
1925
|
const { values, positionals } = parse(args, {
|
|
1834
1926
|
wait: { type: "string" },
|
|
@@ -2139,14 +2231,37 @@ const goal: Command = async (args, io) => {
|
|
|
2139
2231
|
};
|
|
2140
2232
|
|
|
2141
2233
|
const decisions: Command = async (args, io) => {
|
|
2142
|
-
const { values } = parse(
|
|
2234
|
+
const { values } = parse(
|
|
2235
|
+
args,
|
|
2236
|
+
{
|
|
2237
|
+
open: { type: "boolean" },
|
|
2238
|
+
mine: { type: "boolean" },
|
|
2239
|
+
raised: { type: "boolean" },
|
|
2240
|
+
...PAGE_OPTIONS,
|
|
2241
|
+
},
|
|
2242
|
+
false,
|
|
2243
|
+
);
|
|
2143
2244
|
const page = pageOf(values);
|
|
2144
2245
|
const session = await open(io, values);
|
|
2246
|
+
const open_ = on(values, "open") ? { open: true } : {};
|
|
2247
|
+
if (on(values, "mine") || on(values, "raised")) {
|
|
2248
|
+
// One's own across the projects (ADR-0042), addressed to the person or raised by them for
|
|
2249
|
+
// someone else: the waiting first, each with its project's key.
|
|
2250
|
+
const raised = on(values, "raised");
|
|
2251
|
+
const found = await session.client.myDecisions({ ...open_, raised, ...page });
|
|
2252
|
+
answer(io, session, found, () =>
|
|
2253
|
+
found.items.length === 0
|
|
2254
|
+
? raised
|
|
2255
|
+
? "No decisions you asked of others."
|
|
2256
|
+
: "No decisions of yours."
|
|
2257
|
+
: found.items
|
|
2258
|
+
.map(({ project, decision }) => `${project.key} ${formatDecisionLine(decision)}`)
|
|
2259
|
+
.join("\n") + formatMore(found.more, page.limit, page.offset),
|
|
2260
|
+
);
|
|
2261
|
+
return EXIT.ok;
|
|
2262
|
+
}
|
|
2145
2263
|
const project = await session.project();
|
|
2146
|
-
const found = await project.decisions({
|
|
2147
|
-
...(on(values, "open") ? { open: true } : {}),
|
|
2148
|
-
...page,
|
|
2149
|
-
});
|
|
2264
|
+
const found = await project.decisions({ ...open_, ...page });
|
|
2150
2265
|
answer(io, session, found, () =>
|
|
2151
2266
|
found.items.length === 0
|
|
2152
2267
|
? "No decisions."
|
|
@@ -2869,6 +2984,7 @@ const COMMANDS: Readonly<Record<string, Command>> = {
|
|
|
2869
2984
|
comment,
|
|
2870
2985
|
file,
|
|
2871
2986
|
ask,
|
|
2987
|
+
answer: answering,
|
|
2872
2988
|
decision,
|
|
2873
2989
|
decisions,
|
|
2874
2990
|
finish: ending("finished"),
|
package/src/cli/format.ts
CHANGED
|
@@ -386,7 +386,10 @@ export function formatWork(work: RestWork): string {
|
|
|
386
386
|
lines.push(` ${project.key} ${formatTaskLine(task)}`);
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
|
-
lines.push(
|
|
389
|
+
lines.push(
|
|
390
|
+
"",
|
|
391
|
+
work.decisions.length === 0 ? "decisions waiting for you: none" : "decisions waiting for you:",
|
|
392
|
+
);
|
|
390
393
|
for (const { project, decision } of work.decisions) {
|
|
391
394
|
lines.push(` ${project.key} ${formatDecisionLine(decision)}`);
|
|
392
395
|
}
|
|
@@ -404,9 +407,13 @@ export function formatPersonWork(person: RestPerson, work: RestPersonWork): stri
|
|
|
404
407
|
].join("\n");
|
|
405
408
|
}
|
|
406
409
|
|
|
410
|
+
/** Whom a decision is for, where that is not the one who asked (ADR-0042). */
|
|
411
|
+
const addressed = (decision: RestDecision): string =>
|
|
412
|
+
decision.decider.id === decision.raisedBy.id ? "" : `, for ${decision.decider.login}`;
|
|
413
|
+
|
|
407
414
|
export function formatDecisionLine(decision: RestDecision): string {
|
|
408
415
|
const state = decision.answer === null ? "waiting" : `answered ${decision.answer.option}`;
|
|
409
|
-
return `${decision.id} ${state} ${decision.question} (asked by ${asker(decision)})`;
|
|
416
|
+
return `${decision.id} ${state} ${decision.question} (asked by ${asker(decision)}${addressed(decision)})`;
|
|
410
417
|
}
|
|
411
418
|
|
|
412
419
|
/** The options, one a line; the person's own answer, when they wrote one, is the last and says so. */
|
|
@@ -423,7 +430,7 @@ export function formatOptions(decision: RestDecision): string {
|
|
|
423
430
|
export function formatAnswer(decision: RestDecision): string {
|
|
424
431
|
const { answer } = decision;
|
|
425
432
|
if (answer === null) {
|
|
426
|
-
return
|
|
433
|
+
return `waits for ${decision.decider.login}.`;
|
|
427
434
|
}
|
|
428
435
|
const chosen = decision.options.find((option) => option.id === answer.option);
|
|
429
436
|
const lines = [
|
|
@@ -438,7 +445,7 @@ export function formatAnswer(decision: RestDecision): string {
|
|
|
438
445
|
export function formatDecision(decision: RestDecision): string {
|
|
439
446
|
const lines = [
|
|
440
447
|
`decision ${decision.id}: ${decision.question}`,
|
|
441
|
-
`asked by ${asker(decision)}${decision.taskNumber === null ? "" : `, about task #${decision.taskNumber}`}`,
|
|
448
|
+
`asked by ${asker(decision)}${addressed(decision)}${decision.taskNumber === null ? "" : `, about task #${decision.taskNumber}`}`,
|
|
442
449
|
];
|
|
443
450
|
if (decision.body.trim().length > 0) {
|
|
444
451
|
lines.push("", decision.body.trimEnd());
|