@nitsan-ai/ragsuite-test 0.1.2 → 0.1.4

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,86 +1,69 @@
1
1
  # RAGSuite test CLI (`ragsuite-test`)
2
2
 
3
- > **TESTING PACKAGE** temporary. Final public CLI will be `ragsuite` later.
4
-
5
- **One-command public install** (GitHub repo must be public):
3
+ npm-style install: **init once**, then **start / update / stop** no `--repo-root` every time.
6
4
 
7
5
  ```bash
8
6
  npm install -g @nitsan-ai/ragsuite-test
9
- ragsuite-test init
10
- ragsuite-test start --repo-root ~/ragsuite-test --detach
11
- ```
12
7
 
13
- That clones `https://github.com/nitsan-ai/RAGSUITE.git` into `~/ragsuite-test`, writes `.env` (no SMTP/LLM questions), and picks **Docker** if the daemon is up, otherwise **native**.
8
+ # First time only
9
+ ragsuite-test init
10
+ # or without app source on disk:
11
+ # ragsuite-test init --from-images
14
12
 
15
- - API: http://localhost:9090
16
- - Web (Docker): http://localhost:9091
17
- - Web (native): http://localhost:9191
13
+ ragsuite-test start
14
+ # API http://localhost:9090 · Web http://localhost:9091
18
15
 
19
- ```bash
20
- ragsuite-test stop --repo-root ~/ragsuite-test
16
+ ragsuite-test stop
21
17
  ```
22
18
 
23
- ## Prerequisites
19
+ Active install is remembered in `~/.ragsuite-test/config.json` (default app dir: `~/ragsuite-test`).
24
20
 
25
- | Need | Required? | How |
26
- |------|-----------|-----|
27
- | Node.js 18+ | Yes | https://nodejs.org · `brew install node` |
28
- | git | Yes | `xcode-select --install` · `brew install git` |
29
- | Docker | No | Optional; without it init uses native (needs Postgres `:5436` + Redis `:6382`) |
21
+ ---
30
22
 
31
- Real SMTP belongs only in local `.env` (gitignored). Init never asks for it.
32
-
33
- ## Advanced (optional)
23
+ ## Upgrade (keeps data & `.env`)
34
24
 
35
25
  ```bash
36
- # Force native / docker
37
- ragsuite-test init --mode native
38
- ragsuite-test init --mode docker
39
-
40
- # Custom install dir
41
- ragsuite-test init --install-dir ~/my-ragsuite
42
-
43
- # Existing checkout (CI / developers)
44
- ragsuite-test init --repo-root /path/to/RAGSUITE
26
+ npm install -g @nitsan-ai/ragsuite-test@latest
27
+ ragsuite-test update
28
+ ragsuite-test start
45
29
  ```
46
30
 
47
- ## Ports
31
+ | Install type | What `update` does |
32
+ |--------------|--------------------|
33
+ | Git (`init`) | `git pull` in `~/ragsuite-test` |
34
+ | Images (`init --from-images`) | `docker compose pull` + recreate containers |
48
35
 
49
- | Service | Port |
50
- |---------|------|
51
- | API | 9090 |
52
- | Web (Docker) | 9091 |
53
- | Web (native Expo) | 9191 |
54
- | Postgres | 5436 |
55
- | Redis | 6382 |
36
+ **Never** run `docker compose down -v` or `init --force` unless you mean to wipe data.
56
37
 
57
- Busy ports: `ragsuite-test stop --repo-root ~/ragsuite-test` · `lsof -nP -iTCP:9090 -sTCP:LISTEN`
38
+ If you already installed and run `init` again it says **Already installed** and exits OK (does not wipe).
58
39
 
59
- ## Commands
40
+ ---
60
41
 
61
- | Command | Behavior |
62
- |---------|----------|
63
- | `init` | Clone public repo → `.env` + config (default) |
64
- | `start` / `stop` | Docker or native scripts |
65
- | `doctor` | Health / prereq hints |
66
- | `logs` / `update` / `version` | As before |
42
+ ## Which first-time install?
67
43
 
