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.
Files changed (27) hide show
  1. package/dist/commands/backlog/OrderTasksCommand.d.ts +2 -0
  2. package/dist/commands/backlog/OrderTasksCommand.d.ts.map +1 -1
  3. package/dist/commands/backlog/OrderTasksCommand.js +38 -2
  4. package/dist/commands/docs/DocsCommands.d.ts.map +1 -1
  5. package/dist/commands/docs/DocsCommands.js +11 -11
  6. package/dist/commands/estimate/EstimateCommands.d.ts.map +1 -1
  7. package/dist/commands/estimate/EstimateCommands.js +28 -27
  8. package/dist/commands/openapi/OpenapiCommands.d.ts +1 -0
  9. package/dist/commands/openapi/OpenapiCommands.d.ts.map +1 -1
  10. package/dist/commands/openapi/OpenapiCommands.js +11 -1
  11. package/dist/commands/planning/CreateTasksCommand.d.ts.map +1 -1
  12. package/dist/commands/planning/CreateTasksCommand.js +18 -12
  13. package/dist/commands/planning/QaTasksCommand.d.ts +14 -0
  14. package/dist/commands/planning/QaTasksCommand.d.ts.map +1 -1
  15. package/dist/commands/planning/QaTasksCommand.js +89 -7
  16. package/dist/commands/review/CodeReviewCommand.d.ts +14 -0
  17. package/dist/commands/review/CodeReviewCommand.d.ts.map +1 -1
  18. package/dist/commands/review/CodeReviewCommand.js +133 -4
  19. package/dist/commands/work/WorkOnTasksCommand.d.ts +15 -1
  20. package/dist/commands/work/WorkOnTasksCommand.d.ts.map +1 -1
  21. package/dist/commands/work/WorkOnTasksCommand.js +140 -8
  22. package/dist/commands/workspace/ProjectGuidanceCommand.d.ts +1 -0
  23. package/dist/commands/workspace/ProjectGuidanceCommand.d.ts.map +1 -1
  24. package/dist/commands/workspace/ProjectGuidanceCommand.js +81 -1
  25. package/dist/commands/workspace/SetWorkspaceCommand.d.ts.map +1 -1
  26. package/dist/commands/workspace/SetWorkspaceCommand.js +37 -1
  27. 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: parsed.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: result.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
- if (result.warnings.length) {
264
- summary.push(`Warnings: ${result.warnings.join("; ")}`);
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":"AAIA,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,CAAC;IAC7D,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,IAAI,EAAE,OAAO,CAAC;CACf;AA6ED,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,EAAE,KAAG,UAwQrD,CAAC;AAEF,qBAAa,kBAAkB;WAChB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAmHhD"}
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" || normalized === "skip_task" || normalized === "fail_task") {
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
- if (!parsed.projectKey) {
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 requires --project <PROJECT_KEY>");
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: parsed.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
- warnings: result.warnings,
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 (result.warnings.length) {
567
+ if (warnings.length) {
436
568
  // eslint-disable-next-line no-console
437
- console.warn(result.warnings.map((w) => `! ${w}`).join("\n"));
569
+ console.warn(warnings.map((w) => `! ${w}`).join("\n"));
438
570
  }
439
571
  }
440
572
  catch (error) {
@@ -1,5 +1,6 @@
1
1
  export interface ParsedProjectGuidanceArgs {
2
2
  workspaceRoot?: string;
3
+ projectKey?: string;
3
4
  force: boolean;
4
5
  json: boolean;
5
6
  help: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectGuidanceCommand.d.ts","sourceRoot":"","sources":["../../../src/commands/workspace/ProjectGuidanceCommand.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,yBAAyB;IACxC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAUD,eAAO,MAAM,wBAAwB,GAAI,MAAM,MAAM,EAAE,KAAG,yBAqDzD,CAAC;AAEF,qBAAa,sBAAsB;WACpB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CA0ChD"}
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":"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"}
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;AAUd,KAAK,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY,CAAC;AAsWvD,eAAO,MAAM,uBAAuB,GAClC,eAAe,MAAM,EACrB,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC3B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAoBvE,CAAC;AAwGF,KAAK,gBAAgB,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAuD5D,eAAO,MAAM,yBAAyB,GACpC,eAAe,MAAM,KACpB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,gBAAgB,CAAA;CAAE,GAAG,IAAI,CAIxF,CAAC;AAuMF,eAAO,MAAM,yBAAyB,GACpC,gBAAgB,MAAM,KACrB;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAM5D,CAAC;AAsIF,eAAO,MAAM,uBAAuB,GAClC,SAAS,MAAM,EACf,MAAM,aAAa,KAClB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAMpC,CAAC;AA0CF,eAAO,MAAM,qBAAqB,GAChC,SAAS,MAAM,KACd;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAIzC,CAAC;AAgCF,KAAK,aAAa,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AA6B5D,eAAO,MAAM,sBAAsB,GACjC,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAChC;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,aAAa,CAAA;CAWpE,CAAC;AA0DF,eAAO,MAAM,uBAAuB,GAClC,SAAS,MAAM,KACd;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAIzC,CAAC;AA6EF,eAAO,MAAM,0BAA0B,GACrC,SAAS,MAAM,KACd;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAO1D,CAAC;AA8BF,eAAO,MAAM,8BAA8B,GACzC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC3B;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAIzC,CAAC;AAqEF,eAAO,MAAM,sBAAsB,GACjC,SAAS,MAAM,KACd;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAO1D,CAAC;AAyEF,eAAO,MAAM,0BAA0B,GACrC,SAAS,MAAM,EACf,QAAQ,OAAO,KACd;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAO1D,CAAC;AA6CF,eAAO,MAAM,qBAAqB,GAChC,eAAe,MAAM,KACpB,OAAO,CAAC,OAAO,EAAE,CAmBnB,CAAC;AAyNF,qBAAa,mBAAmB;WACjB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CA6EhD"}