newo 3.3.2 → 3.4.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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.4.0] - 2025-12-25
11
+
12
+ ### Added
13
+
14
+ - **Project Registry**: Browse and install project templates from NEWO registries
15
+ - `newo list-registries` - List available registries (production, staging, development, etc.)
16
+ - `newo list-registry-items <registry-idn>` - Browse project templates in a registry with version info
17
+ - `newo add-project <idn> --item <template-idn>` - Install project from registry template
18
+ - Support for multiple registries: production, staging, development, testing, and custom registries
19
+ - Version selection with `--version` flag or automatic latest version detection
20
+ - Auto-update support with `--auto-update` flag for automatic updates when new versions are published
21
+ - Grouped display showing unique projects with version counts and active installations
22
+ - `--all` flag to show all versions of each project template
23
+
24
+ - **Registry Types** (`src/types.ts`):
25
+ - `Registry` - Registry metadata (id, idn, account_id, is_public)
26
+ - `RegistryItem` - Project template with versioning (id, idn, version, project_image, active_project_count)
27
+ - `RegistryItemProjectImage` - Project image metadata for templates
28
+ - `AddProjectFromRegistryRequest` - Request type for registry-based project installation
29
+
30
+ - **Registry API Functions** (`src/api.ts`):
31
+ - `listRegistries()` - Fetch available registries
32
+ - `listRegistryItems()` - Fetch project templates from a specific registry
33
+ - `addProjectFromRegistry()` - Create project from registry template
34
+
35
+ - **Registry Command Handlers** (`src/cli/commands/`):
36
+ - `list-registries.ts` - Display registries in formatted table
37
+ - `list-registry-items.ts` - Display projects with version grouping and sorting
38
+ - `add-project.ts` - Install templates with validation and progress feedback
39
+
40
+ ### Enhanced
41
+
42
+ - **Help Command**: Added registry commands documentation and examples
43
+ - **CLAUDE.md**: Updated with registry feature documentation, API endpoints, and data flow
44
+
10
45
  ## [3.3.0] - 2025-10-20
11
46
 
12
47
  ### Added
@@ -78,15 +113,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
78
113
  - 100% entity match validation (projects, agents, flows, skills)
79
114
  - 100% file integrity verification (SHA256 checksums on attributes)
80
115
  - 100% webhook creation success (all 5 webhooks verified)
81
- - Migration time: ~30 minutes (vs 8-10 hours manual process)
116
+ - Significant time reduction compared to manual migration process
82
117
  - **Entity Count Verification**: Automated comparison validates all entity types
83
118
  - **Webhook Creation**: Batch webhook creation from YAML files tested and verified
84
119
 
85
120
  ### Performance
86
121
 
87
- - **Migration Speed**: 96% time reduction compared to manual migration
88
- - Automated process: ~30 minutes
89
- - Manual process: 8-10 hours
122
+ - **Migration Automation**: 100% automated with zero manual steps required
90
123
  - **Reliability**: 100% success rate across multiple test migrations
91
124
  - **Data Integrity**: Full SHA256 verification ensures zero data loss
92
125
 
package/README.md CHANGED
@@ -8,8 +8,8 @@
8
8
  **NEWO CLI** - Professional command-line tool for NEWO AI Agent development. Features **modular architecture**, **IDN-based file management**, and **comprehensive multi-customer support**.
9
9
 
10
10
  Sync NEWO "Project → Agent → Flow → Skills" structure to local files with:
11
- - 🚀 **Account migration** - Fully automated account copying with 100% accuracy in ~30 minutes (NEW v3.3.0)
12
- - 🏗️ **Complete entity management** - Create, edit, and delete agents, flows, skills, events, and states (NEW v2.0+)
11
+ - 🚀 **Account migration** - Fully automated account copying with 100% accuracy
12
+ - 🏗️ **Complete entity management** - Create, edit, and delete agents, flows, skills, events, and states
13
13
  - 🔄 **Intelligent synchronization** - Pull projects, attributes, and conversations automatically
14
14
  - 🎯 **IDN-based naming** - Skills named as `{skillIdn}.jinja/.guidance` for better organization
15
15
  - 📊 **Real-time progress** - Live progress tracking during large operations (1,000+ skills)
@@ -19,11 +19,11 @@ Sync NEWO "Project → Agent → Flow → Skills" structure to local files with:
19
19
  - ⚡ **Smart change detection** - SHA256-based efficient sync with hash consistency
20
20
  - 🛡️ **File validation** - Multiple file detection with clear warnings and safe handling
21
21
  - 🧠 **AI skill formats** - Support for `.guidance` (AI prompts) and `.jinja` (NSL templates)
22
- - 📡 **Webhook automation** - Automatic webhook creation from YAML configuration (NEW v3.3.0)
22
+ - 📡 **Webhook automation** - Automatic webhook creation from YAML configuration
23
23
  - 📊 **Knowledge base import** - Bulk import AKB articles from structured text files
24
24
  - 💬 **Conversation history** - Extract and sync user conversations and personas
25
- - 🧪 **Sandbox testing** - Interactive agent testing with conversation continuation (NEW v3.1.0)
26
- - ✅ **Migration verification** - Automated validation of migration completeness (NEW v3.3.0)
25
+ - 🧪 **Sandbox testing** - Interactive agent testing with conversation continuation
26
+ - ✅ **Migration verification** - Automated validation of migration completeness
27
27
  - 🔧 **CI/CD ready** - GitHub Actions integration for automated deployments
28
28
 
29
29
  ---
@@ -152,7 +152,7 @@ NEWO_REFRESH_URL=custom_refresh_endpoint # Custom refresh endpoint
152
152
  | `newo import-akb` | Import knowledge base articles | • Structured text parsing<br>• Bulk article import<br>• Validation and error reporting |
153
153
  | `newo meta` | Get project metadata (debug) | • Project structure analysis<br>• Metadata validation |
154
154
 
155
- ### Account Migration Commands (NEW v3.3.0)
155
+ ### Account Migration Commands
156
156
 
157
157
  **Enterprise-grade account migration with 100% automation:**
158
158
 
@@ -179,7 +179,7 @@ newo push --customer DEST_IDN
179
179
  # 4. Verify success
180
180
  newo verify --source SOURCE_IDN --dest DEST_IDN
181
181
 
