agent-workflow-kit-cli 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,6 +8,7 @@ import path from "path";
8
8
  import { renderTemplate, readStaticTemplateFile, getStackRules, getStackSkills, } from "../../core/renderer.js";
9
9
  import { updateFileWithBlock, writeRuleWithChunking, } from "../../core/emitter.js";
10
10
  import { analyzeModule } from "../../core/analyzer.js";
11
+ import { updateGitignore } from "./init.js";
11
12
  export async function runAdd(stack, options) {
12
13
  const targetStack = stack.toLowerCase();
13
14
  const validStacks = ["spring-boot", "react-ts", "fastapi", "python-ai"];
@@ -36,26 +37,63 @@ export async function runAdd(stack, options) {
36
37
  console.error(chalk.red(`Error loading template for stack '${targetStack}': ${err instanceof Error ? err.message : String(err)}`));
37
38
  process.exit(1);
38
39
  }
39
- // 3. Write or Update AGENTS.md in the target directory
40
+ // 3. Write or Update AGENTS.md and/or GEMINI.md in the target directory
41
+ const geminiPath = path.join(targetDir, "GEMINI.md");
40
42
  const agentsPath = path.join(targetDir, "AGENTS.md");
41
43
  const moduleAgentsContent = await renderTemplate("common/AGENTS.md.hbs", {
42
44
  stackContent,
43
45
  });
44
- if (options.dryRun) {
45
- console.log(chalk.gray(`[Dry Run] Would write/update AGENTS.md at ${agentsPath}`));
46
+ if (options.agent === "antigravity" || options.agent === "both") {
47
+ if (options.dryRun) {
48
+ console.log(chalk.gray(`[Dry Run] Would write/update GEMINI.md at ${geminiPath}`));
49
+ }
50
+ else {
51
+ try {
52
+ await fs.access(geminiPath);
53
+ await updateFileWithBlock(geminiPath, "STACK_PACK", stackContent);
54
+ console.log(chalk.green(`✔️ Updated STACK_PACK block in ${geminiPath}`));
55
+ }
56
+ catch {
57
+ await fs.mkdir(targetDir, { recursive: true });
58
+ await fs.writeFile(geminiPath, moduleAgentsContent, "utf8");
59
+ console.log(chalk.green(`✔️ Created ${geminiPath}`));
60
+ }
61
+ }
46
62
  }
47
- else {
48
- try {
49
- await fs.access(agentsPath);
50
- await updateFileWithBlock(agentsPath, "STACK_PACK", stackContent);
51
- console.log(chalk.green(`✔️ Updated STACK_PACK block in ${agentsPath}`));
63
+ if (options.agent === "codex" || options.agent === "both") {
64
+ if (options.dryRun) {
65
+ console.log(chalk.gray(`[Dry Run] Would write/update AGENTS.md at ${agentsPath}`));
66
+ }
67
+ else {
68
+ try {
69
+ await fs.access(agentsPath);
70
+ await updateFileWithBlock(agentsPath, "STACK_PACK", stackContent);
71
+ console.log(chalk.green(`✔️ Updated STACK_PACK block in ${agentsPath}`));
72
+ }
73
+ catch {
74
+ await fs.mkdir(targetDir, { recursive: true });
75
+ await fs.writeFile(agentsPath, moduleAgentsContent, "utf8");
76
+ console.log(chalk.green(`✔️ Created ${agentsPath}`));
77
+ }
52
78
  }
53
- catch {
54
- await fs.mkdir(targetDir, { recursive: true });
55
- await fs.writeFile(agentsPath, moduleAgentsContent, "utf8");
56
- console.log(chalk.green(`✔️ Created ${agentsPath}`));
79
+ }
80
+ // Write IDE Rules to target directory
81
+ const ideRulesContent = await renderTemplate("common/ide-rules.hbs", {
82
+ agent: options.agent,
83
+ });
84
+ const ideFiles = [".cursorrules", ".copilot-instructions.md", ".clinerules"];
85
+ for (const file of ideFiles) {
86
+ if (options.dryRun) {
87
+ console.log(chalk.gray(`[Dry Run] Would write IDE rule file to ${path.join(targetDir, file)}`));
88
+ }
89
+ else {
90
+ await fs.writeFile(path.join(targetDir, file), ideRulesContent, "utf8");
57
91
  }
58
92
  }
93
+ if (!options.dryRun) {
94
+ console.log(chalk.green(`✔️ Created IDE prompt anchors at ${targetDir} (.cursorrules, .copilot-instructions.md, .clinerules)`));
95
+ }
96
+ await updateGitignore(targetDir, options.dryRun);
59
97
  // 4. Copy rules and skills for the target stack
60
98
  const stackCtx = analysis[targetStack] || {};
61
99
  // A. Copy Rules
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ import chalk from "chalk";
6
+ import { ADRService } from "../../core/awos/adr.js";
7
+ export async function createAdrCommand(options) {
8
+ const cwd = process.cwd();
9
+ console.log(chalk.bold.cyan("\n📝 Creating New Architectural Decision Record (ADR)"));
10
+ console.log(chalk.dim("------------------------------------------"));
11
+ const title = options.title || "Decision Title";
12
+ const status = options.status || "proposed";
13
+ const context = options.context || "Explain the problem or background context.";
14
+ const decision = options.decision || "Detail what will change and what decision is made.";
15
+ const consequences = options.consequences || "What are the positive or negative consequences of this path?";
16
+ const decisionMaker = options.decisionMaker || "AWOS System";
17
+ const created = await ADRService.create(cwd, {
18
+ title,
19
+ status,
20
+ context,
21
+ decision,
22
+ consequences,
23
+ metadata: {
24
+ decisionMakerRole: decisionMaker,
25
+ },
26
+ });
27
+ console.log(chalk.bold.green(`\n🎉 ADR Created Successfully!`));
28
+ console.log(`${chalk.bold("ID:")} ${created.id}`);
29
+ console.log(`${chalk.bold("File:")} docs/adr/${created.id}.md`);
30
+ console.log(`${chalk.bold("Title:")} ${created.title}`);
31
+ console.log(`${chalk.bold("Status:")} ${created.status}\n`);
32
+ }
33
+ export async function listAdrsCommand() {
34
+ const cwd = process.cwd();
35
+ console.log(chalk.bold.cyan("\n📖 Architectural Decision Records (ADRs)"));
36
+ console.log(chalk.dim("------------------------------------------"));
37
+ const list = await ADRService.list(cwd);
38
+ if (list.length === 0) {
39
+ console.log(chalk.gray("No ADRs found under 'docs/adr/'."));
40
+ return;
41
+ }
42
+ for (const adr of list) {
43
+ let statusColor = chalk.yellow;
44
+ if (adr.status === "accepted")
45
+ statusColor = chalk.green;
46
+ else if (adr.status === "rejected")
47
+ statusColor = chalk.red;
48
+ else if (adr.status === "superseded")
49
+ statusColor = chalk.gray;
50
+ console.log(`${chalk.bold.green(`- ${adr.id}`)}: ${adr.title} (${statusColor(adr.status)}) [${adr.date}]`);
51
+ if (adr.metadata?.decisionMakerRole) {
52
+ console.log(chalk.gray(` Decision Maker: ${adr.metadata.decisionMakerRole}`));
53
+ }
54
+ }
55
+ console.log("");
56
+ }
57
+ export async function showAdrCommand(id) {
58
+ const cwd = process.cwd();
59
+ const adr = await ADRService.load(cwd, id);
60
+ let statusColor = chalk.yellow;
61
+ if (adr.status === "accepted")
62
+ statusColor = chalk.green;
63
+ else if (adr.status === "rejected")
64
+ statusColor = chalk.red;
65
+ else if (adr.status === "superseded")
66
+ statusColor = chalk.gray;
67
+ console.log(chalk.bold.cyan(`\n🔍 ADR Details: ${adr.id}`));
68
+ console.log(chalk.dim("------------------------------------------"));
69
+ console.log(`${chalk.bold("Title:")} ${adr.title}`);
70
+ console.log(`${chalk.bold("Date:")} ${adr.date}`);
71
+ console.log(`${chalk.bold("Status:")} ${statusColor(adr.status)}`);
72
+ if (adr.metadata?.decisionMakerRole) {
73
+ console.log(`${chalk.bold("Role:")} ${adr.metadata.decisionMakerRole}`);
74
+ }
75
+ console.log(chalk.dim("------------------------------------------"));
76
+ console.log(chalk.bold("Context:"));
77
+ console.log(` ${adr.context}\n`);
78
+ console.log(chalk.bold("Decision:"));
79
+ console.log(` ${adr.decision}\n`);
80
+ console.log(chalk.bold("Consequences:"));
81
+ console.log(` ${adr.consequences}\n`);
82
+ console.log(chalk.dim("------------------------------------------\n"));
83
+ }
84
+ export async function searchAdrsCommand(keyword) {
85
+ const cwd = process.cwd();
86
+ console.log(chalk.bold.cyan(`\n🔎 Searching ADRs for keyword: '${keyword}'`));
87
+ console.log(chalk.dim("------------------------------------------"));
88
+ const results = await ADRService.search(cwd, keyword);
89
+ if (results.length === 0) {
90
+ console.log(chalk.yellow("No decisions found matching the keyword."));
91
+ return;
92
+ }
93
+ console.log(chalk.green(`Found ${results.length} matching ADRs:\n`));
94
+ for (const adr of results) {
95
+ console.log(`${chalk.bold.green(`- ${adr.id}`)}: ${adr.title} (${adr.status})`);
96
+ }
97
+ console.log("");
98
+ }
@@ -101,6 +101,19 @@ export async function runExport(target, options) {
101
101
  output += `=============================\n\n`;
102
102
  }
103
103
  output = output.trim();
104
+ if (options.output) {
105
+ console.log(chalk.blue(`Writing exported workflows to file: ${options.output}...`));
106
+ try {
107
+ const fullOutputPath = path.resolve(cwd, options.output);
108
+ await fs.mkdir(path.dirname(fullOutputPath), { recursive: true });
109
+ await fs.writeFile(fullOutputPath, output, "utf8");
110
+ console.log(chalk.green(`✔️ Successfully exported ${parsedSkills.length} custom workflow(s) to ${options.output}!`));
111
+ }
112
+ catch (err) {
113
+ console.error(chalk.red(`❌ Failed to write export file: ${err instanceof Error ? err.message : String(err)}`));
114
+ }
115
+ return;
116
+ }
104
117
  if (options.clipboard) {
105
118
  console.log(chalk.blue("Copying exported workflows to clipboard..."));
106
119
  const success = await writeToClipboard(output);
@@ -18,7 +18,8 @@ function printSuccessAndNextSteps(options) {
18
18
  console.log(chalk.bold.cyan("\n👉 Next Steps:"));
19
19
  console.log(chalk.dim("------------------------------------------"));
20
20
  console.log(chalk.white(`1. Open & review the generated guidelines:`));
21
- console.log(chalk.gray(` - Root: ${chalk.underline("AGENTS.md")}`));
21
+ const targetFile = options.agent === "antigravity" ? "GEMINI.md" : "AGENTS.md";
22
+ console.log(chalk.gray(` - Root: ${chalk.underline(targetFile)}`));
22
23
  console.log(chalk.gray(` - Stack rules: ${chalk.underline(".agents/rules/")}`));
23
24
  console.log(chalk.white(`2. Setup automatic git pre-commit hook validation:`));
24
25
  console.log(chalk.cyan(` npx agent-workflow-kit-cli doctor --install-hook`));
@@ -27,6 +28,56 @@ function printSuccessAndNextSteps(options) {
27
28
  console.log(chalk.dim("------------------------------------------\n"));
28
29
  }
29
30
  }
31
+ export async function updateGitignore(targetDir, dryRun) {
32
+ const gitignorePath = path.join(targetDir, ".gitignore");
33
+ const rulesToIgnore = [".cursorrules", ".copilot-instructions.md", ".clinerules"];
34
+ if (dryRun) {
35
+ console.log(chalk.gray(`[Dry Run] Would update .gitignore in ${targetDir} to exclude IDE rule files.`));
36
+ return;
37
+ }
38
+ try {
39
+ let content = "";
40
+ try {
41
+ content = await fs.readFile(gitignorePath, "utf8");
42
+ }
43
+ catch {
44
+ // gitignore does not exist
45
+ }
46
+ const lines = content.split("\n").map((l) => l.trim());
47
+ const missingRules = rulesToIgnore.filter((rule) => !lines.includes(rule));
48
+ if (missingRules.length > 0) {
49
+ const separator = content.length > 0 && !content.endsWith("\n") ? "\n\n" : "";
50
+ const newContent = content +
51
+ separator +
52
+ "# Agent Workflow Kit IDE rules\n" +
53
+ missingRules.join("\n") +
54
+ "\n";
55
+ await fs.writeFile(gitignorePath, newContent, "utf8");
56
+ console.log(chalk.green("✔️ Updated .gitignore to exclude IDE rules."));
57
+ }
58
+ }
59
+ catch (err) {
60
+ console.warn(chalk.yellow(`Could not update .gitignore: ${err instanceof Error ? err.message : String(err)}`));
61
+ }
62
+ }
63
+ async function writeWorkspaceIdeRulesAndGitignore(cwd, options) {
64
+ const ideRulesContent = await renderTemplate("common/ide-rules.hbs", {
65
+ agent: options.agent,
66
+ });
67
+ const ideFiles = [".cursorrules", ".copilot-instructions.md", ".clinerules"];
68
+ for (const file of ideFiles) {
69
+ if (options.dryRun) {
70
+ console.log(chalk.gray(`[Dry Run] Would write root ${file}`));
71
+ }
72
+ else {
73
+ await fs.writeFile(path.join(cwd, file), ideRulesContent, "utf8");
74
+ }
75
+ }
76
+ if (!options.dryRun) {
77
+ console.log(chalk.green("✔️ Created workspace IDE prompt anchors (.cursorrules, .copilot-instructions.md, .clinerules)"));
78
+ }
79
+ await updateGitignore(cwd, options.dryRun);
80
+ }
30
81
  export async function runInit(options) {
31
82
  const cwd = process.cwd();
32
83
  console.log(chalk.bold.cyan("\n🚀 Agent Workflow Kit - Initializing..."));
@@ -47,40 +98,74 @@ export async function runInit(options) {
47
98
  const finalAgentsContent = await renderTemplate("common/AGENTS.md.hbs", {
48
99
  stackContent: "",
49
100
  });
50
- if (options.dryRun) {
51
- console.log(chalk.gray(`[Dry Run] Would write root AGENTS.md`));
101
+ if (options.agent === "antigravity" || options.agent === "both") {
102
+ if (options.dryRun) {
103
+ console.log(chalk.gray(`[Dry Run] Would write root GEMINI.md`));
104
+ }
105
+ else {
106
+ const geminiPath = path.join(cwd, "GEMINI.md");
107
+ await fs.writeFile(geminiPath, finalAgentsContent, "utf8");
108
+ console.log(chalk.green("✔️ Created root GEMINI.md with general guidelines."));
109
+ }
52
110
  }
53
- else {
54
- const agentsPath = path.join(cwd, "AGENTS.md");
55
- await fs.writeFile(agentsPath, finalAgentsContent, "utf8");
56
- console.log(chalk.green("✔️ Created root AGENTS.md with general guidelines."));
111
+ if (options.agent === "codex" || options.agent === "both") {
112
+ if (options.dryRun) {
113
+ console.log(chalk.gray(`[Dry Run] Would write root AGENTS.md`));
114
+ }
115
+ else {
116
+ const agentsPath = path.join(cwd, "AGENTS.md");
117
+ await fs.writeFile(agentsPath, finalAgentsContent, "utf8");
118
+ console.log(chalk.green("✔️ Created root AGENTS.md with general guidelines."));
119
+ }
57
120
  }
121
+ await writeWorkspaceIdeRulesAndGitignore(cwd, options);
58
122
  printSuccessAndNextSteps(options);
59
123
  return;
60
124
  }
61
125
  const isMonorepo = modules.length > 1 || (modules.length === 1 && modules[0].name !== ".");
62
- // Write root AGENTS.md with monorepo details
126
+ // Write root files with monorepo details
63
127
  if (isMonorepo) {
64
128
  let monorepoContent = "### 📦 Monorepo Multi-Module Project Structure\n\nThis repository is configured as a monorepo. Please load and follow the stack-specific guidelines in each subdirectory:\n\n";
65
129
  for (const mod of modules) {
66
- monorepoContent += `- **${mod.name}** (${mod.stacks.join(", ")}): Stack rules and guidelines are located at [${mod.name}/AGENTS.md](file:///${mod.dir.replace(/\\/g, "/")}/AGENTS.md)\n`;
130
+ const isAntigravity = options.agent === "antigravity";
131
+ const targetFileName = isAntigravity ? "GEMINI.md" : "AGENTS.md";
132
+ monorepoContent += `- **${mod.name}** (${mod.stacks.join(", ")}): Stack rules and guidelines are located at [${mod.name}/${targetFileName}](file:///${mod.dir.replace(/\\/g, "/")}/${targetFileName})\n`;
67
133
  }
68
134
  const rootAgentsContent = await renderTemplate("common/AGENTS.md.hbs", {
69
135
  stackContent: monorepoContent.trim(),
70
136
  });
137
+ const rootGeminiPath = path.join(cwd, "GEMINI.md");
71
138
  const rootAgentsPath = path.join(cwd, "AGENTS.md");
72
- if (options.dryRun) {
73
- console.log(chalk.gray(`[Dry Run] Would write root AGENTS.md containing monorepo navigation:\n${monorepoContent}`));
139
+ if (options.agent === "antigravity" || options.agent === "both") {
140
+ if (options.dryRun) {
141
+ console.log(chalk.gray(`[Dry Run] Would write root GEMINI.md containing monorepo navigation:\n${monorepoContent}`));
142
+ }
143
+ else {
144
+ try {
145
+ await fs.access(rootGeminiPath);
146
+ await updateFileWithBlock(rootGeminiPath, "STACK_PACK", monorepoContent.trim());
147
+ console.log(chalk.green("✔️ Updated STACK_PACK block in root GEMINI.md."));
148
+ }
149
+ catch {
150
+ await fs.writeFile(rootGeminiPath, rootAgentsContent, "utf8");
151
+ console.log(chalk.green("✔️ Created root GEMINI.md for monorepo."));
152
+ }
153
+ }
74
154
  }
75
- else {
76
- try {
77
- await fs.access(rootAgentsPath);
78
- await updateFileWithBlock(rootAgentsPath, "STACK_PACK", monorepoContent.trim());
79
- console.log(chalk.green("✔️ Updated STACK_PACK block in root AGENTS.md."));
155
+ if (options.agent === "codex" || options.agent === "both") {
156
+ if (options.dryRun) {
157
+ console.log(chalk.gray(`[Dry Run] Would write root AGENTS.md containing monorepo navigation:\n${monorepoContent}`));
80
158
  }
81
- catch {
82
- await fs.writeFile(rootAgentsPath, rootAgentsContent, "utf8");
83
- console.log(chalk.green("✔️ Created root AGENTS.md for monorepo."));
159
+ else {
160
+ try {
161
+ await fs.access(rootAgentsPath);
162
+ await updateFileWithBlock(rootAgentsPath, "STACK_PACK", monorepoContent.trim());
163
+ console.log(chalk.green("✔️ Updated STACK_PACK block in root AGENTS.md."));
164
+ }
165
+ catch {
166
+ await fs.writeFile(rootAgentsPath, rootAgentsContent, "utf8");
167
+ console.log(chalk.green("✔️ Created root AGENTS.md for monorepo."));
168
+ }
84
169
  }
85
170
  }
86
171
  }
@@ -100,23 +185,42 @@ export async function runInit(options) {
100
185
  }
101
186
  }
102
187
  stackContent = stackContent.trim();
103
- // Render AGENTS.md for this module
188
+ // Render agent files for this module
189
+ const geminiPath = path.join(mod.dir, "GEMINI.md");
104
190
  const agentsPath = path.join(mod.dir, "AGENTS.md");
105
191
  const moduleAgentsContent = await renderTemplate("common/AGENTS.md.hbs", {
106
192
  stackContent,
107
193
  });
108
- if (options.dryRun) {
109
- console.log(chalk.gray(`[Dry Run] Would write AGENTS.md at ${agentsPath}`));
194
+ if (options.agent === "antigravity" || options.agent === "both") {
195
+ if (options.dryRun) {
196
+ console.log(chalk.gray(`[Dry Run] Would write GEMINI.md at ${geminiPath}`));
197
+ }
198
+ else {
199
+ try {
200
+ await fs.access(geminiPath);
201
+ await updateFileWithBlock(geminiPath, "STACK_PACK", stackContent);
202
+ console.log(chalk.green(`✔️ Updated STACK_PACK block in ${mod.name}/GEMINI.md`));
203
+ }
204
+ catch {
205
+ await fs.writeFile(geminiPath, moduleAgentsContent, "utf8");
206
+ console.log(chalk.green(`✔️ Created ${mod.name}/GEMINI.md`));
207
+ }
208
+ }
110
209
  }
111
- else {
112
- try {
113
- await fs.access(agentsPath);
114
- await updateFileWithBlock(agentsPath, "STACK_PACK", stackContent);
115
- console.log(chalk.green(`✔️ Updated STACK_PACK block in ${mod.name}/AGENTS.md`));
210
+ if (options.agent === "codex" || options.agent === "both") {
211
+ if (options.dryRun) {
212
+ console.log(chalk.gray(`[Dry Run] Would write AGENTS.md at ${agentsPath}`));
116
213
  }
117
- catch {
118
- await fs.writeFile(agentsPath, moduleAgentsContent, "utf8");
119
- console.log(chalk.green(`✔️ Created ${mod.name}/AGENTS.md`));
214
+ else {
215
+ try {
216
+ await fs.access(agentsPath);
217
+ await updateFileWithBlock(agentsPath, "STACK_PACK", stackContent);
218
+ console.log(chalk.green(`✔️ Updated STACK_PACK block in ${mod.name}/AGENTS.md`));
219
+ }
220
+ catch {
221
+ await fs.writeFile(agentsPath, moduleAgentsContent, "utf8");
222
+ console.log(chalk.green(`✔️ Created ${mod.name}/AGENTS.md`));
223
+ }
120
224
  }
121
225
  }
122
226
  // Copy common skills for this module
@@ -186,5 +290,7 @@ export async function runInit(options) {
186
290
  }
187
291
  }
188
292
  }
293
+ // Write workspace-level IDE rules and update gitignore
294
+ await writeWorkspaceIdeRulesAndGitignore(cwd, options);
189
295
  printSuccessAndNextSteps(options);
190
296
  }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ import chalk from "chalk";
6
+ import { getRepositoryContext } from "../../core/awos/intelligence.js";
7
+ import { loadProfile, validateArchitecture } from "../../core/awos/profiles.js";
8
+ export async function runProfileCheck(options) {
9
+ const cwd = process.cwd();
10
+ console.log(chalk.bold.cyan("\n🔎 AWOS Architecture Profile Linter"));
11
+ console.log(chalk.dim("------------------------------------------"));
12
+ const context = await getRepositoryContext(cwd);
13
+ const targetProfileName = options.profile || context.architecture;
14
+ console.log(`Target Profile: ${chalk.green(targetProfileName)} (detected from ${chalk.yellow(context.stack)})`);
15
+ console.log(chalk.dim("------------------------------------------\n"));
16
+ try {
17
+ const profile = await loadProfile(targetProfileName);
18
+ console.log(chalk.gray("Scanning workspace files for structural rules constraints..."));
19
+ const violations = await validateArchitecture(cwd, profile);
20
+ if (violations.length === 0) {
21
+ console.log(chalk.bold.green("\n🎉 Congratulations! Zero architecture or package boundaries violations found."));
22
+ return;
23
+ }
24
+ console.log(chalk.bold.red(`\n⚠️ Found ${violations.length} architectural rule violations:\n`));
25
+ for (const v of violations) {
26
+ const color = v.severity === "error" ? chalk.red : chalk.yellow;
27
+ console.log(`${color(`[${v.severity.toUpperCase()}]`)} ${chalk.underline(v.filePath)}`);
28
+ console.log(chalk.gray(` Rule: ${v.ruleName} - ${v.message}\n`));
29
+ }
30
+ process.exit(1);
31
+ }
32
+ catch (err) {
33
+ throw new Error(`Profile check failed: ${err instanceof Error ? err.message : String(err)}`);
34
+ }
35
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ import chalk from "chalk";
6
+ import path from "path";
7
+ import { RoleRegistry } from "../../core/awos/registry.js";
8
+ const DEFAULT_ROLES_DIR = ".agents/roles";
9
+ export async function listRoles() {
10
+ const cwd = process.cwd();
11
+ console.log(chalk.bold.cyan("\n👥 Discovered Agent Role Catalog"));
12
+ console.log(chalk.dim("------------------------------------------"));
13
+ const dir = path.join(cwd, DEFAULT_ROLES_DIR);
14
+ const list = await RoleRegistry.discover(dir);
15
+ if (list.length === 0) {
16
+ console.log(chalk.gray(`No roles found under '${DEFAULT_ROLES_DIR}/'.`));
17
+ return;
18
+ }
19
+ for (const role of list) {
20
+ console.log(`${chalk.bold.green(`- ${role.id}`)}: ${role.name}`);
21
+ console.log(chalk.gray(` ${role.description}\n`));
22
+ }
23
+ }
24
+ export async function showRole(id) {
25
+ const cwd = process.cwd();
26
+ const dir = path.join(cwd, DEFAULT_ROLES_DIR);
27
+ const role = await RoleRegistry.load(id, dir);
28
+ console.log(chalk.bold.cyan(`\n🔍 Agent Role: ${role.id}`));
29
+ console.log(chalk.dim("------------------------------------------"));
30
+ console.log(`${chalk.bold("Name:")} ${role.name}`);
31
+ console.log(`${chalk.bold("Description:")} ${role.description}`);
32
+ console.log(chalk.dim("------------------------------------------"));
33
+ console.log(chalk.bold("Responsibilities:"));
34
+ for (const resp of role.responsibilities) {
35
+ console.log(` - ${resp}`);
36
+ }
37
+ console.log(chalk.bold("\nRequired Inputs:"));
38
+ for (const inp of role.requiredInputs) {
39
+ console.log(` - ${inp}`);
40
+ }
41
+ console.log(chalk.bold("\nExpected Outputs:"));
42
+ for (const out of role.expectedOutputs) {
43
+ console.log(` - ${out}`);
44
+ }
45
+ console.log(chalk.bold("\nValidation Checklist:"));
46
+ for (const item of role.validationChecklist) {
47
+ console.log(` - ${item}`);
48
+ }
49
+ console.log(chalk.bold("\nReview Checklist:"));
50
+ for (const item of role.reviewChecklist) {
51
+ console.log(` - ${item}`);
52
+ }
53
+ console.log(chalk.dim("------------------------------------------\n"));
54
+ }
55
+ export async function validateRoles() {
56
+ const cwd = process.cwd();
57
+ const dir = path.join(cwd, DEFAULT_ROLES_DIR);
58
+ try {
59
+ const list = await RoleRegistry.discover(dir);
60
+ if (list.length === 0) {
61
+ console.log(chalk.yellow(`No roles discovered to validate under '${DEFAULT_ROLES_DIR}/'.`));
62
+ return;
63
+ }
64
+ for (const role of list) {
65
+ RoleRegistry.validate(role);
66
+ console.log(chalk.green(`✔️ Role '${role.id}' is schema valid.`));
67
+ }
68
+ console.log(chalk.bold.green(`\n🎉 All ${list.length} agent roles validated successfully!`));
69
+ }
70
+ catch (err) {
71
+ console.error(chalk.bold.red(`\n❌ Agent Role validation failed:`));
72
+ console.error(chalk.red(` ${err instanceof Error ? err.message : String(err)}`));
73
+ process.exit(1);
74
+ }
75
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+ import chalk from "chalk";
6
+ import { promises as fs } from "fs";
7
+ import path from "path";
8
+ import { WorkflowRuntime } from "../../core/awos/runtime.js";
9
+ export async function runWorkflowCommand(workflowPath, options) {
10
+ const cwd = process.cwd();
11
+ console.log(chalk.bold.cyan("\n🚀 AWOS Workflow Executor"));
12
+ console.log(chalk.dim("------------------------------------------"));
13
+ console.log(`Workflow File: ${chalk.green(workflowPath)}`);
14
+ console.log(`Dry Run: ${options.dryRun ? chalk.yellow("Enabled 🧪") : chalk.gray("Disabled")}`);
15
+ console.log(chalk.dim("------------------------------------------\n"));
16
+ let graph;
17
+ try {
18
+ const fullPath = path.resolve(cwd, workflowPath);
19
+ const content = await fs.readFile(fullPath, "utf8");
20
+ graph = JSON.parse(content);
21
+ }
22
+ catch (err) {
23
+ throw new Error(`Failed to load workflow file: ${err instanceof Error ? err.message : String(err)}`);
24
+ }
25
+ let inputs = {};
26
+ if (options.inputs) {
27
+ try {
28
+ const inputsPath = path.resolve(cwd, options.inputs);
29
+ const content = await fs.readFile(inputsPath, "utf8");
30
+ inputs = JSON.parse(content);
31
+ }
32
+ catch {
33
+ // Inline JSON parsing
34
+ try {
35
+ inputs = JSON.parse(options.inputs);
36
+ }
37
+ catch (err) {
38
+ throw new Error(`Failed to parse inputs parameters: ${err instanceof Error ? err.message : String(err)}`);
39
+ }
40
+ }
41
+ }
42
+ const runtime = new WorkflowRuntime(cwd);
43
+ const result = await runtime.run(graph, inputs, { dryRun: options.dryRun });
44
+ console.log(chalk.dim("\n------------------------------------------"));
45
+ if (result.status === "SUCCESS") {
46
+ console.log(chalk.bold.green(`✔️ Workflow execution succeeded! Run ID: ${result.runId}`));
47
+ }
48
+ else if (result.status === "PAUSED") {
49
+ console.log(chalk.bold.yellow(`⚠️ Workflow execution paused. Resume using: awk run resume ${result.runId}`));
50
+ }
51
+ else {
52
+ console.log(chalk.bold.red(`❌ Workflow execution failed. Run ID: ${result.runId}`));
53
+ }
54
+ }
55
+ export async function resumeWorkflowCommand(runId) {
56
+ const cwd = process.cwd();
57
+ const runFile = path.resolve(cwd, `.agents/state/runs/${runId}.json`);
58
+ console.log(chalk.bold.cyan(`\n🚀 AWOS Workflow Executor - Resuming Run '${runId}'`));
59
+ console.log(chalk.dim("------------------------------------------"));
60
+ try {
61
+ const runContent = await fs.readFile(runFile, "utf8");
62
+ const runState = JSON.parse(runContent);
63
+ console.log(`Active step state was: ${chalk.yellow(runState.status)}`);
64
+ // Simulate resumption by completing approval steps
65
+ runState.status = "EXECUTING";
66
+ const pausedStepIdx = runState.steps.findIndex((s) => s.status === "WAITING_APPROVAL");
67
+ if (pausedStepIdx !== -1) {
68
+ runState.steps[pausedStepIdx].status = "COMPLETED";
69
+ runState.steps[pausedStepIdx].completedAt = new Date().toISOString();
70
+ console.log(`✔️ Human approved step: ${chalk.green(runState.steps[pausedStepIdx].name)}`);
71
+ }
72
+ await fs.writeFile(runFile, JSON.stringify(runState, null, 2), "utf8");
73
+ console.log(chalk.green(`✔️ Run '${runId}' successfully resumed & marked completed!`));
74
+ }
75
+ catch (err) {
76
+ throw new Error(`Failed to resume execution run '${runId}': ${err instanceof Error ? err.message : String(err)}`);
77
+ }
78
+ }