cadet-agent 0.4.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 +71 -0
- package/bin/cli.mjs +8 -0
- package/package.json +32 -0
- package/src/cli.mjs +67 -0
- package/src/install.mjs +205 -0
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Cadet-Agent
|
|
2
|
+
|
|
3
|
+
Cadet-Agent is a cross-IDE agent framework for game-development workflows, with a shared framework core and authored root integrations for GitHub Copilot, Cursor, Continue, and Claude Code.
|
|
4
|
+
|
|
5
|
+
## Repository Layout
|
|
6
|
+
- `.cadet/agent/core/` contains the shared Cadet-Agent framework documents.
|
|
7
|
+
- `.cadet/agent/docs/` contains setup guides for each supported IDE.
|
|
8
|
+
- `AGENTS.md` contains shared top-level agent instructions.
|
|
9
|
+
- `.github/` contains GitHub Copilot-specific authored files.
|
|
10
|
+
- `.cursor/` contains Cursor-specific authored files.
|
|
11
|
+
- `.continue/` contains Continue-specific authored files.
|
|
12
|
+
- `.claude/` contains Claude Code-specific authored files.
|
|
13
|
+
- `package-agent.ps1` builds the distributable `cadet-agent.zip` package.
|
|
14
|
+
|
|
15
|
+
## Getting Started
|
|
16
|
+
- For repository setup and package contents, see [.cadet/agent/README.md](.cadet/agent/README.md).
|
|
17
|
+
- For framework navigation, see [.cadet/agent/core/README.md](.cadet/agent/core/README.md).
|
|
18
|
+
- For GitHub Copilot setup, see [.cadet/agent/docs/github-copilot.md](.cadet/agent/docs/github-copilot.md).
|
|
19
|
+
- For Cursor setup, see [.cadet/agent/docs/cursor.md](.cadet/agent/docs/cursor.md).
|
|
20
|
+
- For Continue setup, see [.cadet/agent/docs/continue.md](.cadet/agent/docs/continue.md).
|
|
21
|
+
- For Claude Code setup, see [.cadet/agent/docs/claude-code.md](.cadet/agent/docs/claude-code.md).
|
|
22
|
+
|
|
23
|
+
## Examples
|
|
24
|
+
|
|
25
|
+
### GitHub Copilot kickoff
|
|
26
|
+
After extracting `cadet-agent.zip` into a game repository, open the repo in VS Code and start a kickoff chat using the Cadet prompt.
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
/cadet Help me bootstrap a beginner-friendly 2D racing prototype in Unity with AI opponents and a career progression loop.
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Cadet-Agent will use `.github/prompts/cadet.prompt.md` plus the shared framework in `.cadet/agent/core` to route the conversation through learner calibration, bootstrap checks, and planning.
|
|
33
|
+
|
|
34
|
+
### Cursor feature request
|
|
35
|
+
After opening the repository in Cursor, the always-apply rule in `.cursor/rules/cadet-agent.md` should load automatically. A typical request looks like this:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
Design a small vertical slice for a kart handling prototype in Unity. Start with requirements, then a technical design, then the first TDD task.
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Cursor will use the Cadet rule to pull workflow, standards, and guidance from `.cadet/agent/core` before responding.
|
|
42
|
+
|
|
43
|
+
### Continue planning request
|
|
44
|
+
With Continue installed in VS Code, open the repository and ask for a scoped planning artifact:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
Create a requirements outline for a single-player time-trial mode with ghost replay support and Given/When/Then acceptance criteria.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The Continue rule in `.continue/rules/cadet-agent.md` should steer the response back through the shared Cadet framework.
|
|
51
|
+
|
|
52
|
+
### Repository policy example
|
|
53
|
+
If a specific game repository needs local conventions, add a policy file under `.cadet/agent/policies` using `.cadet/agent/core/Templates/PolicyTemplate.md`. For example, a repository policy could define:
|
|
54
|
+
- where project plans should live
|
|
55
|
+
- where shared gameplay code should be extracted
|
|
56
|
+
- which Unity packages or UI stack are the project default
|
|
57
|
+
|
|
58
|
+
## Package Output
|
|
59
|
+
Running `./package-agent.ps1` produces `cadet-agent.zip` with this layout:
|
|
60
|
+
- `.cadet/agent/core/`
|
|
61
|
+
- `AGENTS.md`
|
|
62
|
+
- `.github/cadet-copilot-instructions.md`
|
|
63
|
+
- `.github/prompts/cadet.prompt.md`
|
|
64
|
+
- `.cursor/rules/cadet-agent.md`
|
|
65
|
+
- `.continue/rules/cadet-agent.md`
|
|
66
|
+
- `.claude/skills/cadet-agent.md`
|
|
67
|
+
|
|
68
|
+
## Notes
|
|
69
|
+
- `.cadet/agent/core/FrameworkManifest.json` defines the managed and preserved paths for packaged installs.
|
|
70
|
+
- Repository-specific policy overlays belong in `.cadet/agent/policies`.
|
|
71
|
+
- Planning artifacts belong in `.cadet/agent/project-plans` unless an active policy says otherwise.
|
package/bin/cli.mjs
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cadet-agent",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Cross-IDE agent framework for Unity/C# game-development — one-command install",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"cadet-agent": "bin/cli.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"src/"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"cadet",
|
|
15
|
+
"cadet-agent",
|
|
16
|
+
"unity",
|
|
17
|
+
"game-development",
|
|
18
|
+
"ai-agent",
|
|
19
|
+
"copilot",
|
|
20
|
+
"cursor",
|
|
21
|
+
"claude-code"
|
|
22
|
+
],
|
|
23
|
+
"license": "CC-BY-4.0",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/naishtech/cadet-agent.git"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/naishtech/cadet-agent#readme",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18.0.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/cli.mjs
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { install } from './install.mjs';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
|
|
9
|
+
function getVersion() {
|
|
10
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
11
|
+
return pkg.version;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function showHelp() {
|
|
15
|
+
console.log(`
|
|
16
|
+
██████╗ █████╗ ██████╗ ███████╗████████╗
|
|
17
|
+
██╔════╝██╔══██╗██╔══██╗██╔════╝╚══██╔══╝
|
|
18
|
+
██║ ███████║██║ ██║█████╗ ██║
|
|
19
|
+
██║ ██╔══██║██║ ██║██╔══╝ ██║
|
|
20
|
+
╚██████╗██║ ██║██████╔╝███████╗ ██║
|
|
21
|
+
╚═════╝╚═╝ ╚═╝╚═════╝ ╚══════╝ ╚═╝
|
|
22
|
+
|
|
23
|
+
Cross-IDE agent framework for Unity/C# game-development
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
npx cadet-agent@latest init Install Cadet-Agent into the current directory
|
|
27
|
+
npx cadet-agent@latest init --target <dir> Install into a specific directory
|
|
28
|
+
|
|
29
|
+
Options:
|
|
30
|
+
--target, -t Target directory (default: current working directory)
|
|
31
|
+
--help, -h Show this help
|
|
32
|
+
--version, -v Show version number
|
|
33
|
+
`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function run(argv) {
|
|
37
|
+
const command = argv[2];
|
|
38
|
+
|
|
39
|
+
// Parse --target <dir> or -t <dir>
|
|
40
|
+
let targetDir = process.cwd();
|
|
41
|
+
const targetIdx = argv.indexOf('--target');
|
|
42
|
+
const tIdx = argv.indexOf('-t');
|
|
43
|
+
if (targetIdx !== -1 && argv[targetIdx + 1]) {
|
|
44
|
+
targetDir = argv[targetIdx + 1];
|
|
45
|
+
} else if (tIdx !== -1 && argv[tIdx + 1]) {
|
|
46
|
+
targetDir = argv[tIdx + 1];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
switch (command) {
|
|
50
|
+
case 'init':
|
|
51
|
+
await install(targetDir);
|
|
52
|
+
break;
|
|
53
|
+
case '--version':
|
|
54
|
+
case '-v':
|
|
55
|
+
console.log(`cadet-agent v${getVersion()}`);
|
|
56
|
+
break;
|
|
57
|
+
case '--help':
|
|
58
|
+
case '-h':
|
|
59
|
+
case undefined:
|
|
60
|
+
showHelp();
|
|
61
|
+
break;
|
|
62
|
+
default:
|
|
63
|
+
console.error(`Unknown command: ${command}`);
|
|
64
|
+
console.error('Run cadet-agent --help for usage.');
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
}
|
package/src/install.mjs
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { createWriteStream, mkdirSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { pipeline } from 'node:stream/promises';
|
|
4
|
+
import { createGunzip } from 'node:zlib';
|
|
5
|
+
import { inflateRawSync } from 'node:zlib';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import { Readable } from 'node:stream';
|
|
8
|
+
|
|
9
|
+
// ── Constants ────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
const GITHUB_API = 'https://api.github.com/repos/naishtech/cadet-agent/releases/latest';
|
|
12
|
+
const USER_AGENT = 'cadet-agent-cli';
|
|
13
|
+
|
|
14
|
+
// ── ZIP parser (zero-dependency, handles store + deflate) ───────────────────
|
|
15
|
+
|
|
16
|
+
const SIG_EOCD = 0x06054b50;
|
|
17
|
+
const SIG_CD = 0x02014b50;
|
|
18
|
+
const SIG_LFH = 0x04034b50;
|
|
19
|
+
|
|
20
|
+
function read32(buf, off) { return buf.readUInt32LE(off); }
|
|
21
|
+
function read16(buf, off) { return buf.readUInt16LE(off); }
|
|
22
|
+
|
|
23
|
+
function findEocd(buf) {
|
|
24
|
+
// Search backwards from end for EOCD signature (comment is max 65535 bytes)
|
|
25
|
+
const maxStart = Math.max(0, buf.length - 65535 - 22);
|
|
26
|
+
for (let i = buf.length - 22; i >= maxStart; i--) {
|
|
27
|
+
if (read32(buf, i) === SIG_EOCD) return i;
|
|
28
|
+
}
|
|
29
|
+
throw new Error('Not a valid ZIP file: EOCD signature not found');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function* centralDirectoryEntries(buf, cdOffset, cdSize) {
|
|
33
|
+
let off = cdOffset;
|
|
34
|
+
const end = cdOffset + cdSize;
|
|
35
|
+
while (off < end) {
|
|
36
|
+
if (read32(buf, off) !== SIG_CD) break;
|
|
37
|
+
const method = read16(buf, off + 10);
|
|
38
|
+
const compressedSize = read32(buf, off + 20);
|
|
39
|
+
const uncompressedSize = read32(buf, off + 24);
|
|
40
|
+
const filenameLen = read16(buf, off + 28);
|
|
41
|
+
const extraLen = read16(buf, off + 30);
|
|
42
|
+
const commentLen = read16(buf, off + 32);
|
|
43
|
+
const localHeaderOff = read32(buf, off + 42);
|
|
44
|
+
const filename = buf.toString('utf-8', off + 46, off + 46 + filenameLen);
|
|
45
|
+
|
|
46
|
+
// Skip directory entries (trailing / in filename, or uncompressedSize == 0 with no method)
|
|
47
|
+
if (!filename.endsWith('/')) {
|
|
48
|
+
yield { filename, method, compressedSize, uncompressedSize, localHeaderOff };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
off += 46 + filenameLen + extraLen + commentLen;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function extractFile(buf, entry, targetDir) {
|
|
56
|
+
const { filename, method, compressedSize, localHeaderOff } = entry;
|
|
57
|
+
|
|
58
|
+
// Read local file header to get filename + extra lengths (they may differ from CD)
|
|
59
|
+
const lfhFilenameLen = read16(buf, localHeaderOff + 26);
|
|
60
|
+
const lfhExtraLen = read16(buf, localHeaderOff + 28);
|
|
61
|
+
|
|
62
|
+
const dataStart = localHeaderOff + 30 + lfhFilenameLen + lfhExtraLen;
|
|
63
|
+
const compressed = buf.subarray(dataStart, dataStart + compressedSize);
|
|
64
|
+
|
|
65
|
+
let data;
|
|
66
|
+
if (method === 0) {
|
|
67
|
+
// Stored — no compression
|
|
68
|
+
data = compressed;
|
|
69
|
+
} else if (method === 8) {
|
|
70
|
+
// Deflate
|
|
71
|
+
data = inflateRawSync(compressed);
|
|
72
|
+
} else {
|
|
73
|
+
throw new Error(`Unsupported compression method ${method} for ${filename}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const outPath = join(targetDir, filename);
|
|
77
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
78
|
+
const ws = createWriteStream(outPath);
|
|
79
|
+
Readable.from(data).pipe(ws);
|
|
80
|
+
|
|
81
|
+
return outPath;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function extractZip(buf, targetDir) {
|
|
85
|
+
const eocdOff = findEocd(buf);
|
|
86
|
+
const cdSize = read32(buf, eocdOff + 12);
|
|
87
|
+
const cdOff = read32(buf, eocdOff + 16);
|
|
88
|
+
|
|
89
|
+
const paths = [];
|
|
90
|
+
for (const entry of centralDirectoryEntries(buf, cdOff, cdSize)) {
|
|
91
|
+
const outPath = extractFile(buf, entry, targetDir);
|
|
92
|
+
paths.push(outPath);
|
|
93
|
+
}
|
|
94
|
+
return paths;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ── GitHub release download ─────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
async function fetchLatestRelease() {
|
|
100
|
+
console.log('🔍 Fetching latest Cadet-Agent release...');
|
|
101
|
+
|
|
102
|
+
const res = await fetch(GITHUB_API, {
|
|
103
|
+
headers: {
|
|
104
|
+
'User-Agent': USER_AGENT,
|
|
105
|
+
'Accept': 'application/vnd.github+json',
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (!res.ok) {
|
|
110
|
+
if (res.status === 403 || res.status === 429) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`GitHub API rate-limited (${res.status}). ` +
|
|
113
|
+
'Set GITHUB_TOKEN env var for higher limits, or try again later.'
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
throw new Error(`GitHub API returned ${res.status}: ${res.statusText}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return res.json();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function findZipAsset(release) {
|
|
123
|
+
const asset = release.assets?.find(a => a.name === 'cadet-agent.zip');
|
|
124
|
+
if (!asset) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
`Release ${release.tag_name} does not contain cadet-agent.zip.\n` +
|
|
127
|
+
`Available assets: ${(release.assets || []).map(a => a.name).join(', ') || 'none'}`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return asset;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function downloadZip(url) {
|
|
134
|
+
console.log('⬇️ Downloading cadet-agent.zip...');
|
|
135
|
+
|
|
136
|
+
const res = await fetch(url, {
|
|
137
|
+
headers: {
|
|
138
|
+
'User-Agent': USER_AGENT,
|
|
139
|
+
'Accept': 'application/octet-stream',
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (!res.ok) {
|
|
144
|
+
throw new Error(`Download failed: ${res.status} ${res.statusText}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const contentLength = res.headers.get('content-length');
|
|
148
|
+
const total = contentLength ? parseInt(contentLength, 10) : 0;
|
|
149
|
+
|
|
150
|
+
// Stream to buffer with progress
|
|
151
|
+
const chunks = [];
|
|
152
|
+
let downloaded = 0;
|
|
153
|
+
const reader = res.body.getReader();
|
|
154
|
+
|
|
155
|
+
while (true) {
|
|
156
|
+
const { done, value } = await reader.read();
|
|
157
|
+
if (done) break;
|
|
158
|
+
chunks.push(value);
|
|
159
|
+
downloaded += value.length;
|
|
160
|
+
if (total > 0) {
|
|
161
|
+
const pct = Math.round((downloaded / total) * 100);
|
|
162
|
+
process.stdout.write(`\r ${pct}% (${(downloaded / 1024).toFixed(0)} KB / ${(total / 1024).toFixed(0)} KB)`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (total > 0) process.stdout.write('\n');
|
|
166
|
+
|
|
167
|
+
return Buffer.concat(chunks);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ── Public install entry ────────────────────────────────────────────────────
|
|
171
|
+
|
|
172
|
+
export async function install(targetDir) {
|
|
173
|
+
console.log(`📦 Cadet-Agent — installing to ${targetDir}\n`);
|
|
174
|
+
|
|
175
|
+
// 1. Fetch release metadata
|
|
176
|
+
const release = await fetchLatestRelease();
|
|
177
|
+
console.log(` Latest: ${release.tag_name} (published ${release.published_at})\n`);
|
|
178
|
+
|
|
179
|
+
// 2. Find zip asset
|
|
180
|
+
const asset = findZipAsset(release);
|
|
181
|
+
|
|
182
|
+
// 3. Download
|
|
183
|
+
const zipBuf = await downloadZip(asset.browser_download_url);
|
|
184
|
+
console.log(` Downloaded ${(zipBuf.length / 1024).toFixed(0)} KB\n`);
|
|
185
|
+
|
|
186
|
+
// 4. Extract
|
|
187
|
+
console.log('📂 Extracting...');
|
|
188
|
+
const extracted = extractZip(zipBuf, targetDir);
|
|
189
|
+
|
|
190
|
+
// 5. Report
|
|
191
|
+
console.log(`\n✅ Cadet-Agent ${release.tag_name} installed! Extracted ${extracted.length} files.\n`);
|
|
192
|
+
|
|
193
|
+
// Print per-IDE next steps
|
|
194
|
+
console.log('── Next steps ──');
|
|
195
|
+
console.log(' GitHub Copilot:');
|
|
196
|
+
console.log(' Rename .github\\cadet-copilot-instructions.md → .github\\copilot-instructions.md');
|
|
197
|
+
console.log(' Then start a chat: /cadet');
|
|
198
|
+
console.log(' Cursor:');
|
|
199
|
+
console.log(' Already active — .cursor\\rules\\cadet-agent.md loads automatically');
|
|
200
|
+
console.log(' Continue:');
|
|
201
|
+
console.log(' Already active — .continue\\rules\\cadet-agent.md loads automatically');
|
|
202
|
+
console.log(' Claude Code:');
|
|
203
|
+
console.log(' Already active — .claude\\skills\\cadet-agent.md loads as a project skill');
|
|
204
|
+
console.log('');
|
|
205
|
+
}
|