@skrr-ai/cli 0.1.10 → 0.1.12
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/dist/base-command.d.ts +1 -13
- package/dist/base-command.js +78 -1
- package/dist/commands/browser/skill/show.js +7 -1
- package/dist/commands/code/index.d.ts +1 -0
- package/dist/commands/code/index.js +9 -1
- package/dist/commands/commitments/analytics/index.js +2 -0
- package/dist/commands/daemon/byok.d.ts +1 -0
- package/dist/commands/daemon/byok.js +2 -1
- package/dist/commands/daemon/install.d.ts +15 -0
- package/dist/commands/daemon/install.js +39 -2
- package/dist/commands/daemon/login.d.ts +31 -0
- package/dist/commands/daemon/login.js +56 -0
- package/dist/commands/daemon/restart.d.ts +9 -0
- package/dist/commands/daemon/restart.js +36 -0
- package/dist/commands/daemon/start.d.ts +1 -0
- package/dist/commands/daemon/start.js +2 -1
- package/dist/commands/daemon/status.d.ts +1 -0
- package/dist/commands/daemon/status.js +2 -1
- package/dist/commands/daemon/stop.d.ts +1 -0
- package/dist/commands/daemon/stop.js +2 -1
- package/dist/commands/daemon/uninstall.d.ts +1 -0
- package/dist/commands/daemon/uninstall.js +2 -1
- package/dist/commands/daemon/unlock.d.ts +9 -0
- package/dist/commands/daemon/unlock.js +33 -0
- package/dist/commands/goals/key-results/create.js +32 -1
- package/dist/commands/goals/key-results/update.d.ts +11 -0
- package/dist/commands/goals/key-results/update.js +80 -2
- package/dist/commands/goals/plan-now.d.ts +54 -2
- package/dist/commands/goals/plan-now.js +175 -18
- package/dist/commands/goals/planner-config.d.ts +60 -9
- package/dist/commands/goals/planner-config.js +82 -34
- package/dist/commands/goals/revisions.js +17 -0
- package/dist/commands/goals/show.d.ts +17 -0
- package/dist/commands/goals/show.js +90 -3
- package/dist/commands/login.js +53 -0
- package/dist/commands/spaces/create.js +2 -1
- package/dist/commands/spaces/index.js +9 -1
- package/dist/commands/spaces/list.d.ts +18 -0
- package/dist/commands/spaces/list.js +57 -7
- package/dist/commands/spaces/show.js +4 -1
- package/dist/commands/spaces/summary.d.ts +4 -0
- package/dist/commands/spaces/summary.js +77 -1
- package/dist/commands/spaces/update.d.ts +4 -0
- package/dist/commands/spaces/update.js +38 -1
- package/dist/commands/tasks/actionability.js +40 -1
- package/dist/commands/tasks/activity.d.ts +29 -0
- package/dist/commands/tasks/activity.js +47 -0
- package/dist/commands/tasks/complete.d.ts +47 -0
- package/dist/commands/tasks/complete.js +159 -12
- package/dist/commands/tasks/create.d.ts +26 -0
- package/dist/commands/tasks/create.js +60 -1
- package/dist/commands/tasks/events/append.d.ts +2 -0
- package/dist/commands/tasks/events/append.js +41 -10
- package/dist/commands/tasks/events/list.js +60 -9
- package/dist/commands/tasks/output.js +22 -2
- package/dist/commands/tasks/ready.d.ts +38 -0
- package/dist/commands/tasks/ready.js +37 -0
- package/dist/commands/tasks/runs.d.ts +22 -0
- package/dist/commands/tasks/runs.js +110 -2
- package/dist/commands/tasks/show.d.ts +28 -0
- package/dist/commands/tasks/show.js +61 -0
- package/dist/commands/tasks/timeline.d.ts +7 -0
- package/dist/commands/tasks/timeline.js +34 -3
- package/dist/commands/tasks/update.d.ts +20 -0
- package/dist/commands/tasks/update.js +38 -1
- package/dist/commands/whoami.d.ts +40 -0
- package/dist/commands/whoami.js +57 -9
- package/dist/commands/wiki/ls.d.ts +23 -0
- package/dist/commands/wiki/ls.js +63 -10
- package/dist/commands/wiki/mv.d.ts +70 -0
- package/dist/commands/wiki/mv.js +198 -4
- package/dist/commands/wiki/rm.js +12 -1
- package/dist/commands/wiki/write.js +35 -3
- package/dist/help.d.ts +27 -0
- package/dist/help.js +48 -0
- package/dist/hooks/command-not-found.d.ts +31 -0
- package/dist/hooks/command-not-found.js +12 -80
- package/dist/lib/command-miss.d.ts +60 -0
- package/dist/lib/command-miss.js +128 -0
- package/dist/lib/commitment-analytics.d.ts +10 -0
- package/dist/lib/commitment-analytics.js +10 -0
- package/dist/lib/config.js +6 -2
- package/dist/lib/daemon-installer.d.ts +62 -0
- package/dist/lib/daemon-installer.js +247 -0
- package/dist/lib/daemon-setup.d.ts +49 -0
- package/dist/lib/daemon-setup.js +103 -0
- package/dist/lib/daemonBroker.d.ts +7 -1
- package/dist/lib/daemonBroker.js +71 -0
- package/dist/lib/daemonHandoff.d.ts +47 -0
- package/dist/lib/daemonHandoff.js +304 -0
- package/dist/lib/exec-oversky.d.ts +30 -0
- package/dist/lib/exec-oversky.js +41 -0
- package/dist/lib/format.d.ts +7 -0
- package/dist/lib/format.js +27 -5
- package/dist/lib/login.js +22 -2
- package/dist/lib/sky-code.js +1 -1
- package/dist/lib/task-transcript.d.ts +9 -0
- package/dist/lib/task-transcript.js +13 -2
- package/dist/lib/tasks.d.ts +34 -0
- package/dist/lib/tasks.js +70 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/configRoot.d.ts +50 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/configRoot.js +67 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +3 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +21 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseKeys.d.ts +87 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseKeys.js +94 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseManifest.d.ts +161 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseManifest.js +235 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/configRoot.d.ts +50 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/configRoot.js +59 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +3 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +10 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseKeys.d.ts +87 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseKeys.js +91 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseManifest.d.ts +161 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseManifest.js +227 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +15 -4
- package/oclif.manifest.json +1831 -1598
- package/package.json +5 -3
|
@@ -4,6 +4,20 @@ 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 format_1 = require("../../lib/format");
|
|
7
|
+
/**
|
|
8
|
+
* The one class of goal change this log does not contain.
|
|
9
|
+
*
|
|
10
|
+
* `PATCH /api/goals/:goalId/planner-config` writes the goal through
|
|
11
|
+
* `Goal.findOneAndUpdate` directly rather than `GoalModel.updateGoal`, which is
|
|
12
|
+
* where `RevisionService` is called from — so arming, re-timing or disarming the
|
|
13
|
+
* auto-planner (and any `maxActiveTasks` change made through that route) leaves
|
|
14
|
+
* no revision. "Who turned the planner on for this goal, and when" is the audit
|
|
15
|
+
* question a shared goal actually raises, and the table answers it with silence.
|
|
16
|
+
*
|
|
17
|
+
* Naming the gap is the honest thing the CLI can do; closing it is a server
|
|
18
|
+
* change. DELETE THIS FOOTER once the planner-config route records a revision.
|
|
19
|
+
*/
|
|
20
|
+
const PLANNER_GAP_NOTE = 'Note: auto-planner changes made with `goals planner-config` are not recorded as revisions.';
|
|
7
21
|
class GoalsRevisions extends base_command_1.BaseCommand {
|
|
8
22
|
static description = 'List revisions for a goal';
|
|
9
23
|
static examples = [
|
|
@@ -33,6 +47,7 @@ class GoalsRevisions extends base_command_1.BaseCommand {
|
|
|
33
47
|
const items = result?.data ?? [];
|
|
34
48
|
if (items.length === 0) {
|
|
35
49
|
this.log('No revisions.');
|
|
50
|
+
this.log(PLANNER_GAP_NOTE);
|
|
36
51
|
return;
|
|
37
52
|
}
|
|
38
53
|
const rows = items.map((rev) => ({
|
|
@@ -49,6 +64,8 @@ class GoalsRevisions extends base_command_1.BaseCommand {
|
|
|
49
64
|
{ key: 'changes', header: 'FIELDS' },
|
|
50
65
|
{ key: 'when', header: 'AT' },
|
|
51
66
|
], (line) => this.log(line));
|
|
67
|
+
this.log('');
|
|
68
|
+
this.log(PLANNER_GAP_NOTE);
|
|
52
69
|
}
|
|
53
70
|
}
|
|
54
71
|
exports.default = GoalsRevisions;
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* The completion row, labelled by what it counts.
|
|
4
|
+
*
|
|
5
|
+
* `null`/absent is rendered `-` rather than `0%`: the server returns null when a
|
|
6
|
+
* goal has NO linked tasks, and printing 0% there says "nothing is done" about a
|
|
7
|
+
* denominator that does not exist.
|
|
8
|
+
*/
|
|
9
|
+
export declare function taskCompletionLine(completionPercent: unknown): string;
|
|
2
10
|
export default class GoalsShow extends BaseCommand {
|
|
3
11
|
static description: string;
|
|
4
12
|
static examples: string[];
|
|
@@ -9,4 +17,13 @@ export default class GoalsShow extends BaseCommand {
|
|
|
9
17
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
18
|
};
|
|
11
19
|
run(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* The goal's key results, best-effort.
|
|
22
|
+
*
|
|
23
|
+
* A second request, and a failure here must not take the goal down with it —
|
|
24
|
+
* `goals show` answering "here is your goal, minus its key results" is far
|
|
25
|
+
* better than answering nothing. `null` distinguishes "could not read" from
|
|
26
|
+
* "there are none", so neither view claims an empty OKR.
|
|
27
|
+
*/
|
|
28
|
+
private readKeyResults;
|
|
12
29
|
}
|
|
@@ -1,9 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.taskCompletionLine = taskCompletionLine;
|
|
3
4
|
const core_1 = require("@oclif/core");
|
|
4
5
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
6
|
const base_command_1 = require("../../base-command");
|
|
7
|
+
const format_1 = require("../../lib/format");
|
|
6
8
|
const web_url_1 = require("../../lib/web-url");
|
|
9
|
+
const planner_config_1 = require("./planner-config");
|
|
10
|
+
/**
|
|
11
|
+
* What `_completionPercent` actually measures.
|
|
12
|
+
*
|
|
13
|
+
* `GoalEnrichment.enrichGoalCompletion` computes it as done ÷ total over the
|
|
14
|
+
* goal's LINKED TASKS. Key-result progress is a parallel axis that never enters
|
|
15
|
+
* it. Rendering that number under the bare label `Progress:` on an OKR goal made
|
|
16
|
+
* it read as "the goal is half achieved" when it meant "1 of the 2 tasks I
|
|
17
|
+
* linked is closed" — dogfooded on a goal whose two key results were at 25% and
|
|
18
|
+
* 40% while the goal reported 0%.
|
|
19
|
+
*
|
|
20
|
+
* So the label names its own denominator, and the key results are printed
|
|
21
|
+
* beside it rather than left to a second command nobody knows to run.
|
|
22
|
+
*/
|
|
23
|
+
const TASK_COMPLETION_NOTE = 'share of linked tasks closed — key results are a separate axis';
|
|
24
|
+
/**
|
|
25
|
+
* The completion row, labelled by what it counts.
|
|
26
|
+
*
|
|
27
|
+
* `null`/absent is rendered `-` rather than `0%`: the server returns null when a
|
|
28
|
+
* goal has NO linked tasks, and printing 0% there says "nothing is done" about a
|
|
29
|
+
* denominator that does not exist.
|
|
30
|
+
*/
|
|
31
|
+
function taskCompletionLine(completionPercent) {
|
|
32
|
+
const value = typeof completionPercent === 'number' ? `${Math.round(completionPercent)}%` : '-';
|
|
33
|
+
return `Tasks done: ${value} (${TASK_COMPLETION_NOTE})`;
|
|
34
|
+
}
|
|
7
35
|
class GoalsShow extends base_command_1.BaseCommand {
|
|
8
36
|
static description = 'Show details for a single goal';
|
|
9
37
|
static examples = [
|
|
@@ -28,11 +56,14 @@ class GoalsShow extends base_command_1.BaseCommand {
|
|
|
28
56
|
}
|
|
29
57
|
const g = goal;
|
|
30
58
|
const url = (0, web_url_1.goalUrlFrom)(this.cliConfig.baseURL, g);
|
|
59
|
+
const planner = (0, planner_config_1.goalPlannerView)(g);
|
|
60
|
+
const keyResults = await this.readKeyResults(args.id);
|
|
31
61
|
if (flags.json) {
|
|
32
|
-
|
|
62
|
+
// Additive: the server's own fields are passed through untouched, with
|
|
63
|
+
// the derived planner reading and the goal's key results beside them.
|
|
64
|
+
this.log(JSON.stringify({ ...g, url, planner, keyResults }, null, 2));
|
|
33
65
|
return;
|
|
34
66
|
}
|
|
35
|
-
const progress = g._completionPercent;
|
|
36
67
|
this.log(`ID: ${g.id ?? g._id ?? '-'}`);
|
|
37
68
|
this.log(`Title: ${g.title ?? '-'}`);
|
|
38
69
|
if (url)
|
|
@@ -47,12 +78,68 @@ class GoalsShow extends base_command_1.BaseCommand {
|
|
|
47
78
|
this.log(`Target date: ${g.targetDate ? String(g.targetDate).slice(0, 10) : '-'}`);
|
|
48
79
|
this.log(`DRI: ${g._driName ?? g.driUserId ?? g.driAgentId ?? '-'}`);
|
|
49
80
|
this.log(`Owner Agent: ${g.ownerAgentId ?? '-'}`);
|
|
50
|
-
this.log(
|
|
81
|
+
this.log(taskCompletionLine(g._completionPercent));
|
|
82
|
+
// The single most consequential piece of state on a goal — a recurring
|
|
83
|
+
// automation that creates tasks, and optionally starts agents on them —
|
|
84
|
+
// was readable only through `--json` field names you had to know in
|
|
85
|
+
// advance, or by WRITING to `planner-config`.
|
|
86
|
+
this.log('');
|
|
87
|
+
this.log('Planner:');
|
|
88
|
+
for (const line of (0, planner_config_1.renderGoalPlannerLines)(planner)) {
|
|
89
|
+
this.log(line);
|
|
90
|
+
}
|
|
91
|
+
this.log('');
|
|
92
|
+
if (keyResults === null) {
|
|
93
|
+
this.log('Key results: (not readable — see the warning above)');
|
|
94
|
+
}
|
|
95
|
+
else if (keyResults.length === 0) {
|
|
96
|
+
this.log('Key results: none');
|
|
97
|
+
this.log(` Add one with \`${this.config.bin} goals key-results create ${args.id} --title ...\``);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
this.log('Key results:');
|
|
101
|
+
(0, format_1.renderTable)(keyResults
|
|
102
|
+
.slice()
|
|
103
|
+
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
|
|
104
|
+
.map((kr) => {
|
|
105
|
+
const value = (0, data_provider_1.resolveKeyResultProgress)(kr);
|
|
106
|
+
return {
|
|
107
|
+
progress: typeof value === 'number' ? `${Math.round(value)}%` : '-',
|
|
108
|
+
status: String(kr.status ?? '-'),
|
|
109
|
+
mode: String(kr.progressMode ?? '-'),
|
|
110
|
+
title: String(kr.title ?? '-'),
|
|
111
|
+
};
|
|
112
|
+
}), [
|
|
113
|
+
{ key: 'progress', header: 'PROGRESS' },
|
|
114
|
+
{ key: 'status', header: 'STATUS' },
|
|
115
|
+
{ key: 'mode', header: 'MODE' },
|
|
116
|
+
{ key: 'title', header: 'TITLE', maxWidth: 48 },
|
|
117
|
+
], (line) => this.log(` ${line}`));
|
|
118
|
+
}
|
|
51
119
|
if (g.description) {
|
|
52
120
|
this.log('');
|
|
53
121
|
this.log('Description:');
|
|
54
122
|
this.log(` ${g.description}`);
|
|
55
123
|
}
|
|
56
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* The goal's key results, best-effort.
|
|
127
|
+
*
|
|
128
|
+
* A second request, and a failure here must not take the goal down with it —
|
|
129
|
+
* `goals show` answering "here is your goal, minus its key results" is far
|
|
130
|
+
* better than answering nothing. `null` distinguishes "could not read" from
|
|
131
|
+
* "there are none", so neither view claims an empty OKR.
|
|
132
|
+
*/
|
|
133
|
+
async readKeyResults(goalId) {
|
|
134
|
+
try {
|
|
135
|
+
const response = await data_provider_1.dataService.listKeyResults(goalId);
|
|
136
|
+
return response?.data ?? [];
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
this.warn(`Could not read this goal's key results: ${err.message}. ` +
|
|
140
|
+
`Try \`${this.config.bin} goals key-results list ${goalId}\`.`);
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
57
144
|
}
|
|
58
145
|
exports.default = GoalsShow;
|
package/dist/commands/login.js
CHANGED
|
@@ -4,7 +4,10 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
5
|
const auth_core_1 = require("@skrr-ai/auth-core");
|
|
6
6
|
const base_command_1 = require("../base-command");
|
|
7
|
+
const daemonHandoff_1 = require("../lib/daemonHandoff");
|
|
8
|
+
const daemon_setup_1 = require("../lib/daemon-setup");
|
|
7
9
|
const config_1 = require("../lib/config");
|
|
10
|
+
const keychain_1 = require("../lib/keychain");
|
|
8
11
|
const login_1 = require("../lib/login");
|
|
9
12
|
const api_fetch_1 = require("../lib/api-fetch");
|
|
10
13
|
const auth_storage_1 = require("../lib/auth-storage");
|
|
@@ -116,6 +119,21 @@ class Login extends base_command_1.BaseCommand {
|
|
|
116
119
|
const previousBaseURL = existing.baseURL;
|
|
117
120
|
const nextBaseURL = (0, config_1.canonicalizeKnownBaseURL)(requestedBaseUrl);
|
|
118
121
|
if (previousBaseURL && previousBaseURL !== nextBaseURL) {
|
|
122
|
+
// Say what is about to be lost. `--base-url` on the default profile
|
|
123
|
+
// silently REPOINTS it, so a session on the other deployment — and
|
|
124
|
+
// every space, task and board reachable through it — becomes
|
|
125
|
+
// unreachable until another interactive login. The `--profile` help
|
|
126
|
+
// advertises profiles as exactly this isolation ("signing into one does
|
|
127
|
+
// not sign you out of another"), so a reader has good reason to expect
|
|
128
|
+
// the flag to be additive rather than a move.
|
|
129
|
+
//
|
|
130
|
+
// A warning rather than a prompt: `cliLogin` completes without a
|
|
131
|
+
// terminal through the daemon broker, which is the case that matters
|
|
132
|
+
// most on a headless box, and a confirmation would break it.
|
|
133
|
+
const profile = (0, keychain_1.getActiveProfile)();
|
|
134
|
+
this.warn(`Pointing profile "${profile}" at ${(0, config_1.describeDeployment)(nextBaseURL)}. ` +
|
|
135
|
+
`Its ${(0, config_1.describeDeployment)(previousBaseURL)} session will be replaced — ` +
|
|
136
|
+
`sign in with \`--profile <name>\` instead to keep both.`);
|
|
119
137
|
undoBaseURL = () => {
|
|
120
138
|
// Re-read rather than reusing `existing`: the flow may have persisted
|
|
121
139
|
// other fields (cliId, migration markers) before failing, and this
|
|
@@ -157,6 +175,41 @@ class Login extends base_command_1.BaseCommand {
|
|
|
157
175
|
this.log('');
|
|
158
176
|
this.log(`Signed in via ${result.flow} on ${(0, config_1.describeDeployment)(this.cliConfig.baseURL)}. ` +
|
|
159
177
|
`Run \`${this.config.bin} whoami\` to confirm your identity.`);
|
|
178
|
+
// One login covers the machine: hand the local daemon a credential of its
|
|
179
|
+
// own. Best-effort by construction — the login above has already SUCCEEDED,
|
|
180
|
+
// and a failure here must not fail it or discard what the user just earned.
|
|
181
|
+
// It degrades to exactly the old behaviour, with a hint naming the command
|
|
182
|
+
// that finishes the job.
|
|
183
|
+
let handoff = await (0, daemonHandoff_1.handOffToLocalDaemon)(process.env, { loginFlow: result.flow });
|
|
184
|
+
// Nothing to hand a credential TO. This is the first moment we can fix
|
|
185
|
+
// that correctly — installing the daemon binds it to a deployment and a
|
|
186
|
+
// profile, and until this login there was no session to take them from.
|
|
187
|
+
// Keyed off `reason` rather than the sentence in `detail`, which is
|
|
188
|
+
// written for a person and expected to be reworded.
|
|
189
|
+
if (handoff.reason === 'no-binary' || handoff.reason === 'no-service') {
|
|
190
|
+
const setup = await (0, daemon_setup_1.offerDaemonSetup)({
|
|
191
|
+
binaryPresent: handoff.reason === 'no-service',
|
|
192
|
+
log: (line) => this.log(line),
|
|
193
|
+
});
|
|
194
|
+
if (setup.status === 'installed') {
|
|
195
|
+
// Re-run rather than duplicating the mint here: it is the same act
|
|
196
|
+
// it always was, and now it has somewhere to land.
|
|
197
|
+
handoff = await (0, daemonHandoff_1.handOffToLocalDaemon)(process.env, { loginFlow: result.flow });
|
|
198
|
+
}
|
|
199
|
+
else if (setup.status === 'failed') {
|
|
200
|
+
this.log(`Could not set up the runtime: ${setup.detail}.`);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
this.log(`This machine is not set up — ${setup.detail}.`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (handoff.status === 'delivered') {
|
|
207
|
+
this.log(`The local daemon was signed in too — ${handoff.detail}.`);
|
|
208
|
+
}
|
|
209
|
+
else if (handoff.status === 'failed') {
|
|
210
|
+
this.log(`The local daemon was not signed in (${handoff.detail}). ` +
|
|
211
|
+
`Run \`${this.config.bin} daemon login\` to finish.`);
|
|
212
|
+
}
|
|
160
213
|
}
|
|
161
214
|
catch (err) {
|
|
162
215
|
// Everything structured in login.ts throws Error; surface the
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_1 = require("@oclif/core");
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
4
5
|
const outbox_1 = require("../../lib/outbox");
|
|
5
6
|
const api_fetch_1 = require("../../lib/api-fetch");
|
|
6
7
|
const base_command_1 = require("../../base-command");
|
|
@@ -24,7 +25,7 @@ class SpacesCreate extends base_command_1.BaseCommand {
|
|
|
24
25
|
}),
|
|
25
26
|
title: core_1.Flags.string({ description: 'Space title (required unless set via --from-json)' }),
|
|
26
27
|
description: core_1.Flags.string({ description: 'Space description' }),
|
|
27
|
-
status: core_1.Flags.string({ description: 'Space status' }),
|
|
28
|
+
status: core_1.Flags.string({ description: 'Space status', options: [...data_provider_1.SPACE_STATUSES] }),
|
|
28
29
|
color: core_1.Flags.string({ description: 'Space color (hex, e.g. #4f46e5)' }),
|
|
29
30
|
'lead-agent': core_1.Flags.string({ description: 'Space Leader agent ID' }),
|
|
30
31
|
'target-date': core_1.Flags.string({ description: 'Target date (ISO string)' }),
|
|
@@ -117,7 +117,15 @@ class Spaces extends base_command_1.BaseCommand {
|
|
|
117
117
|
this.log(JSON.stringify({ object: 'space_guide', data: PERSPECTIVES }, null, 2));
|
|
118
118
|
return;
|
|
119
119
|
}
|
|
120
|
-
this
|
|
120
|
+
// Every perspective below needs a `<space-id>`, and this screen used to
|
|
121
|
+
// start at the first of them — so `skrr spaces`, the natural way to orient
|
|
122
|
+
// yourself in the domain, printed ~14 commands a reader had no way to run
|
|
123
|
+
// and never named the two that unblock them. The entry point to a domain
|
|
124
|
+
// has to answer "how do I get one of these" before "what can I do with
|
|
125
|
+
// one".
|
|
126
|
+
this.log('Get a space id: skrr spaces list Make one: skrr spaces create --title "..."');
|
|
127
|
+
this.log('');
|
|
128
|
+
this.log('Space perspectives (each takes a <space-id>):');
|
|
121
129
|
this.log('');
|
|
122
130
|
for (const item of PERSPECTIVES) {
|
|
123
131
|
this.log(`${item.perspective.padEnd(12)} ${item.purpose}`);
|
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* What an empty `--q` result is allowed to claim.
|
|
4
|
+
*
|
|
5
|
+
* `No spaces matching "space scratch".` is a confident assertion that nothing
|
|
6
|
+
* matched, and for the sense a person means it is FALSE: both words are in the
|
|
7
|
+
* title of a space they own and created a minute earlier. The server ORs one
|
|
8
|
+
* escaped regex built from the whole query across title/description/identifier,
|
|
9
|
+
* so the query is a substring and word order is load-bearing — a fact the
|
|
10
|
+
* message hid, leaving "it does not exist" as the only available reading.
|
|
11
|
+
*
|
|
12
|
+
* So the message reports what was actually run and hands back the two moves
|
|
13
|
+
* that recover: shorten to one distinctive word, or list everything. It says
|
|
14
|
+
* "matched" (what this query did) rather than "matching" (what exists).
|
|
15
|
+
*
|
|
16
|
+
* Pure and exported so the wording is testable without an oclif runtime — the
|
|
17
|
+
* convention `describeResourceNotFound` follows in `base-command`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function noSpacesMatchedMessage(q: string, bin?: string): string;
|
|
2
20
|
export default class SpacesList extends BaseCommand {
|
|
3
21
|
static description: string;
|
|
4
22
|
static examples: string[];
|
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noSpacesMatchedMessage = noSpacesMatchedMessage;
|
|
3
4
|
const core_1 = require("@oclif/core");
|
|
4
5
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
6
|
const base_command_1 = require("../../base-command");
|
|
6
7
|
const web_url_1 = require("../../lib/web-url");
|
|
7
8
|
const format_1 = require("../../lib/format");
|
|
9
|
+
/**
|
|
10
|
+
* What an empty `--q` result is allowed to claim.
|
|
11
|
+
*
|
|
12
|
+
* `No spaces matching "space scratch".` is a confident assertion that nothing
|
|
13
|
+
* matched, and for the sense a person means it is FALSE: both words are in the
|
|
14
|
+
* title of a space they own and created a minute earlier. The server ORs one
|
|
15
|
+
* escaped regex built from the whole query across title/description/identifier,
|
|
16
|
+
* so the query is a substring and word order is load-bearing — a fact the
|
|
17
|
+
* message hid, leaving "it does not exist" as the only available reading.
|
|
18
|
+
*
|
|
19
|
+
* So the message reports what was actually run and hands back the two moves
|
|
20
|
+
* that recover: shorten to one distinctive word, or list everything. It says
|
|
21
|
+
* "matched" (what this query did) rather than "matching" (what exists).
|
|
22
|
+
*
|
|
23
|
+
* Pure and exported so the wording is testable without an oclif runtime — the
|
|
24
|
+
* convention `describeResourceNotFound` follows in `base-command`.
|
|
25
|
+
*/
|
|
26
|
+
function noSpacesMatchedMessage(q, bin = 'skrr') {
|
|
27
|
+
return (`No spaces matched ${JSON.stringify(q)}.\n` +
|
|
28
|
+
' → `--q` is a case-insensitive SUBSTRING match over title, description and identifier, ' +
|
|
29
|
+
'not a tokenized search, so word order matters.\n' +
|
|
30
|
+
` → retry with one distinctive word, or list them all with \`${bin} spaces list\`.`);
|
|
31
|
+
}
|
|
8
32
|
class SpacesList extends base_command_1.BaseCommand {
|
|
9
33
|
static description = 'List spaces';
|
|
10
34
|
static examples = [
|
|
@@ -17,14 +41,32 @@ class SpacesList extends base_command_1.BaseCommand {
|
|
|
17
41
|
];
|
|
18
42
|
static flags = {
|
|
19
43
|
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
20
|
-
|
|
44
|
+
// Enumerated so a typo is refused by the flag parser naming the choices.
|
|
45
|
+
// Undeclared, `--status bogus_status` exited 0 with zero results — which is
|
|
46
|
+
// exactly what a real status with no spaces looks like, so a typo and an
|
|
47
|
+
// empty set were indistinguishable and nothing named a valid value.
|
|
48
|
+
status: core_1.Flags.string({
|
|
49
|
+
description: 'Filter by space status',
|
|
50
|
+
options: [...data_provider_1.SPACE_STATUSES],
|
|
51
|
+
}),
|
|
21
52
|
label: core_1.Flags.string({
|
|
22
53
|
description: 'Filter by label ID (repeatable). Multiple labels AND together — a space must carry all of them.',
|
|
23
54
|
multiple: true,
|
|
24
55
|
}),
|
|
25
56
|
'include-archived': core_1.Flags.boolean({ description: 'Include archived spaces' }),
|
|
26
57
|
q: core_1.Flags.string({
|
|
27
|
-
|
|
58
|
+
// NOT "free-text search". The server ORs ONE escaped regex built from the
|
|
59
|
+
// whole string across title/description/identifier
|
|
60
|
+
// (`api/models/Space.js` → `listSpaces`), so `--q` is a `LIKE %q%`.
|
|
61
|
+
// "Free-text search" made a promise the floor does not keep: it reads as
|
|
62
|
+
// "type the words you remember, in any order", and `--q "space scratch"`
|
|
63
|
+
// then answers "no spaces" for a space titled "… scratch space".
|
|
64
|
+
//
|
|
65
|
+
// A Meilisearch-backed deployment narrows through the index instead and
|
|
66
|
+
// IS tokenized and typo-tolerant — but the CLI cannot tell which path ran,
|
|
67
|
+
// so the help describes the guaranteed floor rather than the better case.
|
|
68
|
+
description: 'Match title, description, or identifier (case-insensitive). Substring, not tokenized — ' +
|
|
69
|
+
'word order matters, so prefer one distinctive word.',
|
|
28
70
|
}),
|
|
29
71
|
limit: core_1.Flags.integer({ description: 'Max results', default: 50 }),
|
|
30
72
|
cursor: core_1.Flags.string({ description: 'Pagination cursor from a previous page' }),
|
|
@@ -53,12 +95,12 @@ class SpacesList extends base_command_1.BaseCommand {
|
|
|
53
95
|
const r = response;
|
|
54
96
|
const items = r?.data ?? [];
|
|
55
97
|
if (items.length === 0) {
|
|
56
|
-
this.log(flags.q ?
|
|
98
|
+
this.log(flags.q ? noSpacesMatchedMessage(flags.q, this.config.bin) : 'No spaces.');
|
|
57
99
|
return;
|
|
58
100
|
}
|
|
59
101
|
const rows = items.map((s) => ({
|
|
60
102
|
id: String(s.id ?? s._id ?? '-'),
|
|
61
|
-
|
|
103
|
+
identifier: String(s.identifier ?? '-'),
|
|
62
104
|
title: String(s.title ?? '-'),
|
|
63
105
|
status: String(s.status ?? '-'),
|
|
64
106
|
goals: String(Array.isArray(s.goalIds) ? s.goalIds.length : 0),
|
|
@@ -66,9 +108,17 @@ class SpacesList extends base_command_1.BaseCommand {
|
|
|
66
108
|
lead: String(s.leadAgentId ?? '-'),
|
|
67
109
|
}));
|
|
68
110
|
(0, format_1.renderTable)(rows, [
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
|
|
111
|
+
// Leads for the same reason it does on tasks: it is the short thing a
|
|
112
|
+
// person types back into another command.
|
|
113
|
+
//
|
|
114
|
+
// Headed `IDENTIFIER`, not `REF`, because `identifier` is the ONLY name
|
|
115
|
+
// this value has anywhere else — it is the wire field, the storage
|
|
116
|
+
// field, and what `@skrr-ai/data-provider` calls it. The column used to
|
|
117
|
+
// say `REF` and the detail view `Ref:`, so all three human surfaces
|
|
118
|
+
// named a key that does not exist in `--json`; a script author read
|
|
119
|
+
// `ref` off every row, got nothing, and concluded `--json` was dropping
|
|
120
|
+
// a field the table plainly had. (OSK S4 dogfood finding)
|
|
121
|
+
{ key: 'identifier', header: 'IDENTIFIER' },
|
|
72
122
|
{ key: 'title', header: 'TITLE', maxWidth: 50 },
|
|
73
123
|
{ key: 'status', header: 'STATUS' },
|
|
74
124
|
{ key: 'goals', header: 'GOALS' },
|
|
@@ -43,7 +43,10 @@ class SpacesShow extends base_command_1.BaseCommand {
|
|
|
43
43
|
}
|
|
44
44
|
const ancestors = Array.isArray(s.ancestorSpaceIds) ? s.ancestorSpaceIds : [];
|
|
45
45
|
this.log(`ID: ${s.id ?? s._id ?? '-'}`);
|
|
46
|
-
|
|
46
|
+
// `Identifier`, not `Ref` — the label has to be the key a reader can then
|
|
47
|
+
// find in `--json`, where this value is `identifier` and no `ref` exists.
|
|
48
|
+
// Matches the `IDENTIFIER` column in `spaces list`.
|
|
49
|
+
this.log(`Identifier: ${s.identifier ?? '-'}`);
|
|
47
50
|
this.log(`Title: ${s.title ?? '-'}`);
|
|
48
51
|
if (url)
|
|
49
52
|
this.log(`URL: ${url}`);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/** Exported for `summary.spec.ts`. `0` is a fact worth printing here — "blocked 0" is the answer, not a blank. */
|
|
3
|
+
export declare function num(value: unknown): string;
|
|
4
|
+
/** `label n · label n`, dropping pairs the payload did not carry at all. */
|
|
5
|
+
export declare function pairs(source: Record<string, unknown> | undefined, keys: [string, string][]): string;
|
|
2
6
|
export default class SpacesSummary extends BaseCommand {
|
|
3
7
|
static description: string;
|
|
4
8
|
static args: {
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.num = num;
|
|
4
|
+
exports.pairs = pairs;
|
|
3
5
|
const core_1 = require("@oclif/core");
|
|
4
6
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
7
|
const base_command_1 = require("../../base-command");
|
|
6
8
|
const space_output_1 = require("../../lib/space-output");
|
|
9
|
+
/** Exported for `summary.spec.ts`. `0` is a fact worth printing here — "blocked 0" is the answer, not a blank. */
|
|
10
|
+
function num(value) {
|
|
11
|
+
return typeof value === 'number' ? String(value) : '-';
|
|
12
|
+
}
|
|
13
|
+
/** `label n · label n`, dropping pairs the payload did not carry at all. */
|
|
14
|
+
function pairs(source, keys) {
|
|
15
|
+
return keys
|
|
16
|
+
.filter(([key]) => source?.[key] !== undefined)
|
|
17
|
+
.map(([key, label]) => `${label} ${num(source?.[key])}`)
|
|
18
|
+
.join(' · ');
|
|
19
|
+
}
|
|
7
20
|
class SpacesSummary extends base_command_1.BaseCommand {
|
|
8
21
|
static description = 'Show the aggregated summary for a space';
|
|
9
22
|
static args = {
|
|
@@ -23,7 +36,70 @@ class SpacesSummary extends base_command_1.BaseCommand {
|
|
|
23
36
|
const output = result && typeof result === 'object'
|
|
24
37
|
? (0, space_output_1.withSpacePerspectiveUrl)(this.cliConfig.baseURL, args.id, 'dashboard', result)
|
|
25
38
|
: result;
|
|
26
|
-
|
|
39
|
+
// This printed the raw payload regardless of `--json`, which differed only
|
|
40
|
+
// by its own `generatedAt` timestamp — so the flag advertised a mode the
|
|
41
|
+
// command did not have, and the FIRST perspective `skrr spaces` points a
|
|
42
|
+
// human at (the dashboard row) was a wall of JSON.
|
|
43
|
+
if (flags.json) {
|
|
44
|
+
this.log(JSON.stringify(output, null, 2));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const o = (output ?? {});
|
|
48
|
+
const tasks = o.tasks;
|
|
49
|
+
const byPriority = tasks?.openByPriority;
|
|
50
|
+
this.log(`Space: ${o.spaceId ?? args.id}`);
|
|
51
|
+
if (o.url)
|
|
52
|
+
this.log(`Web: ${o.url}`);
|
|
53
|
+
this.log('');
|
|
54
|
+
if (tasks) {
|
|
55
|
+
const pct = typeof tasks.percentage === 'number' ? ` (${tasks.percentage}%)` : '';
|
|
56
|
+
this.log(`Tasks ${num(tasks.total)} total · ${num(tasks.done)} done${pct} · ${num(tasks.open)} open`);
|
|
57
|
+
const flow = pairs(tasks, [
|
|
58
|
+
['inProgress', 'in progress'],
|
|
59
|
+
['running', 'running'],
|
|
60
|
+
['blocked', 'blocked'],
|
|
61
|
+
['failed', 'failed'],
|
|
62
|
+
]);
|
|
63
|
+
if (flow)
|
|
64
|
+
this.log(` ${flow}`);
|
|
65
|
+
const timing = pairs(tasks, [
|
|
66
|
+
['dueToday', 'due today'],
|
|
67
|
+
['dueSoon', 'due soon'],
|
|
68
|
+
['stale', 'stale'],
|
|
69
|
+
['unassigned', 'unassigned'],
|
|
70
|
+
]);
|
|
71
|
+
if (timing)
|
|
72
|
+
this.log(` ${timing}`);
|
|
73
|
+
const priority = pairs(byPriority, [
|
|
74
|
+
['critical', 'critical'],
|
|
75
|
+
['high', 'high'],
|
|
76
|
+
['medium', 'medium'],
|
|
77
|
+
['low', 'low'],
|
|
78
|
+
]);
|
|
79
|
+
if (priority)
|
|
80
|
+
this.log(` open by priority: ${priority}`);
|
|
81
|
+
}
|
|
82
|
+
for (const [key, label] of [
|
|
83
|
+
['goals', 'Goals '],
|
|
84
|
+
['conversations', 'Chats '],
|
|
85
|
+
]) {
|
|
86
|
+
const section = o[key];
|
|
87
|
+
if (section?.total !== undefined)
|
|
88
|
+
this.log(`${label} ${num(section.total)}`);
|
|
89
|
+
}
|
|
90
|
+
// Agents carries `activeIds`, not a total — counting the array is the
|
|
91
|
+
// answer to "how many agents are working here", and an empty one is a real
|
|
92
|
+
// answer rather than a missing section.
|
|
93
|
+
const agents = o.agents;
|
|
94
|
+
if (Array.isArray(agents?.activeIds))
|
|
95
|
+
this.log(`Agents ${agents.activeIds.length} active`);
|
|
96
|
+
// The per-status breakdown is in the payload and deliberately not a
|
|
97
|
+
// section: it restates `byStatus` counts the lines above already summarise,
|
|
98
|
+
// and nine near-zero rows would bury the four numbers that matter.
|
|
99
|
+
this.log('');
|
|
100
|
+
this.log('Full breakdown (byStatus, per-section detail): --json');
|
|
101
|
+
if (o.next_command)
|
|
102
|
+
this.log(`Next: ${o.next_command}`);
|
|
27
103
|
}
|
|
28
104
|
}
|
|
29
105
|
exports.default = SpacesSummary;
|
|
@@ -16,6 +16,10 @@ export default class SpacesUpdate extends BaseCommand {
|
|
|
16
16
|
'target-date': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
17
17
|
goal: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
18
18
|
'inherit-access': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
19
|
+
'estimate-scale': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
20
|
+
'estimate-migration': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
21
|
+
'default-estimate': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
22
|
+
'use-default-estimate': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
19
23
|
'worktree-isolation': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
20
24
|
};
|
|
21
25
|
run(): Promise<void>;
|
|
@@ -13,6 +13,7 @@ class SpacesUpdate extends base_command_1.BaseCommand {
|
|
|
13
13
|
'<%= config.bin %> spaces update <space-id> --lead-agent <agent-id>',
|
|
14
14
|
'<%= config.bin %> spaces update <space-id> --no-inherit-access',
|
|
15
15
|
'<%= config.bin %> spaces update <space-id> --from-json patch.json',
|
|
16
|
+
'<%= config.bin %> spaces update <space-id> --estimate-scale fibonacci --estimate-migration migrate',
|
|
16
17
|
];
|
|
17
18
|
static args = {
|
|
18
19
|
id: core_1.Args.string({ description: 'Space ID', required: true, ignoreStdin: true }),
|
|
@@ -24,7 +25,7 @@ class SpacesUpdate extends base_command_1.BaseCommand {
|
|
|
24
25
|
}),
|
|
25
26
|
title: core_1.Flags.string({ description: 'New title' }),
|
|
26
27
|
description: core_1.Flags.string({ description: 'New description' }),
|
|
27
|
-
status: core_1.Flags.string({ description: 'New status' }),
|
|
28
|
+
status: core_1.Flags.string({ description: 'New status', options: [...data_provider_1.SPACE_STATUSES] }),
|
|
28
29
|
color: core_1.Flags.string({ description: 'New color (hex)' }),
|
|
29
30
|
'lead-agent': core_1.Flags.string({ description: 'New Space Leader agent ID' }),
|
|
30
31
|
'target-date': core_1.Flags.string({ description: 'New target date (ISO string)' }),
|
|
@@ -37,6 +38,38 @@ class SpacesUpdate extends base_command_1.BaseCommand {
|
|
|
37
38
|
allowNo: true,
|
|
38
39
|
description: 'Whether this subspace inherits members/permissions from its parent chain. Use --no-inherit-access to make it private. (To change the parent itself, use `spaces move`.)',
|
|
39
40
|
}),
|
|
41
|
+
// `tasks create --estimate` resolves its scale up the ancestor chain, and
|
|
42
|
+
// with nothing set it resolves to `none`, which admits no value — so the
|
|
43
|
+
// estimate is dropped. That was reachable only from the web UI, which made
|
|
44
|
+
// the CLI's own estimate flag conditionally inert with no way to satisfy
|
|
45
|
+
// its prerequisite. `PATCH /api/spaces/:id` has accepted these three fields
|
|
46
|
+
// all along.
|
|
47
|
+
'estimate-scale': core_1.Flags.string({
|
|
48
|
+
description: 'Estimate scale for this space and its descendants. Allowed values per scale: ' +
|
|
49
|
+
Object.entries(data_provider_1.ESTIMATE_SCALE_VALUES)
|
|
50
|
+
.map(([scale, values]) => `${scale} ${values.length ? values.join('/') : '(none)'}`)
|
|
51
|
+
.join(', '),
|
|
52
|
+
options: [...data_provider_1.ESTIMATE_SCALES],
|
|
53
|
+
}),
|
|
54
|
+
// The server makes this MANDATORY on a scale change (deliberately — so no
|
|
55
|
+
// destructive interpretation is inferred) and answers a missing one with
|
|
56
|
+
// ESTIMATE_SCALE_MIGRATION_REQUIRED. Surfacing it as its own flag is the
|
|
57
|
+
// difference between a documented choice and a 400 the caller has to
|
|
58
|
+
// reverse-engineer.
|
|
59
|
+
'estimate-migration': core_1.Flags.string({
|
|
60
|
+
description: 'What happens to estimates that already exist when the scale changes. Required whenever ' +
|
|
61
|
+
'--estimate-scale changes the EFFECTIVE scale. `migrate` maps each estimate onto the ' +
|
|
62
|
+
'nearest value in the new scale; `clear` DELETES every estimate in this space and its ' +
|
|
63
|
+
'descendants. Changing to `none` accepts only `clear`, since there is nothing to map into.',
|
|
64
|
+
options: [...data_provider_1.ESTIMATE_SCALE_MIGRATION_MODES],
|
|
65
|
+
}),
|
|
66
|
+
'default-estimate': core_1.Flags.integer({
|
|
67
|
+
description: 'Estimate applied to unestimated tasks when --use-default-estimate is on. Must be a value the scale allows',
|
|
68
|
+
}),
|
|
69
|
+
'use-default-estimate': core_1.Flags.boolean({
|
|
70
|
+
allowNo: true,
|
|
71
|
+
description: 'Whether rollups treat an unestimated task as --default-estimate',
|
|
72
|
+
}),
|
|
40
73
|
'worktree-isolation': core_1.Flags.string({
|
|
41
74
|
options: ['shared', 'ephemeral'],
|
|
42
75
|
description: "Space default for how this space's autonomous task runs use git worktrees. " +
|
|
@@ -63,6 +96,10 @@ class SpacesUpdate extends base_command_1.BaseCommand {
|
|
|
63
96
|
leadAgentId: flags['lead-agent'],
|
|
64
97
|
targetDate: flags['target-date'],
|
|
65
98
|
inheritAccess: flags['inherit-access'],
|
|
99
|
+
estimateScale: flags['estimate-scale'],
|
|
100
|
+
estimateScaleMigration: flags['estimate-migration'],
|
|
101
|
+
defaultEstimate: flags['default-estimate'],
|
|
102
|
+
useDefaultEstimateForUnestimated: flags['use-default-estimate'],
|
|
66
103
|
});
|
|
67
104
|
// Worktree-isolation default lives on the space's deliveryPolicy subdoc,
|
|
68
105
|
// which the API replaces wholesale on update. Merge into the policy the user
|