context-foundry 2.5.3 → 2.5.6

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 CHANGED
@@ -1,75 +1,46 @@
1
- # Context Foundry
1
+ # Foundry
2
2
 
3
- AI agent pattern-learning system that helps Claude and other AI agents improve over time.
3
+ Autonomous build loop that plans, builds, reviews, and learns -- forever.
4
4
 
5
- ## Installation
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 `cf` command globally. The npm package is a thin wrapper that automatically installs and delegates to the Python engine via pip.
13
+ This installs the `foundry` command globally.
12
14
 
13
15
  ## Requirements
14
16
 
15
- - **Node.js 16+** (for the npm wrapper)
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
- ## Quick Start
19
+ ## Usage
19
20
 
20
21
  ```bash
21
- # Launch the interactive TUI (Mission Control)
22
- cf
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
- ## Commands
25
+ # Interactive studio mode
26
+ foundry --dir /path/to/project studio
30
27
 
31
- ### `cf` - Main CLI
28
+ # Headless mode (CI/logs)
29
+ foundry --dir /path/to/project run --no-tui
32
30
 
33
- ```bash
34
- cf # Launch Mission Control TUI
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
- ## Documentation
35
+ ## Supported Platforms
69
36
 
70
- - [GitHub Repository](https://github.com/context-foundry/context-foundry)
71
- - [Documentation](https://context-foundry.github.io)
37
+ | Platform | Architecture |
38
+ |----------|-------------|
39
+ | macOS | Apple Silicon (arm64) |
40
+ | macOS | Intel (x64) |
41
+ | Linux | x64 |
42
+ | Windows | x64 |
72
43
 
73
- ## License
44
+ ## More Info
74
45
 
75
- MIT
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 CHANGED
@@ -1,45 +1,48 @@
1
1
  {
2
2
  "name": "context-foundry",
3
- "version": "2.5.3",
4
- "description": "AI agent pattern-learning system - npm wrapper for the Python engine",
5
- "keywords": [
6
- "ai",
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": "2.5.6",
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": "git+https://github.com/context-foundry/context-foundry.git"
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
- "license": "MIT",
23
- "author": "Context Foundry",
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
- "cf": "bin/cf.js",
26
- "cfd": "bin/cfd.js"
26
+ "foundry": "bin/foundry"
27
27
  },
28
28
  "scripts": {
29
- "postinstall": "node ./scripts/postinstall.js",
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
- "scripts/",
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();
@@ -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();