@kiipu/cli 0.0.6 → 0.0.8
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 +30 -0
- package/dist/commands/ask.js +234 -0
- package/dist/commands/help.js +68 -2
- package/dist/commands/post.js +315 -21
- package/dist/index.js +44 -27
- package/dist/lib/ask-client.js +276 -0
- package/dist/lib/ask-formatters.js +114 -0
- package/dist/lib/kiipu-user-client.js +44 -0
- package/dist/lib/post-formatters.js +74 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ Publish to Kiipu from your terminal.
|
|
|
7
7
|
Use it to:
|
|
8
8
|
|
|
9
9
|
- sign in on the current device
|
|
10
|
+
- ask questions over your saved posts
|
|
10
11
|
- publish posts from the command line
|
|
11
12
|
- delete, restore, or permanently remove posts by id
|
|
12
13
|
- verify local authentication and API access with `kiipu doctor`
|
|
@@ -39,10 +40,17 @@ kiipu post create "Hello Kiipu"
|
|
|
39
40
|
kiipu doctor
|
|
40
41
|
```
|
|
41
42
|
|
|
43
|
+
4. Ask over your saved posts:
|
|
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
|
|
53
|
+
kiipu ask "What should I follow up on?"
|
|
46
54
|
kiipu post create "Ship the beta today"
|
|
47
55
|
kiipu auth status
|
|
48
56
|
```
|
|
@@ -82,6 +90,23 @@ kiipu post restore --id post_123
|
|
|
82
90
|
kiipu post purge --id post_123
|
|
83
91
|
```
|
|
84
92
|
|
|
93
|
+
## Ask
|
|
94
|
+
|
|
95
|
+
Ask a new question and stream the answer:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
kiipu ask "What did I save about the roadmap?"
|
|
99
|
+
kiipu ask --question "What should I follow up on?"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Continue a conversation or inspect Ask history:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
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
|
|
108
|
+
```
|
|
109
|
+
|
|
85
110
|
## Core Commands
|
|
86
111
|
|
|
87
112
|
```bash
|
|
@@ -89,6 +114,10 @@ kiipu auth login
|
|
|
89
114
|
kiipu auth status
|
|
90
115
|
kiipu auth logout
|
|
91
116
|
|
|
117
|
+
kiipu ask "What did I save about the roadmap?"
|
|
118
|
+
kiipu ask history --limit 10
|
|
119
|
+
kiipu ask show --id conv_123
|
|
120
|
+
|
|
92
121
|
kiipu post create "Hello Kiipu"
|
|
93
122
|
kiipu post delete --id post_123
|
|
94
123
|
kiipu post restore --id post_123
|
|
@@ -122,5 +151,6 @@ See the full command reference in the terminal:
|
|
|
122
151
|
```bash
|
|
123
152
|
kiipu --help
|
|
124
153
|
kiipu auth --help
|
|
154
|
+
kiipu ask --help
|
|
125
155
|
kiipu post --help
|
|
126
156
|
```
|
|
@@ -0,0 +1,234 @@
|
|
|
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, [
|
|
94
|
+
'--question',
|
|
95
|
+
'--conversation-id',
|
|
96
|
+
'--top-k',
|
|
97
|
+
'--source-mode',
|
|
98
|
+
])
|
|
99
|
+
.join(' ')
|
|
100
|
+
.trim();
|
|
101
|
+
}
|
|
102
|
+
async function handleHistory(config, args) {
|
|
103
|
+
const { client, error: clientError } = getAskClient(config);
|
|
104
|
+
if (!client) {
|
|
105
|
+
return clientError;
|
|
106
|
+
}
|
|
107
|
+
const limit = parseLimit(args);
|
|
108
|
+
if (limit === null) {
|
|
109
|
+
return error(`Invalid --limit value. ${usage('history')}`, { code: 'invalid_limit' });
|
|
110
|
+
}
|
|
111
|
+
const response = await client.listConversations({
|
|
112
|
+
query: readFlag(args, '--query'),
|
|
113
|
+
limit,
|
|
114
|
+
archived: hasFlag(args, '--archived'),
|
|
115
|
+
});
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
return error(response.error.message, response.error);
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
ok: true,
|
|
121
|
+
message: formatConversationHistory(hasFlag(args, '--archived') ? 'Archived Ask conversations' : 'Ask conversations', response.data.items, response.data.nextCursor),
|
|
122
|
+
data: response.data,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
async function handleShow(config, args) {
|
|
126
|
+
const { client, error: clientError } = getAskClient(config);
|
|
127
|
+
if (!client) {
|
|
128
|
+
return clientError;
|
|
129
|
+
}
|
|
130
|
+
const id = readFlag(args, '--id')?.trim();
|
|
131
|
+
if (!id) {
|
|
132
|
+
return error(usage('show'), { code: 'missing_conversation_id' });
|
|
133
|
+
}
|
|
134
|
+
const response = await client.getConversation(id);
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
return error(response.error.message, response.error);
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
ok: true,
|
|
140
|
+
message: formatConversationDetail(response.data),
|
|
141
|
+
data: response.data,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
async function handleQuestion(config, args, options) {
|
|
145
|
+
const { client, error: clientError } = getAskClient(config);
|
|
146
|
+
if (!client) {
|
|
147
|
+
return clientError;
|
|
148
|
+
}
|
|
149
|
+
const question = readQuestion(args);
|
|
150
|
+
if (!question) {
|
|
151
|
+
return error(usage(), { code: 'missing_question' });
|
|
152
|
+
}
|
|
153
|
+
const topK = parseTopK(args);
|
|
154
|
+
if (topK === null) {
|
|
155
|
+
return error(`Invalid --top-k value. ${usage()}`, { code: 'invalid_top_k' });
|
|
156
|
+
}
|
|
157
|
+
const sourceMode = parseSourceMode(args);
|
|
158
|
+
if (sourceMode === null) {
|
|
159
|
+
return error('Invalid --source-mode value. Use fresh or locked.', {
|
|
160
|
+
code: 'invalid_source_mode',
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
const result = {
|
|
164
|
+
answer: '',
|
|
165
|
+
conversationId: readFlag(args, '--conversation-id')?.trim() || undefined,
|
|
166
|
+
sources: [],
|
|
167
|
+
};
|
|
168
|
+
const targetConversationId = result.conversationId ?? 'new';
|
|
169
|
+
for await (const event of client.streamMessage({
|
|
170
|
+
conversationId: targetConversationId,
|
|
171
|
+
question,
|
|
172
|
+
topK,
|
|
173
|
+
sourceMode,
|
|
174
|
+
})) {
|
|
175
|
+
switch (event.type) {
|
|
176
|
+
case 'meta':
|
|
177
|
+
result.conversationId = event.conversationId;
|
|
178
|
+
result.title = event.title;
|
|
179
|
+
break;
|
|
180
|
+
case 'sources':
|
|
181
|
+
result.sources = event.sources;
|
|
182
|
+
break;
|
|
183
|
+
case 'delta':
|
|
184
|
+
result.answer += event.text;
|
|
185
|
+
if (options.stream) {
|
|
186
|
+
options.write?.(event.text);
|
|
187
|
+
}
|
|
188
|
+
break;
|
|
189
|
+
case 'done':
|
|
190
|
+
result.turnId = event.turnId;
|
|
191
|
+
result.usage = {
|
|
192
|
+
inputTokens: event.inputTokens,
|
|
193
|
+
outputTokens: event.outputTokens,
|
|
194
|
+
latencyMs: event.latencyMs,
|
|
195
|
+
};
|
|
196
|
+
break;
|
|
197
|
+
case 'title':
|
|
198
|
+
result.conversationId = event.conversationId;
|
|
199
|
+
result.title = event.title;
|
|
200
|
+
break;
|
|
201
|
+
case 'error':
|
|
202
|
+
return error(event.message, {
|
|
203
|
+
code: event.code,
|
|
204
|
+
answer: result.answer,
|
|
205
|
+
conversationId: result.conversationId,
|
|
206
|
+
title: result.title,
|
|
207
|
+
turnId: result.turnId,
|
|
208
|
+
sources: result.sources,
|
|
209
|
+
usage: result.usage,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const footer = formatAskFooter(result);
|
|
214
|
+
return {
|
|
215
|
+
ok: true,
|
|
216
|
+
message: options.stream ? footer : `${result.answer}${footer}`,
|
|
217
|
+
data: result,
|
|
218
|
+
json: {
|
|
219
|
+
ok: true,
|
|
220
|
+
...result,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
export async function runAskCommand(config, args, options = {}) {
|
|
225
|
+
const action = args[1];
|
|
226
|
+
const actionArgs = args.slice(2);
|
|
227
|
+
if (action === 'history') {
|
|
228
|
+
return handleHistory(config, actionArgs);
|
|
229
|
+
}
|
|
230
|
+
if (action === 'show') {
|
|
231
|
+
return handleShow(config, actionArgs);
|
|
232
|
+
}
|
|
233
|
+
return handleQuestion(config, args.slice(1), options);
|
|
234
|
+
}
|
package/dist/commands/help.js
CHANGED
|
@@ -2,6 +2,40 @@ function block(lines) {
|
|
|
2
2
|
return lines.join('\n');
|
|
3
3
|
}
|
|
4
4
|
export function getHelpResult(command) {
|
|
5
|
+
if (command === 'ask') {
|
|
6
|
+
return {
|
|
7
|
+
ok: true,
|
|
8
|
+
message: block([
|
|
9
|
+
'Kiipu CLI',
|
|
10
|
+
'',
|
|
11
|
+
'Usage:',
|
|
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>',
|
|
17
|
+
'',
|
|
18
|
+
'Description:',
|
|
19
|
+
' Ask questions over your Kiipu posts 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
|
+
}
|
|
5
39
|
if (command === 'post') {
|
|
6
40
|
return {
|
|
7
41
|
ok: true,
|
|
@@ -11,6 +45,13 @@ export function getHelpResult(command) {
|
|
|
11
45
|
'Usage:',
|
|
12
46
|
' kiipu post create --content "<text>"',
|
|
13
47
|
' kiipu post create "<text>"',
|
|
48
|
+
' kiipu post list [--tag <tag>] [--sort <updatedAt|createdAt|title>] [--starred] [--deleted]',
|
|
49
|
+
' kiipu post search <query>',
|
|
50
|
+
' kiipu post search --query "<query>"',
|
|
51
|
+
' kiipu post show --id <postId>',
|
|
52
|
+
' kiipu post update --id <postId> --content "<text>" [--title "<title>"] [--visibility public|private] [--tags <a,b,c>]',
|
|
53
|
+
' kiipu post star --id <postId>',
|
|
54
|
+
' kiipu post pin --id <postId>',
|
|
14
55
|
' kiipu post delete --id <postId>',
|
|
15
56
|
' kiipu post restore --id <postId>',
|
|
16
57
|
' kiipu post purge --id <postId>',
|
|
@@ -20,16 +61,36 @@ export function getHelpResult(command) {
|
|
|
20
61
|
'',
|
|
21
62
|
'Actions:',
|
|
22
63
|
' create Create a new post',
|
|
64
|
+
' list List your published, starred, or deleted posts',
|
|
65
|
+
' search Search your published posts',
|
|
66
|
+
' show Show one post with full metadata',
|
|
67
|
+
' update Update post content and metadata',
|
|
68
|
+
' star Toggle a post star',
|
|
69
|
+
' pin Toggle a post pin',
|
|
23
70
|
' delete Soft-delete an existing post by explicit id',
|
|
24
71
|
' restore Restore a deleted post by explicit id',
|
|
25
72
|
' purge Permanently delete a post by explicit id',
|
|
26
73
|
'',
|
|
27
74
|
'Options:',
|
|
28
75
|
' --content "<text>" Post content for create',
|
|
29
|
-
' --id <postId> Required for delete, restore, and purge',
|
|
76
|
+
' --id <postId> Required for show, update, star, pin, delete, restore, and purge',
|
|
77
|
+
' --query "<query>" Search query for post search',
|
|
78
|
+
' --tag <tag> Filter post list results by tag',
|
|
79
|
+
' --sort <field> One of updatedAt, createdAt, or title',
|
|
80
|
+
' --starred List only starred posts',
|
|
81
|
+
' --deleted List only deleted posts',
|
|
82
|
+
' --title "<title>" Optional title for post update',
|
|
83
|
+
' --visibility <value> Optional visibility for post update',
|
|
84
|
+
' --tags <a,b,c> Optional comma-separated tags for post update',
|
|
30
85
|
'',
|
|
31
86
|
'Examples:',
|
|
32
87
|
' kiipu post create "Hello Kiipu"',
|
|
88
|
+
' kiipu post list --sort updatedAt',
|
|
89
|
+
' kiipu post search "roadmap"',
|
|
90
|
+
' kiipu post show --id 123',
|
|
91
|
+
' kiipu post update --id 123 --content "Updated text" --tags work,#notes',
|
|
92
|
+
' kiipu post star --id 123',
|
|
93
|
+
' kiipu post pin --id 123',
|
|
33
94
|
' kiipu post create --content "Hello Kiipu"',
|
|
34
95
|
' kiipu post delete --id 123',
|
|
35
96
|
' kiipu post restore --id 123',
|
|
@@ -112,13 +173,18 @@ export function getHelpResult(command) {
|
|
|
112
173
|
' kiipu <command> [options]',
|
|
113
174
|
'',
|
|
114
175
|
'Core commands:',
|
|
115
|
-
'
|
|
176
|
+
' ask Ask questions over your Kiipu posts and browse Ask conversations',
|
|
177
|
+
' post Create, browse, update, and delete posts with direct CLI arguments',
|
|
116
178
|
' auth Manage local API key authentication',
|
|
117
179
|
' doctor Check local setup, API access, and wrapper readiness',
|
|
118
180
|
' skills Show where the Claude Code plugin package lives',
|
|
119
181
|
'',
|
|
120
182
|
'Core examples:',
|
|
183
|
+
' kiipu ask "What changed in my roadmap notes?"',
|
|
184
|
+
' kiipu ask history --limit 10',
|
|
121
185
|
' kiipu post create "Hello Kiipu"',
|
|
186
|
+
' kiipu post list --starred',
|
|
187
|
+
' kiipu post search "Hello"',
|
|
122
188
|
' kiipu post delete --id 123',
|
|
123
189
|
' kiipu auth login',
|
|
124
190
|
' kiipu doctor',
|