@kiipu/cli 0.0.7 → 0.0.9

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
@@ -1,14 +1,15 @@
1
1
  # Kiipu CLI
2
2
 
3
- Publish to Kiipu from your terminal.
3
+ Create Kiipu notes from your terminal.
4
4
 
5
- `@kiipu/cli` is the official command line interface for Kiipu. It is the best place to start if you want to authenticate locally and post directly from the command line.
5
+ `@kiipu/cli` is the official command line interface for Kiipu. It is the best place to start if you want to authenticate locally and note directly from the command line.
6
6
 
7
7
  Use it to:
8
8
 
9
9
  - sign in on the current device
10
- - publish posts from the command line
11
- - delete, restore, or permanently remove posts by id
10
+ - ask questions over your saved notes
11
+ - create notes from the command line
12
+ - delete, restore, or permanently remove notes by id
12
13
  - verify local authentication and API access with `kiipu doctor`
13
14
 
14
15
  If you want Claude Code integration on top of the CLI, use `@kiipu/claude-plugin`.
@@ -27,10 +28,10 @@ npm install -g @kiipu/cli
27
28
  kiipu auth login
28
29
  ```
29
30
 
30
- 2. Publish a post:
31
+ 2. Create a note:
31
32
 
32
33
  ```bash
33
- kiipu post create "Hello Kiipu"
34
+ kiipu note create "Hello Kiipu"
34
35
  ```
35
36
 
36
37
  3. Confirm local setup:
@@ -39,11 +40,18 @@ kiipu post create "Hello Kiipu"
39
40
  kiipu doctor
40
41
  ```
41
42
 
43
+ 4. Ask over your saved notes:
44
+
45
+ ```bash
46
+ kiipu ask "What did I save about the roadmap?"
47
+ ```
48
+
42
49
  ## Example Workflow
43
50
 
44
51
  ```bash
45
52
  kiipu auth login
46
- kiipu post create "Ship the beta today"
53
+ kiipu ask "What should I follow up on?"
54
+ kiipu note create "Ship the beta today"
47
55
  kiipu auth status
48
56
  ```
49
57
 
@@ -65,21 +73,38 @@ kiipu auth status
65
73
  kiipu auth logout
66
74
  ```
67
75
 
68
- ## Posting
76
+ ## Noteing
77
+
78
+ Create a note:
79
+
80
+ ```bash
81
+ kiipu note create "Ship the beta today"
82
+ kiipu note create --content "Ship the beta today"
83
+ ```
84
+
85
+ Delete, restore, or permanently remove a note by id:
69
86
 
70
- Create a post:
87
+ ```bash
88
+ kiipu note delete --id note_123
89
+ kiipu note restore --id note_123
90
+ kiipu note purge --id note_123
91
+ ```
92
+
93
+ ## Ask
94
+
95
+ Ask a new question and stream the answer:
71
96
 
72
97
  ```bash
73
- kiipu post create "Ship the beta today"
74
- kiipu post create --content "Ship the beta today"
98
+ kiipu ask "What did I save about the roadmap?"
99
+ kiipu ask --question "What should I follow up on?"
75
100
  ```
76
101
 
77
- Delete, restore, or permanently remove a post by id:
102
+ Continue a conversation or inspect Ask history:
78
103
 
79
104
  ```bash
80
- kiipu post delete --id post_123
81
- kiipu post restore --id post_123
82
- kiipu post purge --id post_123
105
+ kiipu ask --conversation-id conv_123 "What should I do next?"
106
+ kiipu ask history --limit 10
107
+ kiipu ask show --id conv_123
83
108
  ```
84
109
 
85
110
  ## Core Commands
@@ -89,10 +114,14 @@ kiipu auth login
89
114
  kiipu auth status
90
115
  kiipu auth logout
91
116
 
92
- kiipu post create "Hello Kiipu"
93
- kiipu post delete --id post_123
94
- kiipu post restore --id post_123
95
- kiipu post purge --id post_123
117
+ kiipu ask "What did I save about the roadmap?"
118
+ kiipu ask history --limit 10
119
+ kiipu ask show --id conv_123
120
+
121
+ kiipu note create "Hello Kiipu"
122
+ kiipu note delete --id note_123
123
+ kiipu note restore --id note_123
124
+ kiipu note purge --id note_123
96
125
 
97
126
  kiipu doctor
98
127
  kiipu --help
@@ -122,5 +151,6 @@ See the full command reference in the terminal:
122
151
  ```bash
