@workflow-cannon/workspace-kit 0.2.0 → 0.3.0
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/cli.js +60 -2
- package/dist/modules/documentation/index.d.ts +1 -1
- package/dist/modules/documentation/index.js +63 -38
- package/dist/modules/documentation/runtime.d.ts +5 -1
- package/dist/modules/documentation/runtime.js +87 -4
- package/dist/modules/documentation/types.d.ts +14 -0
- package/dist/modules/index.d.ts +2 -1
- package/dist/modules/index.js +1 -1
- package/dist/modules/task-engine/generator.d.ts +2 -0
- package/dist/modules/task-engine/generator.js +101 -0
- package/dist/modules/task-engine/importer.d.ts +8 -0
- package/dist/modules/task-engine/importer.js +157 -0
- package/dist/modules/task-engine/index.d.ts +7 -0
- package/dist/modules/task-engine/index.js +225 -2
- package/dist/modules/task-engine/service.d.ts +21 -0
- package/dist/modules/task-engine/service.js +105 -0
- package/dist/modules/task-engine/store.d.ts +16 -0
- package/dist/modules/task-engine/store.js +88 -0
- package/dist/modules/task-engine/suggestions.d.ts +2 -0
- package/dist/modules/task-engine/suggestions.js +51 -0
- package/dist/modules/task-engine/transitions.d.ts +23 -0
- package/dist/modules/task-engine/transitions.js +109 -0
- package/dist/modules/task-engine/types.d.ts +82 -0
- package/dist/modules/task-engine/types.js +1 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { ModuleRegistry } from "./core/module-registry.js";
|
|
6
|
+
import { ModuleCommandRouter } from "./core/module-command-router.js";
|
|
7
|
+
import { documentationModule } from "./modules/documentation/index.js";
|
|
8
|
+
import { taskEngineModule } from "./modules/task-engine/index.js";
|
|
9
|
+
import { approvalsModule } from "./modules/approvals/index.js";
|
|
10
|
+
import { planningModule } from "./modules/planning/index.js";
|
|
11
|
+
import { improvementModule } from "./modules/improvement/index.js";
|
|
5
12
|
const EXIT_SUCCESS = 0;
|
|
6
13
|
const EXIT_VALIDATION_FAILURE = 1;
|
|
7
14
|
const EXIT_USAGE_ERROR = 2;
|
|
@@ -305,7 +312,7 @@ export async function runCli(args, options = {}) {
|
|
|
305
312
|
const writeError = options.writeError ?? console.error;
|
|
306
313
|
const [command] = args;
|
|
307
314
|
if (!command) {
|
|
308
|
-
writeError("Usage: workspace-kit <init|doctor|check|upgrade>");
|
|
315
|
+
writeError("Usage: workspace-kit <init|doctor|check|upgrade|drift-check|run>");
|
|
309
316
|
return EXIT_USAGE_ERROR;
|
|
310
317
|
}
|
|
311
318
|
if (command === "init") {
|
|
@@ -525,8 +532,59 @@ export async function runCli(args, options = {}) {
|
|
|
525
532
|
}
|
|
526
533
|
return EXIT_SUCCESS;
|
|
527
534
|
}
|
|
535
|
+
if (command === "run") {
|
|
536
|
+
const allModules = [
|
|
537
|
+
documentationModule,
|
|
538
|
+
taskEngineModule,
|
|
539
|
+
approvalsModule,
|
|
540
|
+
planningModule,
|
|
541
|
+
improvementModule
|
|
542
|
+
];
|
|
543
|
+
const registry = new ModuleRegistry(allModules);
|
|
544
|
+
const router = new ModuleCommandRouter(registry);
|
|
545
|
+
const subcommand = args[1];
|
|
546
|
+
if (!subcommand) {
|
|
547
|
+
const commands = router.listCommands();
|
|
548
|
+
writeLine("Available module commands:");
|
|
549
|
+
for (const cmd of commands) {
|
|
550
|
+
const desc = cmd.description ? ` — ${cmd.description}` : "";
|
|
551
|
+
writeLine(` ${cmd.name} (${cmd.moduleId})${desc}`);
|
|
552
|
+
}
|
|
553
|
+
writeLine("");
|
|
554
|
+
writeLine("Usage: workspace-kit run <command> [json-args]");
|
|
555
|
+
return EXIT_SUCCESS;
|
|
556
|
+
}
|
|
557
|
+
let commandArgs = {};
|
|
558
|
+
if (args[2]) {
|
|
559
|
+
try {
|
|
560
|
+
const parsed = JSON.parse(args[2]);
|
|
561
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
562
|
+
commandArgs = parsed;
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
writeError("Command args must be a JSON object.");
|
|
566
|
+
return EXIT_USAGE_ERROR;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
catch {
|
|
570
|
+
writeError(`Invalid JSON args: ${args[2]}`);
|
|
571
|
+
return EXIT_USAGE_ERROR;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
const ctx = { runtimeVersion: "0.1", workspacePath: cwd };
|
|
575
|
+
try {
|
|
576
|
+
const result = await router.execute(subcommand, commandArgs, ctx);
|
|
577
|
+
writeLine(JSON.stringify(result, null, 2));
|
|
578
|
+
return result.ok ? EXIT_SUCCESS : EXIT_VALIDATION_FAILURE;
|
|
579
|
+
}
|
|
580
|
+
catch (error) {
|
|
581
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
582
|
+
writeError(`Module command failed: ${message}`);
|
|
583
|
+
return EXIT_INTERNAL_ERROR;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
528
586
|
if (command !== "doctor") {
|
|
529
|
-
writeError(`Unknown command '${command}'. Supported commands: init, doctor, check, upgrade, drift-check.`);
|
|
587
|
+
writeError(`Unknown command '${command}'. Supported commands: init, doctor, check, upgrade, drift-check, run.`);
|
|
530
588
|
return EXIT_USAGE_ERROR;
|
|
531
589
|
}
|
|
532
590
|
const issues = [];
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { WorkflowModule } from "../../contracts/module-contract.js";
|
|
2
|
-
export type { DocumentationConflict, DocumentationGenerateOptions, DocumentationGenerateResult, DocumentationGenerationEvidence, DocumentationValidationIssue } from "./types.js";
|
|
2
|
+
export type { DocumentationBatchResult, DocumentationConflict, DocumentationGenerateOptions, DocumentationGenerateResult, DocumentationGenerationEvidence, DocumentationValidationIssue } from "./types.js";
|
|
3
3
|
export declare const documentationModule: WorkflowModule;
|
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
import { generateDocument } from "./runtime.js";
|
|
1
|
+
import { generateDocument, generateAllDocuments } from "./runtime.js";
|
|
2
|
+
function parseOptions(raw) {
|
|
3
|
+
return {
|
|
4
|
+
dryRun: typeof raw.dryRun === "boolean" ? raw.dryRun : undefined,
|
|
5
|
+
overwrite: typeof raw.overwrite === "boolean" ? raw.overwrite : undefined,
|
|
6
|
+
overwriteAi: typeof raw.overwriteAi === "boolean" ? raw.overwriteAi : undefined,
|
|
7
|
+
overwriteHuman: typeof raw.overwriteHuman === "boolean" ? raw.overwriteHuman : undefined,
|
|
8
|
+
strict: typeof raw.strict === "boolean" ? raw.strict : undefined,
|
|
9
|
+
maxValidationAttempts: typeof raw.maxValidationAttempts === "number" ? raw.maxValidationAttempts : undefined,
|
|
10
|
+
allowWithoutTemplate: typeof raw.allowWithoutTemplate === "boolean" ? raw.allowWithoutTemplate : undefined,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
2
13
|
export const documentationModule = {
|
|
3
14
|
registration: {
|
|
4
15
|
id: "documentation",
|
|
5
|
-
version: "0.
|
|
16
|
+
version: "0.2.0",
|
|
6
17
|
contractVersion: "1",
|
|
7
18
|
capabilities: ["documentation"],
|
|
8
19
|
dependsOn: [],
|
|
@@ -23,51 +34,65 @@ export const documentationModule = {
|
|
|
23
34
|
{
|
|
24
35
|
name: "document-project",
|
|
25
36
|
file: "document-project.md",
|
|
26
|
-
description: "Generate
|
|
37
|
+
description: "Generate all project docs from templates to .ai and docs/maintainers surfaces."
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "generate-document",
|
|
41
|
+
file: "generate-document.md",
|
|
42
|
+
description: "Generate a single document by type for .ai and docs/maintainers surfaces."
|
|
27
43
|
}
|
|
28
44
|
]
|
|
29
45
|
}
|
|
30
46
|
},
|
|
31
47
|
async onCommand(command, ctx) {
|
|
32
|
-
if (command.name !== "document-project" && command.name !== "generate-document") {
|
|
33
|
-
return {
|
|
34
|
-
ok: false,
|
|
35
|
-
code: "unsupported-command",
|
|
36
|
-
message: `Documentation module does not support command '${command.name}'`
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
48
|
const args = command.args ?? {};
|
|
40
49
|
const rawOptions = typeof args.options === "object" && args.options !== null
|
|
41
50
|
? args.options
|
|
42
|
-
:
|
|
43
|
-
const options = rawOptions
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
: {};
|
|
52
|
+
const options = parseOptions(rawOptions);
|
|
53
|
+
if (command.name === "document-project") {
|
|
54
|
+
const batchResult = await generateAllDocuments({ options }, ctx);
|
|
55
|
+
return {
|
|
56
|
+
ok: batchResult.ok,
|
|
57
|
+
code: batchResult.ok ? "documented-project" : "documentation-batch-failed",
|
|
58
|
+
message: batchResult.ok
|
|
59
|
+
? `Generated ${batchResult.summary.succeeded} documents (${batchResult.summary.skipped} skipped)`
|
|
60
|
+
: `Batch failed: ${batchResult.summary.failed} of ${batchResult.summary.total} documents failed`,
|
|
61
|
+
data: {
|
|
62
|
+
summary: batchResult.summary,
|
|
63
|
+
results: batchResult.results.map((r) => ({
|
|
64
|
+
documentType: r.evidence.documentType,
|
|
65
|
+
ok: r.ok,
|
|
66
|
+
aiOutputPath: r.aiOutputPath,
|
|
67
|
+
humanOutputPath: r.humanOutputPath,
|
|
68
|
+
filesWritten: r.evidence.filesWritten,
|
|
69
|
+
filesSkipped: r.evidence.filesSkipped,
|
|
70
|
+
}))
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (command.name === "generate-document") {
|
|
75
|
+
const result = await generateDocument({
|
|
76
|
+
documentType: typeof args.documentType === "string" ? args.documentType : undefined,
|
|
77
|
+
options
|
|
78
|
+
}, ctx);
|
|
79
|
+
return {
|
|
80
|
+
ok: result.ok,
|
|
81
|
+
code: result.ok ? "generated-document" : "generation-failed",
|
|
82
|
+
message: result.ok
|
|
83
|
+
? `Generated document '${args.documentType ?? "unknown"}'`
|
|
84
|
+
: `Failed to generate document '${args.documentType ?? "unknown"}'`,
|
|
85
|
+
data: {
|
|
86
|
+
aiOutputPath: result.aiOutputPath,
|
|
87
|
+
humanOutputPath: result.humanOutputPath,
|
|
88
|
+
evidence: result.evidence
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
60
92
|
return {
|
|
61
|
-
ok:
|
|
62
|
-
code:
|
|
63
|
-
message:
|
|
64
|
-
? `Generated document '${args.documentType ?? "unknown"}'`
|
|
65
|
-
: `Failed to generate document '${args.documentType ?? "unknown"}'`,
|
|
66
|
-
data: {
|
|
67
|
-
aiOutputPath: result.aiOutputPath,
|
|
68
|
-
humanOutputPath: result.humanOutputPath,
|
|
69
|
-
evidence: result.evidence
|
|
70
|
-
}
|
|
93
|
+
ok: false,
|
|
94
|
+
code: "unsupported-command",
|
|
95
|
+
message: `Documentation module does not support command '${command.name}'`
|
|
71
96
|
};
|
|
72
97
|
}
|
|
73
98
|
};
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import type { DocumentationGenerateOptions, DocumentationGenerateResult } from "./types.js";
|
|
1
|
+
import type { DocumentationBatchResult, DocumentationGenerateOptions, DocumentationGenerateResult } from "./types.js";
|
|
2
2
|
import type { ModuleLifecycleContext } from "../../contracts/module-contract.js";
|
|
3
3
|
type GenerateDocumentArgs = {
|
|
4
4
|
documentType?: string;
|
|
5
5
|
options?: DocumentationGenerateOptions;
|
|
6
6
|
};
|
|
7
7
|
export declare function generateDocument(args: GenerateDocumentArgs, ctx: ModuleLifecycleContext): Promise<DocumentationGenerateResult>;
|
|
8
|
+
type GenerateAllDocumentsArgs = {
|
|
9
|
+
options?: DocumentationGenerateOptions;
|
|
10
|
+
};
|
|
11
|
+
export declare function generateAllDocuments(args: GenerateAllDocumentsArgs, ctx: ModuleLifecycleContext): Promise<DocumentationBatchResult>;
|
|
8
12
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { resolve, sep } from "node:path";
|
|
4
|
+
import { readdir } from "node:fs/promises";
|
|
4
5
|
function isPathWithinRoot(path, root) {
|
|
5
6
|
return path === root || path.startsWith(`${root}${sep}`);
|
|
6
7
|
}
|
|
@@ -93,6 +94,7 @@ export async function generateDocument(args, ctx) {
|
|
|
93
94
|
documentType: "unknown",
|
|
94
95
|
filesRead: [],
|
|
95
96
|
filesWritten: [],
|
|
97
|
+
filesSkipped: [],
|
|
96
98
|
validationIssues: [
|
|
97
99
|
{
|
|
98
100
|
check: "template-resolution",
|
|
@@ -110,6 +112,7 @@ export async function generateDocument(args, ctx) {
|
|
|
110
112
|
const config = await loadRuntimeConfig(ctx.workspacePath);
|
|
111
113
|
const filesRead = [];
|
|
112
114
|
const filesWritten = [];
|
|
115
|
+
const filesSkipped = [];
|
|
113
116
|
const validationIssues = [];
|
|
114
117
|
const conflicts = [];
|
|
115
118
|
const aiRoot = resolve(ctx.workspacePath, config.aiRoot.replace(/^\//, ""));
|
|
@@ -124,6 +127,7 @@ export async function generateDocument(args, ctx) {
|
|
|
124
127
|
documentType,
|
|
125
128
|
filesRead,
|
|
126
129
|
filesWritten,
|
|
130
|
+
filesSkipped,
|
|
127
131
|
validationIssues: [
|
|
128
132
|
{
|
|
129
133
|
check: "write-boundary",
|
|
@@ -156,6 +160,7 @@ export async function generateDocument(args, ctx) {
|
|
|
156
160
|
documentType,
|
|
157
161
|
filesRead,
|
|
158
162
|
filesWritten,
|
|
163
|
+
filesSkipped,
|
|
159
164
|
validationIssues,
|
|
160
165
|
conflicts,
|
|
161
166
|
attemptsUsed: 0,
|
|
@@ -190,6 +195,7 @@ export async function generateDocument(args, ctx) {
|
|
|
190
195
|
documentType,
|
|
191
196
|
filesRead,
|
|
192
197
|
filesWritten,
|
|
198
|
+
filesSkipped,
|
|
193
199
|
validationIssues,
|
|
194
200
|
conflicts,
|
|
195
201
|
attemptsUsed,
|
|
@@ -218,6 +224,7 @@ export async function generateDocument(args, ctx) {
|
|
|
218
224
|
documentType,
|
|
219
225
|
filesRead,
|
|
220
226
|
filesWritten,
|
|
227
|
+
filesSkipped,
|
|
221
228
|
validationIssues,
|
|
222
229
|
conflicts,
|
|
223
230
|
attemptsUsed,
|
|
@@ -233,6 +240,7 @@ export async function generateDocument(args, ctx) {
|
|
|
233
240
|
documentType,
|
|
234
241
|
filesRead,
|
|
235
242
|
filesWritten,
|
|
243
|
+
filesSkipped,
|
|
236
244
|
validationIssues,
|
|
237
245
|
conflicts,
|
|
238
246
|
attemptsUsed,
|
|
@@ -241,13 +249,18 @@ export async function generateDocument(args, ctx) {
|
|
|
241
249
|
};
|
|
242
250
|
}
|
|
243
251
|
if (!options.dryRun) {
|
|
244
|
-
|
|
252
|
+
const canOverwriteAi = options.overwriteAi ?? options.overwrite ?? true;
|
|
253
|
+
const canOverwriteHuman = options.overwriteHuman ?? options.overwrite ?? true;
|
|
254
|
+
const aiExists = existsSync(aiOutputPath);
|
|
255
|
+
const humanExists = existsSync(humanOutputPath);
|
|
256
|
+
if ((!canOverwriteAi && aiExists) && (!canOverwriteHuman && humanExists)) {
|
|
245
257
|
return {
|
|
246
258
|
ok: false,
|
|
247
259
|
evidence: {
|
|
248
260
|
documentType,
|
|
249
261
|
filesRead,
|
|
250
262
|
filesWritten,
|
|
263
|
+
filesSkipped: [aiOutputPath, humanOutputPath],
|
|
251
264
|
validationIssues: [
|
|
252
265
|
...validationIssues,
|
|
253
266
|
{
|
|
@@ -264,9 +277,20 @@ export async function generateDocument(args, ctx) {
|
|
|
264
277
|
}
|
|
265
278
|
await mkdir(aiRoot, { recursive: true });
|
|
266
279
|
await mkdir(humanRoot, { recursive: true });
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
280
|
+
if (canOverwriteAi || !aiExists) {
|
|
281
|
+
await writeFile(aiOutputPath, `${aiOutput}\n`, "utf8");
|
|
282
|
+
filesWritten.push(aiOutputPath);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
filesSkipped.push(aiOutputPath);
|
|
286
|
+
}
|
|
287
|
+
if (canOverwriteHuman || !humanExists) {
|
|
288
|
+
await writeFile(humanOutputPath, `${humanOutput}\n`, "utf8");
|
|
289
|
+
filesWritten.push(humanOutputPath);
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
filesSkipped.push(humanOutputPath);
|
|
293
|
+
}
|
|
270
294
|
}
|
|
271
295
|
return {
|
|
272
296
|
ok: true,
|
|
@@ -276,6 +300,7 @@ export async function generateDocument(args, ctx) {
|
|
|
276
300
|
documentType,
|
|
277
301
|
filesRead,
|
|
278
302
|
filesWritten,
|
|
303
|
+
filesSkipped,
|
|
279
304
|
validationIssues,
|
|
280
305
|
conflicts,
|
|
281
306
|
attemptsUsed,
|
|
@@ -283,3 +308,61 @@ export async function generateDocument(args, ctx) {
|
|
|
283
308
|
}
|
|
284
309
|
};
|
|
285
310
|
}
|
|
311
|
+
export async function generateAllDocuments(args, ctx) {
|
|
312
|
+
const config = await loadRuntimeConfig(ctx.workspacePath);
|
|
313
|
+
const templatesDir = resolve(ctx.workspacePath, config.templatesRoot);
|
|
314
|
+
let templateFiles = [];
|
|
315
|
+
try {
|
|
316
|
+
const entries = await readdir(templatesDir);
|
|
317
|
+
templateFiles = entries.filter((f) => f.endsWith(".md")).sort();
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
return {
|
|
321
|
+
ok: false,
|
|
322
|
+
results: [],
|
|
323
|
+
summary: {
|
|
324
|
+
total: 0,
|
|
325
|
+
succeeded: 0,
|
|
326
|
+
failed: 1,
|
|
327
|
+
skipped: 0,
|
|
328
|
+
timestamp: new Date().toISOString()
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
const results = [];
|
|
333
|
+
let succeeded = 0;
|
|
334
|
+
let failed = 0;
|
|
335
|
+
let skipped = 0;
|
|
336
|
+
const batchOptions = {
|
|
337
|
+
...args.options,
|
|
338
|
+
overwriteAi: args.options?.overwriteAi ?? false,
|
|
339
|
+
overwriteHuman: args.options?.overwriteHuman ?? true,
|
|
340
|
+
strict: args.options?.strict ?? false,
|
|
341
|
+
};
|
|
342
|
+
for (const templateFile of templateFiles) {
|
|
343
|
+
const result = await generateDocument({ documentType: templateFile, options: batchOptions }, ctx);
|
|
344
|
+
results.push(result);
|
|
345
|
+
if (result.ok) {
|
|
346
|
+
if (result.evidence.filesWritten.length > 0) {
|
|
347
|
+
succeeded++;
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
skipped++;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
failed++;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
ok: failed === 0,
|
|
359
|
+
results,
|
|
360
|
+
summary: {
|
|
361
|
+
total: templateFiles.length,
|
|
362
|
+
succeeded,
|
|
363
|
+
failed,
|
|
364
|
+
skipped,
|
|
365
|
+
timestamp: new Date().toISOString()
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export type DocumentationGenerateOptions = {
|
|
2
2
|
dryRun?: boolean;
|
|
3
3
|
overwrite?: boolean;
|
|
4
|
+
overwriteAi?: boolean;
|
|
5
|
+
overwriteHuman?: boolean;
|
|
4
6
|
strict?: boolean;
|
|
5
7
|
maxValidationAttempts?: number;
|
|
6
8
|
allowWithoutTemplate?: boolean;
|
|
@@ -19,6 +21,7 @@ export type DocumentationGenerationEvidence = {
|
|
|
19
21
|
documentType: string;
|
|
20
22
|
filesRead: string[];
|
|
21
23
|
filesWritten: string[];
|
|
24
|
+
filesSkipped: string[];
|
|
22
25
|
validationIssues: DocumentationValidationIssue[];
|
|
23
26
|
conflicts: DocumentationConflict[];
|
|
24
27
|
attemptsUsed: number;
|
|
@@ -30,3 +33,14 @@ export type DocumentationGenerateResult = {
|
|
|
30
33
|
humanOutputPath?: string;
|
|
31
34
|
evidence: DocumentationGenerationEvidence;
|
|
32
35
|
};
|
|
36
|
+
export type DocumentationBatchResult = {
|
|
37
|
+
ok: boolean;
|
|
38
|
+
results: DocumentationGenerateResult[];
|
|
39
|
+
summary: {
|
|
40
|
+
total: number;
|
|
41
|
+
succeeded: number;
|
|
42
|
+
failed: number;
|
|
43
|
+
skipped: number;
|
|
44
|
+
timestamp: string;
|
|
45
|
+
};
|
|
46
|
+
};
|
package/dist/modules/index.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ export { documentationModule } from "./documentation/index.js";
|
|
|
3
3
|
export type { DocumentationConflict, DocumentationGenerateOptions, DocumentationGenerateResult, DocumentationGenerationEvidence, DocumentationValidationIssue } from "./documentation/types.js";
|
|
4
4
|
export { improvementModule } from "./improvement/index.js";
|
|
5
5
|
export { planningModule } from "./planning/index.js";
|
|
6
|
-
export { taskEngineModule } from "./task-engine/index.js";
|
|
6
|
+
export { taskEngineModule, TaskStore, TransitionService, TaskEngineError, TransitionValidator, isTransitionAllowed, getTransitionAction, resolveTargetState, getAllowedTransitionsFrom, stateValidityGuard, dependencyCheckGuard, generateTasksMd, importTasksFromMarkdown, getNextActions } from "./task-engine/index.js";
|
|
7
|
+
export type { TaskEntity, TaskStatus, TaskPriority, TaskStoreDocument, TransitionEvidence, TransitionGuard, TransitionContext, GuardResult, TaskEngineErrorCode, TaskAdapter, TaskAdapterCapability, NextActionSuggestion, BlockingAnalysisEntry } from "./task-engine/index.js";
|
package/dist/modules/index.js
CHANGED
|
@@ -2,4 +2,4 @@ export { approvalsModule } from "./approvals/index.js";
|
|
|
2
2
|
export { documentationModule } from "./documentation/index.js";
|
|
3
3
|
export { improvementModule } from "./improvement/index.js";
|
|
4
4
|
export { planningModule } from "./planning/index.js";
|
|
5
|
-
export { taskEngineModule } from "./task-engine/index.js";
|
|
5
|
+
export { taskEngineModule, TaskStore, TransitionService, TaskEngineError, TransitionValidator, isTransitionAllowed, getTransitionAction, resolveTargetState, getAllowedTransitionsFrom, stateValidityGuard, dependencyCheckGuard, generateTasksMd, importTasksFromMarkdown, getNextActions } from "./task-engine/index.js";
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const STATUS_MARKERS = {
|
|
2
|
+
proposed: "[p]",
|
|
3
|
+
ready: "[ ]",
|
|
4
|
+
in_progress: "[~]",
|
|
5
|
+
blocked: "[!]",
|
|
6
|
+
completed: "[x]",
|
|
7
|
+
cancelled: "[-]"
|
|
8
|
+
};
|
|
9
|
+
function groupByPhase(tasks) {
|
|
10
|
+
const groups = new Map();
|
|
11
|
+
for (const task of tasks) {
|
|
12
|
+
const phase = task.phase ?? "Uncategorized";
|
|
13
|
+
const group = groups.get(phase) ?? [];
|
|
14
|
+
group.push(task);
|
|
15
|
+
groups.set(phase, group);
|
|
16
|
+
}
|
|
17
|
+
return groups;
|
|
18
|
+
}
|
|
19
|
+
function buildReadyQueueLine(tasks) {
|
|
20
|
+
const ready = tasks
|
|
21
|
+
.filter((t) => t.status === "ready")
|
|
22
|
+
.sort((a, b) => {
|
|
23
|
+
const pa = a.priority ?? "P9";
|
|
24
|
+
const pb = b.priority ?? "P9";
|
|
25
|
+
return pa.localeCompare(pb);
|
|
26
|
+
});
|
|
27
|
+
if (ready.length === 0)
|
|
28
|
+
return "- Ready queue: _(empty)_";
|
|
29
|
+
return `- Ready queue: ${ready.map((t) => `\`${t.id}\``).join(", ")}`;
|
|
30
|
+
}
|
|
31
|
+
function buildCurrentPhase(tasks) {
|
|
32
|
+
const inProgress = tasks.filter((t) => t.status === "in_progress");
|
|
33
|
+
const ready = tasks.filter((t) => t.status === "ready");
|
|
34
|
+
const active = [...inProgress, ...ready];
|
|
35
|
+
if (active.length === 0)
|
|
36
|
+
return "- Current phase in execution: _(no active tasks)_";
|
|
37
|
+
const phases = [...new Set(active.map((t) => t.phase).filter(Boolean))];
|
|
38
|
+
if (phases.length === 0)
|
|
39
|
+
return "- Current phase in execution: _(unknown)_";
|
|
40
|
+
return `- Current phase in execution: _${phases[0]}_`;
|
|
41
|
+
}
|
|
42
|
+
function renderTask(task) {
|
|
43
|
+
const marker = STATUS_MARKERS[task.status] ?? "[ ]";
|
|
44
|
+
const lines = [];
|
|
45
|
+
lines.push(`### ${marker} ${task.id} ${task.title}`);
|
|
46
|
+
if (task.priority) {
|
|
47
|
+
lines.push(`- Priority: ${task.priority}`);
|
|
48
|
+
}
|
|
49
|
+
if (task.approach) {
|
|
50
|
+
lines.push(`- Approach: ${task.approach}`);
|
|
51
|
+
}
|
|
52
|
+
const deps = task.dependsOn ?? [];
|
|
53
|
+
lines.push(`- Depends on: ${deps.length > 0 ? deps.map((d) => `\`${d}\``).join(", ") : "none"}`);
|
|
54
|
+
const unblocks = task.unblocks ?? [];
|
|
55
|
+
if (unblocks.length > 0) {
|
|
56
|
+
lines.push(`- Unblocks: ${unblocks.map((u) => `\`${u}\``).join(", ")}`);
|
|
57
|
+
}
|
|
58
|
+
if (task.technicalScope && task.technicalScope.length > 0) {
|
|
59
|
+
lines.push("- Technical scope:");
|
|
60
|
+
for (const item of task.technicalScope) {
|
|
61
|
+
lines.push(` - ${item}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (task.acceptanceCriteria && task.acceptanceCriteria.length > 0) {
|
|
65
|
+
lines.push("- Acceptance criteria:");
|
|
66
|
+
for (const item of task.acceptanceCriteria) {
|
|
67
|
+
lines.push(` - ${item}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return lines.join("\n");
|
|
71
|
+
}
|
|
72
|
+
export function generateTasksMd(tasks) {
|
|
73
|
+
const lines = [];
|
|
74
|
+
lines.push("# Workflow Cannon Tasks");
|
|
75
|
+
lines.push("");
|
|
76
|
+
lines.push("> This file is generated by the Task Engine. Do not edit manually.");
|
|
77
|
+
lines.push("");
|
|
78
|
+
lines.push("Status markers:");
|
|
79
|
+
lines.push("- `[p]` proposed");
|
|
80
|
+
lines.push("- `[ ]` ready");
|
|
81
|
+
lines.push("- `[~]` in progress");
|
|
82
|
+
lines.push("- `[!]` blocked");
|
|
83
|
+
lines.push("- `[x]` completed");
|
|
84
|
+
lines.push("- `[-]` cancelled");
|
|
85
|
+
lines.push("");
|
|
86
|
+
lines.push("## Current execution state");
|
|
87
|
+
lines.push("");
|
|
88
|
+
lines.push(buildCurrentPhase(tasks));
|
|
89
|
+
lines.push(buildReadyQueueLine(tasks));
|
|
90
|
+
lines.push("");
|
|
91
|
+
const phaseGroups = groupByPhase(tasks);
|
|
92
|
+
for (const [phase, phaseTasks] of phaseGroups) {
|
|
93
|
+
lines.push(`## ${phase}`);
|
|
94
|
+
lines.push("");
|
|
95
|
+
for (const task of phaseTasks) {
|
|
96
|
+
lines.push(renderTask(task));
|
|
97
|
+
lines.push("");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return lines.join("\n");
|
|
101
|
+
}
|