@skrr-ai/cli 0.1.14 → 0.1.15
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 +14 -0
- package/dist/base-command.js +60 -1
- package/dist/commands/agents/create.d.ts +1 -0
- package/dist/commands/agents/create.js +6 -1
- package/dist/commands/agents/instructions/add.d.ts +21 -0
- package/dist/commands/agents/instructions/add.js +115 -0
- package/dist/commands/agents/instructions/disable.d.ts +13 -0
- package/dist/commands/agents/instructions/disable.js +50 -0
- package/dist/commands/agents/instructions/edit.d.ts +16 -0
- package/dist/commands/agents/instructions/edit.js +79 -0
- package/dist/commands/agents/instructions/enable.d.ts +13 -0
- package/dist/commands/agents/instructions/enable.js +50 -0
- package/dist/commands/agents/instructions/move.d.ts +15 -0
- package/dist/commands/agents/instructions/move.js +61 -0
- package/dist/commands/agents/instructions/preview.d.ts +13 -0
- package/dist/commands/agents/instructions/preview.js +59 -0
- package/dist/commands/agents/instructions/remove.d.ts +13 -0
- package/dist/commands/agents/instructions/remove.js +49 -0
- package/dist/commands/agents/instructions/set.d.ts +17 -0
- package/dist/commands/agents/instructions/set.js +79 -0
- package/dist/commands/agents/instructions/show.d.ts +15 -0
- package/dist/commands/agents/instructions/show.js +76 -0
- package/dist/commands/agents/instructions/upgrade.d.ts +16 -0
- package/dist/commands/agents/instructions/upgrade.js +77 -0
- package/dist/commands/agents/show.js +5 -0
- package/dist/commands/agents/triggers/policy.js +8 -2
- package/dist/commands/agents/update.d.ts +1 -0
- package/dist/commands/agents/update.js +8 -2
- package/dist/commands/daemon/index.js +2 -0
- package/dist/commands/daemon/recover-offline-queue.d.ts +9 -0
- package/dist/commands/daemon/recover-offline-queue.js +24 -0
- package/dist/commands/daemon/start.js +29 -1
- package/dist/commands/instruction-blocks/create.d.ts +19 -0
- package/dist/commands/instruction-blocks/create.js +94 -0
- package/dist/commands/instruction-blocks/delete.d.ts +13 -0
- package/dist/commands/instruction-blocks/delete.js +35 -0
- package/dist/commands/instruction-blocks/list.d.ts +13 -0
- package/dist/commands/instruction-blocks/list.js +71 -0
- package/dist/commands/instruction-blocks/show.d.ts +12 -0
- package/dist/commands/instruction-blocks/show.js +35 -0
- package/dist/commands/instruction-blocks/update.d.ts +23 -0
- package/dist/commands/instruction-blocks/update.js +94 -0
- package/dist/commands/spaces/auto-executor/preview.d.ts +1 -0
- package/dist/commands/spaces/auto-executor/preview.js +62 -3
- package/dist/commands/spaces/auto-executor/update.d.ts +4 -0
- package/dist/commands/spaces/auto-executor/update.js +78 -7
- package/dist/commands/spaces/capacity.js +14 -2
- package/dist/commands/spaces/update.d.ts +4 -0
- package/dist/commands/spaces/update.js +49 -2
- package/dist/commands/tasks/complete.d.ts +12 -5
- package/dist/commands/tasks/complete.js +50 -28
- package/dist/commands/tasks/continue-ready.js +9 -1
- package/dist/commands/tasks/create.d.ts +1 -0
- package/dist/commands/tasks/create.js +25 -1
- package/dist/commands/tasks/deliverable/add.d.ts +23 -0
- package/dist/commands/tasks/deliverable/add.js +70 -0
- package/dist/commands/tasks/events/append.d.ts +1 -1
- package/dist/commands/tasks/events/append.js +4 -4
- package/dist/commands/tasks/expectations.d.ts +12 -0
- package/dist/commands/tasks/expectations.js +92 -0
- package/dist/commands/tasks/judge.d.ts +29 -0
- package/dist/commands/tasks/judge.js +68 -0
- package/dist/commands/tasks/report.d.ts +1 -1
- package/dist/commands/tasks/report.js +3 -3
- package/dist/commands/tasks/update.d.ts +1 -0
- package/dist/commands/tasks/update.js +34 -4
- package/dist/commands/tasks/waive.d.ts +25 -0
- package/dist/commands/tasks/waive.js +58 -0
- package/dist/lib/agent-config.js +5 -0
- package/dist/lib/agent-instructions.d.ts +15 -0
- package/dist/lib/agent-instructions.js +55 -0
- package/dist/lib/command-hygiene.d.ts +6 -1
- package/dist/lib/command-hygiene.js +49 -29
- package/dist/lib/exec-runtime-binary.d.ts +13 -0
- package/dist/lib/exec-runtime-binary.js +45 -0
- package/dist/lib/git-task.d.ts +37 -0
- package/dist/lib/git-task.js +151 -0
- package/dist/lib/instruction-blocks.d.ts +29 -0
- package/dist/lib/instruction-blocks.js +159 -0
- package/dist/lib/runtime-start-verification.d.ts +49 -0
- package/dist/lib/runtime-start-verification.js +108 -0
- package/dist/lib/task-execution.d.ts +18 -2
- package/dist/lib/task-execution.js +19 -14
- package/dist/lib/task-transcript.js +1 -1
- package/dist/lib/tasks.d.ts +50 -0
- package/dist/lib/tasks.js +66 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +2027 -1904
- package/dist/node_modules/@skrr-ai/inference-broker/package.json +1 -1
- package/oclif.manifest.json +13706 -11858
- package/package.json +4 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const base_command_1 = require("../../base-command");
|
|
6
|
+
/**
|
|
7
|
+
* `skrr tasks expectations <task>` — what this task owes, and how far each got.
|
|
8
|
+
*
|
|
9
|
+
* Reads the SERVER's verdict rather than recomputing one. The judge and the
|
|
10
|
+
* completion gate are the same module, so a CLI that decided "met" for itself
|
|
11
|
+
* could disagree with the gate that refuses the task — and the reader would
|
|
12
|
+
* have no way to tell which was lying.
|
|
13
|
+
*/
|
|
14
|
+
const STATE_LABEL = {
|
|
15
|
+
met: 'done',
|
|
16
|
+
waived: 'dropped',
|
|
17
|
+
blocked: 'stuck',
|
|
18
|
+
underway: 'on its way',
|
|
19
|
+
owed: 'not yet',
|
|
20
|
+
unjudged: 'needs a person',
|
|
21
|
+
};
|
|
22
|
+
function line(expectation) {
|
|
23
|
+
const label = STATE_LABEL[expectation.state] || expectation.state;
|
|
24
|
+
// A judged expectation is stated as NOT a gate. A list that reads like a
|
|
25
|
+
// contract while enforcing nothing is worse than no list, because it is
|
|
26
|
+
// trusted.
|
|
27
|
+
const gate = expectation.gating ? '' : ' (does not block completion)';
|
|
28
|
+
const from = expectation.source === 'space' ? ' [space default]' : '';
|
|
29
|
+
return ` ${expectation.id.padEnd(18)} ${label.padEnd(14)} ${expectation.sentence}${gate}${from}`;
|
|
30
|
+
}
|
|
31
|
+
class TasksExpectations extends base_command_1.BaseCommand {
|
|
32
|
+
static description = 'Show what a task must produce and how far each expectation got ("Done when")';
|
|
33
|
+
static examples = [
|
|
34
|
+
'<%= config.bin %> tasks expectations <task-id>',
|
|
35
|
+
'<%= config.bin %> tasks expectations <task-id> --json',
|
|
36
|
+
];
|
|
37
|
+
static args = {
|
|
38
|
+
task: core_1.Args.string({ description: 'Task id or ref', required: true, ignoreStdin: true }),
|
|
39
|
+
};
|
|
40
|
+
static flags = {
|
|
41
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
42
|
+
};
|
|
43
|
+
async run() {
|
|
44
|
+
this.requireAuth();
|
|
45
|
+
const { args, flags } = await this.parse(TasksExpectations);
|
|
46
|
+
let acceptance;
|
|
47
|
+
try {
|
|
48
|
+
acceptance = await data_provider_1.dataService.getTaskAcceptance(args.task);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
this.handleApiError(err);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (flags.json) {
|
|
55
|
+
this.log(JSON.stringify(acceptance, null, 2));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (acceptance.expectations.length === 0) {
|
|
59
|
+
// An empty contract is a real answer, not an unfilled field. Say so
|
|
60
|
+
// plainly rather than prompting for something most tasks do not owe.
|
|
61
|
+
this.log('Nothing durable is expected of this task.');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// The evidence read fails OPEN by design, so "produced nothing" and "we
|
|
65
|
+
// could not look" are indistinguishable downstream unless the server says
|
|
66
|
+
// which happened. Never print the first when it might be the second.
|
|
67
|
+
if (!acceptance.evidenceRead) {
|
|
68
|
+
this.warn('Could not read what this task produced — the states below may understate it. ' +
|
|
69
|
+
'This is not a claim that nothing was produced.');
|
|
70
|
+
}
|
|
71
|
+
this.log('Done when:');
|
|
72
|
+
for (const expectation of acceptance.expectations) {
|
|
73
|
+
this.log(line(expectation));
|
|
74
|
+
}
|
|
75
|
+
const gating = acceptance.expectations.filter((e) => e.gating);
|
|
76
|
+
const met = gating.filter((e) => e.met).length;
|
|
77
|
+
this.log('');
|
|
78
|
+
this.log(`${met} of ${gating.length} met · overall: ${acceptance.state}`);
|
|
79
|
+
// Only the ones actually stored on the task can be judged. A space default
|
|
80
|
+
// is synthesised per read, so pointing at `tasks judge` for one would send
|
|
81
|
+
// the reader at a command that can only refuse it.
|
|
82
|
+
const settleable = acceptance.expectations.filter((e) => e.state === 'unjudged' && e.source !== 'space').length;
|
|
83
|
+
if (settleable > 0) {
|
|
84
|
+
this.log(`${settleable} awaiting a person — settle with \`${this.config.bin} tasks judge\`.`);
|
|
85
|
+
}
|
|
86
|
+
const inherited = acceptance.unjudged - settleable;
|
|
87
|
+
if (inherited > 0) {
|
|
88
|
+
this.log(`${inherited} inherited from the space default — declare them on the task to settle them.`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.default = TasksExpectations;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr tasks judge <task> <expectation-id> --verdict met --reason "<why>"`
|
|
4
|
+
*
|
|
5
|
+
* Settle a `judged` expectation — one whose text nothing here can evaluate.
|
|
6
|
+
*
|
|
7
|
+
* Only a judged one. A checkable expectation is settled by recording what was
|
|
8
|
+
* produced, and letting a person mark it met by hand would make the deliverables
|
|
9
|
+
* ledger optional, which is the same as not having one. The server refuses that
|
|
10
|
+
* case with an error naming the command to use instead.
|
|
11
|
+
*
|
|
12
|
+
* The reason is REQUIRED, for the same purpose a waiver's is: a verdict with no
|
|
13
|
+
* reason is indistinguishable from a click, and the entire value of a judged
|
|
14
|
+
* expectation is that somebody actually looked.
|
|
15
|
+
*/
|
|
16
|
+
export default class TasksJudge extends BaseCommand {
|
|
17
|
+
static description: string;
|
|
18
|
+
static examples: string[];
|
|
19
|
+
static args: {
|
|
20
|
+
task: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
21
|
+
expectation: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
22
|
+
};
|
|
23
|
+
static flags: {
|
|
24
|
+
verdict: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
25
|
+
reason: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
26
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
27
|
+
};
|
|
28
|
+
run(): Promise<void>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const base_command_1 = require("../../base-command");
|
|
6
|
+
/**
|
|
7
|
+
* `skrr tasks judge <task> <expectation-id> --verdict met --reason "<why>"`
|
|
8
|
+
*
|
|
9
|
+
* Settle a `judged` expectation — one whose text nothing here can evaluate.
|
|
10
|
+
*
|
|
11
|
+
* Only a judged one. A checkable expectation is settled by recording what was
|
|
12
|
+
* produced, and letting a person mark it met by hand would make the deliverables
|
|
13
|
+
* ledger optional, which is the same as not having one. The server refuses that
|
|
14
|
+
* case with an error naming the command to use instead.
|
|
15
|
+
*
|
|
16
|
+
* The reason is REQUIRED, for the same purpose a waiver's is: a verdict with no
|
|
17
|
+
* reason is indistinguishable from a click, and the entire value of a judged
|
|
18
|
+
* expectation is that somebody actually looked.
|
|
19
|
+
*/
|
|
20
|
+
class TasksJudge extends base_command_1.BaseCommand {
|
|
21
|
+
static description = 'Settle a judged expectation with a verdict and a stated reason';
|
|
22
|
+
static examples = [
|
|
23
|
+
'<%= config.bin %> tasks judge <task-id> e2 --verdict met --reason "Read it end to end; a non-engineer follows it."',
|
|
24
|
+
'<%= config.bin %> tasks judge <task-id> e2 --verdict unmet --reason "Still assumes the reader knows the schema."',
|
|
25
|
+
];
|
|
26
|
+
static args = {
|
|
27
|
+
task: core_1.Args.string({ description: 'Task id or ref', required: true, ignoreStdin: true }),
|
|
28
|
+
expectation: core_1.Args.string({
|
|
29
|
+
description: 'Expectation id (see `tasks expectations`)',
|
|
30
|
+
required: true,
|
|
31
|
+
ignoreStdin: true,
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
static flags = {
|
|
35
|
+
verdict: core_1.Flags.string({
|
|
36
|
+
options: ['met', 'unmet'],
|
|
37
|
+
required: true,
|
|
38
|
+
description: 'Whether the expectation was met.',
|
|
39
|
+
}),
|
|
40
|
+
reason: core_1.Flags.string({
|
|
41
|
+
required: true,
|
|
42
|
+
description: 'What you looked at. A verdict without one is not a judgement.',
|
|
43
|
+
}),
|
|
44
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
45
|
+
};
|
|
46
|
+
async run() {
|
|
47
|
+
this.requireAuth();
|
|
48
|
+
const { args, flags } = await this.parse(TasksJudge);
|
|
49
|
+
let result;
|
|
50
|
+
try {
|
|
51
|
+
result = await data_provider_1.dataService.judgeTaskExpectation(args.task, {
|
|
52
|
+
expectationId: args.expectation,
|
|
53
|
+
verdict: flags.verdict,
|
|
54
|
+
reason: flags.reason,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
this.handleApiError(err);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (flags.json) {
|
|
62
|
+
this.log(JSON.stringify(result, null, 2));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
this.log(`Recorded ${args.expectation} as ${flags.verdict}: ${flags.reason}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.default = TasksJudge;
|
|
@@ -18,7 +18,7 @@ export default class TasksReport extends BaseCommand {
|
|
|
18
18
|
finding: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
19
19
|
evidence: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
20
20
|
next: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
21
|
-
|
|
21
|
+
deliverable: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
22
22
|
};
|
|
23
23
|
run(): Promise<void>;
|
|
24
24
|
}
|
|
@@ -18,7 +18,7 @@ class TasksReport extends base_command_1.BaseCommand {
|
|
|
18
18
|
static description = 'Record a self-contained outcome, findings, evidence, and next actions';
|
|
19
19
|
static examples = [
|
|
20
20
|
'<%= config.bin %> tasks report <task-id> --outcome "Adopt the event ledger" --finding "The task page omits work-event details" --evidence "Task detail reads only legacy activity" --next "Add an Outcome section"',
|
|
21
|
-
'<%= config.bin %> tasks report <task-id> --kind handoff --outcome "Ready for review" --finding "All acceptance criteria are met" --
|
|
21
|
+
'<%= config.bin %> tasks report <task-id> --kind handoff --outcome "Ready for review" --finding "All acceptance criteria are met" --deliverable "src/task.ts" --json',
|
|
22
22
|
];
|
|
23
23
|
static args = {
|
|
24
24
|
id: core_1.Args.string({
|
|
@@ -48,7 +48,7 @@ class TasksReport extends base_command_1.BaseCommand {
|
|
|
48
48
|
multiple: true,
|
|
49
49
|
}),
|
|
50
50
|
next: core_1.Flags.string({ description: 'A follow-up or next action (repeatable)', multiple: true }),
|
|
51
|
-
|
|
51
|
+
deliverable: core_1.Flags.string({
|
|
52
52
|
description: 'Durable file, commit, PR, or URL (repeatable)',
|
|
53
53
|
multiple: true,
|
|
54
54
|
}),
|
|
@@ -92,7 +92,7 @@ class TasksReport extends base_command_1.BaseCommand {
|
|
|
92
92
|
idempotencyKey: (0, outbox_1.idempotencyKeyFor)('task-report', task.id, JSON.stringify({ report, kind: flags.kind })),
|
|
93
93
|
kind,
|
|
94
94
|
...(0, task_report_1.taskReportEventFields)(report),
|
|
95
|
-
artifacts: flags.
|
|
95
|
+
artifacts: flags.deliverable?.map((ref) => ({ label: 'deliverable', ref })),
|
|
96
96
|
};
|
|
97
97
|
const sessionPath = await (0, session_task_endpoints_1.sessionScopedTaskPath)('event');
|
|
98
98
|
let result;
|
|
@@ -10,6 +10,7 @@ export default class TasksUpdate extends BaseCommand {
|
|
|
10
10
|
task: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
11
|
'from-json': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
12
|
title: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
|
+
expect: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
14
|
description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
14
15
|
'description-format': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
16
|
priority: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
@@ -5,8 +5,9 @@ const data_provider_1 = require("@skrr-ai/data-provider");
|
|
|
5
5
|
const base_command_1 = require("../../base-command");
|
|
6
6
|
const web_url_1 = require("../../lib/web-url");
|
|
7
7
|
const prompt_1 = require("../../lib/prompt");
|
|
8
|
-
const task_resolver_1 = require("../../lib/task-resolver");
|
|
9
8
|
const tasks_1 = require("../../lib/tasks");
|
|
9
|
+
const task_resolver_1 = require("../../lib/task-resolver");
|
|
10
|
+
const tasks_2 = require("../../lib/tasks");
|
|
10
11
|
const create_1 = require("./create");
|
|
11
12
|
class TasksUpdate extends base_command_1.BaseCommand {
|
|
12
13
|
static description = 'Update an existing task (partial patch). Status and PROJECT changes go through ' +
|
|
@@ -41,6 +42,11 @@ class TasksUpdate extends base_command_1.BaseCommand {
|
|
|
41
42
|
description: 'Path to a JSON file (or "-" for stdin) to use as the patch body; may include taskId',
|
|
42
43
|
}),
|
|
43
44
|
title: core_1.Flags.string({ description: 'New title' }),
|
|
45
|
+
expect: core_1.Flags.string({
|
|
46
|
+
multiple: true,
|
|
47
|
+
description: tasks_1.EXPECT_FLAG_HELP +
|
|
48
|
+
' Replaces the whole list; pass "clear" to drop it and inherit the space default.',
|
|
49
|
+
}),
|
|
44
50
|
description: core_1.Flags.string({ description: 'New description' }),
|
|
45
51
|
'description-format': core_1.Flags.string({
|
|
46
52
|
description: 'Input format for --description or from-json description',
|
|
@@ -48,7 +54,7 @@ class TasksUpdate extends base_command_1.BaseCommand {
|
|
|
48
54
|
}),
|
|
49
55
|
priority: core_1.Flags.string({
|
|
50
56
|
description: 'New priority',
|
|
51
|
-
options: [...
|
|
57
|
+
options: [...tasks_2.TASK_PRIORITIES],
|
|
52
58
|
}),
|
|
53
59
|
'start-date': core_1.Flags.string({ description: 'New planned start date (ISO string)' }),
|
|
54
60
|
'due-date': core_1.Flags.string({ description: 'New due date (ISO string)' }),
|
|
@@ -198,8 +204,32 @@ class TasksUpdate extends base_command_1.BaseCommand {
|
|
|
198
204
|
details: { suggestion: failure.suggestion, context: failure.details },
|
|
199
205
|
});
|
|
200
206
|
}
|
|
207
|
+
// `--expect clear` is an explicit empty list, which the API turns into $unset —
|
|
208
|
+
// distinct from omitting the flag, which leaves the declaration untouched.
|
|
209
|
+
// Without the sentinel there is no way to say "stop requiring an output"
|
|
210
|
+
// short of hand-writing --from-json.
|
|
211
|
+
// `--expect clear` empties the list; otherwise the flags REPLACE it, because
|
|
212
|
+
// a partial patch of a list has no honest meaning — "add one" and "this is
|
|
213
|
+
// now the whole contract" are different requests and the flag can only be
|
|
214
|
+
// one of them.
|
|
215
|
+
let expectationsPatch;
|
|
216
|
+
if (flags.expect !== undefined && flags.expect.length > 0) {
|
|
217
|
+
if (flags.expect.length === 1 && flags.expect[0].trim() === 'clear') {
|
|
218
|
+
expectationsPatch = null;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
const parsed = (0, tasks_1.parseExpectations)(flags.expect);
|
|
222
|
+
if (!parsed.ok) {
|
|
223
|
+
this.error(parsed.error, { exit: 1 });
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
expectationsPatch = parsed.value;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
201
230
|
const payload = (0, prompt_1.mergeDefined)((0, task_resolver_1.stripTaskIdentityFields)(body), {
|
|
202
231
|
title: flags.title,
|
|
232
|
+
expectations: expectationsPatch === null ? [] : (expectationsPatch ?? undefined),
|
|
203
233
|
description: flags.description,
|
|
204
234
|
descriptionFormat: flags['description-format'],
|
|
205
235
|
priority: flags.priority,
|
|
@@ -231,7 +261,7 @@ class TasksUpdate extends base_command_1.BaseCommand {
|
|
|
231
261
|
labelIds: flags['label-id'],
|
|
232
262
|
// Worktree isolation override. --worktree ⇒ 'ephemeral', --no-worktree ⇒
|
|
233
263
|
// 'shared', omitted ⇒ unchanged. (Clear-to-inherit via --from-json.)
|
|
234
|
-
executionIsolation: (0,
|
|
264
|
+
executionIsolation: (0, tasks_2.executionIsolationFromWorktreeFlag)(flags.worktree),
|
|
235
265
|
// A mutation option, not a field — `undefined` unless explicitly asked
|
|
236
266
|
// for, so it never counts toward the "nothing to update" check below.
|
|
237
267
|
repairReleasePins: flags['repair-release-pins'] ? true : undefined,
|
|
@@ -253,7 +283,7 @@ class TasksUpdate extends base_command_1.BaseCommand {
|
|
|
253
283
|
retryable: false,
|
|
254
284
|
});
|
|
255
285
|
}
|
|
256
|
-
const mutationGuardFailure = (0,
|
|
286
|
+
const mutationGuardFailure = (0, tasks_2.taskUpdateMutationGuard)(payload);
|
|
257
287
|
if (mutationGuardFailure) {
|
|
258
288
|
this.failWithCliError({
|
|
259
289
|
message: mutationGuardFailure.message,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr tasks waive <task> <expectation-id> --reason "<why>"`
|
|
4
|
+
*
|
|
5
|
+
* Drop a requirement, on the record. The expectation is KEPT and marked, never
|
|
6
|
+
* deleted: deleting it expresses the same decision while destroying the only
|
|
7
|
+
* evidence that it was ever owed, which is what someone asking "why wasn't this
|
|
8
|
+
* done?" needs to read.
|
|
9
|
+
*
|
|
10
|
+
* The reason is required for the same purpose — a waiver without one is
|
|
11
|
+
* indistinguishable from having forgotten.
|
|
12
|
+
*/
|
|
13
|
+
export default class TasksWaive extends BaseCommand {
|
|
14
|
+
static description: string;
|
|
15
|
+
static examples: string[];
|
|
16
|
+
static args: {
|
|
17
|
+
task: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
18
|
+
expectation: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
19
|
+
};
|
|
20
|
+
static flags: {
|
|
21
|
+
reason: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
22
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
23
|
+
};
|
|
24
|
+
run(): Promise<void>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const base_command_1 = require("../../base-command");
|
|
6
|
+
/**
|
|
7
|
+
* `skrr tasks waive <task> <expectation-id> --reason "<why>"`
|
|
8
|
+
*
|
|
9
|
+
* Drop a requirement, on the record. The expectation is KEPT and marked, never
|
|
10
|
+
* deleted: deleting it expresses the same decision while destroying the only
|
|
11
|
+
* evidence that it was ever owed, which is what someone asking "why wasn't this
|
|
12
|
+
* done?" needs to read.
|
|
13
|
+
*
|
|
14
|
+
* The reason is required for the same purpose — a waiver without one is
|
|
15
|
+
* indistinguishable from having forgotten.
|
|
16
|
+
*/
|
|
17
|
+
class TasksWaive extends base_command_1.BaseCommand {
|
|
18
|
+
static description = 'Waive an expectation, with a stated reason';
|
|
19
|
+
static examples = [
|
|
20
|
+
'<%= config.bin %> tasks waive <task-id> c1 --reason "superseded by OSK-7000"',
|
|
21
|
+
];
|
|
22
|
+
static args = {
|
|
23
|
+
task: core_1.Args.string({ description: 'Task id or ref', required: true, ignoreStdin: true }),
|
|
24
|
+
expectation: core_1.Args.string({
|
|
25
|
+
description: 'Expectation id (see `tasks show`)',
|
|
26
|
+
required: true,
|
|
27
|
+
ignoreStdin: true,
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
static flags = {
|
|
31
|
+
reason: core_1.Flags.string({
|
|
32
|
+
description: 'Why this is no longer required. Required — it is the record.',
|
|
33
|
+
required: true,
|
|
34
|
+
}),
|
|
35
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
36
|
+
};
|
|
37
|
+
async run() {
|
|
38
|
+
this.requireAuth();
|
|
39
|
+
const { args, flags } = await this.parse(TasksWaive);
|
|
40
|
+
let result;
|
|
41
|
+
try {
|
|
42
|
+
result = await data_provider_1.dataService.waiveTaskExpectation(args.task, {
|
|
43
|
+
expectationId: args.expectation,
|
|
44
|
+
reason: flags.reason,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
this.handleApiError(err);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (flags.json) {
|
|
52
|
+
this.log(JSON.stringify(result, null, 2));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.log(`Waived ${args.expectation}: ${flags.reason}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.default = TasksWaive;
|
package/dist/lib/agent-config.js
CHANGED
|
@@ -48,5 +48,10 @@ function mergeAgentConfigFlags(body, flags) {
|
|
|
48
48
|
}
|
|
49
49
|
if (flags['recursion-limit'] !== undefined)
|
|
50
50
|
parsed.recursion_limit = flags['recursion-limit'];
|
|
51
|
+
// Cross-Space Task execution ceiling. `0` means unlimited, matching the
|
|
52
|
+
// Space limits' convention so a user learns one spelling of "no cap".
|
|
53
|
+
if (flags['max-concurrent-task-executions'] !== undefined) {
|
|
54
|
+
parsed.maxConcurrentTaskExecutions = flags['max-concurrent-task-executions'];
|
|
55
|
+
}
|
|
51
56
|
return (0, prompt_1.mergeDefined)(body, parsed);
|
|
52
57
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { dataService, type AgentPromptComposition } from '@skrr-ai/data-provider';
|
|
2
|
+
/** Positional and --agent forms agree with the existing `agents tone` UX. */
|
|
3
|
+
export declare function resolveInstructionAgentId(argument?: string, flag?: string): Promise<string>;
|
|
4
|
+
/** Fetch the edit-gated Agent projection used by the web composer. */
|
|
5
|
+
export declare function loadAgentInstructions(agentId: string): Promise<{
|
|
6
|
+
agent: Awaited<ReturnType<typeof dataService.getExpandedAgentById>>;
|
|
7
|
+
composition: AgentPromptComposition;
|
|
8
|
+
}>;
|
|
9
|
+
/**
|
|
10
|
+
* The Agent controller is intentionally the only persistence boundary. It
|
|
11
|
+
* resolves library versions, verifies bindings and materializes legacy
|
|
12
|
+
* `instructions` atomically, so a CLI never tries to compile a prompt itself.
|
|
13
|
+
*/
|
|
14
|
+
export declare function saveAgentInstructions(agentId: string, composition: AgentPromptComposition): Promise<import("@skrr-ai/data-provider").Agent>;
|
|
15
|
+
export declare function renderCompositionLines(composition: AgentPromptComposition): string[];
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveInstructionAgentId = resolveInstructionAgentId;
|
|
4
|
+
exports.loadAgentInstructions = loadAgentInstructions;
|
|
5
|
+
exports.saveAgentInstructions = saveAgentInstructions;
|
|
6
|
+
exports.renderCompositionLines = renderCompositionLines;
|
|
7
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
8
|
+
const agent_resolver_1 = require("./agent-resolver");
|
|
9
|
+
const instruction_blocks_1 = require("./instruction-blocks");
|
|
10
|
+
/** Positional and --agent forms agree with the existing `agents tone` UX. */
|
|
11
|
+
async function resolveInstructionAgentId(argument, flag) {
|
|
12
|
+
return (0, agent_resolver_1.resolveAgentId)((0, agent_resolver_1.mergeExplicitAgentInputs)([
|
|
13
|
+
{ label: 'argument', value: argument },
|
|
14
|
+
{ label: '--agent', value: flag },
|
|
15
|
+
]));
|
|
16
|
+
}
|
|
17
|
+
/** Fetch the edit-gated Agent projection used by the web composer. */
|
|
18
|
+
async function loadAgentInstructions(agentId) {
|
|
19
|
+
const agent = await data_provider_1.dataService.getExpandedAgentById({ agent_id: agentId });
|
|
20
|
+
return { agent, composition: (0, instruction_blocks_1.compositionForAgent)(agent) };
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The Agent controller is intentionally the only persistence boundary. It
|
|
24
|
+
* resolves library versions, verifies bindings and materializes legacy
|
|
25
|
+
* `instructions` atomically, so a CLI never tries to compile a prompt itself.
|
|
26
|
+
*/
|
|
27
|
+
async function saveAgentInstructions(agentId, composition) {
|
|
28
|
+
return data_provider_1.dataService.updateAgent({
|
|
29
|
+
agent_id: agentId,
|
|
30
|
+
data: { promptComposition: composition },
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function renderCompositionLines(composition) {
|
|
34
|
+
if (!composition.entries.length)
|
|
35
|
+
return ['(No custom instruction blocks.)'];
|
|
36
|
+
return composition.entries.flatMap((entry, index) => {
|
|
37
|
+
const state = entry.enabled ? 'on' : 'off';
|
|
38
|
+
if (entry.source.kind === 'inline') {
|
|
39
|
+
const preview = entry.source.body.trim().replace(/\s+/g, ' ').slice(0, 140);
|
|
40
|
+
return [
|
|
41
|
+
`${index + 1}. ${state.padEnd(3)} inline ${entry.source.title} (${entry.instanceId})`,
|
|
42
|
+
` ${preview || '(empty)'}`,
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
const label = entry.resolved?.name || entry.source.blockId;
|
|
46
|
+
const bindings = entry.bindings && Object.keys(entry.bindings).length
|
|
47
|
+
? ` · bindings: ${Object.entries(entry.bindings)
|
|
48
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
49
|
+
.join(', ')}`
|
|
50
|
+
: '';
|
|
51
|
+
return [
|
|
52
|
+
`${index + 1}. ${state.padEnd(3)} library ${label} · v${entry.source.version} (${entry.instanceId})${bindings}`,
|
|
53
|
+
];
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -30,5 +30,10 @@ export type CommandHygieneReport = {
|
|
|
30
30
|
};
|
|
31
31
|
export declare function auditCommands({ rootDir, manifest, }: {
|
|
32
32
|
rootDir: string;
|
|
33
|
-
|
|
33
|
+
/**
|
|
34
|
+
* The generated oclif catalogue is not present in a source checkout. Such
|
|
35
|
+
* callers can still run source-level checks, but cannot make claims about
|
|
36
|
+
* what a generated manifest contains.
|
|
37
|
+
*/
|
|
38
|
+
manifest?: CommandManifest;
|
|
34
39
|
}): CommandHygieneReport;
|
|
@@ -46,13 +46,14 @@ const AGENT_CRITICAL_COMMANDS = [
|
|
|
46
46
|
];
|
|
47
47
|
function auditCommands({ rootDir, manifest, }) {
|
|
48
48
|
const issues = [];
|
|
49
|
-
const commands = manifest
|
|
49
|
+
const commands = manifest?.commands ?? {};
|
|
50
50
|
const commandIds = Object.keys(commands);
|
|
51
|
+
const manifestForCatalog = manifest ?? {};
|
|
51
52
|
// The command manifest is the only catalogue. The progressive `skrr usage`
|
|
52
53
|
// surfaces are useful precisely because they do not make an agent load that
|
|
53
54
|
// whole catalogue at once, so audit every generated continuation page too.
|
|
54
|
-
const overview = (0, usage_discovery_1.buildUsageOverview)(
|
|
55
|
-
const usageBudgetIssues = (0, usage_discovery_1.auditUsageBudgets)(
|
|
55
|
+
const overview = (0, usage_discovery_1.buildUsageOverview)(manifestForCatalog);
|
|
56
|
+
const usageBudgetIssues = (0, usage_discovery_1.auditUsageBudgets)(manifestForCatalog);
|
|
56
57
|
for (const issue of usageBudgetIssues) {
|
|
57
58
|
issues.push({
|
|
58
59
|
code: 'USAGE_DISCOVERY_TOKEN_BUDGET_EXCEEDED',
|
|
@@ -62,14 +63,31 @@ function auditCommands({ rootDir, manifest, }) {
|
|
|
62
63
|
`uses ${issue.estimatedTokens} estimated tokens; budget is ${issue.tokenBudget}.`,
|
|
63
64
|
});
|
|
64
65
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
if (!manifest) {
|
|
67
|
+
// Absence is a distinct answer from "missing", and it should be audible.
|
|
68
|
+
//
|
|
69
|
+
// Skipping silently means a source-only run reports a clean bill of health
|
|
70
|
+
// for a check it never performed. That is the shape that makes an audit
|
|
71
|
+
// untrustworthy — worse than a noisy one, because a green result stops
|
|
72
|
+
// meaning anything. A warning keeps the run green while saying which claim
|
|
73
|
+
// it is NOT making, and names the command that would let it.
|
|
74
|
+
issues.push({
|
|
75
|
+
code: 'AGENT_CRITICAL_COMMANDS_UNCHECKED',
|
|
76
|
+
severity: 'warning',
|
|
77
|
+
message: 'No command manifest was available, so the agent-critical command check did not run. ' +
|
|
78
|
+
'Build the CLI (`npx tsc -p tsconfig.build.json && npx oclif manifest` in cli/) to audit it.',
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (manifest) {
|
|
82
|
+
for (const id of AGENT_CRITICAL_COMMANDS) {
|
|
83
|
+
if (!commands[id]) {
|
|
84
|
+
issues.push({
|
|
85
|
+
code: 'AGENT_CRITICAL_COMMAND_MISSING',
|
|
86
|
+
severity: 'error',
|
|
87
|
+
command: id,
|
|
88
|
+
message: `Agent-critical command is missing from the manifest: ${id}`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
73
91
|
}
|
|
74
92
|
}
|
|
75
93
|
let sourceAvailable = false;
|
|
@@ -129,23 +147,25 @@ function auditCommands({ rootDir, manifest, }) {
|
|
|
129
147
|
message: 'Source command files are unavailable; source-level hygiene checks were skipped.',
|
|
130
148
|
});
|
|
131
149
|
}
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
150
|
+
if (manifest) {
|
|
151
|
+
for (const id of commandIds) {
|
|
152
|
+
const command = commands[id];
|
|
153
|
+
if (!command?.description?.trim()) {
|
|
154
|
+
issues.push({
|
|
155
|
+
code: 'COMMAND_DESCRIPTION_MISSING',
|
|
156
|
+
severity: 'warning',
|
|
157
|
+
command: id,
|
|
158
|
+
message: 'Command is missing a description in the manifest.',
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
if (!hasJsonFlag(command?.flags) && isMachineRelevantCommand(id)) {
|
|
162
|
+
issues.push({
|
|
163
|
+
code: 'JSON_SUCCESS_OUTPUT_MISSING',
|
|
164
|
+
severity: 'warning',
|
|
165
|
+
command: id,
|
|
166
|
+
message: 'Machine-relevant command has no --json success-output flag. Keep human-only commands intentional.',
|
|
167
|
+
});
|
|
168
|
+
}
|
|
149
169
|
}
|
|
150
170
|
}
|
|
151
171
|
const errors = issues.filter((issue) => issue.severity === 'error').length;
|
|
@@ -167,7 +187,7 @@ function auditCommands({ rootDir, manifest, }) {
|
|
|
167
187
|
usage: {
|
|
168
188
|
overviewTokenBudget: overview.tokenBudget,
|
|
169
189
|
overviewEstimatedTokens: overview.estimatedTokens,
|
|
170
|
-
domains: (0, usage_discovery_1.usageDomains)(
|
|
190
|
+
domains: (0, usage_discovery_1.usageDomains)(manifestForCatalog).length,
|
|
171
191
|
budgetErrors: usageBudgetIssues.length,
|
|
172
192
|
},
|
|
173
193
|
issues,
|
|
@@ -72,6 +72,19 @@ export declare function notInstalledMessage(): string;
|
|
|
72
72
|
* may depend on.
|
|
73
73
|
*/
|
|
74
74
|
export declare function execRuntimeBinary(args: string[]): Promise<number>;
|
|
75
|
+
/** Output from a short-lived runtime probe such as `skrrd status --json`. */
|
|
76
|
+
export type RuntimeBinaryOutput = {
|
|
77
|
+
code: number;
|
|
78
|
+
stdout: string;
|
|
79
|
+
stderr: string;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Invoke the installed runtime without inheriting its streams. The regular
|
|
83
|
+
* lifecycle command still owns the terminal; this is only for the wrapper's
|
|
84
|
+
* post-condition probes, where output is evidence rather than user-facing
|
|
85
|
+
* command output.
|
|
86
|
+
*/
|
|
87
|
+
export declare function execRuntimeBinaryCapture(args: string[]): Promise<RuntimeBinaryOutput>;
|
|
75
88
|
/**
|
|
76
89
|
* Check an installed daemon's top-level help before forwarding a command that
|
|
77
90
|
* was added after older daemon releases shipped. A false result is useful to
|