123
152
  kiipu --help
124
153
  kiipu auth --help
125
- kiipu post --help
154
+ kiipu ask --help
155
+ kiipu note --help
126
156
  ```
@@ -0,0 +1,229 @@
1
+ import { KiipuAskClient } from '../lib/ask-client.js';
2
+ import { formatAskFooter, formatConversationDetail, formatConversationHistory, } from '../lib/ask-formatters.js';
3
+ import { hasFlag, readFlag } from '../utils/args.js';
4
+ const sourceModes = new Set(['fresh', 'locked']);
5
+ function error(message, data) {
6
+ return {
7
+ ok: false,
8
+ message,
9
+ ...(data ? { data, json: { ok: false, message, ...data } } : {}),
10
+ };
11
+ }
12
+ function usage(action) {
13
+ if (action === 'history') {
14
+ return 'Usage: kiipu ask history [--query <q>] [--limit <n>] [--archived]';
15
+ }
16
+ if (action === 'show') {
17
+ return 'Usage: kiipu ask show --id <conversationId>';
18
+ }
19
+ return [
20
+ 'Usage: kiipu ask "question"',
21
+ ' or: kiipu ask --question "question"',
22
+ ' or: kiipu ask --conversation-id <id> "follow-up"',
23
+ ].join('\n');
24
+ }
25
+ function getAskClient(config) {
26
+ const apiKey = config.apiKey ?? process.env.KIIPU_API_KEY ?? '';
27
+ if (!apiKey) {
28
+ return {
29
+ error: error('Kiipu API key is missing. Run `kiipu auth login` first.', {
30
+ code: 'missing_api_key',
31
+ }),
32
+ };
33
+ }
34
+ return {
35
+ client: new KiipuAskClient({
36
+ apiBaseUrl: config.apiBaseUrl,
37
+ apiKey,
38
+ }),
39
+ };
40
+ }
41
+ function stripKnownFlags(args, flagsWithValues) {
42
+ const positional = [];
43
+ const flags = new Set(flagsWithValues);
44
+ for (let index = 0; index < args.length; index += 1) {
45
+ const arg = args[index];
46
+ if (!arg) {
47
+ continue;
48
+ }
49
+ if (flags.has(arg)) {
50
+ index += 1;
51
+ continue;
52
+ }
53
+ if (!arg.startsWith('--')) {
54
+ positional.push(arg);
55
+ }
56
+ }
57
+ return positional;
58
+ }
59
+ function parseTopK(args) {
60
+ const raw = readFlag(args, '--top-k');
61
+ if (!raw) {
62
+ return undefined;
63
+ }
64
+ const value = Number(raw);
65
+ if (!Number.isInteger(value) || value < 1 || value > 20) {
66
+ return null;
67
+ }
68
+ return value;
69
+ }
70
+ function parseLimit(args) {
71
+ const raw = readFlag(args, '--limit');
72
+ if (!raw) {
73
+ return undefined;
74
+ }
75
+ const value = Number(raw);
76
+ if (!Number.isInteger(value) || value < 1 || value > 50) {
77
+ return null;
78
+ }
79
+ return value;
80
+ }
81
+ function parseSourceMode(args) {
82
+ const value = readFlag(args, '--source-mode');
83
+ if (!value) {
84
+ return undefined;
85
+ }
86
+ return sourceModes.has(value) ? value : null;
87
+ }
88
+ function readQuestion(args) {
89
+ const explicit = readFlag(args, '--question')?.trim();
90
+ if (explicit) {
91
+ return explicit;
92
+ }
93
+ return stripKnownFlags(args, ['--question', '--conversation-id', '--top-k', '--source-mode'])
94
+ .join(' ')
95
+ .trim();
96
+ }
97
+ async function handleHistory(config, args) {
98
+ const { client, error: clientError } = getAskClient(config);
99
+ if (!client) {
100
+ return clientError;
101
+ }
102
+ const limit = parseLimit(args);
103
+ if (limit === null) {
104
+ return error(`Invalid --limit value. ${usage('history')}`, { code: 'invalid_limit' });
105
+ }
106
+ const response = await client.listConversations({
107
+ query: readFlag(args, '--query'),
108
+ limit,
109
+ archived: hasFlag(args, '--archived'),
110
+ });
111
+ if (!response.ok) {
112
+ return error(response.error.message, response.error);
113
+ }
114
+ return {
115
+ ok: true,
116
+ message: formatConversationHistory(hasFlag(args, '--archived') ? 'Archived Ask conversations' : 'Ask conversations', response.data.items, response.data.nextCursor),
117
+ data: response.data,
118
+ };
119
+ }
120
+ async function handleShow(config, args) {
121
+ const { client, error: clientError } = getAskClient(config);
122
+ if (!client) {
123
+ return clientError;
124
+ }
125
+ const id = readFlag(args, '--id')?.trim();
126
+ if (!id) {
127
+ return error(usage('show'), { code: 'missing_conversation_id' });
128
+ }
129
+ const response = await client.getConversation(id);
130
+ if (!response.ok) {
131
+ return error(response.error.message, response.error);
132
+ }
133
+ return {
134
+ ok: true,
135
+ message: formatConversationDetail(response.data),
136
+ data: response.data,
137
+ };
138
+ }
139
+ async function handleQuestion(config, args, options) {
140
+ const { client, error: clientError } = getAskClient(config);
141
+ if (!client) {
142
+ return clientError;
143
+ }
144
+ const question = readQuestion(args);
145
+ if (!question) {
146
+ return error(usage(), { code: 'missing_question' });
147
+ }
148
+ const topK = parseTopK(args);
149
+ if (topK === null) {
150
+ return error(`Invalid --top-k value. ${usage()}`, { code: 'invalid_top_k' });
151
+ }
152
+ const sourceMode = parseSourceMode(args);
153
+ if (sourceMode === null) {
154
+ return error('Invalid --source-mode value. Use fresh or locked.', {
155
+ code: 'invalid_source_mode',
156
+ });
157
+ }
158
+ const result = {
159
+ answer: '',
160
+ conversationId: readFlag(args, '--conversation-id')?.trim() || undefined,
161
+ sources: [],
162
+ };
163
+ const targetConversationId = result.conversationId ?? 'new';
164
+ for await (const event of client.streamMessage({
165
+ conversationId: targetConversationId,
166
+ question,
167
+ topK,
168
+ sourceMode,
169
+ })) {
170
+ switch (event.type) {
171
+ case 'meta':
172
+ result.conversationId = event.conversationId;
173
+ result.title = event.title;
174
+ break;
175
+ case 'sources':
176
+ result.sources = event.sources;
177
+ break;
178
+ case 'delta':
179
+ result.answer += event.text;
180
+ if (options.stream) {
181
+ options.write?.(event.text);
182
+ }
183
+ break;
184
+ case 'done':
185
+ result.turnId = event.turnId;
186
+ result.usage = {
187
+ inputTokens: event.inputTokens,
188
+ outputTokens: event.outputTokens,
189
+ latencyMs: event.latencyMs,
190
+ };
191
+ break;
192
+ case 'title':
193
+ result.conversationId = event.conversationId;
194
+ result.title = event.title;
195
+ break;
196
+ case 'error':
197
+ return error(event.message, {
198
+ code: event.code,
199
+ answer: result.answer,
200
+ conversationId: result.conversationId,
201
+ title: result.title,
202
+ turnId: result.turnId,
203
+ sources: result.sources,
204
+ usage: result.usage,
205
+ });
206
+ }
207
+ }
208
+ const footer = formatAskFooter(result);
209
+ return {
210
+ ok: true,
211
+ message: options.stream ? footer : `${result.answer}${footer}`,
212
+ data: result,
213
+ json: {
214
+ ok: true,
215
+ ...result,
216
+ },
217
+ };
218
+ }
219
+ export async function runAskCommand(config, args, options = {}) {
220
+ const action = args[1];
221
+ const actionArgs = args.slice(2);
222
+ if (action === 'history') {
223
+ return handleHistory(config, actionArgs);
224
+ }
225
+ if (action === 'show') {
226
+ return handleShow(config, actionArgs);
227
+ }
228
+ return handleQuestion(config, args.slice(1), options);
229
+ }
@@ -1,5 +1,5 @@
1
1
  import { saveKiipuConfig } from '../config/config.js';
2
- import { createAuthState, createLoopbackServer, createPkcePair, getDefaultDeviceName, openBrowser, waitForEnterBeforeOpeningBrowser } from '../lib/browser-auth.js';
2
+ import { createAuthState, createLoopbackServer, createPkcePair, getDefaultDeviceName, openBrowser, waitForEnterBeforeOpeningBrowser, } from '../lib/browser-auth.js';
3
3
  import { KiipuUserApiClient } from '../lib/kiipu-user-client.js';
4
4
  import { logCliEvent } from '../logger/cli-logger.js';
5
5
  async function storeAuthenticatedConfig(config, payload) {
@@ -14,11 +14,15 @@ export async function runDoctorCommand(config) {
14
14
  const checks = [];
15
15
  let ok = true;
16
16
  const apiBaseUrlConfig = getConfiguredApiBaseUrl();
17
- checks.push(config.apiKey ? `OK API key: ${config.keyPrefix ?? 'configured'}` : 'Missing API key. Run `kiipu auth login`.');
17
+ checks.push(config.apiKey
18
+ ? `OK API key: ${config.keyPrefix ?? 'configured'}`
19
+ : 'Missing API key. Run `kiipu auth login`.');
18
20
  if (!(config.apiKey || process.env.KIIPU_API_KEY)) {
19
21
  ok = false;
20
22
  }
21
- checks.push(config.apiKey || process.env.KIIPU_API_KEY ? 'OK post API auth: configured' : 'Missing API key for post requests.');
23
+ checks.push(config.apiKey || process.env.KIIPU_API_KEY
24
+ ? 'OK note API auth: configured'
25
+ : 'Missing API key for note requests.');
22
26
  checks.push(apiBaseUrlConfig.source === 'env'
23
27
  ? `WARN API base URL override: ${config.apiBaseUrl}`
24
28
  : `OK API base URL: ${config.apiBaseUrl}`);
@@ -42,7 +46,9 @@ export async function runDoctorCommand(config) {
42
46
  if (!authStatus.ok) {
43
47
  ok = false;
44
48
  }
45
- checks.push(authStatus.ok ? `OK API key auth: ${authStatus.data.username}` : `API key auth failed: ${authStatus.error.message}`);
49
+ checks.push(authStatus.ok
50
+ ? `OK API key auth: ${authStatus.data.username}`
51
+ : `API key auth failed: ${authStatus.error.message}`);
46
52
  }
47
53
  try {
48
54
  await access(configPath);
@@ -2,64 +2,98 @@ function block(lines) {
2
2
  return lines.join('\n');
3
3
  }
4
4
  export function getHelpResult(command) {
5
- if (command === 'post') {
5
+ if (command === 'ask') {
6
6
  return {
7
7
  ok: true,
8
8
  message: block([
9
9
  'Kiipu CLI',
10
10
  '',
11
11
  'Usage:',
12
- ' kiipu post create --content "<text>"',
13
- ' kiipu post create "<text>"',
14
- ' kiipu post list [--tag <tag>] [--sort <updatedAt|createdAt|title>] [--starred] [--deleted]',
15
- ' kiipu post search <query>',
16
- ' kiipu post search --query "<query>"',
17
- ' kiipu post show --id <postId>',
18
- ' kiipu post update --id <postId> --content "<text>" [--title "<title>"] [--visibility public|private] [--tags <a,b,c>]',
19
- ' kiipu post star --id <postId>',
20
- ' kiipu post pin --id <postId>',
21
- ' kiipu post delete --id <postId>',
22
- ' kiipu post restore --id <postId>',
23
- ' kiipu post purge --id <postId>',
12
+ ' kiipu ask "question"',
13
+ ' kiipu ask --question "question"',
14
+ ' kiipu ask --conversation-id <conversationId> "follow-up"',
15
+ ' kiipu ask history [--query <q>] [--limit <n>] [--archived]',
16
+ ' kiipu ask show --id <conversationId>',
24
17
  '',
25
18
  'Description:',
26
- ' Run direct Kiipu post actions from the local CLI.',
19
+ ' Ask questions over your Kiipu notes using the same conversation-aware Ask surface as the web app.',
20
+ '',
21
+ 'Options:',
22
+ ' --question "<text>" Question to ask when not using a positional argument',
23
+ ' --conversation-id <id> Continue an existing Ask conversation',
24
+ ' --top-k <1-20> Optional retrieval count for Ask',
25
+ ' --source-mode <value> One of fresh or locked',
26
+ ' --query "<query>" Filter Ask history rows',
27
+ ' --limit <1-50> Limit Ask history rows',
28
+ ' --archived List archived Ask conversations',
29
+ ' --id <conversationId> Conversation id for ask show',
30
+ '',
31
+ 'Examples:',
32
+ ' kiipu ask "What did I save about the roadmap?"',
33
+ ' kiipu ask --conversation-id conv_123 "What should I do next?"',
34
+ ' kiipu ask history --limit 10',
35
+ ' kiipu ask show --id conv_123',
36
+ ]),
37
+ };
38
+ }
39
+ if (command === 'note') {
40
+ return {
41
+ ok: true,
42
+ message: block([
43
+ 'Kiipu CLI',
44
+ '',
45
+ 'Usage:',
46
+ ' kiipu note create --content "<text>"',
47
+ ' kiipu note create "<text>"',
48
+ ' kiipu note list [--tag <tag>] [--sort <updatedAt|createdAt|title>] [--starred] [--deleted]',
49
+ ' kiipu note search <query>',
50
+ ' kiipu note search --query "<query>"',
51
+ ' kiipu note show --id <noteId>',
52
+ ' kiipu note update --id <noteId> --content "<text>" [--title "<title>"] [--visibility public|private] [--tags <a,b,c>]',
53
+ ' kiipu note star --id <noteId>',
54
+ ' kiipu note pin --id <noteId>',
55
+ ' kiipu note delete --id <noteId>',
56
+ ' kiipu note restore --id <noteId>',
57
+ ' kiipu note purge --id <noteId>',
58
+ '',
59
+ 'Description:',
60
+ ' Run direct Kiipu note actions from the local CLI.',
27
61
  '',
28
62
  'Actions:',
29
- ' create Create a new post',
30
- ' list List your published, starred, or deleted posts',
31
- ' search Search your published posts',
32
- ' show Show one post with full metadata',
33
- ' update Update post content and metadata',
34
- ' star Toggle a post star',
35
- ' pin Toggle a post pin',
36
- ' delete Soft-delete an existing post by explicit id',
37
- ' restore Restore a deleted post by explicit id',
38
- ' purge Permanently delete a post by explicit id',
63
+ ' create Create a new note',
64
+ ' list List your active, starred, or deleted notes',
65
+ ' search Search your active notes',
66
+ ' show Show one note with full metadata',
67
+ ' update Update note content and metadata',
68
+ ' star Toggle a note star',
69
+ ' pin Toggle a note pin',
70
+ ' delete Soft-delete an existing note by explicit id',
71
+ ' restore Restore a deleted note by explicit id',
72
+ ' purge Permanently delete a note by explicit id',
39
73
  '',
40
74
  'Options:',
41
- ' --content "<text>" Post content for create',
42
- ' --id <postId> Required for show, update, star, pin, delete, restore, and purge',
43
- ' --query "<query>" Search query for post search',
44
- ' --tag <tag> Filter post list results by tag',
75
+ ' --content "<text>" Note content for create',
76
+ ' --id <noteId> Required for show, update, star, pin, delete, restore, and purge',
77
+ ' --query "<query>" Search query for note search',
78
+ ' --tag <tag> Filter note list results by tag',
45
79
  ' --sort <field> One of updatedAt, createdAt, or title',
46
- ' --starred List only starred posts',
47
- ' --deleted List only deleted posts',
48
- ' --title "<title>" Optional title for post update',
49
- ' --visibility <value> Optional visibility for post update',
50
- ' --tags <a,b,c> Optional comma-separated tags for post update',
80
+ ' --starred List only starred notes',
81
+ ' --deleted List only deleted notes',
82
+ ' --title "<title>" Optional title for note update',
83
+ ' --visibility <value> Optional visibility for note update',
84
+ ' --tags <a,b,c> Optional comma-separated tags for note update',
51
85
  '',
52
86
  'Examples:',
53
- ' kiipu post create "Hello Kiipu"',
54
- ' kiipu post list --sort updatedAt',
55
- ' kiipu post search "roadmap"',
56
- ' kiipu post show --id 123',
57
- ' kiipu post update --id 123 --content "Updated text" --tags work,#notes',
58
- ' kiipu post star --id 123',
59
- ' kiipu post pin --id 123',
60
- ' kiipu post create --content "Hello Kiipu"',
61
- ' kiipu post delete --id 123',
62
- ' kiipu post restore --id 123',
87
+ ' kiipu note create "Hello Kiipu"',
88
+ ' kiipu note list --sort updatedAt',
89
+ ' kiipu note search "roadmap"',
90
+ ' kiipu note show --id 123',
91
+ ' kiipu note update --id 123 --content "Updated text" --tags work,#notes',
92
+ ' kiipu note star --id 123',
93
+ ' kiipu note pin --id 123',
94
+ ' kiipu note create --content "Hello Kiipu"',
95
+ ' kiipu note delete --id 123',
96
+ ' kiipu note restore --id 123',
63
97
  ]),
64
98
  };
65
99
  }
@@ -139,16 +173,19 @@ export function getHelpResult(command) {
139
173
  ' kiipu <command> [options]',
140
174
  '',
141
175
  'Core commands:',
142
- ' post Create, browse, update, and delete posts with direct CLI arguments',
176
+ ' ask Ask questions over your Kiipu notes and browse Ask conversations',
177
+ ' note Create, browse, update, and delete notes with direct CLI arguments',
143
178
  ' auth Manage local API key authentication',
144
179
  ' doctor Check local setup, API access, and wrapper readiness',
145
180
  ' skills Show where the Claude Code plugin package lives',
146
181
  '',
147
182
  'Core examples:',
148
- ' kiipu post create "Hello Kiipu"',
149
- ' kiipu post list --starred',
150
- ' kiipu post search "Hello"',
151
- ' kiipu post delete --id 123',
183
+ ' kiipu ask "What changed in my roadmap notes?"',
184
+ ' kiipu ask history --limit 10',
185
+ ' kiipu note create "Hello Kiipu"',
186
+ ' kiipu note list --starred',
187
+ ' kiipu note search "Hello"',
188
+ ' kiipu note delete --id 123',
152
189
  ' kiipu auth login',
153
190
  ' kiipu doctor',
154
191
  ' claude --plugin-dir ./packages/claude-plugin',