@vxnus/siduri 2.0.35 → 2.0.36

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/dist/generator.js CHANGED
@@ -76,7 +76,7 @@ function generateInstanceFiles(options) {
76
76
  const companionSlug = instanceName.toLowerCase().replace(/[^a-z0-9_-]/g, '') || 'default';
77
77
  const instanceId = options.id || 'default';
78
78
  const coreVersion = options.coreVersion || '^2.0.15';
79
- const cliVersion = options.cliVersion || '^2.0.35';
79
+ const cliVersion = options.cliVersion || '^2.0.36';
80
80
  const canonicalOrder = ['brain', 'memory', 'knowledge', 'behavior', 'voice', 'body', 'mouth', 'hands', 'vision', 'ear', 'observation'];
81
81
  const manifests = [...options.selectedManifests].sort((a, b) => {
82
82
  const idxA = canonicalOrder.indexOf(a.organType);
@@ -210,7 +210,7 @@ function generateInstanceFiles(options) {
210
210
  instantiationLines.push(` if (selfRaw) {`);
211
211
  instantiationLines.push(` const parsedSelf = SelfPackageParser.parse(selfRaw);`);
212
212
  instantiationLines.push(` if (parsedSelf.isValid && parsedSelf.manifest) {`);
213
- instantiationLines.push(` await self.setIdentity({ companionId: config.id || 'default', name: parsedSelf.manifest.identity?.name || config.name, archetype: parsedSelf.manifest.identity?.archetype, version: parsedSelf.manifest.version || '1.0.0', updatedAt: new Date().toISOString() });`);
213
+ instantiationLines.push(` await self.setIdentity({ companionId: config.id || 'default', name: parsedSelf.manifest.identity?.name || '', archetype: parsedSelf.manifest.identity?.archetype, version: parsedSelf.manifest.version || '1.0.0', updatedAt: new Date().toISOString() });`);
214
214
  instantiationLines.push(` if (parsedSelf.manifest.directives) {`);
215
215
  instantiationLines.push(` await self.commitDirectives(config.id || 'default', parsedSelf.manifest.directives.map((d) => ({ id: d.id, companionId: config.id || 'default', directive: d.directive, category: (d.category || 'behavioral'), status: 'active', priority: d.priority || 50, createdAt: new Date().toISOString() })));`);
216
216
  instantiationLines.push(` }`);
@@ -337,7 +337,7 @@ function generateInstanceFiles(options) {
337
337
  ` res.end(JSON.stringify({`,
338
338
  ` id: config.id,`,
339
339
  ` companionId: config.id,`,
340
- ` name: identity?.name || config.name || 'Siduri',`,
340
+ ` name: identity?.name || undefined,`,
341
341
  ` archetype: identity?.archetype || identity?.role,`,
342
342
  ` origin: identity?.origin,`,
343
343
  ` ethos: identity?.ethos,`,
@@ -879,7 +879,7 @@ function generateInstanceFiles(options) {
879
879
  `license: "MIT"`,
880
880
  ``,
881
881
  `identity:`,
882
- ` name: "${instanceName}"`,
882
+ ` name: "${(behaviorConfig.name || behaviorConfig.companionName || '').replace(/"/g, '\\"')}"`,
883
883
  ` archetype: "${(behaviorConfig.archetype || 'Knowledge Assistant & Research Partner').replace(/"/g, '\\"')}"`,
884
884
  ` origin: "Constructed companion"`,
885
885
  ` ethos: "${(behaviorConfig.ethos || 'Direct technical candor, thoughtful, concise, and loyal').replace(/"/g, '\\"')}"`,
package/dist/index.d.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { OrganManifest } from './manifest';
3
- export declare const CLI_VERSION = "2.0.35";
3
+ export declare const CLI_VERSION = "2.0.36";
4
4
  import { colors } from './colors';
5
5
  export { colors };
6
6
  export declare function printHeader(): void;
7
7
  export declare function printSection(title: string): void;
8
8
  export declare function printSuccess(message: string): void;
9
9
  export declare function projectDirectoryName(value: string): string;
10
- export declare function formatReviewSummary(companionName: string, selectedManifests: OrganManifest[], organSummaries: Record<string, Record<string, unknown>>): string;
10
+ export declare function validateProjectDirectory(value: string): true | string;
11
+ export declare function formatReviewSummary(projectDirName: string, selectedManifests: OrganManifest[], organSummaries: Record<string, Record<string, unknown>>): string;
11
12
  export declare function withTask<T>(label: string, task: () => Promise<T>): Promise<T>;
12
13
  export declare function runCreateWizard(targetDir?: string, options?: {
13
14
  localPath?: string;
package/dist/index.js CHANGED
@@ -42,6 +42,7 @@ exports.printHeader = printHeader;
42
42
  exports.printSection = printSection;
43
43
  exports.printSuccess = printSuccess;
44
44
  exports.projectDirectoryName = projectDirectoryName;
45
+ exports.validateProjectDirectory = validateProjectDirectory;
45
46
  exports.formatReviewSummary = formatReviewSummary;
46
47
  exports.withTask = withTask;
47
48
  exports.runCreateWizard = runCreateWizard;
@@ -60,7 +61,7 @@ const doctor_1 = require("./doctor");
60
61
  const db_1 = require("./db");
61
62
  const configurators_1 = require("./configurators");
62
63
  const execFile = (0, node_util_1.promisify)(node_child_process_1.execFile);
63
- exports.CLI_VERSION = '2.0.35';
64
+ exports.CLI_VERSION = '2.0.36';
64
65
  const colors_1 = require("./colors");
65
66
  Object.defineProperty(exports, "colors", { enumerable: true, get: function () { return colors_1.colors; } });
66
67
  function printHeader() {
@@ -74,18 +75,27 @@ function printSuccess(message) {
74
75
  console.log(`${colors_1.colors.green}✓${colors_1.colors.reset} ${message}`);
75
76
  }
76
77
  function projectDirectoryName(value) {
77
- const slug = value
78
- .trim()
79
- .toLowerCase()
80
- .replace(/[^a-z0-9]+/g, '-')
81
- .replace(/^-+|-+$/g, '');
82
- return slug || 'siduri';
78
+ return (value || '').trim() || 'Companion';
83
79
  }
84
- function formatReviewSummary(companionName, selectedManifests, organSummaries) {
80
+ function validateProjectDirectory(value) {
81
+ const trimmed = (value || '').trim();
82
+ if (!trimmed) {
83
+ return 'Please enter a project directory.';
84
+ }
85
+ if (/^\d+$/.test(trimmed) || !isNaN(Number(trimmed))) {
86
+ return 'Project directory cannot be a number.';
87
+ }
88
+ const resolved = node_path_1.default.resolve(process.cwd(), trimmed);
89
+ if (node_fs_1.default.existsSync(resolved)) {
90
+ return `Directory "${trimmed}" already exists in the current directory.`;
91
+ }
92
+ return true;
93
+ }
94
+ function formatReviewSummary(projectDirName, selectedManifests, organSummaries) {
85
95
  const lines = [];
86
- lines.push(`\n${colors_1.colors.cyan}── Review ${companionName} ${'─'.repeat(Math.max(2, 42 - (companionName.length + 9)))}${colors_1.colors.reset}\n`);
87
- lines.push(` ${colors_1.colors.bold}Companion${colors_1.colors.reset}`);
88
- lines.push(` ${colors_1.colors.dim}Name:${colors_1.colors.reset} ${companionName}\n`);
96
+ lines.push(`\n${colors_1.colors.cyan}── Review ${projectDirName} ${'─'.repeat(Math.max(2, 42 - (projectDirName.length + 9)))}${colors_1.colors.reset}\n`);
97
+ lines.push(` ${colors_1.colors.bold}Project${colors_1.colors.reset}`);
98
+ lines.push(` ${colors_1.colors.dim}Directory:${colors_1.colors.reset} ${projectDirName}\n`);
89
99
  for (const m of selectedManifests) {
90
100
  const isRequired = m.organType === 'brain';
91
101
  const tag = isRequired ? ` ${colors_1.colors.dim}· required${colors_1.colors.reset}` : '';
@@ -136,20 +146,42 @@ async function runCreateWizard(targetDir, options) {
136
146
  if (availableManifests.length === 0) {
137
147
  throw new Error('No @siduri-x/* organ packages found. Please ensure organs are installed or in workspace.');
138
148
  }
139
- printSection('Companion Details');
140
- const basicAnswers = await inquirer_1.default.prompt([
141
- {
142
- type: 'input',
143
- name: 'name',
144
- message: 'Companion name:',
145
- default: 'Siduri',
146
- validate: nonEmpty,
147
- },
148
- ]);
149
- const companionName = basicAnswers.name;
150
- const projectDir = targetDir ? node_path_1.default.resolve(process.cwd(), targetDir) : node_path_1.default.resolve(process.cwd(), projectDirectoryName(companionName));
149
+ printSection('Project Details');
150
+ let projectDirName;
151
+ if (targetDir) {
152
+ const check = validateProjectDirectory(targetDir);
153
+ if (check !== true) {
154
+ console.warn(`${colors_1.colors.yellow}!${colors_1.colors.reset} Provided directory "${targetDir}" is invalid: ${check}\n`);
155
+ const dirAnswers = await inquirer_1.default.prompt([
156
+ {
157
+ type: 'input',
158
+ name: 'directory',
159
+ message: 'Project directory:',
160
+ default: 'Companion',
161
+ validate: validateProjectDirectory,
162
+ },
163
+ ]);
164
+ projectDirName = dirAnswers.directory.trim();
165
+ }
166
+ else {
167
+ projectDirName = targetDir.trim();
168
+ }
169
+ }
170
+ else {
171
+ const dirAnswers = await inquirer_1.default.prompt([
172
+ {
173
+ type: 'input',
174
+ name: 'directory',
175
+ message: 'Project directory:',
176
+ default: 'Companion',
177
+ validate: validateProjectDirectory,
178
+ },
179
+ ]);
180
+ projectDirName = dirAnswers.directory.trim();
181
+ }
182
+ const projectDir = node_path_1.default.resolve(process.cwd(), projectDirName);
151
183
  printSection('Organ Configuration');
152
- console.log(`${colors_1.colors.dim}Configuring capability organs for ${companionName} sequentially from cognition to physical embodiment.${colors_1.colors.reset}\n`);
184
+ console.log(`${colors_1.colors.dim}Configuring capability organs for ${projectDirName} sequentially from cognition to physical embodiment.${colors_1.colors.reset}\n`);
153
185
  // Canonical organ presentation order: Cognition -> Memory/State -> Identity -> Embodiment & Peripheral
154
186
  const canonicalOrder = ['brain', 'memory', 'knowledge', 'behavior', 'voice', 'body', 'mouth', 'hands', 'vision', 'ear', 'observation'];
155
187
  const orderedManifests = [...availableManifests].sort((a, b) => {
@@ -167,7 +199,7 @@ async function runCreateWizard(targetDir, options) {
167
199
  const sectionTitle = isRequired ? `${m.displayName.split(' ')[0] || m.organType} · required` : m.displayName.split(' ')[0] || m.organType;
168
200
  printSection(sectionTitle);
169
201
  const res = await (0, configurators_1.configureOrgan)(m, {
170
- companionName,
202
+ companionName: projectDirName,
171
203
  existingConfig: organConfigs[m.configKey],
172
204
  });
173
205
  if (res.config?.provider === 'none') {
@@ -179,11 +211,11 @@ async function runCreateWizard(targetDir, options) {
179
211
  }
180
212
  // 2. Final Review and Edit Loop
181
213
  while (true) {
182
- console.log(formatReviewSummary(companionName, selectedManifests, organSummaries));
214
+ console.log(formatReviewSummary(projectDirName, selectedManifests, organSummaries));
183
215
  const { reviewAction } = await inquirer_1.default.prompt({
184
216
  type: 'list',
185
217
  name: 'reviewAction',
186
- message: `Create ${companionName} with this configuration?`,
218
+ message: `Create ${projectDirName} with this configuration?`,
187
219
  choices: [
188
220
  { name: 'Yes, create', value: 'create' },
189
221
  { name: 'Go back and edit', value: 'edit' },
@@ -224,7 +256,7 @@ async function runCreateWizard(targetDir, options) {
224
256
  const sectionTitle = isRequired ? `${m.displayName.split(' ')[0] || m.organType} · required` : m.displayName.split(' ')[0] || m.organType;
225
257
  printSection(sectionTitle);
226
258
  const res = await (0, configurators_1.configureOrgan)(m, {
227
- companionName,
259
+ companionName: projectDirName,
228
260
  existingConfig: organConfigs[m.configKey],
229
261
  });
230
262
  if (res.config?.provider === 'none') {
@@ -251,7 +283,7 @@ async function runCreateWizard(targetDir, options) {
251
283
  }
252
284
  // 4. Generate Instance Files
253
285
  const files = (0, generator_1.generateInstanceFiles)({
254
- name: companionName,
286
+ name: projectDirName,
255
287
  selectedManifests,
256
288
  organConfigs,
257
289
  localPath: options?.localPath,
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ jest.mock('inquirer', () => ({
4
+ prompt: jest.fn(),
5
+ Separator: jest.fn((label) => ({ type: 'separator', line: label })),
6
+ }));
7
+ const index_1 = require("./index");
8
+ describe('Project Directory Validation & Handling', () => {
9
+ test('rejects empty or whitespace-only directory names', () => {
10
+ expect((0, index_1.validateProjectDirectory)('')).toBe('Please enter a project directory.');
11
+ expect((0, index_1.validateProjectDirectory)(' ')).toBe('Please enter a project directory.');
12
+ });
13
+ test('rejects purely numeric directory names', () => {
14
+ expect((0, index_1.validateProjectDirectory)('123')).toBe('Project directory cannot be a number.');
15
+ expect((0, index_1.validateProjectDirectory)('0')).toBe('Project directory cannot be a number.');
16
+ expect((0, index_1.validateProjectDirectory)('42')).toBe('Project directory cannot be a number.');
17
+ expect((0, index_1.validateProjectDirectory)('999999')).toBe('Project directory cannot be a number.');
18
+ });
19
+ test('rejects directory names that already exist in cwd', () => {
20
+ // 'src' definitely exists in cli working directory
21
+ const result = (0, index_1.validateProjectDirectory)('src');
22
+ expect(result).toBe('Directory "src" already exists in the current directory.');
23
+ });
24
+ test('accepts valid non-kebab-case directory names', () => {
25
+ const nonExistentDir = 'CompanionTestDir_' + Date.now();
26
+ expect((0, index_1.validateProjectDirectory)(nonExistentDir)).toBe(true);
27
+ const privateCompanion = 'PrivateCompanion_' + Date.now();
28
+ expect((0, index_1.validateProjectDirectory)(privateCompanion)).toBe(true);
29
+ });
30
+ test('projectDirectoryName preserves casing without forcing kebab-case', () => {
31
+ expect((0, index_1.projectDirectoryName)('Companion')).toBe('Companion');
32
+ expect((0, index_1.projectDirectoryName)('PrivateCompanion')).toBe('PrivateCompanion');
33
+ expect((0, index_1.projectDirectoryName)('MySpecialCompanion')).toBe('MySpecialCompanion');
34
+ expect((0, index_1.projectDirectoryName)(' TrimmedCompanion ')).toBe('TrimmedCompanion');
35
+ expect((0, index_1.projectDirectoryName)('')).toBe('Companion');
36
+ });
37
+ });
@@ -324,7 +324,7 @@ function generateWebHtml(instanceName, manifests) {
324
324
  <header>
325
325
  <div class="brand">
326
326
  <h1>◈ SIDURI</h1>
327
- <span id="companion-display-name" style="font-weight: 600; color: #fff;">${instanceName}</span>
327
+ <span id="companion-display-name" style="font-weight: 600; color: #fff;">Companion</span>
328
328
  <div class="badges">
329
329
  ${organBadges}
330
330
  </div>
@@ -350,7 +350,7 @@ function generateWebHtml(instanceName, manifests) {
350
350
  <div class="chat-pane">
351
351
  <div id="messages" class="messages-container">
352
352
  <div class="message companion">
353
- <div class="meta companion-meta">${instanceName}</div>
353
+ <div class="meta companion-meta">Companion</div>
354
354
  Hello! I am your Siduri companion. How can I help you today?
355
355
  </div>
356
356
  </div>
@@ -415,7 +415,7 @@ function generateWebHtml(instanceName, manifests) {
415
415
 
416
416
  <script>
417
417
  let currentClaims = [];
418
- let companionName = '${instanceName}';
418
+ let companionName = 'Companion';
419
419
 
420
420
  function updateCompanionName(newName) {
421
421
  if (!newName || typeof newName !== 'string' || !newName.trim()) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vxnus/siduri",
3
- "version": "2.0.35",
3
+ "version": "2.0.36",
4
4
  "description": "Experimental CLI for installing and configuring Siduri companions",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -23,6 +23,11 @@
23
23
  "siduri": "dist/index.js"
24
24
  },
25
25
  "main": "dist/index.js",
26
+ "scripts": {
27
+ "build": "tsc && node ./scripts/copy-web-dist.cjs",
28
+ "dev": "tsc -w",
29
+ "test": "jest --config jest.config.json"
30
+ },
26
31
  "dependencies": {
27
32
  "@vxnus/e": "^0.1.5",
28
33
  "@vxnus/e-knowledge": "^0.1.5",
@@ -35,10 +40,5 @@
35
40
  "ts-jest": "^29.4.12",
36
41
  "typescript": "^5.9.3",
37
42
  "@types/inquirer": "^9.0.10"
38
- },
39
- "scripts": {
40
- "build": "tsc && node ./scripts/copy-web-dist.cjs",
41
- "dev": "tsc -w",
42
- "test": "jest --config jest.config.json"
43
43
  }
44
- }
44
+ }