@sdods/cli 0.19.0 → 0.21.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.
@@ -0,0 +1,62 @@
1
+ import { existsSync, realpathSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { dirname, join, resolve, sep } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ const INSTALLER_GIT_HINT = 'Re-run the install script, which updates its checkout in place: curl -fsSL https://sdods.com/install.sh | sh -s -- --source git';
6
+ /** Directory of the @sdods/cli package this code is running from (src/ and dist/ sit one below it). */
7
+ export function runningPackageDir() {
8
+ const here = dirname(fileURLToPath(import.meta.url));
9
+ const dir = resolve(here, '..');
10
+ try {
11
+ return realpathSync(dir);
12
+ }
13
+ catch {
14
+ return dir;
15
+ }
16
+ }
17
+ /** Forward slashes, so one set of markers works on macOS, Linux and Windows paths. */
18
+ function norm(p) {
19
+ return p.split(sep).join('/').replace(/\\/g, '/');
20
+ }
21
+ function within(child, parent) {
22
+ const c = norm(child).replace(/\/+$/, '');
23
+ const p = norm(parent).replace(/\/+$/, '');
24
+ return c === p || c.startsWith(`${p}/`);
25
+ }
26
+ /**
27
+ * Classify an installed package directory. Specific markers are checked before generic ones: a
28
+ * Homebrew keg and an npx cache both contain `node_modules/@sdods/cli` too.
29
+ */
30
+ export function detectInstallMethod(packageDir, o) {
31
+ const p = norm(packageDir);
32
+ const home = o.sdodsHome ?? process.env.SDODS_HOME ?? join(homedir(), '.sdods');
33
+ const isGit = o.isGitCheckout ?? ((dir) => existsSync(join(dir, '.git')));
34
+ const make = (method, upgrade, hint) => ({
35
+ method,
36
+ packageDir,
37
+ upgrade,
38
+ hint,
39
+ });
40
+ if (within(packageDir, join(o.rootDir, 'node_modules')))
41
+ return make('project', null, 'Upgraded with the project package step below.');
42
+ if (/\/_npx\//.test(p))
43
+ return make('npx', null, 'npx fetches @sdods/cli on each run; use `npx -y @sdods/cli@latest`.');
44
+ if (/\/Cellar\/sdods\//i.test(p) || /\/(?:homebrew|linuxbrew)\/.*\/libexec\//i.test(p))
45
+ return make('homebrew', ['brew', 'upgrade', 'sdods'], 'brew upgrade sdods');
46
+ if (/\/\.bun\/install\/global\//.test(p))
47
+ return make('bun', ['bun', 'add', '-g', '@sdods/cli@latest'], 'bun add -g @sdods/cli@latest');
48
+ if (/\/pnpm\/global\//i.test(p) || /\/\.pnpm\/@sdods\+cli@/.test(p))
49
+ return make('pnpm', ['pnpm', 'add', '-g', '@sdods/cli@latest'], 'pnpm add -g @sdods/cli@latest');
50
+ if (/\/node_modules\/@sdods\/cli$/.test(p.replace(/\/+$/, '')))
51
+ return make('npm', ['npm', 'install', '-g', '@sdods/cli@latest'], 'npm install -g @sdods/cli@latest');
52
+ // packages/cli inside a clone of the monorepo
53
+ const checkout = resolve(packageDir, '..', '..');
54
+ if (/\/packages\/cli$/.test(p.replace(/\/+$/, ''))) {
55
+ if (within(checkout, join(home, 'app')))
56
+ return make('installer-git', null, INSTALLER_GIT_HINT);
57
+ if (isGit(checkout))
58
+ return make('checkout', null, `Development checkout: git -C ${checkout} pull, then install.`);
59
+ }
60
+ return make('npm', ['npm', 'install', '-g', '@sdods/cli@latest'], 'npm install -g @sdods/cli@latest');
61
+ }
62
+ //# sourceMappingURL=install-method.js.map
@@ -27,6 +27,13 @@ export declare const SKILL_TARGETS: {
27
27
  };
28
28
  export type SkillTarget = keyof typeof SKILL_TARGETS;
29
29
  export declare const DEFAULT_SKILL_TARGETS: SkillTarget[];
30
+ /** The directory a target resolves to, under a project root or (with `global`) the home directory. */
31
+ export declare function skillTargetDir(base: string, target: SkillTarget, global?: boolean): string;
32
+ /**
33
+ * Targets that already hold at least one bundled SDODS skill. `sdods upgrade` refreshes exactly
34
+ * these, so it updates what someone installed without spreading skills into new folders.
35
+ */
36
+ export declare function installedSkillTargets(base: string, global?: boolean, source?: string): SkillTarget[];
30
37
  export interface InstallSkillsOptions {
31
38
  rootDir: string;
32
39
  targets?: SkillTarget[];
@@ -49,6 +49,18 @@ export const SKILL_TARGETS = {
49
49
  export const DEFAULT_SKILL_TARGETS = ['claude', 'agents'];
50
50
  /** GitHub Copilot's user-level directory is `~/.copilot/skills`, not `~/.github/skills`. */
51
51
  const GLOBAL_OVERRIDES = { copilot: '.copilot/skills' };
52
+ /** The directory a target resolves to, under a project root or (with `global`) the home directory. */
53
+ export function skillTargetDir(base, target, global = false) {
54
+ return join(base, (global && GLOBAL_OVERRIDES[target]) || SKILL_TARGETS[target]);
55
+ }
56
+ /**
57
+ * Targets that already hold at least one bundled SDODS skill. `sdods upgrade` refreshes exactly
58
+ * these, so it updates what someone installed without spreading skills into new folders.
59
+ */
60
+ export function installedSkillTargets(base, global = false, source) {
61
+ const names = listBundledSkills(source).map((s) => s.name);
62
+ return Object.keys(SKILL_TARGETS).filter((t) => names.some((n) => existsSync(join(skillTargetDir(base, t, global), n, 'SKILL.md'))));
63
+ }
52
64
  export function installSkills(o) {
53
65
  const available = listBundledSkills(o.source);
54
66
  const wanted = o.skills?.length ? o.skills : available.map((s) => s.name);
@@ -58,9 +70,9 @@ export function installSkills(o) {
58
70
  const base = o.global ? (o.home ?? homedir()) : o.rootDir;
59
71
  const results = [];
60
72
  for (const target of o.targets?.length ? o.targets : DEFAULT_SKILL_TARGETS) {
61
- const rel = (o.global && GLOBAL_OVERRIDES[target]) || SKILL_TARGETS[target];
73
+ const dir = skillTargetDir(base, target, o.global);
62
74
  for (const skill of available.filter((s) => wanted.includes(s.name))) {
63
- const path = join(base, rel, skill.name);
75
+ const path = join(dir, skill.name);
64
76
  const exists = existsSync(join(path, 'SKILL.md'));
65
77
  if (exists && !o.force) {
66
78
  results.push({ skill: skill.name, target, path, status: 'skipped' });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdods/cli",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "SDODS command line: run, record, analyze, agents, MCP server, scheduler and the web server.",
5
5
  "license": "Apache-2.0",
6
6
  "funding": "https://sdods.com/sponsor/",
@@ -26,8 +26,8 @@
26
26
  "node": ">=22"
27
27
  },
28
28
  "dependencies": {
29
- "@sdods/contracts": "0.19.0",
30
- "@sdods/core": "0.19.0",
29
+ "@sdods/contracts": "0.21.0",
30
+ "@sdods/core": "0.21.0",
31
31
  "commander": "^15.0.0",
32
32
  "execa": "^10.2.1",
33
33
  "picocolors": "^1.1.1",
@@ -36,12 +36,12 @@
36
36
  "zod": "^4.6.7",
37
37
  "@playwright/test": "^1.64.0",
38
38
  "playwright-core": "^1.64.0",
39
- "@sdods/integrations": "0.19.0",
40
- "@sdods/mcp": "0.19.0",
41
- "@sdods/agents": "0.19.0",
42
- "@sdods/db": "0.19.0",
39
+ "@sdods/integrations": "0.21.0",
40
+ "@sdods/mcp": "0.21.0",
41
+ "@sdods/agents": "0.21.0",
42
+ "@sdods/db": "0.21.0",
43
43
  "csv-parse": "^7.0.3",
44
- "@sdods/server": "0.19.0"
44
+ "@sdods/server": "0.21.0"
45
45
  },
46
46
  "homepage": "https://sdods.com",
47
47
  "bugs": {
@@ -101,7 +101,7 @@ Read the rows in order and take the first that matches:
101
101
  |---|---|---|
102
102
  | `package.json` says `sdods-monorepo` — **even if `sdods` is on PATH** | a **source checkout** | `bun run web:build`, then `bun run sdods serve --open` |
103
103
  | `sdods` on PATH, anywhere else | a **global install** (installer, npm, Homebrew) | `sdods serve --open` |
104
- | neither, Docker is running | the **container** | `docker run --rm -p 8080:8080 -v sdods-data:/data ghcr.io/yarlisaisolutions/sdods-server` |
104
+ | neither, Docker is running | the **container** | `docker run --rm -p 8080:8080 -v sdods-data:/data sdods/sdods-server` |
105
105
  | you want no terminal at all | the **desktop app** | install from <https://sdods.com/download>, launch it |
106
106
 
107
107
  > **Inside a checkout, always use `bun run sdods`, never the global `sdods`.** The two drift, and
@@ -155,19 +155,14 @@ The CLI is identical on every OS; only the shell and the paths differ.
155
155
  | Windows (PowerShell) | `sdods serve --open` |
156
156
  | Windows (cmd) | `sdods serve --open` — the installer writes both `sdods.cmd` and `sdods.ps1` |
157
157
  | WSL | use the Linux command inside WSL, not the PowerShell one |
158
- | Docker (any host) | `docker run --rm -p 8080:8080 -v sdods-data:/data ghcr.io/yarlisaisolutions/sdods-server` |
158
+ | Docker (any host) | `docker run --rm -p 8080:8080 -v sdods-data:/data sdods/sdods-server` |
159
159
 
160
160
  Defaults: **`http://127.0.0.1:4444`** for the CLI, **`http://localhost:8080`** for Docker (the
161
161
  image binds `0.0.0.0:8080`). `--open` launches the browser (`open` / `start` / `xdg-open` by
162
162
  platform). `--host 0.0.0.0` exposes it on the LAN — only do that deliberately.
163
163
 
164
- The image is published for `linux/amd64` and `linux/arm64`, so the same command runs natively on
165
- Apple silicon and arm64 servers. Tags up to `0.9.0` carry amd64 only, and Docker refuses that pull
166
- on arm64 rather than emulating; for those, add the platform flag:
167
-
168
- ```bash
169
- docker run --rm --platform linux/amd64 -p 8080:8080 -v sdods-data:/data ghcr.io/yarlisaisolutions/sdods-server:0.9.0
170
- ```
164
+ The image is published on Docker Hub for `linux/amd64` and `linux/arm64` (tags from `0.20.0`), so
165
+ the same command runs natively on Apple silicon and arm64 servers.
171
166
 
172
167
  If the run must come from a source checkout, `bun run web:build` is not optional — see
173
168
  Troubleshooting.