@skrr-ai/cli 0.1.37 → 0.1.38
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/bin/dev-fallback.js +167 -4
- package/dist/base-command.js +46 -2
- package/dist/commands/agents/actions/create.js +18 -7
- package/dist/commands/agents/actions/delete.d.ts +1 -0
- package/dist/commands/agents/actions/delete.js +7 -0
- package/dist/commands/agents/actions/update.js +5 -3
- package/dist/commands/agents/api-actions/add.js +5 -2
- package/dist/commands/agents/api-actions/index.d.ts +11 -0
- package/dist/commands/agents/api-actions/index.js +27 -1
- package/dist/commands/agents/avatar/default/index.d.ts +11 -0
- package/dist/commands/agents/avatar/default/index.js +17 -0
- package/dist/commands/agents/avatar/default/set.d.ts +2 -1
- package/dist/commands/agents/avatar/default/set.js +32 -7
- package/dist/commands/agents/avatar/upload.js +7 -1
- package/dist/commands/agents/categories/delete.js +8 -0
- package/dist/commands/agents/categories/index.d.ts +21 -5
- package/dist/commands/agents/categories/index.js +68 -15
- package/dist/commands/agents/chat.d.ts +21 -0
- package/dist/commands/agents/chat.js +94 -7
- package/dist/commands/agents/create.js +11 -4
- package/dist/commands/agents/list.d.ts +15 -0
- package/dist/commands/agents/list.js +31 -1
- package/dist/commands/agents/scene-background/upload.d.ts +30 -0
- package/dist/commands/agents/scene-background/upload.js +97 -0
- package/dist/commands/agents/show.js +13 -0
- package/dist/commands/agents/tools/calls.d.ts +23 -5
- package/dist/commands/agents/tools/calls.js +81 -18
- package/dist/commands/agents/update.js +15 -5
- package/dist/commands/code/{oversky-agent.d.ts → skrr-agent.d.ts} +3 -3
- package/dist/commands/code/{oversky-agent.js → skrr-agent.js} +11 -8
- package/dist/commands/convos/messages.d.ts +13 -0
- package/dist/commands/convos/messages.js +40 -16
- package/dist/commands/daemon/index.js +8 -1
- package/dist/commands/daemon/logs.d.ts +26 -0
- package/dist/commands/daemon/logs.js +56 -0
- package/dist/commands/daemon/uninstall.js +1 -1
- package/dist/commands/followups/act.js +3 -2
- package/dist/commands/followups/cancel.d.ts +6 -3
- package/dist/commands/followups/cancel.js +16 -6
- package/dist/commands/followups/observability.d.ts +23 -0
- package/dist/commands/followups/observability.js +114 -0
- package/dist/commands/followups/reschedule.js +1 -1
- package/dist/commands/followups/resolve.js +3 -3
- package/dist/commands/followups/show.js +2 -1
- package/dist/commands/followups/watch.js +9 -5
- package/dist/commands/harnesses/forget.js +28 -0
- package/dist/commands/harnesses/install.d.ts +41 -0
- package/dist/commands/harnesses/install.js +165 -0
- package/dist/commands/harnesses/installers.d.ts +45 -0
- package/dist/commands/harnesses/installers.js +155 -0
- package/dist/commands/harnesses/list.d.ts +82 -1
- package/dist/commands/harnesses/list.js +104 -12
- package/dist/commands/harnesses/models.js +10 -1
- package/dist/commands/harnesses/show.d.ts +15 -0
- package/dist/commands/harnesses/show.js +44 -3
- package/dist/commands/harnesses/update.js +1 -1
- package/dist/commands/harnesses/usage.d.ts +25 -0
- package/dist/commands/harnesses/usage.js +24 -2
- package/dist/commands/instructions/status.js +13 -1
- package/dist/commands/machines/dedicated/catalog.js +4 -15
- package/dist/commands/machines/dedicated/create.d.ts +8 -0
- package/dist/commands/machines/dedicated/create.js +58 -1
- package/dist/commands/machines/dedicated/destroy.js +18 -4
- package/dist/commands/machines/dedicated/list.js +9 -6
- package/dist/commands/machines/hosted/list.js +12 -0
- package/dist/commands/machines/list.js +11 -0
- package/dist/commands/machines/show.d.ts +1 -0
- package/dist/commands/machines/show.js +40 -11
- package/dist/commands/profiles/list.d.ts +23 -0
- package/dist/commands/profiles/list.js +96 -0
- package/dist/commands/search/skills.d.ts +11 -1
- package/dist/commands/search/skills.js +26 -8
- package/dist/commands/skills/details.d.ts +2 -0
- package/dist/commands/skills/details.js +24 -1
- package/dist/commands/skills/import-as-actions.d.ts +32 -0
- package/dist/commands/skills/import-as-actions.js +175 -0
- package/dist/commands/skills/install.d.ts +2 -0
- package/dist/commands/skills/install.js +24 -1
- package/dist/commands/skills/invocable.js +6 -2
- package/dist/commands/skills/local.d.ts +40 -0
- package/dist/commands/skills/local.js +160 -0
- package/dist/commands/skills/search.d.ts +11 -1
- package/dist/commands/skills/search.js +29 -8
- package/dist/commands/spaces/actions/add.js +23 -5
- package/dist/commands/spaces/actions/available.js +3 -0
- package/dist/commands/spaces/actions/list.d.ts +5 -0
- package/dist/commands/spaces/actions/list.js +49 -3
- package/dist/commands/spaces/actions/run.d.ts +28 -0
- package/dist/commands/spaces/actions/run.js +146 -0
- package/dist/commands/spaces/actions/update.js +27 -5
- package/dist/commands/spaces/label-summaries.d.ts +27 -0
- package/dist/commands/spaces/label-summaries.js +64 -0
- package/dist/commands/spaces/task-rollups.d.ts +5 -0
- package/dist/commands/spaces/task-rollups.js +15 -4
- package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +63 -3
- package/dist/commands/tasks/actions/attach.js +32 -7
- package/dist/commands/tasks/complete.js +13 -0
- package/dist/commands/tasks/create.js +1 -1
- package/dist/commands/tasks/events/append.js +29 -6
- package/dist/commands/tasks/expectations/assess.d.ts +1 -0
- package/dist/commands/tasks/expectations/assess.js +43 -5
- package/dist/commands/tasks/expectations.js +77 -16
- package/dist/commands/tasks/follow-up.js +2 -2
- package/dist/commands/tasks/lifecycle/definitions.js +21 -2
- package/dist/commands/tasks/lifecycle/detach.d.ts +7 -0
- package/dist/commands/tasks/lifecycle/detach.js +43 -3
- package/dist/commands/tasks/lifecycle/migrate.d.ts +2 -0
- package/dist/commands/tasks/lifecycle/migrate.js +42 -3
- package/dist/commands/tasks/lifecycle/reopen.d.ts +2 -0
- package/dist/commands/tasks/lifecycle/reopen.js +40 -3
- package/dist/commands/tasks/lifecycle/transition.js +33 -2
- package/dist/commands/tasks/list.js +16 -9
- package/dist/commands/tasks/move.js +11 -13
- package/dist/commands/tasks/review-queue.d.ts +15 -0
- package/dist/commands/tasks/review-queue.js +180 -31
- package/dist/commands/tasks/show.d.ts +11 -0
- package/dist/commands/tasks/show.js +105 -12
- package/dist/commands/tasks/trust.js +7 -2
- package/dist/commands/tasks/update.js +46 -10
- package/dist/commands/tasks/updates/add.js +1 -1
- package/dist/commands/token/list.d.ts +22 -0
- package/dist/commands/token/list.js +68 -0
- package/dist/commands/token/revoke.d.ts +23 -0
- package/dist/commands/token/revoke.js +53 -0
- package/dist/lib/agent-actions.d.ts +57 -2
- package/dist/lib/agent-actions.js +113 -21
- package/dist/lib/agent-pin-mute.d.ts +10 -5
- package/dist/lib/agent-pin-mute.js +17 -4
- package/dist/lib/agent-visual-refs.d.ts +27 -0
- package/dist/lib/agent-visual-refs.js +127 -2
- package/dist/lib/agentic-stream.d.ts +6 -0
- package/dist/lib/agentic-stream.js +34 -3
- package/dist/lib/assignee-resolver.d.ts +1 -1
- package/dist/lib/assignee-resolver.js +8 -0
- package/dist/lib/bulk-task-targets.d.ts +12 -0
- package/dist/lib/bulk-task-targets.js +34 -1
- package/dist/lib/cli-installers.d.ts +130 -0
- package/dist/lib/cli-installers.js +145 -0
- package/dist/lib/command-miss.js +55 -7
- package/dist/lib/dedicated-lease-command.d.ts +13 -0
- package/dist/lib/dedicated-lease-command.js +22 -1
- package/dist/lib/dedicated-machines.d.ts +95 -1
- package/dist/lib/dedicated-machines.js +236 -2
- package/dist/lib/failed-lookup.d.ts +3 -0
- package/dist/lib/failed-lookup.js +40 -0
- package/dist/lib/first-party-harness-agent.js +9 -2
- package/dist/lib/first-party-harness-doctor.d.ts +15 -0
- package/dist/lib/first-party-harness-doctor.js +126 -27
- package/dist/lib/followups.d.ts +14 -6
- package/dist/lib/followups.js +31 -6
- package/dist/lib/harnesses.d.ts +8 -0
- package/dist/lib/instruction-input.d.ts +19 -1
- package/dist/lib/instruction-input.js +52 -1
- package/dist/lib/label-scope.d.ts +15 -0
- package/dist/lib/label-scope.js +25 -5
- package/dist/lib/local-skills.d.ts +98 -0
- package/dist/lib/local-skills.js +195 -0
- package/dist/lib/machines.d.ts +10 -10
- package/dist/lib/machines.js +23 -1
- package/dist/lib/search-query.d.ts +19 -0
- package/dist/lib/search-query.js +35 -0
- package/dist/lib/space-custom-actions.d.ts +72 -0
- package/dist/lib/space-custom-actions.js +117 -0
- package/dist/lib/space-resolver.d.ts +1 -21
- package/dist/lib/space-resolver.js +19 -10
- package/dist/lib/task-closure.d.ts +3 -2
- package/dist/lib/task-closure.js +18 -20
- package/dist/lib/task-lifecycle-output.js +117 -9
- package/dist/lib/task-ref-resolver.d.ts +1 -17
- package/dist/lib/task-ref-resolver.js +18 -8
- package/dist/lib/tasks.js +9 -3
- package/dist/node_modules/@skrr-ai/data-provider/index.js +5076 -4947
- package/oclif.manifest.json +26635 -25649
- package/package.json +9 -3
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveSearchQuery = resolveSearchQuery;
|
|
4
|
+
/**
|
|
5
|
+
* The search term, from a positional or `--query`, resolved the same way by
|
|
6
|
+
* both spellings of the same search.
|
|
7
|
+
*
|
|
8
|
+
* `skills search` and `search skills` run the identical store search. Neither
|
|
9
|
+
* accepted the obvious invocation — `skrr skills search git` — so the single
|
|
10
|
+
* most natural way to call a search command failed on both, and `--query` was
|
|
11
|
+
* `required` on both, which is what made it fail rather than prompt.
|
|
12
|
+
*
|
|
13
|
+
* Shared rather than copied because the two spellings staying in step is the
|
|
14
|
+
* whole point: the reason a reader could not tell them apart was that they had
|
|
15
|
+
* drifted (different one-liners, short flags on one of them), and a second copy
|
|
16
|
+
* of this is how that starts again.
|
|
17
|
+
*/
|
|
18
|
+
function resolveSearchQuery(cmd, positional, flag, command) {
|
|
19
|
+
const fromPositional = positional?.trim();
|
|
20
|
+
const fromFlag = flag?.trim();
|
|
21
|
+
// Both given and DIFFERENT is a real ambiguity — refuse rather than pick.
|
|
22
|
+
// Both given and identical is a script that passes belt and braces; that is
|
|
23
|
+
// not an error and silently accepting it costs nobody anything.
|
|
24
|
+
if (fromPositional && fromFlag && fromPositional !== fromFlag) {
|
|
25
|
+
cmd.error(`Two different search terms: "${fromPositional}" and --query "${fromFlag}". Pass one.`, { exit: 2 });
|
|
26
|
+
}
|
|
27
|
+
const query = fromPositional || fromFlag;
|
|
28
|
+
if (!query) {
|
|
29
|
+
// `--query` used to be `required`, so oclif refused with its own generic
|
|
30
|
+
// sentence. Naming the shape people actually type is the point of the
|
|
31
|
+
// change, so the error names it too.
|
|
32
|
+
cmd.error(`What should I search for? Try \`skrr ${command} <term>\`.`, { exit: 2 });
|
|
33
|
+
}
|
|
34
|
+
return query;
|
|
35
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared shape for `skrr spaces actions add|update`.
|
|
3
|
+
*
|
|
4
|
+
* Both commands describe the same stored object, and they had drifted from the
|
|
5
|
+
* server that validates it (`spaceCustomActionSchema` in api/server/routes/spaces.js).
|
|
6
|
+
*/
|
|
7
|
+
import type { SpaceCustomAction, SpaceMemberActionsResponse } from '@skrr-ai/data-provider';
|
|
8
|
+
/**
|
|
9
|
+
* The server's limit on `icon`, which is `z.string().max(4)`.
|
|
10
|
+
*
|
|
11
|
+
* The flag used to be documented as "Lucide icon name". It is not one: the web
|
|
12
|
+
* settings page renders `action.icon` as literal text, and almost no Lucide name
|
|
13
|
+
* fits in four characters, so following the help produced a 400 with a raw Zod
|
|
14
|
+
* message and no hint that an emoji was wanted (OSK-9631).
|
|
15
|
+
*
|
|
16
|
+
* Zod's `max` counts UTF-16 code units, not visible characters, so the check
|
|
17
|
+
* below uses `String.prototype.length` to agree with it exactly. Counting
|
|
18
|
+
* graphemes instead would accept a family emoji the server then refuses.
|
|
19
|
+
*/
|
|
20
|
+
export declare const SPACE_CUSTOM_ACTION_ICON_MAX = 4;
|
|
21
|
+
export declare const spaceCustomActionIconFlag: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
22
|
+
export declare const spaceCustomActionLabelFlag: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
23
|
+
/** Refuse an icon the server would refuse, with a message that says what fits. */
|
|
24
|
+
export declare function spaceCustomActionIconProblem(icon: unknown): string | null;
|
|
25
|
+
type SpaceWithActions = {
|
|
26
|
+
id: string;
|
|
27
|
+
title?: string;
|
|
28
|
+
leadAgentId?: string | null;
|
|
29
|
+
customActions?: SpaceCustomAction[] | null;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Why a space's OWN actions cannot run, or null when they can.
|
|
33
|
+
*
|
|
34
|
+
* A space custom action runs as a task assigned to the Space Leader, so on a
|
|
35
|
+
* space with no Leader every one of them is dead on arrival. That fact used to
|
|
36
|
+
* live only in a tooltip on a disabled web button: `spaces actions add` reported
|
|
37
|
+
* success, `spaces actions list` listed the action happily, and nothing on the
|
|
38
|
+
* CLI said it could never run (OSK-9641).
|
|
39
|
+
*/
|
|
40
|
+
export declare function spaceActionsNotRunnableReason(space: Pick<SpaceWithActions, 'id' | 'leadAgentId'>, bin: string): string | null;
|
|
41
|
+
/**
|
|
42
|
+
* Find a space custom action by the id `spaces actions list` prints, or by the
|
|
43
|
+
* `space-custom:<id>` form the projected quick-action surfaces use.
|
|
44
|
+
*/
|
|
45
|
+
export declare function findSpaceCustomAction(space: SpaceWithActions, ref: string): {
|
|
46
|
+
customAction: SpaceCustomAction;
|
|
47
|
+
index: number;
|
|
48
|
+
} | {
|
|
49
|
+
problem: string;
|
|
50
|
+
};
|
|
51
|
+
export type SpaceQuickActionRow = {
|
|
52
|
+
source: 'space' | 'agent';
|
|
53
|
+
title: string;
|
|
54
|
+
owner: string;
|
|
55
|
+
type: string;
|
|
56
|
+
ref: string;
|
|
57
|
+
run: string;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Everything a space's action panel offers, in the order the web shows it: the
|
|
61
|
+
* space's own actions first, then the actions of member agents the caller can
|
|
62
|
+
* view, filtered by scope with the same predicate the web and server use.
|
|
63
|
+
*
|
|
64
|
+
* `spaces actions list` showed only the first half and `spaces actions
|
|
65
|
+
* available` a wider second half (every member agent, including ones the caller
|
|
66
|
+
* cannot run), so "what can I run here?" had no answer on the CLI (OSK-9641).
|
|
67
|
+
* Every row's `run` is a command `spaces actions run` accepts.
|
|
68
|
+
*/
|
|
69
|
+
export declare function spaceQuickActionRows(space: SpaceWithActions & {
|
|
70
|
+
labelIds?: string[] | null;
|
|
71
|
+
}, members: SpaceMemberActionsResponse['agents'], bin: string): SpaceQuickActionRow[];
|
|
72
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared shape for `skrr spaces actions add|update`.
|
|
4
|
+
*
|
|
5
|
+
* Both commands describe the same stored object, and they had drifted from the
|
|
6
|
+
* server that validates it (`spaceCustomActionSchema` in api/server/routes/spaces.js).
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.spaceCustomActionLabelFlag = exports.spaceCustomActionIconFlag = exports.SPACE_CUSTOM_ACTION_ICON_MAX = void 0;
|
|
10
|
+
exports.spaceCustomActionIconProblem = spaceCustomActionIconProblem;
|
|
11
|
+
exports.spaceActionsNotRunnableReason = spaceActionsNotRunnableReason;
|
|
12
|
+
exports.findSpaceCustomAction = findSpaceCustomAction;
|
|
13
|
+
exports.spaceQuickActionRows = spaceQuickActionRows;
|
|
14
|
+
const core_1 = require("@oclif/core");
|
|
15
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
16
|
+
/**
|
|
17
|
+
* The server's limit on `icon`, which is `z.string().max(4)`.
|
|
18
|
+
*
|
|
19
|
+
* The flag used to be documented as "Lucide icon name". It is not one: the web
|
|
20
|
+
* settings page renders `action.icon` as literal text, and almost no Lucide name
|
|
21
|
+
* fits in four characters, so following the help produced a 400 with a raw Zod
|
|
22
|
+
* message and no hint that an emoji was wanted (OSK-9631).
|
|
23
|
+
*
|
|
24
|
+
* Zod's `max` counts UTF-16 code units, not visible characters, so the check
|
|
25
|
+
* below uses `String.prototype.length` to agree with it exactly. Counting
|
|
26
|
+
* graphemes instead would accept a family emoji the server then refuses.
|
|
27
|
+
*/
|
|
28
|
+
exports.SPACE_CUSTOM_ACTION_ICON_MAX = 4;
|
|
29
|
+
exports.spaceCustomActionIconFlag = core_1.Flags.string({
|
|
30
|
+
description: `Emoji shown on the button, e.g. ✨ (at most ${exports.SPACE_CUSTOM_ACTION_ICON_MAX} UTF-16 units). Not an icon name.`,
|
|
31
|
+
});
|
|
32
|
+
exports.spaceCustomActionLabelFlag = core_1.Flags.string({
|
|
33
|
+
description: 'Button text shown in the space. Not a Label entity — to offer an agent action in every space carrying a Label, use `agents actions create --label`.',
|
|
34
|
+
});
|
|
35
|
+
/** Refuse an icon the server would refuse, with a message that says what fits. */
|
|
36
|
+
function spaceCustomActionIconProblem(icon) {
|
|
37
|
+
if (icon === undefined || icon === null)
|
|
38
|
+
return null;
|
|
39
|
+
if (typeof icon !== 'string')
|
|
40
|
+
return '--icon must be text.';
|
|
41
|
+
if (icon.length > exports.SPACE_CUSTOM_ACTION_ICON_MAX) {
|
|
42
|
+
return (`--icon "${icon}" is too long: it is shown as text on the button and must be an emoji ` +
|
|
43
|
+
`(at most ${exports.SPACE_CUSTOM_ACTION_ICON_MAX} UTF-16 units), e.g. --icon ✨. ` +
|
|
44
|
+
'Icon names such as "Sparkles" are not accepted here.');
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Why a space's OWN actions cannot run, or null when they can.
|
|
50
|
+
*
|
|
51
|
+
* A space custom action runs as a task assigned to the Space Leader, so on a
|
|
52
|
+
* space with no Leader every one of them is dead on arrival. That fact used to
|
|
53
|
+
* live only in a tooltip on a disabled web button: `spaces actions add` reported
|
|
54
|
+
* success, `spaces actions list` listed the action happily, and nothing on the
|
|
55
|
+
* CLI said it could never run (OSK-9641).
|
|
56
|
+
*/
|
|
57
|
+
function spaceActionsNotRunnableReason(space, bin) {
|
|
58
|
+
if (space.leadAgentId)
|
|
59
|
+
return null;
|
|
60
|
+
return (`This space has no Space Leader, so its own actions cannot run: each runs as a task ` +
|
|
61
|
+
`assigned to the Leader. Set one with \`${bin} spaces update ${space.id} --lead-agent <agent-id>\`.`);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Find a space custom action by the id `spaces actions list` prints, or by the
|
|
65
|
+
* `space-custom:<id>` form the projected quick-action surfaces use.
|
|
66
|
+
*/
|
|
67
|
+
function findSpaceCustomAction(space, ref) {
|
|
68
|
+
const actions = space.customActions ?? [];
|
|
69
|
+
const id = (0, data_provider_1.spaceCustomActionSourceId)(ref) ?? ref;
|
|
70
|
+
const index = actions.findIndex((action) => action.id === id);
|
|
71
|
+
if (index >= 0)
|
|
72
|
+
return { customAction: actions[index], index };
|
|
73
|
+
if (actions.length === 0) {
|
|
74
|
+
return { problem: `Space ${space.id} has no custom actions.` };
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
problem: `No custom action ${ref} in space ${space.id}. It has: ` +
|
|
78
|
+
actions.map((action) => `${action.id} (${action.label})`).join(', ') +
|
|
79
|
+
'.',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Everything a space's action panel offers, in the order the web shows it: the
|
|
84
|
+
* space's own actions first, then the actions of member agents the caller can
|
|
85
|
+
* view, filtered by scope with the same predicate the web and server use.
|
|
86
|
+
*
|
|
87
|
+
* `spaces actions list` showed only the first half and `spaces actions
|
|
88
|
+
* available` a wider second half (every member agent, including ones the caller
|
|
89
|
+
* cannot run), so "what can I run here?" had no answer on the CLI (OSK-9641).
|
|
90
|
+
* Every row's `run` is a command `spaces actions run` accepts.
|
|
91
|
+
*/
|
|
92
|
+
function spaceQuickActionRows(space, members, bin) {
|
|
93
|
+
const subject = { spaceId: space.id, labelIds: space.labelIds ?? [] };
|
|
94
|
+
const leaderMissing = !space.leadAgentId;
|
|
95
|
+
return [
|
|
96
|
+
...(space.customActions ?? []).map((action) => ({
|
|
97
|
+
source: 'space',
|
|
98
|
+
title: action.label,
|
|
99
|
+
owner: 'Space Leader',
|
|
100
|
+
type: 'prompt',
|
|
101
|
+
ref: action.id,
|
|
102
|
+
run: leaderMissing
|
|
103
|
+
? '(needs a Space Leader)'
|
|
104
|
+
: `${bin} spaces actions run ${space.id} ${action.id}`,
|
|
105
|
+
})),
|
|
106
|
+
...members.flatMap(({ agent }) => (agent.agentActions ?? [])
|
|
107
|
+
.filter((action) => (0, data_provider_1.isAgentActionAvailableInSpace)(action, subject))
|
|
108
|
+
.map((action) => ({
|
|
109
|
+
source: 'agent',
|
|
110
|
+
title: action.title,
|
|
111
|
+
owner: agent.name ?? agent.id,
|
|
112
|
+
type: (0, data_provider_1.isBuiltinAgentAction)(action) ? `builtin:${action.builtinType}` : 'prompt',
|
|
113
|
+
ref: `${agent.id}:${action.id}`,
|
|
114
|
+
run: `${bin} spaces actions run ${space.id} ${agent.id}:${action.id}`,
|
|
115
|
+
}))),
|
|
116
|
+
];
|
|
117
|
+
}
|
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Space reference resolution — turn what a person typed into exactly one
|
|
3
|
-
* space id, or an actionable failure.
|
|
4
|
-
*
|
|
5
|
-
* Every `--space` flag in the CLI (35 commands at last count) took a raw UUID
|
|
6
|
-
* and nothing else. Passing a name did not error — it filtered on a UUID that
|
|
7
|
-
* matches no space and returned an empty result:
|
|
8
|
-
*
|
|
9
|
-
* skrr tasks list --space "Always on" → "No tasks."
|
|
10
|
-
*
|
|
11
|
-
* A person retries. An agent concludes the space is empty and proceeds on a
|
|
12
|
-
* false premise, which is the failure mode this module exists to remove. The
|
|
13
|
-
* contract is deliberately narrow: a value that already looks like an id is
|
|
14
|
-
* forwarded untouched (no request, no behavior change for existing scripts);
|
|
15
|
-
* anything else must resolve to exactly one space or the command fails.
|
|
16
|
-
*
|
|
17
|
-
* Modeled on `assignee-resolver.ts`, which already does this for people and
|
|
18
|
-
* agents — `--assignee-user frank` has worked for a while. Spaces were the
|
|
19
|
-
* outlier, not the exception.
|
|
20
|
-
*/
|
|
21
1
|
export type SpaceRef = {
|
|
22
2
|
/** The space UUID — what every `--space` flag ultimately wants. */
|
|
23
3
|
id: string;
|
|
@@ -36,7 +16,7 @@ export type SpaceResolutionFailure = {
|
|
|
36
16
|
* hides. Distinct from NOT_FOUND because the fix is different:
|
|
37
17
|
* pass --include-archived (or the id), not a different name.
|
|
38
18
|
*/
|
|
39
|
-
code: 'NOT_FOUND' | 'AMBIGUOUS' | 'ARCHIVED';
|
|
19
|
+
code: 'NOT_FOUND' | 'AMBIGUOUS' | 'ARCHIVED' | 'NOT_A_REFERENCE';
|
|
40
20
|
query: string;
|
|
41
21
|
candidates: SpaceRef[];
|
|
42
22
|
};
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SPACE_CLI_ERROR_CODE = void 0;
|
|
4
|
+
exports.looksLikeSpaceId = looksLikeSpaceId;
|
|
5
|
+
exports.looksLikeCanonicalSpaceId = looksLikeCanonicalSpaceId;
|
|
6
|
+
exports.normalizeSpaceRef = normalizeSpaceRef;
|
|
7
|
+
exports.spaceRefFrom = spaceRefFrom;
|
|
8
|
+
exports.resolveSpaceRef = resolveSpaceRef;
|
|
9
|
+
exports.describeSpace = describeSpace;
|
|
10
|
+
exports.describeSpaceCandidates = describeSpaceCandidates;
|
|
11
|
+
exports.formatSpaceResolutionFailure = formatSpaceResolutionFailure;
|
|
12
|
+
const failed_lookup_1 = require("./failed-lookup");
|
|
2
13
|
/**
|
|
3
14
|
* Space reference resolution — turn what a person typed into exactly one
|
|
4
15
|
* space id, or an actionable failure.
|
|
@@ -19,16 +30,6 @@
|
|
|
19
30
|
* agents — `--assignee-user frank` has worked for a while. Spaces were the
|
|
20
31
|
* outlier, not the exception.
|
|
21
32
|
*/
|
|
22
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
-
exports.SPACE_CLI_ERROR_CODE = void 0;
|
|
24
|
-
exports.looksLikeSpaceId = looksLikeSpaceId;
|
|
25
|
-
exports.looksLikeCanonicalSpaceId = looksLikeCanonicalSpaceId;
|
|
26
|
-
exports.normalizeSpaceRef = normalizeSpaceRef;
|
|
27
|
-
exports.spaceRefFrom = spaceRefFrom;
|
|
28
|
-
exports.resolveSpaceRef = resolveSpaceRef;
|
|
29
|
-
exports.describeSpace = describeSpace;
|
|
30
|
-
exports.describeSpaceCandidates = describeSpaceCandidates;
|
|
31
|
-
exports.formatSpaceResolutionFailure = formatSpaceResolutionFailure;
|
|
32
33
|
const UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
33
34
|
const OBJECT_ID_RE = /^[0-9a-fA-F]{24}$/;
|
|
34
35
|
/**
|
|
@@ -106,6 +107,11 @@ function resolveSpaceRef(query, spaces) {
|
|
|
106
107
|
if (!q) {
|
|
107
108
|
return { ok: false, code: 'NOT_FOUND', query: raw, candidates: spaces };
|
|
108
109
|
}
|
|
110
|
+
// A failed lookup's output is refused before the name tiers can match it.
|
|
111
|
+
// See `failed-lookup.ts`.
|
|
112
|
+
if ((0, failed_lookup_1.isFailedLookupValue)(raw)) {
|
|
113
|
+
return { ok: false, code: 'NOT_A_REFERENCE', query: raw, candidates: [] };
|
|
114
|
+
}
|
|
109
115
|
// An exact id or ref wins over everything, archived or not: both are
|
|
110
116
|
// explicit and unambiguous, so there is nothing to protect the caller from.
|
|
111
117
|
// Names are matched against live spaces only — see the ARCHIVED branch.
|
|
@@ -159,6 +165,8 @@ function describeSpaceCandidates(candidates, limit = 10) {
|
|
|
159
165
|
/** One human-readable block per failure, suitable for a CLI error body. */
|
|
160
166
|
function formatSpaceResolutionFailure(failure, bin = 'skrr') {
|
|
161
167
|
switch (failure.code) {
|
|
168
|
+
case 'NOT_A_REFERENCE':
|
|
169
|
+
return (0, failed_lookup_1.describeFailedLookupValue)(failure.query, 'Space');
|
|
162
170
|
case 'AMBIGUOUS':
|
|
163
171
|
return (`"${failure.query}" matches ${failure.candidates.length} spaces:\n` +
|
|
164
172
|
`${describeSpaceCandidates(failure.candidates)}\n` +
|
|
@@ -178,4 +186,5 @@ exports.SPACE_CLI_ERROR_CODE = {
|
|
|
178
186
|
AMBIGUOUS: 'SPACE_AMBIGUOUS',
|
|
179
187
|
ARCHIVED: 'SPACE_ARCHIVED',
|
|
180
188
|
NOT_FOUND: 'SPACE_NOT_FOUND',
|
|
189
|
+
NOT_A_REFERENCE: 'SPACE_NOT_A_REFERENCE',
|
|
181
190
|
};
|
|
@@ -28,5 +28,6 @@ export type ClosureLike = TaskClosureEvaluation | {
|
|
|
28
28
|
* the whole point is to be able to act without a second lookup.
|
|
29
29
|
*/
|
|
30
30
|
export declare function closureReport(closure: ClosureLike): string[];
|
|
31
|
-
/** `met (agent)` vs `met (
|
|
32
|
-
export
|
|
31
|
+
/** `met (agent)` vs `met (person)` — shared with the web through data-provider. */
|
|
32
|
+
export { describeAssessorKind as describeAssessor } from '@skrr-ai/data-provider';
|
|
33
|
+
export { describeClosure as closureSnapshotLines, type TaskClosureSnapshot as ClosureSnapshot, } from '@skrr-ai/data-provider';
|
package/dist/lib/task-closure.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.closureSnapshotLines = exports.describeAssessor = void 0;
|
|
3
4
|
exports.closureReport = closureReport;
|
|
4
|
-
|
|
5
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
6
|
function isUnavailable(closure) {
|
|
6
7
|
return Boolean(closure && closure.unavailable);
|
|
7
8
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
TASK_RESULT_SPEC_VERSION_MISMATCH: 'the Result answers an older specification',
|
|
12
|
-
TASK_RESULT_CHANGES_REQUESTED: 'a reviewer requested changes',
|
|
13
|
-
TASK_RESULT_REVIEW_REQUIRED: 'an independent accepted Review is required',
|
|
14
|
-
};
|
|
9
|
+
// One phrase table, shared with the web through data-provider. Two copies is how
|
|
10
|
+
// the same blocker came to read differently depending on which surface you asked.
|
|
11
|
+
const SHORT_BLOCKER = data_provider_1.CLOSURE_BLOCKER_PHRASE;
|
|
15
12
|
/**
|
|
16
13
|
* The full account, for the moment a caller has just written something and is
|
|
17
14
|
* deciding what to do next.
|
|
@@ -85,18 +82,9 @@ function closureReport(closure) {
|
|
|
85
82
|
: 'Closing anyway is allowed — all of this is recorded on resolution.closure.');
|
|
86
83
|
return lines;
|
|
87
84
|
}
|
|
88
|
-
/** `met (agent)` vs `met (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return 'agent';
|
|
92
|
-
if (kind === 'human')
|
|
93
|
-
return 'person';
|
|
94
|
-
if (kind === 'system')
|
|
95
|
-
return 'system';
|
|
96
|
-
if (kind === 'integration')
|
|
97
|
-
return 'integration';
|
|
98
|
-
return 'unattributed';
|
|
99
|
-
}
|
|
85
|
+
/** `met (agent)` vs `met (person)` — shared with the web through data-provider. */
|
|
86
|
+
var data_provider_2 = require("@skrr-ai/data-provider");
|
|
87
|
+
Object.defineProperty(exports, "describeAssessor", { enumerable: true, get: function () { return data_provider_2.describeAssessorKind; } });
|
|
100
88
|
function truncate(value, max) {
|
|
101
89
|
const flat = value.replace(/\s+/g, ' ').trim();
|
|
102
90
|
return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;
|
|
@@ -104,3 +92,13 @@ function truncate(value, max) {
|
|
|
104
92
|
function capitalize(value) {
|
|
105
93
|
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
106
94
|
}
|
|
95
|
+
/*
|
|
96
|
+
* The snapshot renderer moved to `@skrr-ai/data-provider`.
|
|
97
|
+
*
|
|
98
|
+
* It lived here, and "here" is `cli/` — so the web could not reach it, and the
|
|
99
|
+
* web is where most people read a finished Task. A single renderer that only one
|
|
100
|
+
* client can import is not a single renderer; it is the first copy. Re-exported
|
|
101
|
+
* under the old names so this module stays the CLI's one door to closure copy.
|
|
102
|
+
*/
|
|
103
|
+
var data_provider_3 = require("@skrr-ai/data-provider");
|
|
104
|
+
Object.defineProperty(exports, "closureSnapshotLines", { enumerable: true, get: function () { return data_provider_3.describeClosure; } });
|
|
@@ -14,15 +14,25 @@ function renderTaskLifecycleSummary(response, log) {
|
|
|
14
14
|
log('Attach one: skrr tasks lifecycle attach <task-id> --pack finding');
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
// Two different version numbers used to share one label. `v1` next to
|
|
18
|
+
// `pack finding` reads as "finding v1", and `finding` is pack v2 — `version`
|
|
19
|
+
// is the installed definition's WorkflowVersion. Name each one.
|
|
20
|
+
const protocol = lifecycle.packId
|
|
21
|
+
? `pack ${lifecycle.packId}${lifecycle.packVersion ? ` v${lifecycle.packVersion}` : ''}`
|
|
22
|
+
: `definition ${lifecycle.definitionId}`;
|
|
23
|
+
log(`Lifecycle: ${lifecycle.status} — ${protocol}, protocol version ${lifecycle.version}, generation ${lifecycle.generation}, revision ${lifecycle.revision}`);
|
|
19
24
|
if (response.run)
|
|
20
25
|
log(`Run: ${response.run.id} — ${response.run.status}`);
|
|
21
26
|
for (const stage of response.stages || []) {
|
|
22
27
|
const marker = stage.key === lifecycle.currentStageKey ? '>' : ' ';
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
// A reopen marks everything behind its entry stage `skipped` in the NEW
|
|
29
|
+
// run. Saying so unqualified told the reader that work nobody skipped had
|
|
30
|
+
// never happened; say which generation did it instead.
|
|
31
|
+
const state = stage.completedInGeneration
|
|
32
|
+
? ` — done in generation ${stage.completedInGeneration}${stage.priorOutcome ? `/${stage.priorOutcome}` : ''}`
|
|
33
|
+
: stage.status
|
|
34
|
+
? ` — ${stage.status}${stage.outcome ? `/${stage.outcome}` : ''}`
|
|
35
|
+
: '';
|
|
26
36
|
log(`${marker} ${stage.label || stage.key}${state}`);
|
|
27
37
|
}
|
|
28
38
|
if (response.gate) {
|
|
@@ -43,28 +53,122 @@ function renderTaskLifecycleSummary(response, log) {
|
|
|
43
53
|
: undefined,
|
|
44
54
|
transition.requires?.deliverableRef ? 'deliverable ref' : undefined,
|
|
45
55
|
].filter(Boolean);
|
|
46
|
-
|
|
56
|
+
// Lead with what it DOES and say where it goes. This printed
|
|
57
|
+
// `id -> outcome`, and `outcome` is an edge name: at triage that read
|
|
58
|
+
// `accept -> accept`, with nothing to separate the branch that continues
|
|
59
|
+
// to Diagnose from the one that ends the finding.
|
|
60
|
+
const destination = transition.endsLifecycle
|
|
61
|
+
? 'ends the lifecycle'
|
|
62
|
+
: transition.toStageLabel || transition.toStageKey;
|
|
63
|
+
log(` ${transition.id}${transition.label ? ` — ${transition.label}` : ''}${destination ? ` -> ${destination}` : ''}${needs.length ? ` — needs ${needs.join(', ')}` : ''}`);
|
|
64
|
+
// A copyable skeleton. Naming the update TYPE and a count of evidence
|
|
65
|
+
// told nobody which keys to put where, so every payload shape was
|
|
66
|
+
// guesswork against a contract only the server could see.
|
|
67
|
+
const skeleton = transitionPayloadSkeleton(transition);
|
|
68
|
+
if (skeleton)
|
|
69
|
+
log(` --payload '${skeleton}'`);
|
|
47
70
|
}
|
|
48
71
|
}
|
|
49
72
|
if (response.pendingEffects?.length) {
|
|
50
73
|
log('');
|
|
74
|
+
const exhausted = response.pendingEffects.filter((effect) => effect.status === 'failed');
|
|
51
75
|
log(`${response.pendingEffects.length} effect(s) pending repair:`);
|
|
52
76
|
for (const effect of response.pendingEffects) {
|
|
53
|
-
|
|
77
|
+
// Say whether it is still trying. A row that has given up and a row
|
|
78
|
+
// mid-retry need different things from the reader.
|
|
79
|
+
const state = effect.status === 'failed' ? ' [stopped retrying]' : '';
|
|
80
|
+
log(` ${effect.kind}${state}${effect.attempts ? ` after ${effect.attempts} attempt(s)` : ''}${effect.lastError ? ` — ${effect.lastError}` : ''}`);
|
|
81
|
+
}
|
|
82
|
+
// Any row carrying an error is already actionable — waiting for it to
|
|
83
|
+
// exhaust its retries before naming the repair leaves the reader staring at
|
|
84
|
+
// a stage that cannot move and a transition list that offers nothing.
|
|
85
|
+
if (response.pendingEffects.some((effect) => effect.lastError)) {
|
|
86
|
+
log(exhausted.length
|
|
87
|
+
? 'Fix what the error names, then re-arm them: skrr tasks lifecycle reconcile <task-id>'
|
|
88
|
+
: 'Still retrying. Fix what the error names, then: skrr tasks lifecycle reconcile <task-id>');
|
|
54
89
|
}
|
|
55
90
|
}
|
|
56
91
|
if (lifecycle.statusDrift) {
|
|
57
92
|
log(`Status drift: stage expects ${lifecycle.statusDrift.expected}, Task shows ${lifecycle.statusDrift.actual}.`);
|
|
58
93
|
}
|
|
59
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* A copyable `--payload` skeleton for one transition, built from what the
|
|
97
|
+
* server says the transition requires.
|
|
98
|
+
*
|
|
99
|
+
* Deliberately built from the RESPONSE rather than from a table in the CLI: the
|
|
100
|
+
* field list per update type is the server's to own, and a second copy here
|
|
101
|
+
* would drift the moment a pack changed.
|
|
102
|
+
*/
|
|
103
|
+
function transitionPayloadSkeleton(transition) {
|
|
104
|
+
const requires = transition.requires;
|
|
105
|
+
if (!requires)
|
|
106
|
+
return undefined;
|
|
107
|
+
const payload = {};
|
|
108
|
+
for (const field of requires.fields || [])
|
|
109
|
+
payload[field] = `<${field}>`;
|
|
110
|
+
if (requires.deliverableRef)
|
|
111
|
+
payload.resourceLinkId = '<resource-link-id>';
|
|
112
|
+
if (requires.updateType) {
|
|
113
|
+
const data = {};
|
|
114
|
+
for (const field of requires.updateFields || []) {
|
|
115
|
+
data[field] = placeholderFor(field, requires.updateFieldShapes?.[field]);
|
|
116
|
+
}
|
|
117
|
+
if (requires.evidence) {
|
|
118
|
+
data.evidenceRefs = [{ kind: 'task', id: '<task-id>' }];
|
|
119
|
+
}
|
|
120
|
+
payload.update = { summary: '<one line about the work>', data };
|
|
121
|
+
}
|
|
122
|
+
return Object.keys(payload).length ? JSON.stringify(payload) : undefined;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* A placeholder that has the field's SHAPE, taken from what the server declared.
|
|
126
|
+
* A list field shown as a string led people to write one comma-joined item
|
|
127
|
+
* where they meant several.
|
|
128
|
+
*/
|
|
129
|
+
function placeholderFor(field, shape) {
|
|
130
|
+
if (!shape)
|
|
131
|
+
return `<${field}>`;
|
|
132
|
+
if (shape === 'string[]')
|
|
133
|
+
return [`<${field} item>`];
|
|
134
|
+
if (shape === 'reference[]')
|
|
135
|
+
return [];
|
|
136
|
+
if (shape === 'principal')
|
|
137
|
+
return { kind: 'human', userId: '<user-id>' };
|
|
138
|
+
// An enum: show the legal values rather than a name to guess at.
|
|
139
|
+
if (shape.includes('|'))
|
|
140
|
+
return `<${shape}>`;
|
|
141
|
+
return `<${field}>`;
|
|
142
|
+
}
|
|
60
143
|
/** Render the outcome of a lifecycle transition call. */
|
|
61
144
|
function renderLifecycleTransitionResult(response, log) {
|
|
62
|
-
|
|
145
|
+
// A replay is not an apply. Retrying is the NORMAL path here — the default
|
|
146
|
+
// idempotency key is derived, so a re-run of the same command replays — and
|
|
147
|
+
// this printed "Transition applied / effect record_update: applied" either
|
|
148
|
+
// way, so the one question a retry asks (did I just do that, or had I
|
|
149
|
+
// already?) had no answer in the output.
|
|
150
|
+
const replayed = response.idempotent === true;
|
|
151
|
+
// A transition can be `applied` while one of its declared effects did not
|
|
152
|
+
// run. The headline said "Transition applied." and the reader had to notice a
|
|
153
|
+
// per-effect `pending` several lines down, so a capture whose source was
|
|
154
|
+
// never recorded read as a success.
|
|
155
|
+
const owed = (response.effects || []).filter((effect) => effect.status !== 'applied');
|
|
156
|
+
log(`Transition ${response.status}${replayed ? ' (replayed — the recorded outcome, no new work)' : ''}${response.code ? ` (${response.code})` : ''}${owed.length ? ` — ${owed.length} of ${response.effects?.length} effect(s) did NOT run` : ''}.`);
|
|
63
157
|
if (response.message)
|
|
64
158
|
log(response.message);
|
|
65
159
|
for (const effect of response.effects || []) {
|
|
66
160
|
log(` effect ${effect.kind}: ${effect.status}${effect.ref ? ` -> ${effect.ref}` : ''}${effect.error ? ` — ${effect.error}` : ''}`);
|
|
67
161
|
}
|
|
162
|
+
// The server names the legal transitions when it refuses an unknown id, and
|
|
163
|
+
// the human renderer dropped them — so a sequencing mistake ("not from this
|
|
164
|
+
// stage") and a typo read identically.
|
|
165
|
+
const allowed = response.details?.allowed;
|
|
166
|
+
if (Array.isArray(allowed) && allowed.length) {
|
|
167
|
+
log(`Legal from this stage: ${allowed.join(', ')}`);
|
|
168
|
+
}
|
|
169
|
+
const actualRevision = response.details?.actualRevision;
|
|
170
|
+
if (actualRevision !== undefined)
|
|
171
|
+
log(`Current revision is ${String(actualRevision)}.`);
|
|
68
172
|
if (response.gate)
|
|
69
173
|
log(`Gate: ${response.gate.message}`);
|
|
70
174
|
if (response.lifecycle) {
|
|
@@ -85,7 +189,11 @@ function renderLifecycleHistory(response, log) {
|
|
|
85
189
|
const parts = [
|
|
86
190
|
event.at,
|
|
87
191
|
event.type,
|
|
88
|
-
|
|
192
|
+
// Name the protocol beside the generation. A Task that wore two packs
|
|
193
|
+
// across a detach and re-attach rendered both passes identically.
|
|
194
|
+
event.generation !== undefined
|
|
195
|
+
? `gen ${event.generation}${event.packId ? ` (${event.packId})` : ''}`
|
|
196
|
+
: undefined,
|
|
89
197
|
event.transitionId,
|
|
90
198
|
event.status,
|
|
91
199
|
event.outcome,
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Task reference resolution — turn what a person typed into exactly one task
|
|
3
|
-
* id, or an actionable failure.
|
|
4
|
-
*
|
|
5
|
-
* Distinct from `task-resolver.ts`, which is about WHICH INPUT WINS among the
|
|
6
|
-
* positional arg, `--task`, `--from-json` and the daemon runtime context. That
|
|
7
|
-
* module does no lookup at all: it picks a string and hands it to the API. This
|
|
8
|
-
* one runs after it and answers a different question — what does that string
|
|
9
|
-
* refer to?
|
|
10
|
-
*
|
|
11
|
-
* Two shapes already work without a lookup. A UUID is the canonical id, and an
|
|
12
|
-
* `OSK-2423` ref resolves server-side (`getTask` special-cases the pattern in
|
|
13
|
-
* api/models/Task.js). A title did not work, which is the gap here: the ref is
|
|
14
|
-
* printed in every listing but a person reading a task board out loud has the
|
|
15
|
-
* title, not the number.
|
|
16
|
-
*/
|
|
17
1
|
export type TaskRef = {
|
|
18
2
|
id: string;
|
|
19
3
|
/** The human-readable ref, e.g. `OSK-2423`. */
|
|
@@ -23,7 +7,7 @@ export type TaskRef = {
|
|
|
23
7
|
};
|
|
24
8
|
export type TaskResolutionRefFailure = {
|
|
25
9
|
ok: false;
|
|
26
|
-
code: 'NOT_FOUND' | 'AMBIGUOUS';
|
|
10
|
+
code: 'NOT_FOUND' | 'AMBIGUOUS' | 'NOT_A_REFERENCE';
|
|
27
11
|
query: string;
|
|
28
12
|
candidates: TaskRef[];
|
|
29
13
|
};
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TASK_REF_CLI_ERROR_CODE = void 0;
|
|
4
|
+
exports.directTaskRef = directTaskRef;
|
|
5
|
+
exports.taskRefFrom = taskRefFrom;
|
|
6
|
+
exports.resolveTaskRef = resolveTaskRef;
|
|
7
|
+
exports.describeTask = describeTask;
|
|
8
|
+
exports.describeTaskCandidates = describeTaskCandidates;
|
|
9
|
+
exports.formatTaskRefFailure = formatTaskRefFailure;
|
|
10
|
+
const failed_lookup_1 = require("./failed-lookup");
|
|
2
11
|
/**
|
|
3
12
|
* Task reference resolution — turn what a person typed into exactly one task
|
|
4
13
|
* id, or an actionable failure.
|
|
@@ -15,14 +24,6 @@
|
|
|
15
24
|
* printed in every listing but a person reading a task board out loud has the
|
|
16
25
|
* title, not the number.
|
|
17
26
|
*/
|
|
18
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.TASK_REF_CLI_ERROR_CODE = void 0;
|
|
20
|
-
exports.directTaskRef = directTaskRef;
|
|
21
|
-
exports.taskRefFrom = taskRefFrom;
|
|
22
|
-
exports.resolveTaskRef = resolveTaskRef;
|
|
23
|
-
exports.describeTask = describeTask;
|
|
24
|
-
exports.describeTaskCandidates = describeTaskCandidates;
|
|
25
|
-
exports.formatTaskRefFailure = formatTaskRefFailure;
|
|
26
27
|
const UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
27
28
|
/** The server's identifier pattern, relaxed to accept a lowercase prefix. */
|
|
28
29
|
const IDENTIFIER_RE = /^[A-Za-z]+-\d+$/;
|
|
@@ -78,6 +79,11 @@ function resolveTaskRef(query, tasks) {
|
|
|
78
79
|
if (!q) {
|
|
79
80
|
return { ok: false, code: 'NOT_FOUND', query: raw, candidates: tasks };
|
|
80
81
|
}
|
|
82
|
+
// A failed lookup's output is refused before the title tiers can match it.
|
|
83
|
+
// See `failed-lookup.ts`.
|
|
84
|
+
if ((0, failed_lookup_1.isFailedLookupValue)(raw)) {
|
|
85
|
+
return { ok: false, code: 'NOT_A_REFERENCE', query: raw, candidates: [] };
|
|
86
|
+
}
|
|
81
87
|
const tiers = [
|
|
82
88
|
// 1. Exact id.
|
|
83
89
|
(t) => t.id === raw,
|
|
@@ -118,6 +124,9 @@ function describeTaskCandidates(candidates, limit = 10) {
|
|
|
118
124
|
return shown.join('\n');
|
|
119
125
|
}
|
|
120
126
|
function formatTaskRefFailure(failure, bin = 'skrr') {
|
|
127
|
+
if (failure.code === 'NOT_A_REFERENCE') {
|
|
128
|
+
return (0, failed_lookup_1.describeFailedLookupValue)(failure.query, 'task');
|
|
129
|
+
}
|
|
121
130
|
if (failure.code === 'AMBIGUOUS') {
|
|
122
131
|
return (`"${failure.query}" matches ${failure.candidates.length} tasks by title:\n` +
|
|
123
132
|
`${describeTaskCandidates(failure.candidates)}\n` +
|
|
@@ -130,4 +139,5 @@ function formatTaskRefFailure(failure, bin = 'skrr') {
|
|
|
130
139
|
exports.TASK_REF_CLI_ERROR_CODE = {
|
|
131
140
|
AMBIGUOUS: 'TASK_REF_AMBIGUOUS',
|
|
132
141
|
NOT_FOUND: 'TASK_REF_NOT_FOUND',
|
|
142
|
+
NOT_A_REFERENCE: 'TASK_REF_NOT_A_REFERENCE',
|
|
133
143
|
};
|