182
- # Complete! Account migrated in ~30 minutes
182
+ # Complete! Account fully migrated
183
183
  ```
184
184
 
185
185
  **What Gets Migrated:**
@@ -191,7 +191,7 @@ newo verify --source SOURCE_IDN --dest DEST_IDN
191
191
  - ✅ All metadata and configuration
192
192
 
193
193
  **Benefits:**
194
- - **Time Savings**: ~30 minutes vs 8-10 hours manual
194
+ - **Automation**: 100% automated with zero manual steps
195
195
  - **Accuracy**: 100% entity match verified
196
196
  - **Reliability**: Tested with 1,084-skill account
197
197
  - **Safety**: Source account read-only, never modified
package/dist/api.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type AxiosInstance } from 'axios';
2
- import type { ProjectMeta, Agent, Skill, FlowEvent, FlowState, AkbImportArticle, CustomerProfile, CustomerAttribute, CustomerAttributesResponse, UserPersonaResponse, UserPersona, ChatHistoryParams, ChatHistoryResponse, CreateAgentRequest, CreateAgentResponse, CreateFlowRequest, CreateFlowResponse, CreateSkillRequest, CreateSkillResponse, CreateFlowEventRequest, CreateFlowEventResponse, CreateFlowStateRequest, CreateFlowStateResponse, CreateSkillParameterRequest, CreateSkillParameterResponse, CreateCustomerAttributeRequest, CreateCustomerAttributeResponse, CreatePersonaRequest, CreatePersonaResponse, CreateProjectRequest, CreateProjectResponse, PublishFlowRequest, PublishFlowResponse, Integration, Connector, CreateSandboxPersonaRequest, CreateSandboxPersonaResponse, CreateActorRequest, CreateActorResponse, SendChatMessageRequest, ConversationActsParams, ConversationActsResponse, ScriptAction, IntegrationSetting, CreateConnectorRequest, CreateConnectorResponse, UpdateConnectorRequest, OutgoingWebhook, IncomingWebhook, PersonaSearchResponse, AkbTopicsResponse } from './types.js';
2
+ import type { ProjectMeta, Agent, Skill, FlowEvent, FlowState, AkbImportArticle, CustomerProfile, CustomerAttribute, CustomerAttributesResponse, UserPersonaResponse, UserPersona, ChatHistoryParams, ChatHistoryResponse, CreateAgentRequest, CreateAgentResponse, CreateFlowRequest, CreateFlowResponse, CreateSkillRequest, CreateSkillResponse, CreateFlowEventRequest, CreateFlowEventResponse, CreateFlowStateRequest, CreateFlowStateResponse, CreateSkillParameterRequest, CreateSkillParameterResponse, CreateCustomerAttributeRequest, CreateCustomerAttributeResponse, CreatePersonaRequest, CreatePersonaResponse, CreateProjectRequest, CreateProjectResponse, PublishFlowRequest, PublishFlowResponse, Integration, Connector, CreateSandboxPersonaRequest, CreateSandboxPersonaResponse, CreateActorRequest, CreateActorResponse, SendChatMessageRequest, ConversationActsParams, ConversationActsResponse, ScriptAction, IntegrationSetting, CreateConnectorRequest, CreateConnectorResponse, UpdateConnectorRequest, OutgoingWebhook, IncomingWebhook, PersonaSearchResponse, AkbTopicsResponse, Registry, RegistryItem, AddProjectFromRegistryRequest } from './types.js';
3
3
  export declare function makeClient(verbose?: boolean, token?: string): Promise<AxiosInstance>;
4
4
  export declare function listProjects(client: AxiosInstance): Promise<ProjectMeta[]>;
5
5
  export declare function listAgents(client: AxiosInstance, projectId: string): Promise<Agent[]>;
@@ -85,4 +85,7 @@ export declare function createIncomingWebhook(client: AxiosInstance, webhookData
85
85
  id: string;
86
86
  url: string;
87
87
  }>;
88
+ export declare function listRegistries(client: AxiosInstance): Promise<Registry[]>;
89
+ export declare function listRegistryItems(client: AxiosInstance, registryId: string): Promise<RegistryItem[]>;
90
+ export declare function addProjectFromRegistry(client: AxiosInstance, projectData: AddProjectFromRegistryRequest): Promise<CreateProjectResponse>;
88
91
  //# sourceMappingURL=api.d.ts.map
package/dist/api.js CHANGED
@@ -356,4 +356,18 @@ export async function createIncomingWebhook(client, webhookData) {
356
356
  const response = await client.post('/api/v1/webhooks/incoming', webhookData);
357
357
  return response.data;
358
358
  }
359
+ // Registry API Functions
360
+ export async function listRegistries(client) {
361
+ const response = await client.get('/api/v1/designer/registries');
362
+ return response.data;
363
+ }
364
+ export async function listRegistryItems(client, registryId) {
365
+ const response = await client.get(`/api/v1/designer/registries/${registryId}/items`);
366
+ return response.data;
367
+ }
368
+ export async function addProjectFromRegistry(client, projectData) {
369
+ // Uses the same endpoint as createProject, but with registry fields populated
370
+ const response = await client.post('/api/v1/designer/projects', projectData);
371
+ return response.data;
372
+ }
359
373
  //# sourceMappingURL=api.js.map
@@ -0,0 +1,3 @@
1
+ import type { MultiCustomerConfig, CliArgs } from '../../types.js';
2
+ export declare function handleAddProjectCommand(customerConfig: MultiCustomerConfig, args: CliArgs, verbose?: boolean): Promise<void>;
3
+ //# sourceMappingURL=add-project.d.ts.map
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Add Project from Registry Command Handler - Installs a project template from registry
3
+ */
4
+ import { makeClient, listRegistries, listRegistryItems, addProjectFromRegistry } from '../../api.js';
5
+ import { getValidAccessToken } from '../../auth.js';
6
+ import { requireSingleCustomer } from '../customer-selection.js';
7
+ export async function handleAddProjectCommand(customerConfig, args, verbose = false) {
8
+ try {
9
+ const selectedCustomer = requireSingleCustomer(customerConfig, args.customer);
10
+ // Parse arguments
11
+ const projectIdn = args._[1];
12
+ const registryIdn = args.registry || 'production';
13
+ const registryItemIdn = args.item;
14
+ const registryItemVersion = args.version || null;
15
+ const title = args.title || projectIdn || registryItemIdn;
16
+ const description = args.description || '';
17
+ const isAutoUpdateEnabled = Boolean(args['auto-update']);
18
+ // Validate required arguments
19
+ if (!registryItemIdn) {
20
+ console.error('Error: Registry item IDN is required');
21
+ console.error('');
22
+ console.error('Usage: newo add-project <project-idn> --item <registry-item-idn> [options]');
23
+ console.error('');
24
+ console.error('Options:');
25
+ console.error(' --item <idn> Registry item/template IDN (required)');
26
+ console.error(' --registry <idn> Registry to use (default: production)');
27
+ console.error(' --version <version> Specific version to install (default: latest)');
28
+ console.error(' --title <title> Project title (default: project IDN)');
29
+ console.error(' --description <desc> Project description');
30
+ console.error(' --auto-update Enable automatic updates from registry');
31
+ console.error('');
32
+ console.error('Examples:');
33
+ console.error(' newo add-project my_weather --item weather_integration');
34
+ console.error(' newo add-project my_calcom --item cal_com_integration --registry production');
35
+ console.error(' newo add-project my_zoho --item zoho_integration --version 1.0.2 --auto-update');
36
+ console.error('');
37
+ console.error('Run "newo list-registries" to see available registries');
38
+ console.error('Run "newo list-registry-items <registry-idn>" to see available project templates');
39
+ process.exit(1);
40
+ }
41
+ // Use registry item IDN as project IDN if not specified
42
+ const finalProjectIdn = projectIdn || registryItemIdn;
43
+ if (verbose) {
44
+ console.log(`📦 Adding project from registry`);
45
+ console.log(` Project IDN: ${finalProjectIdn}`);
46
+ console.log(` Title: ${title}`);
47
+ console.log(` Registry: ${registryIdn}`);
48
+ console.log(` Item: ${registryItemIdn}`);
49
+ console.log(` Version: ${registryItemVersion || 'latest'}`);
50
+ console.log(` Auto-update: ${isAutoUpdateEnabled}`);
51
+ console.log(` Customer: ${selectedCustomer.idn}`);
52
+ }
53
+ // Get access token and create client
54
+ const accessToken = await getValidAccessToken(selectedCustomer);
55
+ const client = await makeClient(verbose, accessToken);
56
+ // Validate registry exists
57
+ console.log(`🔍 Validating registry "${registryIdn}"...`);
58
+ const registries = await listRegistries(client);
59
+ const registry = registries.find((r) => r.idn === registryIdn);
60
+ if (!registry) {
61
+ console.error(`❌ Registry "${registryIdn}" not found`);
62
+ console.error('');
63
+ console.error('Available registries:');
64
+ for (const r of registries) {
65
+ console.error(` • ${r.idn}`);
66
+ }
67
+ process.exit(1);
68
+ }
69
+ // Validate registry item exists and find version
70
+ console.log(`🔍 Validating project template "${registryItemIdn}"...`);
71
+ const items = await listRegistryItems(client, registry.id);
72
+ const matchingItems = items.filter((item) => item.idn === registryItemIdn);
73
+ if (matchingItems.length === 0) {
74
+ console.error(`❌ Project template "${registryItemIdn}" not found in "${registryIdn}" registry`);
75
+ console.error('');
76
+ console.error('Run "newo list-registry-items ' + registryIdn + '" to see available templates');
77
+ process.exit(1);
78
+ }
79
+ // Find the specific version or latest
80
+ let selectedItem;
81
+ if (registryItemVersion) {
82
+ selectedItem = matchingItems.find((item) => item.version === registryItemVersion);
83
+ if (!selectedItem) {
84
+ console.error(`❌ Version "${registryItemVersion}" not found for "${registryItemIdn}"`);
85
+ console.error('');
86
+ console.error('Available versions:');
87
+ const sortedItems = [...matchingItems].sort((a, b) => new Date(b.published_at).getTime() - new Date(a.published_at).getTime());
88
+ for (const item of sortedItems.slice(0, 10)) {
89
+ console.error(` • ${item.version} (published: ${new Date(item.published_at).toISOString().split('T')[0]})`);
90
+ }
91
+ if (sortedItems.length > 10) {
92
+ console.error(` ... and ${sortedItems.length - 10} more`);
93
+ }
94
+ process.exit(1);
95
+ }
96
+ }
97
+ else {
98
+ // Get latest version (sorted by published_at desc)
99
+ const sortedItems = [...matchingItems].sort((a, b) => new Date(b.published_at).getTime() - new Date(a.published_at).getTime());
100
+ selectedItem = sortedItems[0];
101
+ }
102
+ if (!selectedItem) {
103
+ console.error(`❌ Could not determine version for "${registryItemIdn}"`);
104
+ process.exit(1);
105
+ }
106
+ console.log(`📥 Installing "${registryItemIdn}" v${selectedItem.version} as "${finalProjectIdn}"...`);
107
+ // Create project from registry
108
+ const projectData = {
109
+ idn: finalProjectIdn,
110
+ title,
111
+ version: '',
112
+ description,
113
+ is_auto_update_enabled: isAutoUpdateEnabled,
114
+ registry_idn: registryIdn,
115
+ registry_item_idn: registryItemIdn,
116
+ registry_item_version: registryItemVersion
117
+ };
118
+ const response = await addProjectFromRegistry(client, projectData);
119
+ console.log('');
120
+ console.log(`✅ Project installed successfully!`);
121
+ console.log(` Project IDN: ${finalProjectIdn}`);
122
+ console.log(` Project ID: ${response.id}`);
123
+ console.log(` Source: ${registryItemIdn} v${selectedItem.version}`);
124
+ console.log(` Registry: ${registryIdn}`);
125
+ if (isAutoUpdateEnabled) {
126
+ console.log(` Auto-update: Enabled`);
127
+ }
128
+ console.log('');
129
+ console.log(`💡 Run "newo pull" to sync the project locally`);
130
+ }
131
+ catch (error) {
132
+ console.error('❌ Failed to add project from registry:', error instanceof Error ? error.message : String(error));
133
+ process.exit(1);
134
+ }
135
+ }
136
+ //# sourceMappingURL=add-project.js.map
@@ -18,7 +18,10 @@ Core Commands:
18
18
  newo import-akb <file> <persona_id> [--customer <idn>] # import AKB articles from structured text file
19
19
 
20
20
  Project Management:
21
- newo create-project <idn> [--title <title>] [--description <desc>] [--version <version>] [--auto-update] # create project on platform
21
+ newo create-project <idn> [--title <title>] [--description <desc>] [--version <version>] [--auto-update] # create empty project on platform
22
+ newo list-registries [--customer <idn>] # list available project registries (production, staging, etc.)
23
+ newo list-registry-items <registry-idn> [--all] # list available project templates in a registry
24
+ newo add-project <idn> --item <template-idn> [--registry <registry>] [--version <v>] [--auto-update] # install project from registry template
22
25
 
23
26
  Entity Management (Full Lifecycle Support):
24
27
  newo create-agent <idn> --project <project-idn> [--title <title>] [--description <desc>] # create agent → push to platform ✅
@@ -122,6 +125,14 @@ Usage Examples:
122
125
  # Import AKB articles:
123
126
  newo import-akb articles.txt da4550db-2b95-4500-91ff-fb4b60fe7be9
124
127
 
128
+ # Project Registry - Install templates from registry (NEW):
129
+ newo list-registries # See available registries
130
+ newo list-registry-items production # Browse production templates
131
+ newo list-registry-items production --all # See all versions
132
+ newo add-project my_calcom --item cal_com_integration # Install latest version
133
+ newo add-project my_zoho --item zoho_integration --version 1.0.2 # Install specific version
134
+ newo add-project my_weather --item weather_integration --auto-update # With auto-updates
135
+
125
136
  # Sandbox testing (NEW v3.1.0):
126
137
  newo sandbox "Hello, I want to order pizza" # Start new conversation
127
138
  newo sandbox --actor abc123... "I want 2 large pizzas" # Continue conversation
@@ -0,0 +1,3 @@
1
+ import type { MultiCustomerConfig, CliArgs } from '../../types.js';
2
+ export declare function handleListRegistriesCommand(customerConfig: MultiCustomerConfig, args: CliArgs, verbose?: boolean): Promise<void>;
3
+ //# sourceMappingURL=list-registries.d.ts.map
@@ -0,0 +1,39 @@
1
+ /**
2
+ * List Registries Command Handler - Lists available project registries
3
+ */
4
+ import { makeClient, listRegistries } from '../../api.js';
5
+ import { getValidAccessToken } from '../../auth.js';
6
+ import { requireSingleCustomer } from '../customer-selection.js';
7
+ export async function handleListRegistriesCommand(customerConfig, args, verbose = false) {
8
+ try {
9
+ const selectedCustomer = requireSingleCustomer(customerConfig, args.customer);
10
+ if (verbose) {
11
+ console.log(`📋 Fetching registries for customer: ${selectedCustomer.idn}`);
12
+ }
13
+ // Get access token and create client
14
+ const accessToken = await getValidAccessToken(selectedCustomer);
15
+ const client = await makeClient(verbose, accessToken);
16
+ console.log('🔍 Fetching available project registries...\n');
17
+ const registries = await listRegistries(client);
18
+ if (registries.length === 0) {
19
+ console.log('No registries found.');
20
+ return;
21
+ }
22
+ console.log(`✅ Found ${registries.length} registries:\n`);
23
+ // Display registries in a table-like format
24
+ console.log(' IDN │ Public │ ID');
25
+ console.log(' ────────────────────────┼────────┼────────────────────────────────────');
26
+ for (const registry of registries) {
27
+ const publicStatus = registry.is_public ? 'Yes' : 'No';
28
+ const idnPadded = registry.idn.padEnd(22);
29
+ const publicPadded = publicStatus.padEnd(6);
30
+ console.log(` ${idnPadded} │ ${publicPadded} │ ${registry.id}`);
31
+ }
32
+ console.log('\n💡 Use "newo list-registry-items <registry-idn>" to see available projects in a registry');
33
+ }
34
+ catch (error) {
35
+ console.error('❌ Failed to list registries:', error instanceof Error ? error.message : String(error));
36
+ process.exit(1);
37
+ }
38
+ }
39
+ //# sourceMappingURL=list-registries.js.map
@@ -0,0 +1,3 @@
1
+ import type { MultiCustomerConfig, CliArgs } from '../../types.js';
2
+ export declare function handleListRegistryItemsCommand(customerConfig: MultiCustomerConfig, args: CliArgs, verbose?: boolean): Promise<void>;
3
+ //# sourceMappingURL=list-registry-items.d.ts.map
@@ -0,0 +1,112 @@
1
+ /**
2
+ * List Registry Items Command Handler - Lists available projects in a registry
3
+ */
4
+ import { makeClient, listRegistries, listRegistryItems } from '../../api.js';
5
+ import { getValidAccessToken } from '../../auth.js';
6
+ import { requireSingleCustomer } from '../customer-selection.js';
7
+ function groupItemsByIdn(items) {
8
+ const groups = new Map();
9
+ for (const item of items) {
10
+ const existing = groups.get(item.idn) || [];
11
+ existing.push(item);
12
+ groups.set(item.idn, existing);
13
+ }
14
+ const result = [];
15
+ for (const [idn, versions] of groups) {
16
+ // Sort versions by published_at descending (newest first)
17
+ const sortedVersions = [...versions].sort((a, b) => new Date(b.published_at).getTime() - new Date(a.published_at).getTime());
18
+ const latestItem = sortedVersions[0];
19
+ if (!latestItem)
20
+ continue;
21
+ // Sum up active project counts across all versions
22
+ const totalActiveProjects = versions.reduce((sum, v) => sum + v.active_project_count, 0);
23
+ result.push({
24
+ idn,
25
+ versions: sortedVersions,
26
+ latestVersion: latestItem.version,
27
+ totalActiveProjects
28
+ });
29
+ }
30
+ // Sort by IDN alphabetically
31
+ return result.sort((a, b) => a.idn.localeCompare(b.idn));
32
+ }
33
+ export async function handleListRegistryItemsCommand(customerConfig, args, verbose = false) {
34
+ try {
35
+ const selectedCustomer = requireSingleCustomer(customerConfig, args.customer);
36
+ // Parse arguments
37
+ const registryIdn = args._[1];
38
+ const showAllVersions = Boolean(args.all || args.a);
39
+ if (!registryIdn) {
40
+ console.error('Error: Registry IDN is required');
41
+ console.error('Usage: newo list-registry-items <registry-idn> [--all]');
42
+ console.error('');
43
+ console.error('Examples:');
44
+ console.error(' newo list-registry-items production');
45
+ console.error(' newo list-registry-items staging --all');
46
+ console.error('');
47
+ console.error('Run "newo list-registries" to see available registries');
48
+ process.exit(1);
49
+ }
50
+ if (verbose) {
51
+ console.log(`📋 Fetching items from registry: ${registryIdn}`);
52
+ console.log(` Customer: ${selectedCustomer.idn}`);
53
+ console.log(` Show all versions: ${showAllVersions}`);
54
+ }
55
+ // Get access token and create client
56
+ const accessToken = await getValidAccessToken(selectedCustomer);
57
+ const client = await makeClient(verbose, accessToken);
58
+ // First, get registries to find the ID
59
+ console.log(`🔍 Fetching registry "${registryIdn}"...`);
60
+ const registries = await listRegistries(client);
61
+ const registry = registries.find((r) => r.idn === registryIdn);
62
+ if (!registry) {
63
+ console.error(`❌ Registry "${registryIdn}" not found`);
64
+ console.error('');
65
+ console.error('Available registries:');
66
+ for (const r of registries) {
67
+ console.error(` • ${r.idn}`);
68
+ }
69
+ process.exit(1);
70
+ }
71
+ console.log(`📦 Fetching projects from "${registryIdn}" registry (this may take a moment)...\n`);
72
+ const items = await listRegistryItems(client, registry.id);
73
+ if (items.length === 0) {
74
+ console.log(`No projects found in "${registryIdn}" registry.`);
75
+ return;
76
+ }
77
+ if (showAllVersions) {
78
+ // Show all versions
79
+ console.log(`✅ Found ${items.length} project versions in "${registryIdn}" registry:\n`);
80
+ console.log(' Project IDN │ Version │ Active │ Published');
81
+ console.log(' ───────────────────────┼──────────┼────────┼────────────────────');
82
+ for (const item of items) {
83
+ const idnPadded = item.idn.substring(0, 21).padEnd(21);
84
+ const versionPadded = item.version.substring(0, 8).padEnd(8);
85
+ const activePadded = String(item.active_project_count).padEnd(6);
86
+ const published = new Date(item.published_at).toISOString().split('T')[0];
87
+ console.log(` ${idnPadded} │ ${versionPadded} │ ${activePadded} │ ${published}`);
88
+ }
89
+ }
90
+ else {
91
+ // Group by project IDN and show only latest version
92
+ const grouped = groupItemsByIdn(items);
93
+ console.log(`✅ Found ${grouped.length} unique projects in "${registryIdn}" registry:\n`);
94
+ console.log(' Project IDN │ Latest │ Active │ Versions');
95
+ console.log(' ───────────────────────┼──────────┼────────┼──────────');
96
+ for (const group of grouped) {
97
+ const idnPadded = group.idn.substring(0, 21).padEnd(21);
98
+ const versionPadded = group.latestVersion.substring(0, 8).padEnd(8);
99
+ const activePadded = String(group.totalActiveProjects).padEnd(6);
100
+ const versionCount = String(group.versions.length).padEnd(8);
101
+ console.log(` ${idnPadded} │ ${versionPadded} │ ${activePadded} │ ${versionCount}`);
102
+ }
103
+ console.log('\n💡 Use --all flag to see all versions');
104
+ }
105
+ console.log(`\n💡 Use "newo add-project <idn> --registry ${registryIdn} --item <project-idn>" to install a project`);
106
+ }
107
+ catch (error) {
108
+ console.error('❌ Failed to list registry items:', error instanceof Error ? error.message : String(error));
109
+ process.exit(1);
110
+ }
111
+ }
112
+ //# sourceMappingURL=list-registry-items.js.map
package/dist/cli.js CHANGED
@@ -38,6 +38,9 @@ import { handlePushAkbCommand } from './cli/commands/push-akb.js';
38
38
  import { handleMigrateAccountCommand } from './cli/commands/migrate-account.js';
39
39
  import { handleVerifyMigrationCommand } from './cli/commands/verify-migration.js';
40
40
  import { handleCreateWebhooksCommand } from './cli/commands/create-webhooks.js';
41
+ import { handleListRegistriesCommand } from './cli/commands/list-registries.js';
42
+ import { handleListRegistryItemsCommand } from './cli/commands/list-registry-items.js';
43
+ import { handleAddProjectCommand } from './cli/commands/add-project.js';
41
44
  dotenv.config();
42
45
  async function main() {
43
46
  try {
@@ -168,6 +171,15 @@ async function main() {
168
171
  case 'create-webhooks':
169
172
  await handleCreateWebhooksCommand(customerConfig, args, verbose);
170
173
  break;
174
+ case 'list-registries':
175
+ await handleListRegistriesCommand(customerConfig, args, verbose);
176
+ break;
177
+ case 'list-registry-items':
178
+ await handleListRegistryItemsCommand(customerConfig, args, verbose);
179
+ break;
180
+ case 'add-project':
181
+ await handleAddProjectCommand(customerConfig, args, verbose);
182
+ break;
171
183
  default:
172
184
  console.error('Unknown command:', cmd);
173
185
  console.error('Run "newo --help" for usage information');
package/dist/types.d.ts CHANGED
@@ -703,4 +703,42 @@ export interface AkbYamlTopic {
703
703
  labels: string[];
704
704
  topic_summary: string;
705
705
  }
706
+ export interface Registry {
707
+ readonly id: string;
708
+ readonly idn: string;
709
+ readonly account_id: string;
710
+ readonly is_public: boolean;
711
+ }
712
+ export interface RegistryItemProjectImage {
713
+ readonly id: string;
714
+ readonly idn: string;
715
+ readonly image_hash: string;
716
+ readonly storage_id: string;
717
+ readonly customer_idn: string;
718
+ readonly account_id: string;
719
+ readonly created_at: string;
720
+ }
721
+ export interface RegistryItem {
722
+ readonly id: string;
723
+ readonly idn: string;
724
+ readonly version: string;
725
+ readonly project_image: RegistryItemProjectImage;
726
+ readonly account_id: string;
727
+ readonly account_idn: string | null;
728
+ readonly account_email: string | null;
729
+ readonly is_public: boolean;
730
+ readonly active_project_count: number;
731
+ readonly created_at: string;
732
+ readonly published_at: string;
733
+ }
734
+ export interface AddProjectFromRegistryRequest {
735
+ idn: string;
736
+ title: string;
737
+ version?: string;
738
+ description?: string;
739
+ is_auto_update_enabled?: boolean;
740
+ registry_idn: string;
741
+ registry_item_idn: string;
742
+ registry_item_version: string | null;
743
+ }
706
744
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newo",
3
- "version": "3.3.2",
3
+ "version": "3.4.0",
4
4
  "description": "NEWO CLI: Professional command-line tool with modular architecture for NEWO AI Agent development. Features account migration, integration management, webhook automation, AKB knowledge base, project attributes, sandbox testing, IDN-based file management, real-time progress tracking, intelligent sync operations, and comprehensive multi-customer support.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api.ts CHANGED
@@ -52,7 +52,10 @@ import type {
52
52
  OutgoingWebhook,
53
53
  IncomingWebhook,
54
54
  PersonaSearchResponse,
55
- AkbTopicsResponse
55
+ AkbTopicsResponse,
56
+ Registry,
57
+ RegistryItem,
58
+ AddProjectFromRegistryRequest
56
59
  } from './types.js';
57
60
 
58
61
  // Per-request retry tracking to avoid shared state issues
@@ -552,4 +555,25 @@ export async function createIncomingWebhook(
552
555
  ): Promise<{ id: string; url: string }> {
553
556
  const response = await client.post('/api/v1/webhooks/incoming', webhookData);
554
557
  return response.data;
558
+ }
559
+
560
+ // Registry API Functions
561
+
562
+ export async function listRegistries(client: AxiosInstance): Promise<Registry[]> {
563
+ const response = await client.get<Registry[]>('/api/v1/designer/registries');
564
+ return response.data;
565
+ }
566
+
567
+ export async function listRegistryItems(client: AxiosInstance, registryId: string): Promise<RegistryItem[]> {
568
+ const response = await client.get<RegistryItem[]>(`/api/v1/designer/registries/${registryId}/items`);
569
+ return response.data;
570
+ }
571
+
572
+ export async function addProjectFromRegistry(
573
+ client: AxiosInstance,
574
+ projectData: AddProjectFromRegistryRequest
575
+ ): Promise<CreateProjectResponse> {
576
+ // Uses the same endpoint as createProject, but with registry fields populated
577
+ const response = await client.post<CreateProjectResponse>('/api/v1/designer/projects', projectData);
578
+ return response.data;
555
579
  }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Add Project from Registry Command Handler - Installs a project template from registry
3
+ */
4
+ import { makeClient, listRegistries, listRegistryItems, addProjectFromRegistry } from '../../api.js';
5
+ import { getValidAccessToken } from '../../auth.js';
6
+ import { requireSingleCustomer } from '../customer-selection.js';
7
+ import type { MultiCustomerConfig, CliArgs, Registry, RegistryItem, AddProjectFromRegistryRequest } from '../../types.js';
8
+
9
+ export async function handleAddProjectCommand(
10
+ customerConfig: MultiCustomerConfig,
11
+ args: CliArgs,
12
+ verbose: boolean = false
13
+ ): Promise<void> {
14
+ try {
15
+ const selectedCustomer = requireSingleCustomer(customerConfig, args.customer as string | undefined);
16
+
17
+ // Parse arguments
18
+ const projectIdn = args._[1] as string;
19
+ const registryIdn = args.registry as string || 'production';
20
+ const registryItemIdn = args.item as string;
21
+ const registryItemVersion = args.version as string | null || null;
22
+ const title = args.title as string || projectIdn || registryItemIdn;
23
+ const description = args.description as string || '';
24
+ const isAutoUpdateEnabled = Boolean(args['auto-update']);
25
+
26
+ // Validate required arguments
27
+ if (!registryItemIdn) {
28
+ console.error('Error: Registry item IDN is required');
29
+ console.error('');
30
+ console.error('Usage: newo add-project <project-idn> --item <registry-item-idn> [options]');
31
+ console.error('');
32
+ console.error('Options:');
33
+ console.error(' --item <idn> Registry item/template IDN (required)');
34
+ console.error(' --registry <idn> Registry to use (default: production)');
35
+ console.error(' --version <version> Specific version to install (default: latest)');
36
+ console.error(' --title <title> Project title (default: project IDN)');
37
+ console.error(' --description <desc> Project description');
38
+ console.error(' --auto-update Enable automatic updates from registry');
39
+ console.error('');
40
+ console.error('Examples:');
41
+ console.error(' newo add-project my_weather --item weather_integration');
42
+ console.error(' newo add-project my_calcom --item cal_com_integration --registry production');
43
+ console.error(' newo add-project my_zoho --item zoho_integration --version 1.0.2 --auto-update');
44
+ console.error('');
45
+ console.error('Run "newo list-registries" to see available registries');
46
+ console.error('Run "newo list-registry-items <registry-idn>" to see available project templates');
47
+ process.exit(1);
48
+ }
49
+
50
+ // Use registry item IDN as project IDN if not specified
51
+ const finalProjectIdn = projectIdn || registryItemIdn;
52
+
53
+ if (verbose) {
54
+ console.log(`📦 Adding project from registry`);
55
+ console.log(` Project IDN: ${finalProjectIdn}`);
56
+ console.log(` Title: ${title}`);
57
+ console.log(` Registry: ${registryIdn}`);
58
+ console.log(` Item: ${registryItemIdn}`);
59
+ console.log(` Version: ${registryItemVersion || 'latest'}`);
60
+ console.log(` Auto-update: ${isAutoUpdateEnabled}`);
61
+ console.log(` Customer: ${selectedCustomer.idn}`);
62
+ }
63
+
64
+ // Get access token and create client
65
+ const accessToken = await getValidAccessToken(selectedCustomer);
66
+ const client = await makeClient(verbose, accessToken);
67
+
68
+ // Validate registry exists
69
+ console.log(`🔍 Validating registry "${registryIdn}"...`);
70
+ const registries = await listRegistries(client);
71
+ const registry = registries.find((r: Registry) => r.idn === registryIdn);
72
+
73
+ if (!registry) {
74
+ console.error(`❌ Registry "${registryIdn}" not found`);
75
+ console.error('');
76
+ console.error('Available registries:');
77
+ for (const r of registries) {
78
+ console.error(` • ${r.idn}`);
79
+ }
80
+ process.exit(1);
81
+ }
82
+
83
+ // Validate registry item exists and find version
84
+ console.log(`🔍 Validating project template "${registryItemIdn}"...`);
85
+ const items = await listRegistryItems(client, registry.id);
86
+ const matchingItems = items.filter((item: RegistryItem) => item.idn === registryItemIdn);
87
+
88
+ if (matchingItems.length === 0) {
89
+ console.error(`❌ Project template "${registryItemIdn}" not found in "${registryIdn}" registry`);
90
+ console.error('');
91
+ console.error('Run "newo list-registry-items ' + registryIdn + '" to see available templates');
92
+ process.exit(1);
93
+ }
94
+
95
+ // Find the specific version or latest
96
+ let selectedItem: RegistryItem | undefined;
97
+ if (registryItemVersion) {
98
+ selectedItem = matchingItems.find((item: RegistryItem) => item.version === registryItemVersion);
99
+ if (!selectedItem) {
100
+ console.error(`❌ Version "${registryItemVersion}" not found for "${registryItemIdn}"`);
101
+ console.error('');
102
+ console.error('Available versions:');
103
+ const sortedItems = [...matchingItems].sort((a, b) =>
104
+ new Date(b.published_at).getTime() - new Date(a.published_at).getTime()
105
+ );
106
+ for (const item of sortedItems.slice(0, 10)) {
107
+ console.error(` • ${item.version} (published: ${new Date(item.published_at).toISOString().split('T')[0]})`);
108
+ }
109
+ if (sortedItems.length > 10) {
110
+ console.error(` ... and ${sortedItems.length - 10} more`);
111
+ }
112
+ process.exit(1);
113
+ }
114
+ } else {
115
+ // Get latest version (sorted by published_at desc)
116
+ const sortedItems = [...matchingItems].sort((a, b) =>
117
+ new Date(b.published_at).getTime() - new Date(a.published_at).getTime()
118
+ );
119
+ selectedItem = sortedItems[0];
120
+ }
121
+
122
+ if (!selectedItem) {
123
+ console.error(`❌ Could not determine version for "${registryItemIdn}"`);
124
+ process.exit(1);
125
+ }
126
+
127
+ console.log(`📥 Installing "${registryItemIdn}" v${selectedItem.version} as "${finalProjectIdn}"...`);
128
+
129
+ // Create project from registry
130
+ const projectData: AddProjectFromRegistryRequest = {
131
+ idn: finalProjectIdn,
132
+ title,
133
+ version: '',
134
+ description,
135
+ is_auto_update_enabled: isAutoUpdateEnabled,
136
+ registry_idn: registryIdn,
137
+ registry_item_idn: registryItemIdn,
138
+ registry_item_version: registryItemVersion
139
+ };
140
+
141
+ const response = await addProjectFromRegistry(client, projectData);
142
+
143
+ console.log('');
144
+ console.log(`✅ Project installed successfully!`);
145
+ console.log(` Project IDN: ${finalProjectIdn}`);
146
+ console.log(` Project ID: ${response.id}`);
147
+ console.log(` Source: ${registryItemIdn} v${selectedItem.version}`);
148
+ console.log(` Registry: ${registryIdn}`);
149
+ if (isAutoUpdateEnabled) {
150
+ console.log(` Auto-update: Enabled`);
151
+ }
152
+ console.log('');
153
+ console.log(`💡 Run "newo pull" to sync the project locally`);
154
+
155
+ } catch (error: unknown) {
156
+ console.error('❌ Failed to add project from registry:', error instanceof Error ? error.message : String(error));
157
+ process.exit(1);
158
+ }
159
+ }
@@ -19,7 +19,10 @@ Core Commands:
19
19
  newo import-akb <file> <persona_id> [--customer <idn>] # import AKB articles from structured text file
20
20
 
21
21
  Project Management:
22
- newo create-project <idn> [--title <title>] [--description <desc>] [--version <version>] [--auto-update] # create project on platform
22
+ newo create-project <idn> [--title <title>] [--description <desc>] [--version <version>] [--auto-update] # create empty project on platform
23
+ newo list-registries [--customer <idn>] # list available project registries (production, staging, etc.)
24
+ newo list-registry-items <registry-idn> [--all] # list available project templates in a registry
25
+ newo add-project <idn> --item <template-idn> [--registry <registry>] [--version <v>] [--auto-update] # install project from registry template
23
26
 
24
27
  Entity Management (Full Lifecycle Support):
25
28
  newo create-agent <idn> --project <project-idn> [--title <title>] [--description <desc>] # create agent → push to platform ✅
@@ -123,6 +126,14 @@ Usage Examples:
123
126
  # Import AKB articles:
124
127
  newo import-akb articles.txt da4550db-2b95-4500-91ff-fb4b60fe7be9
125
128
 
129
+ # Project Registry - Install templates from registry (NEW):
130
+ newo list-registries # See available registries
131
+ newo list-registry-items production # Browse production templates
132
+ newo list-registry-items production --all # See all versions
133
+ newo add-project my_calcom --item cal_com_integration # Install latest version
134
+ newo add-project my_zoho --item zoho_integration --version 1.0.2 # Install specific version
135
+ newo add-project my_weather --item weather_integration --auto-update # With auto-updates
136
+
126
137
  # Sandbox testing (NEW v3.1.0):
127
138
  newo sandbox "Hello, I want to order pizza" # Start new conversation
128
139
  newo sandbox --actor abc123... "I want 2 large pizzas" # Continue conversation
@@ -0,0 +1,53 @@
1
+ /**
2
+ * List Registries Command Handler - Lists available project registries
3
+ */
4
+ import { makeClient, listRegistries } from '../../api.js';
5
+ import { getValidAccessToken } from '../../auth.js';
6
+ import { requireSingleCustomer } from '../customer-selection.js';
7
+ import type { MultiCustomerConfig, CliArgs } from '../../types.js';
8
+
9
+ export async function handleListRegistriesCommand(
10
+ customerConfig: MultiCustomerConfig,
11
+ args: CliArgs,
12
+ verbose: boolean = false
13
+ ): Promise<void> {
14
+ try {
15
+ const selectedCustomer = requireSingleCustomer(customerConfig, args.customer as string | undefined);
16
+
17
+ if (verbose) {
18
+ console.log(`📋 Fetching registries for customer: ${selectedCustomer.idn}`);
19
+ }
20
+
21
+ // Get access token and create client
22
+ const accessToken = await getValidAccessToken(selectedCustomer);
23
+ const client = await makeClient(verbose, accessToken);
24
+
25
+ console.log('🔍 Fetching available project registries...\n');
26
+
27
+ const registries = await listRegistries(client);
28
+
29
+ if (registries.length === 0) {
30
+ console.log('No registries found.');
31
+ return;
32
+ }
33
+
34
+ console.log(`✅ Found ${registries.length} registries:\n`);
35
+
36
+ // Display registries in a table-like format
37
+ console.log(' IDN │ Public │ ID');
38
+ console.log(' ────────────────────────┼────────┼────────────────────────────────────');
39
+
40
+ for (const registry of registries) {
41
+ const publicStatus = registry.is_public ? 'Yes' : 'No';
42
+ const idnPadded = registry.idn.padEnd(22);
43
+ const publicPadded = publicStatus.padEnd(6);
44
+ console.log(` ${idnPadded} │ ${publicPadded} │ ${registry.id}`);
45
+ }
46
+
47
+ console.log('\n💡 Use "newo list-registry-items <registry-idn>" to see available projects in a registry');
48
+
49
+ } catch (error: unknown) {
50
+ console.error('❌ Failed to list registries:', error instanceof Error ? error.message : String(error));
51
+ process.exit(1);
52
+ }
53
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * List Registry Items Command Handler - Lists available projects in a registry
3
+ */
4
+ import { makeClient, listRegistries, listRegistryItems } from '../../api.js';
5
+ import { getValidAccessToken } from '../../auth.js';
6
+ import { requireSingleCustomer } from '../customer-selection.js';
7
+ import type { MultiCustomerConfig, CliArgs, Registry, RegistryItem } from '../../types.js';
8
+
9
+ interface GroupedItem {
10
+ idn: string;
11
+ versions: RegistryItem[];
12
+ latestVersion: string;
13
+ totalActiveProjects: number;
14
+ }
15
+
16
+ function groupItemsByIdn(items: RegistryItem[]): GroupedItem[] {
17
+ const groups = new Map<string, RegistryItem[]>();
18
+
19
+ for (const item of items) {
20
+ const existing = groups.get(item.idn) || [];
21
+ existing.push(item);
22
+ groups.set(item.idn, existing);
23
+ }
24
+
25
+ const result: GroupedItem[] = [];
26
+
27
+ for (const [idn, versions] of groups) {
28
+ // Sort versions by published_at descending (newest first)
29
+ const sortedVersions = [...versions].sort((a, b) =>
30
+ new Date(b.published_at).getTime() - new Date(a.published_at).getTime()
31
+ );
32
+
33
+ const latestItem = sortedVersions[0];
34
+ if (!latestItem) continue;
35
+
36
+ // Sum up active project counts across all versions
37
+ const totalActiveProjects = versions.reduce((sum, v) => sum + v.active_project_count, 0);
38
+
39
+ result.push({
40
+ idn,
41
+ versions: sortedVersions,
42
+ latestVersion: latestItem.version,
43
+ totalActiveProjects
44
+ });
45
+ }
46
+
47
+ // Sort by IDN alphabetically
48
+ return result.sort((a, b) => a.idn.localeCompare(b.idn));
49
+ }
50
+
51
+ export async function handleListRegistryItemsCommand(
52
+ customerConfig: MultiCustomerConfig,
53
+ args: CliArgs,
54
+ verbose: boolean = false
55
+ ): Promise<void> {
56
+ try {
57
+ const selectedCustomer = requireSingleCustomer(customerConfig, args.customer as string | undefined);
58
+
59
+ // Parse arguments
60
+ const registryIdn = args._[1] as string;
61
+ const showAllVersions = Boolean(args.all || args.a);
62
+
63
+ if (!registryIdn) {
64
+ console.error('Error: Registry IDN is required');
65
+ console.error('Usage: newo list-registry-items <registry-idn> [--all]');
66
+ console.error('');
67
+ console.error('Examples:');
68
+ console.error(' newo list-registry-items production');
69
+ console.error(' newo list-registry-items staging --all');
70
+ console.error('');
71
+ console.error('Run "newo list-registries" to see available registries');
72
+ process.exit(1);
73
+ }
74
+
75
+ if (verbose) {
76
+ console.log(`📋 Fetching items from registry: ${registryIdn}`);
77
+ console.log(` Customer: ${selectedCustomer.idn}`);
78
+ console.log(` Show all versions: ${showAllVersions}`);
79
+ }
80
+
81
+ // Get access token and create client
82
+ const accessToken = await getValidAccessToken(selectedCustomer);
83
+ const client = await makeClient(verbose, accessToken);
84
+
85
+ // First, get registries to find the ID
86
+ console.log(`🔍 Fetching registry "${registryIdn}"...`);
87
+ const registries = await listRegistries(client);
88
+ const registry = registries.find((r: Registry) => r.idn === registryIdn);
89
+
90
+ if (!registry) {
91
+ console.error(`❌ Registry "${registryIdn}" not found`);
92
+ console.error('');
93
+ console.error('Available registries:');
94
+ for (const r of registries) {
95
+ console.error(` • ${r.idn}`);
96
+ }
97
+ process.exit(1);
98
+ }
99
+
100
+ console.log(`📦 Fetching projects from "${registryIdn}" registry (this may take a moment)...\n`);
101
+
102
+ const items = await listRegistryItems(client, registry.id);
103
+
104
+ if (items.length === 0) {
105
+ console.log(`No projects found in "${registryIdn}" registry.`);
106
+ return;
107
+ }
108
+
109
+ if (showAllVersions) {
110
+ // Show all versions
111
+ console.log(`✅ Found ${items.length} project versions in "${registryIdn}" registry:\n`);
112
+
113
+ console.log(' Project IDN │ Version │ Active │ Published');
114
+ console.log(' ───────────────────────┼──────────┼────────┼────────────────────');
115
+
116
+ for (const item of items) {
117
+ const idnPadded = item.idn.substring(0, 21).padEnd(21);
118
+ const versionPadded = item.version.substring(0, 8).padEnd(8);
119
+ const activePadded = String(item.active_project_count).padEnd(6);
120
+ const published = new Date(item.published_at).toISOString().split('T')[0];
121
+ console.log(` ${idnPadded} │ ${versionPadded} │ ${activePadded} │ ${published}`);
122
+ }
123
+ } else {
124
+ // Group by project IDN and show only latest version
125
+ const grouped = groupItemsByIdn(items);
126
+
127
+ console.log(`✅ Found ${grouped.length} unique projects in "${registryIdn}" registry:\n`);
128
+
129
+ console.log(' Project IDN │ Latest │ Active │ Versions');
130
+ console.log(' ───────────────────────┼──────────┼────────┼──────────');
131
+
132
+ for (const group of grouped) {
133
+ const idnPadded = group.idn.substring(0, 21).padEnd(21);
134
+ const versionPadded = group.latestVersion.substring(0, 8).padEnd(8);
135
+ const activePadded = String(group.totalActiveProjects).padEnd(6);
136
+ const versionCount = String(group.versions.length).padEnd(8);
137
+ console.log(` ${idnPadded} │ ${versionPadded} │ ${activePadded} │ ${versionCount}`);
138
+ }
139
+
140
+ console.log('\n💡 Use --all flag to see all versions');
141
+ }
142
+
143
+ console.log(`\n💡 Use "newo add-project <idn> --registry ${registryIdn} --item <project-idn>" to install a project`);
144
+
145
+ } catch (error: unknown) {
146
+ console.error('❌ Failed to list registry items:', error instanceof Error ? error.message : String(error));
147
+ process.exit(1);
148
+ }
149
+ }
package/src/cli.ts CHANGED
@@ -38,6 +38,9 @@ import { handlePushAkbCommand } from './cli/commands/push-akb.js';
38
38
  import { handleMigrateAccountCommand } from './cli/commands/migrate-account.js';
39
39
  import { handleVerifyMigrationCommand } from './cli/commands/verify-migration.js';
40
40
  import { handleCreateWebhooksCommand } from './cli/commands/create-webhooks.js';
41
+ import { handleListRegistriesCommand } from './cli/commands/list-registries.js';
42
+ import { handleListRegistryItemsCommand } from './cli/commands/list-registry-items.js';
43
+ import { handleAddProjectCommand } from './cli/commands/add-project.js';
41
44
  import type { CliArgs, NewoApiError } from './types.js';
42
45
 
43
46
  dotenv.config();
@@ -204,6 +207,18 @@ async function main(): Promise<void> {
204
207
  await handleCreateWebhooksCommand(customerConfig, args, verbose);
205
208
  break;
206
209
 
210
+ case 'list-registries':
211
+ await handleListRegistriesCommand(customerConfig, args, verbose);
212
+ break;
213
+
214
+ case 'list-registry-items':
215
+ await handleListRegistryItemsCommand(customerConfig, args, verbose);
216
+ break;
217
+
218
+ case 'add-project':
219
+ await handleAddProjectCommand(customerConfig, args, verbose);
220
+ break;
221
+
207
222
  default:
208
223
  console.error('Unknown command:', cmd);
209
224
  console.error('Run "newo --help" for usage information');
package/src/types.ts CHANGED
@@ -831,4 +831,47 @@ export interface AkbYamlTopic {
831
831
  updated_at: string;
832
832
  labels: string[];
833
833
  topic_summary: string;
834
+ }
835
+
836
+ // Registry Types (for project marketplace/templates)
837
+ export interface Registry {
838
+ readonly id: string;
839
+ readonly idn: string;
840
+ readonly account_id: string;
841
+ readonly is_public: boolean;
842
+ }
843
+
844
+ export interface RegistryItemProjectImage {
845
+ readonly id: string;
846
+ readonly idn: string;
847
+ readonly image_hash: string;
848
+ readonly storage_id: string;
849
+ readonly customer_idn: string;
850
+ readonly account_id: string;
851
+ readonly created_at: string;
852
+ }
853
+
854
+ export interface RegistryItem {
855
+ readonly id: string;
856
+ readonly idn: string;
857
+ readonly version: string;
858
+ readonly project_image: RegistryItemProjectImage;
859
+ readonly account_id: string;
860
+ readonly account_idn: string | null;
861
+ readonly account_email: string | null;
862
+ readonly is_public: boolean;
863
+ readonly active_project_count: number;
864
+ readonly created_at: string;
865
+ readonly published_at: string;
866
+ }
867
+
868
+ export interface AddProjectFromRegistryRequest {
869
+ idn: string;
870
+ title: string;
871
+ version?: string;
872
+ description?: string;
873
+ is_auto_update_enabled?: boolean;
874
+ registry_idn: string;
875
+ registry_item_idn: string;
876
+ registry_item_version: string | null;
834
877
  }