@notis_ai/cli 0.2.0-beta.41.1 → 0.2.0-beta.57.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -179,9 +179,9 @@ Examples:
179
179
 
180
180
  ### `npx --package @notis_ai/cli@latest -- notis tools toolkits`
181
181
 
182
- List toolkit namespaces available to the active user.
182
+ List toolkit namespaces and connection statuses available to the active user.
183
183
 
184
- When to use: Use this before searching or executing generic tools.
184
+ When to use: Use this to inspect connection state before searching or executing generic tools.
185
185
 
186
186
  Examples:
187
187
  - `npx --package @notis_ai/cli@latest -- notis tools toolkits`
@@ -194,11 +194,11 @@ Search across toolkit namespaces using natural language.
194
194
  When to use: Use this when you need a generic capability that does not have a first-class CLI command.
195
195
 
196
196
  Options:
197
- - `--toolkits <csv-or-json>` — Optional subset of toolkit ids to search.
197
+ - `--known-fields <text>` — Optional known field hints, such as channel_name:general or user_email:a@example.com.
198
198
 
199
199
  Examples:
200
200
  - `npx --package @notis_ai/cli@latest -- notis tools search "send an email"`
201
- - `npx --package @notis_ai/cli@latest -- notis tools search "update framer page" --toolkits mcp-framer`
201
+ - `npx --package @notis_ai/cli@latest -- notis tools search "post on LinkedIn" --known-fields "platform:linkedin"`
202
202
 
203
203
  ### `npx --package @notis_ai/cli@latest -- notis tools describe <tool-name>`
204
204
 
@@ -206,12 +206,9 @@ Describe a generic tool by name.
206
206
 
207
207
  When to use: Use this when you know the tool name and want its parameter schema before execution.
208
208
 
209
- Options:
210
- - `--toolkits <csv-or-json>` — Optional subset of toolkit ids to search.
211
-
212
209
  Examples:
213
- - `npx --package @notis_ai/cli@latest -- notis tools describe composio-gmail-default-send_email`
214
- - `npx --package @notis_ai/cli@latest -- notis tools describe notis-default-query --toolkits notis-default`
210
+ - `npx --package @notis_ai/cli@latest -- notis tools describe composio-gmail-send_email`
211
+ - `npx --package @notis_ai/cli@latest -- notis tools describe LOCAL_NOTIS_DATABASE_QUERY`
215
212
 
216
213
  ### `npx --package @notis_ai/cli@latest -- notis tools exec <tool-name>`
217
214
 
@@ -224,16 +221,15 @@ Options:
224
221
  - `--file <argument-path=local-path>` — Upload a local file into a file-uploadable tool argument. Repeatable.
225
222
  - `--get-schema` — Display the tool parameter schema without executing.
226
223
  - `--dry-run` — Validate arguments against the tool schema without executing.
227
- - `--toolkits <csv-or-json>` — Optional toolkit namespace(s) to use when resolving the tool.
228
224
 
229
225
  Examples:
230
- - `npx --package @notis_ai/cli@latest -- notis tools exec notis-default-query --arguments '{"database_slug":"tasks","query":{}}'`
231
- - `npx --package @notis_ai/cli@latest -- notis tools exec notis-default-get_database --arguments '{"database_slug":"tasks"}'`
232
- - `npx --package @notis_ai/cli@latest -- notis tools exec notis-default-query --get-schema`
233
- - `npx --package @notis_ai/cli@latest -- notis tools exec notis-default-query --dry-run --arguments '{"database_slug":"tasks","query":{}}'`
234
- - `npx --package @notis_ai/cli@latest -- notis tools exec notis-default-query --arguments @query.json`
235
- - `npx --package @notis_ai/cli@latest -- notis tools exec notis-default-query --arguments - < query.json`
236
- - `npx --package @notis_ai/cli@latest -- notis tools exec composio-dropbox-default-upload_file --arguments '{"path":"/target/in/dropbox.pdf"}' --file content=./Invoice.pdf`
226
+ - `npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments '{"database_slug":"tasks","query":{}}'`
227
+ - `npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --arguments '{"database_slug":"tasks"}'`
228
+ - `npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --get-schema`
229
+ - `npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --dry-run --arguments '{"database_slug":"tasks","query":{}}'`
230
+ - `npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments @query.json`
231
+ - `npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments - < query.json`
232
+ - `npx --package @notis_ai/cli@latest -- notis tools exec composio-dropbox-upload_file --arguments '{"path":"/target/in/dropbox.pdf"}' --file content=./Invoice.pdf`
237
233
 
238
234
  ### `npx --package @notis_ai/cli@latest -- notis tools exec-parallel <calls>`
239
235
 
@@ -245,7 +241,7 @@ Options:
245
241
  - `--file <argument-path=local-path>` — Unsupported for exec-parallel; use tools exec for file uploads.
246
242
 
247
243
  Examples:
248
- - `npx --package @notis_ai/cli@latest -- notis tools exec-parallel '[{"tool_name":"notis-default-query","arguments":{"database_slug":"tasks","query":{}}},{"tool_name":"notis-default-list_databases","arguments":{}}]'`
244
+ - `npx --package @notis_ai/cli@latest -- notis tools exec-parallel '[{"tool_name":"LOCAL_NOTIS_DATABASE_QUERY","arguments":{"database_slug":"tasks","query":{}}},{"tool_name":"LOCAL_NOTIS_DATABASE_LIST_DATABASES","arguments":{}}]'`
249
245
 
250
246
  ### `npx --package @notis_ai/cli@latest -- notis tools link <toolkit>`
251
247
 
@@ -272,7 +268,7 @@ Examples:
272
268
 
273
269
  ### `npx --package @notis_ai/cli@latest -- notis whoami`
274
270
 
275
- Display the active profile, user, and available toolkits.
271
+ Display the active profile, user, and available toolkit connection statuses.
276
272
 
277
273
  When to use: Use this to quickly confirm which account and environment a command will target.
278
274
 
@@ -224,13 +224,13 @@ type ActiveTab = 'properties' | 'relations' | 'documents';
224
224
 
225
225
  export default function CatalogPage() {
226
226
  const listDatabases = useTool<ListDatabasesArgs, ListDatabasesResult>(
227
- 'notis-default-list_databases',
227
+ 'LOCAL_NOTIS_DATABASE_LIST_DATABASES',
228
228
  );
229
229
  const getDatabase = useTool<GetDatabaseArgs, GetDatabaseResult>(
230
- 'notis-default-get_database',
230
+ 'LOCAL_NOTIS_DATABASE_GET_DATABASE',
231
231
  );
232
232
  const queryDatabase = useTool<QueryDatabaseArgs, QueryDatabaseResult>(
233
- 'notis-default-query',
233
+ 'LOCAL_NOTIS_DATABASE_QUERY',
234
234
  );
235
235
 
236
236
  const [rows, setRows] = useState<DatabaseCatalogRow[]>([]);
@@ -20,8 +20,8 @@ export default defineNotisApp({
20
20
  },
21
21
  ],
22
22
  tools: [
23
- 'notis-default-list_databases',
24
- 'notis-default-get_database',
25
- 'notis-default-query',
23
+ 'LOCAL_NOTIS_DATABASE_LIST_DATABASES',
24
+ 'LOCAL_NOTIS_DATABASE_GET_DATABASE',
25
+ 'LOCAL_NOTIS_DATABASE_QUERY',
26
26
  ],
27
27
  });
@@ -505,21 +505,21 @@ export function installMockRuntime(): NotisRuntime {
505
505
  },
506
506
  context: {},
507
507
  listTools: async () => [
508
- { name: 'notis-default-list_databases', inputSchema: { type: 'object', properties: {} } },
509
- { name: 'notis-default-get_database', inputSchema: { type: 'object', properties: {} } },
510
- { name: 'notis-default-query', inputSchema: { type: 'object', properties: {} } },
508
+ { name: 'LOCAL_NOTIS_DATABASE_LIST_DATABASES', inputSchema: { type: 'object', properties: {} } },
509
+ { name: 'LOCAL_NOTIS_DATABASE_GET_DATABASE', inputSchema: { type: 'object', properties: {} } },
510
+ { name: 'LOCAL_NOTIS_DATABASE_QUERY', inputSchema: { type: 'object', properties: {} } },
511
511
  ],
