cadet-agent 0.4.0 → 0.6.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.
- package/README.md +22 -2
- package/package.json +1 -1
- package/src/cli.mjs +13 -2
- package/src/install.mjs +141 -9
package/README.md
CHANGED
|
@@ -11,6 +11,27 @@ Cadet-Agent is a cross-IDE agent framework for game-development workflows, with
|
|
|
11
11
|
- `.continue/` contains Continue-specific authored files.
|
|
12
12
|
- `.claude/` contains Claude Code-specific authored files.
|
|
13
13
|
- `package-agent.ps1` builds the distributable `cadet-agent.zip` package.
|
|
14
|
+
- `publish-npm.ps1` publishes the CLI to npm using a token from `~/.npm_token`.
|
|
15
|
+
|
|
16
|
+
## Quick Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx cadet-agent@latest init
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This downloads the latest framework release and extracts it into your current directory. For a specific target directory:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx cadet-agent@latest init --target ./my-unity-project
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Manual Install
|
|
29
|
+
|
|
30
|
+
Download `cadet-agent.zip` from [GitHub Releases](https://github.com/naishtech/cadet-agent/releases) and extract it into your Unity project root:
|
|
31
|
+
|
|
32
|
+
```powershell
|
|
33
|
+
Expand-Archive .\cadet-agent.zip -DestinationPath . -Force
|
|
34
|
+
```
|
|
14
35
|
|
|
15
36
|
## Getting Started
|
|
16
37
|
- For repository setup and package contents, see [.cadet/agent/README.md](.cadet/agent/README.md).
|
|
@@ -23,7 +44,7 @@ Cadet-Agent is a cross-IDE agent framework for game-development workflows, with
|
|
|
23
44
|
## Examples
|
|
24
45
|
|
|
25
46
|
### GitHub Copilot kickoff
|
|
26
|
-
|
|
47
|
+
Run `npx cadet-agent@latest init` in your Unity project root, then open the repo in VS Code and start a kickoff chat using the Cadet prompt.
|
|
27
48
|
|
|
28
49
|
```text
|
|
29
50
|
/cadet Help me bootstrap a beginner-friendly 2D racing prototype in Unity with AI opponents and a career progression loop.
|
|
@@ -59,7 +80,6 @@ If a specific game repository needs local conventions, add a policy file under `
|
|
|
59
80
|
Running `./package-agent.ps1` produces `cadet-agent.zip` with this layout:
|
|
60
81
|
- `.cadet/agent/core/`
|
|
61
82
|
- `AGENTS.md`
|
|
62
|
-
- `.github/cadet-copilot-instructions.md`
|
|
63
83
|
- `.github/prompts/cadet.prompt.md`
|
|
64
84
|
- `.cursor/rules/cadet-agent.md`
|
|
65
85
|
- `.continue/rules/cadet-agent.md`
|
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
|
-
import { install } from './install.mjs';
|
|
4
|
+
import { install, sync } from './install.mjs';
|
|
5
5
|
|
|
6
6
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
7
|
const __dirname = dirname(__filename);
|
|
@@ -25,9 +25,12 @@ function showHelp() {
|
|
|
25
25
|
Usage:
|
|
26
26
|
npx cadet-agent@latest init Install Cadet-Agent into the current directory
|
|
27
27
|
npx cadet-agent@latest init --target <dir> Install into a specific directory
|
|
28
|
+
npx cadet-agent@latest sync Update framework, preserving local policies/plans
|
|
29
|
+
npx cadet-agent@latest sync --target <dir> Sync a specific directory
|
|
28
30
|
|
|
29
31
|
Options:
|
|
30
32
|
--target, -t Target directory (default: current working directory)
|
|
33
|
+
--source Release API URL override (for forked deployments)
|
|
31
34
|
--help, -h Show this help
|
|
32
35
|
--version, -v Show version number
|
|
33
36
|
`);
|
|
@@ -38,17 +41,25 @@ export async function run(argv) {
|
|
|
38
41
|
|
|
39
42
|
// Parse --target <dir> or -t <dir>
|
|
40
43
|
let targetDir = process.cwd();
|
|
44
|
+
let sourceUrl = null;
|
|
41
45
|
const targetIdx = argv.indexOf('--target');
|
|
42
46
|
const tIdx = argv.indexOf('-t');
|
|
47
|
+
const sourceIdx = argv.indexOf('--source');
|
|
43
48
|
if (targetIdx !== -1 && argv[targetIdx + 1]) {
|
|
44
49
|
targetDir = argv[targetIdx + 1];
|
|
45
50
|
} else if (tIdx !== -1 && argv[tIdx + 1]) {
|
|
46
51
|
targetDir = argv[tIdx + 1];
|
|
47
52
|
}
|
|
53
|
+
if (sourceIdx !== -1 && argv[sourceIdx + 1]) {
|
|
54
|
+
sourceUrl = argv[sourceIdx + 1];
|
|
55
|
+
}
|
|
48
56
|
|
|
49
57
|
switch (command) {
|
|
50
58
|
case 'init':
|
|
51
|
-
await install(targetDir);
|
|
59
|
+
await install(targetDir, { sourceUrl });
|
|
60
|
+
break;
|
|
61
|
+
case 'sync':
|
|
62
|
+
await sync(targetDir, { sourceUrl });
|
|
52
63
|
break;
|
|
53
64
|
case '--version':
|
|
54
65
|
case '-v':
|
package/src/install.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createWriteStream, mkdirSync, existsSync } from 'node:fs';
|
|
1
|
+
import { createWriteStream, mkdirSync, existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join, dirname } from 'node:path';
|
|
3
3
|
import { pipeline } from 'node:stream/promises';
|
|
4
4
|
import { createGunzip } from 'node:zlib';
|
|
@@ -8,9 +8,13 @@ import { Readable } from 'node:stream';
|
|
|
8
8
|
|
|
9
9
|
// ── Constants ────────────────────────────────────────────────────────────────
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const DEFAULT_API = 'https://api.github.com/repos/naishtech/cadet-agent/releases/latest';
|
|
12
12
|
const USER_AGENT = 'cadet-agent-cli';
|
|
13
13
|
|
|
14
|
+
function resolveApiUrl(override) {
|
|
15
|
+
return override || process.env.CADET_AGENT_RELEASE_URL || DEFAULT_API;
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
// ── ZIP parser (zero-dependency, handles store + deflate) ───────────────────
|
|
15
19
|
|
|
16
20
|
const SIG_EOCD = 0x06054b50;
|
|
@@ -70,7 +74,12 @@ function extractFile(buf, entry, targetDir) {
|
|
|
70
74
|
// Deflate
|
|
71
75
|
data = inflateRawSync(compressed);
|
|
72
76
|
} else {
|
|
73
|
-
throw new Error(
|
|
77
|
+
throw new Error(
|
|
78
|
+
`Unsupported compression method ${method} for ${filename}.\n` +
|
|
79
|
+
`This zip uses a compression format this CLI doesn't support.\n` +
|
|
80
|
+
`Try downloading cadet-agent.zip manually from:\n` +
|
|
81
|
+
` https://github.com/naishtech/cadet-agent/releases/latest`
|
|
82
|
+
);
|
|
74
83
|
}
|
|
75
84
|
|
|
76
85
|
const outPath = join(targetDir, filename);
|
|
@@ -86,6 +95,15 @@ function extractZip(buf, targetDir) {
|
|
|
86
95
|
const cdSize = read32(buf, eocdOff + 12);
|
|
87
96
|
const cdOff = read32(buf, eocdOff + 16);
|
|
88
97
|
|
|
98
|
+
// ZIP64 detection — 0xFFFFFFFF in 32-bit fields means real values are in ZIP64 extra records
|
|
99
|
+
if (cdOff === 0xFFFFFFFF || cdSize === 0xFFFFFFFF) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
'This zip uses ZIP64 format, which is not supported.\n' +
|
|
102
|
+
'Try downloading cadet-agent.zip manually from:\n' +
|
|
103
|
+
' https://github.com/naishtech/cadet-agent/releases/latest'
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
89
107
|
const paths = [];
|
|
90
108
|
for (const entry of centralDirectoryEntries(buf, cdOff, cdSize)) {
|
|
91
109
|
const outPath = extractFile(buf, entry, targetDir);
|
|
@@ -96,10 +114,11 @@ function extractZip(buf, targetDir) {
|
|
|
96
114
|
|
|
97
115
|
// ── GitHub release download ─────────────────────────────────────────────────
|
|
98
116
|
|
|
99
|
-
async function fetchLatestRelease() {
|
|
117
|
+
async function fetchLatestRelease(apiUrl) {
|
|
118
|
+
const url = resolveApiUrl(apiUrl);
|
|
100
119
|
console.log('🔍 Fetching latest Cadet-Agent release...');
|
|
101
120
|
|
|
102
|
-
const res = await fetch(
|
|
121
|
+
const res = await fetch(url, {
|
|
103
122
|
headers: {
|
|
104
123
|
'User-Agent': USER_AGENT,
|
|
105
124
|
'Accept': 'application/vnd.github+json',
|
|
@@ -169,11 +188,11 @@ async function downloadZip(url) {
|
|
|
169
188
|
|
|
170
189
|
// ── Public install entry ────────────────────────────────────────────────────
|
|
171
190
|
|
|
172
|
-
export async function install(targetDir) {
|
|
191
|
+
export async function install(targetDir, opts = {}) {
|
|
173
192
|
console.log(`📦 Cadet-Agent — installing to ${targetDir}\n`);
|
|
174
193
|
|
|
175
194
|
// 1. Fetch release metadata
|
|
176
|
-
const release = await fetchLatestRelease();
|
|
195
|
+
const release = await fetchLatestRelease(opts.sourceUrl);
|
|
177
196
|
console.log(` Latest: ${release.tag_name} (published ${release.published_at})\n`);
|
|
178
197
|
|
|
179
198
|
// 2. Find zip asset
|
|
@@ -193,8 +212,7 @@ export async function install(targetDir) {
|
|
|
193
212
|
// Print per-IDE next steps
|
|
194
213
|
console.log('── Next steps ──');
|
|
195
214
|
console.log(' GitHub Copilot:');
|
|
196
|
-
console.log('
|
|
197
|
-
console.log(' Then start a chat: /cadet');
|
|
215
|
+
console.log(' Start a chat: /cadet');
|
|
198
216
|
console.log(' Cursor:');
|
|
199
217
|
console.log(' Already active — .cursor\\rules\\cadet-agent.md loads automatically');
|
|
200
218
|
console.log(' Continue:');
|
|
@@ -203,3 +221,117 @@ export async function install(targetDir) {
|
|
|
203
221
|
console.log(' Already active — .claude\\skills\\cadet-agent.md loads as a project skill');
|
|
204
222
|
console.log('');
|
|
205
223
|
}
|
|
224
|
+
|
|
225
|
+
// ── Manifest-aware extraction ───────────────────────────────────────────────
|
|
226
|
+
|
|
227
|
+
function matchesPreservedPath(filename, preservedPaths) {
|
|
228
|
+
// Normalize: strip leading dot (zip paths like ".cadet/agent/policies/...")
|
|
229
|
+
const normalized = filename.replace(/^\.?\/?/, '');
|
|
230
|
+
for (const preserved of preservedPaths) {
|
|
231
|
+
const p = preserved.replace(/^\.?\/?/, '');
|
|
232
|
+
if (normalized === p || normalized.startsWith(p + '/') || normalized.startsWith(p + '\\')) {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function extractZipWithManifest(buf, targetDir, { preserved, managed }) {
|
|
240
|
+
const eocdOff = findEocd(buf);
|
|
241
|
+
const cdSize = read32(buf, eocdOff + 12);
|
|
242
|
+
const cdOff = read32(buf, eocdOff + 16);
|
|
243
|
+
|
|
244
|
+
if (cdOff === 0xFFFFFFFF || cdSize === 0xFFFFFFFF) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
'This zip uses ZIP64 format, which is not supported.\n' +
|
|
247
|
+
'Try downloading cadet-agent.zip manually from:\n' +
|
|
248
|
+
' https://github.com/naishtech/cadet-agent/releases/latest'
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const updated = [];
|
|
253
|
+
const preserved_list = [];
|
|
254
|
+
const added = [];
|
|
255
|
+
|
|
256
|
+
for (const entry of centralDirectoryEntries(buf, cdOff, cdSize)) {
|
|
257
|
+
if (matchesPreservedPath(entry.filename, preserved)) {
|
|
258
|
+
preserved_list.push(entry.filename);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
const outPath = extractFile(buf, entry, targetDir);
|
|
262
|
+
// Check if this file already existed (updated vs added)
|
|
263
|
+
// We can't easily tell without checking before extraction, so approximate:
|
|
264
|
+
// If it's under a managed path, call it updated; otherwise added
|
|
265
|
+
const isManaged = managed.some(m => {
|
|
266
|
+
const mn = m.replace(/^\.?\/?/, '');
|
|
267
|
+
const fn = entry.filename.replace(/^\.?\/?/, '');
|
|
268
|
+
return fn === mn || fn.startsWith(mn + '/') || fn.startsWith(mn + '\\');
|
|
269
|
+
});
|
|
270
|
+
if (isManaged) {
|
|
271
|
+
updated.push(outPath);
|
|
272
|
+
} else {
|
|
273
|
+
added.push(outPath);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return { updated, preserved: preserved_list, added };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ── Public sync entry ───────────────────────────────────────────────────────
|
|
281
|
+
|
|
282
|
+
export async function sync(targetDir, opts = {}) {
|
|
283
|
+
console.log(`🔄 Cadet-Agent — syncing ${targetDir}\n`);
|
|
284
|
+
|
|
285
|
+
// 1. Read existing manifest
|
|
286
|
+
const manifestPath = join(targetDir, '.cadet', 'agent', 'core', 'FrameworkManifest.json');
|
|
287
|
+
let existingManifest = null;
|
|
288
|
+
let oldVersion = 'none';
|
|
289
|
+
try {
|
|
290
|
+
existingManifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
291
|
+
oldVersion = existingManifest.frameworkVersion || 'unknown';
|
|
292
|
+
console.log(` Existing install: v${oldVersion}`);
|
|
293
|
+
} catch {
|
|
294
|
+
console.log(' No existing install found — performing full install.\n');
|
|
295
|
+
return install(targetDir, opts);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// 2. Fetch release metadata
|
|
299
|
+
const release = await fetchLatestRelease(opts.sourceUrl);
|
|
300
|
+
const newVersion = release.tag_name;
|
|
301
|
+
console.log(` Latest: ${newVersion} (published ${release.published_at})\n`);
|
|
302
|
+
|
|
303
|
+
if (oldVersion === newVersion) {
|
|
304
|
+
console.log(`✅ Already up to date (v${oldVersion}). Nothing to sync.\n`);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// 3. Download
|
|
309
|
+
const asset = findZipAsset(release);
|
|
310
|
+
const zipBuf = await downloadZip(asset.browser_download_url);
|
|
311
|
+
console.log(` Downloaded ${(zipBuf.length / 1024).toFixed(0)} KB\n`);
|
|
312
|
+
|
|
313
|
+
// 4. Extract with manifest awareness
|
|
314
|
+
console.log('📂 Extracting (preserving local policies and plans)...');
|
|
315
|
+
const result = extractZipWithManifest(zipBuf, targetDir, {
|
|
316
|
+
preserved: existingManifest.preservedPaths || [],
|
|
317
|
+
managed: existingManifest.managedPaths || [],
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// 5. Report
|
|
321
|
+
console.log('');
|
|
322
|
+
console.log(`✅ Cadet-Agent synced: v${oldVersion} → ${newVersion}`);
|
|
323
|
+
console.log(` Updated: ${result.updated.length} files`);
|
|
324
|
+
if (result.preserved.length > 0) {
|
|
325
|
+
console.log(` Preserved: ${result.preserved.length} files (local policies/plans)`);
|
|
326
|
+
}
|
|
327
|
+
if (result.added.length > 0) {
|
|
328
|
+
console.log(` New: ${result.added.length} files`);
|
|
329
|
+
}
|
|
330
|
+
console.log('');
|
|
331
|
+
|
|
332
|
+
// Print per-IDE next steps
|
|
333
|
+
console.log('── Next steps ──');
|
|
334
|
+
console.log(' Framework files updated. Start a fresh chat for changes to take effect:');
|
|
335
|
+
console.log(' /cadet');
|
|
336
|
+
console.log('');
|
|
337
|
+
}
|