68
- ## Publish
44
+ | | **Git source** `init` | **Images** `init --from-images` |
45
+ |--|----------------------|----------------------------------|
46
+ | App code on disk | Yes | No |
47
+ | Docker | Optional (`--mode native`) | Required |
48
+ | Best for | Dev / native | Testers / “just run it” |
69
49
 
70
- See [PUBLISH.md](./PUBLISH.md).
50
+ ---
71
51
 
72
- ```bash
73
- cd cli && npm run prepublish:check
74
- # Only when approved:
75
- RAGSUITE_TEST_ALLOW_PUBLISH=1 npm publish --access public
76
- ```
52
+ ## Commands
77
53
 
78
- ## Windows
54
+ | Command | Purpose |
55
+ |---------|---------|
56
+ | `init` | First-time install |
57
+ | `start` | Start app (uses saved install path) |
58
+ | `stop` | Stop app (volumes kept) |
59
+ | `update` | Upgrade code/images in place |
60
+ | `doctor` | Health / prereqs |
61
+ | `version` | CLI version |
79
62
 
80
- Use WSL or Git Bash.
63
+ ## Ports
81
64
 
82
- ## Tests
65
+ API **9090** · Web **9091** (Docker/images) · Web **9191** (native) · Postgres **5436** · Redis **6382**
83
66
 
84
- ```bash
85
- cd cli && npm test && npm run prepublish:check
86
- ```
67
+ ## Publish (maintainers)
68
+
69
+ See [PUBLISH.md](./PUBLISH.md). Also publish GHCR images before `--from-images` works for others.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@nitsan-ai/ragsuite-test",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": false,
5
- "description": "RAGSuite testing CLI (ragsuite-test) public install: npm i -g && ragsuite-test init (clones GitHub). Temporary name before public ragsuite release.",
5
+ "description": "RAGSuite testing CLI — init once, then start/update/stop (npm-style).",
6
6
  "main": "src/index.js",
