context-foundry 2.5.3 → 3.0.8
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 +25 -54
- package/bin/foundry +19 -0
- package/bin/foundry-native +0 -0
- package/install.js +185 -0
- package/package.json +26 -23
- package/bin/cf.js +0 -153
- package/bin/cfd.js +0 -153
- package/scripts/postinstall.js +0 -147
package/README.md
CHANGED
|
@@ -1,75 +1,46 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Foundry
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Autonomous build loop that plans, builds, reviews, and learns -- forever.
|
|
4
4
|
|
|
5
|
-
|
|
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
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
10
|
npm install -g context-foundry
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
This installs the `
|
|
13
|
+
This installs the `foundry` command globally.
|
|
12
14
|
|
|
13
15
|
## Requirements
|
|
14
16
|
|
|
15
|
-
-
|
|
16
|
-
- **Python 3.10+** (for the engine)
|
|
17
|
+
- [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) must be installed and authenticated
|
|
17
18
|
|
|
18
|
-
##
|
|
19
|
+
## Usage
|
|
19
20
|
|
|
20
21
|
```bash
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# Or use the daemon for background operations
|
|
25
|
-
cfd start
|
|
26
|
-
cfd status
|
|
27
|
-
```
|
|
22
|
+
# Point at any project with a TASKS.md
|
|
23
|
+
foundry --dir /path/to/project
|
|
28
24
|
|
|
29
|
-
|
|
25
|
+
# Interactive studio mode
|
|
26
|
+
foundry --dir /path/to/project studio
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
# Headless mode (CI/logs)
|
|
29
|
+
foundry --dir /path/to/project run --no-tui
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
cf --version # Show version
|
|
36
|
-
cf --help # Show help
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### `cfd` - Daemon CLI
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
cfd start # Start the daemon
|
|
43
|
-
cfd stop # Stop the daemon
|
|
44
|
-
cfd status # Get daemon status
|
|
45
|
-
cfd submit # Submit a job
|
|
46
|
-
cfd list # List jobs
|
|
47
|
-
cfd logs <id> # Show job logs
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## How It Works
|
|
51
|
-
|
|
52
|
-
The npm package is a **distribution shim** that:
|
|
53
|
-
|
|
54
|
-
1. Checks for Python 3.10+
|
|
55
|
-
2. Installs the Python `context-foundry` package via pip (if needed)
|
|
56
|
-
3. Delegates all commands to the Python CLI
|
|
57
|
-
|
|
58
|
-
This approach lets JavaScript/TypeScript developers install via their familiar `npm install` workflow while keeping the core logic in Python.
|
|
59
|
-
|
|
60
|
-
## Alternative Installation
|
|
61
|
-
|
|
62
|
-
If you prefer, install directly via pip:
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
pip install context-foundry
|
|
31
|
+
# Self-update to latest release
|
|
32
|
+
foundry update
|
|
66
33
|
```
|
|
67
34
|
|
|
68
|
-
##
|
|
35
|
+
## Supported Platforms
|
|
69
36
|
|
|
70
|
-
|
|
71
|
-
|
|
37
|
+
| Platform | Architecture |
|
|
38
|
+
|----------|-------------|
|
|
39
|
+
| macOS | Apple Silicon (arm64) |
|
|
40
|
+
| macOS | Intel (x64) |
|
|
41
|
+
| Linux | x64 |
|
|
42
|
+
| Windows | x64 |
|
|
72
43
|
|
|
73
|
-
##
|
|
44
|
+
## More Info
|
|
74
45
|
|
|
75
|
-
|
|
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
|
+
}
|
|
Binary file
|
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
CHANGED
|
@@ -1,45 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-foundry",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
"agents",
|
|
8
|
-
"claude",
|
|
9
|
-
"mcp",
|
|
10
|
-
"patterns",
|
|
11
|
-
"automation",
|
|
12
|
-
"context-foundry"
|
|
13
|
-
],
|
|
14
|
-
"homepage": "https://github.com/context-foundry/context-foundry",
|
|
3
|
+
"version": "3.0.8",
|
|
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",
|
|
15
7
|
"repository": {
|
|
16
8
|
"type": "git",
|
|
17
|
-
"url": "
|
|
9
|
+
"url": "https://github.com/context-foundry/context-foundry"
|
|
18
10
|
},
|
|
11
|
+
"homepage": "https://github.com/context-foundry/context-foundry",
|
|
19
12
|
"bugs": {
|
|
20
13
|
"url": "https://github.com/context-foundry/context-foundry/issues"
|
|
21
14
|
},
|
|
22
|
-
"
|
|
23
|
-
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai",
|
|
17
|
+
"agents",
|
|
18
|
+
"claude",
|
|
19
|
+
"automation",
|
|
20
|
+
"build-loop",
|
|
21
|
+
"foundry",
|
|
22
|
+
"context-foundry",
|
|
23
|
+
"tui"
|
|
24
|
+
],
|
|
24
25
|
"bin": {
|
|
25
|
-
"
|
|
26
|
-
"cfd": "bin/cfd.js"
|
|
26
|
+
"foundry": "bin/foundry"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
|
-
"postinstall": "node
|
|
30
|
-
"test": "node ./bin/cf.js --version"
|
|
31
|
-
},
|
|
32
|
-
"engines": {
|
|
33
|
-
"node": ">=16.0.0"
|
|
29
|
+
"postinstall": "node install.js"
|
|
34
30
|
},
|
|
35
31
|
"os": [
|
|
36
32
|
"darwin",
|
|
37
33
|
"linux",
|
|
38
34
|
"win32"
|
|
39
35
|
],
|
|
36
|
+
"cpu": [
|
|
37
|
+
"x64",
|
|
38
|
+
"arm64"
|
|
39
|
+
],
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=16"
|
|
42
|
+
},
|
|
40
43
|
"files": [
|
|
41
44
|
"bin/",
|
|
42
|
-
"
|
|
45
|
+
"install.js",
|
|
43
46
|
"README.md"
|
|
44
47
|
]
|
|
45
48
|
}
|
package/bin/cf.js
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Context Foundry CLI - npm wrapper for Python engine
|
|
5
|
-
*
|
|
6
|
-
* This is a thin shim that delegates to the Python `cf` command.
|
|
7
|
-
* The Python package is installed via pip during npm postinstall.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const { spawn, spawnSync } = require('child_process');
|
|
11
|
-
const path = require('path');
|
|
12
|
-
|
|
13
|
-
// ANSI colors
|
|
14
|
-
const colors = {
|
|
15
|
-
red: '\x1b[31m',
|
|
16
|
-
green: '\x1b[32m',
|
|
17
|
-
yellow: '\x1b[33m',
|
|
18
|
-
blue: '\x1b[34m',
|
|
19
|
-
cyan: '\x1b[36m',
|
|
20
|
-
reset: '\x1b[0m',
|
|
21
|
-
bold: '\x1b[1m'
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
function log(msg, color = '') {
|
|
25
|
-
console.log(`${color}${msg}${colors.reset}`);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function error(msg) {
|
|
29
|
-
console.error(`${colors.red}Error: ${msg}${colors.reset}`);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Check if Python 3.10+ is available
|
|
34
|
-
*/
|
|
35
|
-
function checkPython() {
|
|
36
|
-
const pythonCommands = ['python3', 'python'];
|
|
37
|
-
|
|
38
|
-
for (const cmd of pythonCommands) {
|
|
39
|
-
try {
|
|
40
|
-
const result = spawnSync(cmd, ['--version'], { encoding: 'utf-8' });
|
|
41
|
-
if (result.status === 0) {
|
|
42
|
-
const version = result.stdout.trim() || result.stderr.trim();
|
|
43
|
-
const match = version.match(/Python (\d+)\.(\d+)/);
|
|
44
|
-
if (match) {
|
|
45
|
-
const major = parseInt(match[1], 10);
|
|
46
|
-
const minor = parseInt(match[2], 10);
|
|
47
|
-
if (major === 3 && minor >= 10) {
|
|
48
|
-
return { cmd, version: `${major}.${minor}` };
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
} catch (e) {
|
|
53
|
-
// Continue to next command
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Check if the cf command is available in PATH
|
|
61
|
-
*/
|
|
62
|
-
function checkCfInstalled() {
|
|
63
|
-
const result = spawnSync('which', ['cf'], { encoding: 'utf-8' });
|
|
64
|
-
return result.status === 0;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Run the Python cf command with all arguments passed through
|
|
69
|
-
*/
|
|
70
|
-
function runCf(args) {
|
|
71
|
-
const cf = spawn('cf', args, {
|
|
72
|
-
stdio: 'inherit',
|
|
73
|
-
env: process.env
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
cf.on('error', (err) => {
|
|
77
|
-
if (err.code === 'ENOENT') {
|
|
78
|
-
error('The `cf` command is not found in your PATH.');
|
|
79
|
-
log('\nThis usually means the Python package is not installed.', colors.yellow);
|
|
80
|
-
log('Try running:', colors.cyan);
|
|
81
|
-
log(' pip install context-foundry', colors.bold);
|
|
82
|
-
log('\nOr reinstall this npm package:', colors.cyan);
|
|
83
|
-
log(' npm install -g context-foundry', colors.bold);
|
|
84
|
-
process.exit(1);
|
|
85
|
-
}
|
|
86
|
-
throw err;
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
cf.on('close', (code) => {
|
|
90
|
-
process.exit(code);
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Show help with installation instructions
|
|
96
|
-
*/
|
|
97
|
-
function showHelp() {
|
|
98
|
-
log(`
|
|
99
|
-
${colors.cyan}${colors.bold}Context Foundry${colors.reset} - AI Agent Pattern Learning System
|
|
100
|
-
|
|
101
|
-
${colors.yellow}Usage:${colors.reset}
|
|
102
|
-
cf Launch Mission Control TUI
|
|
103
|
-
cf --version Show version
|
|
104
|
-
cf --help Show this help
|
|
105
|
-
|
|
106
|
-
${colors.yellow}Quick Start:${colors.reset}
|
|
107
|
-
1. Run ${colors.cyan}cf${colors.reset} to launch the interactive TUI
|
|
108
|
-
2. Or use the daemon: ${colors.cyan}cfd start${colors.reset}
|
|
109
|
-
|
|
110
|
-
${colors.yellow}More Info:${colors.reset}
|
|
111
|
-
https://github.com/context-foundry/context-foundry
|
|
112
|
-
`);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Main entry point
|
|
116
|
-
function main() {
|
|
117
|
-
const args = process.argv.slice(2);
|
|
118
|
-
|
|
119
|
-
// Check if Python is available
|
|
120
|
-
const python = checkPython();
|
|
121
|
-
if (!python) {
|
|
122
|
-
error('Python 3.10+ is required but not found.');
|
|
123
|
-
log('\nPlease install Python 3.10 or later:', colors.yellow);
|
|
124
|
-
log(' macOS: brew install python@3.12', colors.cyan);
|
|
125
|
-
log(' Ubuntu: sudo apt install python3.12', colors.cyan);
|
|
126
|
-
log(' Windows: https://www.python.org/downloads/', colors.cyan);
|
|
127
|
-
process.exit(1);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Check if cf is installed
|
|
131
|
-
if (!checkCfInstalled()) {
|
|
132
|
-
log(`${colors.yellow}The Python 'cf' command is not installed.${colors.reset}`);
|
|
133
|
-
log(`Installing context-foundry via pip...`);
|
|
134
|
-
|
|
135
|
-
const pip = spawnSync(python.cmd, ['-m', 'pip', 'install', 'context-foundry'], {
|
|
136
|
-
stdio: 'inherit'
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
if (pip.status !== 0) {
|
|
140
|
-
error('Failed to install context-foundry via pip.');
|
|
141
|
-
log('\nTry installing manually:', colors.yellow);
|
|
142
|
-
log(' pip install context-foundry', colors.cyan);
|
|
143
|
-
process.exit(1);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
log(`${colors.green}Successfully installed context-foundry!${colors.reset}\n`);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Pass all args to the Python cf command
|
|
150
|
-
runCf(args);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
main();
|
package/bin/cfd.js
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Context Foundry Daemon CLI - npm wrapper for Python engine
|
|
5
|
-
*
|
|
6
|
-
* This is a thin shim that delegates to the Python `cfd` command.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { spawn, spawnSync } = require('child_process');
|
|
10
|
-
|
|
11
|
-
// ANSI colors
|
|
12
|
-
const colors = {
|
|
13
|
-
red: '\x1b[31m',
|
|
14
|
-
green: '\x1b[32m',
|
|
15
|
-
yellow: '\x1b[33m',
|
|
16
|
-
cyan: '\x1b[36m',
|
|
17
|
-
reset: '\x1b[0m',
|
|
18
|
-
bold: '\x1b[1m'
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
function error(msg) {
|
|
22
|
-
console.error(`${colors.red}Error: ${msg}${colors.reset}`);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function log(msg, color = '') {
|
|
26
|
-
console.log(`${color}${msg}${colors.reset}`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Check if Python 3.10+ is available
|
|
31
|
-
*/
|
|
32
|
-
function checkPython() {
|
|
33
|
-
const pythonCommands = ['python3', 'python'];
|
|
34
|
-
|
|
35
|
-
for (const cmd of pythonCommands) {
|
|
36
|
-
try {
|
|
37
|
-
const result = spawnSync(cmd, ['--version'], { encoding: 'utf-8' });
|
|
38
|
-
if (result.status === 0) {
|
|
39
|
-
const version = result.stdout.trim() || result.stderr.trim();
|
|
40
|
-
const match = version.match(/Python (\d+)\.(\d+)/);
|
|
41
|
-
if (match) {
|
|
42
|
-
const major = parseInt(match[1], 10);
|
|
43
|
-
const minor = parseInt(match[2], 10);
|
|
44
|
-
if (major === 3 && minor >= 10) {
|
|
45
|
-
return { cmd, version: `${major}.${minor}` };
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
} catch (e) {
|
|
50
|
-
// Continue to next command
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Get the path to cfd from the Python package
|
|
58
|
-
*/
|
|
59
|
-
function getCfdPath() {
|
|
60
|
-
// First try: check if cfd is in PATH
|
|
61
|
-
const which = spawnSync('which', ['cfd'], { encoding: 'utf-8' });
|
|
62
|
-
if (which.status === 0) {
|
|
63
|
-
return 'cfd';
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Second try: use the tools/cfd script directly if we're in the repo
|
|
67
|
-
const localCfd = require('path').join(__dirname, '../../tools/cfd');
|
|
68
|
-
try {
|
|
69
|
-
require('fs').accessSync(localCfd, require('fs').constants.X_OK);
|
|
70
|
-
return localCfd;
|
|
71
|
-
} catch (e) {
|
|
72
|
-
// Not in repo or not executable
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Run the Python cfd command with all arguments passed through
|
|
80
|
-
*/
|
|
81
|
-
function runCfd(cfdPath, args) {
|
|
82
|
-
const cfd = spawn(cfdPath, args, {
|
|
83
|
-
stdio: 'inherit',
|
|
84
|
-
env: process.env
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
cfd.on('error', (err) => {
|
|
88
|
-
if (err.code === 'ENOENT') {
|
|
89
|
-
error('The `cfd` command is not found.');
|
|
90
|
-
log('\nTry running:', colors.cyan);
|
|
91
|
-
log(' pip install context-foundry', colors.bold);
|
|
92
|
-
process.exit(1);
|
|
93
|
-
}
|
|
94
|
-
throw err;
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
cfd.on('close', (code) => {
|
|
98
|
-
process.exit(code);
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function showHelp() {
|
|
103
|
-
log(`
|
|
104
|
-
${colors.cyan}${colors.bold}Context Foundry Daemon (cfd)${colors.reset}
|
|
105
|
-
|
|
106
|
-
${colors.yellow}Usage:${colors.reset}
|
|
107
|
-
cfd start Start the daemon
|
|
108
|
-
cfd stop Stop the daemon
|
|
109
|
-
cfd status Get daemon status
|
|
110
|
-
cfd submit Submit a job
|
|
111
|
-
cfd list List jobs
|
|
112
|
-
cfd logs <job-id> Show job logs
|
|
113
|
-
cfd --help Full help
|
|
114
|
-
|
|
115
|
-
${colors.yellow}More Info:${colors.reset}
|
|
116
|
-
https://github.com/context-foundry/context-foundry
|
|
117
|
-
`);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Main entry point
|
|
121
|
-
function main() {
|
|
122
|
-
const args = process.argv.slice(2);
|
|
123
|
-
|
|
124
|
-
// Check Python availability
|
|
125
|
-
const python = checkPython();
|
|
126
|
-
if (!python) {
|
|
127
|
-
error('Python 3.10+ is required but not found.');
|
|
128
|
-
process.exit(1);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Find cfd command
|
|
132
|
-
const cfdPath = getCfdPath();
|
|
133
|
-
if (!cfdPath) {
|
|
134
|
-
log(`${colors.yellow}The 'cfd' command is not installed.${colors.reset}`);
|
|
135
|
-
log(`Installing context-foundry via pip...`);
|
|
136
|
-
|
|
137
|
-
const pip = spawnSync(python.cmd, ['-m', 'pip', 'install', 'context-foundry'], {
|
|
138
|
-
stdio: 'inherit'
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
if (pip.status !== 0) {
|
|
142
|
-
error('Failed to install context-foundry via pip.');
|
|
143
|
-
process.exit(1);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
log(`${colors.green}Successfully installed!${colors.reset}\n`);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Run cfd with all arguments
|
|
150
|
-
runCfd(cfdPath || 'cfd', args);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
main();
|
package/scripts/postinstall.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Post-install script for context-foundry npm package
|
|
5
|
-
*
|
|
6
|
-
* This script runs after `npm install` and ensures the Python
|
|
7
|
-
* package is installed via pip.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const { spawnSync } = require('child_process');
|
|
11
|
-
|
|
12
|
-
// ANSI colors
|
|
13
|
-
const colors = {
|
|
14
|
-
red: '\x1b[31m',
|
|
15
|
-
green: '\x1b[32m',
|
|
16
|
-
yellow: '\x1b[33m',
|
|
17
|
-
cyan: '\x1b[36m',
|
|
18
|
-
dim: '\x1b[2m',
|
|
19
|
-
reset: '\x1b[0m',
|
|
20
|
-
bold: '\x1b[1m'
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
function log(msg, color = '') {
|
|
24
|
-
console.log(`${color}${msg}${colors.reset}`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function error(msg) {
|
|
28
|
-
console.error(`${colors.red}${msg}${colors.reset}`);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Find Python 3.10+ executable
|
|
33
|
-
*/
|
|
34
|
-
function findPython() {
|
|
35
|
-
const pythonCommands = ['python3', 'python'];
|
|
36
|
-
|
|
37
|
-
for (const cmd of pythonCommands) {
|
|
38
|
-
try {
|
|
39
|
-
const result = spawnSync(cmd, ['--version'], {
|
|
40
|
-
encoding: 'utf-8',
|
|
41
|
-
timeout: 5000
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
if (result.status === 0) {
|
|
45
|
-
const version = result.stdout.trim() || result.stderr.trim();
|
|
46
|
-
const match = version.match(/Python (\d+)\.(\d+)/);
|
|
47
|
-
if (match) {
|
|
48
|
-
const major = parseInt(match[1], 10);
|
|
49
|
-
const minor = parseInt(match[2], 10);
|
|
50
|
-
if (major === 3 && minor >= 10) {
|
|
51
|
-
return { cmd, major, minor };
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
} catch (e) {
|
|
56
|
-
// Continue to next command
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Check if context-foundry Python package is already installed
|
|
64
|
-
*/
|
|
65
|
-
function checkInstalled(pythonCmd) {
|
|
66
|
-
const result = spawnSync(pythonCmd, ['-m', 'pip', 'show', 'context-foundry'], {
|
|
67
|
-
encoding: 'utf-8',
|
|
68
|
-
timeout: 10000
|
|
69
|
-
});
|
|
70
|
-
return result.status === 0;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Install the Python package
|
|
75
|
-
*/
|
|
76
|
-
function installPythonPackage(pythonCmd) {
|
|
77
|
-
log('\nInstalling context-foundry Python package...', colors.cyan);
|
|
78
|
-
|
|
79
|
-
const result = spawnSync(
|
|
80
|
-
pythonCmd,
|
|
81
|
-
['-m', 'pip', 'install', '--upgrade', 'context-foundry'],
|
|
82
|
-
{
|
|
83
|
-
stdio: 'inherit',
|
|
84
|
-
timeout: 300000 // 5 minutes
|
|
85
|
-
}
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
return result.status === 0;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function main() {
|
|
92
|
-
log(`\n${'='.repeat(50)}`, colors.dim);
|
|
93
|
-
log(`${colors.bold}Context Foundry${colors.reset} - Post-install setup`);
|
|
94
|
-
log(`${'='.repeat(50)}`, colors.dim);
|
|
95
|
-
|
|
96
|
-
// Step 1: Find Python
|
|
97
|
-
log('\nChecking Python installation...', colors.cyan);
|
|
98
|
-
const python = findPython();
|
|
99
|
-
|
|
100
|
-
if (!python) {
|
|
101
|
-
log(`\n${colors.yellow}Warning: Python 3.10+ not found.${colors.reset}`);
|
|
102
|
-
log(`\nThe npm package is installed, but you'll need Python to run it.`);
|
|
103
|
-
log(`\nInstall Python 3.10+ from:`);
|
|
104
|
-
log(` macOS: ${colors.cyan}brew install python@3.12${colors.reset}`);
|
|
105
|
-
log(` Ubuntu: ${colors.cyan}sudo apt install python3.12${colors.reset}`);
|
|
106
|
-
log(` Windows: ${colors.cyan}https://www.python.org/downloads/${colors.reset}`);
|
|
107
|
-
log(`\nThen run: ${colors.cyan}pip install context-foundry${colors.reset}\n`);
|
|
108
|
-
// Don't fail - the bin scripts will handle this at runtime
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
log(` Found ${colors.green}Python ${python.major}.${python.minor}${colors.reset} (${python.cmd})`);
|
|
113
|
-
|
|
114
|
-
// Step 2: Check if already installed
|
|
115
|
-
if (checkInstalled(python.cmd)) {
|
|
116
|
-
log(` ${colors.green}context-foundry already installed${colors.reset}`);
|
|
117
|
-
log(`\n${colors.green}Ready to use!${colors.reset} Run ${colors.cyan}cf${colors.reset} to get started.\n`);
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Step 3: Install Python package
|
|
122
|
-
const installed = installPythonPackage(python.cmd);
|
|
123
|
-
|
|
124
|
-
if (installed) {
|
|
125
|
-
log(`\n${colors.green}Python package installed!${colors.reset}`);
|
|
126
|
-
|
|
127
|
-
// Run cf setup to configure Claude Code
|
|
128
|
-
log(`\nConfiguring Claude Code integration...`);
|
|
129
|
-
const setup = spawnSync('cf', ['setup'], {
|
|
130
|
-
stdio: 'inherit',
|
|
131
|
-
timeout: 30000
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
if (setup.status === 0) {
|
|
135
|
-
log(`\n${colors.green}Setup complete!${colors.reset}`);
|
|
136
|
-
} else {
|
|
137
|
-
log(`\n${colors.yellow}Note: Run 'cf setup' manually to configure Claude Code.${colors.reset}`);
|
|
138
|
-
}
|
|
139
|
-
} else {
|
|
140
|
-
error(`\nFailed to install Python package.`);
|
|
141
|
-
log(`\nTry installing manually:`);
|
|
142
|
-
log(` ${colors.cyan}pip install context-foundry${colors.reset}\n`);
|
|
143
|
-
// Don't fail npm install - user can fix this manually
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
main();
|