@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
|
@@ -33,13 +33,73 @@ class SpacesWorkflowsLifecycleDiagnostics extends base_command_1.BaseCommand {
|
|
|
33
33
|
`, ${response.stages.completed} completed` +
|
|
34
34
|
` (median ${Math.round(response.stages.durationMs.median / 1000)}s` +
|
|
35
35
|
`, max ${Math.round(response.stages.durationMs.max / 1000)}s)`);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
/*
|
|
37
|
+
* The server's effect counters NEST — `pending` is every effect still owed,
|
|
38
|
+
* `repairs` the owed ones that hit an error, `exhausted` the owed ones that
|
|
39
|
+
* stopped retrying — so they must be printed as parts of one number, never
|
|
40
|
+
* side by side. Side by side, one stopped effect read as "1 pending (oldest
|
|
41
|
+
* 8h), 1 repaired, 1 stopped retrying": three effects, one of them
|
|
42
|
+
* apparently fixed, when there was exactly one and nothing was fixed.
|
|
43
|
+
*/
|
|
44
|
+
const { pending: owed, exhausted: stopped = 0, repairs: faulted } = response.effects;
|
|
45
|
+
const retrying = Math.max(0, owed - stopped);
|
|
46
|
+
this.log(owed
|
|
47
|
+
? ` effects: ${owed} owed (oldest ${Math.round(response.effects.oldestPendingMs / 1000)}s)` +
|
|
48
|
+
` — ${retrying} still retrying, ${stopped} stopped; ${faulted} hit an error`
|
|
49
|
+
: ' effects: none owed');
|
|
39
50
|
this.log(` transitions: ${response.transitions.applied} applied` +
|
|
40
51
|
`, ${response.transitions.gated} gated` +
|
|
41
52
|
`, ${response.transitions.superseded} superseded`);
|
|
42
53
|
this.log(` orphan stages: ${response.orphans}`);
|
|
54
|
+
/*
|
|
55
|
+
* WHAT TO DO. The counts above tell an operator how much; LC-13's bar is
|
|
56
|
+
* that each shape of "stuck" also names its repair, because "slow", "stuck"
|
|
57
|
+
* and "waiting for me" are three different facts and one undifferentiated
|
|
58
|
+
* number is not actionable. Only shapes that are actually present print,
|
|
59
|
+
* so a healthy Space says so instead of listing remedies for nothing.
|
|
60
|
+
*/
|
|
61
|
+
// Diagnostics are aggregate by design, so every repair that needs a Task
|
|
62
|
+
// must say how to FIND that Task — and in the Space the operator just
|
|
63
|
+
// named, so the command runs as printed rather than as a template.
|
|
64
|
+
const space = response.spaceId;
|
|
65
|
+
// Every owed effect places the lifecycle's exceptional hold, so this one
|
|
66
|
+
// query finds the Tasks behind both retrying and stopped effects. Holds
|
|
67
|
+
// other authors placed show up too; the NEXT column says which is which.
|
|
68
|
+
const findHeld = `skrr tasks list --space ${space} --actionability-reason exceptional_hold`;
|
|
69
|
+
const actions = [];
|
|
70
|
+
const waitingOn = response.stages.waitingOn;
|
|
71
|
+
if (waitingOn?.human) {
|
|
72
|
+
actions.push(`${waitingOn.human} stage(s) waiting on a person — nothing is broken; a person owes a decision. ` +
|
|
73
|
+
`Find them: skrr tasks list --space ${space} --actionable-by human`);
|
|
74
|
+
}
|
|
75
|
+
if (waitingOn?.agent) {
|
|
76
|
+
actions.push(`${waitingOn.agent} stage(s) waiting on an agent — an agent owes a turn. ` +
|
|
77
|
+
`Find them: skrr tasks list --space ${space} --actionable-by agent`);
|
|
78
|
+
}
|
|
79
|
+
if (response.bindings.drifted) {
|
|
80
|
+
actions.push(`${response.bindings.drifted} binding(s) whose Task status disagrees with its stage — ` +
|
|
81
|
+
'run "skrr tasks lifecycle reconcile <task-id>", or move the Task back to the stage\'s status');
|
|
82
|
+
}
|
|
83
|
+
if (stopped) {
|
|
84
|
+
actions.push(`${stopped} effect(s) stopped retrying — they will not run again on their own. Find them: ${findHeld}; ` +
|
|
85
|
+
'"skrr tasks lifecycle show <task-id>" names the error; fix the cause, then "skrr tasks lifecycle reconcile <task-id>"');
|
|
86
|
+
}
|
|
87
|
+
if (retrying) {
|
|
88
|
+
actions.push(`${retrying} effect(s) still retrying — usually transient. If they persist, find them: ${findHeld}; ` +
|
|
89
|
+
'"skrr tasks lifecycle show <task-id>" names the error');
|
|
90
|
+
}
|
|
91
|
+
if (response.orphans) {
|
|
92
|
+
actions.push(`${response.orphans} orphan stage(s) — a run waiting for a Task that moved on; ` +
|
|
93
|
+
'"skrr tasks lifecycle reconcile <task-id>" re-syncs it');
|
|
94
|
+
}
|
|
95
|
+
this.log('');
|
|
96
|
+
if (!actions.length) {
|
|
97
|
+
this.log('Nothing needs attention.');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
this.log('What to do:');
|
|
101
|
+
for (const action of actions)
|
|
102
|
+
this.log(` - ${action}`);
|
|
43
103
|
}
|
|
44
104
|
}
|
|
45
105
|
exports.default = SpacesWorkflowsLifecycleDiagnostics;
|
|
@@ -4,10 +4,35 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
5
|
const base_command_1 = require("../../../base-command");
|
|
6
6
|
const bulk_task_targets_1 = require("../../../lib/bulk-task-targets");
|
|
7
|
+
/**
|
|
8
|
+
* Parse `<agentId>:<actionId>`, splitting on the FIRST colon only.
|
|
9
|
+
*
|
|
10
|
+
* Agent ids never contain a colon; action ids can. This used to split on every
|
|
11
|
+
* colon and keep segments 0 and 1, so `<agent>:<action>:ANYTHING` silently
|
|
12
|
+
* attached `<agent>:<action>` — the caller asked for one thing and got another,
|
|
13
|
+
* exit 0 (OSK-9636). Keeping the remainder as the action id means an extra
|
|
14
|
+
* segment reaches the server as part of an id that does not exist, and is
|
|
15
|
+
* refused there.
|
|
16
|
+
*
|
|
17
|
+
* A space custom action's projected id is `space-custom:<id>`. Read as a ref it
|
|
18
|
+
* became agent `space-custom`, and the server correctly answered "Agent
|
|
19
|
+
* space-custom not found" — true of a string nobody meant as an agent id, and
|
|
20
|
+
* no help (OSK-9637). Those actions run from the space and are deliberately not
|
|
21
|
+
* attachable, so say that instead.
|
|
22
|
+
*/
|
|
7
23
|
function parseRef(input) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
24
|
+
const trimmed = input.trim();
|
|
25
|
+
const spaceCustom = `"${trimmed}" is a space custom action. Those run from the space and cannot be attached to a task — only agent quick actions can. List attachable ones with: skrr spaces actions available <space-id>`;
|
|
26
|
+
if ((0, data_provider_1.isSpaceCustomActionId)(trimmed))
|
|
27
|
+
return { problem: spaceCustom };
|
|
28
|
+
const colon = trimmed.indexOf(':');
|
|
29
|
+
const agentId = colon > 0 ? trimmed.slice(0, colon).trim() : '';
|
|
30
|
+
const actionId = colon > 0 ? trimmed.slice(colon + 1).trim() : '';
|
|
31
|
+
if (!agentId || !actionId) {
|
|
32
|
+
return { problem: `Invalid ref "${input}" — expected <agentId>:<actionId>.` };
|
|
33
|
+
}
|
|
34
|
+
if ((0, data_provider_1.isSpaceCustomActionId)(actionId))
|
|
35
|
+
return { problem: spaceCustom };
|
|
11
36
|
return { agentId, actionId };
|
|
12
37
|
}
|
|
13
38
|
const refKey = (ref) => `${ref.agentId}:${ref.actionId}`;
|
|
@@ -55,11 +80,11 @@ class TasksActionsAttach extends base_command_1.BaseCommand {
|
|
|
55
80
|
}
|
|
56
81
|
const incoming = [];
|
|
57
82
|
for (const raw of refPositionals) {
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
60
|
-
this.error(
|
|
83
|
+
const parsed = parseRef(raw);
|
|
84
|
+
if ('problem' in parsed) {
|
|
85
|
+
this.error(parsed.problem, { exit: 1 });
|
|
61
86
|
}
|
|
62
|
-
incoming.push(
|
|
87
|
+
incoming.push(parsed);
|
|
63
88
|
}
|
|
64
89
|
if (incoming.length === 0) {
|
|
65
90
|
this.error('At least one <agentId>:<actionId> ref is required. Discover refs with: skrr spaces actions available <space-id>', { exit: 1 });
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.describePendingReview = describePendingReview;
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
5
|
const base_command_1 = require("../../base-command");
|
|
6
|
+
const task_closure_1 = require("../../lib/task-closure");
|
|
6
7
|
const api_fetch_1 = require("../../lib/api-fetch");
|
|
7
8
|
const outbox_1 = require("../../lib/outbox");
|
|
8
9
|
const prompt_1 = require("../../lib/prompt");
|
|
@@ -271,6 +272,18 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
271
272
|
});
|
|
272
273
|
if (pending)
|
|
273
274
|
this.log(pending);
|
|
275
|
+
/*
|
|
276
|
+
* Say what was closed over, on the door that actually closes things.
|
|
277
|
+
*
|
|
278
|
+
* `tasks complete` reported nothing about the closure at all — no blockers,
|
|
279
|
+
* no `overrode` — so the person who forced past an owner-declared required
|
|
280
|
+
* Review was never told they had. Only `tasks move` read the field, and this
|
|
281
|
+
* is the more common way to finish a Task. Same renderer as `move` and
|
|
282
|
+
* `show`, so all three describe one close identically.
|
|
283
|
+
*/
|
|
284
|
+
for (const sentence of (0, task_closure_1.closureSnapshotLines)(result.task?.resolution?.closure)) {
|
|
285
|
+
this.log(sentence);
|
|
286
|
+
}
|
|
274
287
|
for (const advisory of result.advisories || [])
|
|
275
288
|
this.warn(advisory.message);
|
|
276
289
|
if (url)
|
|
@@ -240,7 +240,7 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
240
240
|
'Omit to pin the definition’s current published version at create time.',
|
|
241
241
|
}),
|
|
242
242
|
'lifecycle-pack': core_1.Flags.string({
|
|
243
|
-
description: 'Bind the created task to a built-in lifecycle pack (
|
|
243
|
+
description: 'Bind the created task to a built-in lifecycle pack (finding, doc_to_task). ' +
|
|
244
244
|
'Alternative to --lifecycle-definition.',
|
|
245
245
|
}),
|
|
246
246
|
};
|
|
@@ -16,10 +16,20 @@ class TasksEventsAppend extends base_command_1.BaseCommand {
|
|
|
16
16
|
// the retired vocabulary read as the intended path — which is how it kept
|
|
17
17
|
// getting exercised.
|
|
18
18
|
static summary = 'DEPRECATED compatibility spelling for `tasks updates add` — append a legacy task event';
|
|
19
|
+
// The FULL mapping, not a sample of it. The previous description named
|
|
20
|
+
// `handoff`/`checkpoint` and `started|lifecycle` and stopped — so a caller
|
|
21
|
+
// passing `--kind evidence` had no way to learn, before writing, that it
|
|
22
|
+
// becomes a `verification` Update. Those are different claims: `evidence`
|
|
23
|
+
// says a flow was exercised, `verification` says a claim was CHECKED, and the
|
|
24
|
+
// typed-Update vocabulary exists precisely so the two cannot be confused.
|
|
25
|
+
// Mirrors `LEGACY_KIND_TO_UPDATE_TYPE` in `api/server/services/TaskWorkEvent.js`.
|
|
19
26
|
static description = 'DEPRECATED compatibility adapter for the historical Event vocabulary. Every kind is ' +
|
|
20
|
-
'stored as a canonical Update, so
|
|
21
|
-
'`
|
|
22
|
-
'
|
|
27
|
+
'stored as a canonical Update of a DIFFERENT name, so read this before relying on one: ' +
|
|
28
|
+
'`evidence` and `verification` both become `verification`; `checkpoint`, `changed`, ' +
|
|
29
|
+
'`completed` and `handoff` all become `progress`; `input_requested` and `failed` become ' +
|
|
30
|
+
'`blocker`; `decision` stays `decision`. `--kind started|lifecycle` writes only to the ' +
|
|
31
|
+
'Activity ledger and will NOT appear in `tasks updates list`. Use `tasks updates add` ' +
|
|
32
|
+
'instead, which writes the type you name.';
|
|
23
33
|
static examples = [
|
|
24
34
|
'<%= config.bin %> tasks events append <task-id> --kind checkpoint --idempotency-key "<task-id>:first-pass" --summary "Parser handles nested fences"',
|
|
25
35
|
'<%= config.bin %> tasks events append <task-id> --kind decision --idempotency-key "<task-id>:storage-choice" --summary "Chose the CRDT path" --evidence "A direct contentHtml write is reverted by the next render tick"',
|
|
@@ -53,7 +63,7 @@ class TasksEventsAppend extends base_command_1.BaseCommand {
|
|
|
53
63
|
// `Invalid request data` / HTTP_400. (OSK-4888)
|
|
54
64
|
kind: core_1.Flags.string({
|
|
55
65
|
required: true,
|
|
56
|
-
description: '
|
|
66
|
+
description: 'Legacy event kind. Stored as a canonical Update of a different name — see the mapping in the description above, and prefer `tasks updates add`',
|
|
57
67
|
options: [...tasks_1.TASK_WORK_EVENT_KINDS],
|
|
58
68
|
}),
|
|
59
69
|
summary: core_1.Flags.string({ required: true, description: 'What is newly true, in one sentence' }),
|
|
@@ -108,8 +118,21 @@ class TasksEventsAppend extends base_command_1.BaseCommand {
|
|
|
108
118
|
if (written.canonicalType) {
|
|
109
119
|
this.log(`Recorded on ${task.id} as a ${written.canonicalType} Update` +
|
|
110
120
|
`${written.id ? ` (${written.id})` : ''}.`);
|
|
111
|
-
|
|
112
|
-
|
|
121
|
+
/*
|
|
122
|
+
* Scope the claim to this KIND.
|
|
123
|
+
*
|
|
124
|
+
* "a compatibility spelling" reads as "same command, older name" — so a
|
|
125
|
+
* caller who has just used it successfully reaches for it again with a
|
|
126
|
+
* kind it does not take. The two vocabularies overlap in only three of
|
|
127
|
+
* fourteen values (`decision`, `blocker`, `verification`), and this line
|
|
128
|
+
* is printed on exactly those — confirming the general claim precisely
|
|
129
|
+
* where it happens to be true. `finding` and `progress` exist only on the
|
|
130
|
+
* canonical command; `evidence`, `checkpoint` and the rest only here.
|
|
131
|
+
*/
|
|
132
|
+
this.log(`\`tasks events append --kind ${written.canonicalType}\` maps onto ` +
|
|
133
|
+
`\`${this.config.bin} tasks updates add --type ${written.canonicalType}\`, which is the ` +
|
|
134
|
+
'canonical command. Their vocabularies differ otherwise — `--type ' +
|
|
135
|
+
'progress|finding` exist only there — and `tasks updates list` reads them back.');
|
|
113
136
|
return;
|
|
114
137
|
}
|
|
115
138
|
// `--kind started|lifecycle` produces no Update at all: it writes a
|
|
@@ -16,6 +16,7 @@ export default class TasksExpectationsAssess extends BaseCommand {
|
|
|
16
16
|
'spec-version': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
17
17
|
'idempotency-key': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
18
18
|
all: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
19
|
+
'include-unmet': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
19
20
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
20
21
|
};
|
|
21
22
|
/**
|
|
@@ -43,7 +43,12 @@ class TasksExpectationsAssess extends base_command_1.BaseCommand {
|
|
|
43
43
|
'idempotency-key': core_1.Flags.string({ description: 'Stable retry key' }),
|
|
44
44
|
all: core_1.Flags.boolean({
|
|
45
45
|
description: 'Assess every Expectation still awaiting one on this Task, with the same decision ' +
|
|
46
|
-
'and reason. Already-settled Expectations are left alone
|
|
46
|
+
'and reason. Already-settled Expectations are left alone, and ones already judged ' +
|
|
47
|
+
'unmet are skipped unless --include-unmet.',
|
|
48
|
+
}),
|
|
49
|
+
'include-unmet': core_1.Flags.boolean({
|
|
50
|
+
description: 'With --all, also re-assess Expectations somebody judged unmet — for after the work ' +
|
|
51
|
+
'was fixed. Overwrites a considered rejection, so it is never implied.',
|
|
47
52
|
}),
|
|
48
53
|
json: core_1.Flags.boolean({ description: 'Output JSON' }),
|
|
49
54
|
};
|
|
@@ -56,11 +61,26 @@ class TasksExpectationsAssess extends base_command_1.BaseCommand {
|
|
|
56
61
|
* batch verb must never do. Overriding an existing assessment stays a
|
|
57
62
|
* deliberate, single, named act.
|
|
58
63
|
*/
|
|
59
|
-
async awaitingIds(taskId) {
|
|
64
|
+
async awaitingIds(taskId, { includeUnmet = false } = {}) {
|
|
60
65
|
const acceptance = await data_provider_1.dataService.getTaskAcceptance(taskId);
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
const gatingUnsettled = acceptance.expectations.filter((expectation) => expectation.gating && !expectation.met);
|
|
67
|
+
/*
|
|
68
|
+
* A row somebody judged `unmet` is SKIPPED by default and NAMED either way.
|
|
69
|
+
*
|
|
70
|
+
* Skipping is right: a blanket `--all --decision met` that swept up a
|
|
71
|
+
* reviewer's considered "no" is the one thing a batch verb must never do.
|
|
72
|
+
* Saying nothing about it was not: the row displayed as "needs assessment"
|
|
73
|
+
* (it no longer does), `--all` reported the Task settled, and the board
|
|
74
|
+
* could never be cleared because the only bulk verb silently declined to
|
|
75
|
+
* touch the only rows left.
|
|
76
|
+
*/
|
|
77
|
+
const rejected = gatingUnsettled
|
|
78
|
+
.filter((expectation) => expectation.state === 'unmet')
|
|
63
79
|
.map((expectation) => expectation.id);
|
|
80
|
+
const ids = gatingUnsettled
|
|
81
|
+
.filter((expectation) => !expectation.assessmentId || (includeUnmet && expectation.state === 'unmet'))
|
|
82
|
+
.map((expectation) => expectation.id);
|
|
83
|
+
return { ids, rejected };
|
|
64
84
|
}
|
|
65
85
|
async run() {
|
|
66
86
|
this.requireAuth();
|
|
@@ -123,17 +143,35 @@ class TasksExpectationsAssess extends base_command_1.BaseCommand {
|
|
|
123
143
|
};
|
|
124
144
|
if (flags.all) {
|
|
125
145
|
let ids;
|
|
146
|
+
let rejected;
|
|
126
147
|
try {
|
|
127
|
-
ids = await this.awaitingIds(task.id
|
|
148
|
+
({ ids, rejected } = await this.awaitingIds(task.id, {
|
|
149
|
+
includeUnmet: flags['include-unmet'],
|
|
150
|
+
}));
|
|
128
151
|
}
|
|
129
152
|
catch (error) {
|
|
130
153
|
this.handleApiError(error);
|
|
131
154
|
return;
|
|
132
155
|
}
|
|
156
|
+
const skippedRejections = flags['include-unmet'] ? [] : rejected;
|
|
133
157
|
if (ids.length === 0) {
|
|
158
|
+
if (skippedRejections.length > 0) {
|
|
159
|
+
// "Nothing is awaiting assessment" was true of what this command
|
|
160
|
+
// WOULD touch and false about the Task, which is the distinction that
|
|
161
|
+
// made the board unclearable.
|
|
162
|
+
this.log(`Nothing is awaiting a first assessment, but ${skippedRejections.length} ` +
|
|
163
|
+
`Expectation(s) were judged unmet: ${skippedRejections.join(', ')}.`);
|
|
164
|
+
this.log(`Fix the work and re-assess ${skippedRejections.length === 1 ? 'it' : 'them'} by id, ` +
|
|
165
|
+
'or re-run with --include-unmet to overwrite the rejection.');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
134
168
|
this.log('Nothing is awaiting assessment on this Task.');
|
|
135
169
|
return;
|
|
136
170
|
}
|
|
171
|
+
if (skippedRejections.length > 0) {
|
|
172
|
+
this.log(`Skipping ${skippedRejections.length} Expectation(s) judged unmet ` +
|
|
173
|
+
`(${skippedRejections.join(', ')}) — pass --include-unmet to re-assess them.`);
|
|
174
|
+
}
|
|
137
175
|
// Each write is reported on its own. A partial failure must leave the
|
|
138
176
|
// caller knowing exactly which Expectations were settled, because the
|
|
139
177
|
// remedy is to re-run for the rest — a single rolled-up "failed" would
|
|
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
5
|
const data_provider_2 = require("@skrr-ai/data-provider");
|
|
6
6
|
const base_command_1 = require("../../base-command");
|
|
7
|
+
const data_provider_3 = require("@skrr-ai/data-provider");
|
|
7
8
|
const task_closure_1 = require("../../lib/task-closure");
|
|
8
9
|
/**
|
|
9
10
|
* `skrr tasks expectations <task>` — what this task owes, and how far each got.
|
|
@@ -13,18 +14,21 @@ const task_closure_1 = require("../../lib/task-closure");
|
|
|
13
14
|
* could disagree with the gate that refuses the task — and the reader would
|
|
14
15
|
* have no way to tell which was lying.
|
|
15
16
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
/*
|
|
18
|
+
* The labels come from `@skrr-ai/data-provider`, not from here.
|
|
19
|
+
*
|
|
20
|
+
* This file said `dropped` where the user had typed `waived` and `done` where
|
|
21
|
+
* they had typed `met`; the web said `Waived` and `Met`; and an unjudged row
|
|
22
|
+
* read as `not yet` in one column and `needs assessment` in the trailer. Three
|
|
23
|
+
* spellings of one vocabulary is how a user stops trusting that the word they
|
|
24
|
+
* wrote is the word that was stored.
|
|
25
|
+
*
|
|
26
|
+
* A state derived from a DECISION now reads back as that decision's own word.
|
|
27
|
+
* `owed` and `unassessed` stay distinct — "nobody produced it" and "nobody
|
|
28
|
+
* judged it" are different next actions — and the legend below says so, which
|
|
29
|
+
* the near-synonymous English never could on its own.
|
|
30
|
+
*/
|
|
31
|
+
const STATE_LABEL = data_provider_3.EXPECTATION_STATE_LABEL;
|
|
28
32
|
/**
|
|
29
33
|
* The evidence gap, on the rows where a reader can act on it.
|
|
30
34
|
*
|
|
@@ -75,7 +79,9 @@ function evidenceGap(expectation) {
|
|
|
75
79
|
function assessorSuffix(expectation) {
|
|
76
80
|
if (!expectation.assessedByKind)
|
|
77
81
|
return '';
|
|
78
|
-
|
|
82
|
+
// `unmet` joins them: a rejection is a judgement, and whose it was matters at
|
|
83
|
+
// least as much as whose `met` it was.
|
|
84
|
+
if (!['met', 'waived', 'unmet'].includes(expectation.state))
|
|
79
85
|
return '';
|
|
80
86
|
return ` (${(0, task_closure_1.describeAssessor)(expectation.assessedByKind)})`;
|
|
81
87
|
}
|
|
@@ -144,9 +150,13 @@ class TasksExpectations extends base_command_1.BaseCommand {
|
|
|
144
150
|
// "2 of 2 met" for a Task where one expectation was dropped, which is the
|
|
145
151
|
// one thing a reader checking this line needs to know.
|
|
146
152
|
const waived = gating.filter((e) => e.state === 'waived').length;
|
|
147
|
-
const met = gating.filter((e) => e.met).length
|
|
153
|
+
const met = gating.filter((e) => e.passed ?? e.state === 'met').length;
|
|
154
|
+
const rejected = gating.filter((e) => e.state === 'unmet').length;
|
|
148
155
|
this.log('');
|
|
149
|
-
this.log(`${met} of ${gating.length} met
|
|
156
|
+
this.log(`${met} of ${gating.length} met` +
|
|
157
|
+
(waived > 0 ? ` · ${waived} waived` : '') +
|
|
158
|
+
(rejected > 0 ? ` · ${rejected} unmet` : '') +
|
|
159
|
+
` · overall: ${acceptance.state}`);
|
|
150
160
|
// Space defaults are copied into the Task at creation. `source=space`
|
|
151
161
|
// remains useful provenance, but these are Task-owned Expectations now and
|
|
152
162
|
// are assessed exactly like explicitly declared ones.
|
|
@@ -173,6 +183,24 @@ class TasksExpectations extends base_command_1.BaseCommand {
|
|
|
173
183
|
if (settleable > 0) {
|
|
174
184
|
this.log(`${settleable} awaiting assessment — use \`${this.config.bin} tasks expectations assess\`.`);
|
|
175
185
|
}
|
|
186
|
+
/*
|
|
187
|
+
* A rejection is not an absence, and it used to read as one.
|
|
188
|
+
*
|
|
189
|
+
* `unmet` collapsed into `owed`/`unassessed`, so a reviewer's written "no"
|
|
190
|
+
* displayed as "needs assessment" — inviting someone to do the judging that
|
|
191
|
+
* had already been done, while the bulk settle skipped the row because it
|
|
192
|
+
* could see the assessment the display had hidden.
|
|
193
|
+
*/
|
|
194
|
+
const rejectedRows = acceptance.expectations.filter((e) => e.state === 'unmet');
|
|
195
|
+
if (rejectedRows.length > 0) {
|
|
196
|
+
this.log(`${rejectedRows.length} judged unmet — fix the work and re-assess, or waive ` +
|
|
197
|
+
`${rejectedRows.length === 1 ? 'it' : 'them'}:`);
|
|
198
|
+
for (const row of rejectedRows) {
|
|
199
|
+
const who = row.assessedByKind ? ` by ${(0, task_closure_1.describeAssessor)(row.assessedByKind)}` : '';
|
|
200
|
+
const when = row.assessedAt ? ` on ${String(row.assessedAt).slice(0, 10)}` : '';
|
|
201
|
+
this.log(` ${row.id}${who}${when}${row.reason ? `: ${row.reason}` : ''}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
176
204
|
// Named separately from `unjudged`, and with a different command, because
|
|
177
205
|
// the two look alike on the board and are not: one needs a verdict on prose,
|
|
178
206
|
// the other needs somebody to look at a thing that already exists.
|
|
@@ -217,7 +245,40 @@ class TasksExpectations extends base_command_1.BaseCommand {
|
|
|
217
245
|
const settled = gating.filter((e) => e.met).length;
|
|
218
246
|
if (gating.length > settled) {
|
|
219
247
|
this.log('');
|
|
220
|
-
|
|
248
|
+
/*
|
|
249
|
+
* Say what is OWED, not what is forbidden.
|
|
250
|
+
*
|
|
251
|
+
* This read: "The current Result cannot be accepted while these
|
|
252
|
+
* Expectations remain unmet." That gate stopped existing in `56a6ded703`
|
|
253
|
+
* — unmet Expectations are advisory, and `assertTaskMayClose` throws on
|
|
254
|
+
* exactly one code, which is not this one. The Task closes; the unmet
|
|
255
|
+
* items are stamped onto `resolution.closure`.
|
|
256
|
+
*
|
|
257
|
+
* So the sentence asserted an outcome this command never evaluated, and
|
|
258
|
+
* asserted it wrongly: an operator who believed it would go hunting for a
|
|
259
|
+
* way to force a close that needs no forcing. It is the same failure as
|
|
260
|
+
* the hard-coded "it is ready for review" that this batch removed,
|
|
261
|
+
* pointing the other way.
|
|
262
|
+
*/
|
|
263
|
+
this.log(
|
|
264
|
+
// Tense-neutral on purpose: this command reads acceptance only, and
|
|
265
|
+
// fetching the Task as well — one extra round trip on every call — just
|
|
266
|
+
// to choose between "closing is allowed" and "was closed over these" is
|
|
267
|
+
// not worth it. This sentence is true before the close and after it.
|
|
268
|
+
`${gating.length - settled} Expectation(s) still owed. They do not block closing; ` +
|
|
269
|
+
'whatever is outstanding when a Task closes is recorded on `resolution.closure`.');
|
|
270
|
+
}
|
|
271
|
+
/*
|
|
272
|
+
* A legend, because two of these states are near-synonyms in English and
|
|
273
|
+
* genuinely different in what they ask of the reader.
|
|
274
|
+
*/
|
|
275
|
+
const shown = new Set(acceptance.expectations.map((e) => e.state));
|
|
276
|
+
const legend = ['owed', 'unassessed', 'unmet']
|
|
277
|
+
.filter((state) => shown.has(state))
|
|
278
|
+
.map((state) => `${data_provider_3.EXPECTATION_STATE_LABEL[state]} = ${data_provider_3.EXPECTATION_STATE_HINT[state]}`);
|
|
279
|
+
if (legend.length > 1) {
|
|
280
|
+
this.log('');
|
|
281
|
+
this.log(legend.join(' · '));
|
|
221
282
|
}
|
|
222
283
|
}
|
|
223
284
|
}
|
|
@@ -73,8 +73,8 @@ class TasksFollowUp extends base_command_1.BaseCommand {
|
|
|
73
73
|
description: 'Optional SLA fallback delay in seconds for event or poll_until follow-ups',
|
|
74
74
|
}),
|
|
75
75
|
visibility: core_1.Flags.string({
|
|
76
|
-
description: '
|
|
77
|
-
options: ['silent'
|
|
76
|
+
description: 'Always silent — the wake speaks only when the check finds something. Kept for compatibility; notify is not available',
|
|
77
|
+
options: ['silent'],
|
|
78
78
|
}),
|
|
79
79
|
priority: core_1.Flags.string({
|
|
80
80
|
description: 'Follow-up task priority',
|
|
@@ -5,7 +5,13 @@ const data_provider_1 = require("@skrr-ai/data-provider");
|
|
|
5
5
|
const base_command_1 = require("../../../base-command");
|
|
6
6
|
class TasksLifecycleDefinitions extends base_command_1.BaseCommand {
|
|
7
7
|
static description = 'List the lifecycle definitions and built-in packs a Task can attach';
|
|
8
|
-
static args = {
|
|
8
|
+
static args = {
|
|
9
|
+
id: core_1.Args.string({
|
|
10
|
+
required: true,
|
|
11
|
+
ignoreStdin: true,
|
|
12
|
+
description: 'Task ID or identifier — definitions are listed for its Space',
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
9
15
|
static flags = { json: core_1.Flags.boolean({ description: 'Output JSON' }) };
|
|
10
16
|
async run() {
|
|
11
17
|
this.requireAuth();
|
|
@@ -24,12 +30,25 @@ class TasksLifecycleDefinitions extends base_command_1.BaseCommand {
|
|
|
24
30
|
this.log('Built-in packs:');
|
|
25
31
|
for (const pack of response.packs) {
|
|
26
32
|
this.log(` ${pack.id} v${pack.packVersion} — ${pack.title}${pack.description ? ` — ${pack.description}` : ''}`);
|
|
33
|
+
if (pack.stageKeys?.length) {
|
|
34
|
+
this.log(` stages: ${pack.stageKeys.join(' → ')}`);
|
|
35
|
+
}
|
|
27
36
|
}
|
|
28
37
|
}
|
|
29
38
|
if (response.definitions.length) {
|
|
30
39
|
this.log('Space definitions:');
|
|
31
40
|
for (const definition of response.definitions) {
|
|
32
|
-
this.log(` ${definition.id} v${definition.version} — ${definition.title}${definition.packId ? ` (pack ${definition.packId})` : ''}`);
|
|
41
|
+
this.log(` ${definition.id} v${definition.version} — ${definition.title}${definition.packId ? ` (pack ${definition.packId} v${definition.packVersion ?? '?'})` : ''}`);
|
|
42
|
+
// `attach --version` and `migrate --version` take a VERSION id, and
|
|
43
|
+
// this was the only place they could come from — and it printed the
|
|
44
|
+
// definition id and a version NUMBER, neither of which those flags
|
|
45
|
+
// accept. `migrate` was unusable without reading the API.
|
|
46
|
+
if (definition.publishedVersionId) {
|
|
47
|
+
this.log(` published version: ${definition.publishedVersionId} (pass to --version)`);
|
|
48
|
+
}
|
|
49
|
+
if (definition.stageKeys?.length) {
|
|
50
|
+
this.log(` stages: ${definition.stageKeys.join(' → ')}`);
|
|
51
|
+
}
|
|
33
52
|
}
|
|
34
53
|
}
|
|
35
54
|
if (!response.packs.length && !response.definitions.length) {
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { BaseCommand } from '../../../base-command';
|
|
2
2
|
export default class TasksLifecycleDetach extends BaseCommand {
|
|
3
3
|
static description: string;
|
|
4
|
+
/**
|
|
5
|
+
* oclif prints `description` in help and nothing else, so a one-liner that
|
|
6
|
+
* repeats the summary tells a reader nothing about an operation that is not
|
|
7
|
+
* obviously reversible. Say what it does to each thing it touches.
|
|
8
|
+
*/
|
|
9
|
+
static examples: string[];
|
|
4
10
|
static args: {
|
|
5
11
|
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
12
|
};
|
|
7
13
|
static flags: {
|
|
8
14
|
reason: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
|
+
'dry-run': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
16
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
17
|
};
|
|
11
18
|
run(): Promise<void>;
|
|
@@ -5,15 +5,49 @@ const data_provider_1 = require("@skrr-ai/data-provider");
|
|
|
5
5
|
const base_command_1 = require("../../../base-command");
|
|
6
6
|
class TasksLifecycleDetach extends base_command_1.BaseCommand {
|
|
7
7
|
static description = 'Detach the lifecycle from a Task — fences the generation, leaves the run as history';
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* oclif prints `description` in help and nothing else, so a one-liner that
|
|
10
|
+
* repeats the summary tells a reader nothing about an operation that is not
|
|
11
|
+
* obviously reversible. Say what it does to each thing it touches.
|
|
12
|
+
*/
|
|
13
|
+
static examples = [
|
|
14
|
+
'<%= config.bin %> tasks lifecycle detach OSK-1234 --dry-run',
|
|
15
|
+
'<%= config.bin %> tasks lifecycle detach OSK-1234 --reason "Wrong protocol for this work"',
|
|
16
|
+
];
|
|
17
|
+
static args = {
|
|
18
|
+
id: core_1.Args.string({ required: true, ignoreStdin: true, description: 'Task ID or identifier' }),
|
|
19
|
+
};
|
|
9
20
|
static flags = {
|
|
10
|
-
reason: core_1.Flags.string({ description: 'Why the lifecycle is detaching' }),
|
|
21
|
+
reason: core_1.Flags.string({ description: 'Why the lifecycle is detaching; recorded in history' }),
|
|
22
|
+
'dry-run': core_1.Flags.boolean({
|
|
23
|
+
description: 'Describe what detaching would do and exit without writing',
|
|
24
|
+
}),
|
|
11
25
|
json: core_1.Flags.boolean({ description: 'Output JSON' }),
|
|
12
26
|
};
|
|
13
27
|
async run() {
|
|
14
28
|
this.requireAuth();
|
|
15
29
|
const { args, flags } = await this.parse(TasksLifecycleDetach);
|
|
16
30
|
const taskId = await this.resolveTaskReference(args.id);
|
|
31
|
+
if (flags['dry-run']) {
|
|
32
|
+
let current;
|
|
33
|
+
try {
|
|
34
|
+
current = await data_provider_1.dataService.getTaskLifecycle(taskId);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
this.handleApiError(error);
|
|
38
|
+
}
|
|
39
|
+
if (flags.json)
|
|
40
|
+
return this.log(JSON.stringify({ dryRun: true, ...current }, null, 2));
|
|
41
|
+
const lifecycle = current.lifecycle;
|
|
42
|
+
if (!lifecycle)
|
|
43
|
+
return this.log('No lifecycle is attached to this Task — nothing to detach.');
|
|
44
|
+
this.log(`Would detach ${lifecycle.packId ? `pack ${lifecycle.packId}` : `definition ${lifecycle.definitionId}`}` +
|
|
45
|
+
` at generation ${lifecycle.generation}, stage ${lifecycle.currentStageKey ?? '—'}.`);
|
|
46
|
+
this.log(' The run is cancelled and kept as history; nothing already recorded is removed.');
|
|
47
|
+
this.log(' The generation is bumped, so no in-flight writer from the old run can land.');
|
|
48
|
+
this.log(' Re-attaching afterwards starts a NEW generation — it does not resume this one.');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
17
51
|
let response;
|
|
18
52
|
try {
|
|
19
53
|
response = await data_provider_1.dataService.detachTaskLifecycle({
|
|
@@ -26,7 +60,13 @@ class TasksLifecycleDetach extends base_command_1.BaseCommand {
|
|
|
26
60
|
}
|
|
27
61
|
if (flags.json)
|
|
28
62
|
return this.log(JSON.stringify(response, null, 2));
|
|
29
|
-
|
|
63
|
+
const lifecycle = response.lifecycle;
|
|
64
|
+
// Name what changed. "Detached lifecycle from the Task." left the reader to
|
|
65
|
+
// diff the state afterwards to learn what had happened.
|
|
66
|
+
this.log(`Detached${lifecycle?.packId ? ` pack ${lifecycle.packId}` : ''} — the run is cancelled and` +
|
|
67
|
+
` kept as history, and the binding is now generation ${lifecycle?.generation ?? '?'}` +
|
|
68
|
+
` (${lifecycle?.status ?? 'detached'}).`);
|
|
69
|
+
this.log('Re-attach with: skrr tasks lifecycle attach <task-id> --pack <pack>');
|
|
30
70
|
}
|
|
31
71
|
}
|
|
32
72
|
exports.default = TasksLifecycleDetach;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { BaseCommand } from '../../../base-command';
|
|
2
2
|
export default class TasksLifecycleMigrate extends BaseCommand {
|
|
3
3
|
static description: string;
|
|
4
|
+
static examples: string[];
|
|
4
5
|
static args: {
|
|
5
6
|
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
7
|
};
|
|
7
8
|
static flags: {
|
|
8
9
|
version: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
'dry-run': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
11
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
12
|
};
|
|
11
13
|
run(): Promise<void>;
|