@skillcdn/console 0.4.2 → 0.4.4
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 +4 -4
- package/dist/api.d.ts +3 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +3 -3
- package/dist/api.js.map +1 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +120 -33
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/format.d.ts +4 -2
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +38 -10
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +22 -15
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/plugin-check.d.ts +10 -1
- package/dist/cli/plugin-check.d.ts.map +1 -1
- package/dist/cli/plugin-check.js +45 -4
- package/dist/cli/plugin-check.js.map +1 -1
- package/dist/cli/plugin-cli.d.ts +1 -1
- package/dist/cli/plugin-cli.d.ts.map +1 -1
- package/dist/cli/plugin-cli.js +80 -5
- package/dist/cli/plugin-cli.js.map +1 -1
- package/dist/cli/plugin-init.d.ts +19 -0
- package/dist/cli/plugin-init.d.ts.map +1 -1
- package/dist/cli/plugin-init.js +141 -3
- package/dist/cli/plugin-init.js.map +1 -1
- package/dist/client.d.ts +7 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +18 -5
- package/dist/client.js.map +1 -1
- package/dist/compat.js +1 -1
- package/dist/components/board.d.ts +8 -0
- package/dist/components/board.d.ts.map +1 -1
- package/dist/components/board.js +3 -3
- package/dist/components/board.js.map +1 -1
- package/dist/components/comments.d.ts.map +1 -1
- package/dist/components/comments.js +1 -1
- package/dist/components/comments.js.map +1 -1
- package/dist/components/decision-form.d.ts +8 -1
- package/dist/components/decision-form.d.ts.map +1 -1
- package/dist/components/decision-form.js +57 -7
- package/dist/components/decision-form.js.map +1 -1
- package/dist/components/decision-list.d.ts +9 -1
- package/dist/components/decision-list.d.ts.map +1 -1
- package/dist/components/decision-list.js +53 -7
- package/dist/components/decision-list.js.map +1 -1
- package/dist/components/event-feed.d.ts.map +1 -1
- package/dist/components/event-feed.js +7 -1
- package/dist/components/event-feed.js.map +1 -1
- package/dist/components/goals.d.ts.map +1 -1
- package/dist/components/goals.js +2 -2
- package/dist/components/goals.js.map +1 -1
- package/dist/components/home.d.ts +35 -0
- package/dist/components/home.d.ts.map +1 -0
- package/dist/components/home.js +326 -0
- package/dist/components/home.js.map +1 -0
- package/dist/components/my-decisions.d.ts +2 -0
- package/dist/components/my-decisions.d.ts.map +1 -1
- package/dist/components/my-decisions.js +2 -2
- package/dist/components/my-decisions.js.map +1 -1
- package/dist/components/pack-connect.d.ts.map +1 -1
- package/dist/components/pack-connect.js +5 -4
- package/dist/components/pack-connect.js.map +1 -1
- package/dist/components/people.d.ts +2 -0
- package/dist/components/people.d.ts.map +1 -1
- package/dist/components/people.js +15 -2
- package/dist/components/people.js.map +1 -1
- package/dist/components/plugin-frame.d.ts +3 -12
- package/dist/components/plugin-frame.d.ts.map +1 -1
- package/dist/components/plugin-frame.js +53 -20
- package/dist/components/plugin-frame.js.map +1 -1
- package/dist/components/plugin-settings.d.ts.map +1 -1
- package/dist/components/plugin-settings.js +151 -26
- package/dist/components/plugin-settings.js.map +1 -1
- package/dist/components/projects.d.ts +0 -8
- package/dist/components/projects.d.ts.map +1 -1
- package/dist/components/projects.js +1 -13
- package/dist/components/projects.js.map +1 -1
- package/dist/components/task-view.d.ts +2 -1
- package/dist/components/task-view.d.ts.map +1 -1
- package/dist/components/task-view.js +2 -1
- package/dist/components/task-view.js.map +1 -1
- package/dist/components/ui.d.ts.map +1 -1
- package/dist/components/ui.js +5 -2
- package/dist/components/ui.js.map +1 -1
- package/dist/connect-page.d.ts.map +1 -1
- package/dist/connect-page.js +5 -6
- package/dist/connect-page.js.map +1 -1
- package/dist/console.css +764 -192
- package/dist/console.d.ts +10 -3
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +49 -28
- package/dist/console.js.map +1 -1
- package/dist/data.d.ts +6 -3
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +31 -7
- package/dist/data.js.map +1 -1
- package/dist/docs-page.d.ts.map +1 -1
- package/dist/docs-page.js +6 -5
- package/dist/docs-page.js.map +1 -1
- package/dist/home-tiles.d.ts +92 -0
- package/dist/home-tiles.d.ts.map +1 -0
- package/dist/home-tiles.js +294 -0
- package/dist/home-tiles.js.map +1 -0
- package/dist/i18n/en.d.ts +180 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +181 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/index.d.ts +4 -2
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +10 -11
- package/dist/i18n/index.js.map +1 -1
- package/dist/i18n/ko.d.ts.map +1 -1
- package/dist/i18n/ko.js +176 -3
- package/dist/i18n/ko.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/limits.d.ts +8 -0
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +8 -0
- package/dist/limits.js.map +1 -1
- package/dist/plugin/bridge.d.ts +44 -3
- package/dist/plugin/bridge.d.ts.map +1 -1
- package/dist/plugin/bridge.js +26 -0
- package/dist/plugin/bridge.js.map +1 -1
- package/dist/plugin/capabilities.d.ts +7 -1
- package/dist/plugin/capabilities.d.ts.map +1 -1
- package/dist/plugin/capabilities.js +14 -1
- package/dist/plugin/capabilities.js.map +1 -1
- package/dist/plugin/context.d.ts +24 -1
- package/dist/plugin/context.d.ts.map +1 -1
- package/dist/plugin/define.d.ts +1 -0
- package/dist/plugin/define.d.ts.map +1 -1
- package/dist/plugin/define.js +1 -1
- package/dist/plugin/define.js.map +1 -1
- package/dist/plugin/limits.d.ts +5 -2
- package/dist/plugin/limits.d.ts.map +1 -1
- package/dist/plugin/limits.js +5 -2
- package/dist/plugin/limits.js.map +1 -1
- package/dist/plugin/manifest.d.ts +83 -6
- package/dist/plugin/manifest.d.ts.map +1 -1
- package/dist/plugin/manifest.js +111 -7
- package/dist/plugin/manifest.js.map +1 -1
- package/dist/plugin/rest-schemas.d.ts +76 -0
- package/dist/plugin/rest-schemas.d.ts.map +1 -1
- package/dist/plugin/rest-schemas.js +23 -2
- package/dist/plugin/rest-schemas.js.map +1 -1
- package/dist/plugin/sandbox.d.ts +4 -3
- package/dist/plugin/sandbox.d.ts.map +1 -1
- package/dist/plugin/sandbox.js +33 -6
- package/dist/plugin/sandbox.js.map +1 -1
- package/dist/plugin/semver.d.ts +26 -0
- package/dist/plugin/semver.d.ts.map +1 -0
- package/dist/plugin/semver.js +76 -0
- package/dist/plugin/semver.js.map +1 -0
- package/dist/plugin/testing.d.ts.map +1 -1
- package/dist/plugin/testing.js +2 -1
- package/dist/plugin/testing.js.map +1 -1
- package/dist/plugin/web.d.ts +10 -1
- package/dist/plugin/web.d.ts.map +1 -1
- package/dist/plugin/web.js +34 -0
- package/dist/plugin/web.js.map +1 -1
- package/dist/plugin.d.ts +3 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +3 -2
- package/dist/plugin.js.map +1 -1
- package/dist/rules.d.ts +2 -2
- package/dist/rules.js +3 -3
- package/dist/schemas.d.ts +574 -11
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +154 -25
- package/dist/schemas.js.map +1 -1
- package/dist/use-action.d.ts +8 -2
- package/dist/use-action.d.ts.map +1 -1
- package/dist/use-action.js +13 -6
- package/dist/use-action.js.map +1 -1
- package/dist/vocabulary.d.ts +3 -1
- package/dist/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary.js +3 -0
- package/dist/vocabulary.js.map +1 -1
- package/docs/specs/cli.md +4 -3
- package/docs/specs/plugins.md +49 -35
- package/docs/specs/rest.md +20 -18
- package/package.json +7 -7
- package/skills/building-a-plugin/SKILL.md +10 -9
- package/skills/planning-the-board/references/setting-up.md +2 -1
- package/skills/working-the-board/SKILL.md +3 -3
- package/src/api.ts +10 -0
- package/src/cli/cli.ts +162 -39
- package/src/cli/format.ts +54 -10
- package/src/cli/help.ts +23 -14
- package/src/cli/plugin-check.ts +68 -2
- package/src/cli/plugin-cli.ts +104 -6
- package/src/cli/plugin-init.ts +178 -3
- package/src/client.ts +36 -9
- package/src/compat.ts +1 -1
- package/src/components/board.tsx +3 -3
- package/src/components/comments.tsx +1 -0
- package/src/components/decision-form.tsx +166 -29
- package/src/components/decision-list.tsx +116 -15
- package/src/components/event-feed.tsx +7 -1
- package/src/components/goals.tsx +4 -1
- package/src/components/home.tsx +615 -0
- package/src/components/my-decisions.tsx +4 -1
- package/src/components/pack-connect.tsx +5 -4
- package/src/components/people.tsx +136 -54
- package/src/components/plugin-frame.tsx +62 -63
- package/src/components/plugin-settings.tsx +428 -87
- package/src/components/projects.tsx +0 -55
- package/src/components/task-view.tsx +9 -1
- package/src/components/ui.tsx +6 -2
- package/src/connect-page.tsx +5 -6
- package/src/console.tsx +98 -42
- package/src/data.ts +41 -11
- package/src/docs-page.tsx +6 -5
- package/src/home-tiles.ts +438 -0
- package/src/i18n/en.ts +185 -0
- package/src/i18n/index.ts +17 -14
- package/src/i18n/ko.ts +181 -3
- package/src/index.ts +7 -5
- package/src/limits.ts +8 -0
- package/src/plugin/bridge.ts +77 -1
- package/src/plugin/capabilities.ts +17 -1
- package/src/plugin/context.ts +24 -2
- package/src/plugin/define.ts +4 -1
- package/src/plugin/limits.ts +5 -2
- package/src/plugin/manifest.ts +154 -10
- package/src/plugin/rest-schemas.ts +27 -1
- package/src/plugin/sandbox.ts +36 -8
- package/src/plugin/semver.ts +89 -0
- package/src/plugin/testing.ts +2 -1
- package/src/plugin/web.ts +49 -0
- package/src/plugin.ts +18 -0
- package/src/rules.ts +3 -3
- package/src/schemas.ts +190 -28
- package/src/styles/console.css +764 -192
- package/src/use-action.ts +16 -7
- package/src/vocabulary.ts +4 -0
- package/template/plugin/console-plugin.json +3 -1
- package/template/plugin/package.json +3 -3
package/src/cli/cli.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
type ConsoleClient,
|
|
6
6
|
createClient,
|
|
7
7
|
type FetchLike,
|
|
8
|
+
type FileUpload,
|
|
8
9
|
type PluginClient,
|
|
9
10
|
type ProjectClient,
|
|
10
11
|
} from "../client.js";
|
|
@@ -20,7 +21,13 @@ import {
|
|
|
20
21
|
formatVersion as versionText,
|
|
21
22
|
} from "../compat.js";
|
|
22
23
|
import { addDays, dayOf as dayOfDate, firstDayOfWeek, isDay, weekOf } from "../dates.js";
|
|
23
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
DEFAULT_PAGE_LIMIT,
|
|
26
|
+
LIST_LIMIT,
|
|
27
|
+
MAX_DECIDERS,
|
|
28
|
+
MAX_OPTIONS,
|
|
29
|
+
MIN_OPTIONS,
|
|
30
|
+
} from "../limits.js";
|
|
24
31
|
import { capabilityWords } from "../plugin/capabilities.js";
|
|
25
32
|
import { PLUGIN_LIST_LIMIT } from "../plugin/limits.js";
|
|
26
33
|
import { fillRules, RULES_PATH, RULES_TITLE } from "../rules.js";
|
|
@@ -76,6 +83,7 @@ import {
|
|
|
76
83
|
} from "./credentials.js";
|
|
77
84
|
import { type DirectorySettings, type DirectoryStore, THE_SENTENCE } from "./directory.js";
|
|
78
85
|
import {
|
|
86
|
+
deciderLogins,
|
|
79
87
|
formatAnswer,
|
|
80
88
|
formatCategoryLine,
|
|
81
89
|
formatComment,
|
|
@@ -1525,6 +1533,8 @@ const plugin: Command = async (args, io) => {
|
|
|
1525
1533
|
);
|
|
1526
1534
|
return EXIT.ok;
|
|
1527
1535
|
}
|
|
1536
|
+
case "capabilities":
|
|
1537
|
+
return pluginCapabilities(io, session, client, rest);
|
|
1528
1538
|
case "settings":
|
|
1529
1539
|
return pluginSettings(io, session, client, rest);
|
|
1530
1540
|
case "secret":
|
|
@@ -1786,7 +1796,60 @@ function capabilityLines(capabilities: readonly string[]): string[] {
|
|
|
1786
1796
|
});
|
|
1787
1797
|
}
|
|
1788
1798
|
|
|
1789
|
-
/**
|
|
1799
|
+
/** The capabilities `--consent` names, given again or with commas between. */
|
|
1800
|
+
function capabilitiesNamed(values: Values): string[] {
|
|
1801
|
+
return texts(values, "consent")
|
|
1802
|
+
.flatMap((value) => value.split(","))
|
|
1803
|
+
.map((capability) => capability.trim())
|
|
1804
|
+
.filter((capability) => capability.length > 0);
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
/** What a plugin requires, and which of what it can go without it holds, as `capabilities` prints it. */
|
|
1808
|
+
function capabilitiesHeld(detail: RestPluginDetail): string {
|
|
1809
|
+
const held = detail.consent ?? [];
|
|
1810
|
+
const optional = detail.optionalCapabilities ?? [];
|
|
1811
|
+
const named = (capabilities: readonly string[]) =>
|
|
1812
|
+
capabilities.length === 0 ? "none" : capabilities.join(", ");
|
|
1813
|
+
return [
|
|
1814
|
+
`Capabilities of ${detail.key}:`,
|
|
1815
|
+
` required: ${named(detail.capabilities)}`,
|
|
1816
|
+
` optional, on: ${named(optional.filter((capability) => held.includes(capability)))}`,
|
|
1817
|
+
` optional, off: ${named(optional.filter((capability) => !held.includes(capability)))}`,
|
|
1818
|
+
].join("\n");
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
/**
|
|
1822
|
+
* What a plugin holds; with `--consent`, which of the optional capabilities it holds from now on,
|
|
1823
|
+
* `none` for none of them. An administrator's.
|
|
1824
|
+
*/
|
|
1825
|
+
async function pluginCapabilities(
|
|
1826
|
+
io: CliIo,
|
|
1827
|
+
session: Session,
|
|
1828
|
+
client: PluginClient,
|
|
1829
|
+
rest: readonly string[],
|
|
1830
|
+
): Promise<number> {
|
|
1831
|
+
const { values, positionals } = parse(rest, { consent: { type: "string", multiple: true } });
|
|
1832
|
+
if (positionals.length > 0) {
|
|
1833
|
+
throw misuse(
|
|
1834
|
+
`Nothing follows capabilities but --consent: console plugin ${client.key} capabilities [--consent <capability>,...|none]`,
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1837
|
+
const detail = await client.get();
|
|
1838
|
+
const named = capabilitiesNamed(values);
|
|
1839
|
+
if (named.length === 0) {
|
|
1840
|
+
answer(io, session, detail, () => capabilitiesHeld(detail));
|
|
1841
|
+
return EXIT.ok;
|
|
1842
|
+
}
|
|
1843
|
+
const chosen = named.filter((capability) => capability !== "none");
|
|
1844
|
+
const changed = await client.updateConsent([
|
|
1845
|
+
...detail.capabilities,
|
|
1846
|
+
...chosen.filter((capability) => !detail.capabilities.includes(capability)),
|
|
1847
|
+
]);
|
|
1848
|
+
answer(io, session, changed, () => capabilitiesHeld(changed));
|
|
1849
|
+
return EXIT.ok;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
/** Enables a plugin, with consent to every capability it requires, named again; an administrator's. */
|
|
1790
1853
|
async function pluginEnable(
|
|
1791
1854
|
io: CliIo,
|
|
1792
1855
|
session: Session,
|
|
@@ -1806,19 +1869,22 @@ async function pluginEnable(
|
|
|
1806
1869
|
}
|
|
1807
1870
|
const [detail, me] = await Promise.all([client.get(), session.client.me()]);
|
|
1808
1871
|
const asked = detail.capabilities;
|
|
1809
|
-
const
|
|
1810
|
-
|
|
1811
|
-
.map((capability) => capability.trim())
|
|
1812
|
-
.filter((capability) => capability.length > 0);
|
|
1872
|
+
const optional = detail.optionalCapabilities ?? [];
|
|
1873
|
+
const consent = capabilitiesNamed(values);
|
|
1813
1874
|
if (asked.length > 0 && !session.json) {
|
|
1814
1875
|
io.stdout(
|
|
1815
|
-
`${detail.key}
|
|
1876
|
+
`${detail.key} requires ${asked.length === 1 ? "one capability" : `${asked.length} capabilities`}:\n${capabilityLines(asked).join("\n")}\n`,
|
|
1877
|
+
);
|
|
1878
|
+
}
|
|
1879
|
+
if (optional.length > 0 && !session.json) {
|
|
1880
|
+
io.stdout(
|
|
1881
|
+
`It can go without these, which it holds if --consent names them too:\n${capabilityLines(optional).join("\n")}\n`,
|
|
1816
1882
|
);
|
|
1817
1883
|
}
|
|
1818
1884
|
const missing = asked.filter((capability) => !consent.includes(capability));
|
|
1819
1885
|
if (missing.length > 0) {
|
|
1820
1886
|
throw misuse(
|
|
1821
|
-
`Enabling ${detail.key} needs consent to every capability it
|
|
1887
|
+
`Enabling ${detail.key} needs consent to every capability it requires, named again: --consent ${asked.join(",")}. Not named: ${missing.join(", ")}.`,
|
|
1822
1888
|
);
|
|
1823
1889
|
}
|
|
1824
1890
|
const boardFlag = text(values, "board");
|
|
@@ -2069,7 +2135,7 @@ const rules: Command = async (args, io) => {
|
|
|
2069
2135
|
|
|
2070
2136
|
/**
|
|
2071
2137
|
* The rules as they read for the person whose token the command holds: `{language}` filled
|
|
2072
|
-
* with the language they
|
|
2138
|
+
* with the language they read in, or with the task's while they have none.
|
|
2073
2139
|
*/
|
|
2074
2140
|
function forPerson<T extends { readonly body: string | null }>(
|
|
2075
2141
|
half: T,
|
|
@@ -2298,18 +2364,7 @@ const comment: Command = async (args, io) => {
|
|
|
2298
2364
|
if (body.trim().length === 0 && paths.length === 0) {
|
|
2299
2365
|
throw misuse(`Say something, or attach a file. ${ask}`);
|
|
2300
2366
|
}
|
|
2301
|
-
const files =
|
|
2302
|
-
for (const path of paths) {
|
|
2303
|
-
let bytes: Uint8Array;
|
|
2304
|
-
try {
|
|
2305
|
-
bytes = await io.readBytes(path);
|
|
2306
|
-
} catch {
|
|
2307
|
-
throw failed(`No file could be read at ${path}. Name the path of a file to attach.`);
|
|
2308
|
-
}
|
|
2309
|
-
const name = basename(path);
|
|
2310
|
-
const contentType = CONTENT_TYPES[extname(name).toLowerCase()];
|
|
2311
|
-
files.push({ name, bytes, ...(contentType === undefined ? {} : { contentType }) });
|
|
2312
|
-
}
|
|
2367
|
+
const files = await attachmentsOf(io, paths);
|
|
2313
2368
|
const session = await open(io, values);
|
|
2314
2369
|
const project = await session.project();
|
|
2315
2370
|
const task = await project.task(ref);
|
|
@@ -2323,10 +2378,30 @@ const comment: Command = async (args, io) => {
|
|
|
2323
2378
|
return EXIT.ok;
|
|
2324
2379
|
};
|
|
2325
2380
|
|
|
2381
|
+
/**
|
|
2382
|
+
* The files named by --attach, read from this machine: each called by its name, without the
|
|
2383
|
+
* path, and typed by its extension where the extension says.
|
|
2384
|
+
*/
|
|
2385
|
+
async function attachmentsOf(io: CliIo, paths: readonly string[]): Promise<FileUpload[]> {
|
|
2386
|
+
const files: FileUpload[] = [];
|
|
2387
|
+
for (const path of paths) {
|
|
2388
|
+
let bytes: Uint8Array;
|
|
2389
|
+
try {
|
|
2390
|
+
bytes = await io.readBytes(path);
|
|
2391
|
+
} catch {
|
|
2392
|
+
throw failed(`No file could be read at ${path}. Name the path of a file to attach.`);
|
|
2393
|
+
}
|
|
2394
|
+
const name = basename(path);
|
|
2395
|
+
const contentType = CONTENT_TYPES[extname(name).toLowerCase()];
|
|
2396
|
+
files.push({ name, bytes, ...(contentType === undefined ? {} : { contentType }) });
|
|
2397
|
+
}
|
|
2398
|
+
return files;
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2326
2401
|
/**
|
|
2327
2402
|
* A file of the project, read from the console and written on this machine: one a run handed
|
|
2328
|
-
* in, one put in a comment, or one attached to a page, by its id, so that an
|
|
2329
|
-
* a picture a person put on the board.
|
|
2403
|
+
* in, one put in a comment or on a decision, or one attached to a page, by its id, so that an
|
|
2404
|
+
* agent can look at a picture a person put on the board.
|
|
2330
2405
|
*/
|
|
2331
2406
|
const file: Command = async (args, io) => {
|
|
2332
2407
|
const { values, positionals } = parse(args, { out: { type: "string" } });
|
|
@@ -2520,15 +2595,19 @@ const ask: Command = async (args, io) => {
|
|
|
2520
2595
|
file: { type: "string" },
|
|
2521
2596
|
run: { type: "string" },
|
|
2522
2597
|
wait: { type: "string" },
|
|
2523
|
-
to: { type: "string" },
|
|
2598
|
+
to: { type: "string", multiple: true },
|
|
2599
|
+
attach: { type: "string", multiple: true },
|
|
2524
2600
|
});
|
|
2525
2601
|
const question = theOne(
|
|
2526
2602
|
positionals,
|
|
2527
2603
|
'Say the question: console ask "<question>" --option "<label>" --option "<label>"',
|
|
2528
2604
|
);
|
|
2529
|
-
const
|
|
2530
|
-
if (
|
|
2531
|
-
throw misuse("Say whom the decision is for: --to <login|me
|
|
2605
|
+
const tos = texts(values, "to");
|
|
2606
|
+
if (tos.includes("none")) {
|
|
2607
|
+
throw misuse("Say whom the decision is for: --to <login|me>, given again for several.");
|
|
2608
|
+
}
|
|
2609
|
+
if (tos.length > MAX_DECIDERS) {
|
|
2610
|
+
throw misuse(`Name up to ${MAX_DECIDERS} people with --to.`);
|
|
2532
2611
|
}
|
|
2533
2612
|
const options = texts(values, "option");
|
|
2534
2613
|
if (options.length < MIN_OPTIONS || options.length > MAX_OPTIONS) {
|
|
@@ -2536,23 +2615,41 @@ const ask: Command = async (args, io) => {
|
|
|
2536
2615
|
}
|
|
2537
2616
|
const seconds = secondsOf(text(values, "wait"), DEFAULT_ASK_WAIT_SECONDS);
|
|
2538
2617
|
const body = await bodyOf(io, values, text(values, "body"));
|
|
2618
|
+
// The files put on the decision (ADR-0055): a picture of what is to be chosen between, for one.
|
|
2619
|
+
const files = await attachmentsOf(io, texts(values, "attach"));
|
|
2539
2620
|
const session = await open(io, values);
|
|
2540
2621
|
const project = await session.project();
|
|
2541
2622
|
const runId = await currentRun(project, values);
|
|
2542
|
-
// Whom the decision is for (ADR-0042): the person the agent works for, unless --to
|
|
2543
|
-
|
|
2544
|
-
const
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
}
|
|
2623
|
+
// Whom the decision is for (ADR-0042, ADR-0057): the person the agent works for, unless --to
|
|
2624
|
+
// names others of the project, each once; several, and any of them may answer.
|
|
2625
|
+
const deciderIds: string[] = [];
|
|
2626
|
+
for (const to of tos) {
|
|
2627
|
+
const id = await personIdOf(session, to);
|
|
2628
|
+
if (typeof id === "string" && !deciderIds.includes(id)) {
|
|
2629
|
+
deciderIds.push(id);
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
const raised = await project.raiseDecision(
|
|
2633
|
+
{
|
|
2634
|
+
question,
|
|
2635
|
+
options: options.map((label) => ({ label })),
|
|
2636
|
+
runId,
|
|
2637
|
+
...(body === undefined ? {} : { body }),
|
|
2638
|
+
...(deciderIds.length === 0
|
|
2639
|
+
? {}
|
|
2640
|
+
: deciderIds.length === 1
|
|
2641
|
+
? { deciderId: deciderIds[0] }
|
|
2642
|
+
: { deciderId: deciderIds[0], deciderIds }),
|
|
2643
|
+
},
|
|
2644
|
+
files,
|
|
2645
|
+
);
|
|
2646
|
+
const withFiles =
|
|
2647
|
+
files.length === 0 ? "" : `, with ${files.length} file${files.length === 1 ? "" : "s"}`;
|
|
2551
2648
|
// The id is printed before any waiting, so that a wait cut short loses nothing.
|
|
2552
2649
|
io.stdout(
|
|
2553
2650
|
session.json
|
|
2554
2651
|
? `${JSON.stringify(raised, null, 2)}\n`
|
|
2555
|
-
: `Decision ${raised.id} raised on run ${runId}; the run waits for ${raised
|
|
2652
|
+
: `Decision ${raised.id} raised on run ${runId}${withFiles}; the run waits for ${deciderLogins(raised)}.\ndecision id: ${raised.id}\n${formatOptions(raised)}\n`,
|
|
2556
2653
|
);
|
|
2557
2654
|
return settle(io, session, project, raised, seconds, true);
|
|
2558
2655
|
};
|
|
@@ -3101,8 +3198,19 @@ const people: Command = async (args, io) => {
|
|
|
3101
3198
|
const { values } = parse(args, {}, false);
|
|
3102
3199
|
const session = await open(io, values);
|
|
3103
3200
|
const found = await session.client.people();
|
|
3201
|
+
const loginOf = new Map(found.items.map((person) => [person.id, person.login]));
|
|
3104
3202
|
answer(io, session, found, () =>
|
|
3105
|
-
[
|
|
3203
|
+
[
|
|
3204
|
+
...found.items.map((person) =>
|
|
3205
|
+
formatPersonLine(
|
|
3206
|
+
person,
|
|
3207
|
+
person.managerId === null || person.managerId === undefined
|
|
3208
|
+
? undefined
|
|
3209
|
+
: (loginOf.get(person.managerId) ?? person.managerId),
|
|
3210
|
+
),
|
|
3211
|
+
),
|
|
3212
|
+
...found.listed.map(formatListingLine),
|
|
3213
|
+
].join("\n"),
|
|
3106
3214
|
);
|
|
3107
3215
|
return EXIT.ok;
|
|
3108
3216
|
};
|
|
@@ -3183,7 +3291,7 @@ const workspace: Command = async (args, io) => {
|
|
|
3183
3291
|
};
|
|
3184
3292
|
|
|
3185
3293
|
const PERSON_USAGE =
|
|
3186
|
-
"Say what to do: console person role <login> <admin|member> | person add <login> [--host gh|google] | person remove <login> | person deactivate <login> | person reactivate <login>";
|
|
3294
|
+
"Say what to do: console person role <login> <admin|member> | person manager <login> <manager login|none> | person add <login> [--host gh|google] | person remove <login> | person deactivate <login> | person reactivate <login>";
|
|
3187
3295
|
|
|
3188
3296
|
const person: Command = async (args, io) => {
|
|
3189
3297
|
const what = args[0];
|
|
@@ -3200,6 +3308,21 @@ const person: Command = async (args, io) => {
|
|
|
3200
3308
|
answer(io, session, changed, () => formatPersonLine(changed));
|
|
3201
3309
|
return EXIT.ok;
|
|
3202
3310
|
}
|
|
3311
|
+
if (what === "manager") {
|
|
3312
|
+
const { values, positionals } = parse(args.slice(1), {});
|
|
3313
|
+
const [login, manager] = theTwo(
|
|
3314
|
+
positionals,
|
|
3315
|
+
"Say whom and whom they report to: console person manager <login> <manager login|none>",
|
|
3316
|
+
);
|
|
3317
|
+
const session = await open(io, values);
|
|
3318
|
+
const found = await personOf(session, login);
|
|
3319
|
+
const managerId = manager === "none" ? null : (await personOf(session, manager)).id;
|
|
3320
|
+
const changed = await session.client.updatePerson(found.id, { managerId });
|
|
3321
|
+
answer(io, session, changed, () =>
|
|
3322
|
+
formatPersonLine(changed, managerId === null ? undefined : manager),
|
|
3323
|
+
);
|
|
3324
|
+
return EXIT.ok;
|
|
3325
|
+
}
|
|
3203
3326
|
if (what === "add") {
|
|
3204
3327
|
const { values, positionals } = parse(args.slice(1), { host: { type: "string" } });
|
|
3205
3328
|
const login = theOne(positionals, "Say whom: console person add <login> [--host gh|google]");
|
package/src/cli/format.ts
CHANGED
|
@@ -119,12 +119,13 @@ export function formatProject(project: RestProject): string {
|
|
|
119
119
|
return lines.join("\n");
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
/** One person: their login, their name where the host gives one, what they are in the workspace,
|
|
123
|
-
export function formatPersonLine(person: RestPerson): string {
|
|
122
|
+
/** One person: their login, their name where the host gives one, what they are in the workspace, that they are out when they are, and whom they report to where known. */
|
|
123
|
+
export function formatPersonLine(person: RestPerson, reportsTo?: string | undefined): string {
|
|
124
124
|
const standing = person.status === "active" ? person.role : `${person.role}, deactivated`;
|
|
125
|
+
const line = reportsTo === undefined ? "" : `, reports to ${reportsTo}`;
|
|
125
126
|
return person.name === null
|
|
126
|
-
? `${person.login} (${standing})`
|
|
127
|
-
: `${person.login} ${person.name} (${standing})`;
|
|
127
|
+
? `${person.login} (${standing}${line})`
|
|
128
|
+
: `${person.login} ${person.name} (${standing}${line})`;
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
/** The provider a listing names, as people call it. */
|
|
@@ -219,6 +220,7 @@ export function formatTask(
|
|
|
219
220
|
`#${task.number} ${task.title}`,
|
|
220
221
|
`state: ${task.state} priority: ${task.priority} owner: ${task.owner.login} workers: ${task.workers.length === 0 ? "nobody" : logins(task.workers)}`,
|
|
221
222
|
`id: ${task.id}${task.parentId === null ? "" : ` part of: ${task.parentId}`}`,
|
|
223
|
+
...(task.agent == null ? [] : [`written through: ${task.agent}`]),
|
|
222
224
|
...scheduleLines(task),
|
|
223
225
|
];
|
|
224
226
|
if (task.links.length > 0) {
|
|
@@ -379,7 +381,7 @@ export function formatGoalLine(goal: RestGoal): string {
|
|
|
379
381
|
export function formatGoal(goal: RestGoal, tasks?: readonly RestTask[]): string {
|
|
380
382
|
const lines = [
|
|
381
383
|
`goal ${goal.id}: ${goal.title}`,
|
|
382
|
-
`state: ${goal.state} category: ${goal.category?.key ?? "none"} starts: ${goal.startsOn ?? "none"} due: ${goal.dueOn ?? "none"} set by: ${goal.createdBy.login}`,
|
|
384
|
+
`state: ${goal.state} category: ${goal.category?.key ?? "none"} starts: ${goal.startsOn ?? "none"} due: ${goal.dueOn ?? "none"} set by: ${goal.createdBy.login}${through(goal.agent)}`,
|
|
383
385
|
`progress: ${progressOf(goal)}`,
|
|
384
386
|
];
|
|
385
387
|
if (goal.body.trim().length > 0) {
|
|
@@ -431,9 +433,27 @@ export function formatPersonWork(person: RestPerson, work: RestPersonWork): stri
|
|
|
431
433
|
].join("\n");
|
|
432
434
|
}
|
|
433
435
|
|
|
434
|
-
/**
|
|
436
|
+
/** The agent a thing came through, beside the person (ADR-0056); nothing when they did it themselves. */
|
|
437
|
+
const through = (agent: string | null | undefined): string =>
|
|
438
|
+
agent === null || agent === undefined ? "" : ` through ${agent}`;
|
|
439
|
+
|
|
440
|
+
/** Everyone a decision is addressed to (ADR-0057): `deciders` where the console answers it, else the one decider. */
|
|
441
|
+
const decidersOf = (decision: RestDecision): readonly RestPerson[] =>
|
|
442
|
+
decision.deciders !== undefined && decision.deciders.length > 0
|
|
443
|
+
? decision.deciders
|
|
444
|
+
: [decision.decider];
|
|
445
|
+
|
|
446
|
+
/** The logins of everyone a decision is addressed to, with commas between. */
|
|
447
|
+
export const deciderLogins = (decision: RestDecision): string =>
|
|
448
|
+
decidersOf(decision)
|
|
449
|
+
.map((person) => person.login)
|
|
450
|
+
.join(", ");
|
|
451
|
+
|
|
452
|
+
/** Whom a decision is for, where that is not the one who asked alone (ADR-0042, ADR-0057). */
|
|
435
453
|
const addressed = (decision: RestDecision): string =>
|
|
436
|
-
decision.
|
|
454
|
+
decidersOf(decision).every((person) => person.id === decision.raisedBy.id)
|
|
455
|
+
? ""
|
|
456
|
+
: `, for ${deciderLogins(decision)}`;
|
|
437
457
|
|
|
438
458
|
export function formatDecisionLine(decision: RestDecision): string {
|
|
439
459
|
const state = decision.answer === null ? "waiting" : `answered ${decision.answer.option}`;
|
|
@@ -445,7 +465,7 @@ export function formatOptions(decision: RestDecision): string {
|
|
|
445
465
|
return decision.options
|
|
446
466
|
.map(
|
|
447
467
|
(option) =>
|
|
448
|
-
` ${option.id}) ${option.label}${option.own === true ? " (the person's own answer, not one given)" : ""}`,
|
|
468
|
+
` ${option.id}) ${option.label}${option.key === undefined ? "" : ` [${option.key}]`}${option.own === true ? " (the person's own answer, not one given)" : ""}`,
|
|
449
469
|
)
|
|
450
470
|
.join("\n");
|
|
451
471
|
}
|
|
@@ -454,11 +474,11 @@ export function formatOptions(decision: RestDecision): string {
|
|
|
454
474
|
export function formatAnswer(decision: RestDecision): string {
|
|
455
475
|
const { answer } = decision;
|
|
456
476
|
if (answer === null) {
|
|
457
|
-
return `waits for ${decision
|
|
477
|
+
return `waits for ${deciderLogins(decision)}.`;
|
|
458
478
|
}
|
|
459
479
|
const chosen = decision.options.find((option) => option.id === answer.option);
|
|
460
480
|
const lines = [
|
|
461
|
-
`Answered: ${answer.option}) ${chosen?.label ?? answer.option}${chosen?.own === true ? ", in their own words" : ""}, by ${answer.by.login} at ${answer.at}`,
|
|
481
|
+
`Answered: ${answer.option}) ${chosen?.label ?? answer.option}${chosen?.own === true ? ", in their own words" : ""}, by ${answer.by.login}${through(answer.agent)} at ${answer.at}`,
|
|
462
482
|
];
|
|
463
483
|
if (answer.note !== null && answer.note.length > 0) {
|
|
464
484
|
lines.push(`Note: ${answer.note}`);
|
|
@@ -474,9 +494,27 @@ export function formatDecision(decision: RestDecision): string {
|
|
|
474
494
|
if (decision.body.trim().length > 0) {
|
|
475
495
|
lines.push("", decision.body.trimEnd());
|
|
476
496
|
}
|
|
497
|
+
// The files put on it (ADR-0055), each with the command that reads it.
|
|
498
|
+
const files = decision.files ?? [];
|
|
499
|
+
if (files.length > 0) {
|
|
500
|
+
lines.push(
|
|
501
|
+
"",
|
|
502
|
+
...files.map(
|
|
503
|
+
(attached) =>
|
|
504
|
+
`file: ${attached.file.name} (${attached.file.size} bytes, ${attached.file.contentType}): console file ${attached.id}`,
|
|
505
|
+
),
|
|
506
|
+
);
|
|
507
|
+
}
|
|
477
508
|
lines.push("", "options:", formatOptions(decision), "", formatAnswer(decision));
|
|
478
509
|
if (decision.outcome !== null && decision.outcome.trim().length > 0) {
|
|
479
510
|
lines.push("", "what followed:", indent(decision.outcome));
|
|
511
|
+
if (decision.outcomeBy != null && decision.outcomeAt != null) {
|
|
512
|
+
lines.push(
|
|
513
|
+
indent(
|
|
514
|
+
`written by ${decision.outcomeBy.login}${through(decision.outcomeAgent)} at ${decision.outcomeAt}`,
|
|
515
|
+
),
|
|
516
|
+
);
|
|
517
|
+
}
|
|
480
518
|
}
|
|
481
519
|
return lines.join("\n");
|
|
482
520
|
}
|
|
@@ -579,6 +617,9 @@ export function localized(
|
|
|
579
617
|
export function formatPluginLine(plugin: RestPlugin, language: string | null | undefined): string {
|
|
580
618
|
const notes = [
|
|
581
619
|
plugin.roles.length === 0 ? undefined : `roles: ${plugin.roles.join(", ")}`,
|
|
620
|
+
plugin.console === null || plugin.console === undefined
|
|
621
|
+
? undefined
|
|
622
|
+
: `console: ${plugin.console}`,
|
|
582
623
|
plugin.problems.length === 0 ? undefined : `problems: ${plugin.problems.join("; ")}`,
|
|
583
624
|
].filter((note): note is string => note !== undefined);
|
|
584
625
|
return `${plugin.key} ${localized(plugin.name, language)} ${plugin.version} ${plugin.state}${notes.length === 0 ? "" : ` ${notes.join(" ")}`}`;
|
|
@@ -750,6 +791,9 @@ export function formatPlugin(
|
|
|
750
791
|
if (detail.problems.length > 0) {
|
|
751
792
|
lines.push(`problems: ${detail.problems.join("; ")}`);
|
|
752
793
|
}
|
|
794
|
+
if (detail.console !== null && detail.console !== undefined) {
|
|
795
|
+
lines.push(`Runs on consoles ${detail.console}.`);
|
|
796
|
+
}
|
|
753
797
|
lines.push(
|
|
754
798
|
detail.roles.length === 0
|
|
755
799
|
? "You hold no role in it."
|
package/src/cli/help.ts
CHANGED
|
@@ -7,12 +7,14 @@ import {
|
|
|
7
7
|
MAX_CATEGORY_KEY_LENGTH,
|
|
8
8
|
MAX_CATEGORY_NAME_LENGTH,
|
|
9
9
|
MAX_COMMENTS_PER_TASK,
|
|
10
|
+
MAX_DECIDERS,
|
|
10
11
|
MAX_DECISIONS_PER_PROJECT,
|
|
11
12
|
MAX_DOCUMENT_LENGTH,
|
|
12
13
|
MAX_DOCUMENT_PATH_LENGTH,
|
|
13
14
|
MAX_DOCUMENTS_PER_PROJECT,
|
|
14
15
|
MAX_FILE_BYTES,
|
|
15
16
|
MAX_FILES_PER_COMMENT,
|
|
17
|
+
MAX_FILES_PER_DECISION,
|
|
16
18
|
MAX_FILES_PER_DOCUMENT,
|
|
17
19
|
MAX_GOALS_PER_PROJECT,
|
|
18
20
|
MAX_LINK_LABEL_LENGTH,
|
|
@@ -246,14 +248,15 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
246
248
|
},
|
|
247
249
|
person: {
|
|
248
250
|
usage:
|
|
249
|
-
"person role <login> <admin|member> | person add <login> [--host gh|google] | person remove <login> | person deactivate <login> | person reactivate <login>",
|
|
251
|
+
"person role <login> <admin|member> | person manager <login> <manager login|none> | person add <login> [--host gh|google] | person remove <login> | person deactivate <login> | person reactivate <login>",
|
|
250
252
|
synopsis:
|
|
251
|
-
"Says what a person is in the workspace, lets a login in or takes one out; for an administrator.",
|
|
253
|
+
"Says what a person is in the workspace and whom they report to, lets a login in or takes one out; for an administrator.",
|
|
252
254
|
about:
|
|
253
|
-
"For an administrator: the person themselves on the People page, or their agent here, named on it. role says what a person is in the workspace; the board keeps at least one administrator in. add lets a login in: whoever signs in with that account at that host is a person from then on; the host is read off the login (an address is Google's, anything else GitHub's) unless --host says. remove takes a login away that nobody has signed in with yet. deactivate takes a person out: refused on every request at once, their agents with them, kept with everything they did, and listed nowhere; reactivate takes them back in. A person the organization let in comes and goes with it, and is neither.",
|
|
255
|
+
"For an administrator: the person themselves on the People page, or their agent here, named on it. role says what a person is in the workspace; the board keeps at least one administrator in. manager says whom a person reports to, the reporting line of the directory, which the plugins read as a default (the approver of their leave, say) in place of a line of their own: another active person of the workspace who does not report to them, directly or through others, or none; console people shows it. add lets a login in: whoever signs in with that account at that host is a person from then on; the host is read off the login (an address is Google's, anything else GitHub's) unless --host says. remove takes a login away that nobody has signed in with yet. deactivate takes a person out: refused on every request at once, their agents with them, kept with everything they did, and listed nowhere; reactivate takes them back in. A person the organization let in comes and goes with it, and is neither.",
|
|
254
256
|
refusals: [
|
|
255
257
|
"auth.forbidden: only an administrator says who is in and what they are",
|
|
256
258
|
"person.last_admin: the board keeps at least one administrator in; make another one first",
|
|
259
|
+
"person.invalid_manager: the manager is not another active person of the workspace, or reports to this person already",
|
|
257
260
|
"person.of_organization: the organization says whether that person is in",
|
|
258
261
|
"listing.exists: the login is let in already, or is a person's who is in",
|
|
259
262
|
"listing.unknown_host: nobody signs in through that provider here",
|
|
@@ -377,9 +380,9 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
377
380
|
},
|
|
378
381
|
plugin: {
|
|
379
382
|
usage:
|
|
380
|
-
"plugin <key> | plugin <key> help [<command>] | plugin <key> <command> [<args>] [--<name> <value>]... [--file <path> | -] [--run <id>] | plugin <key> records <collection> [<id>] [--where <field>=<value>]... [--search <words>] [--project <key>] [--order <field>[:desc]] [--limit <n>] [--after <cursor>] [--archived] [--versions] | plugin <key> skills [<folder>|<file>] [--search <words> [--limit <n>] [--cursor <cursor>]] | plugin <key> skill <path> | plugin <key> skill --file <path> [--offset <n>] [--limit <n>] | plugin <key> enable --consent <capability>,... [--board new|<key>] [--projects all|<key>,...] [--set <key>=<value>]... | plugin <key> disable | plugin <key> settings [--set <key>=<value>]... | plugin <key> secret <name> [--remove] | plugin <key> grant <role> --person <login>|--project <key>|--everyone | plugin <key> revoke <grant id>",
|
|
383
|
+
"plugin <key> | plugin <key> help [<command>] | plugin <key> <command> [<args>] [--<name> <value>]... [--file <path> | -] [--run <id>] | plugin <key> records <collection> [<id>] [--where <field>=<value>]... [--search <words>] [--project <key>] [--order <field>[:desc]] [--limit <n>] [--after <cursor>] [--archived] [--versions] | plugin <key> skills [<folder>|<file>] [--search <words> [--limit <n>] [--cursor <cursor>]] | plugin <key> skill <path> | plugin <key> skill --file <path> [--offset <n>] [--limit <n>] | plugin <key> enable --consent <capability>,... [--board new|<key>] [--projects all|<key>,...] [--set <key>=<value>]... | plugin <key> disable | plugin <key> capabilities [--consent <capability>,...|none] | plugin <key> settings [--set <key>=<value>]... | plugin <key> secret <name> [--remove] | plugin <key> grant <role> --person <login>|--project <key>|--everyone | plugin <key> revoke <grant id>",
|
|
381
384
|
overview:
|
|
382
|
-
"plugin <key> [help [<command>] | <command> [<args>] | records <collection> | skills | skill <path> | enable | disable | settings | secret | grant | revoke]",
|
|
385
|
+
"plugin <key> [help [<command>] | <command> [<args>] | records <collection> | skills | skill <path> | enable | disable | capabilities | settings | secret | grant | revoke]",
|
|
383
386
|
synopsis:
|
|
384
387
|
"One plugin: what it is for and its commands; a command run as you; its records, its skills; its settings, for an administrator.",
|
|
385
388
|
about:
|
|
@@ -387,7 +390,8 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
387
390
|
limits: [
|
|
388
391
|
"a command line: up to 200 words of up to 50,000 characters each; one file, up to the file bound (console help hand-in), with the plugin's files capability",
|
|
389
392
|
"records: a page of up to 200; where: a field the collection indexes",
|
|
390
|
-
"enable: consent names every capability the manifest
|
|
393
|
+
"enable: consent names every capability the manifest requires, and the optional ones to turn on; settings and secrets as the manifest bounds them",
|
|
394
|
+
"capabilities: --consent names the optional capabilities the plugin holds from now on, none for none of them; those it requires stay",
|
|
391
395
|
],
|
|
392
396
|
refusals: [
|
|
393
397
|
"plugin.not_found: no plugin has that key, or you may not know of it; or the plugin has no such command or query",
|
|
@@ -450,31 +454,36 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
450
454
|
},
|
|
451
455
|
ask: {
|
|
452
456
|
usage:
|
|
453
|
-
'ask "<question>" --option "<label>" --option "<label>" [...] [--body <markdown>|--file <path>] [--to <login|me>] [--wait <seconds>] [--run <id>]',
|
|
457
|
+
'ask "<question>" --option "<label>" --option "<label>" [...] [--body <markdown>|--file <path>] [--attach <path>]... [--to <login|me>]... [--wait <seconds>] [--run <id>]',
|
|
454
458
|
overview:
|
|
455
|
-
'ask "<question>" --option "<label>" --option "<label>" [...] [--body <markdown>|--file <path>] [--to <login|me>] [--wait <seconds>]',
|
|
459
|
+
'ask "<question>" --option "<label>" --option "<label>" [...] [--body <markdown>|--file <path>] [--attach <path>]... [--to <login|me>]... [--wait <seconds>]',
|
|
456
460
|
synopsis:
|
|
457
461
|
"Raises a decision a person must make, with options, and waits a while for the answer.",
|
|
458
|
-
about: `Raises a decision a person must make: the question, the options to choose from, and what they need to know. It is for the person you work for unless --to names another of the project, who then finds it among their decisions (ADR-0042); anyone of the project may still answer. The run waits for the answer. The command waits 60 seconds for it unless --wait says otherwise, and exits with 3 if it is still to come: keep waiting with \`console decision <id> --wait 100\`, within what your shell gives one command. The decision's id is printed as soon as it is raised. The person chooses one of the options, or answers in their own words: the answer then names one more option, their words, marked own; act on it as given. ${ON_THE_RUN}`,
|
|
462
|
+
about: `Raises a decision a person must make: the question, the options to choose from, and what they need to know. --attach puts a file on the decision, in the one request: a picture of what is to be chosen between, a screenshot of what was made; a picture the body names as  is shown there, the rest after the body, and the person reads each back with \`console file <id>\` (ADR-0055). It is for the person you work for unless --to names another of the project, who then finds it among their decisions (ADR-0042); --to given again names several, each of whom finds it among theirs, and the first answer ends it (ADR-0057); anyone of the project may still answer. The run waits for the answer. The command waits 60 seconds for it unless --wait says otherwise, and exits with 3 if it is still to come: keep waiting with \`console decision <id> --wait 100\`, within what your shell gives one command. The decision's id is printed as soon as it is raised. The person chooses one of the options, or answers in their own words: the answer then names one more option, their words, marked own; act on it as given. ${ON_THE_RUN}`,
|
|
459
463
|
limits: [
|
|
460
464
|
`question: one line, up to ${MAX_QUESTION_LENGTH} characters`,
|
|
461
465
|
`options: ${MIN_OPTIONS} to ${MAX_OPTIONS}, each one line up to ${MAX_OPTION_LABEL_LENGTH} characters`,
|
|
462
466
|
`body: ${MARKDOWN}`,
|
|
463
|
-
|
|
467
|
+
`attach: a file: up to ${MAX_FILE_BYTES} bytes, not empty; called by its name, without the path; a picture is placed in the body as `,
|
|
468
|
+
`at most ${MAX_FILES_PER_DECISION} files on one decision, together up to ${MAX_FILE_BYTES} bytes`,
|
|
469
|
+
`to: a login of the project, or me; up to ${MAX_DECIDERS} of them`,
|
|
464
470
|
`at most ${MAX_DECISIONS_PER_PROJECT} decisions in one project`,
|
|
465
471
|
],
|
|
466
472
|
refusals: [
|
|
467
473
|
...RUN_CHOICE,
|
|
468
474
|
"decision.invalid_run: the run is not yours, or is over",
|
|
469
|
-
"decision.invalid_decider:
|
|
475
|
+
"decision.invalid_decider: a person named may not work in the project",
|
|
476
|
+
"decision.too_many_deciders: more people named than one decision takes",
|
|
470
477
|
"decision.too_many: the project holds as many decisions as one may",
|
|
478
|
+
"decision.too_many_files: more files than one decision carries",
|
|
479
|
+
"request.too_large: a file, or the files together, over the bound",
|
|
471
480
|
],
|
|
472
481
|
},
|
|
473
482
|
answer: {
|
|
474
483
|
usage: 'answer <id> --option <n>|--own "<answer>" [--note <markdown>|--file <path>]',
|
|
475
484
|
synopsis: "Writes a person's answer on a decision: the option they chose, or their own words.",
|
|
476
485
|
about:
|
|
477
|
-
"Writes on the board the answer a person gave: one of the decision's options by its number, or their own words, which become one more option of the decision marked as theirs; and a note about it. The answer is the person's, written in their name through you (ADR-0021):
|
|
486
|
+
"Writes on the board the answer a person gave: one of the decision's options by its number, or their own words, which become one more option of the decision marked as theirs; and a note about it. The answer is the person's, written in their name through you (ADR-0021), and the record says so: the decision names you beside them as the agent the answer came through, where an answer given on the pages names the person alone (ADR-0056). A choice they made in chat, in a meeting or by changing the task is written here, with where and when they made it in the note, so that the record is whole and a run that waited goes on. A decision is answered once; what followed is written with console decision <id> --outcome.",
|
|
478
487
|
limits: [
|
|
479
488
|
`own: one line, up to ${MAX_OPTION_LABEL_LENGTH} characters`,
|
|
480
489
|
`note: Markdown, up to ${MAX_NOTE_LENGTH} characters`,
|
|
@@ -490,7 +499,7 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
490
499
|
synopsis:
|
|
491
500
|
"One decision in full; waits for its answer with --wait; writes what followed with --outcome.",
|
|
492
501
|
about:
|
|
493
|
-
"One decision, with its context, its options, its answer (one of the options, or the person's own words as one more, marked own) and what followed. With --wait, holds on for the answer that long, and exits with 3 if it is still to come. With --outcome, writes down what followed the decision, so that it reads as a record.",
|
|
502
|
+
"One decision, with its context, its options, its answer (one of the options, or the person's own words as one more, marked own) and what followed. With --wait, holds on for the answer that long, and exits with 3 if it is still to come. With --outcome, writes down what followed the decision, so that it reads as a record; the record names who wrote it, you beside them, and when (ADR-0056).",
|
|
494
503
|
limits: [`outcome: ${MARKDOWN}`],
|
|
495
504
|
refusals: ["decision.not_found: no decision has that id"],
|
|
496
505
|
},
|
|
@@ -558,7 +567,7 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
558
567
|
usage: "file <id> [--out <path>]",
|
|
559
568
|
synopsis: "Reads a file of the project to this machine, by its id.",
|
|
560
569
|
about:
|
|
561
|
-
"Reads a file of the project from the console and writes it on this machine: one a run handed in, one put in a comment, or one attached to a page, by the id the task, the run or the page shows. Written under its own name in the working directory, at --out <path>, or into --out <directory>. Then look at it with your own tools.",
|
|
570
|
+
"Reads a file of the project from the console and writes it on this machine: one a run handed in, one put in a comment or on a decision, or one attached to a page, by the id the task, the run, the decision or the page shows. Written under its own name in the working directory, at --out <path>, or into --out <directory>. Then look at it with your own tools.",
|
|
562
571
|
refusals: ["file.not_found: no file of this project has that id"],
|
|
563
572
|
},
|
|
564
573
|
docs: {
|