@castlekit/castle 0.1.3 → 0.1.5

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
@@ -28,6 +28,22 @@ npm run dev
28
28
 
29
29
  Then open [http://localhost:3333](http://localhost:3333).
30
30
 
31
+ ## Releasing
32
+
33
+ ```bash
34
+ # 1. Bump version
35
+ npm version patch --no-git-tag-version
36
+
37
+ # 2. Commit and push
38
+ git add -A && git commit -m "Release vX.Y.Z" && git push
39
+
40
+ # 3. Publish to npm (requires 2FA)
41
+ npm publish --access public
42
+
43
+ # 4. Tag the release
44
+ git tag vX.Y.Z && git push origin vX.Y.Z
45
+ ```
46
+
31
47
  ## Requirements
32
48
 
33
49
  - Node.js >= 22
package/bin/castle.js CHANGED
@@ -1,15 +1,32 @@
1
- #!/usr/bin/env -S node --import tsx
1
+ #!/usr/bin/env node
2
2
 
3
3
  // Castle CLI - The multi-agent workspace
4
4
  // https://castlekit.com
5
5
 
6
- import { program } from "commander";
7
- import pc from "picocolors";
8
- import { readFileSync } from "fs";
9
- import { resolve, dirname } from "path";
6
+ // Bootstrap tsx from the package's own node_modules so it works
7
+ // regardless of the user's current working directory.
8
+ import { dirname, resolve } from "path";
10
9
  import { fileURLToPath } from "url";
11
10
 
12
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
+
13
+ if (!process.env._CASTLE_CLI) {
14
+ const { execFileSync } = await import("child_process");
15
+ const tsxImport = resolve(__dirname, "..", "node_modules", "tsx", "dist", "esm", "index.mjs");
16
+ try {
17
+ execFileSync(process.execPath, ["--import", tsxImport, ...process.argv.slice(1)], {
18
+ stdio: "inherit",
19
+ env: { ...process.env, _CASTLE_CLI: "1" },
20
+ });
21
+ } catch (e) {
22
+ process.exit(e.status || 1);
23
+ }
24
+ process.exit(0);
25
+ }
26
+
27
+ const { program } = await import("commander");
28
+ const pc = (await import("picocolors")).default;
29
+ const { readFileSync } = await import("fs");
13
30
  let version = "0.0.0";
14
31
  try {
15
32
  version = JSON.parse(readFileSync(resolve(__dirname, "..", "package.json"), "utf-8")).version;
@@ -41,6 +58,70 @@ program
41
58
  await open(url);
42
59
  });
43
60
 
61
+ program
62
+ .command("update")
63
+ .description("Check for updates and install the latest version")
64
+ .action(async () => {
65
+ const { execSync } = await import("child_process");
66
+
67
+ console.log(pc.bold("\n 🏰 Castle\n"));
68
+ console.log(pc.dim(" Checking for updates...\n"));
69
+
70
+ let latest;
71
+ try {
72
+ latest = execSync("npm view @castlekit/castle version", {
73
+ encoding: "utf-8",
74
+ timeout: 15000,
75
+ stdio: ["ignore", "pipe", "ignore"],
76
+ }).trim();
77
+ } catch {
78
+ console.log(pc.yellow(" Could not check for updates. Try again later.\n"));
79
+ return;
80
+ }
81
+
82
+ if (version === latest) {
83
+ console.log(` You're on the latest version (${pc.green(version)}).\n`);
84
+ return;
85
+ }
86
+
87
+ // Check for major version bump
88
+ const currentMajor = parseInt(version.split(".")[0], 10);
89
+ const latestMajor = parseInt(latest.split(".")[0], 10);
90
+
91
+ if (latestMajor > currentMajor) {
92
+ const readline = await import("readline");
93
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
94
+ const answer = await new Promise((resolve) => {
95
+ rl.question(
96
+ ` ${pc.yellow("⚠")} Castle ${pc.cyan(latest)} is available (you have ${pc.dim(version)}).\n` +
97
+ ` This is a major version update and may include breaking changes.\n\n` +
98
+ ` Continue? (y/N) `,
99
+ resolve
100
+ );
101
+ });
102
+ rl.close();
103
+
104
+ if (answer.toLowerCase() !== "y") {
105
+ console.log(pc.dim("\n Update cancelled.\n"));
106
+ return;
107
+ }
108
+ console.log();
109
+ }
110
+
111
+ console.log(` Updating Castle from ${pc.dim(version)} to ${pc.cyan(latest)}...\n`);
112
+
113
+ try {
114
+ execSync(`npm install -g @castlekit/castle@${latest}`, {
115
+ stdio: "inherit",
116
+ timeout: 120000,
117
+ });
118
+ console.log(pc.green(`\n ✔ Updated successfully!\n`));
119
+ } catch {
120
+ console.log(pc.red(`\n Update failed.`));
121
+ console.log(` Try manually: ${pc.cyan(`npm install -g @castlekit/castle@${latest}`)}\n`);
122
+ }
123
+ });
124
+
44
125
  program
