@descix/cli 3.0.0 → 3.0.2
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 +7 -5
- package/agent-assets/guides/dynamic-site-pattern.md +1 -2
- package/agent-assets/guides/knowledge-only-pattern.md +2 -3
- package/agent-assets/guides/microservice-pattern.md +2 -3
- package/agent-assets/guides/static-site-pattern.md +2 -3
- package/agent-assets/instructions/onboarding-flow.md +51 -69
- package/agent-assets/instructions/standard-folder-structure.md +14 -11
- package/agent-assets/reference/app-creation.md +9 -8
- package/agent-assets/reference/architecture.md +14 -17
- package/agent-assets/reference/local-dev.md +36 -32
- package/agent-assets/reference/mcp-integration.md +8 -9
- package/agent-assets/reference/workspace-config.md +59 -25
- package/bin/descix.js +79 -18
- package/lib/commands/config.js +18 -0
- package/lib/descix-command.js +43 -0
- package/lib/verb-invokes.generated.json +3 -0
- package/lib/workspace-config.js +101 -4
- package/package.json +3 -2
|
@@ -303,18 +303,17 @@ User asks question
|
|
|
303
303
|
When `--scope project` is used:
|
|
304
304
|
|
|
305
305
|
1. CLI/MCP loads `workspace.json`
|
|
306
|
-
2. Extracts
|
|
307
|
-
3. Filters `tell_me_how` results to only tools applicable to
|
|
306
|
+
2. Extracts the app this repo holds (`env.app`)
|
|
307
|
+
3. Filters `tell_me_how` results to only tools applicable to that app
|
|
308
308
|
4. Returns focused recommendations
|
|
309
309
|
|
|
310
310
|
```javascript
|
|
311
|
-
// workspace.json (
|
|
311
|
+
// workspace.json (v3 single-app)
|
|
312
312
|
{
|
|
313
|
+
"version": "3.0",
|
|
313
314
|
"env": {
|
|
314
|
-
"apiUrl": "https://
|
|
315
|
-
"
|
|
316
|
-
{ "appId": "daita-agent", "communityId": "daita", "localPath": ".", "kbId": "General" }
|
|
317
|
-
]
|
|
315
|
+
"apiUrl": "https://descix.net",
|
|
316
|
+
"app": { "appId": "daita-agent", "communityId": "daita", "kbId": "General" }
|
|
318
317
|
}
|
|
319
318
|
}
|
|
320
319
|
|
|
@@ -338,11 +337,11 @@ descix tell-me-how "How do I sync my knowledge base?"
|
|
|
338
337
|
|
|
339
338
|
### 6.2 workspace.json from Hydration
|
|
340
339
|
|
|
341
|
-
`descix config init --env …` creates `workspace.json`; `descix app init`
|
|
340
|
+
`descix config init --env …` creates `workspace.json`; `descix app init` (from the repo root) writes the app's `env.app`; no verb writes `driveConfig.base_folder_id` — set it in `.descix/workspace.json` when you use `descix drive pull/push`:
|
|
342
341
|
|
|
343
342
|
```javascript
|
|
344
343
|
{
|
|
345
|
-
"env": { "apiUrl": "https://
|
|
344
|
+
"env": { "apiUrl": "https://descix.net", "app": { /* THE app this repo holds */ } },
|
|
346
345
|
"driveConfig": {
|
|
347
346
|
"base_folder_id": "..." // Only used by `descix drive pull/push` — unrelated to tell_me_how scope
|
|
348
347
|
}
|
|
@@ -1,33 +1,44 @@
|
|
|
1
1
|
# Workspace Configuration Guide
|
|
2
2
|
|
|
3
|
-
`.descix/workspace.json` is the CLI's only local configuration file. It is **version
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
`.descix/workspace.json` is the CLI's only local configuration file. It is **version 3.0
|
|
4
|
+
(single-app)**: one repo, one app, and the repo root IS the workspace root. It is written **only by
|
|
5
|
+
CLI verbs**, and the loader refuses the shapes that hand edits produce. This page is the schema and
|
|
6
|
+
the verb that owns each key.
|
|
6
7
|
|
|
7
8
|
---
|
|
8
9
|
|
|
9
|
-
## 1. Format (
|
|
10
|
+
## 1. Format (v3 — the only format)
|
|
11
|
+
|
|
12
|
+
An app repo's workspace holds exactly one app, `env.app`:
|
|
10
13
|
|
|
11
14
|
```json
|
|
12
15
|
{
|
|
13
|
-
"version": "
|
|
16
|
+
"version": "3.0",
|
|
14
17
|
"type": "workspace",
|
|
15
|
-
"workspaceRoot": "/path/to/
|
|
18
|
+
"workspaceRoot": "/path/to/my-app-repo",
|
|
16
19
|
"env": {
|
|
17
|
-
"environment": "
|
|
18
|
-
"apiUrl": "https://
|
|
20
|
+
"environment": "PROD",
|
|
21
|
+
"apiUrl": "https://descix.net",
|
|
19
22
|
"gateway": { "port": 5599 },
|
|
20
23
|
"devCerts": { "dir": "/Users/you/.descix/dev-certs-san" },
|
|
21
|
-
"powchUrl": "https://powch.
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
"powchUrl": "https://powch.descix.net",
|
|
25
|
+
"app": {
|
|
26
|
+
"appId": "egpt-mydocs",
|
|
27
|
+
"communityId": "egpt",
|
|
28
|
+
"kbId": "General",
|
|
29
|
+
"site": { "static": "site" },
|
|
30
|
+
"microservice": { "port": 4001 }
|
|
31
|
+
}
|
|
26
32
|
},
|
|
27
33
|
"driveConfig": { "base_folder_id": "1ABC..." }
|
|
28
34
|
}
|
|
29
35
|
```
|
|
30
36
|
|
|
37
|
+
Run `descix app init` **from the repo root** — it writes `env.app` and makes that directory the
|
|
38
|
+
workspace root. Inside another DeSciX workspace it refuses (`This directory is nested inside another
|
|
39
|
+
DeSciX workspace`) rather than registering into the parent: keep app repos as sibling checkouts. A
|
|
40
|
+
workspace that already holds a different app refuses a second one — that app gets its own repo.
|
|
41
|
+
|
|
31
42
|
| Key | Meaning | Owned by |
|
|
32
43
|
|---|---|---|
|
|
33
44
|
| `env.environment` | `DEV`, `DEMO` or `PROD` | `descix config init --env dev\|demo\|prod` |
|
|
@@ -36,29 +47,53 @@ is the schema and the verb that owns each key.
|
|
|
36
47
|
| `env.devCerts` | `dir`, or `cert` + `key` — the TLS pair for the gateway and every app behind it; unset means the SDK's shipped pair. Passkey sign-in needs it trusted: `descix dev-certs check`, then the human runs `descix dev-certs trust` (see `reference/local-dev.md` §3.3) | `descix config set-dev-certs --dir\|--cert\|--key\|--clear` |
|
|
37
48
|
| `env.powchUrl` | Powch's own origin (optional; Powch is cross-origin from the shell by design) | `descix config set-powch-url <url>` |
|
|
38
49
|
| `env.siteUrl` | the App Shell origin the gateway proxies `/` to (optional; default is the API origin) | `descix config set-site-url <url>` |
|
|
39
|
-
| `env.
|
|
40
|
-
| `env.platform` | the platform shell's own entry — platform contributors only; an app developer has none | platform runbook |
|
|
50
|
+
| `env.app` | THE app this repo holds, see below | `descix app init`, `app set-site`, `app set-port` |
|
|
41
51
|
| `driveConfig.base_folder_id` | the Drive base folder for `descix drive pull/push` | no verb; set it in `.descix/workspace.json` |
|
|
42
52
|
|
|
43
|
-
|
|
53
|
+
`env.app`:
|
|
44
54
|
|
|
45
55
|
| Field | Meaning | Written by |
|
|
46
56
|
|---|---|---|
|
|
47
57
|
| `appId` | the platform app id (`<community>-<short>`) | `descix app init` |
|
|
48
58
|
| `communityId` | the app's community; stored so context detection can name it | `descix app init` |
|
|
49
|
-
| `localPath` | the app directory, relative to `workspaceRoot` (`.` when the workspace root is the app) | `descix app init -p`, `descix app set-localpath` |
|
|
50
59
|
| `kbId` | the default knowledge base (default `General`) | `descix app init --kb` |
|
|
51
|
-
| `site.static` | a directory
|
|
60
|
+
| `site.static` | a directory relative to the workspace root (the repo root) served from disk at `/p/<appId>/` — `site`, `site/dist`, `.` (the root itself) or `../scratch`; an absolute path is refused at the write and on read | `descix app set-site --static <dir>` |
|
|
52
61
|
| `site.port` | a framework dev server the gateway proxies `/p/<appId>` to (no path rewrite) | `descix app set-site --port <n>` |
|
|
62
|
+
| `site.protocol` | `http` or `https` — the dev server's scheme (dev-server mode only; default `https`) | `descix app set-site --port <n> --protocol <scheme>` |
|
|
53
63
|
| `microservice.port` | the local port the gateway proxies `/s/<appId>` to; `descix microservice init` requires it | `descix app set-port -p <n>` |
|
|
54
64
|
|
|
55
65
|
Sites and services are started by you (or your framework); the CLI does not store or run a dev command.
|
|
56
66
|
|
|
67
|
+
### Gateway workspaces
|
|
68
|
+
|
|
69
|
+
A workspace that serves several local app repos behind one `descix serve` (platform contributors)
|
|
70
|
+
lists them under `env.gateway`, with paths relative to its own root:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
"gateway": { "port": 5599, "shell": "../platform-shell", "appRepos": ["../my-app", "../other-app"] }
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
| Key | Meaning | Verb |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `env.gateway.shell` | the repo whose app the gateway serves at `/` | `descix config gateway set-shell <repo\|none>` |
|
|
79
|
+
| `env.gateway.appRepos[]` | repos whose apps the gateway serves at `/p/<appId>` | `descix config gateway add-repo <repo>`, `descix config gateway remove-repo <repo>` |
|
|
80
|
+
|
|
81
|
+
Run the verbs from the gateway workspace's root; `<repo>` is relative to that root. Each listed repo
|
|
82
|
+
holds its OWN v3 workspace (`descix app init` in that repo), and that file is the sole authority on
|
|
83
|
+
its app's id, site and microservice — the gateway workspace never restates them. The verbs refuse,
|
|
84
|
+
writing nothing: a run in a single-app workspace (one that holds `env.app`), an absolute path, a
|
|
85
|
+
repo with no `.descix/workspace.json` or no `env.app`, a repo already listed (or already the
|
|
86
|
+
shell), one appId listed twice, and `remove-repo` of a repo that is not listed. `descix serve`
|
|
87
|
+
applies the same checks when it reads the file.
|
|
88
|
+
|
|
57
89
|
### Refused shapes
|
|
58
90
|
|
|
59
91
|
- A `communities` block with no `env` block — the v1 format — fails on load: `v1 workspace format is
|
|
60
|
-
not supported. Migrate to
|
|
92
|
+
not supported. Migrate to v3.` The file is left untouched; re-create it with `descix config init
|
|
61
93
|
--env …` and `descix app init`.
|
|
94
|
+
- An `env.platform` or `env.products` block — the multi-app format — fails on load: `v2.1 multi-app
|
|
95
|
+
workspace format is not supported. Migrate to v3 (single-app).` The file is left untouched; run
|
|
96
|
+
`descix config init --env …` and `descix app init -a <app_id>` from each app repo's own root.
|
|
62
97
|
- A top-level `apiUrl` key is refused by name; the origin lives at `env.apiUrl` and nowhere else.
|
|
63
98
|
|
|
64
99
|
---
|
|
@@ -143,14 +178,13 @@ The git path (`descix kb corpus sync`) needs none of this.
|
|
|
143
178
|
|
|
144
179
|
## 6. CLI Context Resolution
|
|
145
180
|
|
|
146
|
-
`WorkspaceConfig.detectContext()`
|
|
147
|
-
|
|
148
|
-
`communityId` is `null` for an entry registered by a CLI that did not yet store it.
|
|
181
|
+
`WorkspaceConfig.detectContext()` returns `{ communityId, appId, kbId }` from `env.app` when your
|
|
182
|
+
current directory is the workspace root or anywhere below it.
|
|
149
183
|
`resolveContextWithOptions()` lets explicit `-c` / `-a` / `-k` flags win over detection.
|
|
150
184
|
|
|
151
185
|
```bash
|
|
152
|
-
# From inside
|
|
153
|
-
cd my-app
|
|
186
|
+
# From inside the app repo — detected
|
|
187
|
+
cd my-app-repo
|
|
154
188
|
descix kb corpus sync
|
|
155
189
|
|
|
156
190
|
# From anywhere — flags
|
|
@@ -164,7 +198,7 @@ descix site status -c egpt -a egpt-mydocs # site status/list need both when
|
|
|
164
198
|
|
|
165
199
|
| Concept | Owner |
|
|
166
200
|
|---|---|
|
|
167
|
-
| Workspace loading, the
|
|
201
|
+
| Workspace loading, the v3 writer, app registration, context detection | `WorkspaceConfig`, internal to `@descix/cli` |
|
|
168
202
|
| Origin resolution precedence | flag → `DESCIX_API_URL` → `env.apiUrl` → default (`descix config` commands) |
|
|
169
203
|
| User-level settings (`~/.descixrc`) | `GlobalConfig`, internal to `@descix/cli` |
|
|
170
204
|
| `.descix/wallet.json` | written by `descix login` |
|
package/bin/descix.js
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* Combines CLI and MCP functionality with unified authentication.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { Option } from 'commander';
|
|
11
|
+
import { DescixCommand } from '../lib/descix-command.js';
|
|
11
12
|
import chalk from 'chalk';
|
|
12
13
|
import { DeSciXApiClient } from '../lib/api-client.js';
|
|
13
14
|
import { requireAuth } from '../lib/auth-guard.js';
|
|
@@ -73,7 +74,7 @@ function fail(error) {
|
|
|
73
74
|
process.exit(1);
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
const program = new
|
|
77
|
+
const program = new DescixCommand();
|
|
77
78
|
|
|
78
79
|
program
|
|
79
80
|
.name('descix')
|
|
@@ -155,6 +156,7 @@ program.hook('preAction', (thisCommand) => {
|
|
|
155
156
|
|
|
156
157
|
program
|
|
157
158
|
.command('login')
|
|
159
|
+
.runbookAnchor('#_3-pin-production-then-sign-in')
|
|
158
160
|
.description('Authenticate with DeSciX via device login (opens browser)')
|
|
159
161
|
.option('-u, --url <url>', 'API URL override')
|
|
160
162
|
.option('--wallet', 'Use direct wallet connection (advanced, not yet implemented)')
|
|
@@ -174,6 +176,7 @@ program
|
|
|
174
176
|
|
|
175
177
|
program
|
|
176
178
|
.command('logout')
|
|
179
|
+
.runbookAnchor('#_3-pin-production-then-sign-in')
|
|
177
180
|
.description('Clear saved credentials')
|
|
178
181
|
.action(async () => {
|
|
179
182
|
try {
|
|
@@ -229,6 +232,7 @@ program
|
|
|
229
232
|
|
|
230
233
|
program
|
|
231
234
|
.command('whoami')
|
|
235
|
+
.runbookAnchor('#_3-pin-production-then-sign-in')
|
|
232
236
|
.description('Show current authentication status')
|
|
233
237
|
.action(async () => {
|
|
234
238
|
try {
|
|
@@ -265,6 +269,7 @@ program
|
|
|
265
269
|
|
|
266
270
|
program
|
|
267
271
|
.command('doctor')
|
|
272
|
+
.runbookAnchor('#_3-pin-production-then-sign-in')
|
|
268
273
|
.description('Diagnose environment issues (Node, Auth, gcloud, ADC)')
|
|
269
274
|
.action(async () => {
|
|
270
275
|
try {
|
|
@@ -1041,6 +1046,7 @@ appCommand
|
|
|
1041
1046
|
|
|
1042
1047
|
appCommand
|
|
1043
1048
|
.command('init')
|
|
1049
|
+
.runbookAnchor('#_4-connect-the-repository-to-its-app')
|
|
1044
1050
|
// ONE PATH TO AN APP (CEO-D-2026-08-14): `app init` is THE app entry point — it creates the
|
|
1045
1051
|
// app when it does not exist yet and then registers/scaffolds it locally. The separate
|
|
1046
1052
|
// `app create` command is DELETED (no compat fence): two commands for one outcome is how apps
|
|
@@ -1151,6 +1157,7 @@ appCommand
|
|
|
1151
1157
|
|
|
1152
1158
|
appCommand
|
|
1153
1159
|
.command('set-listed')
|
|
1160
|
+
.runbookAnchor('#_9-list-it-and-discover-the-rest')
|
|
1154
1161
|
.description('List or unlist your app in the app store (on: shown in the public store; off: not shown to non-admin visitors)')
|
|
1155
1162
|
.argument('<state>', 'on or off')
|
|
1156
1163
|
.requiredOption('-c, --community <id>', 'Community ID')
|
|
@@ -1482,7 +1489,7 @@ appCommand
|
|
|
1482
1489
|
appCommand
|
|
1483
1490
|
.command('set-port')
|
|
1484
1491
|
.description('Set the microservice port for this workspace\'s app (writes env.app.microservice.port). Pass "n" to remove.')
|
|
1485
|
-
.requiredOption('-a, --app <app_id>', 'App ID (must be
|
|
1492
|
+
.requiredOption('-a, --app <app_id>', 'App ID (must be this workspace\'s app, env.app)')
|
|
1486
1493
|
.requiredOption('-p, --port <port>', 'Microservice port number (1-65535), or "n" to remove')
|
|
1487
1494
|
.action(async (options) => {
|
|
1488
1495
|
try {
|
|
@@ -1503,7 +1510,7 @@ appCommand
|
|
|
1503
1510
|
}
|
|
1504
1511
|
|
|
1505
1512
|
// setMicroservicePort mutates the live env entry and saves — and hard-fails (canonical
|
|
1506
|
-
// "not
|
|
1513
|
+
// "is not this workspace's app" error) for any other app id, surfaced in the catch below.
|
|
1507
1514
|
await workspaceConfig.setMicroservicePort(appId, portNum);
|
|
1508
1515
|
|
|
1509
1516
|
console.log(chalk.green(`\n✓ Microservice port set for ${appId}`));
|
|
@@ -1520,9 +1527,9 @@ appCommand
|
|
|
1520
1527
|
//
|
|
1521
1528
|
// Canonical write path for an app's site.{} slot in workspace.json. TWO MODES, one active
|
|
1522
1529
|
// per write:
|
|
1523
|
-
// static mode (--static): site.static — the relative
|
|
1524
|
-
// "." = the
|
|
1525
|
-
// serves at /p/{appId}/.
|
|
1530
|
+
// static mode (--static): site.static — the dir relative to the workspace root (the
|
|
1531
|
+
// repo root; "." = the root itself) the gateway's
|
|
1532
|
+
// staticSitePlugin serves at /p/{appId}/.
|
|
1526
1533
|
// dev-server mode (--port): site.port [+ site.protocol] — the local dev server the
|
|
1527
1534
|
// gateway proxies /p/{appId} to (vite-style hotserving).
|
|
1528
1535
|
// Setting one mode CLEARS the other's fields; passing BOTH flags in one invocation is the
|
|
@@ -1532,9 +1539,10 @@ appCommand
|
|
|
1532
1539
|
// Backed by WorkspaceConfig.setStaticSite. It writes ONLY the local workspace.json site.{} slot.
|
|
1533
1540
|
appCommand
|
|
1534
1541
|
.command('set-site')
|
|
1542
|
+
.runbookAnchor('#_5-run-it-locally-descix-serve')
|
|
1535
1543
|
.description("Set this workspace app's site config (env.app.site). One active mode per write: --static <dir> (built site served at /p/<app>/) or --port <n> [--protocol http|https] (dev server proxied at /p/<app>); setting one clears the other. Pass both flags together to keep both — the gateway then serves the dev server (site.port wins). --unset clears site.{} entirely.")
|
|
1536
|
-
.requiredOption('-a, --app <app_id>', 'App ID (must be
|
|
1537
|
-
.option('--static <path>', 'Static mode:
|
|
1544
|
+
.requiredOption('-a, --app <app_id>', 'App ID (must be this workspace\'s app, env.app)')
|
|
1545
|
+
.option('--static <path>', 'Static mode: a dir relative to the workspace root (the repo root), e.g. "site" or "../scratch"; "." = the root itself. An absolute path is refused. Alone, it clears site.port and site.protocol (one active mode per write).')
|
|
1538
1546
|
.option('--port <port>', 'Dev-server mode: proxy /p/<app> to your local dev server on this port (1-65535). Alone, it clears site.static. With --static in the same call, both are kept and the gateway serves the dev server.')
|
|
1539
1547
|
.option('--protocol <scheme>', 'Dev-server upstream scheme: "http" or "https" (default https when unset). Dev-server mode only — requires site.port. A wrong scheme fails loud at the gateway (502); there is no auto-detection.')
|
|
1540
1548
|
.option('--unset', 'Remove site.static, site.port and site.protocol for the app, clearing the site.{} slot')
|
|
@@ -1558,13 +1566,9 @@ appCommand
|
|
|
1558
1566
|
|
|
1559
1567
|
const fields = {};
|
|
1560
1568
|
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
process.exit(1);
|
|
1565
|
-
}
|
|
1566
|
-
fields.static = options.static;
|
|
1567
|
-
}
|
|
1569
|
+
// The --static path rule has ONE owner (assertSiteStatic, applied in setStaticSite);
|
|
1570
|
+
// a refusal surfaces through the catch below.
|
|
1571
|
+
if (options.static !== undefined) fields.static = options.static;
|
|
1568
1572
|
|
|
1569
1573
|
if (options.port !== undefined) {
|
|
1570
1574
|
const portNum = parseInt(options.port, 10);
|
|
@@ -1584,7 +1588,7 @@ appCommand
|
|
|
1584
1588
|
}
|
|
1585
1589
|
|
|
1586
1590
|
// setStaticSite applies ONE-ACTIVE-MODE-PER-WRITE and saves — it hard-fails (canonical
|
|
1587
|
-
// "not
|
|
1591
|
+
// "is not this workspace's app" error, or protocol-without-port) in the catch below.
|
|
1588
1592
|
await workspaceConfig.setStaticSite(appId, fields);
|
|
1589
1593
|
|
|
1590
1594
|
console.log(chalk.green(`\n✓ site config set for ${appId}`));
|
|
@@ -1622,7 +1626,7 @@ appCommand
|
|
|
1622
1626
|
appCommand
|
|
1623
1627
|
.command('open')
|
|
1624
1628
|
.description('Resolve (and optionally open) an app\'s local gateway dev URL. Hard-fails if the app has no site config.')
|
|
1625
|
-
.requiredOption('-a, --app <app_id>', 'App ID (
|
|
1629
|
+
.requiredOption('-a, --app <app_id>', 'App ID (an app this workspace serves, with a site)')
|
|
1626
1630
|
.option('--open', 'Open the resolved URL in the default browser')
|
|
1627
1631
|
.action(async (options) => {
|
|
1628
1632
|
try {
|
|
@@ -1700,6 +1704,7 @@ const kbCommand = program
|
|
|
1700
1704
|
|
|
1701
1705
|
kbCommand
|
|
1702
1706
|
.command('list')
|
|
1707
|
+
.runbookAnchor('#_6-knowledge-bases')
|
|
1703
1708
|
.description('List knowledge bases for an app')
|
|
1704
1709
|
.requiredOption('-a, --app <app_id>', 'App ID')
|
|
1705
1710
|
.option('--cached', 'Show the fast cached vector counter instead of the TRUE live Pinecone count. The cached counter is written ONLY by the CLI/PWA sync path, so it reports 0 for KBs ingested by any other route even when their vectors retrieve. Default is the live count.')
|
|
@@ -1996,6 +2001,7 @@ const corpusCommand = kbCommand
|
|
|
1996
2001
|
|
|
1997
2002
|
corpusCommand
|
|
1998
2003
|
.command('sync')
|
|
2004
|
+
.runbookAnchor('#_6-knowledge-bases')
|
|
1999
2005
|
.description('Sync the files a corpus manifest names into the app\'s knowledge base')
|
|
2000
2006
|
.requiredOption('-a, --app <id>', 'App ID')
|
|
2001
2007
|
.option('-c, --community <community_id>', 'Community ID (optional; resolved from Products when omitted)')
|
|
@@ -2025,6 +2031,7 @@ corpusCommand
|
|
|
2025
2031
|
|
|
2026
2032
|
corpusCommand
|
|
2027
2033
|
.command('status')
|
|
2034
|
+
.runbookAnchor('#_6-knowledge-bases')
|
|
2028
2035
|
.description('Show corpus sync state (files, chunks, last sync, resolved ref)')
|
|
2029
2036
|
.requiredOption('-a, --app <id>', 'App ID')
|
|
2030
2037
|
.option('-k, --kb <name>', 'KB name (default: all)')
|
|
@@ -2187,6 +2194,7 @@ siteCommand
|
|
|
2187
2194
|
// site upload - Deploy to GCS (context-aware, manifest-aware)
|
|
2188
2195
|
siteCommand
|
|
2189
2196
|
.command('upload')
|
|
2197
|
+
.runbookAnchor('#_8-deploy-the-site')
|
|
2190
2198
|
.description('Upload site to GCS and update app metadata (uses .descix/manifests/site.json when present)')
|
|
2191
2199
|
.option('-c, --community <id>', 'Community ID (auto-detects from context)')
|
|
2192
2200
|
.option('-a, --app <id>', 'App ID (auto-detects from context)')
|
|
@@ -2590,6 +2598,7 @@ siteCommand
|
|
|
2590
2598
|
// site status - Get deployment status (context-aware)
|
|
2591
2599
|
siteCommand
|
|
2592
2600
|
.command('status')
|
|
2601
|
+
.runbookAnchor('#_8-deploy-the-site')
|
|
2593
2602
|
.description('Get deployment status and manifest')
|
|
2594
2603
|
.option('-c, --community <id>', 'Community ID (auto-detects from context)')
|
|
2595
2604
|
.option('-a, --app <id>', 'App ID (auto-detects from context)')
|
|
@@ -3353,6 +3362,7 @@ program
|
|
|
3353
3362
|
|
|
3354
3363
|
program
|
|
3355
3364
|
.command('chat [question...]')
|
|
3365
|
+
.runbookAnchor('#_6-knowledge-bases')
|
|
3356
3366
|
.description('Chat with an app agent. Usage: descix chat "Your question" or descix chat -q "Your question"')
|
|
3357
3367
|
.option('-c, --community <id>', 'Community ID (optional; server resolves it from Products)')
|
|
3358
3368
|
.option('-a, --app <id>', 'App ID (required unless run from inside a mapped app directory)')
|
|
@@ -3669,6 +3679,7 @@ const configCommand = program
|
|
|
3669
3679
|
|
|
3670
3680
|
configCommand
|
|
3671
3681
|
.command('show')
|
|
3682
|
+
.runbookAnchor('#_3-pin-production-then-sign-in')
|
|
3672
3683
|
.description('Show current configuration')
|
|
3673
3684
|
.action(async () => {
|
|
3674
3685
|
try {
|
|
@@ -3680,6 +3691,7 @@ configCommand
|
|
|
3680
3691
|
|
|
3681
3692
|
configCommand
|
|
3682
3693
|
.command('init')
|
|
3694
|
+
.runbookAnchor('#_3-pin-production-then-sign-in')
|
|
3683
3695
|
.description('Pin this workspace to an environment (requires the global --env dev|demo|prod)')
|
|
3684
3696
|
// `--dev` DELETED, not deprecated: its absence meant PRODUCTION, so the most common
|
|
3685
3697
|
// invocation (`descix config init`) wrote a prod origin nobody named. There is no default.
|
|
@@ -3728,6 +3740,51 @@ configCommand
|
|
|
3728
3740
|
}
|
|
3729
3741
|
});
|
|
3730
3742
|
|
|
3743
|
+
// The gateway workspace's repo list (env.gateway.shell, env.gateway.appRepos[]). One writer:
|
|
3744
|
+
// WorkspaceConfig.setGatewayRepo(), which validates with the gateway's own reader.
|
|
3745
|
+
const configGatewayCommand = configCommand
|
|
3746
|
+
.command('gateway')
|
|
3747
|
+
.description('Write a gateway workspace\'s repo list (env.gateway.shell, env.gateway.appRepos[]) — never hand-edit it')
|
|
3748
|
+
.addHelpText('after', '\nRepo paths are relative to the gateway workspace root (sibling checkouts: ../<repo>).\n' +
|
|
3749
|
+
'Each listed repo must carry its own v3 .descix/workspace.json with env.app (descix app init there).\n' +
|
|
3750
|
+
'Refused in a single-app workspace (one that holds env.app).\n');
|
|
3751
|
+
|
|
3752
|
+
configGatewayCommand
|
|
3753
|
+
.command('set-shell')
|
|
3754
|
+
.description('Set the repo whose app the gateway serves at / (env.gateway.shell)')
|
|
3755
|
+
.argument('<repo>', 'Repo root relative to the workspace root, or "none" to remove')
|
|
3756
|
+
.action(async (repo) => {
|
|
3757
|
+
try {
|
|
3758
|
+
await configCommands.setGatewayRepo('set-shell', repo === 'none' ? null : repo);
|
|
3759
|
+
} catch (error) {
|
|
3760
|
+
fail(error);
|
|
3761
|
+
}
|
|
3762
|
+
});
|
|
3763
|
+
|
|
3764
|
+
configGatewayCommand
|
|
3765
|
+
.command('add-repo')
|
|
3766
|
+
.description('List a repo whose app the gateway serves at /p/<appId> (env.gateway.appRepos[])')
|
|
3767
|
+
.argument('<repo>', 'Repo root relative to the workspace root')
|
|
3768
|
+
.action(async (repo) => {
|
|
3769
|
+
try {
|
|
3770
|
+
await configCommands.setGatewayRepo('add-repo', repo);
|
|
3771
|
+
} catch (error) {
|
|
3772
|
+
fail(error);
|
|
3773
|
+
}
|
|
3774
|
+
});
|
|
3775
|
+
|
|
3776
|
+
configGatewayCommand
|
|
3777
|
+
.command('remove-repo')
|
|
3778
|
+
.description('Remove a listed repo from env.gateway.appRepos[]')
|
|
3779
|
+
.argument('<repo>', 'Repo root relative to the workspace root, as listed')
|
|
3780
|
+
.action(async (repo) => {
|
|
3781
|
+
try {
|
|
3782
|
+
await configCommands.setGatewayRepo('remove-repo', repo);
|
|
3783
|
+
} catch (error) {
|
|
3784
|
+
fail(error);
|
|
3785
|
+
}
|
|
3786
|
+
});
|
|
3787
|
+
|
|
3731
3788
|
configCommand
|
|
3732
3789
|
.command('set-dev-certs')
|
|
3733
3790
|
.description('Point local dev servers at your own TLS cert pair (env.devCerts) — the gateway AND every app behind it')
|
|
@@ -4055,6 +4112,7 @@ mcpCommand
|
|
|
4055
4112
|
|
|
4056
4113
|
program
|
|
4057
4114
|
.command('tell-me-how')
|
|
4115
|
+
.runbookAnchor('#_9-list-it-and-discover-the-rest')
|
|
4058
4116
|
.description('Discover platform tools using natural language (Intelligent MCP Mesh)')
|
|
4059
4117
|
.argument('<question>', 'What do you want to accomplish? Use natural language.')
|
|
4060
4118
|
.option('-s, --scope <scope>', 'Search scope (validated against the published tell_me_how enum): bootstrap, artifact, project, entitlements (default), discovery', 'entitlements')
|
|
@@ -4195,6 +4253,7 @@ program
|
|
|
4195
4253
|
|
|
4196
4254
|
program
|
|
4197
4255
|
.command('serve')
|
|
4256
|
+
.runbookAnchor('#_5-run-it-locally-descix-serve')
|
|
4198
4257
|
.description('Start the unified local gateway (one HTTPS origin: App Shell at /, your app at /p/<app>, /apifront to the API)')
|
|
4199
4258
|
.option('-p, --port <port>', 'Gateway port for this run (default: env.gateway.port, else 5173)')
|
|
4200
4259
|
.option('-w, --workspace <path>', 'Workspace root override')
|
|
@@ -4226,6 +4285,7 @@ const devCertsCommand = program
|
|
|
4226
4285
|
|
|
4227
4286
|
devCertsCommand
|
|
4228
4287
|
.command('check')
|
|
4288
|
+
.runbookAnchor('#_5-run-it-locally-descix-serve')
|
|
4229
4289
|
.description('Check whether the dev-server cert is trusted for https://localhost (exit 0 only when trusted)')
|
|
4230
4290
|
.option('--json', 'Output raw JSON')
|
|
4231
4291
|
.action(async (options) => {
|
|
@@ -4239,6 +4299,7 @@ devCertsCommand
|
|
|
4239
4299
|
|
|
4240
4300
|
devCertsCommand
|
|
4241
4301
|
.command('trust')
|
|
4302
|
+
.runbookAnchor('#_5-run-it-locally-descix-serve')
|
|
4242
4303
|
.description('Trust the dev-server cert in the macOS keychain (prompts for your password)')
|
|
4243
4304
|
.action(async () => {
|
|
4244
4305
|
try {
|
package/lib/commands/config.js
CHANGED
|
@@ -308,3 +308,21 @@ export async function setSiteUrl(url) {
|
|
|
308
308
|
'App Shell URL set — `descix serve` will proxy / here.',
|
|
309
309
|
);
|
|
310
310
|
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* `descix config gateway set-shell | add-repo | remove-repo` — the CLI face of the one writer,
|
|
314
|
+
* WorkspaceConfig.setGatewayRepo(). A refusal propagates to the bin's fail(), which prints it once.
|
|
315
|
+
*
|
|
316
|
+
* @param {'set-shell'|'add-repo'|'remove-repo'} op
|
|
317
|
+
* @param {string|null} repo
|
|
318
|
+
*/
|
|
319
|
+
export async function setGatewayRepo(op, repo) {
|
|
320
|
+
const workspaceConfig = await WorkspaceConfig.load();
|
|
321
|
+
const configPath = await workspaceConfig.setGatewayRepo(op, repo);
|
|
322
|
+
const gateway = workspaceConfig.env?.gateway || {};
|
|
323
|
+
console.log(chalk.green(`\n✅ Gateway ${op}: ${repo === null ? '(shell removed)' : repo}\n`));
|
|
324
|
+
console.log(chalk.white(` env.gateway.shell: ${gateway.shell ?? '(none)'}`));
|
|
325
|
+
console.log(chalk.white(` env.gateway.appRepos: ${gateway.appRepos?.length ? gateway.appRepos.join(', ') : '(none)'}`));
|
|
326
|
+
console.log(chalk.gray(` Saved to: ${configPath}\n`));
|
|
327
|
+
return configPath;
|
|
328
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DescixCommand — the CLI's commander Command, carrying ONE extra fact per verb: the
|
|
3
|
+
* app-developer runbook section that documents it.
|
|
4
|
+
*
|
|
5
|
+
* The fact is declared BESIDE the verb's own definition in bin/descix.js
|
|
6
|
+
* (`.command('serve').runbookAnchor('#_5-run-it-locally-descix-serve')`), so the verb tree and
|
|
7
|
+
* its runbook pointers can never be two lists. `createCommand` makes every subcommand a
|
|
8
|
+
* DescixCommand (commander's documented subclassing hook), so `.runbookAnchor()` chains on any
|
|
9
|
+
* `.command(...)` in the tree.
|
|
10
|
+
*
|
|
11
|
+
* The value is the anchor VitePress renders for the runbook heading (leading '#'). Whether it
|
|
12
|
+
* names a REAL heading is gated by tests/cli-verbs-generated.test.js against the runbook
|
|
13
|
+
* source (scripts/lib/runbook-anchors.mjs owns the slug rule). A verb with no runbook section
|
|
14
|
+
* declares nothing — there is no placeholder anchor. Read only by
|
|
15
|
+
* scripts/lib/cli-verb-tree-hook.mjs, which publishes the tree into
|
|
16
|
+
* @descix/platform-api/mcp-tools (cli-verbs.generated.json).
|
|
17
|
+
*/
|
|
18
|
+
import { Command } from 'commander';
|
|
19
|
+
|
|
20
|
+
const ANCHOR_SHAPE = /^#[^\sA-Z#]+$/;
|
|
21
|
+
|
|
22
|
+
export class DescixCommand extends Command {
|
|
23
|
+
createCommand(name) {
|
|
24
|
+
return new DescixCommand(name);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** @param {string} anchor '#<slug>' as rendered by the docs site @returns {this} */
|
|
28
|
+
runbookAnchor(anchor) {
|
|
29
|
+
if (this._descixRunbookAnchor !== undefined) {
|
|
30
|
+
throw new Error(`descix ${this.name()}: runbookAnchor declared twice (${this._descixRunbookAnchor}, ${anchor}) — one verb, one section`);
|
|
31
|
+
}
|
|
32
|
+
if (typeof anchor !== 'string' || !ANCHOR_SHAPE.test(anchor)) {
|
|
33
|
+
throw new Error(`descix ${this.name()}: runbookAnchor must be a rendered anchor like "#_5-run-it-locally-descix-serve"; got ${JSON.stringify(anchor)}`);
|
|
34
|
+
}
|
|
35
|
+
this._descixRunbookAnchor = anchor;
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** @returns {string|null} the declared anchor, or null when the verb has no runbook section */
|
|
40
|
+
declaredRunbookAnchor() {
|
|
41
|
+
return this._descixRunbookAnchor ?? null;
|
|
42
|
+
}
|
|
43
|
+
}
|