@skillcdn/console 0.1.23 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/api.d.ts +1 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +1 -1
- package/dist/api.js.map +1 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +80 -54
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/format.d.ts +4 -28
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +7 -260
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/help.d.ts +6 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +47 -24
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/pack.d.ts +46 -0
- package/dist/cli/pack.d.ts.map +1 -0
- package/dist/cli/pack.js +250 -0
- package/dist/cli/pack.js.map +1 -0
- package/dist/compat.d.ts +1 -1
- package/dist/compat.js +1 -1
- package/dist/components/decision-list.d.ts +13 -1
- package/dist/components/decision-list.d.ts.map +1 -1
- package/dist/components/decision-list.js +74 -29
- package/dist/components/decision-list.js.map +1 -1
- package/dist/components/paged.d.ts +24 -0
- package/dist/components/paged.d.ts.map +1 -1
- package/dist/components/paged.js +40 -4
- package/dist/components/paged.js.map +1 -1
- package/dist/components/skills.d.ts.map +1 -1
- package/dist/components/skills.js +8 -4
- package/dist/components/skills.js.map +1 -1
- package/dist/components/ui.d.ts +2 -0
- package/dist/components/ui.d.ts.map +1 -1
- package/dist/components/ui.js +7 -2
- package/dist/components/ui.js.map +1 -1
- package/dist/components/work.d.ts +2 -0
- package/dist/components/work.d.ts.map +1 -1
- package/dist/components/work.js +3 -2
- package/dist/components/work.js.map +1 -1
- package/dist/console.css +112 -5
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +5 -3
- package/dist/console.js.map +1 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +27 -2
- package/dist/data.js.map +1 -1
- package/dist/i18n/en.d.ts +13 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +15 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/ko.d.ts.map +1 -1
- package/dist/i18n/ko.js +15 -0
- package/dist/i18n/ko.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/router.d.ts +6 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +13 -7
- package/dist/router.js.map +1 -1
- package/dist/rules.js +3 -3
- package/dist/rules.js.map +1 -1
- package/dist/schemas.d.ts +557 -162
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +72 -158
- package/dist/schemas.js.map +1 -1
- package/docs/specs/cli.md +12 -12
- package/docs/specs/rest.md +7 -15
- package/package.json +2 -1
- package/skills/deploying-a-console/SKILL.md +2 -2
- package/skills/working-the-board/SKILL.md +9 -14
- package/skills/working-the-board/references/setting-up.md +3 -0
- package/skills/working-the-board/references/terms.md +9 -0
- package/skills/working-the-board/references/the-sentence.md +5 -0
- package/src/api.ts +2 -7
- package/src/cli/cli.ts +113 -62
- package/src/cli/format.ts +10 -324
- package/src/cli/help.ts +53 -27
- package/src/cli/pack.ts +356 -0
- package/src/compat.ts +1 -1
- package/src/components/decision-list.tsx +227 -94
- package/src/components/paged.tsx +72 -3
- package/src/components/skills.tsx +51 -33
- package/src/components/ui.tsx +16 -3
- package/src/components/work.tsx +9 -5
- package/src/console.tsx +8 -2
- package/src/data.ts +32 -2
- package/src/i18n/en.ts +18 -0
- package/src/i18n/ko.ts +17 -0
- package/src/index.ts +9 -1
- package/src/router.ts +20 -9
- package/src/rules.ts +3 -3
- package/src/schemas.ts +87 -184
- package/src/styles/console.css +112 -5
package/src/console.tsx
CHANGED
|
@@ -98,6 +98,7 @@ import {
|
|
|
98
98
|
calendarHref,
|
|
99
99
|
customPageHref,
|
|
100
100
|
decisionHref,
|
|
101
|
+
decisionsHref,
|
|
101
102
|
docHref,
|
|
102
103
|
type GoalTaskView,
|
|
103
104
|
goalHref,
|
|
@@ -1112,6 +1113,8 @@ function ProjectPage(props: {
|
|
|
1112
1113
|
taskHref={href}
|
|
1113
1114
|
docHref={toDoc}
|
|
1114
1115
|
decisionHref={(decision) => decisionHref(key, decision.id)}
|
|
1116
|
+
page={route.page}
|
|
1117
|
+
pageHref={(page) => decisionsHref(key, page)}
|
|
1115
1118
|
onAnswer={onAnswer}
|
|
1116
1119
|
onUpdate={onUpdate}
|
|
1117
1120
|
busy={busy}
|
|
@@ -1748,9 +1751,10 @@ function PersonAgentsPage(props: {
|
|
|
1748
1751
|
);
|
|
1749
1752
|
}
|
|
1750
1753
|
|
|
1751
|
-
/** What a plate's page shows: the tasks, and the decisions waiting on the person's own. */
|
|
1754
|
+
/** What a plate's page shows: the tasks, open and lately closed, and the decisions waiting on the person's own. */
|
|
1752
1755
|
interface Plate {
|
|
1753
1756
|
readonly tasks: RestWork["tasks"];
|
|
1757
|
+
readonly closed?: RestWork["closed"] | undefined;
|
|
1754
1758
|
readonly decisions?: RestWork["decisions"] | undefined;
|
|
1755
1759
|
}
|
|
1756
1760
|
|
|
@@ -1825,7 +1829,9 @@ function PlatePage(props: {
|
|
|
1825
1829
|
<Spinner label={props.loading} />
|
|
1826
1830
|
</div>
|
|
1827
1831
|
)
|
|
1828
|
-
) : plate.tasks.length === 0 &&
|
|
1832
|
+
) : plate.tasks.length === 0 &&
|
|
1833
|
+
(plate.closed?.length ?? 0) === 0 &&
|
|
1834
|
+
(plate.decisions?.length ?? 0) === 0 ? (
|
|
1829
1835
|
<EmptyState title={props.empty.title} body={props.empty.body} />
|
|
1830
1836
|
) : (
|
|
1831
1837
|
<components.WorkList
|
package/src/data.ts
CHANGED
|
@@ -245,6 +245,28 @@ async function readTasks(
|
|
|
245
245
|
return { items };
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
/** How many pages of the decisions are read before the record is shown as it stands. */
|
|
249
|
+
const DECISIONS_MAX_PAGES = 20;
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* The decisions of a project, whole: page after page while the server says there is more, so
|
|
253
|
+
* that the record is read to its end and the pages turn it without asking again.
|
|
254
|
+
*/
|
|
255
|
+
async function readDecisions(
|
|
256
|
+
scope: ProjectClient,
|
|
257
|
+
signal: AbortSignal | undefined,
|
|
258
|
+
): Promise<{ readonly items: RestDecision[] }> {
|
|
259
|
+
const items: RestDecision[] = [];
|
|
260
|
+
for (let page = 0; page < DECISIONS_MAX_PAGES; page += 1) {
|
|
261
|
+
const result = await scope.decisions({ limit: LIST_LIMIT, offset: page * LIST_LIMIT }, signal);
|
|
262
|
+
items.push(...result.items);
|
|
263
|
+
if (!result.more) {
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return { items };
|
|
268
|
+
}
|
|
269
|
+
|
|
248
270
|
const wordsOf = (failure: unknown, fallback: string): string =>
|
|
249
271
|
failure instanceof ApiError ? failure.message : fallback;
|
|
250
272
|
|
|
@@ -310,7 +332,7 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
|
|
|
310
332
|
await Promise.all([
|
|
311
333
|
scope.get(signal),
|
|
312
334
|
readTasks(scope, signal),
|
|
313
|
-
scope
|
|
335
|
+
readDecisions(scope, signal),
|
|
314
336
|
scope.goals({}, signal),
|
|
315
337
|
scope.runs(undefined, signal),
|
|
316
338
|
scope.members(signal),
|
|
@@ -427,7 +449,15 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
|
|
|
427
449
|
// No answer at all is said on the page as the deployment being out of reach,
|
|
428
450
|
// rather than waited for.
|
|
429
451
|
if (!signal.aborted) {
|
|
430
|
-
setSkills({
|
|
452
|
+
setSkills({
|
|
453
|
+
address: null,
|
|
454
|
+
source: "",
|
|
455
|
+
page: null,
|
|
456
|
+
status: "unavailable",
|
|
457
|
+
refusal: null,
|
|
458
|
+
items: [],
|
|
459
|
+
mount: null,
|
|
460
|
+
});
|
|
431
461
|
}
|
|
432
462
|
});
|
|
433
463
|
if (typeof EventSource === "undefined") {
|
package/src/i18n/en.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
ProjectVisibility,
|
|
10
10
|
ProviderKey,
|
|
11
11
|
RunStatus,
|
|
12
|
+
SkillsStatus,
|
|
12
13
|
TaskPriority,
|
|
13
14
|
TaskState,
|
|
14
15
|
} from "../vocabulary.js";
|
|
@@ -102,6 +103,12 @@ export const en = {
|
|
|
102
103
|
copy: "Copy",
|
|
103
104
|
copied: "Copied",
|
|
104
105
|
showMore: (n: number, of: number) => `Show ${n} more of ${of}`,
|
|
106
|
+
pager: {
|
|
107
|
+
label: "Pages",
|
|
108
|
+
previous: "Previous page",
|
|
109
|
+
next: "Next page",
|
|
110
|
+
range: (from: number, to: number, of: number) => `${from}-${to} of ${of}`,
|
|
111
|
+
},
|
|
105
112
|
},
|
|
106
113
|
|
|
107
114
|
nav: {
|
|
@@ -325,6 +332,9 @@ export const en = {
|
|
|
325
332
|
waitingForPerson: "Waiting for a person.",
|
|
326
333
|
options: "Options",
|
|
327
334
|
notePlaceholder: "A word about it, if any",
|
|
335
|
+
own: "Something else",
|
|
336
|
+
ownPlaceholder: "Your answer, in a line",
|
|
337
|
+
ownAnswer: "in their own words",
|
|
328
338
|
answer: "Answer",
|
|
329
339
|
asked: "asked",
|
|
330
340
|
askedThrough: (agent: string) => `asked through ${agent}`,
|
|
@@ -662,6 +672,14 @@ export const en = {
|
|
|
662
672
|
connect:
|
|
663
673
|
"An agent reads these with the console command (console skill <path>), or through its own MCP connection to this address, which a person makes once:",
|
|
664
674
|
connectLink: "how, on the address's page at SkillCDN",
|
|
675
|
+
fromMachine: "Read from a directory on the console's machine, at",
|
|
676
|
+
connectLocal:
|
|
677
|
+
"An agent reads these with the console command (console skill <path>); the pack is on the console's machine and nowhere else.",
|
|
678
|
+
localStatus: {
|
|
679
|
+
not_found:
|
|
680
|
+
"No pack is served from the console's machine at this address: whoever runs the console puts it in the packs directory, as <owner>/<name>.",
|
|
681
|
+
unavailable: "The pack could not be read from the console's machine. Look again later.",
|
|
682
|
+
} as Partial<Record<SkillsStatus, string>>,
|
|
665
683
|
status: {
|
|
666
684
|
indexing: "SkillCDN is still indexing the repository. Look again in a moment.",
|
|
667
685
|
failed: "SkillCDN could not index the repository. What it found is on the address's page.",
|
package/src/i18n/ko.ts
CHANGED
|
@@ -72,6 +72,12 @@ export const ko: Messages = {
|
|
|
72
72
|
copy: "복사",
|
|
73
73
|
copied: "복사됨",
|
|
74
74
|
showMore: (n, of) => `${of}개 중 ${n}개 더 보기`,
|
|
75
|
+
pager: {
|
|
76
|
+
label: "페이지",
|
|
77
|
+
previous: "이전",
|
|
78
|
+
next: "다음",
|
|
79
|
+
range: (from, to, of) => `${of}건 중 ${from}-${to}`,
|
|
80
|
+
},
|
|
75
81
|
},
|
|
76
82
|
|
|
77
83
|
nav: {
|
|
@@ -294,6 +300,9 @@ export const ko: Messages = {
|
|
|
294
300
|
waitingForPerson: "사람을 기다리는 중입니다.",
|
|
295
301
|
options: "선택지",
|
|
296
302
|
notePlaceholder: "덧붙일 말이 있다면",
|
|
303
|
+
own: "직접 입력",
|
|
304
|
+
ownPlaceholder: "원하는 답을 한 줄로",
|
|
305
|
+
ownAnswer: "직접 입력",
|
|
297
306
|
answer: "답변",
|
|
298
307
|
asked: "질문",
|
|
299
308
|
askedThrough: (agent) => `${agent}을(를) 통해 질문`,
|
|
@@ -626,6 +635,14 @@ export const ko: Messages = {
|
|
|
626
635
|
connect:
|
|
627
636
|
"에이전트는 콘솔 명령(console skill <경로>)으로, 또는 이 주소에 자기 MCP 연결로 스킬을 받습니다. MCP 연결은 사람이 한 번 해 줍니다:",
|
|
628
637
|
connectLink: "SkillCDN 페이지의 연결 안내",
|
|
638
|
+
fromMachine: "콘솔 기계의 디렉터리에서 읽음, 주소:",
|
|
639
|
+
connectLocal:
|
|
640
|
+
"에이전트는 콘솔 명령(console skill <경로>)으로 읽습니다. 이 팩은 콘솔 기계에만 있습니다.",
|
|
641
|
+
localStatus: {
|
|
642
|
+
not_found:
|
|
643
|
+
"이 주소로 콘솔 기계에서 제공되는 팩이 없습니다. 콘솔 운영자가 팩 디렉터리에 <owner>/<name>으로 넣습니다.",
|
|
644
|
+
unavailable: "콘솔 기계에서 팩을 읽지 못했습니다. 나중에 다시 보세요.",
|
|
645
|
+
},
|
|
629
646
|
status: {
|
|
630
647
|
indexing: "SkillCDN이 아직 저장소를 색인하고 있습니다. 잠시 후 다시 보세요.",
|
|
631
648
|
failed: "SkillCDN이 저장소를 색인하지 못했습니다. 찾은 내용은 주소의 페이지에 있습니다.",
|
package/src/index.ts
CHANGED
|
@@ -82,7 +82,14 @@ export {
|
|
|
82
82
|
goalMonth,
|
|
83
83
|
} from "./components/goals.js";
|
|
84
84
|
export { Markdown, type MarkdownProps } from "./components/markdown.js";
|
|
85
|
-
export {
|
|
85
|
+
export {
|
|
86
|
+
PAGE_SIZE,
|
|
87
|
+
Pager,
|
|
88
|
+
pageOf,
|
|
89
|
+
RECORD_PAGE_SIZE,
|
|
90
|
+
ShowMore,
|
|
91
|
+
usePaged,
|
|
92
|
+
} from "./components/paged.js";
|
|
86
93
|
export {
|
|
87
94
|
ListingForm,
|
|
88
95
|
type ListingFormProps,
|
|
@@ -246,6 +253,7 @@ export {
|
|
|
246
253
|
customPageHref,
|
|
247
254
|
type DocsView,
|
|
248
255
|
decisionHref,
|
|
256
|
+
decisionsHref,
|
|
249
257
|
docHref,
|
|
250
258
|
GOAL_TASK_VIEWS,
|
|
251
259
|
type GoalPageOptions,
|
package/src/router.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { isDay, isMonth } from "./dates.js";
|
|
|
5
5
|
// route, and the hrefs below are its inverse. A project's pages live under `/projects/<key>`,
|
|
6
6
|
// the word the REST API uses; an address names what it shows (a task by its number, a decision
|
|
7
7
|
// by its id, a document by its path, a goal by its id), every form has one, and the query says
|
|
8
|
-
// how a page is shown (`?edit`, `?ask`, `?new`, `?version=`, `?q=`, `?month=`, `?week=`, and the
|
|
8
|
+
// how a page is shown (`?edit`, `?ask`, `?new`, `?version=`, `?q=`, `?month=`, `?week=`, `?page=`, and the
|
|
9
9
|
// board's filters). The old addresses are taken to the new.
|
|
10
10
|
|
|
11
11
|
/** How a task's page is shown: as it is, or with one of its forms open. */
|
|
@@ -49,7 +49,8 @@ export type Route =
|
|
|
49
49
|
readonly path: string;
|
|
50
50
|
readonly view: DocsView;
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
/** The decisions of a project: the waiting ones, and the record on one of its pages, the first when the address names none. */
|
|
53
|
+
| { readonly name: "decisions"; readonly project: string; readonly page: number | undefined }
|
|
53
54
|
| { readonly name: "new-decision"; readonly project: string }
|
|
54
55
|
| { readonly name: "decision"; readonly project: string; readonly id: string }
|
|
55
56
|
/** The calendar of a project: the month shown, or this one when the address names none, narrowed as the board is. */
|
|
@@ -155,13 +156,10 @@ const NUMBER = /^[1-9][0-9]{0,8}$/;
|
|
|
155
156
|
/** A code as it may be typed into an address: checked for what it is by the page. */
|
|
156
157
|
const CODE_TEXT = /^[A-Za-z0-9-]{1,16}$/;
|
|
157
158
|
/** The pages of a project that take nothing after their name. */
|
|
158
|
-
const PLAIN_PAGES: readonly Exclude<
|
|
159
|
-
|
|
160
|
-
"
|
|
161
|
-
|
|
162
|
-
"skills",
|
|
163
|
-
"members",
|
|
164
|
-
];
|
|
159
|
+
const PLAIN_PAGES: readonly Exclude<
|
|
160
|
+
ProjectPage,
|
|
161
|
+
"docs" | "decisions" | "calendar" | "roadmap" | "settings"
|
|
162
|
+
>[] = ["archive", "feed", "skills", "members"];
|
|
165
163
|
const NOT_FOUND: Route = { name: "not-found" };
|
|
166
164
|
|
|
167
165
|
/** Where an address of the 0.1 line before this one leads now, or nothing for a current one. */
|
|
@@ -386,6 +384,13 @@ export function matchRoute(pathname: string, search = ""): Route {
|
|
|
386
384
|
: undefined,
|
|
387
385
|
};
|
|
388
386
|
}
|
|
387
|
+
if (page === "decisions") {
|
|
388
|
+
const turned = params.get("page");
|
|
389
|
+
if (turned !== null && !NUMBER.test(turned)) {
|
|
390
|
+
return NOT_FOUND;
|
|
391
|
+
}
|
|
392
|
+
return { name: "decisions", project, page: turned === null ? undefined : Number(turned) };
|
|
393
|
+
}
|
|
389
394
|
const found = PLAIN_PAGES.find((candidate) => candidate === page);
|
|
390
395
|
return found === undefined ? NOT_FOUND : { name: found, project };
|
|
391
396
|
}
|
|
@@ -469,6 +474,12 @@ export function decisionHref(key: string, id: string): string {
|
|
|
469
474
|
return `${PATHS.project}/${key}/decisions/${id}`;
|
|
470
475
|
}
|
|
471
476
|
|
|
477
|
+
/** Where a project's decisions are: the waiting ones and the record, on one of its pages, or on the first when none is named. */
|
|
478
|
+
export function decisionsHref(key: string, page?: number): string {
|
|
479
|
+
const base = projectHref(key, "decisions");
|
|
480
|
+
return page === undefined || page <= 1 ? base : `${base}?page=${page}`;
|
|
481
|
+
}
|
|
482
|
+
|
|
472
483
|
/** Where the person's own work is, with the week of a day at the top, or this week's when none is named. */
|
|
473
484
|
export function workHref(week?: string): string {
|
|
474
485
|
return week === undefined ? PATHS.work : `${PATHS.work}?week=${week}`;
|
package/src/rules.ts
CHANGED
|
@@ -23,13 +23,13 @@ export const REFERENCE_RULES = [
|
|
|
23
23
|
"",
|
|
24
24
|
"## Start here, every time",
|
|
25
25
|
"",
|
|
26
|
-
"1. These rules come with the task: `console take` prints them under it, and `console rules` reads them any time. Read them before anything is made. The task says where the work is and what it links to; `console task <number>` shows a task in full, with its plan, its decisions, the runs on it and the discussion on it, without taking it. A task put away (archived) is out of the listings and the board, and opens by its number still. How the board is worked is the skill working-the-board, shipped with the command
|
|
26
|
+
"1. These rules come with the task: `console take` prints them under it, and `console rules` reads them any time. Read them before anything is made. The task says where the work is and what it links to; `console task <number>` shows a task in full, with its plan, its decisions, the runs on it and the discussion on it, without taking it. A task put away (archived) is out of the listings and the board, and opens by its number still. How the board is worked is the skill working-the-board, shipped with the command (`console whoami` prints where it is): read it, if you have not yet, before anything is made; these rules say what this organization asks, not how the command works.",
|
|
27
27
|
"2. Act as the person whose token the command holds, and no further. What they may not do, the console refuses; that step goes back to them in a report, never around them.",
|
|
28
28
|
'3. Work given in chat, with no task on the board yet, goes on the board before it is done: write it (`console task new "<title>" --body <markdown>`), tell the person its number, take it (`console take <number>`) and work it as any task, without asking whether to. Until the task is written, the board is only read.',
|
|
29
29
|
"",
|
|
30
30
|
"## The organization's skills",
|
|
31
31
|
"",
|
|
32
|
-
"`console skills` lists the skills of this project's pack
|
|
32
|
+
"`console skills` lists the skills of this project's pack, each with the path of its SKILL.md, and `console skill <path>` reads one whole, its rules first, through the console, a private pack with the pack token the project holds (`console help skills` says how the pack is browsed and searched). 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
|
"",
|
|
@@ -45,7 +45,7 @@ export const REFERENCE_RULES = [
|
|
|
45
45
|
"",
|
|
46
46
|
"## Reports",
|
|
47
47
|
"",
|
|
48
|
-
"Report at the milestones, not every step: what was found, what was done, what is next, in words for people; a problem as a problem, with what it blocks. What people read on the board, the reports, the questions and their options, the tasks and the pages, is written in {language}. A task's title says what is to be done, in plain words that stand on their own; what it refers to, a milestone, a commit, a page, goes in the body, linked where it can be. The result goes in a last report or is handed in as a link or a file, with the check that ran and its result; a check that did not run is said to be not run. A report is the run's record; a word to the task's people, an answer to what they asked in its comments, a question that is not a decision, a remark, is a comment on the task
|
|
48
|
+
"Report at the milestones, not every step: what was found, what was done, what is next, in words for people; a problem as a problem, with what it blocks. What people read on the board, the reports, the questions and their options, the tasks and the pages, is written in {language}. A task's title says what is to be done, in plain words that stand on their own; what it refers to, a milestone, a commit, a page, goes in the body, linked where it can be. The result goes in a last report or is handed in as a link or a file, with the check that ran and its result; a check that did not run is said to be not run. A report is the run's record; a word to the task's people, an answer to what they asked in its comments, a question that is not a decision, a remark, is a comment on the task, not a report.",
|
|
49
49
|
"",
|
|
50
50
|
"## What never happens",
|
|
51
51
|
"",
|
package/src/schemas.ts
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
// most of what a page downloads. The server validates what it is sent with the input schemas
|
|
3
3
|
// and builds the output shapes; the pages and a custom console parse answers with the rest.
|
|
4
4
|
// Absent values are `null` on the wire, never missing keys. Changes within a version are additive.
|
|
5
|
+
import {
|
|
6
|
+
restBrowseSchema as packBrowseSchema,
|
|
7
|
+
restFileSchema as packFileSchema,
|
|
8
|
+
restFindSchema as packFindSchema,
|
|
9
|
+
restMountSchema as packMountSchema,
|
|
10
|
+
restSkillSchema as packSkillSchema,
|
|
11
|
+
} from "@skillcdn/core";
|
|
5
12
|
import * as z from "zod/mini";
|
|
6
13
|
import { isDocumentPath } from "./documents.js";
|
|
7
14
|
import {
|
|
@@ -548,9 +555,15 @@ export const restGoalPatchSchema = z.object({
|
|
|
548
555
|
export type RestGoalPatch = z.infer<typeof restGoalPatchSchema>;
|
|
549
556
|
|
|
550
557
|
export const restDecisionOptionSchema = z.object({
|
|
551
|
-
/** What an answer names: stable
|
|
558
|
+
/** What an answer names: stable once given. */
|
|
552
559
|
id: z.string(),
|
|
553
560
|
label: z.string(),
|
|
561
|
+
/**
|
|
562
|
+
* On the one option a person's own answer became, when they answered in their own words
|
|
563
|
+
* instead of choosing (`POST .../answer` with `own`): added then, after the options given,
|
|
564
|
+
* which carry no `own`.
|
|
565
|
+
*/
|
|
566
|
+
own: z.optional(z.literal(true)),
|
|
554
567
|
});
|
|
555
568
|
export type RestDecisionOption = z.infer<typeof restDecisionOptionSchema>;
|
|
556
569
|
|
|
@@ -603,19 +616,26 @@ const projectRef = z.object({
|
|
|
603
616
|
|
|
604
617
|
/**
|
|
605
618
|
* `GET /api/v1/work`: what is on the asker's plate across the projects they may see (ADR-0019):
|
|
606
|
-
* the open tasks assigned to them, and the decisions that wait for a person, each with its
|
|
619
|
+
* the open tasks assigned to them, and the decisions that wait for a person, each with its
|
|
620
|
+
* project; and, for the week on the plate, `closed`, the tasks assigned to them that are done
|
|
621
|
+
* or dropped and not yet put away. Left out by a console older than it.
|
|
607
622
|
*/
|
|
608
623
|
export const restWorkSchema = z.object({
|
|
609
624
|
tasks: z.array(z.object({ project: projectRef, task: restTaskSchema })),
|
|
625
|
+
closed: z.optional(z.array(z.object({ project: projectRef, task: restTaskSchema }))),
|
|
610
626
|
decisions: z.array(z.object({ project: projectRef, decision: restDecisionSchema })),
|
|
611
627
|
});
|
|
612
628
|
export type RestWork = z.infer<typeof restWorkSchema>;
|
|
613
629
|
|
|
614
630
|
/**
|
|
615
|
-
* What `GET /api/v1/people/<id>/work` answers: another person's open tasks,
|
|
616
|
-
* the asker may see, for whoever oversees them. No
|
|
631
|
+
* What `GET /api/v1/people/<id>/work` answers: another person's open tasks, and their closed
|
|
632
|
+
* ones for the week, within the projects the asker may see, for whoever oversees them. No
|
|
633
|
+
* decisions, which wait for anyone.
|
|
617
634
|
*/
|
|
618
|
-
export const restPersonWorkSchema = z.object({
|
|
635
|
+
export const restPersonWorkSchema = z.object({
|
|
636
|
+
tasks: restWorkSchema.shape.tasks,
|
|
637
|
+
closed: restWorkSchema.shape.closed,
|
|
638
|
+
});
|
|
619
639
|
export type RestPersonWork = z.infer<typeof restPersonWorkSchema>;
|
|
620
640
|
|
|
621
641
|
/** What `POST /api/v1/projects/<key>/decisions` is sent: the question, its context, and the options. */
|
|
@@ -639,11 +659,24 @@ export const restDecisionPatchSchema = z.object({
|
|
|
639
659
|
});
|
|
640
660
|
export type RestDecisionPatch = z.infer<typeof restDecisionPatchSchema>;
|
|
641
661
|
|
|
642
|
-
/**
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
662
|
+
/**
|
|
663
|
+
* What `POST .../decisions/<id>/answer` is sent: one of the options by its id, or the person's
|
|
664
|
+
* own answer in a line, one of the two; and a word about either.
|
|
665
|
+
*/
|
|
666
|
+
export const restAnswerInputSchema = z
|
|
667
|
+
.object({
|
|
668
|
+
/** The id of the chosen option. */
|
|
669
|
+
option: z.optional(z.string().check(z.minLength(1), z.maxLength(64))),
|
|
670
|
+
/** The person's own answer, when none of the options is it: one line, worded as an option is. */
|
|
671
|
+
own: z.optional(line(MAX_OPTION_LABEL_LENGTH)),
|
|
672
|
+
note: z.optional(paragraphs(MAX_NOTE_LENGTH)),
|
|
673
|
+
})
|
|
674
|
+
.check(
|
|
675
|
+
z.refine(
|
|
676
|
+
(input) => (input.option === undefined) !== (input.own === undefined),
|
|
677
|
+
"must name an option or give an answer of its own, one of the two",
|
|
678
|
+
),
|
|
679
|
+
);
|
|
647
680
|
export type RestAnswerInput = z.infer<typeof restAnswerInputSchema>;
|
|
648
681
|
|
|
649
682
|
/**
|
|
@@ -887,15 +920,39 @@ export const restClaimSchema = z.union([
|
|
|
887
920
|
]);
|
|
888
921
|
export type RestClaim = z.infer<typeof restClaimSchema>;
|
|
889
922
|
|
|
890
|
-
/**
|
|
923
|
+
/** Why a pack did not answer a read: the code and the words the deployment, or the console reading a directory, gave, as the main repository's API spells them. */
|
|
924
|
+
export const restPackRefusalSchema = z.object({
|
|
925
|
+
code: z.string(),
|
|
926
|
+
message: z.string(),
|
|
927
|
+
/** With a file the pack will not carry: where it is at its source, or `null`. */
|
|
928
|
+
sourceUrl: z.nullable(z.string()),
|
|
929
|
+
/** With `skill.ambiguous`: the directories to choose from. */
|
|
930
|
+
directories: z.array(z.string()),
|
|
931
|
+
});
|
|
932
|
+
export type RestPackRefusal = z.infer<typeof restPackRefusalSchema>;
|
|
933
|
+
|
|
934
|
+
/** What every answer about a pack says first: its address, where it is read, and how the read went. */
|
|
935
|
+
const packEnvelope = {
|
|
936
|
+
/** The address, canonical, or `null` when neither the project nor the console names one. */
|
|
937
|
+
address: z.nullable(z.string()),
|
|
938
|
+
/** The origin of the SkillCDN deployment the pack is read at; `null` for a pack read from a directory on the console's machine (ADR-0031). */
|
|
939
|
+
source: z.nullable(z.string()),
|
|
940
|
+
/** Where a person browses the address at the deployment, or `null` without one. */
|
|
941
|
+
page: z.nullable(z.string()),
|
|
942
|
+
status: z.enum(SKILLS_STATUSES),
|
|
943
|
+
/** Why the read did not happen, in the pack's own words, when `status` says it did not; `null` otherwise. */
|
|
944
|
+
refusal: z.nullable(restPackRefusalSchema),
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
/** One of the project's skills, as the pack lists it: what the repository says of it, where a person reads it, what an agent loads it by. */
|
|
891
948
|
export const restSkillSchema = z.object({
|
|
892
949
|
name: z.string(),
|
|
893
950
|
description: z.string(),
|
|
894
951
|
/** The skill's folder in the repository (`""` for the root), and the path of its `SKILL.md`. */
|
|
895
952
|
directory: z.string(),
|
|
896
953
|
path: z.string(),
|
|
897
|
-
/** Where a person reads the skill: its page at the deployment. */
|
|
898
|
-
page: z.string(),
|
|
954
|
+
/** Where a person reads the skill: its page at the deployment, or `null` for a pack read from a directory. */
|
|
955
|
+
page: z.nullable(z.string()),
|
|
899
956
|
/** What an agent loads the skill by, through its own SkillCDN connection; `null` when the path is not one. */
|
|
900
957
|
uri: z.nullable(z.string()),
|
|
901
958
|
/** The title and the description in other languages, by language tag, as the repository gives them. */
|
|
@@ -906,15 +963,9 @@ export const restSkillSchema = z.object({
|
|
|
906
963
|
});
|
|
907
964
|
export type RestSkill = z.infer<typeof restSkillSchema>;
|
|
908
965
|
|
|
909
|
-
/** `GET /api/v1/projects/<key>/skills`: the project's
|
|
966
|
+
/** `GET /api/v1/projects/<key>/skills`: the project's pack, read by address (ADR-0028, ADR-0031). */
|
|
910
967
|
export const restSkillsSchema = z.object({
|
|
911
|
-
|
|
912
|
-
address: z.nullable(z.string()),
|
|
913
|
-
/** The origin of the SkillCDN deployment the skills are read through. */
|
|
914
|
-
source: z.string(),
|
|
915
|
-
/** Where a person browses the address, or `null` without one. */
|
|
916
|
-
page: z.nullable(z.string()),
|
|
917
|
-
status: z.enum(SKILLS_STATUSES),
|
|
968
|
+
...packEnvelope,
|
|
918
969
|
/** 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
970
|
pack: z.optional(
|
|
920
971
|
z.object({
|
|
@@ -923,10 +974,12 @@ export const restSkillsSchema = z.object({
|
|
|
923
974
|
language: z.nullable(z.string()),
|
|
924
975
|
}),
|
|
925
976
|
),
|
|
926
|
-
/** How many skills and documents the
|
|
977
|
+
/** How many skills and documents the index found at the address, when `ready`. */
|
|
927
978
|
counts: z.optional(z.object({ skills: count, documents: count })),
|
|
928
979
|
/** The skills, when `ready`; empty otherwise. */
|
|
929
980
|
items: z.array(restSkillSchema),
|
|
981
|
+
/** What the address serves, as the main repository's REST API answers it (`RestMount` of `@skillcdn/core`), for a program that renders what a model is given; `null` when nothing was answered. */
|
|
982
|
+
mount: z.nullable(packMountSchema),
|
|
930
983
|
});
|
|
931
984
|
export type RestSkills = z.infer<typeof restSkillsSchema>;
|
|
932
985
|
|
|
@@ -938,181 +991,31 @@ export const restPackTokenInputSchema = z.object({
|
|
|
938
991
|
});
|
|
939
992
|
export type RestPackTokenInput = z.infer<typeof restPackTokenInputSchema>;
|
|
940
993
|
|
|
941
|
-
/**
|
|
942
|
-
export const restSkillTextSchema = z.object({
|
|
943
|
-
path: z.string(),
|
|
944
|
-
body: z.string(),
|
|
945
|
-
truncated: z.boolean(),
|
|
946
|
-
});
|
|
947
|
-
/** A skill as the deployment serves it, on one page of its context: what `load_skill` gives a model, in the console's shape. */
|
|
948
|
-
export const restSkillPageSchema = z.object({
|
|
949
|
-
path: z.string(),
|
|
950
|
-
directory: z.string(),
|
|
951
|
-
name: z.string(),
|
|
952
|
-
description: z.string(),
|
|
953
|
-
/** The rules that hold for the skill, from the repository's root to the nearest manifest. */
|
|
954
|
-
rules: z.array(restSkillTextSchema),
|
|
955
|
-
body: z.string(),
|
|
956
|
-
/** The files the skill includes with its body. */
|
|
957
|
-
included: z.array(restSkillTextSchema),
|
|
958
|
-
/** The paths of the skill's own files, read with the file route. */
|
|
959
|
-
files: z.array(z.string()),
|
|
960
|
-
references: z.array(
|
|
961
|
-
z.object({
|
|
962
|
-
path: z.string(),
|
|
963
|
-
status: z.enum(["available", "blocked", "missing", "outside_mount"]),
|
|
964
|
-
}),
|
|
965
|
-
),
|
|
966
|
-
/** Whether this page completes the skill; else the cursor of the next. */
|
|
967
|
-
complete: z.boolean(),
|
|
968
|
-
nextCursor: z.nullable(z.string()),
|
|
969
|
-
/** How the skill is served: whole, or described only, with its license and where it is read at its host. */
|
|
970
|
-
serving: z.object({
|
|
971
|
-
full: z.boolean(),
|
|
972
|
-
license: z.nullable(z.string()),
|
|
973
|
-
sourceUrl: z.nullable(z.string()),
|
|
974
|
-
}),
|
|
975
|
-
warnings: z.array(z.string()),
|
|
976
|
-
});
|
|
977
|
-
export type RestSkillPage = z.infer<typeof restSkillPageSchema>;
|
|
978
|
-
|
|
979
|
-
/** `GET .../skills/one?path=&cursor=`: one skill of the pack, a page of it; `skill` is `null` unless `ready`. */
|
|
994
|
+
/** `GET .../skills/one?path=&cursor=`: a page of a skill's context, as the pack answers `load_skill` (`RestSkill` of `@skillcdn/core`); `read` is `null` unless `ready`. */
|
|
980
995
|
export const restSkillLoadSchema = z.object({
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
page: z.nullable(z.string()),
|
|
984
|
-
status: z.enum(SKILLS_STATUSES),
|
|
985
|
-
skill: z.nullable(restSkillPageSchema),
|
|
996
|
+
...packEnvelope,
|
|
997
|
+
read: z.nullable(packSkillSchema),
|
|
986
998
|
});
|
|
987
999
|
export type RestSkillLoad = z.infer<typeof restSkillLoadSchema>;
|
|
988
1000
|
|
|
989
|
-
/** `GET .../skills/file?path=&offset=&limit=`: a page of a file
|
|
1001
|
+
/** `GET .../skills/file?path=&offset=&limit=`: a page of a file, or what a folder holds, as the pack answers `read_repo_file` (`RestFile` of `@skillcdn/core`); `read` is `null` unless `ready`. */
|
|
990
1002
|
export const restSkillFileSchema = z.object({
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
status: z.enum(SKILLS_STATUSES),
|
|
994
|
-
file: z.nullable(
|
|
995
|
-
z.object({
|
|
996
|
-
path: z.string(),
|
|
997
|
-
content: z.string(),
|
|
998
|
-
offset: count,
|
|
999
|
-
nextOffset: z.nullable(count),
|
|
1000
|
-
totalLength: count,
|
|
1001
|
-
}),
|
|
1002
|
-
),
|
|
1003
|
-
folder: z.nullable(
|
|
1004
|
-
z.object({
|
|
1005
|
-
path: z.string(),
|
|
1006
|
-
entries: z.array(
|
|
1007
|
-
z.object({
|
|
1008
|
-
path: z.string(),
|
|
1009
|
-
kind: z.enum(["directory", "file"]),
|
|
1010
|
-
size: z.nullable(count),
|
|
1011
|
-
}),
|
|
1012
|
-
),
|
|
1013
|
-
truncated: z.boolean(),
|
|
1014
|
-
}),
|
|
1015
|
-
),
|
|
1003
|
+
...packEnvelope,
|
|
1004
|
+
read: z.nullable(packFileSchema),
|
|
1016
1005
|
});
|
|
1017
1006
|
export type RestSkillFile = z.infer<typeof restSkillFileSchema>;
|
|
1018
1007
|
|
|
1019
|
-
/**
|
|
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`. */
|
|
1008
|
+
/** `GET .../skills/browse?path=&cursor=&limit=`: what a folder holds, a page of it, as the pack answers `browse_repo` (`RestBrowse` of `@skillcdn/core`); `read` is `null` unless `ready`. */
|
|
1040
1009
|
export const restSkillBrowseSchema = z.object({
|
|
1041
|
-
|
|
1042
|
-
|
|
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
|
-
),
|
|
1010
|
+
...packEnvelope,
|
|
1011
|
+
read: z.nullable(packBrowseSchema),
|
|
1061
1012
|
});
|
|
1062
1013
|
export type RestSkillBrowse = z.infer<typeof restSkillBrowseSchema>;
|
|
1063
1014
|
|
|
1064
|
-
/**
|
|
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`. */
|
|
1015
|
+
/** `GET .../skills/find?query=&path=&cursor=&limit=`: what words find, a page of it in relevance order, as the pack answers `search_repo` (`RestFind` of `@skillcdn/core`); `read` is `null` unless `ready`. */
|
|
1102
1016
|
export const restSkillFindSchema = z.object({
|
|
1103
|
-
|
|
1104
|
-
|
|
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
|
-
),
|
|
1017
|
+
...packEnvelope,
|
|
1018
|
+
read: z.nullable(packFindSchema),
|
|
1116
1019
|
});
|
|
1117
1020
|
export type RestSkillFind = z.infer<typeof restSkillFindSchema>;
|
|
1118
1021
|
|