@descix/cli 2.0.1 → 2.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/bin/descix.js CHANGED
@@ -2411,11 +2411,20 @@ siteCommand
2411
2411
  filesToUpload.forEach(f => console.log(chalk.gray(` + ${f.path} (${(f.size / 1024).toFixed(1)}KB)`)));
2412
2412
  // The server's last deploy manifest names what it holds; the confirm step lists the
2413
2413
  // storage prefix itself, so this preview is the manifest's view of what would go.
2414
- const lastDeployed = Object.keys(existing_manifest?.files || {});
2415
- const wouldRemove = lastDeployed.filter(p => !(p in localFiles)).sort();
2416
- if (wouldRemove.length > 0) {
2417
- console.log(chalk.yellow('\n Would remove from the server (in the last deploy, not in this build):'));
2418
- wouldRemove.forEach(f => console.log(chalk.gray(` - ${f}`)));
2414
+ // THE REMOVE SET IS ALWAYS STATED, including empty (regression from 1.0.20 measured
2415
+ // 2026-10-10: the silent branch read as "didn't check", and an absence claim the
2416
+ // instrument cannot make — no server manifest — must say THAT, not imply none).
2417
+ if (existing_manifest?.files) {
2418
+ const lastDeployed = Object.keys(existing_manifest.files);
2419
+ const wouldRemove = lastDeployed.filter(p => !(p in localFiles)).sort();
2420
+ if (wouldRemove.length > 0) {
2421
+ console.log(chalk.yellow('\n Would remove from the server (in the last deploy, not in this build):'));
2422
+ wouldRemove.forEach(f => console.log(chalk.gray(` - ${f}`)));
2423
+ } else {
2424
+ console.log(chalk.gray('\n Would remove: none (every file of the last deploy is in this build)'));
2425
+ }
2426
+ } else {
2427
+ console.log(chalk.gray('\n Would remove: unknown — the server returned no previous deploy manifest (first deploy, or manifest unreadable)'));
2419
2428
  }
2420
2429
  console.log(chalk.gray(`\n Site URL: ${site_url}\n`));
2421
2430
  return;
@@ -23,7 +23,8 @@ import { generateAgentFiles } from '../agent-files.js';
23
23
  */
24
24
  export async function runAppInit(apiClient, options) {
25
25
  let appId = options.app;
26
- const kbId = options.kb || 'General';
26
+ let kbId = options.kb || 'General';
27
+ let createdHere = false;
27
28
 
28
29
  // v3 single-app: THIS directory becomes (or already is) the workspace root. A workspace at an
29
30
  // ANCESTOR is a refusal, loud, BEFORE anything is written anywhere — the walk-up used to
@@ -59,6 +60,7 @@ export async function runAppInit(apiClient, options) {
59
60
  const created = response.message || response;
60
61
  appId = created.app_id || appId;
61
62
  communityId = created.community_id || options.community;
63
+ createdHere = true;
62
64
  console.log(chalk.green(`\n✅ App created: ${appId}`));
63
65
  if (appId !== options.app) {
64
66
  console.log(chalk.gray(` The platform composed the id from the community and the name you gave.`));
@@ -71,6 +73,35 @@ export async function runAppInit(apiClient, options) {
71
73
  );
72
74
  }
73
75
 
76
+ // MAPPING AN EXISTING APP ADOPTS ITS KNOWLEDGE BASES AND NEVER CREATES ONE (measured
77
+ // 2026-10-10: mapping `egpt` without --kb created an empty `General` on PROD beside its
78
+ // real `Corpus`). One existing KB → adopted; several → --kb must name one of THEM; a named
79
+ // KB that does not exist is a refusal, never a create. Only the CREATE leg above may create,
80
+ // because only a brand-new app has nothing to adopt.
81
+ if (!createdHere) {
82
+ const kbResp = await apiClient.invoke('list_knowledge_bases', { community_id: communityId, app_id: appId });
83
+ const existingKbs = ((kbResp.message || kbResp).knowledge_bases || []).map((kb) => kb.name);
84
+ if (options.kb) {
85
+ if (!existingKbs.includes(options.kb)) {
86
+ throw new Error(
87
+ `App '${appId}' has no KB named '${options.kb}' — it has: ${existingKbs.join(', ') || '(none)'}.\n` +
88
+ `Mapping an existing app never creates a KB; name one that exists with --kb, or create ` +
89
+ `one deliberately with its own verb.`
90
+ );
91
+ }
92
+ kbId = options.kb;
93
+ } else if (existingKbs.length === 1) {
94
+ kbId = existingKbs[0];
95
+ } else if (existingKbs.length > 1) {
96
+ throw new Error(
97
+ `App '${appId}' has ${existingKbs.length} knowledge bases: ${existingKbs.join(', ')}.\n` +
98
+ `Name the one this repo syncs with --kb <name>.`
99
+ );
100
+ }
101
+ // zero KBs: every app is guaranteed a default KB at creation, so an empty list is a
102
+ // platform-side anomaly — keep the declared kbId and let the sync surface report it.
103
+ }
104
+
74
105
  // 1. Workspace.json — THIS repo root is the workspace root and holds exactly this app (v3).
75
106
  // registerApp is idempotent for the same id and REFUSES a different one: a repo that already
76
107
  // holds another app errors loudly instead of being silently re-pointed.
@@ -100,9 +131,12 @@ export async function runAppInit(apiClient, options) {
100
131
  }
101
132
  console.log(chalk.gray(` Created: site/, microservice/, assets/`));
102
133
 
103
- // 3. Create KnowledgeBase Firestore doc (Git Mode — no Drive required)
104
- const kbResponse = await apiClient.invoke('init_git_mode_kb', { app_id: appId, kb_name: kbId });
105
- const kbResult = kbResponse.message || kbResponse;
134
+ // 3. The default KB — CREATE leg only (see the adopt block above: map never creates).
135
+ let kbResult = null;
136
+ if (createdHere) {
137
+ const kbResponse = await apiClient.invoke('init_git_mode_kb', { app_id: appId, kb_name: kbId });
138
+ kbResult = kbResponse.message || kbResponse;
139
+ }
106
140
 
107
141
  console.log(chalk.green(`\n✓ ${appId} initialized`));
108
142
  console.log(chalk.gray(` Community: ${communityId}`));
@@ -119,7 +153,7 @@ export async function runAppInit(apiClient, options) {
119
153
  console.log(chalk.gray(` Retry with: descix community refresh-identity -c ${communityId}`));
120
154
  }
121
155
  }
122
- console.log(chalk.gray(` KB: ${kbId} — ${kbResult.created ? 'created' : 'already exists'}\n`));
156
+ console.log(chalk.gray(` KB: ${kbId} — ${kbResult ? (kbResult.created ? 'created' : 'already exists') : 'adopted (existing app; map never creates)'}\n`));
123
157
  console.log(chalk.gray(` (every app is guaranteed a default KB at creation; an empty one`));
124
158
  console.log(chalk.gray(` says so rather than answering from general knowledge)\n`));
125
159
 
@@ -12,6 +12,7 @@
12
12
  "create_app_for_community",
13
13
  "get_product_context",
14
14
  "init_git_mode_kb",
15
+ "list_knowledge_bases",
15
16
  "update_community_metadata"
16
17
  ],
17
18
  "app invite": [
@@ -189,6 +190,7 @@
189
190
  "find_communities",
190
191
  "get_product_context",
191
192
  "init_git_mode_kb",
193
+ "list_knowledge_bases",
192
194
  "resolve_invite",
193
195
  "sync_auto_purchases",
194
196
  "update_community_metadata"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@descix/cli",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "DeSciX CLI and MCP Server - Unified command-line interface for DeSciX platform",
5
5
  "repository": {
6
6
  "type": "git",