context-foundry 0.7.9
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 +46 -0
- package/bin/foundry +19 -0
- package/install.js +185 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Foundry
|
|
2
|
+
|
|
3
|
+
Autonomous build loop that plans, builds, reviews, and learns -- forever.
|
|
4
|
+
|
|
5
|
+
This is the npm installer for Foundry. It downloads the correct pre-built binary for your platform from [GitHub Releases](https://github.com/context-foundry/context-foundry/releases).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g context-foundry
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This installs the `foundry` command globally.
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
- [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) must be installed and authenticated
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Point at any project with a TASKS.md
|
|
23
|
+
foundry --dir /path/to/project
|
|
24
|
+
|
|
25
|
+
# Interactive studio mode
|
|
26
|
+
foundry --dir /path/to/project studio
|
|
27
|
+
|
|
28
|
+
# Headless mode (CI/logs)
|
|
29
|
+
foundry --dir /path/to/project run --no-tui
|
|
30
|
+
|
|
31
|
+
# Self-update to latest release
|
|
32
|
+
foundry update
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Supported Platforms
|
|
36
|
+
|
|
37
|
+
| Platform | Architecture |
|
|
38
|
+
|----------|-------------|
|
|
39
|
+
| macOS | Apple Silicon (arm64) |
|
|
40
|
+
| macOS | Intel (x64) |
|
|
41
|
+
| Linux | x64 |
|
|
42
|
+
| Windows | x64 |
|
|
43
|
+
|
|
44
|
+
## More Info
|
|
45
|
+
|
|
46
|
+
See the full documentation at [github.com/context-foundry/context-foundry](https://github.com/context-foundry/context-foundry).
|
package/bin/foundry
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const { execFileSync } = require("child_process");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
|
|
7
|
+
const nativeName = os.platform() === "win32" ? "foundry-native.exe" : "foundry-native";
|
|
8
|
+
const binaryPath = path.join(__dirname, nativeName);
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
execFileSync(binaryPath, process.argv.slice(2), { stdio: "inherit" });
|
|
12
|
+
} catch (err) {
|
|
13
|
+
if (err.status !== undefined) {
|
|
14
|
+
process.exit(err.status);
|
|
15
|
+
}
|
|
16
|
+
console.error(`Failed to run foundry: ${err.message}`);
|
|
17
|
+
console.error("Try reinstalling: npm install -g context-foundry");
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
package/install.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Downloads the correct Foundry binary from GitHub Releases during npm install.
|
|
4
|
+
|
|
5
|
+
const https = require("https");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const { execSync } = require("child_process");
|
|
9
|
+
const os = require("os");
|
|
10
|
+
const zlib = require("zlib");
|
|
11
|
+
|
|
12
|
+
const REPO = "context-foundry/context-foundry";
|
|
13
|
+
|
|
14
|
+
function getTarget() {
|
|
15
|
+
const platform = os.platform();
|
|
16
|
+
const arch = os.arch();
|
|
17
|
+
|
|
18
|
+
if (platform === "darwin" && arch === "arm64") return "mac-apple-silicon";
|
|
19
|
+
if (platform === "darwin" && arch === "x64") return "mac-intel";
|
|
20
|
+
if (platform === "linux" && arch === "x64") return "linux";
|
|
21
|
+
if (platform === "win32" && arch === "x64") return "windows";
|
|
22
|
+
|
|
23
|
+
throw new Error(`Unsupported platform: ${platform}-${arch}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function fetchLatestVersion() {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
https.get(
|
|
29
|
+
`https://api.github.com/repos/${REPO}/releases/latest`,
|
|
30
|
+
{ headers: { "User-Agent": "context-foundry-npm" } },
|
|
31
|
+
(res) => {
|
|
32
|
+
const chunks = [];
|
|
33
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
34
|
+
res.on("end", () => {
|
|
35
|
+
try {
|
|
36
|
+
const data = JSON.parse(Buffer.concat(chunks).toString());
|
|
37
|
+
resolve(data.tag_name);
|
|
38
|
+
} catch (e) {
|
|
39
|
+
reject(new Error("Failed to parse GitHub API response"));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
res.on("error", reject);
|
|
43
|
+
}
|
|
44
|
+
).on("error", reject);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getBinaryName() {
|
|
49
|
+
return os.platform() === "win32" ? "foundry.exe" : "foundry";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getArchiveExt() {
|
|
53
|
+
return os.platform() === "win32" ? "zip" : "tar.gz";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function fetch(url) {
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
https
|
|
59
|
+
.get(url, { headers: { "User-Agent": "context-foundry-npm" } }, (res) => {
|
|
60
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
61
|
+
return fetch(res.headers.location).then(resolve, reject);
|
|
62
|
+
}
|
|
63
|
+
if (res.statusCode !== 200) {
|
|
64
|
+
return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
|
|
65
|
+
}
|
|
66
|
+
const chunks = [];
|
|
67
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
68
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
69
|
+
res.on("error", reject);
|
|
70
|
+
})
|
|
71
|
+
.on("error", reject);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function extractTarGz(buffer, destDir, binaryName) {
|
|
76
|
+
const tmpFile = path.join(os.tmpdir(), `foundry-${Date.now()}.tar.gz`);
|
|
77
|
+
fs.writeFileSync(tmpFile, buffer);
|
|
78
|
+
execSync(`tar xzf "${tmpFile}" -C "${destDir}"`, { stdio: "ignore" });
|
|
79
|
+
fs.unlinkSync(tmpFile);
|
|
80
|
+
|
|
81
|
+
const extracted = path.join(destDir, binaryName);
|
|
82
|
+
if (!fs.existsSync(extracted)) {
|
|
83
|
+
// Some archives nest in a directory
|
|
84
|
+
const entries = fs.readdirSync(destDir);
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
const nested = path.join(destDir, entry, binaryName);
|
|
87
|
+
if (fs.existsSync(nested)) {
|
|
88
|
+
fs.renameSync(nested, extracted);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return extracted;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function extractZip(buffer, destDir, binaryName) {
|
|
97
|
+
const tmpFile = path.join(os.tmpdir(), `foundry-${Date.now()}.zip`);
|
|
98
|
+
fs.writeFileSync(tmpFile, buffer);
|
|
99
|
+
|
|
100
|
+
if (os.platform() === "win32") {
|
|
101
|
+
execSync(
|
|
102
|
+
`powershell -Command "Expand-Archive -Path '${tmpFile}' -DestinationPath '${destDir}' -Force"`,
|
|
103
|
+
{ stdio: "ignore" }
|
|
104
|
+
);
|
|
105
|
+
} else {
|
|
106
|
+
execSync(`unzip -o "${tmpFile}" -d "${destDir}"`, { stdio: "ignore" });
|
|
107
|
+
}
|
|
108
|
+
fs.unlinkSync(tmpFile);
|
|
109
|
+
|
|
110
|
+
const extracted = path.join(destDir, binaryName);
|
|
111
|
+
if (!fs.existsSync(extracted)) {
|
|
112
|
+
const entries = fs.readdirSync(destDir);
|
|
113
|
+
for (const entry of entries) {
|
|
114
|
+
const nested = path.join(destDir, entry, binaryName);
|
|
115
|
+
if (fs.existsSync(nested)) {
|
|
116
|
+
fs.renameSync(nested, extracted);
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return extracted;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function main() {
|
|
125
|
+
const target = getTarget();
|
|
126
|
+
const binaryName = getBinaryName();
|
|
127
|
+
const ext = getArchiveExt();
|
|
128
|
+
const version = await fetchLatestVersion();
|
|
129
|
+
const archiveName = `foundry-${target}.${ext}`;
|
|
130
|
+
const url = `https://github.com/${REPO}/releases/download/${version}/${archiveName}`;
|
|
131
|
+
|
|
132
|
+
const binDir = path.join(__dirname, "bin");
|
|
133
|
+
const nativeName = os.platform() === "win32" ? "foundry-native.exe" : "foundry-native";
|
|
134
|
+
const destPath = path.join(binDir, nativeName);
|
|
135
|
+
|
|
136
|
+
// Skip if binary already exists and is the right version
|
|
137
|
+
if (fs.existsSync(destPath)) {
|
|
138
|
+
try {
|
|
139
|
+
const existing = execSync(`"${destPath}" --version`, { encoding: "utf8" }).trim();
|
|
140
|
+
if (existing.includes(version.replace("v", ""))) {
|
|
141
|
+
console.log(`foundry ${version} already installed.`);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
} catch {
|
|
145
|
+
// Version check failed, re-download
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.log(`Downloading foundry ${version} for ${target}...`);
|
|
150
|
+
|
|
151
|
+
const buffer = await fetch(url);
|
|
152
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "foundry-"));
|
|
153
|
+
|
|
154
|
+
if (ext === "tar.gz") {
|
|
155
|
+
await extractTarGz(buffer, tmpDir, binaryName);
|
|
156
|
+
} else {
|
|
157
|
+
await extractZip(buffer, tmpDir, binaryName);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const extractedBinary = path.join(tmpDir, binaryName);
|
|
161
|
+
if (!fs.existsSync(extractedBinary)) {
|
|
162
|
+
throw new Error(`Binary not found after extraction in ${tmpDir}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!fs.existsSync(binDir)) {
|
|
166
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
fs.copyFileSync(extractedBinary, destPath);
|
|
170
|
+
|
|
171
|
+
if (os.platform() !== "win32") {
|
|
172
|
+
fs.chmodSync(destPath, 0o755);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Clean up
|
|
176
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
177
|
+
|
|
178
|
+
console.log(`foundry ${version} installed successfully.`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
main().catch((err) => {
|
|
182
|
+
console.error(`Failed to install foundry: ${err.message}`);
|
|
183
|
+
console.error("You can install manually from: https://github.com/context-foundry/context-foundry/releases");
|
|
184
|
+
process.exit(1);
|
|
185
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "context-foundry",
|
|
3
|
+
"version": "0.7.9",
|
|
4
|
+
"description": "Autonomous build loop that plans, builds, reviews, and learns. npm wrapper that installs the Foundry binary.",
|
|
5
|
+
"author": "Context Foundry",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/context-foundry/context-foundry"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/context-foundry/context-foundry",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/context-foundry/context-foundry/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai",
|
|
17
|
+
"agents",
|
|
18
|
+
"claude",
|
|
19
|
+
"automation",
|
|
20
|
+
"build-loop",
|
|
21
|
+
"foundry",
|
|
22
|
+
"context-foundry",
|
|
23
|
+
"tui"
|
|
24
|
+
],
|
|
25
|
+
"bin": {
|
|
26
|
+
"foundry": "bin/foundry"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"postinstall": "node install.js"
|
|
30
|
+
},
|
|
31
|
+
"os": [
|
|
32
|
+
"darwin",
|
|
33
|
+
"linux",
|
|
34
|
+
"win32"
|
|
35
|
+
],
|
|
36
|
+
"cpu": [
|
|
37
|
+
"x64",
|
|
38
|
+
"arm64"
|
|
39
|
+
],
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=16"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"bin/",
|
|
45
|
+
"install.js",
|
|
46
|
+
"README.md"
|
|
47
|
+
]
|
|
48
|
+
}
|