@notis_ai/cli 0.2.0-beta.102.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.
Files changed (191) hide show
  1. package/README.md +334 -0
  2. package/bin/notis.js +2 -0
  3. package/config/notis_app_boundary_rules.json +50 -0
  4. package/dist/scaffolds/notis-database/CHANGELOG.md +5 -0
  5. package/dist/scaffolds/notis-database/app/globals.css +44 -0
  6. package/dist/scaffolds/notis-database/app/layout.tsx +6 -0
  7. package/dist/scaffolds/notis-database/app/page.tsx +1091 -0
  8. package/dist/scaffolds/notis-database/components/ui/badge.tsx +28 -0
  9. package/dist/scaffolds/notis-database/components/ui/button.tsx +53 -0
  10. package/dist/scaffolds/notis-database/components/ui/card.tsx +56 -0
  11. package/dist/scaffolds/notis-database/components/ui/table.tsx +120 -0
  12. package/dist/scaffolds/notis-database/components.json +20 -0
  13. package/dist/scaffolds/notis-database/index.html +12 -0
  14. package/dist/scaffolds/notis-database/lib/types.ts +134 -0
  15. package/dist/scaffolds/notis-database/lib/utils.ts +6 -0
  16. package/dist/scaffolds/notis-database/metadata/screenshot-1.png +0 -0
  17. package/dist/scaffolds/notis-database/notis.config.ts +26 -0
  18. package/dist/scaffolds/notis-database/package.json +31 -0
  19. package/dist/scaffolds/notis-database/postcss.config.mjs +8 -0
  20. package/dist/scaffolds/notis-database/src/dev-main.tsx +23 -0
  21. package/dist/scaffolds/notis-database/src/mock-runtime.ts +558 -0
  22. package/dist/scaffolds/notis-database/tailwind.config.ts +59 -0
  23. package/dist/scaffolds/notis-database/tsconfig.json +23 -0
  24. package/dist/scaffolds/notis-database/vite.config.ts +22 -0
  25. package/dist/scaffolds/notis-journal/CHANGELOG.md +25 -0
  26. package/dist/scaffolds/notis-journal/app/globals.css +37 -0
  27. package/dist/scaffolds/notis-journal/app/insights/page.tsx +513 -0
  28. package/dist/scaffolds/notis-journal/app/journal-core.tsx +362 -0
  29. package/dist/scaffolds/notis-journal/app/journal-ui.tsx +337 -0
  30. package/dist/scaffolds/notis-journal/app/layout.tsx +6 -0
  31. package/dist/scaffolds/notis-journal/app/page.tsx +485 -0
  32. package/dist/scaffolds/notis-journal/components/ui/badge.tsx +28 -0
  33. package/dist/scaffolds/notis-journal/components/ui/button.tsx +53 -0
  34. package/dist/scaffolds/notis-journal/components/ui/card.tsx +56 -0
  35. package/dist/scaffolds/notis-journal/components.json +20 -0
  36. package/dist/scaffolds/notis-journal/index.html +12 -0
  37. package/dist/scaffolds/notis-journal/lib/utils.ts +6 -0
  38. package/dist/scaffolds/notis-journal/metadata/screenshot-1.png +0 -0
  39. package/dist/scaffolds/notis-journal/metadata/screenshot-2.png +0 -0
  40. package/dist/scaffolds/notis-journal/metadata/screenshot-3.png +0 -0
  41. package/dist/scaffolds/notis-journal/metadata/screenshot-4.png +0 -0
  42. package/dist/scaffolds/notis-journal/metadata/screenshot-5.png +0 -0
  43. package/dist/scaffolds/notis-journal/metadata/screenshot-6.png +0 -0
  44. package/dist/scaffolds/notis-journal/metadata/screenshot-fixtures.json +132 -0
  45. package/dist/scaffolds/notis-journal/notis.config.ts +93 -0
  46. package/dist/scaffolds/notis-journal/package.json +34 -0
  47. package/dist/scaffolds/notis-journal/packages/sdk/package.json +36 -0
  48. package/dist/scaffolds/notis-journal/packages/sdk/src/components/DocumentEditor.tsx +93 -0
  49. package/dist/scaffolds/notis-journal/packages/sdk/src/components/Markdown.tsx +60 -0
  50. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectActionBar.tsx +278 -0
  51. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
  52. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
  53. package/dist/scaffolds/notis-journal/packages/sdk/src/config.ts +145 -0
  54. package/dist/scaffolds/notis-journal/packages/sdk/src/documents.ts +229 -0
  55. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useBackend.ts +41 -0
  56. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
  57. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocument.ts +78 -0
  58. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocuments.ts +121 -0
  59. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useMultiSelect.ts +539 -0
  60. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotis.ts +34 -0
  61. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
  62. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTool.ts +64 -0
  63. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTools.ts +56 -0
  64. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
  65. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
  66. package/dist/scaffolds/notis-journal/packages/sdk/src/index.ts +83 -0
  67. package/dist/scaffolds/notis-journal/packages/sdk/src/provider.tsx +43 -0
  68. package/dist/scaffolds/notis-journal/packages/sdk/src/runtime.ts +220 -0
  69. package/dist/scaffolds/notis-journal/packages/sdk/src/styles.css +186 -0
  70. package/dist/scaffolds/notis-journal/packages/sdk/src/ui.ts +15 -0
  71. package/dist/scaffolds/notis-journal/packages/sdk/src/vite.ts +56 -0
  72. package/dist/scaffolds/notis-journal/packages/sdk/tsconfig.json +15 -0
  73. package/dist/scaffolds/notis-journal/postcss.config.mjs +8 -0
  74. package/dist/scaffolds/notis-journal/skills/journal-onboarding/SKILL.md +120 -0
  75. package/dist/scaffolds/notis-journal/src/dev-main.tsx +58 -0
  76. package/dist/scaffolds/notis-journal/src/mock-runtime.ts +197 -0
  77. package/dist/scaffolds/notis-journal/tailwind.config.ts +58 -0
  78. package/dist/scaffolds/notis-journal/tsconfig.json +23 -0
  79. package/dist/scaffolds/notis-journal/vite.config.ts +10 -0
  80. package/dist/scaffolds/notis-notes/CHANGELOG.md +5 -0
  81. package/dist/scaffolds/notis-notes/app/globals.css +3 -0
  82. package/dist/scaffolds/notis-notes/app/layout.tsx +6 -0
  83. package/dist/scaffolds/notis-notes/app/page.tsx +1950 -0
  84. package/dist/scaffolds/notis-notes/components/ui/badge.tsx +28 -0
  85. package/dist/scaffolds/notis-notes/components/ui/button.tsx +53 -0
  86. package/dist/scaffolds/notis-notes/components/ui/card.tsx +56 -0
  87. package/dist/scaffolds/notis-notes/components.json +20 -0
  88. package/dist/scaffolds/notis-notes/lib/utils.ts +6 -0
  89. package/dist/scaffolds/notis-notes/metadata/screenshot-1.png +0 -0
  90. package/dist/scaffolds/notis-notes/notis.config.ts +35 -0
  91. package/dist/scaffolds/notis-notes/package.json +31 -0
  92. package/dist/scaffolds/notis-notes/postcss.config.mjs +8 -0
  93. package/dist/scaffolds/notis-notes/tailwind.config.ts +58 -0
  94. package/dist/scaffolds/notis-notes/tsconfig.json +23 -0
  95. package/dist/scaffolds/notis-notes/vite.config.ts +10 -0
  96. package/dist/scaffolds/notis-random/CHANGELOG.md +5 -0
  97. package/dist/scaffolds/notis-random/README.md +33 -0
  98. package/dist/scaffolds/notis-random/app/globals.css +11 -0
  99. package/dist/scaffolds/notis-random/app/history/page.tsx +66 -0
  100. package/dist/scaffolds/notis-random/app/layout.tsx +7 -0
  101. package/dist/scaffolds/notis-random/app/page.tsx +222 -0
  102. package/dist/scaffolds/notis-random/index.html +12 -0
  103. package/dist/scaffolds/notis-random/lib/notis-tools.ts +109 -0
  104. package/dist/scaffolds/notis-random/lib/rng.ts +42 -0
  105. package/dist/scaffolds/notis-random/lib/roll-record.ts +102 -0
  106. package/dist/scaffolds/notis-random/lib/utils.ts +25 -0
  107. package/dist/scaffolds/notis-random/metadata/screenshot-1.png +0 -0
  108. package/dist/scaffolds/notis-random/metadata/screenshot-2.png +0 -0
  109. package/dist/scaffolds/notis-random/notis.config.ts +42 -0
  110. package/dist/scaffolds/notis-random/package.json +32 -0
  111. package/dist/scaffolds/notis-random/postcss.config.mjs +6 -0
  112. package/dist/scaffolds/notis-random/src/dev-main.tsx +70 -0
  113. package/dist/scaffolds/notis-random/src/mock-runtime.ts +129 -0
  114. package/dist/scaffolds/notis-random/tailwind.config.ts +43 -0
  115. package/dist/scaffolds/notis-random/tsconfig.json +23 -0
  116. package/dist/scaffolds/notis-random/vite.config.ts +11 -0
  117. package/dist/scaffolds.json +47 -0
  118. package/package.json +45 -0
  119. package/skills/notis-apps/SKILL.md +219 -0
  120. package/skills/notis-apps/cli.md +244 -0
  121. package/skills/notis-cli/SKILL.md +269 -0
  122. package/skills/notis-query/cli.md +39 -0
  123. package/src/cli.js +175 -0
  124. package/src/command-specs/apps.js +1939 -0
  125. package/src/command-specs/helpers.js +186 -0
  126. package/src/command-specs/index.js +14 -0
  127. package/src/command-specs/meta.js +161 -0
  128. package/src/command-specs/tools.js +499 -0
  129. package/src/runtime/agent-browser.js +464 -0
  130. package/src/runtime/app-boundary-validator.js +183 -0
  131. package/src/runtime/app-changelog.js +79 -0
  132. package/src/runtime/app-dev-server.js +712 -0
  133. package/src/runtime/app-dev-sessions.js +251 -0
  134. package/src/runtime/app-platform.js +1500 -0
  135. package/src/runtime/assets/store-screenshot-dark.png +0 -0
  136. package/src/runtime/cli-mode.generated.js +4 -0
  137. package/src/runtime/cli-mode.js +29 -0
  138. package/src/runtime/desktop-auth.js +93 -0
  139. package/src/runtime/errors.js +55 -0
  140. package/src/runtime/help.js +60 -0
  141. package/src/runtime/output.js +180 -0
  142. package/src/runtime/ports.js +15 -0
  143. package/src/runtime/profiles.js +261 -0
  144. package/src/runtime/store-screenshot.js +138 -0
  145. package/src/runtime/transport.js +347 -0
  146. package/template/.harness/index.html.tmpl +333 -0
  147. package/template/CHANGELOG.md +5 -0
  148. package/template/app/globals.css +3 -0
  149. package/template/app/layout.tsx +6 -0
  150. package/template/app/page.tsx +60 -0
  151. package/template/components/ui/badge.tsx +28 -0
  152. package/template/components/ui/button.tsx +53 -0
  153. package/template/components/ui/card.tsx +56 -0
  154. package/template/components.json +20 -0
  155. package/template/lib/utils.ts +6 -0
  156. package/template/metadata/screenshot-1.png +0 -0
  157. package/template/metadata/screenshot-2.png +0 -0
  158. package/template/metadata/screenshot-3.png +0 -0
  159. package/template/notis.config.ts +36 -0
  160. package/template/package-lock.json +4137 -0
  161. package/template/package.json +32 -0
  162. package/template/packages/sdk/package.json +36 -0
  163. package/template/packages/sdk/src/components/DocumentEditor.tsx +93 -0
  164. package/template/packages/sdk/src/components/Markdown.tsx +60 -0
  165. package/template/packages/sdk/src/components/MultiSelectActionBar.tsx +278 -0
  166. package/template/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
  167. package/template/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
  168. package/template/packages/sdk/src/config.ts +145 -0
  169. package/template/packages/sdk/src/documents.ts +229 -0
  170. package/template/packages/sdk/src/hooks/useBackend.ts +41 -0
  171. package/template/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
  172. package/template/packages/sdk/src/hooks/useDocument.ts +78 -0
  173. package/template/packages/sdk/src/hooks/useDocuments.ts +121 -0
  174. package/template/packages/sdk/src/hooks/useMultiSelect.ts +539 -0
  175. package/template/packages/sdk/src/hooks/useNotis.ts +34 -0
  176. package/template/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
  177. package/template/packages/sdk/src/hooks/useTool.ts +64 -0
  178. package/template/packages/sdk/src/hooks/useTools.ts +56 -0
  179. package/template/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
  180. package/template/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
  181. package/template/packages/sdk/src/index.ts +83 -0
  182. package/template/packages/sdk/src/provider.tsx +43 -0
  183. package/template/packages/sdk/src/runtime.ts +220 -0
  184. package/template/packages/sdk/src/styles.css +186 -0
  185. package/template/packages/sdk/src/ui.ts +15 -0
  186. package/template/packages/sdk/src/vite.ts +56 -0
  187. package/template/packages/sdk/tsconfig.json +15 -0
  188. package/template/postcss.config.mjs +8 -0
  189. package/template/tailwind.config.ts +58 -0
  190. package/template/tsconfig.json +22 -0
  191. package/template/vite.config.ts +10 -0