45
126
  .command("status")
46
127
  .description("Show Castle and agent status")
@@ -83,10 +164,11 @@ if (process.argv.length <= 2) {
83
164
  } else {
84
165
  console.log(pc.bold("\n 🏰 Castle\n"));
85
166
  console.log(pc.dim(" The multi-agent workspace.\n"));
86
- console.log(` ${pc.cyan("castle open")} Open the web UI`);
87
- console.log(` ${pc.cyan("castle setup")} Re-run setup wizard`);
88
- console.log(` ${pc.cyan("castle status")} Show status`);
89
- console.log(` ${pc.cyan("castle --help")} All commands\n`);
167
+ console.log(` ${pc.cyan("castle open")} Open the web UI`);
168
+ console.log(` ${pc.cyan("castle setup")} Re-run setup wizard`);
169
+ console.log(` ${pc.cyan("castle status")} Show status`);
170
+ console.log(` ${pc.cyan("castle update")} Check for updates`);
171
+ console.log(` ${pc.cyan("castle --help")} All commands\n`);
90
172
  }
91
173
  })();
92
174
  } else {
package/install.sh CHANGED
@@ -556,11 +556,7 @@ install_castle() {
556
556
 
557
557
  # Check if this version is already installed
558
558
  local installed_version=""
559
- installed_version="$(npm list -g @castlekit/castle --depth=0 --json 2>/dev/null | node -e "
560
- let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{
561
- try{const j=JSON.parse(d);console.log(j.dependencies?.['@castlekit/castle']?.version||'')}catch{}
562
- })
563
- " 2>/dev/null || true)"
559
+ installed_version="$(npm list -g @castlekit/castle --depth=0 2>/dev/null | grep '@castlekit/castle@' | sed 's/.*@castlekit\/castle@//' | tr -d '[:space:]' || true)"
564
560
 
565
561
  if [[ -n "$resolved_version" && "$installed_version" == "$resolved_version" ]]; then
566
562
  echo -e "${SUCCESS}✓${NC} Castle ${INFO}${resolved_version}${NC} already installed"
@@ -599,9 +595,24 @@ main() {
599
595
 
600
596
  # Check for existing installation
601
597
  local is_upgrade=false
602
- if [[ -n "$(type -P castle 2>/dev/null || true)" ]]; then
603
- echo -e "${WARN}→${NC} Existing Castle installation detected"
598
+ local existing_bin=""
599
+ existing_bin="$(type -P castle 2>/dev/null || true)"
600
+ if [[ -n "$existing_bin" ]]; then
604
601
  is_upgrade=true
602
+
603
+ # Check if already fully set up with the latest version
604
+ local current_ver=""
605
+ current_ver="$(npm list -g @castlekit/castle --depth=0 2>/dev/null | grep '@castlekit/castle@' | sed 's/.*@castlekit\/castle@//' | tr -d '[:space:]' || true)"
606
+ local target_ver=""
607
+ target_ver="$(npm view "@castlekit/castle@${CASTLE_VERSION}" version 2>/dev/null || true)"
608
+
609
+ if [[ -n "$current_ver" && -n "$target_ver" && "$current_ver" == "$target_ver" && -f "$HOME/.castle/castle.json" ]]; then
610
+ echo -e "${SUCCESS}✓${NC} Castle ${INFO}${current_ver}${NC} already installed and configured"
611
+ echo -e "${MUTED}Nothing to do. Run ${INFO}castle setup${NC} to reconfigure.${NC}"
612
+ return 0
613
+ fi
614
+
615
+ echo -e "${WARN}→${NC} Existing Castle installation detected"
605
616
  fi
606
617
 
607
618
  # Step 1: Homebrew (macOS only)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@castlekit/castle",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "The multi-agent workspace",
5
5
  "type": "module",
6
6
  "bin": {