7
7
  "bin": {
8
8
  "ragsuite-test": "./bin/ragsuite-test.js"
@@ -10,6 +10,7 @@
10
10
  "files": [
11
11
  "bin/",
12
12
  "src/",
13
+ "templates/",
13
14
  "package.json",
14
15
  "README.md",
15
16
  "LICENSE"
@@ -2,22 +2,22 @@
2
2
 
3
3
  const path = require('path');
4
4
  const { resolveMode } = require('../utils/config');
5
- const { resolveRepoRoot } = require('../utils/paths');
5
+ const { resolveRepoRoot, isImagesInstall } = require('../utils/paths');
6
6
  const { runScript } = require('../utils/spawn');
7
7
 
8
8
  const name = 'doctor';
9
- const summary = 'Run scripts/doctor.sh prerequisite checks';
9
+ const summary = 'Run doctor checks (git or images install)';
10
10
 
11
11
  function help() {
12
12
  return `Usage: ragsuite-test doctor [options]
13
13
 
14
- Runs scripts/doctor.sh. Mode selects which checklist gates the exit code
15
- (RAGSUITE_MODE / config / --mode). Both mode sections are still printed.
14
+ Git install: scripts/doctor.sh
15
+ Images install: scripts/docker-doctor-images.sh
16
16
 
17
17
  Options:
18
- --mode <docker|native> Gate exit on this mode (default: docker)
19
- --repo-root <path> Monorepo root
20
- --dry-run Print the script path and exit 0
18
+ --mode <docker|native> Git install gate
19
+ --repo-root <path>
20
+ --dry-run
21
21
  `;
22
22
  }
23
23
 
@@ -26,12 +26,16 @@ function run(ctx) {
26
26
  cwd: ctx.cwd,
27
27
  repoRootFlag: ctx.globals.repoRoot,
28
28
  });
29
- const mode = resolveMode({
30
- flagMode: ctx.globals.mode,
31
- repoRoot,
32
- env: ctx.env,
33
- });
34
- return runScript(repoRoot, path.join('scripts', 'doctor.sh'), [], {
29
+ const images = isImagesInstall(repoRoot);
30
+ const mode = images
31
+ ? 'docker'
32
+ : resolveMode({
33
+ flagMode: ctx.globals.mode,
34
+ repoRoot,
35
+ env: ctx.env,
36
+ });
37
+ const script = images ? 'docker-doctor-images.sh' : 'doctor.sh';
38
+ return runScript(repoRoot, path.join('scripts', script), [], {
35
39
  dryRun: ctx.globals.dryRun,
36
40
  env: { ...ctx.env, RAGSUITE_MODE: mode },
37
41
  });
@@ -1,14 +1,22 @@
1
1
  'use strict';
2
2
 
3
3
  const path = require('path');
4
- const { writeConfig, CONFIG_DIR } = require('../utils/config');
5
- const { ensureRagsuiteDir, resolveRepoRoot } = require('../utils/paths');
4
+ const { writeConfig, CONFIG_DIR, readConfig } = require('../utils/config');
5
+ const {
6
+ ensureRagsuiteDir,
7
+ resolveRepoRoot,
8
+ isImagesInstall,
9
+ looksLikeRepoRoot,
10
+ isDirEmptyOrMissing,
11
+ } = require('../utils/paths');
12
+ const { setActiveInstall } = require('../utils/global-config');
6
13
  const { assertSupportedInstall, installHint } = require('../utils/distribution');
7
14
  const {
8
15
  DEFAULT_GIT_URL,
9
16
  defaultInstallDir,
10
17
  cloneGitToInstallDir,
11
18
  } = require('../utils/git-install');
19
+ const { materializeImagesInstall } = require('../utils/images-install');
12
20
  const { detectOs } = require('../utils/os-detect');
13
21
  const {
14
22
  printPrereqs,
@@ -20,57 +28,76 @@ const {
20
28
  writeInstallEnv,
21
29
  } = require('../utils/env-file');
22
30
  const { assertPortsFree } = require('../utils/port');
23
- const { info, error } = require('../utils/log');
31
+ const { info, warn, error } = require('../utils/log');
24
32
 
25
33
  const name = 'init';
26
- const summary = 'One-command install: clone public GitHub repo .env + config';
34
+ const summary = 'First-time install (git or images). Safe if already installed.';
27
35
 
28
- function printSuccess(repoRoot, mode) {
36
+ function printSuccess(repoRoot, mode, source) {
29
37
  info('');
30
38
  info('=== Install complete ===');
31
39
  info(` Install dir : ${repoRoot}`);
40
+ info(` Source : ${source}`);
32
41
  info(` Mode : ${mode}`);
33
- info(` Config : ${path.join(repoRoot, CONFIG_DIR, 'config.json')}`);
34
42
  info('');
35
43
  info(' API http://localhost:9090');
36
- if (mode === 'native') {
37
- info(' Web http://localhost:9191 (Expo web — native mode)');
44
+ if (source === 'images' || mode === 'docker') {
45
+ info(' Web http://localhost:9091');
38
46
  } else {
39
- info(' Web http://localhost:9091 (Docker nginx)');
47
+ info(' Web http://localhost:9191');
40
48
  }
41
49
  info('');
42
- info('Next steps:');
43
- info(` 1. ragsuite-test start --repo-root ${repoRoot}${mode === 'docker' ? ' --detach' : ''}`);
44
- if (mode === 'native') {
45
- info(' 2. Open http://localhost:9191 and bootstrap the first admin');
46
- } else {
47
- info(' 2. Open http://localhost:9091 and bootstrap the first admin');
48
- }
49
- info(` 3. ragsuite-test doctor --repo-root ${repoRoot}`);
50
+ info('Next (no --repo-root needed):');
51
+ info(' ragsuite-test start');
52
+ info(' ragsuite-test stop');
53
+ info(' ragsuite-test update # later upgrades, keeps data');
50
54
  info('');
51
55
  info(installHint());
52
56
  }
53
57
 
58
+ function printAlreadyInstalled(repoRoot) {
59
+ const cfg = readConfig(repoRoot);
60
+ setActiveInstall(repoRoot);
61
+ info('');
62
+ info('=== Already installed ===');
63
+ info(` ${repoRoot}`);
64
+ if (cfg) info(` source=${cfg.source} mode=${cfg.mode}`);
65
+ info('');
66
+ info('Use:');
67
+ info(' ragsuite-test start # run the app');
68
+ info(' ragsuite-test update # upgrade code/images (keeps .env + data)');
69
+ info(' ragsuite-test stop');
70
+ info('');
71
+ info('Do NOT run init --force unless you intend to wipe and reinstall.');
72
+ }
73
+
74
+ function isInitializedInstall(dir) {
75
+ return looksLikeRepoRoot(dir) && Boolean(readConfig(dir));
76
+ }
77
+
54
78
  function help() {
55
79
  return `Usage: ragsuite-test init [options]
56
80
 
57
- Simple public install (default — no questions):
58
- clones ${DEFAULT_GIT_URL}
59
- into ~/ragsuite-test
60
- picks docker mode if Docker is running, otherwise native
61
- writes .env (JWT auto; SMTP/LLM from .env.example — no prompts)
62
-
63
- ragsuite-test init
64
- ragsuite-test start --repo-root ~/ragsuite-test --detach
65
-
66
- Advanced (optional):
67
- --from-git [url] Clone this URL instead of the default
68
- --repo-root <path> Use an existing checkout (no clone)
69
- --install-dir <path> Clone target (default: ~/ragsuite-test)
70
- --mode <docker|native> Force run mode
71
- --force Overwrite non-empty install dir / existing .env
72
- --yes, -y Same defaults; for scripts/CI
73
- --llm-api-key / --smtp-* Optional overrides (usually not needed)
81
+ First-time setup only (npm-style):
82
+
83
+ ragsuite-test init # git clone → ~/ragsuite-test
84
+ ragsuite-test init --from-images # Docker images only (no app source)
85
+
86
+ If already installed, init exits OK and tells you to start/update.
87
+
88
+ Day-to-day:
89
+ ragsuite-test start
90
+ ragsuite-test update
91
+ ragsuite-test stop
92
+
93
+ Options:
94
+ --from-images Images-only install
95
+ --from-git [url] Clone URL (default public repo)
96
+ --repo-root <path> Point at an existing checkout
97
+ --install-dir <path> Default: ~/ragsuite-test
98
+ --mode <docker|native> Git install only
99
+ --force Wipe install dir and reinstall (destructive)
100
+ --yes, -y Non-interactive
74
101
  `;
75
102
  }
76
103
 
@@ -91,9 +118,10 @@ function resolveLlmOverride(g, env) {
91
118
  return null;
92
119
  }
93
120
 
94
- function countSources(fromGit, repoRootFlag) {
121
+ function countSources(fromGit, fromImages, repoRootFlag) {
95
122
  let n = 0;
96
123
  if (fromGit !== null && fromGit !== undefined) n += 1;
124
+ if (fromImages) n += 1;
97
125
  if (repoRootFlag) n += 1;
98
126
  return n;
99
127
  }
@@ -103,12 +131,12 @@ async function run(ctx) {
103
131
  const g = ctx.globals;
104
132
 
105
133
  if (g.fromRelease) {
106
- error('--from-release is not supported. Use default init, --from-git, or --repo-root.');
134
+ error('--from-release is not supported.');
107
135
  return 1;
108
136
  }
109
137
 
110
- if (countSources(g.fromGit, g.repoRoot) > 1) {
111
- error('Use only one of --from-git or --repo-root');
138
+ if (countSources(g.fromGit, g.fromImages, g.repoRoot) > 1) {
139
+ error('Use only one of --from-images, --from-git, or --repo-root');
112
140
  return 1;
113
141
  }
114
142
 
@@ -116,20 +144,60 @@ async function run(ctx) {
116
144
  info(`OS: ${osInfo.label} (${osInfo.platform})`);
117
145
 
118
146
  let fromGit = g.fromGit;
147
+ let fromImages = Boolean(g.fromImages);
119
148
  let repoRootFlag = g.repoRoot;
120
- const installDir = g.installDir || defaultInstallDir();
149
+ const installDir = path.resolve(g.installDir || defaultInstallDir());
150
+
151
+ // Existing configured install → friendly no-op (like other npm CLIs)
152
+ if (!g.force) {
153
+ if (repoRootFlag && isInitializedInstall(path.resolve(repoRootFlag))) {
154
+ printAlreadyInstalled(path.resolve(repoRootFlag));
155
+ return 0;
156
+ }
157
+ if (!repoRootFlag && isInitializedInstall(installDir)) {
158
+ printAlreadyInstalled(installDir);
159
+ return 0;
160
+ }
161
+ }
121
162
 
122
- // Default public path: no menus, no path prompts — clone GitHub → ~/ragsuite-test
123
- if (fromGit === null && !repoRootFlag) {
163
+ if (!fromImages && fromGit === null && !repoRootFlag) {
124
164
  fromGit = '';
125
- info(`Install: clone ${DEFAULT_GIT_URL}`);
126
- info(`Target: ${path.resolve(installDir)}`);
165
+ info(`First-time install (git): ${DEFAULT_GIT_URL}`);
166
+ info(` ${installDir}`);
167
+ }
168
+ if (fromImages) {
169
+ info(`First-time install (images, no source): → ${installDir}`);
127
170
  }
128
171
 
129
- const mode = g.mode || resolvePreferredMode(null);
130
- info(`Run mode: ${mode} (Docker optional — pass --mode docker|native to force)`);
172
+ // Checkout already on disk but never inited → wire config/.env, do not re-clone
173
+ if (
174
+ !g.force &&
175
+ !repoRootFlag &&
176
+ !fromImages &&
177
+ looksLikeRepoRoot(installDir) &&
178
+ !readConfig(installDir)
179
+ ) {
180
+ info(`Found existing source at ${installDir} — finishing setup (no re-clone).`);
181
+ repoRootFlag = installDir;
182
+ fromGit = null;
183
+ }
184
+
185
+ if (!g.force && !isDirEmptyOrMissing(installDir) && !looksLikeRepoRoot(installDir) && !repoRootFlag) {
186
+ error(
187
+ [
188
+ `Directory exists but is not a RAGSuite install: ${installDir}`,
189
+ ' Fix leftover files, or:',
190
+ ' ragsuite-test init --force # wipe & reinstall (destructive)',
191
+ ' ragsuite-test init --install-dir ~/ragsuite-test-2',
192
+ ].join('\n'),
193
+ );
194
+ return 1;
195
+ }
131
196
 
132
- const needGit = fromGit !== null && fromGit !== undefined;
197
+ let mode = fromImages ? 'docker' : g.mode || resolvePreferredMode(null);
198
+ info(`Run mode: ${mode}`);
199
+
200
+ const needGit = !fromImages && fromGit !== null && fromGit !== undefined;
133
201
  if (!printPrereqs(mode, { needGit })) {
134
202
  error('Fix prerequisites above and re-run init');
135
203
  return 1;
@@ -137,7 +205,6 @@ async function run(ctx) {
137
205
 
138
206
  const jwtSecret = generateJwtSecret();
139
207
  info(`JWT_SECRET_KEY will be auto-generated (…${jwtSecret.slice(-8)})`);
140
- info('SMTP + LLM: from .env.example into .env (no prompts).');
141
208
 
142
209
  const llmKey = resolveLlmOverride(g, ctx.env);
143
210
  const smtp = optionalSmtpOverrides(g, ctx.env);
@@ -155,22 +222,22 @@ async function run(ctx) {
155
222
  let source = 'checkout';
156
223
 
157
224
  try {
158
- if (fromGit !== null && fromGit !== undefined) {
225
+ if (fromImages) {
226
+ const result = materializeImagesInstall({ installDir, force: g.force });
227
+ repoRoot = result.repoRoot;
228
+ source = 'images';
229
+ } else if (fromGit !== null && fromGit !== undefined) {
159
230
  const url = fromGit === '' ? DEFAULT_GIT_URL : fromGit;
160
- info(`Cloning ${url} ${path.resolve(installDir)}`);
161
- const result = cloneGitToInstallDir(url, {
162
- installDir,
163
- force: g.force,
164
- });
231
+ const result = cloneGitToInstallDir(url, { installDir, force: g.force });
165
232
  repoRoot = result.repoRoot;
166
233
  source = 'git';
167
- info(`Installed app at ${repoRoot}`);
168
234
  } else {
169
235
  repoRoot = resolveRepoRoot({
170
236
  cwd: ctx.cwd,
171
237
  repoRootFlag,
172
238
  env: ctx.env,
173
239
  });
240
+ if (isImagesInstall(repoRoot)) source = 'images';
174
241
  }
175
242
  } catch (err) {
176
243
  error(err.message || String(err));
@@ -184,6 +251,7 @@ async function run(ctx) {
184
251
  repoRoot,
185
252
  installDir: repoRoot,
186
253
  });
254
+ setActiveInstall(repoRoot);
187
255
 
188
256
  try {
189
257
  writeInstallEnv(repoRoot, {
@@ -193,17 +261,19 @@ async function run(ctx) {
193
261
  smtp,
194
262
  });
195
263
  } catch (err) {
196
- if (err.code === 'ENV_EXISTS' || err.code === 'ENV_INCOMPLETE') {
264
+ if (err.code === 'ENV_EXISTS') {
265
+ warn('.env already present — keeping it (use --force only if you want to regenerate).');
266
+ } else if (err.code === 'ENV_INCOMPLETE') {
197
267
  error(err.message);
198
268
  return 1;
269
+ } else {
270
+ error(err.message || String(err));
271
+ return 1;
199
272
  }
200
- error(err.message || String(err));
201
- return 1;
202
273
  }
203
274
 
204
- info(`Wrote ${path.join(repoRoot, CONFIG_DIR, 'config.json')}`);
205
- info(` mode=${cfg.mode} source=${cfg.source}`);
206
- printSuccess(repoRoot, mode);
275
+ info(`Config: mode=${cfg.mode} source=${cfg.source}`);
276
+ printSuccess(repoRoot, mode, source);
207
277
  return 0;
208
278
  }
209
279
 
@@ -2,25 +2,25 @@
2
2
 
3
3
  const path = require('path');
4
4
  const { resolveMode } = require('../utils/config');
5
- const { resolveRepoRoot } = require('../utils/paths');
5
+ const { resolveRepoRoot, isImagesInstall } = require('../utils/paths');
6
6
  const { runScript } = require('../utils/spawn');
7
7
  const { assertApiPortFree } = require('../utils/port');
8
8
  const { error } = require('../utils/log');
9
9
 
10
10
  const name = 'start';
11
- const summary = 'Start stack via docker-start.sh or native-start.sh';
11
+ const summary = 'Start stack (git scripts or images pull-only)';
12
12
 
13
13
  function help() {
14
- return `Usage: ragsuite-test start [options] [-- script-args...]
14
+ return `Usage: ragsuite-test start [options]
15
15
 
16
- Calls scripts/docker-start.sh (mode=docker) or scripts/native-start.sh (mode=native).
17
- Does not reimplement compose logic. Extra args after -- are forwarded (e.g. --detach).
16
+ Git install: scripts/docker-start.sh or native-start.sh
17
+ Images install (--from-images): scripts/docker-start-images.sh (pull, no build)
18
18
 
19
19
  Options:
20
- --mode <docker|native> Override config / RAGSUITE_MODE
21
- --repo-root <path> Monorepo root
22
- --dry-run Print the script path and exit 0
23
- --detach, -d Forwarded to docker-start.sh (also accepted without --)
20
+ --mode <docker|native> Override (git install; images always docker)
21
+ --repo-root <path> Install root
22
+ --dry-run Print script path and exit 0
23
+ --detach, -d Detached start
24
24
  `;
25
25
  }
26
26
 
@@ -29,14 +29,22 @@ async function run(ctx) {
29
29
  cwd: ctx.cwd,
30
30
  repoRootFlag: ctx.globals.repoRoot,
31
31
  });
32
- const mode = resolveMode({
33
- flagMode: ctx.globals.mode,
34
- repoRoot,
35
- env: ctx.env,
36
- });
32
+ const images = isImagesInstall(repoRoot);
33
+ const mode = images
34
+ ? 'docker'
35
+ : resolveMode({
36
+ flagMode: ctx.globals.mode,
37
+ repoRoot,
38
+ env: ctx.env,
39
+ });
40
+
41
+ let scriptName;
42
+ if (images) {
43
+ scriptName = 'docker-start-images.sh';
44
+ } else {
45
+ scriptName = mode === 'native' ? 'native-start.sh' : 'docker-start.sh';
46
+ }
37
47
 
38
- const scriptName = mode === 'native' ? 'native-start.sh' : 'docker-start.sh';
39
- const rel = path.join('scripts', scriptName);
40
48
  const forward = normalizeStartArgs(ctx.commandArgs);
41
49
 
42
50
  if (!ctx.globals.dryRun) {
@@ -48,7 +56,7 @@ async function run(ctx) {
48
56
  }
49
57
  }
50
58
 
51
- return runScript(repoRoot, rel, forward, {
59
+ return runScript(repoRoot, path.join('scripts', scriptName), forward, {
52
60
  dryRun: ctx.globals.dryRun,
53
61
  env: { ...ctx.env, RAGSUITE_MODE: mode },
54
62
  });
@@ -2,22 +2,23 @@
2
2
 
3
3
  const path = require('path');
4
4
  const { resolveMode } = require('../utils/config');
5
- const { resolveRepoRoot } = require('../utils/paths');
5
+ const { resolveRepoRoot, isImagesInstall } = require('../utils/paths');
6
6
  const { runScript } = require('../utils/spawn');
7
7
 
8
8
  const name = 'stop';
9
- const summary = 'Stop stack via docker-stop.sh or native-stop.sh';
9
+ const summary = 'Stop stack (git scripts or images pull-only)';
10
10
 
11
11
  function help() {
12
12
  return `Usage: ragsuite-test stop [options]
13
13
 
14
- Calls scripts/docker-stop.sh or scripts/native-stop.sh based on mode.
15
- Never wipes Docker volumes (same as docker-stop.sh).
14
+ Git install: docker-stop.sh / native-stop.sh
15
+ Images install: docker-stop-images.sh
16
+ Never wipes Docker volumes.
16
17
 
17
18
  Options:
18
- --mode <docker|native> Override config / RAGSUITE_MODE
19
- --repo-root <path> Monorepo root
20
- --dry-run Print the script path and exit 0
19
+ --mode <docker|native> Override (git install)
20
+ --repo-root <path> Install root
21
+ --dry-run Print script path and exit 0
21
22
  `;
22
23
  }
23
24
 
@@ -26,12 +27,21 @@ function run(ctx) {
26
27
  cwd: ctx.cwd,
27
28
  repoRootFlag: ctx.globals.repoRoot,
28
29
  });
29
- const mode = resolveMode({
30
- flagMode: ctx.globals.mode,
31
- repoRoot,
32
- env: ctx.env,
33
- });
34
- const scriptName = mode === 'native' ? 'native-stop.sh' : 'docker-stop.sh';
30
+ const images = isImagesInstall(repoRoot);
31
+ const mode = images
32
+ ? 'docker'
33
+ : resolveMode({
34
+ flagMode: ctx.globals.mode,
35
+ repoRoot,
36
+ env: ctx.env,
37
+ });
38
+
39
+ const scriptName = images
40
+ ? 'docker-stop-images.sh'
41
+ : mode === 'native'
42
+ ? 'native-stop.sh'
43
+ : 'docker-stop.sh';
44
+
35
45
  return runScript(repoRoot, path.join('scripts', scriptName), [], {
36
46
  dryRun: ctx.globals.dryRun,
37
47
  env: { ...ctx.env, RAGSUITE_MODE: mode },