@@ -0,0 +1,186 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { CliError, EXIT_CODES, usageError } from '../runtime/errors.js';
3
+ import { callTool, httpRequest } from '../runtime/transport.js';
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
+
33
+ export function parseJson(value, label) {
34
+ try {
35
+ return JSON.parse(value);
36
+ } catch (error) {
37
+ throw usageError(`${label} must be valid JSON`, { value });
38
+ }
39
+ }
40
+
41
+ export function parseMaybeJson(value, label) {
42
+ if (!value) {
43
+ return undefined;
44
+ }
45
+ return parseJson(value, label);
46
+ }
47
+
48
+ export function nextIdempotencyKey(globalOptions) {
49
+ return globalOptions.idempotencyKey || randomUUID();
50
+ }
51
+
52
+ export async function runToolCommand({
53
+ runtime,
54
+ toolName,
55
+ arguments_ = {},
56
+ mutating = false,
57
+ idempotencyKey,
58
+ fileBindings = [],
59
+ }) {
60
+ const result = await callTool({
61
+ runtime: { ...runtime, mutating },
62
+ toolName,
63
+ arguments_,
64
+ idempotencyKey: mutating ? idempotencyKey : null,
65
+ fileBindings,
66
+ });
67
+ return result;
68
+ }
69
+
70
+ export async function fetchToolkits(runtime) {
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
+ }));
80
+ }
81
+
82
+ export async function probeAuth(runtime) {
83
+ return fetchToolDiscovery(runtime, 'List available toolkit namespaces and connection statuses');
84
+ }
85
+
86
+ export async function healthCheck(runtime) {
87
+ return httpRequest({
88
+ runtime,
89
+ method: 'GET',
90
+ path: '/health',
91
+ requireAuth: false,
92
+ });
93
+ }
94
+
95
+ export async function fetchToolSchema(runtime, toolName) {
96
+ const result = await runToolCommand({
97
+ runtime,
98
+ toolName: COMPOSIO_GET_TOOL_SCHEMAS,
99
+ arguments_: {
100
+ tool_slugs: [toolName],
101
+ },
102
+ });
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) {
110
+ throw usageError(`Tool "${toolName}" not found.`);
111
+ }
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 || {};
135
+ }
136
+
137
+ export function validateArguments(schema, args) {
138
+ const errors = [];
139
+ if (!schema || schema.type !== 'object') return errors;
140
+
141
+ const properties = schema.properties || {};
142
+ const required = schema.required || [];
143
+
144
+ for (const field of required) {
145
+ if (!(field in args)) {
146
+ errors.push(`Missing required field: "${field}"`);
147
+ }
148
+ }
149
+
150
+ if (schema.additionalProperties === false) {
151
+ for (const key of Object.keys(args)) {
152
+ if (!(key in properties)) {
153
+ errors.push(`Unknown field: "${key}"`);
154
+ }
155
+ }
156
+ }
157
+
158
+ for (const [key, value] of Object.entries(args)) {
159
+ const prop = properties[key];
160
+ if (!prop) continue;
161
+ if (prop.type === 'string' && typeof value !== 'string') {
162
+ errors.push(`Field "${key}" should be a string, got ${typeof value}`);
163
+ }
164
+ if (prop.type === 'integer' && !Number.isInteger(value)) {
165
+ errors.push(`Field "${key}" should be an integer, got ${typeof value}`);
166
+ }
167
+ if (prop.type === 'array' && !Array.isArray(value)) {
168
+ errors.push(`Field "${key}" should be an array, got ${typeof value}`);
169
+ }
170
+ if (prop.type === 'object' && (typeof value !== 'object' || value === null || Array.isArray(value))) {
171
+ errors.push(`Field "${key}" should be an object, got ${typeof value}`);
172
+ }
173
+ }
174
+
175
+ return errors;
176
+ }
177
+
178
+ export function toolConflictToError(payload, defaultMessage) {
179
+ return new CliError({
180
+ code: 'conflict',
181
+ message: payload?.error?.message || payload?.message || defaultMessage,
182
+ exitCode: EXIT_CODES.conflict,
183
+ details: payload || {},
184
+ hints: payload?.hints || [],
185
+ });
186
+ }
@@ -0,0 +1,14 @@
1
+ import { appsCommandSpecs } from './apps.js';
2
+ import { toolsCommandSpecs } from './tools.js';
3
+ import { metaCommandSpecs } from './meta.js';
4
+
5
+ export const GROUP_SUMMARIES = {
6
+ apps: 'Develop, deploy, and submit Notis Apps.',
7
+ tools: 'Discover and execute generic tools exposed through Notis.',
8
+ };
9
+
10
+ export const COMMAND_SPECS = [
11
+ ...appsCommandSpecs,
12
+ ...toolsCommandSpecs,
13
+ ...metaCommandSpecs,
14
+ ];
@@ -0,0 +1,161 @@
1
+ import { COMPOSIO_SEARCH_TOOLS, healthCheck, probeAuth } from './helpers.js';
2
+ import { findCommandSpec, formatDescribe } from '../runtime/help.js';
3
+ import { createExpiredAuthError } from '../runtime/desktop-auth.js';
4
+ import { isJwtExpired } from '../runtime/profiles.js';
5
+
6
+ async function doctorHandler(ctx) {
7
+ const checks = {
8
+ config: 'ok',
9
+ auth: 'missing',
10
+ health: 'unknown',
11
+ tool_roundtrip: 'unknown',
12
+ };
13
+
14
+ checks.auth = ctx.runtime.jwt
15
+ ? (isJwtExpired(ctx.runtime.jwt) ? 'expired' : 'configured')
16
+ : 'missing';
17
+
18
+ try {
19
+ await healthCheck(ctx.runtime);
20
+ checks.health = 'ok';
21
+ } catch (error) {
22
+ checks.health = 'error';
23
+ }
24
+
25
+ if (ctx.runtime.jwt) {
26
+ try {
27
+ const payload = await probeAuth(ctx.runtime);
28
+ checks.tool_roundtrip = Array.isArray(payload.toolkit_connection_statuses) ? 'ok' : 'error';
29
+ } catch {
30
+ checks.tool_roundtrip = 'error';
31
+ }
32
+ }
33
+
34
+ const hints = [];
35
+ if (checks.auth === 'missing') {
36
+ hints.push({ command: 'Open the Notis desktop app and sign in', reason: 'Configure CLI credentials' });
37
+ } else if (checks.auth === 'expired') {
38
+ hints.push(...createExpiredAuthError(ctx.runtime).hints);
39
+ }
40
+ if (checks.health === 'error' && checks.auth !== 'expired') {
41
+ hints.push({ command: 'Open the Notis desktop app and sign in again', reason: 'Refresh local CLI configuration' });
42
+ }
43
+ if (checks.tool_roundtrip === 'error') {
44
+ hints.push({ command: 'notis whoami', reason: 'Verify your account and permissions' });
45
+ }
46
+
47
+ return ctx.output.emitSuccess({
48
+ command: ctx.spec.command_path.join(' '),
49
+ data: {
50
+ profile: ctx.runtime.profileName,
51
+ api_base: ctx.runtime.apiBase,
52
+ checks,
53
+ },
54
+ humanSummary: `Doctor checks completed for profile ${ctx.runtime.profileName}`,
55
+ hints,
56
+ renderHuman: () =>
57
+ Object.entries(checks)
58
+ .map(([name, status]) => `${name.padEnd(14)} ${status}`)
59
+ .join('\n'),
60
+ });
61
+ }
62
+
63
+ function decodeJwtUserId(jwt) {
64
+ if (!jwt) return null;
65
+ try {
66
+ const parts = jwt.split('.');
67
+ if (parts.length !== 3) return null;
68
+ const decoded = JSON.parse(Buffer.from(parts[1], 'base64url').toString());
69
+ return decoded.sub || decoded.user_id || null;
70
+ } catch {
71
+ return null;
72
+ }
73
+ }
74
+
75
+ async function whoamiHandler(ctx) {
76
+ const payload = await probeAuth(ctx.runtime);
77
+ const toolkits = payload.toolkit_connection_statuses || [];
78
+ const userId = decodeJwtUserId(ctx.runtime.jwt);
79
+
80
+ return ctx.output.emitSuccess({
81
+ command: ctx.spec.command_path.join(' '),
82
+ data: {
83
+ profile: ctx.runtime.profileName,
84
+ api_base: ctx.runtime.apiBase,
85
+ user_id: userId,
86
+ toolkit_count: toolkits.length,
87
+ toolkits: toolkits.map((t) => t.toolkit),
88
+ cli_version: ctx.runtime.cliVersion,
89
+ },
90
+ humanSummary: `Logged in as ${userId || 'unknown'} via profile "${ctx.runtime.profileName}"`,
91
+ renderHuman: () =>
92
+ [
93
+ `Profile: ${ctx.runtime.profileName}`,
94
+ `API: ${ctx.runtime.apiBase}`,
95
+ `User: ${userId || 'unknown'}`,
96
+ `Toolkits: ${toolkits.length}`,
97
+ `Version: ${ctx.runtime.cliVersion}`,
98
+ ].join('\n'),
99
+ hints: [
100
+ { command: 'notis tools toolkits', reason: 'List available toolkit namespaces and connection statuses' },
101
+ { command: 'notis doctor', reason: 'Run a full health check' },
102
+ ],
103
+ });
104
+ }
105
+
106
+ async function describeHandler(ctx) {
107
+ const spec = findCommandSpec(ctx.registrySpecs, ctx.args.commandPath);
108
+ return ctx.output.emitSuccess({
109
+ command: ctx.spec.command_path.join(' '),
110
+ data: { spec },
111
+ humanSummary: `Described ${spec.command_path.join(' ')}`,
112
+ renderHuman: () => formatDescribe(spec),
113
+ });
114
+ }
115
+
116
+ export const metaCommandSpecs = [
117
+ {
118
+ command_path: ['whoami'],
119
+ summary: 'Display the active profile, user, and available toolkit connection statuses.',
120
+ when_to_use: 'Use this to quickly confirm which account and environment a command will target.',
121
+ args_schema: { arguments: [], options: [] },
122
+ examples: ['notis whoami', 'notis whoami --json'],
123
+ output_schema: 'Returns profile, api_base, user_id, toolkit count, and CLI version.',
124
+ mutates: false,
125
+ idempotent: true,
126
+ related_commands: ['notis doctor'],
127
+ backend_call: { type: 'tool', name: COMPOSIO_SEARCH_TOOLS },
128
+ handler: whoamiHandler,
129
+ },
130
+ {
131
+ command_path: ['doctor'],
132
+ summary: 'Run a quick CLI health check for config, auth, and API reachability.',
133
+ when_to_use: 'Use this before relying on the CLI in automation or after changing environments.',
134
+ args_schema: { arguments: [], options: [] },
135
+ examples: ['notis doctor', 'notis doctor --json'],
136
+ output_schema: 'Returns config, auth, health, and roundtrip check statuses.',
137
+ mutates: false,
138
+ idempotent: true,
139
+ require_auth: false,
140
+ related_commands: ['notis tools toolkits'],
141
+ backend_call: { type: 'health+tool_roundtrip' },
142
+ handler: doctorHandler,
143
+ },
144
+ {
145
+ command_path: ['describe'],
146
+ summary: 'Describe a first-class CLI command in detail.',
147
+ when_to_use: 'Use this when an agent or human needs the exact shape, examples, and semantics of a command.',
148
+ args_schema: {
149
+ arguments: [{ token: '<command...>', key: 'commandPath', description: 'Command path to describe, such as "apps deploy".' }],
150
+ options: [],
151
+ },
152
+ examples: ['notis describe apps deploy', 'notis describe tools exec'],
153
+ output_schema: 'Returns the command spec metadata for the requested command.',
154
+ mutates: false,
155
+ idempotent: true,
156
+ require_auth: false,
157
+ related_commands: ['notis --help', 'notis tools describe <tool-name>'],
158
+ backend_call: { type: 'local_registry' },
159
+ handler: describeHandler,
160
+ },
161
+ ];