agent-workflow-kit-cli 1.1.0 โ 1.2.1
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/commands/add.js +172 -0
- package/dist/cli/commands/export.js +3 -2
- package/dist/cli/commands/init.js +136 -30
- package/dist/cli/index.js +17 -1
- package/dist/core/detector.js +63 -52
- package/package.json +1 -1
- package/templates/common/ide-rules.hbs +12 -0
|
@@ -0,0 +1,172 @@
|
|
|
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 { renderTemplate, readStaticTemplateFile, getStackRules, getStackSkills, } from "../../core/renderer.js";
|
|
9
|
+
import { updateFileWithBlock, writeRuleWithChunking, } from "../../core/emitter.js";
|
|
10
|
+
import { analyzeModule } from "../../core/analyzer.js";
|
|
11
|
+
import { updateGitignore } from "./init.js";
|
|
12
|
+
export async function runAdd(stack, options) {
|
|
13
|
+
const targetStack = stack.toLowerCase();
|
|
14
|
+
const validStacks = ["spring-boot", "react-ts", "fastapi", "python-ai"];
|
|
15
|
+
if (!validStacks.includes(targetStack)) {
|
|
16
|
+
console.error(chalk.red(`Error: Invalid stack '${stack}'. Supported stacks are: ${validStacks.join(", ")}`));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const targetDir = path.resolve(options.path);
|
|
20
|
+
console.log(chalk.bold.cyan("\n๐ Agent Workflow Kit - Adding Stack Pack..."));
|
|
21
|
+
console.log(chalk.dim("------------------------------------------"));
|
|
22
|
+
console.log(`${chalk.bold("Target Folder:")} ${chalk.green(targetDir)}`);
|
|
23
|
+
console.log(`${chalk.bold("Stack Pack:")} ${chalk.green(targetStack)}`);
|
|
24
|
+
console.log(`${chalk.bold("Agent Profile:")} ${chalk.green(options.agent)}`);
|
|
25
|
+
console.log(`${chalk.bold("Dry Run:")} ${options.dryRun ? chalk.yellow("Enabled ๐งช") : chalk.gray("Disabled")}`);
|
|
26
|
+
console.log(chalk.dim("------------------------------------------\n"));
|
|
27
|
+
// 1. Analyze the target directory for this specific stack
|
|
28
|
+
const analysis = await analyzeModule(targetDir, [targetStack]);
|
|
29
|
+
// 2. Render stack guidelines for AGENTS.md
|
|
30
|
+
let stackContent = "";
|
|
31
|
+
try {
|
|
32
|
+
const stackCtx = analysis[targetStack] || {};
|
|
33
|
+
const rendered = await renderTemplate(`${targetStack}/AGENTS.md.hbs`, stackCtx);
|
|
34
|
+
stackContent = rendered.trim();
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
console.error(chalk.red(`Error loading template for stack '${targetStack}': ${err instanceof Error ? err.message : String(err)}`));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
// 3. Write or Update AGENTS.md and/or GEMINI.md in the target directory
|
|
41
|
+
const geminiPath = path.join(targetDir, "GEMINI.md");
|
|
42
|
+
const agentsPath = path.join(targetDir, "AGENTS.md");
|
|
43
|
+
const moduleAgentsContent = await renderTemplate("common/AGENTS.md.hbs", {
|
|
44
|
+
stackContent,
|
|
45
|
+
});
|
|
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
|
+
}
|
|
62
|
+
}
|
|
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
|
+
}
|
|
78
|
+
}
|
|
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");
|
|
91
|
+
}
|
|
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);
|
|
97
|
+
// 4. Copy rules and skills for the target stack
|
|
98
|
+
const stackCtx = analysis[targetStack] || {};
|
|
99
|
+
// A. Copy Rules
|
|
100
|
+
const rules = await getStackRules(targetStack);
|
|
101
|
+
for (const rule of rules) {
|
|
102
|
+
if (rule === "microservice-style.md" && stackCtx.isMicroservice !== true) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const relativeRulePath = `${targetStack}/rules/${rule}`;
|
|
106
|
+
try {
|
|
107
|
+
const ruleContent = await readStaticTemplateFile(relativeRulePath, stackCtx);
|
|
108
|
+
const targetRulePath = path.join(targetDir, ".agents", "rules", rule);
|
|
109
|
+
if (options.dryRun) {
|
|
110
|
+
console.log(chalk.gray(`[Dry Run] Would write rule to ${targetRulePath}`));
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
await writeRuleWithChunking(targetRulePath, ruleContent);
|
|
114
|
+
console.log(chalk.green(`โ๏ธ Wrote rule ${rule} to ${targetRulePath}`));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
console.error(chalk.red(`Failed to copy rule ${rule}: ${err instanceof Error ? err.message : String(err)}`));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// B. Copy Skills
|
|
122
|
+
const skills = await getStackSkills(targetStack);
|
|
123
|
+
for (const skill of skills) {
|
|
124
|
+
const relativeSkillPath = `${targetStack}/skills/${skill}`;
|
|
125
|
+
try {
|
|
126
|
+
const skillContent = await readStaticTemplateFile(relativeSkillPath, stackCtx);
|
|
127
|
+
const targetSkillPath = path.join(targetDir, ".agents", "skills", skill);
|
|
128
|
+
if (options.dryRun) {
|
|
129
|
+
console.log(chalk.gray(`[Dry Run] Would write skill to ${targetSkillPath}`));
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
await fs.mkdir(path.dirname(targetSkillPath), { recursive: true });
|
|
133
|
+
await fs.writeFile(targetSkillPath, skillContent, "utf8");
|
|
134
|
+
console.log(chalk.green(`โ๏ธ Wrote skill ${skill} to ${targetSkillPath}`));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
console.error(chalk.red(`Failed to copy skill ${skill}: ${err instanceof Error ? err.message : String(err)}`));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// C. Copy Common Skills if not present
|
|
142
|
+
try {
|
|
143
|
+
const commonSkills = await getStackSkills("common");
|
|
144
|
+
for (const skill of commonSkills) {
|
|
145
|
+
const relativeSkillPath = `common/skills/${skill}`;
|
|
146
|
+
try {
|
|
147
|
+
const skillContent = await readStaticTemplateFile(relativeSkillPath, {});
|
|
148
|
+
const targetSkillPath = path.join(targetDir, ".agents", "skills", skill);
|
|
149
|
+
if (options.dryRun) {
|
|
150
|
+
console.log(chalk.gray(`[Dry Run] Would ensure common skill ${skill} exists`));
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
try {
|
|
154
|
+
await fs.access(targetSkillPath);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
await fs.mkdir(path.dirname(targetSkillPath), { recursive: true });
|
|
158
|
+
await fs.writeFile(targetSkillPath, skillContent, "utf8");
|
|
159
|
+
console.log(chalk.green(`โ๏ธ Wrote common skill ${skill} to ${targetSkillPath}`));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
console.error(chalk.red(`Failed to copy common skill ${skill}: ${err instanceof Error ? err.message : String(err)}`));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// Ignore
|
|
170
|
+
}
|
|
171
|
+
console.log(chalk.bold.green("\n๐ Add completed successfully!"));
|
|
172
|
+
}
|
|
@@ -58,8 +58,9 @@ export function parseSkillFile(content, defaultName) {
|
|
|
58
58
|
return { name, description, body: body.trim() };
|
|
59
59
|
}
|
|
60
60
|
export async function runExport(target, options) {
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
const normTarget = target.toLowerCase();
|
|
62
|
+
if (normTarget !== "antigravity" && normTarget !== "codex") {
|
|
63
|
+
console.warn(chalk.yellow(`Warning: Currently, only 'antigravity' and 'codex' targets are officially optimized, but trying to export custom rules anyway.`));
|
|
63
64
|
}
|
|
64
65
|
const cwd = process.cwd();
|
|
65
66
|
const skillsDir = path.join(cwd, ".agents", "skills");
|
|
@@ -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
|
-
|
|
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.
|
|
51
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
73
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
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.
|
|
109
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
import { runInit } from "./commands/init.js";
|
|
8
|
+
import { runAdd } from "./commands/add.js";
|
|
8
9
|
import { runSync } from "./commands/sync.js";
|
|
9
10
|
import { runDoctor } from "./commands/doctor.js";
|
|
10
11
|
import { runExport } from "./commands/export.js";
|
|
@@ -13,7 +14,7 @@ export function runCli() {
|
|
|
13
14
|
program
|
|
14
15
|
.name("agent-workflow-kit")
|
|
15
16
|
.description("Generate AI coding workflows/rules/templates for Codex and Antigravity")
|
|
16
|
-
.version("1.1
|
|
17
|
+
.version("1.2.1");
|
|
17
18
|
program
|
|
18
19
|
.command("init")
|
|
19
20
|
.description("Initialize agent guidelines and skills for the repository")
|
|
@@ -29,6 +30,21 @@ export function runCli() {
|
|
|
29
30
|
process.exit(1);
|
|
30
31
|
}
|
|
31
32
|
});
|
|
33
|
+
program
|
|
34
|
+
.command("add <stack>")
|
|
35
|
+
.description("Manually add/install a stack pack to a specific folder")
|
|
36
|
+
.option("--path <path>", "Target folder path to install the guidelines", ".")
|
|
37
|
+
.option("--agent <agent>", "Specify target agent profile: both | codex | antigravity", "both")
|
|
38
|
+
.option("--dry-run", "Output actions to console without writing any files", false)
|
|
39
|
+
.action(async (stack, options) => {
|
|
40
|
+
try {
|
|
41
|
+
await runAdd(stack, options);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
console.error(chalk.red(`Error running add: ${err instanceof Error ? err.message : String(err)}`));
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
32
48
|
program
|
|
33
49
|
.command("sync")
|
|
34
50
|
.description("Sync generated guidelines and skills inside managed blocks")
|
package/dist/core/detector.js
CHANGED
|
@@ -87,73 +87,84 @@ async function detectProjectStackDirect(cwd) {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
catch {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
export async function detectProjectStack(cwd) {
|
|
99
|
-
const stacks = await detectProjectStackDirect(cwd);
|
|
100
|
-
// Scan 1-level deep subdirectories for potential monorepos/multimodules
|
|
101
|
-
try {
|
|
102
|
-
const entries = await fs.readdir(cwd, { withFileTypes: true });
|
|
103
|
-
for (const entry of entries) {
|
|
104
|
-
if (entry.isDirectory() &&
|
|
105
|
-
!entry.name.startsWith(".") &&
|
|
106
|
-
entry.name !== "node_modules" &&
|
|
107
|
-
entry.name !== "dist") {
|
|
108
|
-
const subPath = path.join(cwd, entry.name);
|
|
109
|
-
const subStacks = await detectProjectStackDirect(subPath);
|
|
110
|
-
for (const stack of subStacks) {
|
|
111
|
-
if (!stacks.includes(stack)) {
|
|
112
|
-
stacks.push(stack);
|
|
113
|
-
}
|
|
90
|
+
try {
|
|
91
|
+
const pipfilePath = path.join(cwd, "Pipfile");
|
|
92
|
+
const content = await fs.readFile(pipfilePath, "utf8");
|
|
93
|
+
if (content.includes("fastapi")) {
|
|
94
|
+
stacks.push("fastapi");
|
|
95
|
+
}
|
|
96
|
+
if (aiKeywords.some(kw => content.includes(kw))) {
|
|
97
|
+
stacks.push("python-ai");
|
|
114
98
|
}
|
|
115
99
|
}
|
|
100
|
+
catch {
|
|
101
|
+
// Ignore
|
|
102
|
+
}
|
|
116
103
|
}
|
|
117
104
|
}
|
|
118
|
-
catch {
|
|
119
|
-
// Ignore
|
|
120
|
-
}
|
|
121
105
|
return stacks;
|
|
122
106
|
}
|
|
123
107
|
/**
|
|
124
|
-
*
|
|
108
|
+
* Helper to recursively search for modules containing manifest configurations up to maxDepth.
|
|
125
109
|
*/
|
|
126
|
-
|
|
110
|
+
async function findModulesRecursively(baseDir, currentDir, maxDepth, currentDepth = 0) {
|
|
127
111
|
const modules = [];
|
|
128
|
-
|
|
129
|
-
|
|
112
|
+
// Skip standard build and internal directories to avoid deep scans
|
|
113
|
+
const dirName = path.basename(currentDir);
|
|
114
|
+
if (dirName.startsWith(".") ||
|
|
115
|
+
dirName === "node_modules" ||
|
|
116
|
+
dirName === "target" ||
|
|
117
|
+
dirName === "build" ||
|
|
118
|
+
dirName === "dist" ||
|
|
119
|
+
dirName === "bin" ||
|
|
120
|
+
dirName === "out" ||
|
|
121
|
+
dirName === "venv" ||
|
|
122
|
+
dirName === ".venv") {
|
|
123
|
+
return modules;
|
|
124
|
+
}
|
|
125
|
+
// Detect direct stack presets in this current directory
|
|
126
|
+
const directStacks = await detectProjectStackDirect(currentDir);
|
|
127
|
+
if (directStacks.length > 0) {
|
|
130
128
|
modules.push({
|
|
131
|
-
dir:
|
|
132
|
-
name: ".",
|
|
133
|
-
stacks:
|
|
129
|
+
dir: currentDir,
|
|
130
|
+
name: currentDir === baseDir ? "." : path.relative(baseDir, currentDir).replace(/\\/g, "/"),
|
|
131
|
+
stacks: directStacks,
|
|
134
132
|
});
|
|
135
133
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
entry.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (subStacks.length > 0) {
|
|
146
|
-
modules.push({
|
|
147
|
-
dir: subPath,
|
|
148
|
-
name: entry.name,
|
|
149
|
-
stacks: subStacks,
|
|
150
|
-
});
|
|
134
|
+
// If we haven't hit max depth, traverse subdirectories
|
|
135
|
+
if (currentDepth < maxDepth) {
|
|
136
|
+
try {
|
|
137
|
+
const entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
138
|
+
for (const entry of entries) {
|
|
139
|
+
if (entry.isDirectory()) {
|
|
140
|
+
const subPath = path.join(currentDir, entry.name);
|
|
141
|
+
const subModules = await findModulesRecursively(baseDir, subPath, maxDepth, currentDepth + 1);
|
|
142
|
+
modules.push(...subModules);
|
|
151
143
|
}
|
|
152
144
|
}
|
|
153
145
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
146
|
+
catch {
|
|
147
|
+
// Ignore directory read errors
|
|
148
|
+
}
|
|
157
149
|
}
|
|
158
150
|
return modules;
|
|
159
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Scans the workspace directory and subfolders (up to 3 levels deep) for monorepo detection.
|
|
154
|
+
*/
|
|
155
|
+
export async function detectProjectStack(cwd) {
|
|
156
|
+
const modules = await detectProjectModules(cwd);
|
|
157
|
+
const stacks = new Set();
|
|
158
|
+
for (const mod of modules) {
|
|
159
|
+
for (const stack of mod.stacks) {
|
|
160
|
+
stacks.add(stack);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return Array.from(stacks);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Detects stacks grouped by module directories (root + up to 3 levels deep directories).
|
|
167
|
+
*/
|
|
168
|
+
export async function detectProjectModules(cwd) {
|
|
169
|
+
return findModulesRecursively(cwd, cwd, 3);
|
|
170
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Agent Workflow Kit - IDE System Instructions
|
|
2
|
+
|
|
3
|
+
You are an AI assistant ({{#if (eq agent "antigravity")}}Antigravity{{else}}{{#if (eq agent "codex")}}Codex{{else}}Antigravity / Codex{{/if}}{{/if}}) coding inside this repository. You MUST follow these repository standards for every query:
|
|
4
|
+
|
|
5
|
+
1. Always check for and follow {{#if (eq agent "antigravity")}}[GEMINI.md](file:///GEMINI.md){{else}}{{#if (eq agent "codex")}}[AGENTS.md](file:///AGENTS.md){{else}}[GEMINI.md](file:///GEMINI.md) (if you are Antigravity) or [AGENTS.md](file:///AGENTS.md) (if you are Codex){{/if}}{{/if}} at the workspace root (or current module workspace) before executing any task.
|
|
6
|
+
2. Follow the strict 5-Step Execution Workflow:
|
|
7
|
+
- Step 1: Research & Context Analysis (scan workspace, read relevant rules in `.agents/rules/` matching the active stack, e.g. `react-style.md` for React. Do not scan irrelevant files).
|
|
8
|
+
- Step 2: Formulate Implementation Plan (list files to create, modify, or delete).
|
|
9
|
+
- Step 3: Implementation & Clean Coding (maintain type-safety, styles, and layering).
|
|
10
|
+
- Step 4: Verification (run validation commands: compile, lint, test, build).
|
|
11
|
+
- Step 5: Self-Review & QA (review diff, clean logs, summarize edits).
|
|
12
|
+
3. Check and execute custom skills defined inside `.agents/skills/` (like `/spring-feature` or `/react-feature`) by reading their `SKILL.md` instruction file.
|