512
512
  callTool: async <TResult = unknown>(
513
513
  name: string,
514
514
  args?: Record<string, unknown>,
515
515
  ): Promise<TResult> => {
516
- if (name === 'notis-default-list_databases') {
516
+ if (name === 'LOCAL_NOTIS_DATABASE_LIST_DATABASES') {
517
517
  return {
518
518
  databases: SEED.map((s) => s.catalog),
519
519
  total_count: SEED.length,
520
520
  } as TResult;
521
521
  }
522
- if (name === 'notis-default-get_database') {
522
+ if (name === 'LOCAL_NOTIS_DATABASE_GET_DATABASE') {
523
523
  const request = args ?? {};
524
524
  const id = typeof request.database_id === 'string' ? request.database_id : null;
525
525
  const slug = typeof request.database_slug === 'string' ? request.database_slug : null;
@@ -531,7 +531,7 @@ export function installMockRuntime(): NotisRuntime {
531
531
  }
532
532
  return { database: match.detail } as TResult;
533
533
  }
534
- if (name === 'notis-default-query') {
534
+ if (name === 'LOCAL_NOTIS_DATABASE_QUERY') {
535
535
  const request = args ?? {};
536
536
  const slug = typeof request.database_slug === 'string' ? request.database_slug : null;
537
537
  const match = SEED.find((s) => slug && s.detail.slug === slug);
@@ -358,7 +358,7 @@ function normalizeCollectionItem(value: unknown): CollectionItemContext | null {
358
358
  }
359
359
 
360
360
  function useNoteSchema(databaseSlug: string) {
361
- const getDatabase = useTool<GetDatabaseArgs, GetDatabaseResult>('notis-default-get_database');
361
+ const getDatabase = useTool<GetDatabaseArgs, GetDatabaseResult>('LOCAL_NOTIS_DATABASE_GET_DATABASE');
362
362
  const [properties, setProperties] = useState<DatabaseProperty[]>([]);
363
363
  const [loading, setLoading] = useState(true);
364
364
  const [error, setError] = useState<Error | null>(null);
@@ -399,7 +399,7 @@ function useNoteSchema(databaseSlug: string) {
399
399
  }
400
400
 
401
401
  function useQueryDocuments(databaseSlug: string, options: { filter?: QueryFilter; pageSize?: number; offset?: number } = {}) {
402
- const queryTool = useTool<QueryDatabaseArgs, QueryDatabaseResult>('notis-default-query');
402
+ const queryTool = useTool<QueryDatabaseArgs, QueryDatabaseResult>('LOCAL_NOTIS_DATABASE_QUERY');
403
403
  const [documents, setDocuments] = useState<DocumentRecord[]>([]);
404
404
  const [loading, setLoading] = useState(true);
405
405
  const [error, setError] = useState<Error | null>(null);
@@ -1061,7 +1061,7 @@ export default function NotesPage() {
1061
1061
  const { app, route, context } = useNotis();
1062
1062
  const navigation = useNotisNavigation();
1063
1063
  const { request } = useBackend();
1064
- const upsertNote = useTool<UpsertNoteArgs, UpsertNoteResult>('notis-default-upsert_notes');
1064
+ const upsertNote = useTool<UpsertNoteArgs, UpsertNoteResult>('LOCAL_NOTIS_DATABASE_UPSERT_NOTES');
1065
1065
 
1066
1066
  const [activeTab, setActiveTab] = useState<TabKey>('gallery');
1067
1067
  const [activeDateProperty, setActiveDateProperty] = useState('');
@@ -29,8 +29,8 @@ export default defineNotisApp({
29
29
  },
30
30
  ],
31
31
  tools: [
32
- 'notis-default-query',
33
- 'notis-default-get_database',
34
- 'notis-default-upsert_notes',
32
+ 'LOCAL_NOTIS_DATABASE_QUERY',
33
+ 'LOCAL_NOTIS_DATABASE_GET_DATABASE',
34
+ 'LOCAL_NOTIS_DATABASE_UPSERT_NOTES',
35
35
  ],
36
36
  });
@@ -37,7 +37,7 @@ type UpsertRollResult = {
37
37
  };
38
38
 
39
39
  export function useRollDocuments(pageSize?: number) {
40
- const queryRolls = useTool<QueryRollsArgs, QueryRollsResult>('notis-default-query');
40
+ const queryRolls = useTool<QueryRollsArgs, QueryRollsResult>('LOCAL_NOTIS_DATABASE_QUERY');
41
41
  const [documents, setDocuments] = useState<RollDocument[]>([]);
42
42
  const [error, setError] = useState<Error | null>(null);
43
43
  const [fetchKey, setFetchKey] = useState(0);
@@ -80,7 +80,7 @@ export function useRollDocuments(pageSize?: number) {
80
80
  }
81
81
 
82
82
  export function usePersistRoll() {
83
- const upsertRoll = useTool<UpsertRollArgs, UpsertRollResult>('notis-default-upsert_rolls');
83
+ const upsertRoll = useTool<UpsertRollArgs, UpsertRollResult>('LOCAL_NOTIS_DATABASE_UPSERT_ROLLS');
84
84
 
85
85
  const persist = useCallback(
86
86
  async (roll: Roll) => {
@@ -37,7 +37,7 @@ export default defineNotisApp({
37
37
  ],
38
38
 
39
39
  tools: [
40
- 'notis-default-query',
41
- 'notis-default-upsert_rolls',
40
+ 'LOCAL_NOTIS_DATABASE_QUERY',
41
+ 'LOCAL_NOTIS_DATABASE_UPSERT_ROLLS',
42
42
  ],
43
43
  });
@@ -34,16 +34,16 @@ export function installMockRuntime(initialRoute: 'home' | 'history' = 'home'): N
34
34
 
35
35
  listTools: async () => [
36
36
  {
37
- name: 'notis-default-query',
37
+ name: 'LOCAL_NOTIS_DATABASE_QUERY',
38
38
  inputSchema: { type: 'object', properties: { database_slug: { type: 'string' } } },
39
39
  },
40
40
  {
41
- name: 'notis-default-upsert_rolls',
41
+ name: 'LOCAL_NOTIS_DATABASE_UPSERT_ROLLS',
42
42
  inputSchema: { type: 'object', properties: {} },
43
43
  },
44
44
  ],
45
45
  callTool: async <TResult = unknown>(name: string, args?: Record<string, unknown>): Promise<TResult> => {
46
- if (name === 'notis-default-query') {
46
+ if (name === 'LOCAL_NOTIS_DATABASE_QUERY') {
47
47
  const request = args ?? {};
48
48
  const databaseSlug = typeof request.database_slug === 'string' ? request.database_slug : 'rolls';
49
49
  const query = request.query && typeof request.query === 'object'
@@ -56,7 +56,7 @@ export function installMockRuntime(initialRoute: 'home' | 'history' = 'home'): N
56
56
  return { documents: typeof pageSize === 'number' ? sliced.slice(0, pageSize) : sliced } as TResult;
57
57
  }
58
58
 
59
- if (name === 'notis-default-upsert_rolls') {
59
+ if (name === 'LOCAL_NOTIS_DATABASE_UPSERT_ROLLS') {
60
60
  const request = args ?? {};
61
61
  const docs = STATE.rolls ?? [];
62
62
  const now = new Date().toISOString();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notis_ai/cli",
3
- "version": "0.2.0-beta.41.1",
3
+ "version": "0.2.0-beta.57.1",
4
4
  "description": "Agent-first Notis CLI for apps and generic tool execution",
5
5
  "type": "module",
6
6
  "bin": {
@@ -138,7 +138,7 @@ export default defineNotisApp({
138
138
  default: true,
139
139
  },
140
140
  ],
141
- tools: ['notis-default-query', 'notis-default-upsert_tasks'],
141
+ tools: ['LOCAL_NOTIS_DATABASE_QUERY', 'LOCAL_NOTIS_DATABASE_UPSERT_TASKS'],
142
142
  });
143
143
  ```
144
144
 
@@ -149,7 +149,7 @@ Use SDK hooks for data and tool access:
149
149
  ```tsx
150
150
  import { useTool } from '@notis/sdk';
151
151
 
152
- const query = useTool('notis-default-query');
152
+ const query = useTool('LOCAL_NOTIS_DATABASE_QUERY');
153
153
  ```
154
154
 
155
155
  Do not fetch Notis backend endpoints directly from app code unless the SDK explicitly requires it. The Portal provides authentication, runtime context, and route rendering.
@@ -130,7 +130,7 @@ This is the main escape hatch for:
130
130
  - `npx --package @notis_ai/cli@latest -- notis tools toolkits`
131
131
  2. Search for the capability you need using natural language:
132
132
  - `npx --package @notis_ai/cli@latest -- notis tools search "<query>"`
133
- - optionally narrow with `--toolkits <csv-or-json>`
133
+ - optionally add known field hints with `--known-fields "<key:value>"`
134
134
  3. If needed, inspect the exact tool and parameter schema:
135
135
  - `npx --package @notis_ai/cli@latest -- notis tools describe <tool-name>`
136
136
  - `npx --package @notis_ai/cli@latest -- notis tools exec <tool-name> --get-schema`
@@ -155,7 +155,7 @@ This is the main escape hatch for:
155
155
 
156
156
  Typical toolkit namespaces include:
157
157
 
158
- - `notis-default` for native Notis tools
158
+ - `notis` for native Notis tools
159
159
  - `composio-*` for Composio-backed integrations
160
160
  - `mcp-*` for MCP-backed tools
161
161
 
@@ -178,22 +178,22 @@ npx --package @notis_ai/cli@latest -- notis tools search "list today's calendar
178
178
  Inspect a tool before execution:
179
179
 
180
180
  ```bash
181
- npx --package @notis_ai/cli@latest -- notis tools describe composio-googlecalendar-default-list_events
182
- npx --package @notis_ai/cli@latest -- notis tools exec composio-googlecalendar-default-list_events --get-schema
181
+ npx --package @notis_ai/cli@latest -- notis tools describe composio-googlecalendar-list_events
182
+ npx --package @notis_ai/cli@latest -- notis tools exec composio-googlecalendar-list_events --get-schema
183
183
  ```
184
184
 
185
185
  Dry-run a tool call:
186
186
 
187
187
  ```bash
188
- npx --package @notis_ai/cli@latest -- notis tools exec notis-default-query --dry-run --arguments '{"database_slug":"tasks","query":{"page_size":10}}'
189
- npx --package @notis_ai/cli@latest -- notis tools exec notis-default-get_database --dry-run --arguments '{"database_slug":"tasks"}'
188
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --dry-run --arguments '{"database_slug":"tasks"}'
189
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --dry-run --arguments '{"database_id":"tasks-db-id","query":{"page_size":10}}'
190
190
  ```
191
191
 
192
192
  Execute a tool call:
193
193
 
194
194
  ```bash
195
- npx --package @notis_ai/cli@latest -- notis tools exec notis-default-query --arguments '{"database_slug":"tasks","query":{"page_size":10}}'
196
- npx --package @notis_ai/cli@latest -- notis tools exec notis-default-get_database --arguments '{"database_slug":"tasks"}'
195
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --arguments '{"database_slug":"tasks"}'
196
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments '{"database_id":"tasks-db-id","query":{"page_size":10}}'
197
197
  ```
198
198
 
199
199
  Connect a missing toolkit:
@@ -206,21 +206,23 @@ npx --package @notis_ai/cli@latest -- notis tools link github
206
206
 
207
207
  Native Notis databases are accessed through the generic tool workflow, not a first-class database command group. Use these canonical tool names:
208
208
 
209
- - `notis-default-list_databases` -- list databases accessible to the current profile
210
- - `notis-default-get_database` -- inspect read-only metadata and schema detail
211
- - `notis-default-query` -- query documents from a database
212
- - `notis-default-upsert_database` -- create or update a database schema
209
+ - `LOCAL_NOTIS_DATABASE_LIST_DATABASES` -- list databases accessible to the current profile
210
+ - `LOCAL_NOTIS_DATABASE_GET_DATABASE` -- inspect read-only metadata and schema detail
211
+ - `LOCAL_NOTIS_DATABASE_QUERY` -- query documents from a database
212
+ - `LOCAL_NOTIS_DATABASE_UPSERT_DATABASE` -- create or update a database schema
213
213
 
214
214
  Example workflow before building an app:
215
215
 
216
216
  ```bash
217
217
  npx --package @notis_ai/cli@latest -- notis tools search "list Notis databases"
218
- npx --package @notis_ai/cli@latest -- notis tools exec notis-default-list_databases --arguments '{}'
219
- npx --package @notis_ai/cli@latest -- notis tools exec notis-default-get_database --get-schema
220
- npx --package @notis_ai/cli@latest -- notis tools exec notis-default-get_database --arguments '{"database_slug":"social_media_calendar"}'
221
- npx --package @notis_ai/cli@latest -- notis tools exec notis-default-query --arguments '{"database_slug":"social_media_calendar","query":{"page_size":1}}'
218
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_LIST_DATABASES --arguments '{}'
219
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --get-schema
220
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --arguments '{"database_slug":"social_media_calendar"}'
221
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments '{"database_id":"social-media-calendar-db-id","query":{"page_size":1}}'
222
222
  ```
223
223
 
224
+ When `LOCAL_NOTIS_DATABASE_LIST_DATABASES` or `LOCAL_NOTIS_DATABASE_GET_DATABASE` returns a database ID, prefer `database_id` for `LOCAL_NOTIS_DATABASE_QUERY`; `database_slug` remains supported as a fallback.
225
+
224
226
  ## Supporting commands
225
227
 
226
228
  - `npx --package @notis_ai/cli@latest -- notis doctor` — verify CLI config, auth, and API reachability before relying on the CLI
@@ -10,20 +10,20 @@ For CI, hosted agents, or internal scripts, pass a non-persisted token with `NOT
10
10
 
11
11
  ## Canonical database tools
12
12
 
13
- - `notis-default-list_databases` — list native databases.
14
- - `notis-default-get_database` — inspect one database schema.
15
- - `notis-default-query` — query native database documents.
16
- - `notis-default-upsert_database` — create or update database schema.
13
+ - `LOCAL_NOTIS_DATABASE_LIST_DATABASES` — list native databases.
14
+ - `LOCAL_NOTIS_DATABASE_GET_DATABASE` — inspect one database schema.
15
+ - `LOCAL_NOTIS_DATABASE_QUERY` — query native database documents.
16
+ - `LOCAL_NOTIS_DATABASE_UPSERT_DATABASE` — create or update database schema.
17
17
 
18
18
  ## Workflow
19
19
 
20
20
  ```bash
21
21
  npx --package @notis_ai/cli@latest -- notis tools search "list Notis databases"
22
- npx --package @notis_ai/cli@latest -- notis tools exec notis-default-list_databases --arguments '{}'
23
- npx --package @notis_ai/cli@latest -- notis tools exec notis-default-get_database --get-schema
24
- npx --package @notis_ai/cli@latest -- notis tools exec notis-default-get_database --arguments '{"database_slug":"tasks"}'
25
- npx --package @notis_ai/cli@latest -- notis tools exec notis-default-query --dry-run --arguments '{"database_slug":"tasks","query":{"page_size":10}}'
26
- npx --package @notis_ai/cli@latest -- notis tools exec notis-default-query --arguments '{"database_slug":"tasks","query":{"page_size":10}}'
22
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_LIST_DATABASES --arguments '{}'
23
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --get-schema
24
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --arguments '{"database_slug":"tasks"}'
25
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --dry-run --arguments '{"database_slug":"tasks","query":{"page_size":10}}'
26
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments '{"database_slug":"tasks","query":{"page_size":10}}'
27
27
  ```
28
28
 
29
29
  ## Supporting command
@@ -37,4 +37,3 @@ When to use: Use this before relying on the CLI in automation or after changing
37
37
  Examples:
38
38
  - `npx --package @notis_ai/cli@latest -- notis doctor`
39
39
  - `npx --package @notis_ai/cli@latest -- notis doctor --json`
40
-
@@ -46,6 +46,7 @@ import {
46
46
  import { getAvailablePort } from '../runtime/ports.js';
47
47
  import { getCliMode } from '../runtime/cli-mode.js';
48
48
  import {
49
+ localNotisToolSlug,
49
50
  nextIdempotencyKey,
50
51
  runToolCommand,
51
52
  toolConflictToError,
@@ -54,8 +55,11 @@ import {
54
55
  const DEFAULT_DEV_PORT = 5173;
55
56
  const DEV_HEARTBEAT_INTERVAL_MS = 10_000;
56
57
  const CONFIG_FILENAMES = ['notis.config.ts', 'notis.config.js', 'notis.config.mjs'];
57
- const ENSURE_DEV_APP_INSTALLATION_TOOL = 'notis-default-ensure_dev_app_installation';
58
- const GET_APP_TOOL = 'notis-default-get_app';
58
+ const ENSURE_DEV_APP_INSTALLATION_TOOL = 'LOCAL_NOTIS_ENSURE_DEV_APP_INSTALLATION';
59
+ const GET_APP_TOOL = 'LOCAL_NOTIS_GET_APP';
60
+ const LIST_APPS_TOOL = 'LOCAL_NOTIS_LIST_APPS';
61
+ const CREATE_APP_TOOL = 'LOCAL_NOTIS_CREATE_APP';
62
+ const SAVE_APP_FILES_TOOL = 'LOCAL_NOTIS_SAVE_APP_FILES';
59
63
 
60
64
  // ---------------------------------------------------------------------------
61
65
  // Formatters
@@ -192,9 +196,29 @@ function pickDefaultRouteSlug(manifest) {
192
196
  return firstWithSlug ? firstWithSlug.slug : null;
193
197
  }
194
198
 
195
- export function buildDevelopmentDesktopUrl(appHref = null) {
199
+ const DESKTOP_DEEP_LINK_SCHEME_PATTERN = /^[a-z][a-z0-9-]*$/;
200
+
201
+ export function resolveDevelopmentDesktopScheme(env = process.env) {
202
+ // Mirror the desktop app's own scheme resolution (electron main + forge config):
203
+ // local dev launches register `notis-dev`, while installed prod/beta builds claim
204
+ // `notis`. Hardcoding `notis` here is what made `apps dev` open the installed
205
+ // prod/beta app instead of the local dev app.
206
+ const scheme = (env.NOTIS_DESKTOP_DEEP_LINK_SCHEME || '').trim();
207
+ return DESKTOP_DEEP_LINK_SCHEME_PATTERN.test(scheme) ? scheme : 'notis';
208
+ }
209
+
210
+ export function buildDevelopmentDesktopUrl(appHref = null, scheme = 'notis') {
196
211
  const route = String(appHref || '/store').replace(/^\/+/, '');
197
- return `notis://${route || 'store'}`;
212
+ const normalizedScheme = DESKTOP_DEEP_LINK_SCHEME_PATTERN.test(scheme) ? scheme : 'notis';
213
+ return `${normalizedScheme}://${route || 'store'}`;
214
+ }
215
+
216
+ export function shouldOpenDevelopmentTab(options = {}) {
217
+ // Commander stores the negatable `--no-open` flag as `options.open === false`;
218
+ // it never sets `options.noOpen`. Reading the non-existent `noOpen` key meant
219
+ // `--no-open` was silently ignored and `apps dev` always ran `open notis://…`,
220
+ // which macOS routes to the installed prod/beta desktop app.
221
+ return options.open !== false;
198
222
  }
199
223
 
200
224
  function buildAppHref({ appSlug, appId, manifest }) {
@@ -311,14 +335,14 @@ function assertHarnessResult(result, route, databaseSlugs) {
311
335
  const seen = (result.runtimeCalls || []).some(
312
336
  (call) =>
313
337
  call?.op === 'callTool' &&
314
- call?.args?.name === 'notis-default-query' &&
338
+ localNotisToolSlug(call?.args?.name) === 'LOCAL_NOTIS_DATABASE_QUERY' &&
315
339
  call?.args?.arguments?.database_slug === databaseSlug,
316
340
  );
317
341
  if (!seen) {
318
342
  assertions.push({
319
343
  ok: false,
320
344
  code: 'missing_database_query',
321
- message: `Route "${route.slug}" did not call notis-default-query for "${databaseSlug}".`,
345
+ message: `Route "${route.slug}" did not call LOCAL_NOTIS_DATABASE_QUERY for "${databaseSlug}".`,
322
346
  details: { databaseSlug },
323
347
  });
324
348
  }
@@ -447,7 +471,7 @@ function databaseMaterializationWarnings(apps) {
447
471
  async function assertDirectDeployAccess(runtime, appId) {
448
472
  const result = await runToolCommand({
449
473
  runtime,
450
- toolName: 'notis_list_apps',
474
+ toolName: LIST_APPS_TOOL,
451
475
  });
452
476
  const apps = result.payload.apps || [];
453
477
  const hasAccess = apps.some((app) => (app.app_id || app.id) === appId);
@@ -463,7 +487,7 @@ async function assertDirectDeployAccess(runtime, appId) {
463
487
  async function appsListHandler(ctx) {
464
488
  const result = await runToolCommand({
465
489
  runtime: ctx.runtime,
466
- toolName: 'notis_list_apps',
490
+ toolName: LIST_APPS_TOOL,
467
491
  });
468
492
  const apps = result.payload.apps || [];
469
493
  return ctx.output.emitSuccess({
@@ -511,7 +535,7 @@ async function appsCreateHandler(ctx) {
511
535
  const idempotencyKey = nextIdempotencyKey(ctx.globalOptions);
512
536
  const result = await runToolCommand({
513
537
  runtime: ctx.runtime,
514
- toolName: 'notis_create_app',
538
+ toolName: CREATE_APP_TOOL,
515
539
  arguments_: {
516
540
  name: ctx.args.name,
517
541
  description: appConfig?.description || undefined,
@@ -634,7 +658,10 @@ async function appsDevHandler(ctx) {
634
658
  }),
635
659
  };
636
660
  });
637
- const developmentTabUrl = buildDevelopmentDesktopUrl(apps[0]?.appHref);
661
+ const developmentTabUrl = buildDevelopmentDesktopUrl(
662
+ apps[0]?.appHref,
663
+ resolveDevelopmentDesktopScheme(),
664
+ );
638
665
  const warnings = databaseMaterializationWarnings(apps);
639
666
 
640
667
  const devServer = await startAppDevServer({
@@ -710,7 +737,7 @@ async function appsDevHandler(ctx) {
710
737
  ].join('\n'),
711
738
  });
712
739
 
713
- if (!ctx.options.noOpen) {
740
+ if (shouldOpenDevelopmentTab(ctx.options)) {
714
741
  openInBrowser(developmentTabUrl);
715
742
  }
716
743
 
@@ -1073,7 +1100,7 @@ async function appsDeployHandler(ctx) {
1073
1100
  try {
1074
1101
  result = await runToolCommand({
1075
1102
  runtime: ctx.runtime,
1076
- toolName: 'notis_save_app_files',
1103
+ toolName: SAVE_APP_FILES_TOOL,
1077
1104
  arguments_: {
1078
1105
  app_id: appId,
1079
1106
  files,
@@ -1185,7 +1212,7 @@ export const appsCommandSpecs = [
1185
1212
  examples: ['notis apps list', 'notis apps list --json'],
1186
1213
  mutates: false,
1187
1214
  idempotent: true,
1188
- backend_call: { type: 'tool', name: 'notis_list_apps' },
1215
+ backend_call: { type: 'tool', name: LIST_APPS_TOOL },
1189
1216
  handler: appsListHandler,
1190
1217
  },
1191
1218
  {
@@ -1242,7 +1269,7 @@ export const appsCommandSpecs = [
1242
1269
  ],
1243
1270
  mutates: true,
1244
1271
  idempotent: false,
1245
- backend_call: { type: 'tool', name: 'notis_create_app' },
1272
+ backend_call: { type: 'tool', name: CREATE_APP_TOOL },
1246
1273
  handler: appsCreateHandler,
1247
1274
  },
1248
1275
  {
@@ -1376,7 +1403,7 @@ export const appsCommandSpecs = [
1376
1403
  examples: ['notis apps deploy', 'notis apps deploy --skip-build', 'notis apps deploy --app-id abc123', 'notis apps deploy --direct'],
1377
1404
  mutates: true,
1378
1405
  idempotent: true,
1379
- backend_call: { type: 'tool', name: 'notis_save_app_files' },
1406
+ backend_call: { type: 'tool', name: SAVE_APP_FILES_TOOL },
1380
1407
  handler: appsDeployHandler,
1381
1408
  },
1382
1409
  {
@@ -2,6 +2,34 @@ import { randomUUID } from 'node:crypto';
2
2
  import { CliError, EXIT_CODES, usageError } from '../runtime/errors.js';
3
3
  import { callTool, httpRequest } from '../runtime/transport.js';
4
4
 
5
+ export const COMPOSIO_SEARCH_TOOLS = 'COMPOSIO_SEARCH_TOOLS';
6
+ export const COMPOSIO_GET_TOOL_SCHEMAS = 'COMPOSIO_GET_TOOL_SCHEMAS';
7
+ export const COMPOSIO_MULTI_EXECUTE_TOOL = 'COMPOSIO_MULTI_EXECUTE_TOOL';
8
+
9
+ const NOTIS_DATABASE_CORE_NAMES = new Set([
10
+ 'query',
11
+ 'get_database',
12
+ 'get_document',
13
+ 'list_databases',
14
+ 'upsert_database',
15
+ ]);
16
+
17
+ function isNotisDatabaseCoreName(coreName) {
18
+ // Mirrors server is_database_tool_name: canonical database ops plus generated
19
+ // upsert_<db> tools. Database tools live in the NOTIS_DATABASE toolkit so their
20
+ // public slug carries the DATABASE segment.
21
+ return NOTIS_DATABASE_CORE_NAMES.has(coreName) || coreName.startsWith('upsert_');
22
+ }
23
+
24
+ export function localNotisToolSlug(toolName) {
25
+ if (typeof toolName !== 'string' || !toolName.startsWith('notis-')) {
26
+ return toolName;
27
+ }
28
+ const coreName = toolName.slice('notis-'.length);
29
+ const prefix = isNotisDatabaseCoreName(coreName) ? 'LOCAL_NOTIS_DATABASE_' : 'LOCAL_NOTIS_';
30
+ return `${prefix}${coreName.replace(/-/g, '_').toUpperCase()}`;
31
+ }
32
+
5
33
  export function parseJson(value, label) {
6
34
  try {
7
35
  return JSON.parse(value);
@@ -17,23 +45,6 @@ export function parseMaybeJson(value, label) {
17
45
  return parseJson(value, label);
18
46
  }
19
47
 
20
- export function normalizeToolkits(value) {
21
- if (!value) {
22
- return [];
23
- }
24
- if (value.startsWith('[')) {
25
- const parsed = parseJson(value, 'toolkits');
26
- if (!Array.isArray(parsed)) {
27
- throw usageError('toolkits JSON must be an array of toolkit strings');
28
- }
29
- return parsed;
30
- }
31
- return value
32
- .split(',')
33
- .map((entry) => entry.trim())
34
- .filter(Boolean);
35
- }
36
-
37
48
  export function nextIdempotencyKey(globalOptions) {
38
49
  return globalOptions.idempotencyKey || randomUUID();
39
50
  }
@@ -57,28 +68,19 @@ export async function runToolCommand({
57
68
  }
58
69
 
59
70
  export async function fetchToolkits(runtime) {
60
- const result = await runToolCommand({
61
- runtime,
62
- toolName: 'notis_find_toolkits',
63
- });
64
- return result.payload.toolkits || [];
65
- }
66
-
67
- export async function resolveSearchToolkits(runtime, rawToolkits) {
68
- const toolkits = normalizeToolkits(rawToolkits);
69
- if (toolkits.length) {
70
- return toolkits;
71
- }
72
- const availableToolkits = await fetchToolkits(runtime);
73
- return availableToolkits.map((entry) => entry.id);
71
+ const payload = await fetchToolDiscovery(runtime, 'List available toolkit namespaces and connection statuses');
72
+ return (payload.toolkit_connection_statuses || []).map((entry) => ({
73
+ id: entry.toolkit,
74
+ provider: typeof entry.toolkit === 'string' ? entry.toolkit.split('-', 1)[0] : undefined,
75
+ description: entry.description || entry.status_message || entry.toolkit,
76
+ has_active_connection: Boolean(entry.has_active_connection),
77
+ status_message: entry.status_message || '',
78
+ connection_details: entry.connection_details || {},
79
+ }));
74
80
  }
75
81
 
76
82
  export async function probeAuth(runtime) {
77
- const result = await runToolCommand({
78
- runtime,
79
- toolName: 'notis_find_toolkits',
80
- });
81
- return result.payload;
83
+ return fetchToolDiscovery(runtime, 'List available toolkit namespaces and connection statuses');
82
84
  }
83
85
 
84
86
  export async function healthCheck(runtime) {
@@ -90,22 +92,46 @@ export async function healthCheck(runtime) {
90
92
  });
91
93
  }
92
94
 
93
- export async function fetchToolSchema(runtime, toolName, rawToolkits) {
94
- const toolkits = normalizeToolkits(rawToolkits);
95
+ export async function fetchToolSchema(runtime, toolName) {
95
96
  const result = await runToolCommand({
96
97
  runtime,
97
- toolName: 'notis_find_tools',
98
+ toolName: COMPOSIO_GET_TOOL_SCHEMAS,
98
99
  arguments_: {
99
- query: toolName,
100
- ...(toolkits.length ? { toolkits } : {}),
100
+ tool_slugs: [toolName],
101
101
  },
102
102
  });
103
- const tools = result.payload.tools || [];
104
- const match = tools.find((t) => t.name === toolName);
105
- if (!match) {
103
+ const payload = result.payload || {};
104
+ const schema = (
105
+ payload.tool_schemas?.[toolName] ||
106
+ payload.schemas?.[toolName] ||
107
+ payload.tools?.find?.((tool) => tool?.tool_slug === toolName || tool?.name === toolName)
108
+ );
109
+ if (!schema) {
106
110
  throw usageError(`Tool "${toolName}" not found.`);
107
111
  }
108
- return match;
112
+ return {
113
+ name: toolName,
114
+ toolkit_id: schema.toolkit,
115
+ description: schema.description || '',
116
+ parameters: schema.input_schema || { type: 'object', properties: {} },
117
+ output_schema: schema.output_schema || {},
118
+ schema_available: Boolean(schema.hasFullSchema),
119
+ };
120
+ }
121
+
122
+ export async function fetchToolDiscovery(runtime, useCase, knownFields = '') {
123
+ const query = { use_case: useCase };
124
+ if (knownFields) {
125
+ query.known_fields = knownFields;
126
+ }
127
+ const result = await runToolCommand({
128
+ runtime,
129
+ toolName: COMPOSIO_SEARCH_TOOLS,
130
+ arguments_: {
131
+ queries: [query],
132
+ },
133
+ });
134
+ return result.payload || {};
109
135
  }
110
136
 
111
137
  export function validateArguments(schema, args) {
@@ -1,4 +1,4 @@
1
- import { healthCheck, probeAuth } from './helpers.js';
1
+ import { COMPOSIO_SEARCH_TOOLS, healthCheck, probeAuth } from './helpers.js';
2
2
  import { findCommandSpec, formatDescribe } from '../runtime/help.js';
3
3
 
4
4
  async function doctorHandler(ctx) {
@@ -21,7 +21,7 @@ async function doctorHandler(ctx) {
21
21
  if (ctx.runtime.jwt) {
22
22
  try {
23
23
  const payload = await probeAuth(ctx.runtime);
24
- checks.tool_roundtrip = Array.isArray(payload.toolkits) ? 'ok' : 'error';
24
+ checks.tool_roundtrip = Array.isArray(payload.toolkit_connection_statuses) ? 'ok' : 'error';
25
25
  } catch {
26
26
  checks.tool_roundtrip = 'error';
27
27
  }
@@ -68,7 +68,7 @@ function decodeJwtUserId(jwt) {
68
68
 
69
69
  async function whoamiHandler(ctx) {
70
70
  const payload = await probeAuth(ctx.runtime);
71
- const toolkits = payload.toolkits || [];
71
+ const toolkits = payload.toolkit_connection_statuses || [];
72
72
  const userId = decodeJwtUserId(ctx.runtime.jwt);
73
73
 
74
74
  return ctx.output.emitSuccess({
@@ -78,7 +78,7 @@ async function whoamiHandler(ctx) {
78
78
  api_base: ctx.runtime.apiBase,
79
79
  user_id: userId,
80
80
  toolkit_count: toolkits.length,
81
- toolkits: toolkits.map((t) => t.id),
81
+ toolkits: toolkits.map((t) => t.toolkit),
82
82
  cli_version: ctx.runtime.cliVersion,
83
83
  },
84
84
  humanSummary: `Logged in as ${userId || 'unknown'} via profile "${ctx.runtime.profileName}"`,
@@ -91,7 +91,7 @@ async function whoamiHandler(ctx) {
91
91
  `Version: ${ctx.runtime.cliVersion}`,
92
92
  ].join('\n'),
93
93
  hints: [
94
- { command: 'notis tools toolkits', reason: 'List available toolkit namespaces' },
94
+ { command: 'notis tools toolkits', reason: 'List available toolkit namespaces and connection statuses' },
95
95
  { command: 'notis doctor', reason: 'Run a full health check' },
96
96
  ],
97
97
  });
@@ -110,7 +110,7 @@ async function describeHandler(ctx) {
110
110
  export const metaCommandSpecs = [
111
111
  {
112
112
  command_path: ['whoami'],
113
- summary: 'Display the active profile, user, and available toolkits.',
113
+ summary: 'Display the active profile, user, and available toolkit connection statuses.',
114
114
  when_to_use: 'Use this to quickly confirm which account and environment a command will target.',
115
115
  args_schema: { arguments: [], options: [] },
116
116
  examples: ['notis whoami', 'notis whoami --json'],
@@ -118,7 +118,7 @@ export const metaCommandSpecs = [
118
118
  mutates: false,
119
119
  idempotent: true,
120
120
  related_commands: ['notis doctor'],
121
- backend_call: { type: 'tool', name: 'notis_find_toolkits' },
121
+ backend_call: { type: 'tool', name: COMPOSIO_SEARCH_TOOLS },
122
122
  handler: whoamiHandler,
123
123
  },
124
124
  {
@@ -3,13 +3,16 @@ import { createHash } from 'node:crypto';
3
3
  import { basename } from 'node:path';
4
4
  import { usageError } from '../runtime/errors.js';
5
5
  import {
6
+ COMPOSIO_GET_TOOL_SCHEMAS,
7
+ COMPOSIO_MULTI_EXECUTE_TOOL,
8
+ COMPOSIO_SEARCH_TOOLS,
9
+ fetchToolDiscovery,
6
10
  fetchToolkits,
7
11
  fetchToolSchema,
12
+ localNotisToolSlug,
8
13
  nextIdempotencyKey,
9
14
  parseJson,
10
15
  parseMaybeJson,
11
- normalizeToolkits,
12
- resolveSearchToolkits,
13
16
  runToolCommand,
14
17
  validateArguments,
15
18
  } from './helpers.js';
@@ -38,10 +41,10 @@ async function resolveJsonInput(value, label) {
38
41
 
39
42
  const LONG_RUNNING_TOOL_TIMEOUT_MS = 600000;
40
43
  const LONG_RUNNING_TOOL_NAMES = new Set([
41
- 'notis-default-generate_image_openai',
42
- 'notis-default-edit_image_openai',
43
- 'notis-default-generate_image_gemini',
44
- 'notis-default-edit_image_gemini',
44
+ 'LOCAL_NOTIS_GENERATE_IMAGE_OPENAI',
45
+ 'LOCAL_NOTIS_EDIT_IMAGE_OPENAI',
46
+ 'LOCAL_NOTIS_GENERATE_IMAGE_GEMINI',
47
+ 'LOCAL_NOTIS_EDIT_IMAGE_GEMINI',
45
48
  ]);
46
49
 
47
50
  const EXTENSION_CONTENT_TYPES = new Map([
@@ -129,7 +132,7 @@ async function parseFileBindings(rawFileOptions) {
129
132
 
130
133
  function ensureLongRunningToolTimeout(ctx, toolNames) {
131
134
  const names = Array.isArray(toolNames) ? toolNames : [toolNames];
132
- if (!names.some((name) => LONG_RUNNING_TOOL_NAMES.has(name))) {
135
+ if (!names.some((name) => LONG_RUNNING_TOOL_NAMES.has(localNotisToolSlug(name)))) {
133
136
  return;
134
137
  }
135
138
  ctx.runtime.timeoutMs = Math.max(ctx.runtime.timeoutMs || 0, LONG_RUNNING_TOOL_TIMEOUT_MS);
@@ -142,27 +145,55 @@ async function toolsToolkitsHandler(ctx) {
142
145
  data: { toolkits },
143
146
  humanSummary: `Found ${toolkits.length} toolkits`,
144
147
  hints: [
145
- { command: 'notis tools search <query>', reason: 'Search for tools in these toolkits' },
148
+ { command: 'notis tools search <query>', reason: 'Search canonical tools and schemas' },
146
149
  ],
147
- renderHuman: () => toolkits.map((entry) => `${entry.id} ${entry.description}`).join('\n'),
150
+ renderHuman: () =>
151
+ toolkits
152
+ .map((entry) => {
153
+ const status = entry.has_active_connection ? 'connected' : 'needs auth';
154
+ return `${entry.id} ${status} ${entry.description}`;
155
+ })
156
+ .join('\n'),
148
157
  });
149
158
  }
150
159
 
160
+ function discoveryToolRows(payload) {
161
+ const schemas = payload.tool_schemas || {};
162
+ const statuses = new Map(
163
+ (payload.toolkit_connection_statuses || [])
164
+ .filter((entry) => entry && typeof entry.toolkit === 'string')
165
+ .map((entry) => [entry.toolkit, entry])
166
+ );
167
+ const seen = new Set();
168
+ const rows = [];
169
+ for (const result of payload.results || []) {
170
+ for (const name of [...(result.primary_tool_slugs || []), ...(result.related_tool_slugs || [])]) {
171
+ if (!name || seen.has(name)) continue;
172
+ seen.add(name);
173
+ const schema = schemas[name] || {};
174
+ const toolkit = schema.toolkit || name.split('-').slice(0, -1).join('-');
175
+ const status = statuses.get(toolkit);
176
+ rows.push({
177
+ name,
178
+ toolkit,
179
+ description: schema.description || '',
180
+ schema_available: Boolean(schemas[name]),
181
+ connection_status: status
182
+ ? (status.has_active_connection ? 'connected' : 'needs auth')
183
+ : 'unknown',
184
+ });
185
+ }
186
+ }
187
+ return rows;
188
+ }
189
+
151
190
  async function toolsSearchHandler(ctx) {
152
- const toolkits = await resolveSearchToolkits(ctx.runtime, ctx.options.toolkits);
153
- const result = await runToolCommand({
154
- runtime: ctx.runtime,
155
- toolName: 'notis_find_tools',
156
- arguments_: {
157
- query: ctx.args.query || undefined,
158
- toolkits,
159
- },
160
- });
161
- const tools = result.payload.tools || [];
191
+ const payload = await fetchToolDiscovery(ctx.runtime, ctx.args.query || 'Find tools', ctx.options.knownFields || '');
192
+ const tools = discoveryToolRows(payload);
162
193
  const firstTool = tools[0];
163
194
  return ctx.output.emitSuccess({
164
195
  command: ctx.spec.command_path.join(' '),
165
- data: { toolkits, tools },
196
+ data: payload,
166
197
  humanSummary: `Found ${tools.length} tools`,
167
198
  hints: firstTool
168
199
  ? [
@@ -172,53 +203,41 @@ async function toolsSearchHandler(ctx) {
172
203
  : [],
173
204
  renderHuman: () =>
174
205
  tools
175
- .map((tool) => `${tool.name}\n ${tool.toolkit_id || 'unknown toolkit'}\n ${tool.description || 'No description'}\n`)
206
+ .map((tool) => `${tool.name}\n toolkit: ${tool.toolkit || 'unknown'} (${tool.connection_status})\n schema: ${tool.schema_available ? 'available' : 'missing'}\n ${tool.description || 'No description'}\n`)
176
207
  .join('\n')
177
208
  .trim(),
178
209
  });
179
210
  }
180
211
 
181
212
  async function toolsDescribeHandler(ctx) {
182
- const toolkits = await resolveSearchToolkits(ctx.runtime, ctx.options.toolkits);
183
- const result = await runToolCommand({
184
- runtime: ctx.runtime,
185
- toolName: 'notis_find_tools',
186
- arguments_: {
187
- query: ctx.args.toolName,
188
- toolkits,
189
- },
190
- });
191
- const tools = result.payload.tools || [];
192
- const match = tools.find((tool) => tool.name === ctx.args.toolName);
193
- if (!match) {
194
- throw usageError(`Tool ${ctx.args.toolName} was not found in the selected toolkits.`);
195
- }
213
+ const requestedToolName = localNotisToolSlug(ctx.args.toolName);
214
+ const match = await fetchToolSchema(ctx.runtime, requestedToolName);
196
215
 
197
216
  return ctx.output.emitSuccess({
198
217
  command: ctx.spec.command_path.join(' '),
199
- data: { tool: match, toolkits },
200
- humanSummary: `Described tool ${ctx.args.toolName}`,
218
+ data: { tool: match },
219
+ humanSummary: `Described tool ${requestedToolName}`,
201
220
  hints: [
202
- { command: `notis tools exec ${ctx.args.toolName} --dry-run --arguments '{}'`, reason: 'Validate arguments first' },
203
- { command: `notis tools exec ${ctx.args.toolName} --arguments '{}'`, reason: 'Execute this tool' },
221
+ { command: `notis tools exec ${requestedToolName} --dry-run --arguments '{}'`, reason: 'Validate arguments first' },
222
+ { command: `notis tools exec ${requestedToolName} --arguments '{}'`, reason: 'Execute this tool' },
204
223
  ],
205
224
  renderHuman: () => JSON.stringify(match, null, 2),
206
225
  });
207
226
  }
208
227
 
209
228
  async function toolsExecHandler(ctx) {
210
- ensureLongRunningToolTimeout(ctx, ctx.args.toolName);
211
- const toolkits = normalizeToolkits(ctx.options.toolkits);
229
+ const requestedToolName = localNotisToolSlug(ctx.args.toolName);
230
+ ensureLongRunningToolTimeout(ctx, requestedToolName);
212
231
  const fileBindings = await parseFileBindings(ctx.options.file);
213
232
 
214
233
  if (ctx.options.getSchema) {
215
- const tool = await fetchToolSchema(ctx.runtime, ctx.args.toolName, ctx.options.toolkits);
234
+ const tool = await fetchToolSchema(ctx.runtime, requestedToolName);
216
235
  return ctx.output.emitSuccess({
217
236
  command: ctx.spec.command_path.join(' '),
218
- data: { tool, toolkits },
219
- humanSummary: `Schema for ${ctx.args.toolName}`,
237
+ data: { tool },
238
+ humanSummary: `Schema for ${requestedToolName}`,
220
239
  hints: [
221
- { command: `notis tools exec ${ctx.args.toolName} --dry-run --arguments '{}'`, reason: 'Validate arguments before executing' },
240
+ { command: `notis tools exec ${requestedToolName} --dry-run --arguments '{}'`, reason: 'Validate arguments before executing' },
222
241
  ],
223
242
  renderHuman: () => JSON.stringify(tool, null, 2),
224
243
  });
@@ -233,16 +252,16 @@ async function toolsExecHandler(ctx) {
233
252
  if (fileBindings.length) {
234
253
  throw usageError('--file is only supported when executing a tool, not with --dry-run');
235
254
  }
236
- const tool = await fetchToolSchema(ctx.runtime, ctx.args.toolName, ctx.options.toolkits);
255
+ const tool = await fetchToolSchema(ctx.runtime, requestedToolName);
237
256
  const errors = validateArguments(tool.parameters, args);
238
257
 
239
258
  if (errors.length) {
240
259
  return ctx.output.emitSuccess({
241
260
  command: ctx.spec.command_path.join(' '),
242
- data: { valid: false, errors, tool: ctx.args.toolName, toolkits },
261
+ data: { valid: false, errors, tool: requestedToolName },
243
262
  humanSummary: `Dry run failed: ${errors.length} validation error(s)`,
244
263
  hints: [
245
- { command: `notis tools exec ${ctx.args.toolName} --get-schema`, reason: 'Review the full parameter schema' },
264
+ { command: `notis tools exec ${requestedToolName} --get-schema`, reason: 'Review the full parameter schema' },
246
265
  ],
247
266
  renderHuman: () => errors.map((e) => ` - ${e}`).join('\n'),
248
267
  });
@@ -250,10 +269,10 @@ async function toolsExecHandler(ctx) {
250
269
 
251
270
  return ctx.output.emitSuccess({
252
271
  command: ctx.spec.command_path.join(' '),
253
- data: { valid: true, tool: ctx.args.toolName, arguments: args, toolkits },
254
- humanSummary: `Dry run passed for ${ctx.args.toolName}`,
272
+ data: { valid: true, tool: requestedToolName, arguments: args },
273
+ humanSummary: `Dry run passed for ${requestedToolName}`,
255
274
  hints: [
256
- { command: `notis tools exec ${ctx.args.toolName} --arguments '${JSON.stringify(args)}'`, reason: 'Execute with these arguments' },
275
+ { command: `notis tools exec ${requestedToolName} --arguments '${JSON.stringify(args)}'`, reason: 'Execute with these arguments' },
257
276
  ],
258
277
  });
259
278
  }
@@ -262,11 +281,9 @@ async function toolsExecHandler(ctx) {
262
281
 
263
282
  const result = await runToolCommand({
264
283
  runtime: ctx.runtime,
265
- toolName: 'notis_execute_tool',
284
+ toolName: COMPOSIO_MULTI_EXECUTE_TOOL,
266
285
  arguments_: {
267
- tool_name: ctx.args.toolName,
268
- arguments: args,
269
- ...(toolkits.length ? { toolkits } : {}),
286
+ tools: [{ tool_slug: requestedToolName, arguments: args }],
270
287
  },
271
288
  mutating: true,
272
289
  idempotencyKey,
@@ -276,7 +293,7 @@ async function toolsExecHandler(ctx) {
276
293
  return ctx.output.emitSuccess({
277
294
  command: ctx.spec.command_path.join(' '),
278
295
  data: result.payload,
279
- humanSummary: `Executed tool ${ctx.args.toolName}`,
296
+ humanSummary: `Executed tool ${requestedToolName}`,
280
297
  meta: { mutating: true, idempotency_key: idempotencyKey },
281
298
  renderHuman: () => JSON.stringify(result.payload, null, 2),
282
299
  });
@@ -297,16 +314,15 @@ async function toolsExecParallelHandler(ctx) {
297
314
  throw usageError(`calls[${i}] missing required "tool_name" string`);
298
315
  }
299
316
  }
300
- ensureLongRunningToolTimeout(ctx, calls.map((call) => call.tool_name));
317
+ ensureLongRunningToolTimeout(ctx, calls.map((call) => localNotisToolSlug(call.tool_name)));
301
318
 
302
319
  const data = await Promise.all(
303
320
  calls.map((call) =>
304
321
  runToolCommand({
305
322
  runtime: ctx.runtime,
306
- toolName: 'notis_execute_tool',
323
+ toolName: COMPOSIO_MULTI_EXECUTE_TOOL,
307
324
  arguments_: {
308
- tool_name: call.tool_name,
309
- arguments: call.arguments || {},
325
+ tools: [{ tool_slug: localNotisToolSlug(call.tool_name), arguments: call.arguments || {} }],
310
326
  },
311
327
  mutating: true,
312
328
  idempotencyKey: nextIdempotencyKey(ctx.globalOptions),
@@ -369,15 +385,15 @@ async function toolsLinkHandler(ctx) {
369
385
  export const toolsCommandSpecs = [
370
386
  {
371
387
  command_path: ['tools', 'toolkits'],
372
- summary: 'List toolkit namespaces available to the active user.',
373
- when_to_use: 'Use this before searching or executing generic tools.',
388
+ summary: 'List toolkit namespaces and connection statuses available to the active user.',
389
+ when_to_use: 'Use this to inspect connection state before searching or executing generic tools.',
374
390
  args_schema: { arguments: [], options: [] },
375
391
  examples: ['notis tools toolkits', 'notis tools toolkits --json'],
376
- output_schema: 'Returns available toolkit namespaces.',
392
+ output_schema: 'Returns toolkit_connection_statuses from COMPOSIO_SEARCH_TOOLS, rendered as toolkit rows.',
377
393
  mutates: false,
378
394
  idempotent: true,
379
395
  related_commands: ['notis tools search <query>', 'notis tools describe <tool-name>'],
380
- backend_call: { type: 'tool', name: 'notis_find_toolkits' },
396
+ backend_call: { type: 'tool', name: COMPOSIO_SEARCH_TOOLS },
381
397
  handler: toolsToolkitsHandler,
382
398
  },
383
399
  {
@@ -386,14 +402,14 @@ export const toolsCommandSpecs = [
386
402
  when_to_use: 'Use this when you need a generic capability that does not have a first-class CLI command.',
387
403
  args_schema: {
388
404
  arguments: [{ token: '<query>', description: 'Natural language description of the tool you need.' }],
389
- options: [{ flags: '--toolkits <csv-or-json>', description: 'Optional subset of toolkit ids to search.' }],
405
+ options: [{ flags: '--known-fields <text>', key: 'knownFields', description: 'Optional known field hints, such as channel_name:general or user_email:a@example.com.' }],
390
406
  },
391
- examples: ['notis tools search "send an email"', 'notis tools search "update framer page" --toolkits mcp-framer'],
392
- output_schema: 'Returns tool matches with descriptions and parameter schemas.',
407
+ examples: ['notis tools search "send an email"', 'notis tools search "post on LinkedIn" --known-fields "platform:linkedin"'],
408
+ output_schema: 'Returns the full COMPOSIO_SEARCH_TOOLS response. Human output renders canonical names, connection status, and schema availability.',
393
409
  mutates: false,
394
410
  idempotent: true,
395
411
  related_commands: ['notis tools toolkits', 'notis tools exec <tool-name>'],
396
- backend_call: { type: 'tool', name: 'notis_find_tools' },
412
+ backend_call: { type: 'tool', name: COMPOSIO_SEARCH_TOOLS },
397
413
  handler: toolsSearchHandler,
398
414
  },
399
415
  {
@@ -402,14 +418,14 @@ export const toolsCommandSpecs = [
402
418
  when_to_use: 'Use this when you know the tool name and want its parameter schema before execution.',
403
419
  args_schema: {
404
420
  arguments: [{ token: '<tool-name>', description: 'Exact tool name to locate and describe.' }],
405
- options: [{ flags: '--toolkits <csv-or-json>', description: 'Optional subset of toolkit ids to search.' }],
421
+ options: [],
406
422
  },
407
- examples: ['notis tools describe composio-gmail-default-send_email', 'notis tools describe notis-default-query --toolkits notis-default'],
423
+ examples: ['notis tools describe composio-gmail-send_email', 'notis tools describe LOCAL_NOTIS_DATABASE_QUERY'],
408
424
  output_schema: 'Returns one tool descriptor with name, description, and parameters.',
409
425
  mutates: false,
410
426
  idempotent: true,
411
427
  related_commands: ['notis tools search <query>', 'notis tools exec <tool-name>'],
412
- backend_call: { type: 'tool', name: 'notis_find_tools' },
428
+ backend_call: { type: 'tool', name: COMPOSIO_GET_TOOL_SCHEMAS },
413
429
  handler: toolsDescribeHandler,
414
430
  },
415
431
  {
@@ -423,23 +439,22 @@ export const toolsCommandSpecs = [
423
439
  { flags: '--file <argument-path=local-path>', description: 'Upload a local file into a file-uploadable tool argument. Repeatable.', collect: true },
424
440
  { flags: '--get-schema', description: 'Display the tool parameter schema without executing.' },
425
441
  { flags: '--dry-run', description: 'Validate arguments against the tool schema without executing.' },
426
- { flags: '--toolkits <csv-or-json>', description: 'Optional toolkit namespace(s) to use when resolving the tool.' },
427
442
  ],
428
443
  },
429
444
  examples: [
430
- 'notis tools exec notis-default-query --arguments \'{"database_slug":"tasks","query":{}}\'',
431
- 'notis tools exec notis-default-get_database --arguments \'{"database_slug":"tasks"}\'',
432
- 'notis tools exec notis-default-query --get-schema',
433
- 'notis tools exec notis-default-query --dry-run --arguments \'{"database_slug":"tasks","query":{}}\'',
434
- 'notis tools exec notis-default-query --arguments @query.json',
435
- 'notis tools exec notis-default-query --arguments - < query.json',
436
- 'notis tools exec composio-dropbox-default-upload_file --arguments \'{"path":"/target/in/dropbox.pdf"}\' --file content=./Invoice.pdf',
445
+ 'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments \'{"database_slug":"tasks","query":{}}\'',
446
+ 'notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --arguments \'{"database_slug":"tasks"}\'',
447
+ 'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --get-schema',
448
+ 'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --dry-run --arguments \'{"database_slug":"tasks","query":{}}\'',
449
+ 'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments @query.json',
450
+ 'notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments - < query.json',
451
+ 'notis tools exec composio-dropbox-upload_file --arguments \'{"path":"/target/in/dropbox.pdf"}\' --file content=./Invoice.pdf',
437
452
  ],
438
453
  output_schema: 'Returns the raw tool execution payload.',
439
454
  mutates: true,
440
455
  idempotent: true,
441
456
  related_commands: ['notis tools search <query>', 'notis tools describe <tool-name>', 'notis tools exec-parallel <calls>'],
442
- backend_call: { type: 'tool', name: 'notis_execute_tool' },
457
+ backend_call: { type: 'tool', name: COMPOSIO_MULTI_EXECUTE_TOOL },
443
458
  handler: toolsExecHandler,
444
459
  },
445
460
  {
@@ -455,13 +470,13 @@ export const toolsCommandSpecs = [
455
470
  ],
456
471
  },
457
472
  examples: [
458
- 'notis tools exec-parallel \'[{"tool_name":"notis-default-query","arguments":{"database_slug":"tasks","query":{}}},{"tool_name":"notis-default-list_databases","arguments":{}}]\'',
473
+ 'notis tools exec-parallel \'[{"tool_name":"LOCAL_NOTIS_DATABASE_QUERY","arguments":{"database_slug":"tasks","query":{}}},{"tool_name":"LOCAL_NOTIS_DATABASE_LIST_DATABASES","arguments":{}}]\'',
459
474
  ],
460
475
  output_schema: 'Returns an array of results, one per tool call.',
461
476
  mutates: true,
462
477
  idempotent: true,
463
478
  related_commands: ['notis tools exec <tool-name>', 'notis tools search <query>'],
464
- backend_call: { type: 'tool', name: 'notis_execute_tool' },
479
+ backend_call: { type: 'tool', name: COMPOSIO_MULTI_EXECUTE_TOOL },
465
480
  handler: toolsExecParallelHandler,
466
481
  },
467
482
  {
@@ -212,15 +212,21 @@ export function resolveRuntimeProfile(globalOptions = {}, { requireAuth = true }
212
212
  });
213
213
  }
214
214
 
215
+ // An explicit NOTIS_JWT is a complete credential override: use it verbatim and do
216
+ // NOT attempt a stored-profile dev_portal token refresh (whose refresh_token may be
217
+ // stale or belong to a different session). Without this, a long-lived shell with an
218
+ // expired profile refresh_token fails every command with invalid_grant even though
219
+ // the supplied NOTIS_JWT is valid.
220
+ const usingEnvJwt = Boolean(process.env.NOTIS_JWT);
215
221
  return {
216
222
  config,
217
223
  profileName,
218
224
  apiBase,
219
225
  jwt,
220
- authMode: profile.auth_mode,
221
- refreshToken: profile.refresh_token,
222
- accessExpiresAt: profile.access_expires_at,
223
- refreshExpiresAt: profile.refresh_expires_at,
226
+ authMode: usingEnvJwt ? 'jwt' : profile.auth_mode,
227
+ refreshToken: usingEnvJwt ? undefined : profile.refresh_token,
228
+ accessExpiresAt: usingEnvJwt ? undefined : profile.access_expires_at,
229
+ refreshExpiresAt: usingEnvJwt ? undefined : profile.refresh_expires_at,
224
230
  agentMode,
225
231
  nonInteractive,
226
232
  outputMode,
@@ -9,33 +9,6 @@ import {
9
9
  saveConfig,
10
10
  } from './profiles.js';
11
11
 
12
- const META_TOOL_NAMES = new Set([
13
- 'notis_find_toolkits',
14
- 'notis_find_tools',
15
- 'notis_execute_tool',
16
- 'notis_find_additional_tools',
17
- ]);
18
-
19
- function canonicalizeToolName(toolName) {
20
- if (typeof toolName !== 'string' || !toolName.length) {
21
- return toolName;
22
- }
23
-
24
- if (META_TOOL_NAMES.has(toolName) || toolName.startsWith('notis-default-')) {
25
- return toolName;
26
- }
27
-
28
- if (toolName.startsWith('notis_')) {
29
- return `notis-default-${toolName.slice('notis_'.length)}`;
30
- }
31
-
32
- if (toolName.includes('_notis_')) {
33
- return `notis-default-${toolName.replace(/_notis_/g, '_')}`;
34
- }
35
-
36
- return toolName;
37
- }
38
-
39
12
  function escapeMultipartHeaderValue(value) {
40
13
  return String(value ?? '')
41
14
  .replace(/"/g, '%22')
@@ -397,7 +370,7 @@ export async function callTool({
397
370
  }) {
398
371
  const requestId = idempotencyKey || (runtime.mutating ? randomUUID() : null);
399
372
  const payload = {
400
- tool_name: canonicalizeToolName(toolName),
373
+ tool_name: toolName,
401
374
  arguments: argumentsPayload,
402
375
  idempotency_key: requestId,
403
376
  cli_context: {
@@ -25,7 +25,7 @@ type QueryItemsResult = {
25
25
 
26
26
  export default function HomePage() {
27
27
  const { app, ready } = useNotis();
28
- const queryItems = useTool<QueryItemsArgs, QueryItemsResult>('notis-default-query');
28
+ const queryItems = useTool<QueryItemsArgs, QueryItemsResult>('LOCAL_NOTIS_DATABASE_QUERY');
29
29
  const [documents, setDocuments] = useState<ItemDocument[]>([]);
30
30
 
31
31
  useEffect(() => {
@@ -32,6 +32,6 @@ export default defineNotisApp({
32
32
  ],
33
33
 
34
34
  tools: [
35
- 'notis-default-query',
35
+ 'LOCAL_NOTIS_DATABASE_QUERY',
36
36
  ],
37
37
  });
@@ -25,7 +25,7 @@ export interface UseToolResult<
25
25
  * Call a specific Notis tool by name.
26
26
  *
27
27
  * ```tsx
28
- * const { call, loading } = useTool('notis-default-web_search');
28
+ * const { call, loading } = useTool('LOCAL_NOTIS_WEB_SEARCH');
29
29
  * const result = await call({ query: 'latest news' });
30
30
  * ```
31
31
  */