workflow-agent-cli 2.2.1 → 2.2.2

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/index.js CHANGED
@@ -1,27 +1,29 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ hasUncommittedChanges,
4
+ runAllChecks,
5
+ stageAllChanges
6
+ } from "../chunk-AN6UOHBB.js";
2
7
  import {
3
8
  validateBranchName,
4
9
  validateCommitMessage,
5
10
  validatePRTitle
6
11
  } from "../chunk-NMHWD2GA.js";
7
12
  import {
8
- DEFAULT_RESERVED_SCOPE_NAMES,
9
13
  hasConfig,
10
14
  loadConfig,
11
- validateConfig,
12
- validateScopeDefinitions,
13
- validateScopeName
14
- } from "../chunk-JCDT4363.js";
15
+ validateScopeDefinitions
16
+ } from "../chunk-RDVTKGQV.js";
15
17
 
16
18
  // src/cli/index.ts
17
- import { Command as Command2 } from "commander";
19
+ import { Command } from "commander";
18
20
 
19
21
  // src/cli/commands/init.ts
20
22
  import * as p from "@clack/prompts";
21
23
  import chalk from "chalk";
22
- import { existsSync as existsSync4 } from "fs";
23
- import { writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
24
- import { join as join4, dirname } from "path";
24
+ import { existsSync } from "fs";
25
+ import { writeFile, mkdir } from "fs/promises";
26
+ import { join, dirname } from "path";
25
27
  import { fileURLToPath } from "url";
26
28
 
27
29
  // src/templates/renderer.ts
@@ -192,6 +194,18 @@ async function renderTemplateFile(templatePath, outputPath, context) {
192
194
  await fs.mkdir(path.dirname(outputPath), { recursive: true });
193
195
  await fs.writeFile(outputPath, rendered, "utf-8");
194
196
  }
197
+ async function renderTemplateDirectory(templateDir, outputDir, context) {
198
+ const files = await fs.readdir(templateDir);
199
+ const rendered = [];
200
+ for (const file of files) {
201
+ if (!file.match(/\.(md|ts|json)$/)) continue;
202
+ const templatePath = path.join(templateDir, file);
203
+ const outputPath = path.join(outputDir, file);
204
+ await renderTemplateFile(templatePath, outputPath, context);
205
+ rendered.push(file);
206
+ }
207
+ return rendered;
208
+ }
195
209
  async function getProjectName(projectPath) {
196
210
  try {
197
211
  const pkgPath = path.join(projectPath, "package.json");
@@ -222,694 +236,13 @@ async function validateTemplateDirectory(templateDir) {
222
236
  }
223
237
  }
224
238
 
225
- // src/templates/metadata.ts
226
- var templateMetadata = {
227
- "AGENT_EDITING_INSTRUCTIONS.md": {
228
- filename: "AGENT_EDITING_INSTRUCTIONS.md",
229
- displayName: "Agent Editing Instructions",
230
- mandatory: true,
231
- category: "workflow",
232
- validators: ["implementation-plan"],
233
- description: "Core rules for AI agents: implementation plans, coding standards, architecture"
234
- },
235
- "BRANCHING_STRATEGY.md": {
236
- filename: "BRANCHING_STRATEGY.md",
237
- displayName: "Branching Strategy",
238
- mandatory: true,
239
- category: "workflow",
240
- validators: ["branch-name", "pr-title"],
241
- description: "Git branch naming conventions, PR requirements, merge policies"
242
- },
243
- "TESTING_STRATEGY.md": {
244
- filename: "TESTING_STRATEGY.md",
245
- displayName: "Testing Strategy",
246
- mandatory: true,
247
- category: "development",
248
- validators: ["test-coverage"],
249
- description: "Testing pyramid, Vitest/Playwright patterns, when tests are required"
250
- },
251
- "SELF_IMPROVEMENT_MANDATE.md": {
252
- filename: "SELF_IMPROVEMENT_MANDATE.md",
253
- displayName: "Self-Improvement Mandate",
254
- mandatory: true,
255
- category: "workflow",
256
- validators: [],
257
- description: "Continuous improvement tracking, changelog requirements"
258
- },
259
- "SINGLE_SOURCE_OF_TRUTH.md": {
260
- filename: "SINGLE_SOURCE_OF_TRUTH.md",
261
- displayName: "Single Source of Truth",
262
- mandatory: true,
263
- category: "workflow",
264
- validators: [],
265
- description: "Canonical code locations, service patterns, avoiding duplication"
266
- },
267
- "COMPONENT_LIBRARY.md": {
268
- filename: "COMPONENT_LIBRARY.md",
269
- displayName: "Component Library",
270
- mandatory: false,
271
- category: "development",
272
- validators: [],
273
- description: "UI component patterns, design tokens, decision tree"
274
- },
275
- "DEPLOYMENT_STRATEGY.md": {
276
- filename: "DEPLOYMENT_STRATEGY.md",
277
- displayName: "Deployment Strategy",
278
- mandatory: false,
279
- category: "development",
280
- validators: [],
281
- description: "Deployment workflow, environments, migrations, rollback"
282
- },
283
- "LIBRARY_INVENTORY.md": {
284
- filename: "LIBRARY_INVENTORY.md",
285
- displayName: "Library Inventory",
286
- mandatory: false,
287
- category: "development",
288
- validators: [],
289
- description: "Dependency catalog, approved libraries, new library process"
290
- },
291
- "SCOPE_CREATION_WORKFLOW.md": {
292
- filename: "SCOPE_CREATION_WORKFLOW.md",
293
- displayName: "Scope Creation Workflow",
294
- mandatory: false,
295
- category: "workflow",
296
- validators: [],
297
- description: "Workflow for AI agents creating custom scopes"
298
- },
299
- "CUSTOM_SCOPE_TEMPLATE.md": {
300
- filename: "CUSTOM_SCOPE_TEMPLATE.md",
301
- displayName: "Custom Scope Template",
302
- mandatory: false,
303
- category: "workflow",
304
- validators: [],
305
- description: "Template for defining custom scope packages"
306
- },
307
- "PROJECT_TEMPLATE_README.md": {
308
- filename: "PROJECT_TEMPLATE_README.md",
309
- displayName: "Project Template README",
310
- mandatory: false,
311
- category: "documentation",
312
- validators: [],
313
- description: "Meta-document describing project structure"
314
- },
315
- "Guidelines.md": {
316
- filename: "Guidelines.md",
317
- displayName: "Custom Guidelines",
318
- mandatory: false,
319
- category: "documentation",
320
- validators: [],
321
- description: "Placeholder for custom user guidelines"
322
- }
323
- };
324
- function getMandatoryTemplates() {
325
- return Object.values(templateMetadata).filter((t) => t.mandatory);
326
- }
327
- function getOptionalTemplates() {
328
- return Object.values(templateMetadata).filter((t) => !t.mandatory);
329
- }
330
- function getMandatoryTemplateFilenames() {
331
- return getMandatoryTemplates().map((t) => t.filename);
332
- }
333
-
334
- // src/utils/hooks.ts
335
- import { existsSync } from "fs";
336
- import { readFile, writeFile, unlink, chmod, rename, mkdir } from "fs/promises";
337
- import { join } from "path";
338
- function getGitHooksDir(projectPath = process.cwd()) {
339
- return join(projectPath, ".git", "hooks");
340
- }
341
- function hasGitRepo(projectPath = process.cwd()) {
342
- return existsSync(join(projectPath, ".git"));
343
- }
344
- function generatePreCommitHook(config) {
345
- const checks = config?.preCommit || ["validate-branch", "check-guidelines"];
346
- const checkCommands = checks.map((check) => {
347
- switch (check) {
348
- case "validate-branch":
349
- return " workflow validate branch";
350
- case "validate-commit":
351
- return " workflow validate commit";
352
- case "check-guidelines":
353
- return " workflow doctor --check-guidelines-only 2>/dev/null || true";
354
- case "validate-scopes":
355
- return " workflow config validate";
356
- default:
357
- return "";
358
- }
359
- }).filter(Boolean).join("\n");
360
- return `#!/bin/sh
361
- # Workflow Agent pre-commit hook
362
- # Auto-generated - do not edit manually
363
- # To reinstall: workflow hooks install
364
- # To uninstall: workflow hooks uninstall
365
-
366
- # Skip in CI environment
367
- if [ -n "\${CI:-}" ] || [ -n "\${GITHUB_ACTIONS:-}" ] || [ -n "\${GITLAB_CI:-}" ]; then
368
- exit 0
369
- fi
370
-
371
- # Run workflow checks
372
- ${checkCommands}
373
-
374
- # Run original hook if it exists
375
- if [ -f ".git/hooks/pre-commit.original" ]; then
376
- .git/hooks/pre-commit.original "$@"
377
- fi
378
- `;
379
- }
380
- function generateCommitMsgHook(config) {
381
- const checks = config?.commitMsg || ["validate-commit"];
382
- const checkCommands = checks.map((check) => {
383
- switch (check) {
384
- case "validate-commit":
385
- return ' workflow validate commit "$(cat "$1")"';
386
- default:
387
- return "";
388
- }
389
- }).filter(Boolean).join("\n");
390
- return `#!/bin/sh
391
- # Workflow Agent commit-msg hook
392
- # Auto-generated - do not edit manually
393
- # To reinstall: workflow hooks install
394
- # To uninstall: workflow hooks uninstall
395
-
396
- # Skip in CI environment
397
- if [ -n "\${CI:-}" ] || [ -n "\${GITHUB_ACTIONS:-}" ] || [ -n "\${GITLAB_CI:-}" ]; then
398
- exit 0
399
- fi
400
-
401
- # Run workflow checks
402
- ${checkCommands}
403
-
404
- # Run original hook if it exists
405
- if [ -f ".git/hooks/commit-msg.original" ]; then
406
- .git/hooks/commit-msg.original "$@"
407
- fi
408
- `;
409
- }
410
- async function isWorkflowHook(hookPath) {
411
- try {
412
- const content = await readFile(hookPath, "utf-8");
413
- return content.includes("Workflow Agent") && content.includes("Auto-generated");
414
- } catch {
415
- return false;
416
- }
417
- }
418
- async function getHookStatus(hookType, projectPath = process.cwd()) {
419
- const hooksDir = getGitHooksDir(projectPath);
420
- const hookPath = join(hooksDir, hookType);
421
- const originalPath = join(hooksDir, `${hookType}.original`);
422
- const status = {
423
- installed: false,
424
- hookType,
425
- hasExistingHook: false,
426
- wrappedOriginal: false
427
- };
428
- if (!existsSync(hookPath)) {
429
- return status;
430
- }
431
- status.hasExistingHook = true;
432
- if (await isWorkflowHook(hookPath)) {
433
- status.installed = true;
434
- status.wrappedOriginal = existsSync(originalPath);
435
- }
436
- return status;
437
- }
438
- async function getAllHooksStatus(projectPath = process.cwd()) {
439
- return Promise.all([
440
- getHookStatus("pre-commit", projectPath),
441
- getHookStatus("commit-msg", projectPath)
442
- ]);
443
- }
444
- async function installSingleHook(hookType, config, projectPath = process.cwd()) {
445
- const hooksDir = getGitHooksDir(projectPath);
446
- const hookPath = join(hooksDir, hookType);
447
- const originalPath = join(hooksDir, `${hookType}.original`);
448
- const result = {
449
- success: false,
450
- hookType,
451
- wrappedExisting: false
452
- };
453
- try {
454
- if (!existsSync(hooksDir)) {
455
- await mkdir(hooksDir, { recursive: true });
456
- }
457
- if (existsSync(hookPath)) {
458
- const isOurs = await isWorkflowHook(hookPath);
459
- if (!isOurs) {
460
- await rename(hookPath, originalPath);
461
- result.wrappedExisting = true;
462
- }
463
- }
464
- const hookContent = hookType === "pre-commit" ? generatePreCommitHook(config) : generateCommitMsgHook(config);
465
- await writeFile(hookPath, hookContent, "utf-8");
466
- await chmod(hookPath, 493);
467
- result.success = true;
468
- } catch (error) {
469
- result.error = error instanceof Error ? error.message : String(error);
470
- }
471
- return result;
472
- }
473
- async function installHooks(config, projectPath = process.cwd()) {
474
- if (!hasGitRepo(projectPath)) {
475
- return [
476
- {
477
- success: false,
478
- hookType: "pre-commit",
479
- wrappedExisting: false,
480
- error: "No git repository found. Run git init first."
481
- }
482
- ];
483
- }
484
- const results = await Promise.all([
485
- installSingleHook("pre-commit", config, projectPath),
486
- installSingleHook("commit-msg", config, projectPath)
487
- ]);
488
- return results;
489
- }
490
- async function uninstallSingleHook(hookType, projectPath = process.cwd()) {
491
- const hooksDir = getGitHooksDir(projectPath);
492
- const hookPath = join(hooksDir, hookType);
493
- const originalPath = join(hooksDir, `${hookType}.original`);
494
- const result = {
495
- success: false,
496
- hookType,
497
- wrappedExisting: false
498
- };
499
- try {
500
- if (!existsSync(hookPath)) {
501
- result.success = true;
502
- return result;
503
- }
504
- const isOurs = await isWorkflowHook(hookPath);
505
- if (!isOurs) {
506
- result.error = "Hook is not managed by Workflow Agent";
507
- return result;
508
- }
509
- await unlink(hookPath);
510
- if (existsSync(originalPath)) {
511
- await rename(originalPath, hookPath);
512
- result.wrappedExisting = true;
513
- }
514
- result.success = true;
515
- } catch (error) {
516
- result.error = error instanceof Error ? error.message : String(error);
517
- }
518
- return result;
519
- }
520
- async function uninstallHooks(projectPath = process.cwd()) {
521
- return Promise.all([
522
- uninstallSingleHook("pre-commit", projectPath),
523
- uninstallSingleHook("commit-msg", projectPath)
524
- ]);
525
- }
526
-
527
- // src/utils/git-repo.ts
528
- import { execa } from "execa";
529
- import { existsSync as existsSync2 } from "fs";
530
- import { readFile as readFile2 } from "fs/promises";
531
- import { join as join2 } from "path";
532
- async function isGitRepo(projectPath = process.cwd()) {
533
- try {
534
- await execa("git", ["rev-parse", "--git-dir"], { cwd: projectPath });
535
- return true;
536
- } catch {
537
- return false;
538
- }
539
- }
540
- async function getGitRemoteUrl(projectPath = process.cwd()) {
541
- try {
542
- const { stdout } = await execa("git", ["remote", "get-url", "origin"], {
543
- cwd: projectPath
544
- });
545
- return stdout.trim() || null;
546
- } catch {
547
- return null;
548
- }
549
- }
550
- function isGitHubRemote(remoteUrl) {
551
- if (!remoteUrl) return false;
552
- return remoteUrl.includes("github.com");
553
- }
554
- function parseGitHubUrl(remoteUrl) {
555
- if (!remoteUrl || !isGitHubRemote(remoteUrl)) return null;
556
- const match = remoteUrl.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
557
- if (match) {
558
- return {
559
- owner: match[1],
560
- repo: match[2].replace(/\.git$/, "")
561
- };
562
- }
563
- return null;
564
- }
565
- async function getDefaultBranch(projectPath = process.cwd()) {
566
- try {
567
- const { stdout } = await execa(
568
- "git",
569
- ["symbolic-ref", "refs/remotes/origin/HEAD"],
570
- {
571
- cwd: projectPath
572
- }
573
- );
574
- return stdout.trim().replace("refs/remotes/origin/", "") || "main";
575
- } catch {
576
- try {
577
- const { stdout } = await execa(
578
- "git",
579
- ["rev-parse", "--abbrev-ref", "HEAD"],
580
- {
581
- cwd: projectPath
582
- }
583
- );
584
- return stdout.trim() || "main";
585
- } catch {
586
- return "main";
587
- }
588
- }
589
- }
590
- async function getRepoInfo(projectPath = process.cwd()) {
591
- const isRepo = await isGitRepo(projectPath);
592
- if (!isRepo) {
593
- return {
594
- isGitRepo: false,
595
- remoteUrl: null,
596
- isGitHub: false,
597
- github: null,
598
- defaultBranch: null
599
- };
600
- }
601
- const remoteUrl = await getGitRemoteUrl(projectPath);
602
- const isGitHub = isGitHubRemote(remoteUrl);
603
- const github = parseGitHubUrl(remoteUrl);
604
- const defaultBranch = await getDefaultBranch(projectPath);
605
- return {
606
- isGitRepo: true,
607
- remoteUrl,
608
- isGitHub,
609
- github,
610
- defaultBranch
611
- };
612
- }
613
- async function detectPackageManager(projectPath = process.cwd()) {
614
- if (existsSync2(join2(projectPath, "pnpm-lock.yaml"))) {
615
- return "pnpm";
616
- }
617
- if (existsSync2(join2(projectPath, "yarn.lock"))) {
618
- return "yarn";
619
- }
620
- if (existsSync2(join2(projectPath, "bun.lockb"))) {
621
- return "bun";
622
- }
623
- if (existsSync2(join2(projectPath, "package-lock.json"))) {
624
- return "npm";
625
- }
626
- try {
627
- const pkgPath = join2(projectPath, "package.json");
628
- if (existsSync2(pkgPath)) {
629
- const pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
630
- if (pkg.packageManager) {
631
- if (pkg.packageManager.startsWith("pnpm")) return "pnpm";
632
- if (pkg.packageManager.startsWith("yarn")) return "yarn";
633
- if (pkg.packageManager.startsWith("bun")) return "bun";
634
- }
635
- }
636
- } catch {
637
- }
638
- return "npm";
639
- }
640
- async function isMonorepo(projectPath = process.cwd()) {
641
- if (existsSync2(join2(projectPath, "pnpm-workspace.yaml"))) {
642
- return true;
643
- }
644
- try {
645
- const pkgPath = join2(projectPath, "package.json");
646
- if (existsSync2(pkgPath)) {
647
- const pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
648
- if (pkg.workspaces) {
649
- return true;
650
- }
651
- }
652
- } catch {
653
- }
654
- if (existsSync2(join2(projectPath, "lerna.json"))) {
655
- return true;
656
- }
657
- if (existsSync2(join2(projectPath, "nx.json"))) {
658
- return true;
659
- }
660
- if (existsSync2(join2(projectPath, "turbo.json"))) {
661
- return true;
662
- }
663
- return false;
664
- }
665
- async function getPackageScripts(projectPath = process.cwd()) {
666
- try {
667
- const pkgPath = join2(projectPath, "package.json");
668
- if (!existsSync2(pkgPath)) {
669
- return {};
670
- }
671
- const pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
672
- return pkg.scripts || {};
673
- } catch {
674
- return {};
675
- }
676
- }
677
- async function getProjectInfo(projectPath = process.cwd()) {
678
- const packageManager = await detectPackageManager(projectPath);
679
- const monorepo = await isMonorepo(projectPath);
680
- const scripts = await getPackageScripts(projectPath);
681
- return {
682
- packageManager,
683
- isMonorepo: monorepo,
684
- hasLintScript: "lint" in scripts,
685
- hasTypecheckScript: "typecheck" in scripts || "type-check" in scripts,
686
- hasFormatScript: "format" in scripts || "format:check" in scripts,
687
- hasTestScript: "test" in scripts,
688
- hasBuildScript: "build" in scripts
689
- };
690
- }
691
- function getInstallCommand(packageManager) {
692
- switch (packageManager) {
693
- case "pnpm":
694
- return "pnpm install --frozen-lockfile";
695
- case "yarn":
696
- return "yarn install --frozen-lockfile";
697
- case "bun":
698
- return "bun install --frozen-lockfile";
699
- case "npm":
700
- default:
701
- return "npm ci";
702
- }
703
- }
704
- function getRunCommand(packageManager, script, isMonorepo2 = false) {
705
- switch (packageManager) {
706
- case "pnpm":
707
- return isMonorepo2 ? `pnpm -r run ${script}` : `pnpm run ${script}`;
708
- case "yarn":
709
- return isMonorepo2 ? `yarn workspaces run ${script}` : `yarn run ${script}`;
710
- case "bun":
711
- return `bun run ${script}`;
712
- case "npm":
713
- default:
714
- return isMonorepo2 ? `npm run ${script} --workspaces --if-present` : `npm run ${script}`;
715
- }
716
- }
717
-
718
- // src/utils/github-actions.ts
719
- import { existsSync as existsSync3 } from "fs";
720
- import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
721
- import { join as join3 } from "path";
722
- function generateCIWorkflowContent(options) {
723
- const {
724
- packageManager,
725
- isMonorepo: isMonorepo2,
726
- checks,
727
- nodeVersions,
728
- defaultBranch,
729
- hasLintScript,
730
- hasTypecheckScript,
731
- hasFormatScript,
732
- hasTestScript,
733
- hasBuildScript
734
- } = options;
735
- const installCmd = getInstallCommand(packageManager);
736
- const steps = [];
737
- steps.push(` - name: Checkout
738
- uses: actions/checkout@v4`);
739
- let cacheType = packageManager;
740
- if (packageManager === "bun") {
741
- cacheType = "npm";
742
- }
743
- const useMatrix = nodeVersions.length > 1;
744
- const nodeVersionValue = useMatrix ? `\${{ matrix.node-version }}` : nodeVersions[0] || "20";
745
- steps.push(`
746
- - name: Setup Node.js
747
- uses: actions/setup-node@v4
748
- with:
749
- node-version: '${nodeVersionValue}'
750
- cache: '${cacheType}'`);
751
- if (packageManager === "pnpm") {
752
- steps.push(`
753
- - name: Setup pnpm
754
- uses: pnpm/action-setup@v4
755
- with:
756
- version: 9`);
757
- }
758
- steps.push(`
759
- - name: Install dependencies
760
- run: ${installCmd}`);
761
- if (checks.includes("lint")) {
762
- if (hasLintScript) {
763
- const lintCmd = getRunCommand(packageManager, "lint", isMonorepo2);
764
- steps.push(`
765
- - name: Lint
766
- run: ${lintCmd}`);
767
- } else {
768
- steps.push(`
769
- - name: Lint
770
- run: echo "No lint script configured - add 'lint' to package.json scripts"`);
771
- }
772
- }
773
- if (checks.includes("typecheck")) {
774
- if (hasTypecheckScript) {
775
- const typecheckCmd = getRunCommand(
776
- packageManager,
777
- "typecheck",
778
- isMonorepo2
779
- );
780
- steps.push(`
781
- - name: Type check
782
- run: ${typecheckCmd}`);
783
- } else {
784
- steps.push(`
785
- - name: Type check
786
- run: npx tsc --noEmit`);
787
- }
788
- }
789
- if (checks.includes("format")) {
790
- if (hasFormatScript) {
791
- const formatCmd = getRunCommand(
792
- packageManager,
793
- "format:check",
794
- isMonorepo2
795
- );
796
- steps.push(`
797
- - name: Format check
798
- run: ${formatCmd} || npx prettier --check "**/*.{ts,tsx,js,jsx,json,md}"`);
799
- } else {
800
- steps.push(`
801
- - name: Format check
802
- run: npx prettier --check "**/*.{ts,tsx,js,jsx,json,md}"`);
803
- }
804
- }
805
- if (checks.includes("build")) {
806
- if (hasBuildScript) {
807
- const buildCmd = getRunCommand(packageManager, "build", isMonorepo2);
808
- steps.push(`
809
- - name: Build
810
- run: ${buildCmd}`);
811
- } else {
812
- steps.push(`
813
- - name: Build
814
- run: echo "No build script configured - add 'build' to package.json scripts"`);
815
- }
816
- }
817
- if (checks.includes("test")) {
818
- if (hasTestScript) {
819
- const testCmd = getRunCommand(packageManager, "test", isMonorepo2);
820
- steps.push(`
821
- - name: Test
822
- run: ${testCmd}`);
823
- } else {
824
- steps.push(`
825
- - name: Test
826
- run: echo "No test script configured - add 'test' to package.json scripts"`);
827
- }
828
- }
829
- let matrixStrategy = "";
830
- if (useMatrix) {
831
- matrixStrategy = `
832
- strategy:
833
- matrix:
834
- node-version: [${nodeVersions.map((v) => `'${v}'`).join(", ")}]`;
835
- }
836
- const workflow = `# Workflow Agent CI Pipeline
837
- # Auto-generated - modifications will be preserved on regeneration
838
- # To regenerate: workflow github:setup
839
-
840
- name: CI
841
-
842
- on:
843
- push:
844
- branches: [${defaultBranch}, develop]
845
- pull_request:
846
- branches: [${defaultBranch}, develop]
847
-
848
- jobs:
849
- ci:
850
- runs-on: ubuntu-latest${matrixStrategy}
851
-
852
- steps:
853
- ${steps.join("\n")}
854
- `;
855
- return workflow;
856
- }
857
- async function createCIWorkflow(options = {}) {
858
- const projectPath = options.projectPath || process.cwd();
859
- const workflowsDir = join3(projectPath, ".github", "workflows");
860
- const workflowPath = join3(workflowsDir, "ci.yml");
861
- const result = {
862
- success: false,
863
- filePath: workflowPath
864
- };
865
- try {
866
- const projectInfo = await getProjectInfo(projectPath);
867
- const packageManager = options.packageManager || projectInfo.packageManager;
868
- const isMonorepo2 = options.isMonorepo ?? projectInfo.isMonorepo;
869
- const checks = options.ciConfig?.checks || [
870
- "lint",
871
- "typecheck",
872
- "format",
873
- "build",
874
- "test"
875
- ];
876
- const nodeVersions = options.nodeVersions || ["20"];
877
- const defaultBranch = options.defaultBranch || "main";
878
- if (!existsSync3(workflowsDir)) {
879
- await mkdir2(workflowsDir, { recursive: true });
880
- }
881
- const content = generateCIWorkflowContent({
882
- packageManager,
883
- isMonorepo: isMonorepo2,
884
- checks,
885
- nodeVersions,
886
- defaultBranch,
887
- hasLintScript: projectInfo.hasLintScript,
888
- hasTypecheckScript: projectInfo.hasTypecheckScript,
889
- hasFormatScript: projectInfo.hasFormatScript,
890
- hasTestScript: projectInfo.hasTestScript,
891
- hasBuildScript: projectInfo.hasBuildScript
892
- });
893
- await writeFile2(workflowPath, content, "utf-8");
894
- result.success = true;
895
- } catch (error) {
896
- result.error = error instanceof Error ? error.message : String(error);
897
- }
898
- return result;
899
- }
900
- function hasCIWorkflow(projectPath = process.cwd()) {
901
- const workflowsDir = join3(projectPath, ".github", "workflows");
902
- const possibleNames = ["ci.yml", "ci.yaml", "main.yml", "main.yaml"];
903
- return possibleNames.some((name) => existsSync3(join3(workflowsDir, name)));
904
- }
905
-
906
239
  // src/cli/commands/init.ts
907
240
  var __filename = fileURLToPath(import.meta.url);
908
241
  var __dirname = dirname(__filename);
909
242
  async function initCommand(options) {
910
243
  console.log(chalk.bold.cyan("\n\u{1F680} Workflow Agent Initialization\n"));
911
244
  const cwd = process.cwd();
912
- const isNonInteractive = !!(options.preset && options.name) || !!options.yes;
245
+ const isNonInteractive = !!(options.preset && options.name);
913
246
  if (hasConfig(cwd) && !options.yes && !isNonInteractive) {
914
247
  const shouldContinue = await p.confirm({
915
248
  message: "Workflow Agent is already configured. Continue and overwrite?",
@@ -967,10 +300,10 @@ async function initCommand(options) {
967
300
  try {
968
301
  const presetModule = await import(`@workflow/scopes-${preset}`);
969
302
  scopes = presetModule.scopes || presetModule.default.scopes;
970
- const spinner5 = p.spinner();
971
- spinner5.start(`Loading ${presetModule.default?.name || preset} preset`);
303
+ const spinner4 = p.spinner();
304
+ spinner4.start(`Loading ${presetModule.default?.name || preset} preset`);
972
305
  await new Promise((resolve) => setTimeout(resolve, 500));
973
- spinner5.stop(`\u2713 Loaded ${scopes.length} scopes from preset`);
306
+ spinner4.stop(`\u2713 Loaded ${scopes.length} scopes from preset`);
974
307
  } catch (error) {
975
308
  console.log(
976
309
  chalk.yellow(
@@ -979,32 +312,16 @@ async function initCommand(options) {
979
312
  )
980
313
  );
981
314
  scopes = [
982
- {
983
- name: "feat",
984
- description: "New features and enhancements",
985
- emoji: "\u2728"
986
- },
987
- { name: "fix", description: "Bug fixes and patches", emoji: "\u{1F41B}" },
988
- {
989
- name: "documentation",
990
- description: "Documentation updates and improvements",
991
- emoji: "\u{1F4DA}"
992
- }
315
+ { name: "feat", description: "New features", emoji: "\u2728" },
316
+ { name: "fix", description: "Bug fixes", emoji: "\u{1F41B}" },
317
+ { name: "docs", description: "Documentation", emoji: "\u{1F4DA}" }
993
318
  ];
994
319
  }
995
320
  } else {
996
321
  scopes = [
997
- {
998
- name: "feat",
999
- description: "New features and enhancements",
1000
- emoji: "\u2728"
1001
- },
1002
- { name: "fix", description: "Bug fixes and patches", emoji: "\u{1F41B}" },
1003
- {
1004
- name: "documentation",
1005
- description: "Documentation updates and improvements",
1006
- emoji: "\u{1F4DA}"
1007
- }
322
+ { name: "feat", description: "New features", emoji: "\u2728" },
323
+ { name: "fix", description: "Bug fixes", emoji: "\u{1F41B}" },
324
+ { name: "docs", description: "Documentation", emoji: "\u{1F4DA}" }
1008
325
  ];
1009
326
  console.log(
1010
327
  chalk.dim(
@@ -1016,169 +333,48 @@ async function initCommand(options) {
1016
333
  projectName,
1017
334
  scopes,
1018
335
  enforcement: "strict",
1019
- language: "en",
1020
- hooks: {
1021
- enabled: true,
1022
- preCommit: ["validate-branch", "check-guidelines"],
1023
- commitMsg: ["validate-commit"]
1024
- },
1025
- ci: {
1026
- enabled: true,
1027
- provider: "github",
1028
- checks: ["lint", "typecheck", "format", "build", "test"]
1029
- },
1030
- reservedScopeNames: DEFAULT_RESERVED_SCOPE_NAMES
336
+ language: "en"
1031
337
  };
1032
- const configPath = join4(cwd, "workflow.config.json");
1033
- await writeFile3(configPath, JSON.stringify(config, null, 2));
1034
- const workflowDir = join4(cwd, ".workflow");
1035
- if (!existsSync4(workflowDir)) {
1036
- await mkdir3(workflowDir, { recursive: true });
1037
- }
1038
- const repoInfo = await getRepoInfo(cwd);
1039
- const projectInfo = await getProjectInfo(cwd);
1040
- const mandatoryTemplates = getMandatoryTemplates();
1041
- const optionalTemplates = getOptionalTemplates();
1042
- console.log(
1043
- chalk.dim(
1044
- `
1045
- \u{1F4CB} Generating ${mandatoryTemplates.length} mandatory guidelines...`
1046
- )
1047
- );
1048
- const guidelinesDir = join4(cwd, "guidelines");
1049
- const templatesDir = join4(__dirname, "../../templates");
1050
- let mandatoryGenerated = 0;
1051
- let optionalGenerated = 0;
1052
- try {
1053
- await validateTemplateDirectory(templatesDir);
1054
- const context = await buildTemplateContext(config, cwd);
1055
- await mkdir3(guidelinesDir, { recursive: true });
1056
- for (const template of mandatoryTemplates) {
1057
- try {
1058
- const templatePath = join4(templatesDir, template.filename);
1059
- const outputPath = join4(guidelinesDir, template.filename);
1060
- await renderTemplateFile(templatePath, outputPath, {
1061
- ...context,
1062
- reservedScopeNames: (config.reservedScopeNames || []).join(", ")
1063
- });
1064
- mandatoryGenerated++;
1065
- } catch (error) {
1066
- console.log(
1067
- chalk.yellow(` \u26A0\uFE0F Could not generate ${template.filename}`)
1068
- );
1069
- }
1070
- }
1071
- console.log(
1072
- chalk.green(`\u2713 Generated ${mandatoryGenerated} mandatory guidelines`)
1073
- );
1074
- let shouldGenerateOptional = isNonInteractive;
1075
- if (!isNonInteractive) {
1076
- const response = await p.confirm({
1077
- message: `Generate ${optionalTemplates.length} optional guidelines (deployment, library inventory, etc.)?`,
1078
- initialValue: true
1079
- });
1080
- shouldGenerateOptional = !p.isCancel(response) && response;
1081
- }
1082
- if (shouldGenerateOptional) {
1083
- for (const template of optionalTemplates) {
1084
- try {
1085
- const templatePath = join4(templatesDir, template.filename);
1086
- const outputPath = join4(guidelinesDir, template.filename);
1087
- await renderTemplateFile(templatePath, outputPath, context);
1088
- optionalGenerated++;
1089
- } catch {
1090
- }
1091
- }
1092
- console.log(
1093
- chalk.green(`\u2713 Generated ${optionalGenerated} optional guidelines`)
1094
- );
1095
- }
1096
- } catch (error) {
1097
- console.log(
1098
- chalk.yellow(
1099
- `
1100
- \u26A0\uFE0F Could not generate guidelines: ${error instanceof Error ? error.message : String(error)}`
1101
- )
1102
- );
1103
- console.log(chalk.dim("You can manually copy guidelines later if needed."));
338
+ const configPath = join(cwd, "workflow.config.json");
339
+ await writeFile(configPath, JSON.stringify(config, null, 2));
340
+ const workflowDir = join(cwd, ".workflow");
341
+ if (!existsSync(workflowDir)) {
342
+ await mkdir(workflowDir, { recursive: true });
343
+ }
344
+ const shouldGenerateGuidelines = await p.confirm({
345
+ message: "Generate workflow guidelines from templates?",
346
+ initialValue: true
347
+ });
348
+ if (p.isCancel(shouldGenerateGuidelines)) {
349
+ p.cancel("Initialization cancelled");
350
+ process.exit(0);
1104
351
  }
1105
- if (hasGitRepo(cwd)) {
1106
- let shouldInstallHooks = isNonInteractive;
1107
- if (!isNonInteractive) {
1108
- const response = await p.confirm({
1109
- message: "Install git hooks for pre-commit validation?",
1110
- initialValue: true
1111
- });
1112
- shouldInstallHooks = !p.isCancel(response) && response;
1113
- }
1114
- if (shouldInstallHooks) {
1115
- const hookSpinner = p.spinner();
1116
- hookSpinner.start("Installing git hooks...");
1117
- const hookResults = await installHooks(
1118
- config.hooks || {
1119
- enabled: true,
1120
- preCommit: ["validate-branch", "check-guidelines"],
1121
- commitMsg: ["validate-commit"]
1122
- },
1123
- cwd
352
+ if (shouldGenerateGuidelines) {
353
+ const spinner4 = p.spinner();
354
+ spinner4.start("Generating guidelines...");
355
+ try {
356
+ const templatesDir = join(__dirname, "../../templates");
357
+ await validateTemplateDirectory(templatesDir);
358
+ const context = await buildTemplateContext(config, cwd);
359
+ const guidelinesDir = join(cwd, "guidelines");
360
+ await mkdir(guidelinesDir, { recursive: true });
361
+ const renderedFiles = await renderTemplateDirectory(
362
+ templatesDir,
363
+ guidelinesDir,
364
+ context
1124
365
  );
1125
- const allSuccess = hookResults.every((r) => r.success);
1126
- if (allSuccess) {
1127
- hookSpinner.stop("\u2713 Installed git hooks");
1128
- } else {
1129
- hookSpinner.stop("\u26A0\uFE0F Some hooks could not be installed");
1130
- }
1131
- }
1132
- }
1133
- if (repoInfo.isGitHub) {
1134
- const existingCI = hasCIWorkflow(cwd);
1135
- if (!existingCI) {
366
+ spinner4.stop(`\u2713 Generated ${renderedFiles.length} guideline documents`);
367
+ } catch (error) {
368
+ spinner4.stop("\u26A0\uFE0F Could not generate guidelines");
1136
369
  console.log(
1137
- chalk.dim(
1138
- "\n\u{1F527} Setting up GitHub Actions CI (mandatory for GitHub repos)..."
370
+ chalk.yellow(
371
+ `
372
+ Reason: ${error instanceof Error ? error.message : String(error)}`
1139
373
  )
1140
374
  );
1141
- const ciResult = await createCIWorkflow({
1142
- projectPath: cwd,
1143
- packageManager: projectInfo.packageManager,
1144
- isMonorepo: projectInfo.isMonorepo,
1145
- ciConfig: config.ci || {
1146
- enabled: true,
1147
- provider: "github",
1148
- checks: ["lint", "typecheck", "format", "build", "test"]
1149
- },
1150
- defaultBranch: repoInfo.defaultBranch || "main"
1151
- });
1152
- if (ciResult.success) {
1153
- console.log(chalk.green("\u2713 Created GitHub Actions CI workflow"));
1154
- console.log(chalk.dim(` File: .github/workflows/ci.yml`));
1155
- } else {
1156
- console.log(
1157
- chalk.yellow(`\u26A0\uFE0F Could not create CI workflow: ${ciResult.error}`)
1158
- );
1159
- }
1160
- } else {
1161
- console.log(chalk.dim("\n\u2713 GitHub Actions CI workflow already exists"));
1162
- }
1163
- } else if (repoInfo.isGitRepo) {
1164
- let shouldSetupCI = false;
1165
- if (!isNonInteractive) {
1166
- const response = await p.confirm({
1167
- message: "No GitHub remote detected. Create CI workflow anyway?",
1168
- initialValue: false
1169
- });
1170
- shouldSetupCI = !p.isCancel(response) && response;
1171
- }
1172
- if (shouldSetupCI) {
1173
- const ciResult = await createCIWorkflow({
1174
- projectPath: cwd,
1175
- packageManager: projectInfo.packageManager,
1176
- isMonorepo: projectInfo.isMonorepo,
1177
- defaultBranch: repoInfo.defaultBranch || "main"
1178
- });
1179
- if (ciResult.success) {
1180
- console.log(chalk.green("\u2713 Created CI workflow"));
1181
- }
375
+ console.log(
376
+ chalk.dim("You can manually copy guidelines later if needed.")
377
+ );
1182
378
  }
1183
379
  }
1184
380
  p.outro(chalk.green("\u2713 Workflow Agent initialized successfully!"));
@@ -1186,24 +382,25 @@ async function initCommand(options) {
1186
382
  console.log(
1187
383
  chalk.dim(" 1. Review your configuration in workflow.config.json")
1188
384
  );
1189
- console.log(
1190
- chalk.dim(" 2. Review generated guidelines in guidelines/ directory")
1191
- );
1192
- console.log(chalk.dim(" 3. Run: workflow validate branch"));
1193
- console.log(chalk.dim(" 4. Run: workflow doctor (for health check)\n"));
1194
- if (repoInfo.isGitHub) {
385
+ if (shouldGenerateGuidelines) {
386
+ console.log(
387
+ chalk.dim(" 2. Review generated guidelines in guidelines/ directory")
388
+ );
389
+ console.log(chalk.dim(" 3. Run: workflow validate branch"));
1195
390
  console.log(
1196
- chalk.cyan("\u{1F4A1} Recommended: Enable branch protection on GitHub")
391
+ chalk.dim(" 4. Run: workflow doctor (for optimization suggestions)\n")
1197
392
  );
393
+ } else {
394
+ console.log(chalk.dim(" 2. Run: workflow validate branch"));
1198
395
  console.log(
1199
- chalk.dim(" Settings \u2192 Branches \u2192 Add rule \u2192 Require status checks\n")
396
+ chalk.dim(" 3. Run: workflow doctor (for optimization suggestions)\n")
1200
397
  );
1201
398
  }
1202
399
  }
1203
400
 
1204
401
  // src/cli/commands/validate.ts
1205
402
  import chalk2 from "chalk";
1206
- import { execa as execa2 } from "execa";
403
+ import { execa } from "execa";
1207
404
  async function validateCommand(type, value, _options = {}) {
1208
405
  const config = await loadConfig();
1209
406
  if (!config) {
@@ -1218,7 +415,7 @@ async function validateCommand(type, value, _options = {}) {
1218
415
  switch (type) {
1219
416
  case "branch": {
1220
417
  if (!targetValue) {
1221
- const { stdout } = await execa2("git", ["branch", "--show-current"]);
418
+ const { stdout } = await execa("git", ["branch", "--show-current"]);
1222
419
  targetValue = stdout.trim();
1223
420
  }
1224
421
  result = await validateBranchName(targetValue, config);
@@ -1226,7 +423,7 @@ async function validateCommand(type, value, _options = {}) {
1226
423
  }
1227
424
  case "commit": {
1228
425
  if (!targetValue) {
1229
- const { stdout } = await execa2("git", ["log", "-1", "--pretty=%s"]);
426
+ const { stdout } = await execa("git", ["log", "-1", "--pretty=%s"]);
1230
427
  targetValue = stdout.trim();
1231
428
  }
1232
429
  result = await validateCommitMessage(targetValue, config);
@@ -1275,324 +472,10 @@ async function validateCommand(type, value, _options = {}) {
1275
472
  }
1276
473
 
1277
474
  // src/cli/commands/config.ts
1278
- import { Command } from "commander";
1279
- import { join as join5 } from "path";
1280
- import { existsSync as existsSync5, writeFileSync } from "fs";
1281
- import prompts from "prompts";
1282
475
  import chalk3 from "chalk";
1283
- function createConfigCommand() {
1284
- const command = new Command("config").description("Manage workflow configuration").option("-f, --force", "Skip validation checks").option("--cwd <path>", "Working directory", process.cwd());
1285
- command.command("validate").description("Validate workflow configuration").action(async () => {
1286
- const opts = command.opts();
1287
- await validateConfigAction(opts);
1288
- });
1289
- command.command("add").description("Add configuration items").argument("<type>", "Type to add (scope)").action(async (type) => {
1290
- const opts = command.opts();
1291
- if (type === "scope") {
1292
- await addScopeAction(opts);
1293
- } else {
1294
- console.error(
1295
- chalk3.red(
1296
- `Unknown type: ${type}. Currently only "scope" is supported.`
1297
- )
1298
- );
1299
- process.exit(1);
1300
- }
1301
- });
1302
- command.command("remove").description("Remove configuration items").argument("<type>", "Type to remove (scope)").argument("<name>", "Name of the item to remove").action(async (type, name) => {
1303
- const opts = command.opts();
1304
- if (type === "scope") {
1305
- await removeScopeAction(name, opts);
1306
- } else {
1307
- console.error(
1308
- chalk3.red(
1309
- `Unknown type: ${type}. Currently only "scope" is supported.`
1310
- )
1311
- );
1312
- process.exit(1);
1313
- }
1314
- });
1315
- command.command("list").description("List configuration items").argument("[type]", "Type to list (scopes, reserved, all)", "all").action(async (type) => {
1316
- const opts = command.opts();
1317
- await listConfigAction(type, opts);
1318
- });
1319
- command.command("get").description("Get a configuration value").argument("<path>", "Configuration path (e.g., scopes[0].name)").action(async (path2) => {
1320
- const opts = command.opts();
1321
- await getConfigValue(path2, opts);
1322
- });
1323
- command.command("set").description("Set a configuration value").argument("<path>", "Configuration path (e.g., reservedScopeNames)").argument("<value>", "Value to set").action(async (path2, value) => {
1324
- const opts = command.opts();
1325
- await setConfigValue(path2, value, opts);
1326
- });
1327
- return command;
1328
- }
1329
- async function validateConfigAction(opts) {
1330
- console.log(chalk3.blue("\u{1F50D} Validating workflow configuration..."));
1331
- const result = await validateConfig(opts.cwd || process.cwd());
1332
- if (result.valid) {
1333
- console.log(chalk3.green("\u2713 Configuration is valid"));
1334
- process.exit(0);
1335
- } else {
1336
- console.log(chalk3.red("\u2717 Configuration has errors:\n"));
1337
- result.errors.forEach((err) => {
1338
- console.log(chalk3.red(` \u2022 ${err}`));
1339
- });
1340
- if (result.warnings.length > 0) {
1341
- console.log(chalk3.yellow("\n\u26A0 Warnings:\n"));
1342
- result.warnings.forEach((warn) => {
1343
- console.log(chalk3.yellow(` \u2022 ${warn}`));
1344
- });
1345
- }
1346
- console.log(chalk3.gray("\n\u{1F4A1} Fix these issues in workflow.config.json"));
1347
- process.exit(1);
1348
- }
1349
- }
1350
- async function addScopeAction(opts) {
1351
- const cwd = opts.cwd || process.cwd();
1352
- const configPath = join5(cwd, "workflow.config.json");
1353
- if (!existsSync5(configPath)) {
1354
- console.error(
1355
- chalk3.red("No workflow.config.json found. Run: workflow init")
1356
- );
1357
- process.exit(1);
1358
- }
1359
- const config = await loadConfig(cwd);
1360
- if (!config) {
1361
- console.error(chalk3.red("Failed to load configuration"));
1362
- process.exit(1);
1363
- }
1364
- const reservedNames = config.reservedScopeNames || DEFAULT_RESERVED_SCOPE_NAMES;
1365
- const existingNames = config.scopes.map((s) => s.name);
1366
- const response = await prompts([
1367
- {
1368
- type: "text",
1369
- name: "name",
1370
- message: "Scope name:",
1371
- validate: (value) => {
1372
- if (!value) return "Name is required";
1373
- if (existingNames.includes(value))
1374
- return `Scope "${value}" already exists`;
1375
- const validation = validateScopeName(value, reservedNames);
1376
- if (!validation.valid) {
1377
- return validation.error + (validation.suggestion ? ` Try: ${validation.suggestion}` : "");
1378
- }
1379
- return true;
1380
- }
1381
- },
1382
- {
1383
- type: "text",
1384
- name: "description",
1385
- message: "Description:",
1386
- validate: (value) => value ? true : "Description is required"
1387
- },
1388
- {
1389
- type: "multiselect",
1390
- name: "allowedTypes",
1391
- message: "Allowed commit types (space to select, enter to continue):",
1392
- choices: [
1393
- { title: "feat", value: "feat", selected: true },
1394
- { title: "fix", value: "fix", selected: true },
1395
- { title: "docs", value: "docs", selected: false },
1396
- { title: "style", value: "style", selected: false },
1397
- { title: "refactor", value: "refactor", selected: false },
1398
- { title: "perf", value: "perf", selected: false },
1399
- { title: "test", value: "test", selected: false },
1400
- { title: "build", value: "build", selected: false },
1401
- { title: "ci", value: "ci", selected: false },
1402
- { title: "chore", value: "chore", selected: false },
1403
- { title: "revert", value: "revert", selected: false }
1404
- ],
1405
- min: 1
1406
- },
1407
- {
1408
- type: "text",
1409
- name: "mandatoryGuidelines",
1410
- message: "Mandatory guidelines (comma-separated, or press enter to skip):",
1411
- initial: ""
1412
- }
1413
- ]);
1414
- if (!response.name) {
1415
- console.log(chalk3.yellow("Cancelled"));
1416
- process.exit(0);
1417
- }
1418
- if (!opts.force) {
1419
- const validation = validateScopeName(response.name, reservedNames);
1420
- if (!validation.valid) {
1421
- console.error(chalk3.red(`
1422
- \u2717 ${validation.error}`));
1423
- if (validation.suggestion) {
1424
- console.log(chalk3.yellow(`\u{1F4A1} Suggestion: ${validation.suggestion}`));
1425
- }
1426
- console.log(chalk3.gray("\nUse --force to override this check"));
1427
- process.exit(1);
1428
- }
1429
- }
1430
- const newScope = {
1431
- name: response.name,
1432
- description: response.description,
1433
- allowedTypes: response.allowedTypes
1434
- };
1435
- if (response.mandatoryGuidelines) {
1436
- const guidelines = response.mandatoryGuidelines.split(",").map((g) => g.trim()).filter((g) => g.length > 0);
1437
- if (guidelines.length > 0) {
1438
- newScope.mandatoryGuidelines = guidelines;
1439
- }
1440
- }
1441
- config.scopes.push(newScope);
1442
- const configContent = JSON.stringify(config, null, 2) + "\n";
1443
- writeFileSync(configPath, configContent, "utf-8");
1444
- console.log(chalk3.green(`
1445
- \u2713 Added scope: ${response.name}`));
1446
- console.log(chalk3.gray(` Description: ${response.description}`));
1447
- console.log(chalk3.gray(` Types: ${response.allowedTypes.join(", ")}`));
1448
- if (newScope.mandatoryGuidelines) {
1449
- console.log(
1450
- chalk3.gray(` Guidelines: ${newScope.mandatoryGuidelines.join(", ")}`)
1451
- );
1452
- }
1453
- }
1454
- async function removeScopeAction(name, opts) {
1455
- const cwd = opts.cwd || process.cwd();
1456
- const configPath = join5(cwd, "workflow.config.json");
1457
- if (!existsSync5(configPath)) {
1458
- console.error(chalk3.red("No workflow.config.json found"));
1459
- process.exit(1);
1460
- }
1461
- const config = await loadConfig(cwd);
1462
- if (!config) {
1463
- console.error(chalk3.red("Failed to load configuration"));
1464
- process.exit(1);
1465
- }
1466
- const scopeIndex = config.scopes.findIndex((s) => s.name === name);
1467
- if (scopeIndex === -1) {
1468
- console.error(chalk3.red(`Scope "${name}" not found`));
1469
- process.exit(1);
1470
- }
1471
- const response = await prompts({
1472
- type: "confirm",
1473
- name: "confirmed",
1474
- message: `Remove scope "${name}"?`,
1475
- initial: false
1476
- });
1477
- if (!response.confirmed) {
1478
- console.log(chalk3.yellow("Cancelled"));
1479
- process.exit(0);
1480
- }
1481
- config.scopes.splice(scopeIndex, 1);
1482
- const configContent = JSON.stringify(config, null, 2) + "\n";
1483
- writeFileSync(configPath, configContent, "utf-8");
1484
- console.log(chalk3.green(`\u2713 Removed scope: ${name}`));
1485
- }
1486
- async function listConfigAction(type, opts) {
1487
- const cwd = opts.cwd || process.cwd();
1488
- const config = await loadConfig(cwd);
1489
- if (!config) {
1490
- console.error(chalk3.red("No configuration found"));
1491
- process.exit(1);
1492
- }
1493
- if (type === "scopes" || type === "all") {
1494
- console.log(chalk3.blue("\n\u{1F4CB} Scopes:"));
1495
- if (config.scopes.length === 0) {
1496
- console.log(chalk3.gray(" (none)"));
1497
- } else {
1498
- config.scopes.forEach((scope, index) => {
1499
- console.log(chalk3.green(`
1500
- ${index + 1}. ${scope.name}`));
1501
- console.log(chalk3.gray(` ${scope.description}`));
1502
- console.log(
1503
- chalk3.gray(` Types: ${scope.allowedTypes?.join(", ") || "all"}`)
1504
- );
1505
- if (scope.mandatoryGuidelines && scope.mandatoryGuidelines.length > 0) {
1506
- console.log(
1507
- chalk3.gray(
1508
- ` Guidelines: ${scope.mandatoryGuidelines.join(", ")}`
1509
- )
1510
- );
1511
- }
1512
- });
1513
- }
1514
- }
1515
- if (type === "reserved" || type === "all") {
1516
- const reserved = config.reservedScopeNames || DEFAULT_RESERVED_SCOPE_NAMES;
1517
- console.log(chalk3.blue("\n\u{1F6AB} Reserved Scope Names:"));
1518
- console.log(chalk3.gray(` ${reserved.join(", ")}`));
1519
- console.log(
1520
- chalk3.gray(
1521
- '\n \u{1F4A1} Configure in workflow.config.json: "reservedScopeNames"'
1522
- )
1523
- );
1524
- }
1525
- }
1526
- async function getConfigValue(path2, opts) {
1527
- const cwd = opts.cwd || process.cwd();
1528
- const config = await loadConfig(cwd);
1529
- if (!config) {
1530
- console.error(chalk3.red("No configuration found"));
1531
- process.exit(1);
1532
- }
1533
- const value = resolvePath(config, path2);
1534
- if (value === void 0) {
1535
- console.error(chalk3.red(`Path not found: ${path2}`));
1536
- process.exit(1);
1537
- }
1538
- console.log(JSON.stringify(value, null, 2));
1539
- }
1540
- async function setConfigValue(path2, value, opts) {
1541
- const cwd = opts.cwd || process.cwd();
1542
- const configPath = join5(cwd, "workflow.config.json");
1543
- if (!existsSync5(configPath)) {
1544
- console.error(chalk3.red("No workflow.config.json found"));
1545
- process.exit(1);
1546
- }
1547
- const config = await loadConfig(cwd);
1548
- if (!config) {
1549
- console.error(chalk3.red("Failed to load configuration"));
1550
- process.exit(1);
1551
- }
1552
- let parsedValue;
1553
- try {
1554
- parsedValue = JSON.parse(value);
1555
- } catch {
1556
- parsedValue = value;
1557
- }
1558
- setPath(config, path2, parsedValue);
1559
- if (!opts.force) {
1560
- const validation = await validateConfig(cwd);
1561
- if (!validation.valid) {
1562
- console.error(chalk3.red("\u2717 Invalid configuration after change:"));
1563
- validation.errors.forEach(
1564
- (err) => console.error(chalk3.red(` \u2022 ${err}`))
1565
- );
1566
- console.log(chalk3.gray("\nUse --force to skip validation"));
1567
- process.exit(1);
1568
- }
1569
- }
1570
- const configContent = JSON.stringify(config, null, 2) + "\n";
1571
- writeFileSync(configPath, configContent, "utf-8");
1572
- console.log(chalk3.green(`\u2713 Set ${path2} = ${JSON.stringify(parsedValue)}`));
1573
- }
1574
- function resolvePath(obj, path2) {
1575
- const parts = path2.split(/[\.\[\]]+/).filter(Boolean);
1576
- let current = obj;
1577
- for (const part of parts) {
1578
- if (current === void 0 || current === null) {
1579
- return void 0;
1580
- }
1581
- current = current[part];
1582
- }
1583
- return current;
1584
- }
1585
- function setPath(obj, path2, value) {
1586
- const parts = path2.split(/[\.\[\]]+/).filter(Boolean);
1587
- let current = obj;
1588
- for (let i = 0; i < parts.length - 1; i++) {
1589
- const part = parts[i];
1590
- if (current[part] === void 0) {
1591
- current[part] = {};
1592
- }
1593
- current = current[part];
1594
- }
1595
- current[parts[parts.length - 1]] = value;
476
+ async function configCommand(action, key, value) {
477
+ console.log(chalk3.yellow("Config command not yet implemented"));
478
+ console.log({ action, key, value });
1596
479
  }
1597
480
 
1598
481
  // src/cli/commands/suggest.ts
@@ -1617,370 +500,53 @@ async function suggestCommand(feedback, options = {}) {
1617
500
  if (p2.isCancel(categoryChoice)) {
1618
501
  p2.cancel("Suggestion cancelled");
1619
502
  process.exit(0);
1620
- }
1621
- category = categoryChoice;
1622
- }
1623
- const result = await tracker.submit(feedback, options.author, category);
1624
- if (!result.success) {
1625
- console.log(chalk4.red("\u2717 Suggestion rejected"));
1626
- console.log(chalk4.dim(` Reason: ${result.error}`));
1627
- process.exit(1);
1628
- }
1629
- console.log(chalk4.green("\u2713 Suggestion submitted successfully!"));
1630
- console.log(chalk4.dim(` ID: ${result.suggestion?.id}`));
1631
- console.log(chalk4.dim(` Status: ${result.suggestion?.status}`));
1632
- console.log(chalk4.dim(` Category: ${result.suggestion?.category}`));
1633
- console.log(chalk4.dim("\nYour suggestion will be:"));
1634
- console.log(chalk4.dim(" 1. Reviewed by the community"));
1635
- console.log(chalk4.dim(" 2. Prioritized based on impact"));
1636
- console.log(
1637
- chalk4.dim(" 3. Incorporated into future releases if approved\n")
1638
- );
1639
- }
1640
-
1641
- // src/cli/commands/doctor.ts
1642
- import chalk5 from "chalk";
1643
-
1644
- // src/validators/guidelines.ts
1645
- import { existsSync as existsSync6 } from "fs";
1646
- import { readFile as readFile3, readdir } from "fs/promises";
1647
- import { join as join6 } from "path";
1648
- function getEffectiveMandatoryTemplates(guidelinesConfig) {
1649
- const coreMandatory = getMandatoryTemplateFilenames();
1650
- if (!guidelinesConfig) {
1651
- return coreMandatory;
1652
- }
1653
- let mandatory = [...coreMandatory];
1654
- if (guidelinesConfig.additionalMandatory) {
1655
- for (const template of guidelinesConfig.additionalMandatory) {
1656
- if (!mandatory.includes(template) && templateMetadata[template]) {
1657
- mandatory.push(template);
1658
- }
1659
- }
1660
- }
1661
- if (guidelinesConfig.optionalOverrides) {
1662
- mandatory = mandatory.filter(
1663
- (t) => !guidelinesConfig.optionalOverrides.includes(t)
1664
- );
1665
- }
1666
- return mandatory;
1667
- }
1668
- async function validateGuidelinesExist(projectPath = process.cwd(), config) {
1669
- const guidelinesDir = join6(projectPath, "guidelines");
1670
- const result = {
1671
- valid: true,
1672
- missingMandatory: [],
1673
- presentMandatory: [],
1674
- presentOptional: [],
1675
- errors: []
1676
- };
1677
- if (!existsSync6(guidelinesDir)) {
1678
- const mandatory2 = getEffectiveMandatoryTemplates(config?.guidelines);
1679
- result.valid = false;
1680
- result.missingMandatory = mandatory2;
1681
- result.errors.push(
1682
- "Guidelines directory does not exist. Run: workflow init"
1683
- );
1684
- return result;
1685
- }
1686
- let existingFiles = [];
1687
- try {
1688
- existingFiles = await readdir(guidelinesDir);
1689
- } catch (error) {
1690
- result.valid = false;
1691
- result.errors.push(
1692
- `Cannot read guidelines directory: ${error instanceof Error ? error.message : String(error)}`
1693
- );
1694
- return result;
1695
- }
1696
- const mandatory = getEffectiveMandatoryTemplates(config?.guidelines);
1697
- for (const template of mandatory) {
1698
- if (existingFiles.includes(template)) {
1699
- result.presentMandatory.push(template);
1700
- } else {
1701
- result.missingMandatory.push(template);
1702
- }
1703
- }
1704
- const allTemplateNames = Object.keys(templateMetadata);
1705
- for (const file of existingFiles) {
1706
- if (allTemplateNames.includes(file) && !mandatory.includes(file)) {
1707
- result.presentOptional.push(file);
1708
- }
1709
- }
1710
- if (result.missingMandatory.length > 0) {
1711
- result.valid = false;
1712
- result.errors.push(
1713
- `Missing mandatory guidelines: ${result.missingMandatory.join(", ")}. Run: workflow init`
1714
- );
1715
- }
1716
- return result;
1717
- }
1718
- async function validateGitHubActionsSetup(projectPath = process.cwd()) {
1719
- const workflowsDir = join6(projectPath, ".github", "workflows");
1720
- const result = {
1721
- valid: true,
1722
- hasWorkflowFile: false,
1723
- hasLintCheck: false,
1724
- hasTypecheckCheck: false,
1725
- hasFormatCheck: false,
1726
- hasBuildCheck: false,
1727
- hasTestCheck: false,
1728
- errors: [],
1729
- warnings: []
1730
- };
1731
- if (!existsSync6(workflowsDir)) {
1732
- result.valid = false;
1733
- result.errors.push(
1734
- "GitHub Actions workflows directory does not exist. Run: workflow github:setup"
1735
- );
1736
- return result;
1737
- }
1738
- let workflowFiles = [];
1739
- try {
1740
- workflowFiles = await readdir(workflowsDir);
1741
- } catch (error) {
1742
- result.valid = false;
1743
- result.errors.push(
1744
- `Cannot read workflows directory: ${error instanceof Error ? error.message : String(error)}`
1745
- );
1746
- return result;
1747
- }
1748
- const ciWorkflowNames = [
1749
- "ci.yml",
1750
- "ci.yaml",
1751
- "main.yml",
1752
- "main.yaml",
1753
- "build.yml",
1754
- "build.yaml",
1755
- "test.yml",
1756
- "test.yaml"
1757
- ];
1758
- const ciWorkflows = workflowFiles.filter(
1759
- (f) => ciWorkflowNames.includes(f.toLowerCase())
1760
- );
1761
- if (ciWorkflows.length === 0) {
1762
- result.valid = false;
1763
- result.errors.push("No CI workflow file found. Run: workflow github:setup");
1764
- return result;
1765
- }
1766
- result.hasWorkflowFile = true;
1767
- const workflowPath = join6(workflowsDir, ciWorkflows[0]);
1768
- let workflowContent = "";
1769
- try {
1770
- workflowContent = await readFile3(workflowPath, "utf-8");
1771
- } catch (error) {
1772
- result.warnings.push(
1773
- `Cannot read workflow file: ${error instanceof Error ? error.message : String(error)}`
1774
- );
1775
- return result;
1776
- }
1777
- const contentLower = workflowContent.toLowerCase();
1778
- if (contentLower.includes("lint") || contentLower.includes("eslint")) {
1779
- result.hasLintCheck = true;
1780
- } else {
1781
- result.warnings.push("CI workflow may be missing lint check");
1782
- }
1783
- if (contentLower.includes("typecheck") || contentLower.includes("type-check") || contentLower.includes("tsc")) {
1784
- result.hasTypecheckCheck = true;
1785
- } else {
1786
- result.warnings.push("CI workflow may be missing typecheck");
1787
- }
1788
- if (contentLower.includes("format") || contentLower.includes("prettier")) {
1789
- result.hasFormatCheck = true;
1790
- } else {
1791
- result.warnings.push("CI workflow may be missing format check");
1792
- }
1793
- if (contentLower.includes("build")) {
1794
- result.hasBuildCheck = true;
1795
- } else {
1796
- result.warnings.push("CI workflow may be missing build step");
1797
- }
1798
- if (contentLower.includes("test") && !contentLower.includes("typecheck")) {
1799
- result.hasTestCheck = true;
1800
- } else {
1801
- result.warnings.push("CI workflow may be missing test step");
1802
- }
1803
- const mandatoryChecks = [
1804
- result.hasLintCheck,
1805
- result.hasTypecheckCheck,
1806
- result.hasFormatCheck
1807
- ];
1808
- if (!mandatoryChecks.every(Boolean)) {
1809
- result.valid = false;
1810
- result.errors.push(
1811
- "CI workflow is missing mandatory checks (lint, typecheck, format)"
1812
- );
1813
- }
1814
- return result;
1815
- }
1816
-
1817
- // src/cli/commands/doctor.ts
1818
- async function doctorCommand(options = {}) {
1819
- const cwd = process.cwd();
1820
- if (options.checkGuidelinesOnly) {
1821
- const result = await validateGuidelinesExist(cwd);
1822
- if (!result.valid) {
1823
- console.error(chalk5.red("\u2717 Missing mandatory guidelines"));
1824
- for (const missing of result.missingMandatory) {
1825
- console.error(chalk5.red(` \u2022 ${missing}`));
1826
- }
1827
- process.exit(1);
1828
- }
1829
- process.exit(0);
1830
- }
1831
- console.log(chalk5.bold.cyan("\n\u{1F3E5} Workflow Agent Health Check\n"));
1832
- const config = await loadConfig();
1833
- let hasErrors = false;
1834
- let hasWarnings = false;
1835
- console.log(chalk5.bold("\u{1F4CB} Configuration"));
1836
- if (!config) {
1837
- console.error(chalk5.red(" \u2717 No workflow configuration found"));
1838
- console.log(chalk5.yellow(" Run: workflow init"));
1839
- process.exit(1);
1840
- }
1841
- console.log(chalk5.green(" \u2713 Configuration loaded successfully"));
1842
- console.log(chalk5.dim(` Project: ${config.projectName}`));
1843
- console.log(chalk5.dim(` Scopes: ${config.scopes.length} configured`));
1844
- console.log(chalk5.dim(` Enforcement: ${config.enforcement}`));
1845
- console.log(chalk5.dim(` Language: ${config.language}`));
1846
- console.log(chalk5.bold("\n\u{1F4DA} Guidelines"));
1847
- const guidelinesResult = await validateGuidelinesExist(cwd, config);
1848
- if (guidelinesResult.valid) {
1849
- console.log(
1850
- chalk5.green(
1851
- ` \u2713 All ${guidelinesResult.presentMandatory.length} mandatory guidelines present`
1852
- )
1853
- );
1854
- if (guidelinesResult.presentOptional.length > 0) {
1855
- console.log(
1856
- chalk5.dim(
1857
- ` + ${guidelinesResult.presentOptional.length} optional guidelines`
1858
- )
1859
- );
1860
- }
1861
- } else {
1862
- hasErrors = true;
1863
- console.log(
1864
- chalk5.red(
1865
- ` \u2717 Missing ${guidelinesResult.missingMandatory.length} mandatory guidelines:`
1866
- )
1867
- );
1868
- for (const missing of guidelinesResult.missingMandatory) {
1869
- console.log(chalk5.red(` \u2022 ${missing}`));
1870
- }
1871
- console.log(chalk5.yellow(" Run: workflow init"));
1872
- }
1873
- console.log(chalk5.bold("\n\u{1F517} Git Hooks"));
1874
- if (!hasGitRepo(cwd)) {
1875
- console.log(chalk5.yellow(" \u26A0 No git repository found"));
1876
- hasWarnings = true;
1877
- } else {
1878
- const hookStatuses = await getAllHooksStatus(cwd);
1879
- const installedHooks = hookStatuses.filter((h) => h.installed);
1880
- if (installedHooks.length === hookStatuses.length) {
1881
- console.log(
1882
- chalk5.green(` \u2713 All ${installedHooks.length} hooks installed`)
1883
- );
1884
- for (const hook of hookStatuses) {
1885
- const extra = hook.wrappedOriginal ? " (wrapping original)" : "";
1886
- console.log(chalk5.dim(` \u2022 ${hook.hookType}${extra}`));
1887
- }
1888
- } else if (installedHooks.length > 0) {
1889
- hasWarnings = true;
1890
- console.log(
1891
- chalk5.yellow(
1892
- ` \u26A0 ${installedHooks.length}/${hookStatuses.length} hooks installed`
1893
- )
1894
- );
1895
- for (const hook of hookStatuses) {
1896
- if (hook.installed) {
1897
- console.log(chalk5.green(` \u2713 ${hook.hookType}`));
1898
- } else {
1899
- console.log(chalk5.yellow(` \u2717 ${hook.hookType}`));
1900
- }
1901
- }
1902
- console.log(chalk5.yellow(" Run: workflow hooks install"));
1903
- } else {
1904
- hasWarnings = true;
1905
- console.log(chalk5.yellow(" \u26A0 No hooks installed"));
1906
- console.log(chalk5.yellow(" Run: workflow hooks install"));
1907
- }
1908
- }
1909
- console.log(chalk5.bold("\n\u{1F680} CI/CD Pipeline"));
1910
- const repoInfo = await getRepoInfo(cwd);
1911
- if (!repoInfo.isGitRepo) {
1912
- console.log(chalk5.dim(" \u25CB No git repository (CI check skipped)"));
1913
- } else if (!repoInfo.isGitHub) {
1914
- console.log(chalk5.dim(" \u25CB Not a GitHub repository (CI check skipped)"));
1915
- console.log(chalk5.dim(` Remote: ${repoInfo.remoteUrl || "none"}`));
1916
- } else {
1917
- console.log(
1918
- chalk5.dim(
1919
- ` Repository: ${repoInfo.github?.owner}/${repoInfo.github?.repo}`
1920
- )
1921
- );
1922
- const ciResult = await validateGitHubActionsSetup(cwd);
1923
- if (ciResult.valid) {
1924
- console.log(chalk5.green(" \u2713 GitHub Actions CI configured correctly"));
1925
- const checks = [
1926
- ciResult.hasLintCheck && "lint",
1927
- ciResult.hasTypecheckCheck && "typecheck",
1928
- ciResult.hasFormatCheck && "format",
1929
- ciResult.hasBuildCheck && "build",
1930
- ciResult.hasTestCheck && "test"
1931
- ].filter(Boolean);
1932
- console.log(chalk5.dim(` Checks: ${checks.join(", ")}`));
1933
- } else if (!ciResult.hasWorkflowFile) {
1934
- hasErrors = true;
1935
- console.log(chalk5.red(" \u2717 No CI workflow found"));
1936
- console.log(chalk5.yellow(" Run: workflow github:setup"));
1937
- } else {
1938
- hasWarnings = true;
1939
- console.log(chalk5.yellow(" \u26A0 CI workflow may be incomplete"));
1940
- for (const warning of ciResult.warnings) {
1941
- console.log(chalk5.yellow(` \u2022 ${warning}`));
1942
- }
1943
- console.log(chalk5.yellow(" Run: workflow github:setup to regenerate"));
1944
- }
1945
- }
1946
- console.log(chalk5.bold("\n\u{1F4A1} Suggestions"));
1947
- if (repoInfo.isGitHub && hasCIWorkflow(cwd)) {
1948
- console.log(chalk5.cyan(" \u2192 Enable branch protection on GitHub"));
1949
- console.log(chalk5.dim(" Settings \u2192 Branches \u2192 Add rule"));
1950
- console.log(
1951
- chalk5.dim(" \u2611 Require status checks to pass before merging")
1952
- );
1953
- console.log(
1954
- chalk5.dim(" \u2611 Require branches to be up to date before merging")
1955
- );
1956
- }
1957
- if (config.enforcement !== "strict") {
1958
- console.log(chalk5.cyan(` \u2192 Consider switching to 'strict' enforcement`));
1959
- console.log(chalk5.dim(` Current: ${config.enforcement}`));
503
+ }
504
+ category = categoryChoice;
1960
505
  }
1961
- if (config.scopes.length < 5) {
1962
- console.log(
1963
- chalk5.cyan(" \u2192 Consider adding more scopes for better organization")
1964
- );
1965
- console.log(chalk5.dim(" Run: workflow config add scope <name>"));
506
+ const result = await tracker.submit(feedback, options.author, category);
507
+ if (!result.success) {
508
+ console.log(chalk4.red("\u2717 Suggestion rejected"));
509
+ console.log(chalk4.dim(` Reason: ${result.error}`));
510
+ process.exit(1);
1966
511
  }
1967
- console.log(chalk5.bold("\n\u{1F4CA} Summary"));
1968
- if (hasErrors) {
1969
- console.log(chalk5.red(" \u2717 Health check failed - issues found"));
512
+ console.log(chalk4.green("\u2713 Suggestion submitted successfully!"));
513
+ console.log(chalk4.dim(` ID: ${result.suggestion?.id}`));
514
+ console.log(chalk4.dim(` Status: ${result.suggestion?.status}`));
515
+ console.log(chalk4.dim(` Category: ${result.suggestion?.category}`));
516
+ console.log(chalk4.dim("\nYour suggestion will be:"));
517
+ console.log(chalk4.dim(" 1. Reviewed by the community"));
518
+ console.log(chalk4.dim(" 2. Prioritized based on impact"));
519
+ console.log(
520
+ chalk4.dim(" 3. Incorporated into future releases if approved\n")
521
+ );
522
+ }
523
+
524
+ // src/cli/commands/doctor.ts
525
+ import chalk5 from "chalk";
526
+ async function doctorCommand() {
527
+ console.log(chalk5.bold.cyan("\n\u{1F3E5} Workflow Agent Health Check\n"));
528
+ const config = await loadConfig();
529
+ if (!config) {
530
+ console.error(chalk5.red("\u2717 No workflow configuration found"));
531
+ console.log(chalk5.yellow(" Run: workflow init"));
1970
532
  process.exit(1);
1971
- } else if (hasWarnings) {
1972
- console.log(chalk5.yellow(" \u26A0 Health check passed with warnings"));
1973
- } else {
1974
- console.log(chalk5.green(" \u2713 All checks passed"));
1975
533
  }
1976
- console.log("");
534
+ console.log(chalk5.green("\u2713 Configuration loaded successfully"));
535
+ console.log(chalk5.dim(` Project: ${config.projectName}`));
536
+ console.log(chalk5.dim(` Scopes: ${config.scopes.length} configured`));
537
+ console.log(chalk5.dim(` Enforcement: ${config.enforcement}`));
538
+ console.log(chalk5.dim(` Language: ${config.language}`));
539
+ console.log(chalk5.cyan("\n\u{1F4A1} Suggestions:\n"));
540
+ console.log(chalk5.dim(" \u2022 Health check analysis coming soon"));
541
+ console.log(chalk5.dim(" \u2022 Git history analysis coming soon"));
542
+ console.log(chalk5.dim(" \u2022 Optimization recommendations coming soon"));
1977
543
  }
1978
544
 
1979
545
  // src/cli/commands/setup.ts
1980
546
  import * as p3 from "@clack/prompts";
1981
547
  import chalk6 from "chalk";
1982
- import { readFileSync, writeFileSync as writeFileSync2, existsSync as existsSync7 } from "fs";
1983
- import { join as join7 } from "path";
548
+ import { readFileSync, writeFileSync, existsSync as existsSync2 } from "fs";
549
+ import { join as join2 } from "path";
1984
550
  var WORKFLOW_SCRIPTS = {
1985
551
  "workflow:init": "workflow-agent init",
1986
552
  "workflow:validate": "workflow-agent validate",
@@ -1990,8 +556,8 @@ var WORKFLOW_SCRIPTS = {
1990
556
  async function setupCommand() {
1991
557
  p3.intro(chalk6.bgBlue(" workflow-agent setup "));
1992
558
  const cwd = process.cwd();
1993
- const packageJsonPath = join7(cwd, "package.json");
1994
- if (!existsSync7(packageJsonPath)) {
559
+ const packageJsonPath = join2(cwd, "package.json");
560
+ if (!existsSync2(packageJsonPath)) {
1995
561
  p3.cancel("No package.json found in current directory");
1996
562
  process.exit(1);
1997
563
  }
@@ -2034,7 +600,7 @@ async function setupCommand() {
2034
600
  for (const [scriptName, scriptCommand] of Object.entries(scriptsToAdd)) {
2035
601
  packageJson.scripts[scriptName] = scriptCommand;
2036
602
  }
2037
- writeFileSync2(
603
+ writeFileSync(
2038
604
  packageJsonPath,
2039
605
  JSON.stringify(packageJson, null, 2) + "\n",
2040
606
  "utf-8"
@@ -2052,15 +618,15 @@ async function setupCommand() {
2052
618
  // src/cli/commands/scope-create.ts
2053
619
  import * as p4 from "@clack/prompts";
2054
620
  import chalk7 from "chalk";
2055
- import { existsSync as existsSync8 } from "fs";
2056
- import { writeFile as writeFile4, mkdir as mkdir4, readFile as readFile4 } from "fs/promises";
2057
- import { join as join8 } from "path";
621
+ import { existsSync as existsSync3 } from "fs";
622
+ import { writeFile as writeFile2, mkdir as mkdir2, readFile } from "fs/promises";
623
+ import { join as join3 } from "path";
2058
624
  async function scopeCreateCommand(options) {
2059
625
  console.log(chalk7.bold.cyan("\n\u{1F3A8} Create Custom Scope Package\n"));
2060
626
  const cwd = process.cwd();
2061
627
  const isNonInteractive = !!(options.name && options.scopes && options.presetName);
2062
- const isMonorepo2 = existsSync8(join8(cwd, "pnpm-workspace.yaml"));
2063
- if (isMonorepo2) {
628
+ const isMonorepo = existsSync3(join3(cwd, "pnpm-workspace.yaml"));
629
+ if (isMonorepo) {
2064
630
  console.log(chalk7.dim("\u2713 Detected monorepo workspace\n"));
2065
631
  }
2066
632
  const packageNameInput = isNonInteractive ? options.name : await p4.text({
@@ -2204,8 +770,8 @@ async function scopeCreateCommand(options) {
2204
770
  let outputDir;
2205
771
  if (options.outputDir) {
2206
772
  outputDir = options.outputDir;
2207
- } else if (isMonorepo2) {
2208
- outputDir = join8(cwd, "packages", `scopes-${packageName}`);
773
+ } else if (isMonorepo) {
774
+ outputDir = join3(cwd, "packages", `scopes-${packageName}`);
2209
775
  } else {
2210
776
  const customDir = await p4.text({
2211
777
  message: "Output directory:",
@@ -2216,9 +782,9 @@ async function scopeCreateCommand(options) {
2216
782
  p4.cancel("Operation cancelled");
2217
783
  process.exit(0);
2218
784
  }
2219
- outputDir = join8(cwd, customDir);
785
+ outputDir = join3(cwd, customDir);
2220
786
  }
2221
- if (existsSync8(outputDir)) {
787
+ if (existsSync3(outputDir)) {
2222
788
  const shouldOverwrite = await p4.confirm({
2223
789
  message: `Directory ${outputDir} already exists. Overwrite?`,
2224
790
  initialValue: false
@@ -2228,10 +794,10 @@ async function scopeCreateCommand(options) {
2228
794
  process.exit(0);
2229
795
  }
2230
796
  }
2231
- const spinner5 = p4.spinner();
2232
- spinner5.start("Creating package structure...");
797
+ const spinner4 = p4.spinner();
798
+ spinner4.start("Creating package structure...");
2233
799
  try {
2234
- await mkdir4(join8(outputDir, "src"), { recursive: true });
800
+ await mkdir2(join3(outputDir, "src"), { recursive: true });
2235
801
  const packageJson = {
2236
802
  name: `@workflow/scopes-${packageName}`,
2237
803
  version: "1.0.0",
@@ -2271,8 +837,8 @@ async function scopeCreateCommand(options) {
2271
837
  access: "public"
2272
838
  }
2273
839
  };
2274
- await writeFile4(
2275
- join8(outputDir, "package.json"),
840
+ await writeFile2(
841
+ join3(outputDir, "package.json"),
2276
842
  JSON.stringify(packageJson, null, 2),
2277
843
  "utf-8"
2278
844
  );
@@ -2284,8 +850,8 @@ async function scopeCreateCommand(options) {
2284
850
  },
2285
851
  include: ["src/**/*"]
2286
852
  };
2287
- await writeFile4(
2288
- join8(outputDir, "tsconfig.json"),
853
+ await writeFile2(
854
+ join3(outputDir, "tsconfig.json"),
2289
855
  JSON.stringify(tsconfig, null, 2),
2290
856
  "utf-8"
2291
857
  );
@@ -2299,7 +865,7 @@ export default defineConfig({
2299
865
  sourcemap: true,
2300
866
  });
2301
867
  `;
2302
- await writeFile4(join8(outputDir, "tsup.config.ts"), tsupConfig, "utf-8");
868
+ await writeFile2(join3(outputDir, "tsup.config.ts"), tsupConfig, "utf-8");
2303
869
  const indexTs = `import type { Scope } from '@hawkinside_out/workflow-agent/config';
2304
870
 
2305
871
  export const scopes: Scope[] = ${JSON.stringify(scopes, null, 2)};
@@ -2313,7 +879,7 @@ export const preset = {
2313
879
 
2314
880
  export default preset;
2315
881
  `;
2316
- await writeFile4(join8(outputDir, "src", "index.ts"), indexTs, "utf-8");
882
+ await writeFile2(join3(outputDir, "src", "index.ts"), indexTs, "utf-8");
2317
883
  if (!options.noTest) {
2318
884
  const testFile = `import { describe, it, expect } from 'vitest';
2319
885
  import { scopes, preset } from './index.js';
@@ -2354,16 +920,16 @@ describe('${presetName} Scope Preset', () => {
2354
920
  });
2355
921
  });
2356
922
  `;
2357
- await writeFile4(
2358
- join8(outputDir, "src", "index.test.ts"),
923
+ await writeFile2(
924
+ join3(outputDir, "src", "index.test.ts"),
2359
925
  testFile,
2360
926
  "utf-8"
2361
927
  );
2362
928
  }
2363
- spinner5.stop("\u2713 Package structure created");
2364
- if (isMonorepo2) {
2365
- const workspaceFile = join8(cwd, "pnpm-workspace.yaml");
2366
- const workspaceContent = await readFile4(workspaceFile, "utf-8");
929
+ spinner4.stop("\u2713 Package structure created");
930
+ if (isMonorepo) {
931
+ const workspaceFile = join3(cwd, "pnpm-workspace.yaml");
932
+ const workspaceContent = await readFile(workspaceFile, "utf-8");
2367
933
  const packagePath = `packages/scopes-${packageName}`;
2368
934
  if (!workspaceContent.includes(packagePath) && !workspaceContent.includes("packages/*")) {
2369
935
  console.log(
@@ -2412,7 +978,7 @@ describe('${presetName} Scope Preset', () => {
2412
978
  );
2413
979
  }
2414
980
  } catch (error) {
2415
- spinner5.stop("\u2717 Failed to create package");
981
+ spinner4.stop("\u2717 Failed to create package");
2416
982
  console.error(chalk7.red("\nError:"), error);
2417
983
  process.exit(1);
2418
984
  }
@@ -2421,9 +987,9 @@ describe('${presetName} Scope Preset', () => {
2421
987
  // src/cli/commands/scope-migrate.ts
2422
988
  import * as p5 from "@clack/prompts";
2423
989
  import chalk8 from "chalk";
2424
- import { existsSync as existsSync9 } from "fs";
2425
- import { writeFile as writeFile5, mkdir as mkdir5, readFile as readFile5 } from "fs/promises";
2426
- import { join as join9 } from "path";
990
+ import { existsSync as existsSync4 } from "fs";
991
+ import { writeFile as writeFile3, mkdir as mkdir3, readFile as readFile2 } from "fs/promises";
992
+ import { join as join4 } from "path";
2427
993
  async function scopeMigrateCommand(options) {
2428
994
  console.log(chalk8.bold.cyan("\n\u{1F504} Migrate Scopes to Custom Package\n"));
2429
995
  const cwd = process.cwd();
@@ -2467,8 +1033,8 @@ async function scopeMigrateCommand(options) {
2467
1033
  p5.cancel("Migration cancelled");
2468
1034
  process.exit(0);
2469
1035
  }
2470
- const isMonorepo2 = existsSync9(join9(cwd, "pnpm-workspace.yaml"));
2471
- if (isMonorepo2) {
1036
+ const isMonorepo = existsSync4(join4(cwd, "pnpm-workspace.yaml"));
1037
+ if (isMonorepo) {
2472
1038
  console.log(chalk8.dim("\n\u2713 Detected monorepo workspace\n"));
2473
1039
  }
2474
1040
  const packageNameInput = options.name || await p5.text({
@@ -2516,8 +1082,8 @@ async function scopeMigrateCommand(options) {
2516
1082
  let outputDir;
2517
1083
  if (options.outputDir) {
2518
1084
  outputDir = options.outputDir;
2519
- } else if (isMonorepo2) {
2520
- outputDir = join9(cwd, "packages", `scopes-${packageName}`);
1085
+ } else if (isMonorepo) {
1086
+ outputDir = join4(cwd, "packages", `scopes-${packageName}`);
2521
1087
  } else {
2522
1088
  const customDir = await p5.text({
2523
1089
  message: "Output directory:",
@@ -2528,9 +1094,9 @@ async function scopeMigrateCommand(options) {
2528
1094
  p5.cancel("Migration cancelled");
2529
1095
  process.exit(0);
2530
1096
  }
2531
- outputDir = join9(cwd, customDir);
1097
+ outputDir = join4(cwd, customDir);
2532
1098
  }
2533
- if (existsSync9(outputDir)) {
1099
+ if (existsSync4(outputDir)) {
2534
1100
  const shouldOverwrite = await p5.confirm({
2535
1101
  message: `Directory ${outputDir} already exists. Overwrite?`,
2536
1102
  initialValue: false
@@ -2540,10 +1106,10 @@ async function scopeMigrateCommand(options) {
2540
1106
  process.exit(0);
2541
1107
  }
2542
1108
  }
2543
- const spinner5 = p5.spinner();
2544
- spinner5.start("Migrating scopes to package...");
1109
+ const spinner4 = p5.spinner();
1110
+ spinner4.start("Migrating scopes to package...");
2545
1111
  try {
2546
- await mkdir5(join9(outputDir, "src"), { recursive: true });
1112
+ await mkdir3(join4(outputDir, "src"), { recursive: true });
2547
1113
  const packageJson = {
2548
1114
  name: `@workflow/scopes-${packageName}`,
2549
1115
  version: "1.0.0",
@@ -2583,8 +1149,8 @@ async function scopeMigrateCommand(options) {
2583
1149
  access: "public"
2584
1150
  }
2585
1151
  };
2586
- await writeFile5(
2587
- join9(outputDir, "package.json"),
1152
+ await writeFile3(
1153
+ join4(outputDir, "package.json"),
2588
1154
  JSON.stringify(packageJson, null, 2),
2589
1155
  "utf-8"
2590
1156
  );
@@ -2596,8 +1162,8 @@ async function scopeMigrateCommand(options) {
2596
1162
  },
2597
1163
  include: ["src/**/*"]
2598
1164
  };
2599
- await writeFile5(
2600
- join9(outputDir, "tsconfig.json"),
1165
+ await writeFile3(
1166
+ join4(outputDir, "tsconfig.json"),
2601
1167
  JSON.stringify(tsconfig, null, 2),
2602
1168
  "utf-8"
2603
1169
  );
@@ -2611,7 +1177,7 @@ export default defineConfig({
2611
1177
  sourcemap: true,
2612
1178
  });
2613
1179
  `;
2614
- await writeFile5(join9(outputDir, "tsup.config.ts"), tsupConfig, "utf-8");
1180
+ await writeFile3(join4(outputDir, "tsup.config.ts"), tsupConfig, "utf-8");
2615
1181
  const indexTs = `import type { Scope } from '@hawkinside_out/workflow-agent/config';
2616
1182
 
2617
1183
  export const scopes: Scope[] = ${JSON.stringify(config.scopes, null, 2)};
@@ -2625,7 +1191,7 @@ export const preset = {
2625
1191
 
2626
1192
  export default preset;
2627
1193
  `;
2628
- await writeFile5(join9(outputDir, "src", "index.ts"), indexTs, "utf-8");
1194
+ await writeFile3(join4(outputDir, "src", "index.ts"), indexTs, "utf-8");
2629
1195
  const testFile = `import { describe, it, expect } from 'vitest';
2630
1196
  import { scopes, preset } from './index.js';
2631
1197
  import { ScopeSchema } from '@hawkinside_out/workflow-agent/config';
@@ -2668,11 +1234,11 @@ describe('${presetName} Scope Preset (Migrated)', () => {
2668
1234
  });
2669
1235
  });
2670
1236
  `;
2671
- await writeFile5(join9(outputDir, "src", "index.test.ts"), testFile, "utf-8");
2672
- spinner5.stop("\u2713 Package created from migrated scopes");
2673
- if (isMonorepo2) {
2674
- const workspaceFile = join9(cwd, "pnpm-workspace.yaml");
2675
- const workspaceContent = await readFile5(workspaceFile, "utf-8");
1237
+ await writeFile3(join4(outputDir, "src", "index.test.ts"), testFile, "utf-8");
1238
+ spinner4.stop("\u2713 Package created from migrated scopes");
1239
+ if (isMonorepo) {
1240
+ const workspaceFile = join4(cwd, "pnpm-workspace.yaml");
1241
+ const workspaceContent = await readFile2(workspaceFile, "utf-8");
2676
1242
  const packagePath = `packages/scopes-${packageName}`;
2677
1243
  if (!workspaceContent.includes(packagePath) && !workspaceContent.includes("packages/*")) {
2678
1244
  console.log(
@@ -2688,7 +1254,7 @@ describe('${presetName} Scope Preset (Migrated)', () => {
2688
1254
  initialValue: false
2689
1255
  });
2690
1256
  if (!p5.isCancel(keepConfig) && !keepConfig) {
2691
- const configPath = join9(cwd, "workflow.config.json");
1257
+ const configPath = join4(cwd, "workflow.config.json");
2692
1258
  const updatedConfig = {
2693
1259
  ...config,
2694
1260
  scopes: [],
@@ -2696,7 +1262,7 @@ describe('${presetName} Scope Preset (Migrated)', () => {
2696
1262
  preset: `scopes-${packageName}`
2697
1263
  // Reference the new package
2698
1264
  };
2699
- await writeFile5(
1265
+ await writeFile3(
2700
1266
  configPath,
2701
1267
  JSON.stringify(updatedConfig, null, 2),
2702
1268
  "utf-8"
@@ -2741,710 +1307,100 @@ describe('${presetName} Scope Preset (Migrated)', () => {
2741
1307
  )
2742
1308
  );
2743
1309
  } catch (error) {
2744
- spinner5.stop("\u2717 Migration failed");
1310
+ spinner4.stop("\u2717 Migration failed");
2745
1311
  console.error(chalk8.red("\nError:"), error);
2746
1312
  process.exit(1);
2747
1313
  }
2748
1314
  }
2749
1315
 
2750
- // src/cli/commands/hooks.ts
1316
+ // src/cli/commands/verify.ts
2751
1317
  import chalk9 from "chalk";
2752
- async function hooksCommand(action) {
2753
- const cwd = process.cwd();
2754
- switch (action) {
2755
- case "install":
2756
- await installHooksAction(cwd);
2757
- break;
2758
- case "uninstall":
2759
- await uninstallHooksAction(cwd);
2760
- break;
2761
- case "status":
2762
- await statusHooksAction(cwd);
2763
- break;
2764
- default:
2765
- console.error(chalk9.red(`Unknown action: ${action}`));
2766
- console.log(chalk9.dim("Available actions: install, uninstall, status"));
2767
- process.exit(1);
2768
- }
2769
- }
2770
- async function installHooksAction(cwd) {
2771
- console.log(chalk9.bold.cyan("\n\u{1F517} Installing Workflow Agent Git Hooks\n"));
2772
- if (!hasGitRepo(cwd)) {
2773
- console.error(chalk9.red("\u2717 No git repository found"));
2774
- console.log(chalk9.yellow(" Run: git init"));
2775
- process.exit(1);
2776
- }
2777
- const config = await loadConfig();
2778
- const hooksConfig = config?.hooks;
2779
- const results = await installHooks(hooksConfig, cwd);
2780
- let hasErrors = false;
2781
- for (const result of results) {
2782
- if (result.success) {
2783
- if (result.wrappedExisting) {
2784
- console.log(
2785
- chalk9.green(
2786
- `\u2713 Installed ${result.hookType} hook (wrapped existing hook)`
2787
- )
2788
- );
2789
- } else {
2790
- console.log(chalk9.green(`\u2713 Installed ${result.hookType} hook`));
2791
- }
2792
- } else {
2793
- console.error(
2794
- chalk9.red(`\u2717 Failed to install ${result.hookType}: ${result.error}`)
2795
- );
2796
- hasErrors = true;
2797
- }
2798
- }
2799
- if (!hasErrors) {
2800
- console.log(chalk9.green("\n\u2713 Git hooks installed successfully"));
2801
- console.log(chalk9.dim("\nHooks will run automatically on commit."));
2802
- console.log(chalk9.dim("They will be skipped in CI environments."));
2803
- } else {
2804
- process.exit(1);
2805
- }
2806
- }
2807
- async function uninstallHooksAction(cwd) {
2808
- console.log(chalk9.bold.cyan("\n\u{1F513} Uninstalling Workflow Agent Git Hooks\n"));
2809
- if (!hasGitRepo(cwd)) {
2810
- console.error(chalk9.red("\u2717 No git repository found"));
2811
- process.exit(1);
2812
- }
2813
- const results = await uninstallHooks(cwd);
2814
- let hasErrors = false;
2815
- for (const result of results) {
2816
- if (result.success) {
2817
- if (result.wrappedExisting) {
2818
- console.log(
2819
- chalk9.green(`\u2713 Removed ${result.hookType} hook (restored original)`)
2820
- );
2821
- } else {
2822
- console.log(chalk9.green(`\u2713 Removed ${result.hookType} hook`));
2823
- }
2824
- } else if (result.error) {
2825
- console.error(
2826
- chalk9.red(`\u2717 Failed to remove ${result.hookType}: ${result.error}`)
2827
- );
2828
- hasErrors = true;
2829
- }
2830
- }
2831
- if (!hasErrors) {
2832
- console.log(chalk9.green("\n\u2713 Git hooks uninstalled successfully"));
2833
- } else {
2834
- process.exit(1);
2835
- }
2836
- }
2837
- async function statusHooksAction(cwd) {
2838
- console.log(chalk9.bold.cyan("\n\u{1F4CA} Workflow Agent Git Hooks Status\n"));
2839
- if (!hasGitRepo(cwd)) {
2840
- console.error(chalk9.red("\u2717 No git repository found"));
2841
- process.exit(1);
2842
- }
2843
- const statuses = await getAllHooksStatus(cwd);
2844
- for (const status of statuses) {
2845
- const icon = status.installed ? "\u2713" : "\u2717";
2846
- const color = status.installed ? chalk9.green : chalk9.yellow;
2847
- let message = `${icon} ${status.hookType}`;
2848
- if (status.installed) {
2849
- message += " - installed";
2850
- if (status.wrappedOriginal) {
2851
- message += " (wrapping original hook)";
2852
- }
2853
- } else if (status.hasExistingHook) {
2854
- message += " - existing hook (not managed by Workflow Agent)";
2855
- } else {
2856
- message += " - not installed";
2857
- }
2858
- console.log(color(message));
2859
- }
2860
- const allInstalled = statuses.every((s) => s.installed);
2861
- if (!allInstalled) {
2862
- console.log(chalk9.dim("\nTo install hooks: workflow hooks install"));
2863
- }
2864
- }
2865
-
2866
- // src/cli/commands/github-actions.ts
2867
- import chalk10 from "chalk";
2868
- import * as p6 from "@clack/prompts";
2869
- async function githubCommand(action) {
1318
+ import { execa as execa2 } from "execa";
1319
+ async function verifyCommand(options) {
2870
1320
  const cwd = process.cwd();
2871
- switch (action) {
2872
- case "setup":
2873
- await setupAction(cwd);
2874
- break;
2875
- case "check":
2876
- await checkAction(cwd);
2877
- break;
2878
- default:
2879
- console.error(chalk10.red(`Unknown action: ${action}`));
2880
- console.log(chalk10.dim("Available actions: setup, check"));
2881
- process.exit(1);
2882
- }
2883
- }
2884
- async function setupAction(cwd) {
2885
- console.log(chalk10.bold.cyan("\n\u{1F527} Setting Up GitHub Actions CI\n"));
2886
- const repoInfo = await getRepoInfo(cwd);
2887
- if (!repoInfo.isGitRepo) {
2888
- console.error(chalk10.red("\u2717 No git repository found"));
2889
- console.log(chalk10.yellow(" Run: git init"));
2890
- process.exit(1);
2891
- }
2892
- if (!repoInfo.isGitHub) {
2893
- console.log(chalk10.yellow("\u26A0\uFE0F No GitHub remote detected"));
2894
- const shouldContinue = await p6.confirm({
2895
- message: "Create GitHub Actions workflow anyway?",
2896
- initialValue: true
2897
- });
2898
- if (p6.isCancel(shouldContinue) || !shouldContinue) {
2899
- p6.cancel("Setup cancelled");
2900
- process.exit(0);
2901
- }
2902
- } else {
2903
- console.log(
2904
- chalk10.dim(
2905
- `Repository: ${repoInfo.github?.owner}/${repoInfo.github?.repo}`
2906
- )
2907
- );
2908
- }
2909
- if (hasCIWorkflow(cwd)) {
2910
- const shouldOverwrite = await p6.confirm({
2911
- message: "CI workflow already exists. Overwrite?",
2912
- initialValue: false
2913
- });
2914
- if (p6.isCancel(shouldOverwrite) || !shouldOverwrite) {
2915
- p6.cancel("Setup cancelled");
2916
- process.exit(0);
2917
- }
2918
- }
2919
- const projectInfo = await getProjectInfo(cwd);
2920
- console.log(chalk10.dim(`Package manager: ${projectInfo.packageManager}`));
2921
- console.log(chalk10.dim(`Monorepo: ${projectInfo.isMonorepo ? "yes" : "no"}`));
2922
- const config = await loadConfig();
2923
- const ciConfig = config?.ci;
2924
- const spinner5 = p6.spinner();
2925
- spinner5.start("Creating CI workflow...");
2926
- const result = await createCIWorkflow({
2927
- projectPath: cwd,
2928
- packageManager: projectInfo.packageManager,
2929
- isMonorepo: projectInfo.isMonorepo,
2930
- ciConfig,
2931
- defaultBranch: repoInfo.defaultBranch || "main"
1321
+ const maxRetries = options.maxRetries ? parseInt(options.maxRetries, 10) : 10;
1322
+ const autoFix = options.fix ?? false;
1323
+ const shouldCommit = options.commit ?? false;
1324
+ const dryRun = options.dryRun ?? false;
1325
+ console.log(chalk9.bold.cyan("\n\u{1F50D} Workflow Agent Quality Verification\n"));
1326
+ if (dryRun) {
1327
+ console.log(chalk9.yellow("\u{1F4CB} DRY-RUN MODE: No changes will be applied\n"));
1328
+ }
1329
+ console.log(chalk9.dim(` Auto-fix: ${autoFix ? "enabled" : "disabled"}`));
1330
+ console.log(chalk9.dim(` Max retries: ${maxRetries}`));
1331
+ console.log(chalk9.dim(` Commit on success: ${shouldCommit ? "yes" : "no"}`));
1332
+ console.log(chalk9.dim(` Dry-run: ${dryRun ? "yes" : "no"}`));
1333
+ const startTime = Date.now();
1334
+ const result = await runAllChecks(cwd, {
1335
+ maxRetries,
1336
+ autoFix,
1337
+ dryRun
2932
1338
  });
1339
+ const totalTime = ((Date.now() - startTime) / 1e3).toFixed(2);
1340
+ console.log(`
1341
+ ${"\u2501".repeat(50)}`);
2933
1342
  if (result.success) {
2934
- spinner5.stop(chalk10.green("\u2713 Created CI workflow"));
2935
- console.log(chalk10.dim(` File: ${result.filePath}`));
2936
- console.log(chalk10.green("\n\u2713 GitHub Actions CI setup complete"));
2937
- console.log(chalk10.dim("\nThe workflow will run on:"));
2938
- console.log(chalk10.dim(" \u2022 Push to main/develop branches"));
2939
- console.log(chalk10.dim(" \u2022 Pull requests to main/develop branches"));
2940
- console.log(chalk10.dim("\nChecks included:"));
2941
- const checks = ciConfig?.checks || [
2942
- "lint",
2943
- "typecheck",
2944
- "format",
2945
- "build",
2946
- "test"
2947
- ];
2948
- for (const check of checks) {
2949
- const hasScript = check === "lint" ? projectInfo.hasLintScript : check === "typecheck" ? projectInfo.hasTypecheckScript : check === "format" ? projectInfo.hasFormatScript : check === "test" ? projectInfo.hasTestScript : check === "build" ? projectInfo.hasBuildScript : false;
2950
- const status = hasScript ? chalk10.green("\u2713") : chalk10.yellow("\u26A0");
2951
- const note = hasScript ? "" : " (add script to package.json)";
2952
- console.log(chalk10.dim(` ${status} ${check}${note}`));
1343
+ console.log(chalk9.bold.green("\n\u2705 ALL QUALITY CHECKS PASSED!\n"));
1344
+ console.log(chalk9.dim(` Total time: ${totalTime}s`));
1345
+ console.log(chalk9.dim(` Validation cycles: ${result.totalAttempts}`));
1346
+ console.log(chalk9.dim(` Fixes applied: ${result.fixesApplied}`));
1347
+ if (shouldCommit) {
1348
+ const hasChanges = await hasUncommittedChanges(cwd);
1349
+ if (hasChanges) {
1350
+ console.log(chalk9.cyan("\n\u{1F4E6} Staging and committing changes...\n"));
1351
+ const staged = await stageAllChanges(cwd);
1352
+ if (!staged) {
1353
+ console.log(chalk9.red("\u274C Failed to stage changes"));
1354
+ process.exit(1);
1355
+ }
1356
+ try {
1357
+ await execa2(
1358
+ "git",
1359
+ ["commit", "-m", "chore: auto-fix quality issues"],
1360
+ { cwd }
1361
+ );
1362
+ console.log(chalk9.green("\u2705 Changes committed successfully"));
1363
+ } catch (error) {
1364
+ console.log(chalk9.red("\u274C Failed to commit changes"));
1365
+ console.log(chalk9.dim(error.message));
1366
+ process.exit(1);
1367
+ }
1368
+ } else {
1369
+ console.log(chalk9.dim("\n No changes to commit."));
1370
+ }
2953
1371
  }
2954
- console.log(chalk10.cyan("\n\u{1F4A1} Recommended: Enable branch protection"));
2955
- console.log(chalk10.dim(" Go to GitHub \u2192 Settings \u2192 Branches \u2192 Add rule"));
1372
+ console.log(chalk9.cyan("\n\u{1F4A1} Next steps:\n"));
1373
+ console.log(chalk9.dim(" 1. git add ."));
2956
1374
  console.log(
2957
- chalk10.dim(' Enable "Require status checks to pass before merging"')
1375
+ chalk9.dim(' 2. git commit -m "<type>(<scope>): <description>"')
2958
1376
  );
2959
- console.log(chalk10.dim(' Select the "ci" status check'));
2960
- } else {
2961
- spinner5.stop(chalk10.red("\u2717 Failed to create CI workflow"));
2962
- console.error(chalk10.red(` Error: ${result.error}`));
2963
- process.exit(1);
2964
- }
2965
- }
2966
- async function checkAction(cwd) {
2967
- console.log(chalk10.bold.cyan("\n\u{1F50D} Checking GitHub Actions CI Setup\n"));
2968
- const result = await validateGitHubActionsSetup(cwd);
2969
- if (result.hasWorkflowFile) {
2970
- console.log(chalk10.green("\u2713 CI workflow file found"));
2971
- } else {
2972
- console.log(chalk10.red("\u2717 No CI workflow file found"));
2973
- console.log(chalk10.yellow(" Run: workflow github:setup"));
2974
- process.exit(1);
2975
- }
2976
- const checks = [
2977
- { name: "Lint", present: result.hasLintCheck },
2978
- { name: "Type check", present: result.hasTypecheckCheck },
2979
- { name: "Format", present: result.hasFormatCheck },
2980
- { name: "Build", present: result.hasBuildCheck },
2981
- { name: "Test", present: result.hasTestCheck }
2982
- ];
2983
- console.log(chalk10.dim("\nCI checks:"));
2984
- for (const check of checks) {
2985
- const icon = check.present ? chalk10.green("\u2713") : chalk10.yellow("\u26A0");
2986
- console.log(` ${icon} ${check.name}`);
2987
- }
2988
- if (result.errors.length > 0) {
2989
- console.log(chalk10.red("\nErrors:"));
2990
- for (const error of result.errors) {
2991
- console.log(chalk10.red(` \u2022 ${error}`));
2992
- }
2993
- }
2994
- if (result.warnings.length > 0) {
2995
- console.log(chalk10.yellow("\nWarnings:"));
2996
- for (const warning of result.warnings) {
2997
- console.log(chalk10.yellow(` \u2022 ${warning}`));
2998
- }
2999
- }
3000
- if (result.valid) {
3001
- console.log(chalk10.green("\n\u2713 CI setup is valid"));
1377
+ console.log(chalk9.dim(" 3. git push origin <branch-name>"));
1378
+ console.log("");
1379
+ process.exit(0);
3002
1380
  } else {
3003
- console.log(chalk10.red("\n\u2717 CI setup has issues"));
3004
- console.log(chalk10.yellow(" Run: workflow github:setup"));
3005
- process.exit(1);
3006
- }
3007
- }
3008
-
3009
- // src/cli/commands/fix.ts
3010
- import { existsSync as existsSync10, readFileSync as readFileSync2, writeFileSync as writeFileSync3, mkdirSync } from "fs";
3011
- import { dirname as dirname2, join as join10, relative } from "path";
3012
- import { execa as execa3 } from "execa";
3013
- import * as p7 from "@clack/prompts";
3014
- import pc from "picocolors";
3015
- function extractFilePaths(errorMessage, cwd) {
3016
- const paths = /* @__PURE__ */ new Set();
3017
- const patterns = [
3018
- /(?:at\s+)?([\/\w\.\-]+\.(?:ts|js|tsx|jsx|mjs|cjs|vue|svelte))(?::\d+(?::\d+)?)?/g,
3019
- /(?:Error in |from )\.?([\/\w\.\-]+\.(?:ts|js|tsx|jsx|mjs|cjs|vue|svelte))/g,
3020
- /['"]([\/\w\.\-]+\.(?:ts|js|tsx|jsx|mjs|cjs|vue|svelte))['"]/g
3021
- ];
3022
- for (const pattern of patterns) {
3023
- let match;
3024
- while ((match = pattern.exec(errorMessage)) !== null) {
3025
- let filePath = match[1];
3026
- if (!filePath.startsWith("/")) {
3027
- filePath = join10(cwd, filePath);
1381
+ console.log(chalk9.bold.red("\n\u274C QUALITY CHECKS FAILED\n"));
1382
+ console.log(chalk9.dim(` Total time: ${totalTime}s`));
1383
+ console.log(chalk9.dim(` Validation cycles: ${result.totalAttempts}`));
1384
+ console.log(chalk9.dim(` Fixes applied: ${result.fixesApplied}`));
1385
+ if (result.pendingFixes && result.pendingFixes.length > 0) {
1386
+ console.log(chalk9.yellow("\n\u{1F4CB} Pending fixes (dry-run):"));
1387
+ for (const fix of result.pendingFixes) {
1388
+ console.log(chalk9.dim(` \u2022 ${fix.check.displayName}: ${fix.command}`));
3028
1389
  }
3029
- if (existsSync10(filePath)) {
3030
- paths.add(filePath);
3031
- }
3032
- }
3033
- }
3034
- return Array.from(paths);
3035
- }
3036
- function readFileContents(paths) {
3037
- const contents = {};
3038
- for (const filePath of paths) {
3039
- try {
3040
- if (existsSync10(filePath)) {
3041
- contents[filePath] = readFileSync2(filePath, "utf-8");
3042
- }
3043
- } catch {
3044
- }
3045
- }
3046
- return contents;
3047
- }
3048
- async function generateFixWithLLM(errorMessage, _fileContents, _context) {
3049
- console.log(pc.dim(" Analyzing error with LLM..."));
3050
- return {
3051
- analysis: `Error analysis for: ${errorMessage.slice(0, 100)}...`,
3052
- rootCause: "Unable to determine root cause without LLM API access",
3053
- suggestedFix: {
3054
- description: "Manual intervention required - LLM API not configured",
3055
- files: []
3056
- },
3057
- confidence: 0
3058
- };
3059
- }
3060
- async function applyChanges(changes, dryRun) {
3061
- for (const change of changes) {
3062
- const actionColor = change.action === "create" ? pc.green : change.action === "delete" ? pc.red : pc.yellow;
3063
- console.log(` ${actionColor(change.action.toUpperCase())} ${change.path}`);
3064
- if (dryRun) {
3065
- console.log(pc.dim(" (dry run - no changes made)"));
3066
- continue;
3067
- }
3068
- switch (change.action) {
3069
- case "create":
3070
- case "modify":
3071
- if (change.content) {
3072
- const dir = dirname2(change.path);
3073
- if (!existsSync10(dir)) {
3074
- mkdirSync(dir, { recursive: true });
3075
- }
3076
- writeFileSync3(change.path, change.content);
3077
- }
3078
- break;
3079
- case "delete":
3080
- console.log(pc.dim(" (delete skipped for safety)"));
3081
- break;
3082
- }
3083
- }
3084
- }
3085
- async function commitAndPush(message, cwd) {
3086
- try {
3087
- await execa3("git", ["add", "-A"], { cwd });
3088
- const { stdout: status } = await execa3(
3089
- "git",
3090
- ["status", "--porcelain"],
3091
- { cwd }
3092
- );
3093
- if (!status.trim()) {
3094
- return { success: true };
3095
- }
3096
- await execa3("git", ["commit", "-m", message], { cwd });
3097
- await execa3("git", ["push"], { cwd });
3098
- return { success: true };
3099
- } catch (error) {
3100
- return {
3101
- success: false,
3102
- error: error instanceof Error ? error.message : String(error)
3103
- };
3104
- }
3105
- }
3106
- async function fixCommand(options) {
3107
- const cwd = process.cwd();
3108
- console.log(pc.cyan("\n\u{1F527} Auto-Heal: Fixing Pipeline Error\n"));
3109
- if (!options.error) {
3110
- console.error(pc.red("Error: --error flag is required"));
3111
- process.exit(1);
3112
- }
3113
- console.log(pc.bold("Error Message:"));
3114
- console.log(pc.dim(options.error.slice(0, 500)));
3115
- if (options.error.length > 500) {
3116
- console.log(pc.dim(`... (${options.error.length - 500} more characters)`));
3117
- }
3118
- console.log("");
3119
- let filePaths = options.files || [];
3120
- if (filePaths.length === 0) {
3121
- console.log(pc.dim("Extracting file paths from error..."));
3122
- filePaths = extractFilePaths(options.error, cwd);
3123
- }
3124
- if (filePaths.length > 0) {
3125
- console.log(pc.bold("\nRelevant Files:"));
3126
- for (const path2 of filePaths) {
3127
- console.log(` \u{1F4C4} ${relative(cwd, path2)}`);
3128
- }
3129
- console.log("");
3130
- }
3131
- const fileContents = readFileContents(filePaths);
3132
- let context;
3133
- if (options.context) {
3134
- try {
3135
- context = JSON.parse(options.context);
3136
- } catch {
3137
- context = options.context;
3138
1390
  }
3139
- }
3140
- console.log(pc.bold("Generating Fix...\n"));
3141
- const fix = await generateFixWithLLM(options.error, fileContents, context);
3142
- console.log(pc.bold("Analysis:"));
3143
- console.log(` ${fix.analysis}`);
3144
- console.log("");
3145
- console.log(pc.bold("Root Cause:"));
3146
- console.log(` ${fix.rootCause}`);
3147
- console.log("");
3148
- console.log(pc.bold("Suggested Fix:"));
3149
- console.log(` ${fix.suggestedFix.description}`);
3150
- console.log(` Confidence: ${Math.round(fix.confidence * 100)}%`);
3151
- console.log("");
3152
- if (fix.suggestedFix.files.length === 0) {
3153
- console.log(
3154
- pc.yellow("\u26A0\uFE0F No automatic fix available. Manual intervention required.")
3155
- );
3156
- process.exit(1);
3157
- }
3158
- if (fix.confidence < 0.5) {
3159
1391
  console.log(
3160
- pc.yellow(
3161
- `\u26A0\uFE0F Low confidence (${Math.round(fix.confidence * 100)}%). Skipping automatic fix.`
3162
- )
1392
+ chalk9.yellow("\n\u26A0\uFE0F Please fix the errors above and run again.")
3163
1393
  );
3164
- process.exit(1);
3165
- }
3166
- if (!options.auto) {
3167
- const shouldApply = await p7.confirm({
3168
- message: "Apply suggested changes?",
3169
- initialValue: false
3170
- });
3171
- if (p7.isCancel(shouldApply) || !shouldApply) {
3172
- console.log(pc.dim("Fix cancelled."));
3173
- process.exit(0);
3174
- }
3175
- }
3176
- console.log(pc.bold("\nApplying Changes...\n"));
3177
- await applyChanges(fix.suggestedFix.files, options.dryRun || false);
3178
- if (options.dryRun) {
3179
- console.log(pc.yellow("\n\u26A0\uFE0F Dry run complete. No changes were made."));
3180
- process.exit(0);
3181
- }
3182
- if (options.auto) {
3183
- console.log(pc.bold("\nCommitting and Pushing...\n"));
3184
- const scope = filePaths.length > 0 ? dirname2(relative(cwd, filePaths[0])).split("/")[0] || "core" : "core";
3185
- const commitMessage = `fix(${scope}): auto-heal pipeline failure
3186
-
3187
- ${fix.rootCause}
3188
-
3189
- Auto-generated fix with ${Math.round(fix.confidence * 100)}% confidence.
3190
- `;
3191
- const result = await commitAndPush(commitMessage, cwd);
3192
- if (result.success) {
3193
- console.log(pc.green("\u2705 Fix applied, committed, and pushed!"));
3194
- } else {
3195
- console.log(pc.red(`\u274C Failed to commit/push: ${result.error}`));
3196
- process.exit(1);
3197
- }
3198
- } else {
3199
- console.log(pc.green("\n\u2705 Fix applied! Don't forget to commit and push."));
3200
- }
3201
- }
3202
-
3203
- // src/cli/commands/visual.ts
3204
- import { existsSync as existsSync11, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
3205
- import { dirname as dirname3, join as join11 } from "path";
3206
- import * as p8 from "@clack/prompts";
3207
- import pc2 from "picocolors";
3208
- function getBaselinesDir() {
3209
- const cwd = process.cwd();
3210
- return join11(cwd, ".visual-baselines");
3211
- }
3212
- function getMetadataPath() {
3213
- return join11(getBaselinesDir(), "baselines.json");
3214
- }
3215
- function loadMetadata() {
3216
- const metadataPath = getMetadataPath();
3217
- if (!existsSync11(metadataPath)) {
3218
- return {};
3219
- }
3220
- try {
3221
- return JSON.parse(readFileSync3(metadataPath, "utf-8"));
3222
- } catch {
3223
- return {};
3224
- }
3225
- }
3226
- function saveMetadata(metadata) {
3227
- const dir = getBaselinesDir();
3228
- if (!existsSync11(dir)) {
3229
- mkdirSync2(dir, { recursive: true });
3230
- }
3231
- writeFileSync4(getMetadataPath(), JSON.stringify(metadata, null, 2));
3232
- }
3233
- async function checkPlaywright() {
3234
- try {
3235
- await import("playwright");
3236
- return true;
3237
- } catch {
3238
- return false;
3239
- }
3240
- }
3241
- async function visualCaptureCommand(name, url, options) {
3242
- console.log(pc2.cyan("\n\u{1F4F8} Capturing Visual Baseline\n"));
3243
- const hasPlaywright = await checkPlaywright();
3244
- if (!hasPlaywright) {
3245
- console.log(pc2.yellow("\u26A0\uFE0F Playwright is not installed."));
3246
- console.log(pc2.dim("Install it with: npm install playwright"));
3247
1394
  console.log(
3248
- pc2.dim("Then install browsers: npx playwright install chromium")
3249
- );
3250
- process.exit(1);
3251
- }
3252
- const width = parseInt(options.width || "1280");
3253
- const height = parseInt(options.height || "720");
3254
- const delay = parseInt(options.delay || "0");
3255
- console.log(` Name: ${pc2.bold(name)}`);
3256
- console.log(` URL: ${url}`);
3257
- console.log(` Viewport: ${width}x${height}`);
3258
- console.log("");
3259
- try {
3260
- const { chromium } = await import("playwright");
3261
- const browser = await chromium.launch({ headless: true });
3262
- const context = await browser.newContext({
3263
- viewport: { width, height }
3264
- });
3265
- const page = await context.newPage();
3266
- console.log(pc2.dim(" Loading page..."));
3267
- await page.goto(url, { waitUntil: "networkidle" });
3268
- if (options.waitFor) {
3269
- console.log(pc2.dim(` Waiting for selector: ${options.waitFor}`));
3270
- await page.waitForSelector(options.waitFor, { timeout: 1e4 });
3271
- }
3272
- if (delay > 0) {
3273
- console.log(pc2.dim(` Waiting ${delay}ms...`));
3274
- await page.waitForTimeout(delay);
3275
- }
3276
- const baselinesDir = getBaselinesDir();
3277
- const outputPath = options.output || join11(baselinesDir, "screenshots", `${name}.png`);
3278
- const outputDir = dirname3(outputPath);
3279
- if (!existsSync11(outputDir)) {
3280
- mkdirSync2(outputDir, { recursive: true });
3281
- }
3282
- console.log(pc2.dim(" Capturing screenshot..."));
3283
- await page.screenshot({
3284
- path: outputPath,
3285
- fullPage: options.fullPage
3286
- });
3287
- await browser.close();
3288
- const metadata = loadMetadata();
3289
- const now = (/* @__PURE__ */ new Date()).toISOString();
3290
- metadata[name] = {
3291
- name,
3292
- url,
3293
- path: outputPath,
3294
- width,
3295
- height,
3296
- createdAt: metadata[name]?.createdAt || now,
3297
- updatedAt: now
3298
- };
3299
- saveMetadata(metadata);
3300
- console.log(pc2.green(`
3301
- \u2705 Baseline "${name}" saved to ${outputPath}`));
3302
- } catch (error) {
3303
- console.error(pc2.red("\n\u274C Failed to capture screenshot:"), error);
3304
- process.exit(1);
3305
- }
3306
- }
3307
- async function visualCompareCommand(url, options) {
3308
- console.log(pc2.cyan("\n\u{1F50D} Comparing Against Baseline\n"));
3309
- const metadata = loadMetadata();
3310
- const baseline = metadata[options.baseline];
3311
- if (!baseline) {
3312
- console.log(pc2.red(`\u274C Baseline "${options.baseline}" not found`));
3313
- console.log(pc2.dim("Available baselines:"));
3314
- Object.keys(metadata).forEach((name) => {
3315
- console.log(pc2.dim(` - ${name}`));
3316
- });
3317
- process.exit(1);
3318
- }
3319
- if (!existsSync11(baseline.path)) {
3320
- console.log(pc2.red(`\u274C Baseline screenshot not found: ${baseline.path}`));
3321
- process.exit(1);
3322
- }
3323
- console.log(` Baseline: ${pc2.bold(options.baseline)}`);
3324
- console.log(` URL: ${url}`);
3325
- console.log(` Viewport: ${baseline.width}x${baseline.height}`);
3326
- console.log("");
3327
- const hasPlaywright = await checkPlaywright();
3328
- if (!hasPlaywright) {
3329
- console.log(pc2.yellow("\u26A0\uFE0F Playwright is not installed."));
3330
- process.exit(1);
3331
- }
3332
- try {
3333
- const { chromium } = await import("playwright");
3334
- const browser = await chromium.launch({ headless: true });
3335
- const context = await browser.newContext({
3336
- viewport: { width: baseline.width, height: baseline.height }
3337
- });
3338
- const page = await context.newPage();
3339
- console.log(pc2.dim(" Loading page..."));
3340
- await page.goto(url, { waitUntil: "networkidle" });
3341
- const baselinesDir = getBaselinesDir();
3342
- const comparePath = options.output || join11(
3343
- baselinesDir,
3344
- "comparisons",
3345
- `${options.baseline}-${Date.now()}.png`
1395
+ chalk9.dim(" Run with --fix to auto-fix lint and format issues.")
3346
1396
  );
3347
- const compareDir = dirname3(comparePath);
3348
- if (!existsSync11(compareDir)) {
3349
- mkdirSync2(compareDir, { recursive: true });
3350
- }
3351
- console.log(pc2.dim(" Capturing screenshot..."));
3352
- await page.screenshot({ path: comparePath });
3353
- await browser.close();
3354
- const baselineBuffer = readFileSync3(baseline.path);
3355
- const compareBuffer = readFileSync3(comparePath);
3356
- const areSameSize = baselineBuffer.length === compareBuffer.length;
3357
- const areIdentical = areSameSize && baselineBuffer.equals(compareBuffer);
3358
- console.log("");
3359
- if (areIdentical) {
3360
- console.log(pc2.green("\u2705 Screenshots are identical"));
3361
- } else {
3362
- console.log(pc2.yellow("\u26A0\uFE0F Screenshots differ"));
3363
- console.log(pc2.dim(` Baseline size: ${baselineBuffer.length} bytes`));
3364
- console.log(pc2.dim(` Current size: ${compareBuffer.length} bytes`));
3365
- console.log("");
3366
- console.log(
3367
- pc2.dim(
3368
- "For detailed LLM-based comparison, use the GitHub App's visual testing."
3369
- )
3370
- );
3371
- console.log(pc2.dim(`Comparison saved to: ${comparePath}`));
3372
- process.exit(1);
3373
- }
3374
- } catch (error) {
3375
- console.error(pc2.red("\n\u274C Failed to compare:"), error);
3376
- process.exit(1);
3377
- }
3378
- }
3379
- async function visualListCommand(options) {
3380
- const metadata = loadMetadata();
3381
- const baselines = Object.values(metadata);
3382
- if (options.json) {
3383
- console.log(JSON.stringify(baselines, null, 2));
3384
- return;
3385
- }
3386
- if (baselines.length === 0) {
3387
- console.log(pc2.dim("No baselines found."));
3388
- console.log(pc2.dim("Create one with: workflow visual capture <name> <url>"));
3389
- return;
3390
- }
3391
- console.log(pc2.cyan("\n\u{1F4F8} Visual Baselines\n"));
3392
- for (const baseline of baselines) {
3393
- console.log(` ${pc2.bold(baseline.name)}`);
3394
- console.log(` URL: ${baseline.url}`);
3395
- console.log(` Viewport: ${baseline.width}x${baseline.height}`);
3396
- console.log(` Path: ${baseline.path}`);
3397
- console.log(` Updated: ${baseline.updatedAt}`);
3398
1397
  console.log("");
3399
- }
3400
- }
3401
- async function visualUpdateCommand(name, options) {
3402
- const metadata = loadMetadata();
3403
- const baseline = metadata[name];
3404
- if (!baseline) {
3405
- console.log(pc2.red(`\u274C Baseline "${name}" not found`));
3406
- process.exit(1);
3407
- }
3408
- console.log(pc2.cyan(`
3409
- \u{1F504} Updating baseline "${name}"...
3410
- `));
3411
- await visualCaptureCommand(name, baseline.url, {
3412
- width: options.width || String(baseline.width),
3413
- height: options.height || String(baseline.height),
3414
- fullPage: options.fullPage,
3415
- output: baseline.path
3416
- });
3417
- }
3418
- async function visualApproveCommand(name) {
3419
- const metadata = loadMetadata();
3420
- const baseline = metadata[name];
3421
- if (!baseline) {
3422
- console.log(pc2.red(`\u274C Baseline "${name}" not found`));
3423
1398
  process.exit(1);
3424
1399
  }
3425
- const confirm8 = await p8.confirm({
3426
- message: `Update baseline "${name}" with the latest comparison?`,
3427
- initialValue: false
3428
- });
3429
- if (p8.isCancel(confirm8) || !confirm8) {
3430
- console.log(pc2.dim("Cancelled."));
3431
- return;
3432
- }
3433
- const baselinesDir = getBaselinesDir();
3434
- const comparisonsDir = join11(baselinesDir, "comparisons");
3435
- if (!existsSync11(comparisonsDir)) {
3436
- console.log(pc2.yellow("No comparisons found to approve."));
3437
- return;
3438
- }
3439
- console.log(
3440
- pc2.yellow(
3441
- "To approve, re-capture the baseline with: workflow visual update " + name
3442
- )
3443
- );
3444
1400
  }
3445
1401
 
3446
1402
  // src/cli/index.ts
3447
- var program = new Command2();
1403
+ var program = new Command();
3448
1404
  program.name("workflow").description(
3449
1405
  "A self-evolving workflow management system for AI agent development"
3450
1406
  ).version("1.0.0");
@@ -3459,43 +1415,18 @@ program.command("validate <type>").description("Validate branch name, commit mes
3459
1415
  "--suggest-on-error",
3460
1416
  "Offer improvement suggestions on validation errors"
3461
1417
  ).action(validateCommand);
3462
- program.addCommand(createConfigCommand());
1418
+ program.command("config <action>").description("Manage workflow configuration").argument("<action>", "Action: get, set, add, remove").argument("[key]", "Config key").argument("[value]", "Config value").action(configCommand);
3463
1419
  program.command("suggest").description("Submit an improvement suggestion").argument("<feedback>", "Your improvement suggestion").option("--author <author>", "Your name or username").option(
3464
1420
  "--category <category>",
3465
1421
  "Category: feature, bug, documentation, performance, other"
3466
1422
  ).action(suggestCommand);
3467
1423
  program.command("setup").description("Add workflow scripts to package.json").action(setupCommand);
3468
- program.command("doctor").description("Run health check and get optimization suggestions").option(
3469
- "--check-guidelines-only",
3470
- "Only check mandatory guidelines exist (exits 0 or 1)"
3471
- ).action(doctorCommand);
3472
- program.command("hooks").description("Manage git hooks (install, uninstall, status)").argument("<action>", "Action: install, uninstall, status").action(hooksCommand);
3473
- program.command("github").description("Manage GitHub Actions CI (setup, check)").argument("<action>", "Action: setup, check").action(githubCommand);
1424
+ program.command("doctor").description("Run health check and get optimization suggestions").action(doctorCommand);
3474
1425
  program.command("scope:create").description("Create a custom scope package").option("--name <name>", 'Package name (e.g., "fintech", "gaming")').option(
3475
1426
  "--scopes <scopes>",
3476
1427
  "Comma-separated scopes (format: name:description:emoji:category)"
3477
1428
  ).option("--preset-name <preset>", "Preset display name").option("--output-dir <dir>", "Output directory").option("--no-test", "Skip test file generation").action(scopeCreateCommand);
3478
1429
  program.command("scope:migrate").description("Migrate inline scopes to a custom package").option("--name <name>", "Package name for the preset").option("--output-dir <dir>", "Output directory").option("--keep-config", "Keep inline scopes in config after migration").action(scopeMigrateCommand);
3479
- program.command("fix").description("Auto-heal pipeline failures using LLM").option("--error <error>", "Error message to fix (required)").option("--context <context>", "Additional context JSON").option("--files <files>", "Comma-separated file paths to analyze").option("--auto", "Apply fix automatically without confirmation").option("--dry-run", "Show what would be changed without applying").action((options) => {
3480
- fixCommand({
3481
- error: options.error,
3482
- context: options.context,
3483
- files: options.files?.split(","),
3484
- auto: options.auto,
3485
- dryRun: options.dryRun
3486
- });
3487
- });
3488
- var visual = program.command("visual").description("Visual testing commands");
3489
- visual.command("capture").description("Capture a new baseline screenshot").argument("<name>", "Name for the baseline").argument("<url>", "URL to capture").option("-w, --width <width>", "Viewport width", "1280").option("-h, --height <height>", "Viewport height", "720").option("--full-page", "Capture full page").option("-o, --output <path>", "Output path").option("--wait-for <selector>", "Wait for selector before capture").option("--delay <ms>", "Additional delay in ms").action(visualCaptureCommand);
3490
- visual.command("compare").description("Compare a URL against a baseline").argument("<url>", "URL to compare").option("-b, --baseline <name>", "Baseline name to compare against (required)").option("-o, --output <path>", "Output path for comparison screenshot").option("-t, --threshold <percent>", "Difference threshold percentage").action((url, options) => {
3491
- if (!options.baseline) {
3492
- console.error("Error: --baseline is required");
3493
- process.exit(1);
3494
- }
3495
- visualCompareCommand(url, options);
3496
- });
3497
- visual.command("list").description("List all baselines").option("--json", "Output as JSON").action(visualListCommand);
3498
- visual.command("update").description("Update an existing baseline").argument("<name>", "Baseline name to update").option("-w, --width <width>", "Override viewport width").option("-h, --height <height>", "Override viewport height").option("--full-page", "Capture full page").action(visualUpdateCommand);
3499
- visual.command("approve").description("Approve a comparison as the new baseline").argument("<name>", "Baseline name to approve").action(visualApproveCommand);
1430
+ program.command("verify").description("Run all quality checks with fix-and-revalidate pattern").option("--fix", "Enable auto-fix for lint and format issues").option("--max-retries <n>", "Maximum retry cycles (default: 10)", "10").option("--commit", "Commit changes if all checks pass").option("--dry-run", "Preview fixes without applying them").action(verifyCommand);
3500
1431
  program.parse();
3501
1432
  //# sourceMappingURL=index.js.map