mcoda 0.1.19 → 0.1.21
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/commands/backlog/OrderTasksCommand.d.ts +2 -0
- package/dist/commands/backlog/OrderTasksCommand.d.ts.map +1 -1
- package/dist/commands/backlog/OrderTasksCommand.js +38 -2
- package/dist/commands/docs/DocsCommands.d.ts.map +1 -1
- package/dist/commands/docs/DocsCommands.js +11 -11
- package/dist/commands/estimate/EstimateCommands.d.ts.map +1 -1
- package/dist/commands/estimate/EstimateCommands.js +28 -27
- package/dist/commands/openapi/OpenapiCommands.d.ts +1 -0
- package/dist/commands/openapi/OpenapiCommands.d.ts.map +1 -1
- package/dist/commands/openapi/OpenapiCommands.js +11 -1
- package/dist/commands/planning/CreateTasksCommand.d.ts.map +1 -1
- package/dist/commands/planning/CreateTasksCommand.js +18 -12
- package/dist/commands/planning/QaTasksCommand.d.ts +14 -0
- package/dist/commands/planning/QaTasksCommand.d.ts.map +1 -1
- package/dist/commands/planning/QaTasksCommand.js +89 -7
- package/dist/commands/review/CodeReviewCommand.d.ts +14 -0
- package/dist/commands/review/CodeReviewCommand.d.ts.map +1 -1
- package/dist/commands/review/CodeReviewCommand.js +133 -4
- package/dist/commands/work/WorkOnTasksCommand.d.ts +15 -1
- package/dist/commands/work/WorkOnTasksCommand.d.ts.map +1 -1
- package/dist/commands/work/WorkOnTasksCommand.js +140 -8
- package/dist/commands/workspace/ProjectGuidanceCommand.d.ts +1 -0
- package/dist/commands/workspace/ProjectGuidanceCommand.d.ts.map +1 -1
- package/dist/commands/workspace/ProjectGuidanceCommand.js +81 -1
- package/dist/commands/workspace/SetWorkspaceCommand.d.ts.map +1 -1
- package/dist/commands/workspace/SetWorkspaceCommand.js +37 -1
- package/package.json +5 -5
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { WorkspaceRepository } from "@mcoda/db";
|
|
2
3
|
import { CodeReviewService, WorkspaceResolver } from "@mcoda/core";
|
|
3
4
|
import { REVIEW_ALLOWED_STATUSES, filterTaskStatuses, normalizeReviewStatuses } from "@mcoda/shared";
|
|
4
5
|
const usage = `mcoda code-review \\
|
|
@@ -13,6 +14,8 @@ const usage = `mcoda code-review \\
|
|
|
13
14
|
[--agent <NAME>] \\
|
|
14
15
|
[--agent-stream <true|false>] \\
|
|
15
16
|
[--create-followup-tasks <true|false>] \\
|
|
17
|
+
[--execution-context-policy <best_effort|require_any|require_sds_or_openapi>] \\
|
|
18
|
+
[--empty-diff-approval-policy <ready_to_qa|complete>] \\
|
|
16
19
|
[--rate-agents] \\
|
|
17
20
|
[--json]
|
|
18
21
|
|
|
@@ -35,6 +38,24 @@ const parseCsv = (value) => {
|
|
|
35
38
|
.map((v) => v.trim())
|
|
36
39
|
.filter(Boolean);
|
|
37
40
|
};
|
|
41
|
+
const normalizeExecutionContextPolicy = (value) => {
|
|
42
|
+
if (!value)
|
|
43
|
+
return undefined;
|
|
44
|
+
const normalized = value.trim().toLowerCase();
|
|
45
|
+
if (normalized === "best_effort" || normalized === "require_any" || normalized === "require_sds_or_openapi") {
|
|
46
|
+
return normalized;
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
};
|
|
50
|
+
const normalizeEmptyDiffApprovalPolicy = (value) => {
|
|
51
|
+
if (!value)
|
|
52
|
+
return undefined;
|
|
53
|
+
const normalized = value.trim().toLowerCase();
|
|
54
|
+
if (normalized === "ready_to_qa" || normalized === "complete") {
|
|
55
|
+
return normalized;
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
};
|
|
38
59
|
export const parseCodeReviewArgs = (argv) => {
|
|
39
60
|
let workspaceRoot;
|
|
40
61
|
let projectKey;
|
|
@@ -50,6 +71,8 @@ export const parseCodeReviewArgs = (argv) => {
|
|
|
50
71
|
let agentStream;
|
|
51
72
|
let rateAgents = false;
|
|
52
73
|
let createFollowupTasks = false;
|
|
74
|
+
let executionContextPolicy;
|
|
75
|
+
let emptyDiffApprovalPolicy;
|
|
53
76
|
let json = false;
|
|
54
77
|
for (let i = 0; i < argv.length; i += 1) {
|
|
55
78
|
const arg = argv[i];
|
|
@@ -79,6 +102,20 @@ export const parseCodeReviewArgs = (argv) => {
|
|
|
79
102
|
createFollowupTasks = parseBooleanFlag(raw, true);
|
|
80
103
|
continue;
|
|
81
104
|
}
|
|
105
|
+
if (arg.startsWith("--execution-context-policy=")) {
|
|
106
|
+
const [, raw] = arg.split("=", 2);
|
|
107
|
+
const parsedPolicy = normalizeExecutionContextPolicy(raw);
|
|
108
|
+
if (parsedPolicy)
|
|
109
|
+
executionContextPolicy = parsedPolicy;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (arg.startsWith("--empty-diff-approval-policy=")) {
|
|
113
|
+
const [, raw] = arg.split("=", 2);
|
|
114
|
+
const parsedPolicy = normalizeEmptyDiffApprovalPolicy(raw);
|
|
115
|
+
if (parsedPolicy)
|
|
116
|
+
emptyDiffApprovalPolicy = parsedPolicy;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
82
119
|
switch (arg) {
|
|
83
120
|
case "--workspace":
|
|
84
121
|
case "--workspace-root":
|
|
@@ -160,6 +197,28 @@ export const parseCodeReviewArgs = (argv) => {
|
|
|
160
197
|
}
|
|
161
198
|
break;
|
|
162
199
|
}
|
|
200
|
+
case "--execution-context-policy": {
|
|
201
|
+
const next = argv[i + 1];
|
|
202
|
+
if (next && !next.startsWith("--")) {
|
|
203
|
+
const parsedPolicy = normalizeExecutionContextPolicy(next);
|
|
204
|
+
if (parsedPolicy) {
|
|
205
|
+
executionContextPolicy = parsedPolicy;
|
|
206
|
+
i += 1;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
case "--empty-diff-approval-policy": {
|
|
212
|
+
const next = argv[i + 1];
|
|
213
|
+
if (next && !next.startsWith("--")) {
|
|
214
|
+
const parsedPolicy = normalizeEmptyDiffApprovalPolicy(next);
|
|
215
|
+
if (parsedPolicy) {
|
|
216
|
+
emptyDiffApprovalPolicy = parsedPolicy;
|
|
217
|
+
i += 1;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
163
222
|
case "--json":
|
|
164
223
|
json = true;
|
|
165
224
|
break;
|
|
@@ -191,9 +250,55 @@ export const parseCodeReviewArgs = (argv) => {
|
|
|
191
250
|
agentStream: agentStream ?? false,
|
|
192
251
|
rateAgents,
|
|
193
252
|
createFollowupTasks,
|
|
253
|
+
executionContextPolicy: executionContextPolicy ?? "require_sds_or_openapi",
|
|
254
|
+
emptyDiffApprovalPolicy: emptyDiffApprovalPolicy ?? "ready_to_qa",
|
|
194
255
|
json,
|
|
195
256
|
};
|
|
196
257
|
};
|
|
258
|
+
const listWorkspaceProjects = async (workspaceRoot) => {
|
|
259
|
+
const repo = await WorkspaceRepository.create(workspaceRoot);
|
|
260
|
+
try {
|
|
261
|
+
const rows = await repo
|
|
262
|
+
.getDb()
|
|
263
|
+
.all(`SELECT key, created_at FROM projects ORDER BY created_at ASC, key ASC`);
|
|
264
|
+
return rows
|
|
265
|
+
.map((row) => ({ key: String(row.key), createdAt: row.created_at ?? null }))
|
|
266
|
+
.filter((row) => row.key.trim().length > 0);
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
finally {
|
|
272
|
+
await repo.close();
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
export const pickCodeReviewProjectKey = (options) => {
|
|
276
|
+
const warnings = [];
|
|
277
|
+
const requestedKey = options.requestedKey?.trim() || undefined;
|
|
278
|
+
const configuredKey = options.configuredKey?.trim() || undefined;
|
|
279
|
+
const existing = options.existing ?? [];
|
|
280
|
+
const firstExisting = existing[0]?.key;
|
|
281
|
+
if (requestedKey) {
|
|
282
|
+
if (configuredKey && configuredKey !== requestedKey) {
|
|
283
|
+
warnings.push(`Using explicitly requested project key "${requestedKey}"; overriding configured project key "${configuredKey}".`);
|
|
284
|
+
}
|
|
285
|
+
if (firstExisting && requestedKey !== firstExisting) {
|
|
286
|
+
warnings.push(`Using explicitly requested project key "${requestedKey}"; first workspace project is "${firstExisting}".`);
|
|
287
|
+
}
|
|
288
|
+
return { projectKey: requestedKey, warnings };
|
|
289
|
+
}
|
|
290
|
+
if (configuredKey) {
|
|
291
|
+
if (firstExisting && configuredKey !== firstExisting) {
|
|
292
|
+
warnings.push(`Using configured project key "${configuredKey}" instead of first workspace project "${firstExisting}".`);
|
|
293
|
+
}
|
|
294
|
+
return { projectKey: configuredKey, warnings };
|
|
295
|
+
}
|
|
296
|
+
if (firstExisting) {
|
|
297
|
+
warnings.push(`No --project provided; defaulting to first workspace project "${firstExisting}".`);
|
|
298
|
+
return { projectKey: firstExisting, warnings };
|
|
299
|
+
}
|
|
300
|
+
return { projectKey: undefined, warnings };
|
|
301
|
+
};
|
|
197
302
|
export class CodeReviewCommand {
|
|
198
303
|
static async run(argv) {
|
|
199
304
|
const parsed = parseCodeReviewArgs(argv);
|
|
@@ -202,11 +307,31 @@ export class CodeReviewCommand {
|
|
|
202
307
|
explicitWorkspace: parsed.workspaceRoot,
|
|
203
308
|
noRepoWrites: true,
|
|
204
309
|
});
|
|
310
|
+
const existingProjects = parsed.projectKey ? [] : await listWorkspaceProjects(workspace.workspaceRoot);
|
|
311
|
+
const configuredKey = typeof workspace.config?.projectKey === "string" && workspace.config.projectKey.trim().length > 0
|
|
312
|
+
? workspace.config.projectKey
|
|
313
|
+
: undefined;
|
|
314
|
+
const projectResolution = pickCodeReviewProjectKey({
|
|
315
|
+
requestedKey: parsed.projectKey,
|
|
316
|
+
configuredKey,
|
|
317
|
+
existing: existingProjects,
|
|
318
|
+
});
|
|
319
|
+
const commandWarnings = [...projectResolution.warnings];
|
|
320
|
+
if (!projectResolution.projectKey) {
|
|
321
|
+
// eslint-disable-next-line no-console
|
|
322
|
+
console.error("code-review could not resolve a project key. Provide --project <PROJECT_KEY> or create tasks for this workspace first.");
|
|
323
|
+
process.exitCode = 1;
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (commandWarnings.length && !parsed.json) {
|
|
327
|
+
// eslint-disable-next-line no-console
|
|
328
|
+
console.warn(commandWarnings.map((warning) => `! ${warning}`).join("\n"));
|
|
329
|
+
}
|
|
205
330
|
const service = await CodeReviewService.create(workspace);
|
|
206
331
|
try {
|
|
207
332
|
const result = await service.reviewTasks({
|
|
208
333
|
workspace,
|
|
209
|
-
projectKey:
|
|
334
|
+
projectKey: projectResolution.projectKey,
|
|
210
335
|
epicKey: parsed.epicKey,
|
|
211
336
|
storyKey: parsed.storyKey,
|
|
212
337
|
taskKeys: parsed.taskKeys.length ? parsed.taskKeys : undefined,
|
|
@@ -220,8 +345,11 @@ export class CodeReviewCommand {
|
|
|
220
345
|
agentStream: parsed.agentStream,
|
|
221
346
|
rateAgents: parsed.rateAgents,
|
|
222
347
|
createFollowupTasks: parsed.createFollowupTasks,
|
|
348
|
+
executionContextPolicy: parsed.executionContextPolicy,
|
|
349
|
+
emptyDiffApprovalPolicy: parsed.emptyDiffApprovalPolicy,
|
|
223
350
|
});
|
|
224
351
|
if (parsed.json) {
|
|
352
|
+
const warnings = [...commandWarnings, ...result.warnings];
|
|
225
353
|
// eslint-disable-next-line no-console
|
|
226
354
|
console.log(JSON.stringify({
|
|
227
355
|
job: { id: result.jobId, commandRunId: result.commandRunId },
|
|
@@ -236,7 +364,7 @@ export class CodeReviewCommand {
|
|
|
236
364
|
followupTasks: t.followupTasks,
|
|
237
365
|
})),
|
|
238
366
|
errors: result.tasks.filter((t) => t.error).map((t) => ({ taskId: t.taskId, taskKey: t.taskKey, error: t.error })),
|
|
239
|
-
warnings
|
|
367
|
+
warnings,
|
|
240
368
|
}, null, 2));
|
|
241
369
|
return;
|
|
242
370
|
}
|
|
@@ -260,8 +388,9 @@ export class CodeReviewCommand {
|
|
|
260
388
|
`Artifacts: ${path.join(workspace.mcodaDir, "jobs", result.jobId, "review")}`,
|
|
261
389
|
...lines,
|
|
262
390
|
];
|
|
263
|
-
|
|
264
|
-
|
|
391
|
+
const warnings = [...commandWarnings, ...result.warnings];
|
|
392
|
+
if (warnings.length) {
|
|
393
|
+
summary.push(`Warnings: ${warnings.join("; ")}`);
|
|
265
394
|
}
|
|
266
395
|
// eslint-disable-next-line no-console
|
|
267
396
|
console.log(summary.join("\n"));
|
|
@@ -17,11 +17,25 @@ interface ParsedArgs {
|
|
|
17
17
|
workRunner?: string;
|
|
18
18
|
useCodali?: boolean;
|
|
19
19
|
agentAdapterOverride?: string;
|
|
20
|
-
missingTestsPolicy?: "block_job" | "skip_task" | "fail_task";
|
|
20
|
+
missingTestsPolicy?: "block_job" | "skip_task" | "fail_task" | "continue_task";
|
|
21
21
|
allowMissingTests?: boolean;
|
|
22
|
+
missingContextPolicy?: "allow" | "warn" | "block";
|
|
23
|
+
executionContextPolicy?: "best_effort" | "require_any" | "require_sds_or_openapi";
|
|
22
24
|
json: boolean;
|
|
23
25
|
}
|
|
26
|
+
type ProjectKeyCandidate = {
|
|
27
|
+
key: string;
|
|
28
|
+
createdAt?: string | null;
|
|
29
|
+
};
|
|
24
30
|
export declare const parseWorkOnTasksArgs: (argv: string[]) => ParsedArgs;
|
|
31
|
+
export declare const pickWorkOnTasksProjectKey: (options: {
|
|
32
|
+
requestedKey?: string;
|
|
33
|
+
configuredKey?: string;
|
|
34
|
+
existing: ProjectKeyCandidate[];
|
|
35
|
+
}) => {
|
|
36
|
+
projectKey?: string;
|
|
37
|
+
warnings: string[];
|
|
38
|
+
};
|
|
25
39
|
export declare class WorkOnTasksCommand {
|
|
26
40
|
static run(argv: string[]): Promise<void>;
|
|
27
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkOnTasksCommand.d.ts","sourceRoot":"","sources":["../../../src/commands/work/WorkOnTasksCommand.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"WorkOnTasksCommand.d.ts","sourceRoot":"","sources":["../../../src/commands/work/WorkOnTasksCommand.ts"],"names":[],"mappings":"AAKA,UAAU,UAAU;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,eAAe,CAAC;IAC/E,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,oBAAoB,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IAClD,sBAAsB,CAAC,EAAE,aAAa,GAAG,aAAa,GAAG,wBAAwB,CAAC;IAClF,IAAI,EAAE,OAAO,CAAC;CACf;AAED,KAAK,mBAAmB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAgHtE,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,EAAE,KAAG,UA8SrD,CAAC;AAoBF,eAAO,MAAM,yBAAyB,GAAI,SAAS;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CACjC,KAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAkC5C,CAAC;AAEF,qBAAa,kBAAkB;WAChB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAwIhD"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { WorkspaceRepository } from "@mcoda/db";
|
|
2
3
|
import { JobService, WorkOnTasksService, WorkspaceResolver } from "@mcoda/core";
|
|
3
4
|
import { WORK_ALLOWED_STATUSES, filterTaskStatuses } from "@mcoda/shared";
|
|
4
5
|
const usage = `mcoda work-on-tasks \\
|
|
@@ -14,8 +15,10 @@ const usage = `mcoda work-on-tasks \\
|
|
|
14
15
|
[--agent-stream <true|false>] \\
|
|
15
16
|
[--work-runner <codali|default>] \\
|
|
16
17
|
[--use-codali <true|false>] \\
|
|
17
|
-
[--missing-tests-policy <block_job|skip_task|fail_task>] \\
|
|
18
|
+
[--missing-tests-policy <continue_task|block_job|skip_task|fail_task>] \\
|
|
18
19
|
[--allow-missing-tests <true|false>] \\
|
|
20
|
+
[--missing-context-policy <allow|warn|block>] \\
|
|
21
|
+
[--execution-context-policy <best_effort|require_any|require_sds_or_openapi>] \\
|
|
19
22
|
[--rate-agents] \\
|
|
20
23
|
[--auto-merge <true|false>] \\
|
|
21
24
|
[--auto-push <true|false>] \\
|
|
@@ -52,7 +55,34 @@ const normalizeMissingTestsPolicy = (value) => {
|
|
|
52
55
|
if (!value)
|
|
53
56
|
return undefined;
|
|
54
57
|
const normalized = value.trim().toLowerCase().replace(/-/g, "_");
|
|
55
|
-
if (normalized === "block_job" ||
|
|
58
|
+
if (normalized === "block_job" ||
|
|
59
|
+
normalized === "skip_task" ||
|
|
60
|
+
normalized === "fail_task" ||
|
|
61
|
+
normalized === "continue_task" ||
|
|
62
|
+
normalized === "continue" ||
|
|
63
|
+
normalized === "allow" ||
|
|
64
|
+
normalized === "warn_task") {
|
|
65
|
+
if (normalized === "continue" || normalized === "allow" || normalized === "warn_task") {
|
|
66
|
+
return "continue_task";
|
|
67
|
+
}
|
|
68
|
+
return normalized;
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
};
|
|
72
|
+
const normalizeMissingContextPolicy = (value) => {
|
|
73
|
+
if (!value)
|
|
74
|
+
return undefined;
|
|
75
|
+
const normalized = value.trim().toLowerCase();
|
|
76
|
+
if (normalized === "allow" || normalized === "warn" || normalized === "block") {
|
|
77
|
+
return normalized;
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
80
|
+
};
|
|
81
|
+
const normalizeExecutionContextPolicy = (value) => {
|
|
82
|
+
if (!value)
|
|
83
|
+
return undefined;
|
|
84
|
+
const normalized = value.trim().toLowerCase();
|
|
85
|
+
if (normalized === "best_effort" || normalized === "require_any" || normalized === "require_sds_or_openapi") {
|
|
56
86
|
return normalized;
|
|
57
87
|
}
|
|
58
88
|
return undefined;
|
|
@@ -93,6 +123,8 @@ export const parseWorkOnTasksArgs = (argv) => {
|
|
|
93
123
|
let useCodali;
|
|
94
124
|
let missingTestsPolicy;
|
|
95
125
|
let allowMissingTests;
|
|
126
|
+
let missingContextPolicy;
|
|
127
|
+
let executionContextPolicy;
|
|
96
128
|
let json = false;
|
|
97
129
|
for (let i = 0; i < argv.length; i += 1) {
|
|
98
130
|
const arg = argv[i];
|
|
@@ -149,6 +181,20 @@ export const parseWorkOnTasksArgs = (argv) => {
|
|
|
149
181
|
allowMissingTests = parseBooleanFlag(raw, true);
|
|
150
182
|
continue;
|
|
151
183
|
}
|
|
184
|
+
if (arg.startsWith("--missing-context-policy=")) {
|
|
185
|
+
const [, raw] = arg.split("=", 2);
|
|
186
|
+
const parsedPolicy = normalizeMissingContextPolicy(raw);
|
|
187
|
+
if (parsedPolicy)
|
|
188
|
+
missingContextPolicy = parsedPolicy;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (arg.startsWith("--execution-context-policy=")) {
|
|
192
|
+
const [, raw] = arg.split("=", 2);
|
|
193
|
+
const parsedPolicy = normalizeExecutionContextPolicy(raw);
|
|
194
|
+
if (parsedPolicy)
|
|
195
|
+
executionContextPolicy = parsedPolicy;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
152
198
|
switch (arg) {
|
|
153
199
|
case "--workspace":
|
|
154
200
|
case "--workspace-root":
|
|
@@ -248,6 +294,28 @@ export const parseWorkOnTasksArgs = (argv) => {
|
|
|
248
294
|
}
|
|
249
295
|
break;
|
|
250
296
|
}
|
|
297
|
+
case "--missing-context-policy": {
|
|
298
|
+
const next = argv[i + 1];
|
|
299
|
+
if (next && !next.startsWith("--")) {
|
|
300
|
+
const parsedPolicy = normalizeMissingContextPolicy(next);
|
|
301
|
+
if (parsedPolicy) {
|
|
302
|
+
missingContextPolicy = parsedPolicy;
|
|
303
|
+
}
|
|
304
|
+
i += 1;
|
|
305
|
+
}
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
case "--execution-context-policy": {
|
|
309
|
+
const next = argv[i + 1];
|
|
310
|
+
if (next && !next.startsWith("--")) {
|
|
311
|
+
const parsedPolicy = normalizeExecutionContextPolicy(next);
|
|
312
|
+
if (parsedPolicy) {
|
|
313
|
+
executionContextPolicy = parsedPolicy;
|
|
314
|
+
}
|
|
315
|
+
i += 1;
|
|
316
|
+
}
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
251
319
|
case "--auto-merge": {
|
|
252
320
|
const next = argv[i + 1];
|
|
253
321
|
if (next && !next.startsWith("--")) {
|
|
@@ -335,9 +403,55 @@ export const parseWorkOnTasksArgs = (argv) => {
|
|
|
335
403
|
agentAdapterOverride: runnerOverride.agentAdapterOverride,
|
|
336
404
|
missingTestsPolicy,
|
|
337
405
|
allowMissingTests,
|
|
406
|
+
missingContextPolicy,
|
|
407
|
+
executionContextPolicy: executionContextPolicy ?? "require_sds_or_openapi",
|
|
338
408
|
json,
|
|
339
409
|
};
|
|
340
410
|
};
|
|
411
|
+
const listWorkspaceProjects = async (workspaceRoot) => {
|
|
412
|
+
const repo = await WorkspaceRepository.create(workspaceRoot);
|
|
413
|
+
try {
|
|
414
|
+
const rows = await repo
|
|
415
|
+
.getDb()
|
|
416
|
+
.all(`SELECT key, created_at FROM projects ORDER BY created_at ASC, key ASC`);
|
|
417
|
+
return rows
|
|
418
|
+
.map((row) => ({ key: String(row.key), createdAt: row.created_at ?? null }))
|
|
419
|
+
.filter((row) => row.key.trim().length > 0);
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
return [];
|
|
423
|
+
}
|
|
424
|
+
finally {
|
|
425
|
+
await repo.close();
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
export const pickWorkOnTasksProjectKey = (options) => {
|
|
429
|
+
const warnings = [];
|
|
430
|
+
const requestedKey = options.requestedKey?.trim() || undefined;
|
|
431
|
+
const configuredKey = options.configuredKey?.trim() || undefined;
|
|
432
|
+
const existing = options.existing ?? [];
|
|
433
|
+
const firstExisting = existing[0]?.key;
|
|
434
|
+
if (requestedKey) {
|
|
435
|
+
if (configuredKey && configuredKey !== requestedKey) {
|
|
436
|
+
warnings.push(`Using explicitly requested project key "${requestedKey}"; overriding configured project key "${configuredKey}".`);
|
|
437
|
+
}
|
|
438
|
+
if (firstExisting && requestedKey !== firstExisting) {
|
|
439
|
+
warnings.push(`Using explicitly requested project key "${requestedKey}"; first workspace project is "${firstExisting}".`);
|
|
440
|
+
}
|
|
441
|
+
return { projectKey: requestedKey, warnings };
|
|
442
|
+
}
|
|
443
|
+
if (configuredKey) {
|
|
444
|
+
if (firstExisting && configuredKey !== firstExisting) {
|
|
445
|
+
warnings.push(`Using configured project key "${configuredKey}" instead of first workspace project "${firstExisting}".`);
|
|
446
|
+
}
|
|
447
|
+
return { projectKey: configuredKey, warnings };
|
|
448
|
+
}
|
|
449
|
+
if (firstExisting) {
|
|
450
|
+
warnings.push(`No --project provided; defaulting to first workspace project "${firstExisting}".`);
|
|
451
|
+
return { projectKey: firstExisting, warnings };
|
|
452
|
+
}
|
|
453
|
+
return { projectKey: undefined, warnings };
|
|
454
|
+
};
|
|
341
455
|
export class WorkOnTasksCommand {
|
|
342
456
|
static async run(argv) {
|
|
343
457
|
const parsed = parseWorkOnTasksArgs(argv);
|
|
@@ -349,12 +463,26 @@ export class WorkOnTasksCommand {
|
|
|
349
463
|
cwd: process.cwd(),
|
|
350
464
|
explicitWorkspace: parsed.workspaceRoot,
|
|
351
465
|
});
|
|
352
|
-
|
|
466
|
+
const existingProjects = parsed.projectKey ? [] : await listWorkspaceProjects(workspace.workspaceRoot);
|
|
467
|
+
const configuredKey = typeof workspace.config?.projectKey === "string" && workspace.config.projectKey.trim().length > 0
|
|
468
|
+
? workspace.config.projectKey
|
|
469
|
+
: undefined;
|
|
470
|
+
const projectResolution = pickWorkOnTasksProjectKey({
|
|
471
|
+
requestedKey: parsed.projectKey,
|
|
472
|
+
configuredKey,
|
|
473
|
+
existing: existingProjects,
|
|
474
|
+
});
|
|
475
|
+
const commandWarnings = [...projectResolution.warnings];
|
|
476
|
+
if (!projectResolution.projectKey) {
|
|
353
477
|
// eslint-disable-next-line no-console
|
|
354
|
-
console.error("work-on-tasks
|
|
478
|
+
console.error("work-on-tasks could not resolve a project key. Provide --project <PROJECT_KEY> or create tasks for this workspace first.");
|
|
355
479
|
process.exitCode = 1;
|
|
356
480
|
return;
|
|
357
481
|
}
|
|
482
|
+
if (commandWarnings.length && !parsed.json) {
|
|
483
|
+
// eslint-disable-next-line no-console
|
|
484
|
+
console.warn(commandWarnings.map((warning) => `! ${warning}`).join("\n"));
|
|
485
|
+
}
|
|
358
486
|
const service = await WorkOnTasksService.create(workspace);
|
|
359
487
|
try {
|
|
360
488
|
const abortController = new AbortController();
|
|
@@ -382,7 +510,7 @@ export class WorkOnTasksCommand {
|
|
|
382
510
|
const onAgentChunk = parsed.agentStream !== false ? streamSink : undefined;
|
|
383
511
|
const result = await service.workOnTasks({
|
|
384
512
|
workspace,
|
|
385
|
-
projectKey:
|
|
513
|
+
projectKey: projectResolution.projectKey,
|
|
386
514
|
epicKey: parsed.epicKey,
|
|
387
515
|
storyKey: parsed.storyKey,
|
|
388
516
|
taskKeys: parsed.taskKeys.length ? parsed.taskKeys : undefined,
|
|
@@ -402,9 +530,12 @@ export class WorkOnTasksCommand {
|
|
|
402
530
|
agentAdapterOverride: parsed.agentAdapterOverride,
|
|
403
531
|
missingTestsPolicy: parsed.missingTestsPolicy,
|
|
404
532
|
allowMissingTests: parsed.allowMissingTests,
|
|
533
|
+
missingContextPolicy: parsed.missingContextPolicy,
|
|
534
|
+
executionContextPolicy: parsed.executionContextPolicy,
|
|
405
535
|
onAgentChunk,
|
|
406
536
|
abortSignal: abortController.signal,
|
|
407
537
|
});
|
|
538
|
+
const warnings = [...commandWarnings, ...result.warnings];
|
|
408
539
|
const success = result.results.filter((r) => r.status === "succeeded").length;
|
|
409
540
|
const failed = result.results.filter((r) => r.status === "failed").length;
|
|
410
541
|
const skipped = result.results.filter((r) => r.status === "skipped").length;
|
|
@@ -420,7 +551,8 @@ export class WorkOnTasksCommand {
|
|
|
420
551
|
succeeded: success,
|
|
421
552
|
failed,
|
|
422
553
|
skipped,
|
|
423
|
-
|
|
554
|
+
projectKey: projectResolution.projectKey,
|
|
555
|
+
warnings,
|
|
424
556
|
}, null, 2));
|
|
425
557
|
return;
|
|
426
558
|
}
|
|
@@ -432,9 +564,9 @@ export class WorkOnTasksCommand {
|
|
|
432
564
|
.join("\n");
|
|
433
565
|
// eslint-disable-next-line no-console
|
|
434
566
|
console.log(summary);
|
|
435
|
-
if (
|
|
567
|
+
if (warnings.length) {
|
|
436
568
|
// eslint-disable-next-line no-console
|
|
437
|
-
console.warn(
|
|
569
|
+
console.warn(warnings.map((w) => `! ${w}`).join("\n"));
|
|
438
570
|
}
|
|
439
571
|
}
|
|
440
572
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectGuidanceCommand.d.ts","sourceRoot":"","sources":["../../../src/commands/workspace/ProjectGuidanceCommand.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ProjectGuidanceCommand.d.ts","sourceRoot":"","sources":["../../../src/commands/workspace/ProjectGuidanceCommand.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,yBAAyB;IACxC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAUD,eAAO,MAAM,wBAAwB,GAAI,MAAM,MAAM,EAAE,KAAG,yBAgEzD,CAAC;AA2DF,qBAAa,sBAAsB;WACpB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAiEhD"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { WorkspaceRepository } from "@mcoda/db";
|
|
2
3
|
import { WorkspaceResolver, ensureProjectGuidance } from "@mcoda/core";
|
|
3
|
-
const USAGE = "Usage: mcoda project-guidance [--workspace <path>|--workspace-root <path>] [--force] [--json]";
|
|
4
|
+
const USAGE = "Usage: mcoda project-guidance [--workspace <path>|--workspace-root <path>] [--project <key>] [--force] [--json]";
|
|
4
5
|
const parseBooleanFlag = (value, defaultValue) => {
|
|
5
6
|
if (value === undefined)
|
|
6
7
|
return defaultValue;
|
|
@@ -25,6 +26,12 @@ export const parseProjectGuidanceArgs = (argv) => {
|
|
|
25
26
|
parsed.workspaceRoot = path.resolve(raw);
|
|
26
27
|
continue;
|
|
27
28
|
}
|
|
29
|
+
if (arg.startsWith("--project=")) {
|
|
30
|
+
const [, raw] = arg.split("=", 2);
|
|
31
|
+
if (raw)
|
|
32
|
+
parsed.projectKey = raw.trim() || undefined;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
28
35
|
if (arg.startsWith("--force=")) {
|
|
29
36
|
const [, raw] = arg.split("=", 2);
|
|
30
37
|
parsed.force = parseBooleanFlag(raw, true);
|
|
@@ -43,6 +50,12 @@ export const parseProjectGuidanceArgs = (argv) => {
|
|
|
43
50
|
i += 1;
|
|
44
51
|
}
|
|
45
52
|
break;
|
|
53
|
+
case "--project":
|
|
54
|
+
if (argv[i + 1] && !argv[i + 1].startsWith("--")) {
|
|
55
|
+
parsed.projectKey = argv[i + 1].trim() || undefined;
|
|
56
|
+
i += 1;
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
46
59
|
case "--force": {
|
|
47
60
|
const next = argv[i + 1];
|
|
48
61
|
if (next && !next.startsWith("--")) {
|
|
@@ -67,6 +80,51 @@ export const parseProjectGuidanceArgs = (argv) => {
|
|
|
67
80
|
}
|
|
68
81
|
return parsed;
|
|
69
82
|
};
|
|
83
|
+
const listWorkspaceProjects = async (workspaceRoot) => {
|
|
84
|
+
const repo = await WorkspaceRepository.create(workspaceRoot);
|
|
85
|
+
try {
|
|
86
|
+
const rows = await repo
|
|
87
|
+
.getDb()
|
|
88
|
+
.all(`SELECT key, created_at FROM projects ORDER BY created_at ASC, key ASC`);
|
|
89
|
+
return rows
|
|
90
|
+
.map((row) => ({ key: String(row.key), createdAt: row.created_at ?? null }))
|
|
91
|
+
.filter((row) => row.key.trim().length > 0);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
await repo.close();
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const pickProjectGuidanceProjectKey = (options) => {
|
|
101
|
+
const warnings = [];
|
|
102
|
+
const requestedKey = options.requestedKey?.trim() || undefined;
|
|
103
|
+
const configuredKey = options.configuredKey?.trim() || undefined;
|
|
104
|
+
const existing = options.existing ?? [];
|
|
105
|
+
const firstExisting = existing[0]?.key;
|
|
106
|
+
if (requestedKey) {
|
|
107
|
+
if (configuredKey && configuredKey !== requestedKey) {
|
|
108
|
+
warnings.push(`Using explicitly requested project key \"${requestedKey}\"; overriding configured project key \"${configuredKey}\".`);
|
|
109
|
+
}
|
|
110
|
+
if (firstExisting && requestedKey !== firstExisting) {
|
|
111
|
+
warnings.push(`Using explicitly requested project key \"${requestedKey}\"; first workspace project is \"${firstExisting}\".`);
|
|
112
|
+
}
|
|
113
|
+
return { projectKey: requestedKey, warnings };
|
|
114
|
+
}
|
|
115
|
+
if (configuredKey) {
|
|
116
|
+
if (firstExisting && configuredKey !== firstExisting) {
|
|
117
|
+
warnings.push(`Using configured project key \"${configuredKey}\" instead of first workspace project \"${firstExisting}\".`);
|
|
118
|
+
}
|
|
119
|
+
return { projectKey: configuredKey, warnings };
|
|
120
|
+
}
|
|
121
|
+
if (firstExisting) {
|
|
122
|
+
warnings.push(`No --project provided; defaulting to first workspace project \"${firstExisting}\".`);
|
|
123
|
+
return { projectKey: firstExisting, warnings };
|
|
124
|
+
}
|
|
125
|
+
warnings.push("No workspace project found; creating workspace-global project guidance.");
|
|
126
|
+
return { projectKey: undefined, warnings };
|
|
127
|
+
};
|
|
70
128
|
export class ProjectGuidanceCommand {
|
|
71
129
|
static async run(argv) {
|
|
72
130
|
const parsed = parseProjectGuidanceArgs(argv);
|
|
@@ -80,20 +138,42 @@ export class ProjectGuidanceCommand {
|
|
|
80
138
|
cwd: process.cwd(),
|
|
81
139
|
explicitWorkspace: parsed.workspaceRoot,
|
|
82
140
|
});
|
|
141
|
+
const existingProjects = parsed.projectKey ? [] : await listWorkspaceProjects(workspace.workspaceRoot);
|
|
142
|
+
const configuredKey = typeof workspace.config?.projectKey === "string" && workspace.config.projectKey.trim().length > 0
|
|
143
|
+
? workspace.config.projectKey
|
|
144
|
+
: undefined;
|
|
145
|
+
const projectResolution = pickProjectGuidanceProjectKey({
|
|
146
|
+
requestedKey: parsed.projectKey,
|
|
147
|
+
configuredKey,
|
|
148
|
+
existing: existingProjects,
|
|
149
|
+
});
|
|
83
150
|
const result = await ensureProjectGuidance(workspace.workspaceRoot, {
|
|
84
151
|
mcodaDir: workspace.mcodaDir,
|
|
85
152
|
force: parsed.force,
|
|
153
|
+
projectKey: projectResolution.projectKey,
|
|
86
154
|
});
|
|
87
155
|
if (parsed.json) {
|
|
88
156
|
// eslint-disable-next-line no-console
|
|
89
157
|
console.log(JSON.stringify({
|
|
90
158
|
workspaceRoot: workspace.workspaceRoot,
|
|
91
159
|
mcodaDir: workspace.mcodaDir,
|
|
160
|
+
projectKey: projectResolution.projectKey ?? null,
|
|
92
161
|
path: result.path,
|
|
93
162
|
status: result.status,
|
|
163
|
+
source: result.source ?? null,
|
|
164
|
+
sdsSource: result.sdsSource ?? null,
|
|
165
|
+
warnings: [...projectResolution.warnings, ...(result.warnings ?? [])],
|
|
94
166
|
}, null, 2));
|
|
95
167
|
return;
|
|
96
168
|
}
|
|
169
|
+
for (const warning of projectResolution.warnings) {
|
|
170
|
+
// eslint-disable-next-line no-console
|
|
171
|
+
console.warn(`[project-guidance] ${warning}`);
|
|
172
|
+
}
|
|
173
|
+
for (const warning of result.warnings ?? []) {
|
|
174
|
+
// eslint-disable-next-line no-console
|
|
175
|
+
console.warn(`[project-guidance] ${warning}`);
|
|
176
|
+
}
|
|
97
177
|
// eslint-disable-next-line no-console
|
|
98
178
|
console.log(`project-guidance ${result.status}: ${result.path}`);
|
|
99
179
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SetWorkspaceCommand.d.ts","sourceRoot":"","sources":["../../../src/commands/workspace/SetWorkspaceCommand.ts"],"names":[],"mappings":"AAqBA,eAAO,MAAM,qBAAqB,GAChC,MAAM,MAAM,EAAE,KACb;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAwCnF,CAAC;AAEF,KAAK,OAAO,GACR,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,IAAI,GACJ,KAAK,GACL,MAAM,GACN,SAAS,GACT,cAAc,GACd,KAAK,GACL,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"SetWorkspaceCommand.d.ts","sourceRoot":"","sources":["../../../src/commands/workspace/SetWorkspaceCommand.ts"],"names":[],"mappings":"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"}
|