agent-flutter 0.1.1 → 0.1.3
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/README.md +5 -1
- package/package.json +1 -1
- package/src/cli.js +160 -22
package/README.md
CHANGED
|
@@ -14,12 +14,15 @@ By default `init` installs adapters for all supported IDEs:
|
|
|
14
14
|
- Cursor
|
|
15
15
|
- Windsurf
|
|
16
16
|
- Cline
|
|
17
|
+
- GitHub (Copilot)
|
|
17
18
|
|
|
18
19
|
## Commands
|
|
19
20
|
|
|
20
21
|
```bash
|
|
21
22
|
npx agent-flutter@latest init --ide all --cwd /path/to/project --force
|
|
22
23
|
npx agent-flutter@latest init --ide trae,codex
|
|
24
|
+
npx agent-flutter@latest sync
|
|
25
|
+
npx agent-flutter@latest sync --ide trae,codex,github
|
|
23
26
|
npx agent-flutter@latest list --cwd /path/to/project
|
|
24
27
|
```
|
|
25
28
|
|
|
@@ -49,9 +52,10 @@ npm run release:major
|
|
|
49
52
|
|
|
50
53
|
## Installed files
|
|
51
54
|
|
|
52
|
-
- Shared pack: `.agent-flutter/`
|
|
55
|
+
- Shared pack (for Trae/Codex/Cursor/Windsurf/Cline): `.agent-flutter/`
|
|
53
56
|
- Trae: `.trae/`
|
|
54
57
|
- Codex: `AGENTS.md`
|
|
55
58
|
- Cursor: `.cursor/rules/agent-flutter.mdc`
|
|
56
59
|
- Windsurf: `.windsurf/rules/agent-flutter.md`
|
|
57
60
|
- Cline: `.clinerules/agent-flutter.md`
|
|
61
|
+
- GitHub: `.github/skills/`, `.github/rules/`, `.github/copilot-instructions.md`
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -2,17 +2,19 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
4
|
|
|
5
|
-
const SUPPORTED_IDES = ['trae', 'codex', 'cursor', 'windsurf', 'cline'];
|
|
5
|
+
const SUPPORTED_IDES = ['trae', 'codex', 'cursor', 'windsurf', 'cline', 'github'];
|
|
6
6
|
|
|
7
7
|
const USAGE = `
|
|
8
8
|
agent-flutter
|
|
9
9
|
|
|
10
10
|
Usage:
|
|
11
|
-
npx agent-flutter@latest init [--ide all|trae,codex,cursor,windsurf,cline] [--cwd <project_dir>] [--force]
|
|
11
|
+
npx agent-flutter@latest init [--ide all|trae,codex,cursor,windsurf,cline,github] [--cwd <project_dir>] [--force]
|
|
12
|
+
npx agent-flutter@latest sync [--ide all|trae,codex,cursor,windsurf,cline,github] [--cwd <project_dir>]
|
|
12
13
|
npx agent-flutter@latest list [--cwd <project_dir>]
|
|
13
14
|
|
|
14
15
|
Commands:
|
|
15
16
|
init Install shared Flutter skills/rules and IDE adapters.
|
|
17
|
+
sync Update installed shared pack and adapters from latest template.
|
|
16
18
|
list Print available skills/rules from the shared pack.
|
|
17
19
|
`;
|
|
18
20
|
|
|
@@ -29,6 +31,9 @@ export async function runCli(argv) {
|
|
|
29
31
|
case 'init':
|
|
30
32
|
await runInit(options);
|
|
31
33
|
return;
|
|
34
|
+
case 'sync':
|
|
35
|
+
await runSync(options);
|
|
36
|
+
return;
|
|
32
37
|
case 'list':
|
|
33
38
|
await runList(options);
|
|
34
39
|
return;
|
|
@@ -82,17 +87,76 @@ async function runInit(options) {
|
|
|
82
87
|
const ideTargets = resolveIdeTargets(options.get('ide', 'all'));
|
|
83
88
|
const force = options.hasFlag('force');
|
|
84
89
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
await applyPack({
|
|
91
|
+
templateRoot,
|
|
92
|
+
projectRoot,
|
|
93
|
+
ideTargets,
|
|
94
|
+
force,
|
|
95
|
+
mode: 'install',
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
console.log('');
|
|
99
|
+
console.log('Done.');
|
|
100
|
+
console.log('Use --force to overwrite existing adapters.');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function runSync(options) {
|
|
104
|
+
const packageRoot = getPackageRoot();
|
|
105
|
+
const templateRoot = path.join(packageRoot, 'templates', 'shared');
|
|
106
|
+
await assertDirExists(templateRoot, `Shared template not found: ${templateRoot}`);
|
|
107
|
+
|
|
108
|
+
const projectRoot = path.resolve(options.get('cwd', process.cwd()));
|
|
109
|
+
await assertDirExists(projectRoot, `Project directory not found: ${projectRoot}`);
|
|
110
|
+
|
|
111
|
+
let ideTargets;
|
|
112
|
+
const ideOption = options.get('ide', null);
|
|
113
|
+
if (ideOption) {
|
|
114
|
+
ideTargets = resolveIdeTargets(ideOption);
|
|
88
115
|
} else {
|
|
89
|
-
await
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
116
|
+
ideTargets = await detectInstalledIdeTargets(projectRoot);
|
|
117
|
+
if (ideTargets.size) {
|
|
118
|
+
console.log(`Detected adapters: ${[...ideTargets].join(', ')}`);
|
|
119
|
+
} else {
|
|
120
|
+
ideTargets = new Set(SUPPORTED_IDES);
|
|
121
|
+
console.log('No existing adapters detected. Syncing all adapters.');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
await applyPack({
|
|
126
|
+
templateRoot,
|
|
127
|
+
projectRoot,
|
|
128
|
+
ideTargets,
|
|
129
|
+
force: true,
|
|
130
|
+
mode: 'sync',
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
console.log('');
|
|
134
|
+
console.log('Sync completed.');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function applyPack({
|
|
138
|
+
templateRoot,
|
|
139
|
+
projectRoot,
|
|
140
|
+
ideTargets,
|
|
141
|
+
force,
|
|
142
|
+
mode,
|
|
143
|
+
}) {
|
|
144
|
+
const verb = mode === 'sync' ? 'Synced' : 'Installed';
|
|
145
|
+
const sharedPackIdes = new Set(['trae', 'codex', 'cursor', 'windsurf', 'cline']);
|
|
146
|
+
const requiresSharedPack = [...ideTargets].some((ide) => sharedPackIdes.has(ide));
|
|
147
|
+
const sharedTarget = path.join(projectRoot, '.agent-flutter');
|
|
148
|
+
if (requiresSharedPack) {
|
|
149
|
+
if ((await exists(sharedTarget)) && !force) {
|
|
150
|
+
console.log(`Using existing shared pack: ${sharedTarget}`);
|
|
151
|
+
} else {
|
|
152
|
+
await copyTemplateDirectory({
|
|
153
|
+
sourceDir: templateRoot,
|
|
154
|
+
destinationDir: sharedTarget,
|
|
155
|
+
projectRoot,
|
|
156
|
+
force: true,
|
|
157
|
+
});
|
|
158
|
+
console.log(`${verb} shared pack: ${sharedTarget}`);
|
|
159
|
+
}
|
|
96
160
|
}
|
|
97
161
|
|
|
98
162
|
if (ideTargets.has('trae')) {
|
|
@@ -106,12 +170,15 @@ async function runInit(options) {
|
|
|
106
170
|
projectRoot,
|
|
107
171
|
force: true,
|
|
108
172
|
});
|
|
109
|
-
console.log(
|
|
173
|
+
console.log(`${verb} Trae adapter: ${traeTarget}`);
|
|
110
174
|
}
|
|
111
175
|
}
|
|
112
176
|
|
|
113
|
-
const
|
|
114
|
-
|
|
177
|
+
const metadataSource = requiresSharedPack
|
|
178
|
+
? sharedTarget
|
|
179
|
+
: templateRoot;
|
|
180
|
+
const skills = await loadSkillMetadata(path.join(metadataSource, 'skills'));
|
|
181
|
+
const rules = await loadRuleMetadata(path.join(metadataSource, 'rules'));
|
|
115
182
|
|
|
116
183
|
if (ideTargets.has('codex')) {
|
|
117
184
|
const agentsPath = path.join(projectRoot, 'AGENTS.md');
|
|
@@ -127,7 +194,7 @@ async function runInit(options) {
|
|
|
127
194
|
);
|
|
128
195
|
console.log(
|
|
129
196
|
written
|
|
130
|
-
?
|
|
197
|
+
? `${verb} Codex adapter: ${agentsPath}`
|
|
131
198
|
: `Skipped Codex adapter (exists): ${agentsPath}`,
|
|
132
199
|
);
|
|
133
200
|
}
|
|
@@ -141,7 +208,7 @@ async function runInit(options) {
|
|
|
141
208
|
);
|
|
142
209
|
console.log(
|
|
143
210
|
written
|
|
144
|
-
?
|
|
211
|
+
? `${verb} Cursor adapter: ${cursorPath}`
|
|
145
212
|
: `Skipped Cursor adapter (exists): ${cursorPath}`,
|
|
146
213
|
);
|
|
147
214
|
}
|
|
@@ -155,7 +222,7 @@ async function runInit(options) {
|
|
|
155
222
|
);
|
|
156
223
|
console.log(
|
|
157
224
|
written
|
|
158
|
-
?
|
|
225
|
+
? `${verb} Windsurf adapter: ${windsurfPath}`
|
|
159
226
|
: `Skipped Windsurf adapter (exists): ${windsurfPath}`,
|
|
160
227
|
);
|
|
161
228
|
}
|
|
@@ -169,14 +236,72 @@ async function runInit(options) {
|
|
|
169
236
|
);
|
|
170
237
|
console.log(
|
|
171
238
|
written
|
|
172
|
-
?
|
|
239
|
+
? `${verb} Cline adapter: ${clinePath}`
|
|
173
240
|
: `Skipped Cline adapter (exists): ${clinePath}`,
|
|
174
241
|
);
|
|
175
242
|
}
|
|
176
243
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
244
|
+
if (ideTargets.has('github')) {
|
|
245
|
+
const githubSkillsPath = path.join(projectRoot, '.github', 'skills');
|
|
246
|
+
if ((await exists(githubSkillsPath)) && !force) {
|
|
247
|
+
console.log(`Skipped GitHub skills (exists): ${githubSkillsPath}`);
|
|
248
|
+
} else {
|
|
249
|
+
await copyTemplateDirectory({
|
|
250
|
+
sourceDir: path.join(templateRoot, 'skills'),
|
|
251
|
+
destinationDir: githubSkillsPath,
|
|
252
|
+
projectRoot,
|
|
253
|
+
force: true,
|
|
254
|
+
});
|
|
255
|
+
console.log(`${verb} GitHub skills: ${githubSkillsPath}`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const githubRulesPath = path.join(projectRoot, '.github', 'rules');
|
|
259
|
+
if ((await exists(githubRulesPath)) && !force) {
|
|
260
|
+
console.log(`Skipped GitHub rules (exists): ${githubRulesPath}`);
|
|
261
|
+
} else {
|
|
262
|
+
await copyTemplateDirectory({
|
|
263
|
+
sourceDir: path.join(templateRoot, 'rules'),
|
|
264
|
+
destinationDir: githubRulesPath,
|
|
265
|
+
projectRoot,
|
|
266
|
+
force: true,
|
|
267
|
+
});
|
|
268
|
+
console.log(`${verb} GitHub rules: ${githubRulesPath}`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const githubPath = path.join(projectRoot, '.github', 'copilot-instructions.md');
|
|
272
|
+
const written = await writeTextFile(
|
|
273
|
+
githubPath,
|
|
274
|
+
buildGithubCopilotInstructions(),
|
|
275
|
+
{ force },
|
|
276
|
+
);
|
|
277
|
+
console.log(
|
|
278
|
+
written
|
|
279
|
+
? `${verb} GitHub adapter: ${githubPath}`
|
|
280
|
+
: `Skipped GitHub adapter (exists): ${githubPath}`,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async function detectInstalledIdeTargets(projectRoot) {
|
|
286
|
+
const detected = new Set();
|
|
287
|
+
if (await exists(path.join(projectRoot, '.trae'))) detected.add('trae');
|
|
288
|
+
if (await exists(path.join(projectRoot, 'AGENTS.md'))) detected.add('codex');
|
|
289
|
+
if (await exists(path.join(projectRoot, '.cursor', 'rules', 'agent-flutter.mdc'))) {
|
|
290
|
+
detected.add('cursor');
|
|
291
|
+
}
|
|
292
|
+
if (await exists(path.join(projectRoot, '.windsurf', 'rules', 'agent-flutter.md'))) {
|
|
293
|
+
detected.add('windsurf');
|
|
294
|
+
}
|
|
295
|
+
if (await exists(path.join(projectRoot, '.clinerules', 'agent-flutter.md'))) {
|
|
296
|
+
detected.add('cline');
|
|
297
|
+
}
|
|
298
|
+
if (
|
|
299
|
+
(await exists(path.join(projectRoot, '.github', 'copilot-instructions.md')))
|
|
300
|
+
|| (await exists(path.join(projectRoot, '.github', 'skills')))
|
|
301
|
+
) {
|
|
302
|
+
detected.add('github');
|
|
303
|
+
}
|
|
304
|
+
return detected;
|
|
180
305
|
}
|
|
181
306
|
|
|
182
307
|
async function runList(options) {
|
|
@@ -467,6 +592,19 @@ Execution checklist:
|
|
|
467
592
|
`;
|
|
468
593
|
}
|
|
469
594
|
|
|
595
|
+
function buildGithubCopilotInstructions() {
|
|
596
|
+
return `# Agent Flutter Copilot Instructions
|
|
597
|
+
|
|
598
|
+
This repository uses local instruction packs in \`.github/skills\` and \`.github/rules\`.
|
|
599
|
+
|
|
600
|
+
Follow this order when generating code:
|
|
601
|
+
1. Read applicable files in \`.github/rules/\`.
|
|
602
|
+
2. If task matches a skill, read \`.github/skills/<skill>/SKILL.md\`.
|
|
603
|
+
3. Keep architecture, localization, and UI conventions aligned with the shared pack.
|
|
604
|
+
4. Update specs/docs when UI/API behavior changes.
|
|
605
|
+
`;
|
|
606
|
+
}
|
|
607
|
+
|
|
470
608
|
async function exists(filePath) {
|
|
471
609
|
try {
|
|
472
610
|
await fs.access(filePath);
|