agent-flutter 0.1.1 → 0.1.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.
Files changed (3) hide show
  1. package/README.md +4 -0
  2. package/package.json +1 -1
  3. package/src/cli.js +115 -11
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
 
@@ -55,3 +58,4 @@ npm run release:major
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/copilot-instructions.md`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-flutter",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Portable Flutter skill/rule pack initializer for multiple AI IDEs.",
5
5
  "type": "module",
6
6
  "bin": {
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,6 +87,61 @@ async function runInit(options) {
82
87
  const ideTargets = resolveIdeTargets(options.get('ide', 'all'));
83
88
  const force = options.hasFlag('force');
84
89
 
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);
115
+ } else {
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';
85
145
  const sharedTarget = path.join(projectRoot, '.agent-flutter');
86
146
  if ((await exists(sharedTarget)) && !force) {
87
147
  console.log(`Using existing shared pack: ${sharedTarget}`);
@@ -92,7 +152,7 @@ async function runInit(options) {
92
152
  projectRoot,
93
153
  force: true,
94
154
  });
95
- console.log(`Installed shared pack: ${sharedTarget}`);
155
+ console.log(`${verb} shared pack: ${sharedTarget}`);
96
156
  }
97
157
 
98
158
  if (ideTargets.has('trae')) {
@@ -106,7 +166,7 @@ async function runInit(options) {
106
166
  projectRoot,
107
167
  force: true,
108
168
  });
109
- console.log(`Installed Trae adapter: ${traeTarget}`);
169
+ console.log(`${verb} Trae adapter: ${traeTarget}`);
110
170
  }
111
171
  }
112
172
 
@@ -127,7 +187,7 @@ async function runInit(options) {
127
187
  );
128
188
  console.log(
129
189
  written
130
- ? `Installed Codex adapter: ${agentsPath}`
190
+ ? `${verb} Codex adapter: ${agentsPath}`
131
191
  : `Skipped Codex adapter (exists): ${agentsPath}`,
132
192
  );
133
193
  }
@@ -141,7 +201,7 @@ async function runInit(options) {
141
201
  );
142
202
  console.log(
143
203
  written
144
- ? `Installed Cursor adapter: ${cursorPath}`
204
+ ? `${verb} Cursor adapter: ${cursorPath}`
145
205
  : `Skipped Cursor adapter (exists): ${cursorPath}`,
146
206
  );
147
207
  }
@@ -155,7 +215,7 @@ async function runInit(options) {
155
215
  );
156
216
  console.log(
157
217
  written
158
- ? `Installed Windsurf adapter: ${windsurfPath}`
218
+ ? `${verb} Windsurf adapter: ${windsurfPath}`
159
219
  : `Skipped Windsurf adapter (exists): ${windsurfPath}`,
160
220
  );
161
221
  }
@@ -169,14 +229,45 @@ async function runInit(options) {
169
229
  );
170
230
  console.log(
171
231
  written
172
- ? `Installed Cline adapter: ${clinePath}`
232
+ ? `${verb} Cline adapter: ${clinePath}`
173
233
  : `Skipped Cline adapter (exists): ${clinePath}`,
174
234
  );
175
235
  }
176
236
 
177
- console.log('');
178
- console.log('Done.');
179
- console.log('Use --force to overwrite existing adapters.');
237
+ if (ideTargets.has('github')) {
238
+ const githubPath = path.join(projectRoot, '.github', 'copilot-instructions.md');
239
+ const written = await writeTextFile(
240
+ githubPath,
241
+ buildGithubCopilotInstructions(),
242
+ { force },
243
+ );
244
+ console.log(
245
+ written
246
+ ? `${verb} GitHub adapter: ${githubPath}`
247
+ : `Skipped GitHub adapter (exists): ${githubPath}`,
248
+ );
249
+ }
250
+ }
251
+
252
+ async function detectInstalledIdeTargets(projectRoot) {
253
+ const checks = [
254
+ ['trae', path.join(projectRoot, '.trae')],
255
+ ['codex', path.join(projectRoot, 'AGENTS.md')],
256
+ ['cursor', path.join(projectRoot, '.cursor', 'rules', 'agent-flutter.mdc')],
257
+ ['windsurf', path.join(projectRoot, '.windsurf', 'rules', 'agent-flutter.md')],
258
+ ['cline', path.join(projectRoot, '.clinerules', 'agent-flutter.md')],
259
+ ['github', path.join(projectRoot, '.github', 'copilot-instructions.md')],
260
+ ];
261
+
262
+ const results = await Promise.all(
263
+ checks.map(async ([ide, targetPath]) => [ide, await exists(targetPath)]),
264
+ );
265
+
266
+ const detected = new Set();
267
+ for (const [ide, installed] of results) {
268
+ if (installed) detected.add(ide);
269
+ }
270
+ return detected;
180
271
  }
181
272
 
182
273
  async function runList(options) {
@@ -467,6 +558,19 @@ Execution checklist:
467
558
  `;
468
559
  }
469
560
 
561
+ function buildGithubCopilotInstructions() {
562
+ return `# Agent Flutter Copilot Instructions
563
+
564
+ This repository uses a shared local instruction pack in \`.agent-flutter\`.
565
+
566
+ Follow this order when generating code:
567
+ 1. Read applicable files in \`.agent-flutter/rules/\`.
568
+ 2. If task matches a skill, read \`.agent-flutter/skills/<skill>/SKILL.md\`.
569
+ 3. Keep architecture, localization, and UI conventions aligned with the shared pack.
570
+ 4. Update specs/docs when UI/API behavior changes.
571
+ `;
572
+ }
573
+
470
574
  async function exists(filePath) {
471
575
  try {
472
576
  await fs.access(filePath);