workflow-agent-cli 2.15.0 → 2.16.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Workflow Agent Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/cli/index.js CHANGED
@@ -58,8 +58,8 @@ var adapters = {
58
58
  name: "Next.js App Router",
59
59
  description: "Next.js 13+ with app directory",
60
60
  detect: async () => {
61
- const fs2 = await import("fs");
62
- return fs2.existsSync("app") && fs2.existsSync("next.config.ts") || fs2.existsSync("next.config.js");
61
+ const fs3 = await import("fs");
62
+ return fs3.existsSync("app") && fs3.existsSync("next.config.ts") || fs3.existsSync("next.config.js");
63
63
  },
64
64
  paths: {
65
65
  actions: "app/actions",
@@ -75,8 +75,8 @@ var adapters = {
75
75
  name: "Next.js Pages Router",
76
76
  description: "Next.js with pages directory",
77
77
  detect: async () => {
78
- const fs2 = await import("fs");
79
- return fs2.existsSync("pages") && (fs2.existsSync("next.config.ts") || fs2.existsSync("next.config.js"));
78
+ const fs3 = await import("fs");
79
+ return fs3.existsSync("pages") && (fs3.existsSync("next.config.ts") || fs3.existsSync("next.config.js"));
80
80
  },
81
81
  paths: {
82
82
  components: "components",
@@ -91,8 +91,8 @@ var adapters = {
91
91
  name: "Vite + React",
92
92
  description: "Vite-powered React application",
93
93
  detect: async () => {
94
- const fs2 = await import("fs");
95
- return fs2.existsSync("vite.config.ts") || fs2.existsSync("vite.config.js");
94
+ const fs3 = await import("fs");
95
+ return fs3.existsSync("vite.config.ts") || fs3.existsSync("vite.config.js");
96
96
  },
97
97
  paths: {
98
98
  components: "src/components",
@@ -107,8 +107,8 @@ var adapters = {
107
107
  name: "Remix",
108
108
  description: "Remix full-stack framework",
109
109
  detect: async () => {
110
- const fs2 = await import("fs");
111
- return fs2.existsSync("app/routes") && (fs2.existsSync("remix.config.js") || fs2.existsSync("package.json"));
110
+ const fs3 = await import("fs");
111
+ return fs3.existsSync("app/routes") && (fs3.existsSync("remix.config.js") || fs3.existsSync("package.json"));
112
112
  },
113
113
  paths: {
114
114
  components: "app/components",
@@ -122,8 +122,8 @@ var adapters = {
122
122
  name: "Astro",
123
123
  description: "Astro static site framework",
124
124
  detect: async () => {
125
- const fs2 = await import("fs");
126
- return fs2.existsSync("astro.config.mjs") || fs2.existsSync("astro.config.ts");
125
+ const fs3 = await import("fs");
126
+ return fs3.existsSync("astro.config.mjs") || fs3.existsSync("astro.config.ts");
127
127
  },
128
128
  paths: {
129
129
  components: "src/components",
@@ -137,8 +137,8 @@ var adapters = {
137
137
  name: "SvelteKit",
138
138
  description: "SvelteKit full-stack framework",
139
139
  detect: async () => {
140
- const fs2 = await import("fs");
141
- return fs2.existsSync("svelte.config.js") || fs2.existsSync("src/routes");
140
+ const fs3 = await import("fs");
141
+ return fs3.existsSync("svelte.config.js") || fs3.existsSync("src/routes");
142
142
  },
143
143
  paths: {
144
144
  components: "src/lib/components",
@@ -1817,11 +1817,11 @@ async function recordSuccessfulFixes(cwd, result) {
1817
1817
  let framework = "unknown";
1818
1818
  let frameworkVersion = "0.0.0";
1819
1819
  try {
1820
- const fs2 = await import("fs");
1821
- const path3 = await import("path");
1822
- const packageJsonPath = path3.join(cwd, "package.json");
1820
+ const fs3 = await import("fs");
1821
+ const path4 = await import("path");
1822
+ const packageJsonPath = path4.join(cwd, "package.json");
1823
1823
  const packageJson = JSON.parse(
1824
- await fs2.promises.readFile(packageJsonPath, "utf-8")
1824
+ await fs3.promises.readFile(packageJsonPath, "utf-8")
1825
1825
  );
1826
1826
  const deps = {
1827
1827
  ...packageJson.dependencies,
@@ -5226,11 +5226,17 @@ async function statusHooksAction(cwd) {
5226
5226
  // src/cli/commands/learn.ts
5227
5227
  import chalk16 from "chalk";
5228
5228
  import * as p11 from "@clack/prompts";
5229
+ import * as fs2 from "fs";
5230
+ import * as path2 from "path";
5229
5231
  import {
5230
5232
  PatternStore as PatternStore2,
5231
5233
  ContributorManager as ContributorManager2,
5232
5234
  PatternAnonymizer,
5233
- TelemetryCollector as TelemetryCollector2
5235
+ TelemetryCollector as TelemetryCollector2,
5236
+ FixPatternSchema,
5237
+ BlueprintSchema,
5238
+ SolutionPatternSchema,
5239
+ createDefaultMetrics
5234
5240
  } from "@hawkinside_out/workflow-improvement-tracker";
5235
5241
 
5236
5242
  // src/sync/registry-client.ts
@@ -5335,8 +5341,8 @@ var RegistryClient = class {
5335
5341
  /**
5336
5342
  * Make an HTTP request to the registry
5337
5343
  */
5338
- async request(path3, options) {
5339
- const url = `${this.baseUrl}${path3}`;
5344
+ async request(path4, options) {
5345
+ const url = `${this.baseUrl}${path4}`;
5340
5346
  let lastError = null;
5341
5347
  for (let attempt = 1; attempt <= this.retries; attempt++) {
5342
5348
  try {
@@ -6434,11 +6440,294 @@ async function learnStatsCommand() {
6434
6440
  }
6435
6441
  console.log("");
6436
6442
  }
6443
+ async function learnValidateCommand(options) {
6444
+ const cwd = getWorkspacePath();
6445
+ const patternsPath = path2.join(cwd, ".workflow", "patterns");
6446
+ const patternType = options.type ?? "all";
6447
+ const shouldFix = options.fix ?? false;
6448
+ const verbose = options.verbose ?? false;
6449
+ const specificFile = options.file;
6450
+ console.log(chalk16.cyan("\n\u{1F50D} Validating Pattern Files\n"));
6451
+ const results = [];
6452
+ if (specificFile) {
6453
+ const fileResult = await validateSingleFile(specificFile, verbose);
6454
+ if (fileResult) {
6455
+ results.push(fileResult);
6456
+ }
6457
+ } else {
6458
+ if (patternType === "all" || patternType === "fix") {
6459
+ const fixesPath = path2.join(patternsPath, "fixes");
6460
+ const fixResults = await validatePatternDirectory(
6461
+ fixesPath,
6462
+ "fix",
6463
+ FixPatternSchema,
6464
+ verbose
6465
+ );
6466
+ results.push(...fixResults);
6467
+ }
6468
+ if (patternType === "all" || patternType === "blueprint") {
6469
+ const blueprintsPath = path2.join(patternsPath, "blueprints");
6470
+ const bpResults = await validatePatternDirectory(
6471
+ blueprintsPath,
6472
+ "blueprint",
6473
+ BlueprintSchema,
6474
+ verbose
6475
+ );
6476
+ results.push(...bpResults);
6477
+ }
6478
+ if (patternType === "all" || patternType === "solution") {
6479
+ const solutionsPath = path2.join(patternsPath, "solutions");
6480
+ const solResults = await validatePatternDirectory(
6481
+ solutionsPath,
6482
+ "solution",
6483
+ SolutionPatternSchema,
6484
+ verbose
6485
+ );
6486
+ results.push(...solResults);
6487
+ }
6488
+ }
6489
+ const valid = results.filter((r) => r.valid);
6490
+ const invalid = results.filter((r) => !r.valid);
6491
+ const fixable = invalid.filter((r) => r.fixable);
6492
+ console.log(chalk16.dim("\u2501".repeat(50)));
6493
+ console.log(chalk16.bold(`
6494
+ \u{1F4CA} Validation Summary
6495
+ `));
6496
+ console.log(chalk16.green(` \u2713 Valid: ${valid.length}`));
6497
+ if (invalid.length > 0) {
6498
+ console.log(chalk16.red(` \u2717 Invalid: ${invalid.length}`));
6499
+ console.log(chalk16.yellow(` \u{1F527} Auto-fixable: ${fixable.length}`));
6500
+ }
6501
+ if (invalid.length > 0) {
6502
+ console.log(chalk16.red(`
6503
+ \u274C Invalid Patterns:
6504
+ `));
6505
+ for (const result of invalid) {
6506
+ console.log(chalk16.white(` ${result.file} (${result.type})`));
6507
+ for (const err of result.errors.slice(0, 5)) {
6508
+ console.log(chalk16.dim(` - ${err}`));
6509
+ }
6510
+ if (result.errors.length > 5) {
6511
+ console.log(chalk16.dim(` ... and ${result.errors.length - 5} more`));
6512
+ }
6513
+ if (result.fixable) {
6514
+ console.log(chalk16.yellow(` \u2192 Can be auto-fixed`));
6515
+ }
6516
+ }
6517
+ }
6518
+ if (shouldFix && fixable.length > 0) {
6519
+ console.log(chalk16.cyan(`
6520
+ \u{1F527} Auto-fixing ${fixable.length} patterns...
6521
+ `));
6522
+ let fixed = 0;
6523
+ for (const result of fixable) {
6524
+ if (result.fixedData) {
6525
+ const filePath = path2.join(
6526
+ patternsPath,
6527
+ result.type === "fix" ? "fixes" : result.type === "blueprint" ? "blueprints" : "solutions",
6528
+ result.file
6529
+ );
6530
+ try {
6531
+ await fs2.promises.writeFile(
6532
+ filePath,
6533
+ JSON.stringify(result.fixedData, null, 2)
6534
+ );
6535
+ console.log(chalk16.green(` \u2713 Fixed: ${result.file}`));
6536
+ fixed++;
6537
+ } catch (error) {
6538
+ console.log(
6539
+ chalk16.red(
6540
+ ` \u2717 Failed to fix: ${result.file} - ${error instanceof Error ? error.message : "Unknown error"}`
6541
+ )
6542
+ );
6543
+ }
6544
+ }
6545
+ }
6546
+ console.log(chalk16.green(`
6547
+ \u2705 Fixed ${fixed}/${fixable.length} patterns`));
6548
+ } else if (fixable.length > 0 && !shouldFix) {
6549
+ console.log(
6550
+ chalk16.yellow(
6551
+ `
6552
+ \u{1F4A1} Run with --fix to auto-fix ${fixable.length} patterns`
6553
+ )
6554
+ );
6555
+ }
6556
+ console.log("");
6557
+ const unfixable = invalid.filter((r) => !r.fixable);
6558
+ if (unfixable.length > 0 && !shouldFix) {
6559
+ process.exit(1);
6560
+ }
6561
+ if (invalid.length > 0 && !shouldFix) {
6562
+ process.exit(1);
6563
+ }
6564
+ }
6565
+ async function validatePatternDirectory(dirPath, type, schema, verbose) {
6566
+ const results = [];
6567
+ try {
6568
+ const files = await fs2.promises.readdir(dirPath);
6569
+ for (const file of files) {
6570
+ if (!file.endsWith(".json")) continue;
6571
+ const filePath = path2.join(dirPath, file);
6572
+ const result = await validatePatternFile(filePath, file, type, schema, verbose);
6573
+ results.push(result);
6574
+ if (verbose) {
6575
+ if (result.valid) {
6576
+ console.log(chalk16.green(` \u2713 ${file}`));
6577
+ } else {
6578
+ console.log(chalk16.red(` \u2717 ${file}`));
6579
+ }
6580
+ }
6581
+ }
6582
+ } catch (error) {
6583
+ if (error.code !== "ENOENT") {
6584
+ console.log(
6585
+ chalk16.yellow(` \u26A0 Could not read ${type} directory: ${dirPath}`)
6586
+ );
6587
+ }
6588
+ }
6589
+ return results;
6590
+ }
6591
+ async function validateSingleFile(filePath, verbose) {
6592
+ const fileName = path2.basename(filePath);
6593
+ let type;
6594
+ let schema;
6595
+ if (filePath.includes("/fixes/") || filePath.includes("\\fixes\\")) {
6596
+ type = "fix";
6597
+ schema = FixPatternSchema;
6598
+ } else if (filePath.includes("/blueprints/") || filePath.includes("\\blueprints\\")) {
6599
+ type = "blueprint";
6600
+ schema = BlueprintSchema;
6601
+ } else if (filePath.includes("/solutions/") || filePath.includes("\\solutions\\")) {
6602
+ type = "solution";
6603
+ schema = SolutionPatternSchema;
6604
+ } else {
6605
+ try {
6606
+ const content = await fs2.promises.readFile(filePath, "utf-8");
6607
+ const data = JSON.parse(content);
6608
+ if (data.trigger && data.solution) {
6609
+ type = "fix";
6610
+ schema = FixPatternSchema;
6611
+ } else if (data.stack && data.structure) {
6612
+ type = "blueprint";
6613
+ schema = BlueprintSchema;
6614
+ } else if (data.context && data.approach) {
6615
+ type = "solution";
6616
+ schema = SolutionPatternSchema;
6617
+ } else {
6618
+ type = "blueprint";
6619
+ schema = BlueprintSchema;
6620
+ }
6621
+ } catch {
6622
+ type = "blueprint";
6623
+ schema = BlueprintSchema;
6624
+ }
6625
+ }
6626
+ return validatePatternFile(filePath, fileName, type, schema, verbose);
6627
+ }
6628
+ async function validatePatternFile(filePath, fileName, type, schema, verbose) {
6629
+ try {
6630
+ const content = await fs2.promises.readFile(filePath, "utf-8");
6631
+ const data = JSON.parse(content);
6632
+ const validation = schema.safeParse(data);
6633
+ if (validation.success) {
6634
+ return {
6635
+ file: fileName,
6636
+ type,
6637
+ valid: true,
6638
+ errors: [],
6639
+ fixable: false
6640
+ };
6641
+ }
6642
+ const errors = validation.error.issues.map(
6643
+ (i) => `${i.path.join(".")}: ${i.message}`
6644
+ );
6645
+ const { fixable, fixedData } = tryAutoFix(data, type, validation.error.issues);
6646
+ return {
6647
+ file: fileName,
6648
+ type,
6649
+ valid: false,
6650
+ errors,
6651
+ fixable,
6652
+ fixedData
6653
+ };
6654
+ } catch (error) {
6655
+ return {
6656
+ file: fileName,
6657
+ type,
6658
+ valid: false,
6659
+ errors: [
6660
+ error instanceof Error ? error.message : "Failed to parse JSON"
6661
+ ],
6662
+ fixable: false
6663
+ };
6664
+ }
6665
+ }
6666
+ function tryAutoFix(data, type, issues) {
6667
+ const fixedData = { ...data };
6668
+ let allFixable = true;
6669
+ for (const issue of issues) {
6670
+ const pathStr = issue.path.join(".");
6671
+ if (pathStr === "metrics" && issue.code === "invalid_type") {
6672
+ fixedData.metrics = createDefaultMetrics();
6673
+ continue;
6674
+ }
6675
+ if (pathStr === "setup" && issue.code === "invalid_type" && type === "blueprint") {
6676
+ fixedData.setup = {
6677
+ commands: [],
6678
+ envVars: [],
6679
+ dependencies: []
6680
+ };
6681
+ continue;
6682
+ }
6683
+ if (pathStr === "relatedPatterns" && issue.code === "invalid_type") {
6684
+ fixedData.relatedPatterns = [];
6685
+ continue;
6686
+ }
6687
+ if (pathStr === "tags" && issue.code === "invalid_type") {
6688
+ fixedData.tags = [];
6689
+ continue;
6690
+ }
6691
+ if (pathStr === "errorSignatures" && issue.code === "invalid_type" && type === "fix") {
6692
+ fixedData.errorSignatures = [];
6693
+ continue;
6694
+ }
6695
+ if (pathStr === "codeChanges" && issue.code === "invalid_type" && type === "fix") {
6696
+ fixedData.codeChanges = [];
6697
+ continue;
6698
+ }
6699
+ if (pathStr === "problemKeywords" && issue.code === "invalid_type" && type === "solution") {
6700
+ fixedData.problemKeywords = [];
6701
+ continue;
6702
+ }
6703
+ if (pathStr === "implementations" && issue.code === "invalid_type" && type === "solution") {
6704
+ fixedData.implementations = [];
6705
+ continue;
6706
+ }
6707
+ allFixable = false;
6708
+ }
6709
+ let schema;
6710
+ if (type === "fix") {
6711
+ schema = FixPatternSchema;
6712
+ } else if (type === "blueprint") {
6713
+ schema = BlueprintSchema;
6714
+ } else {
6715
+ schema = SolutionPatternSchema;
6716
+ }
6717
+ const revalidation = schema.safeParse(fixedData);
6718
+ if (revalidation.success) {
6719
+ return { fixable: true, fixedData };
6720
+ }
6721
+ if (allFixable) {
6722
+ return { fixable: true, fixedData };
6723
+ }
6724
+ return { fixable: false };
6725
+ }
6437
6726
 
6438
6727
  // src/cli/commands/solution.ts
6439
6728
  import chalk17 from "chalk";
6440
6729
  import * as p12 from "@clack/prompts";
6441
- import * as path2 from "path";
6730
+ import * as path3 from "path";
6442
6731
  import {
6443
6732
  PatternStore as PatternStore3,
6444
6733
  CodeAnalyzer
@@ -6496,7 +6785,7 @@ async function solutionCaptureCommand(options) {
6496
6785
  }
6497
6786
  targetPath = pathInput;
6498
6787
  }
6499
- const absolutePath = path2.isAbsolute(targetPath) ? targetPath : path2.resolve(cwd, targetPath);
6788
+ const absolutePath = path3.isAbsolute(targetPath) ? targetPath : path3.resolve(cwd, targetPath);
6500
6789
  let name = options.name;
6501
6790
  if (!name) {
6502
6791
  const nameInput = await p12.text({
@@ -6788,13 +7077,13 @@ async function solutionApplyCommand(solutionId, options) {
6788
7077
  spinner10.start("Applying solution...");
6789
7078
  try {
6790
7079
  const outputDir = options.output || cwd;
6791
- const fs2 = await import("fs");
7080
+ const fs3 = await import("fs");
6792
7081
  const pathModule = await import("path");
6793
7082
  for (const file of filesToApply) {
6794
7083
  const filePath = pathModule.join(outputDir, file.path);
6795
7084
  const dir = pathModule.dirname(filePath);
6796
- await fs2.promises.mkdir(dir, { recursive: true });
6797
- await fs2.promises.writeFile(filePath, file.content);
7085
+ await fs3.promises.mkdir(dir, { recursive: true });
7086
+ await fs3.promises.writeFile(filePath, file.content);
6798
7087
  }
6799
7088
  await store.updateSolutionMetrics(solution.id, true);
6800
7089
  spinner10.stop("Solution applied");
@@ -6961,6 +7250,11 @@ program.command("learn:config").description("Configure learning settings").optio
6961
7250
  program.command("learn:deprecate <patternId> <reason>").description("Deprecate an outdated pattern").argument("<patternId>", "Pattern ID to deprecate").argument("<reason>", "Reason for deprecation").action(learnDeprecateCommand);
6962
7251
  program.command("learn:publish [patternId]").description("Mark pattern(s) as public for syncing").option("--private", "Mark as private instead of public").option("--all", "Apply to all patterns").option("-y, --yes", "Skip confirmation prompts").action(learnPublishCommand);
6963
7252
  program.command("learn:stats").description("Show learning statistics").action(learnStatsCommand);
7253
+ program.command("learn:validate").description("Validate pattern files and optionally auto-fix common issues").option(
7254
+ "-t, --type <type>",
7255
+ "Pattern type to validate (fix, blueprint, solution, all)",
7256
+ "all"
7257
+ ).option("-f, --file <path>", "Validate a specific file by path").option("--fix", "Automatically fix common issues (missing metrics, setup, etc.)").option("-v, --verbose", "Show detailed validation output").action(learnValidateCommand);
6964
7258
  program.command("solution:capture").description("Capture a solution pattern from working code").option("--name <name>", "Solution name").option("--description <desc>", "Solution description").option(
6965
7259
  "--category <cat>",
6966
7260
  "Category (auth, api, database, ui, testing, deployment, integration, performance, security, other)"