@kiipu/cli 0.0.8 → 0.0.10
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 +22 -22
- package/dist/commands/ask.js +1 -6
- package/dist/commands/auth.js +1 -1
- package/dist/commands/doctor.js +9 -3
- package/dist/commands/help.js +50 -50
- package/dist/commands/{post.js → note.js} +101 -96
- package/dist/config/load-env.js +4 -1
- package/dist/index.js +3 -3
- package/dist/lib/ask-client.js +6 -4
- package/dist/lib/ask-formatters.js +1 -1
- package/dist/lib/kiipu-integration-client.js +26 -20
- package/dist/lib/kiipu-user-client.js +20 -14
- package/dist/lib/{post-actions.js → note-actions.js} +20 -20
- package/dist/lib/{post-formatters.js → note-formatters.js} +26 -23
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# Kiipu CLI
|
|
2
2
|
|
|
3
|
-
|
|
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
|
|
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
|
-
- ask questions over your saved
|
|
11
|
-
-
|
|
12
|
-
- delete, restore, or permanently remove
|
|
10
|
+
- ask questions over your saved notes
|
|
11
|
+
- create notes from the command line
|
|
12
|
+
- delete, restore, or permanently remove notes by id
|
|
13
13
|
- verify local authentication and API access with `kiipu doctor`
|
|
14
14
|
|
|
15
15
|
If you want Claude Code integration on top of the CLI, use `@kiipu/claude-plugin`.
|
|
@@ -28,10 +28,10 @@ npm install -g @kiipu/cli
|
|
|
28
28
|
kiipu auth login
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
2.
|
|
31
|
+
2. Create a note:
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
kiipu
|
|
34
|
+
kiipu note create "Hello Kiipu"
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
3. Confirm local setup:
|
|
@@ -40,7 +40,7 @@ kiipu post create "Hello Kiipu"
|
|
|
40
40
|
kiipu doctor
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
4. Ask over your saved
|
|
43
|
+
4. Ask over your saved notes:
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
46
|
kiipu ask "What did I save about the roadmap?"
|
|
@@ -51,7 +51,7 @@ kiipu ask "What did I save about the roadmap?"
|
|
|
51
51
|
```bash
|
|
52
52
|
kiipu auth login
|
|
53
53
|
kiipu ask "What should I follow up on?"
|
|
54
|
-
kiipu
|
|
54
|
+
kiipu note create "Ship the beta today"
|
|
55
55
|
kiipu auth status
|
|
56
56
|
```
|
|
57
57
|
|
|
@@ -73,21 +73,21 @@ kiipu auth status
|
|
|
73
73
|
kiipu auth logout
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
##
|
|
76
|
+
## Noteing
|
|
77
77
|
|
|
78
|
-
Create a
|
|
78
|
+
Create a note:
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
kiipu
|
|
82
|
-
kiipu
|
|
81
|
+
kiipu note create "Ship the beta today"
|
|
82
|
+
kiipu note create --content "Ship the beta today"
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
Delete, restore, or permanently remove a
|
|
85
|
+
Delete, restore, or permanently remove a note by id:
|
|
86
86
|
|
|
87
87
|
```bash
|
|
88
|
-
kiipu
|
|
89
|
-
kiipu
|
|
90
|
-
kiipu
|
|
88
|
+
kiipu note delete --id note_123
|
|
89
|
+
kiipu note restore --id note_123
|
|
90
|
+
kiipu note purge --id note_123
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
## Ask
|
|
@@ -118,10 +118,10 @@ kiipu ask "What did I save about the roadmap?"
|
|
|
118
118
|
kiipu ask history --limit 10
|
|
119
119
|
kiipu ask show --id conv_123
|
|
120
120
|
|
|
121
|
-
kiipu
|
|
122
|
-
kiipu
|
|
123
|
-
kiipu
|
|
124
|
-
kiipu
|
|
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
|
|
125
125
|
|
|
126
126
|
kiipu doctor
|
|
127
127
|
kiipu --help
|
|
@@ -152,5 +152,5 @@ See the full command reference in the terminal:
|
|
|
152
152
|
kiipu --help
|
|
153
153
|
kiipu auth --help
|
|
154
154
|
kiipu ask --help
|
|
155
|
-
kiipu
|
|
155
|
+
kiipu note --help
|
|
156
156
|
```
|
package/dist/commands/ask.js
CHANGED
|
@@ -90,12 +90,7 @@ function readQuestion(args) {
|
|
|
90
90
|
if (explicit) {
|
|
91
91
|
return explicit;
|
|
92
92
|
}
|
|
93
|
-
return stripKnownFlags(args, [
|
|
94
|
-
'--question',
|
|
95
|
-
'--conversation-id',
|
|
96
|
-
'--top-k',
|
|
97
|
-
'--source-mode',
|
|
98
|
-
])
|
|
93
|
+
return stripKnownFlags(args, ['--question', '--conversation-id', '--top-k', '--source-mode'])
|
|
99
94
|
.join(' ')
|
|
100
95
|
.trim();
|
|
101
96
|
}
|
package/dist/commands/auth.js
CHANGED
|
@@ -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) {
|
package/dist/commands/doctor.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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);
|
package/dist/commands/help.js
CHANGED
|
@@ -16,7 +16,7 @@ export function getHelpResult(command) {
|
|
|
16
16
|
' kiipu ask show --id <conversationId>',
|
|
17
17
|
'',
|
|
18
18
|
'Description:',
|
|
19
|
-
' Ask questions over your Kiipu
|
|
19
|
+
' Ask questions over your Kiipu notes using the same conversation-aware Ask surface as the web app.',
|
|
20
20
|
'',
|
|
21
21
|
'Options:',
|
|
22
22
|
' --question "<text>" Question to ask when not using a positional argument',
|
|
@@ -36,64 +36,64 @@ export function getHelpResult(command) {
|
|
|
36
36
|
]),
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
if (command === '
|
|
39
|
+
if (command === 'note') {
|
|
40
40
|
return {
|
|
41
41
|
ok: true,
|
|
42
42
|
message: block([
|
|
43
43
|
'Kiipu CLI',
|
|
44
44
|
'',
|
|
45
45
|
'Usage:',
|
|
46
|
-
' kiipu
|
|
47
|
-
' kiipu
|
|
48
|
-
' kiipu
|
|
49
|
-
' kiipu
|
|
50
|
-
' kiipu
|
|
51
|
-
' kiipu
|
|
52
|
-
' kiipu
|
|
53
|
-
' kiipu
|
|
54
|
-
' kiipu
|
|
55
|
-
' kiipu
|
|
56
|
-
' kiipu
|
|
57
|
-
' kiipu
|
|
46
|
+
' kiipu note create --content "<text>" [--title "<title>"]',
|
|
47
|
+
' kiipu note create "<text>" [--title "<title>"]',
|
|
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]',
|
|
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
58
|
'',
|
|
59
59
|
'Description:',
|
|
60
|
-
' Run direct Kiipu
|
|
60
|
+
' Run direct Kiipu note actions from the local CLI.',
|
|
61
61
|
'',
|
|
62
62
|
'Actions:',
|
|
63
|
-
' create Create a new
|
|
64
|
-
' list List your
|
|
65
|
-
' search Search your
|
|
66
|
-
' show Show one
|
|
67
|
-
' update Update
|
|
68
|
-
' star Toggle a
|
|
69
|
-
' pin Toggle a
|
|
70
|
-
' delete Soft-delete an existing
|
|
71
|
-
' restore Restore a deleted
|
|
72
|
-
' purge Permanently delete a
|
|
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',
|
|
73
73
|
'',
|
|
74
74
|
'Options:',
|
|
75
|
-
' --content "<text>"
|
|
76
|
-
' --id <
|
|
77
|
-
' --query "<query>" Search query for
|
|
78
|
-
' --tag <tag> Filter
|
|
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',
|
|
79
79
|
' --sort <field> One of updatedAt, createdAt, or title',
|
|
80
|
-
' --starred List only starred
|
|
81
|
-
' --deleted List only deleted
|
|
82
|
-
' --title "<title>" Optional title for
|
|
83
|
-
' --visibility <value> Optional visibility for
|
|
84
|
-
'
|
|
80
|
+
' --starred List only starred notes',
|
|
81
|
+
' --deleted List only deleted notes',
|
|
82
|
+
' --title "<title>" Optional title for note create or update',
|
|
83
|
+
' --visibility <value> Optional visibility for note update',
|
|
84
|
+
' Tags are derived from #tag tokens in note content',
|
|
85
85
|
'',
|
|
86
86
|
'Examples:',
|
|
87
|
-
' kiipu
|
|
88
|
-
' kiipu
|
|
89
|
-
' kiipu
|
|
90
|
-
' kiipu
|
|
91
|
-
' kiipu
|
|
92
|
-
' kiipu
|
|
93
|
-
' kiipu
|
|
94
|
-
' kiipu
|
|
95
|
-
' kiipu
|
|
96
|
-
' kiipu
|
|
87
|
+
' kiipu note create "Hello Kiipu" --title "Welcome"',
|
|
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 #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',
|
|
97
97
|
]),
|
|
98
98
|
};
|
|
99
99
|
}
|
|
@@ -173,8 +173,8 @@ export function getHelpResult(command) {
|
|
|
173
173
|
' kiipu <command> [options]',
|
|
174
174
|
'',
|
|
175
175
|
'Core commands:',
|
|
176
|
-
' ask Ask questions over your Kiipu
|
|
177
|
-
'
|
|
176
|
+
' ask Ask questions over your Kiipu notes and browse Ask conversations',
|
|
177
|
+
' note Create, browse, update, and delete notes with direct CLI arguments',
|
|
178
178
|
' auth Manage local API key authentication',
|
|
179
179
|
' doctor Check local setup, API access, and wrapper readiness',
|
|
180
180
|
' skills Show where the Claude Code plugin package lives',
|
|
@@ -182,10 +182,10 @@ export function getHelpResult(command) {
|
|
|
182
182
|
'Core examples:',
|
|
183
183
|
' kiipu ask "What changed in my roadmap notes?"',
|
|
184
184
|
' kiipu ask history --limit 10',
|
|
185
|
-
' kiipu
|
|
186
|
-
' kiipu
|
|
187
|
-
' kiipu
|
|
188
|
-
' kiipu
|
|
185
|
+
' kiipu note create "Hello Kiipu"',
|
|
186
|
+
' kiipu note list --starred',
|
|
187
|
+
' kiipu note search "Hello"',
|
|
188
|
+
' kiipu note delete --id 123',
|
|
189
189
|
' kiipu auth login',
|
|
190
190
|
' kiipu doctor',
|
|
191
191
|
' claude --plugin-dir ./packages/claude-plugin',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { formatNoteCollection, formatNoteDetail } from '../lib/note-formatters.js';
|
|
2
2
|
import { KiipuUserApiClient } from '../lib/kiipu-user-client.js';
|
|
3
|
-
import {
|
|
3
|
+
import { executeNoteAction } from '../lib/note-actions.js';
|
|
4
4
|
import { hasFlag, readFlag } from '../utils/args.js';
|
|
5
5
|
const actions = new Set([
|
|
6
6
|
'create',
|
|
@@ -18,21 +18,21 @@ const sortValues = new Set(['updatedAt', 'createdAt', 'title']);
|
|
|
18
18
|
function usage(action) {
|
|
19
19
|
switch (action) {
|
|
20
20
|
case 'create':
|
|
21
|
-
return 'Usage: kiipu
|
|
21
|
+
return 'Usage: kiipu note create --content "<text>" [--title "<title>"]\n or: kiipu note create "<text>" [--title "<title>"]';
|
|
22
22
|
case 'list':
|
|
23
|
-
return 'Usage: kiipu
|
|
23
|
+
return 'Usage: kiipu note list [--tag <tag>] [--sort <updatedAt|createdAt|title>] [--starred] [--deleted]';
|
|
24
24
|
case 'search':
|
|
25
|
-
return 'Usage: kiipu
|
|
25
|
+
return 'Usage: kiipu note search <query>\n or: kiipu note search --query "<query>"';
|
|
26
26
|
case 'show':
|
|
27
|
-
return 'Usage: kiipu
|
|
27
|
+
return 'Usage: kiipu note show --id <noteId>';
|
|
28
28
|
case 'update':
|
|
29
|
-
return 'Usage: kiipu
|
|
29
|
+
return 'Usage: kiipu note update --id <noteId> [--content "<text>" [--title "<title>"]] [--visibility public|private]';
|
|
30
30
|
case 'star':
|
|
31
|
-
return 'Usage: kiipu
|
|
31
|
+
return 'Usage: kiipu note star --id <noteId>';
|
|
32
32
|
case 'pin':
|
|
33
|
-
return 'Usage: kiipu
|
|
33
|
+
return 'Usage: kiipu note pin --id <noteId>';
|
|
34
34
|
default:
|
|
35
|
-
return `Usage: kiipu
|
|
35
|
+
return `Usage: kiipu note ${action} --id <noteId>`;
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
function error(message) {
|
|
@@ -81,58 +81,39 @@ function validateSort(sort) {
|
|
|
81
81
|
}
|
|
82
82
|
return sortValues.has(sort) ? sort : null;
|
|
83
83
|
}
|
|
84
|
-
function
|
|
85
|
-
const
|
|
86
|
-
return
|
|
84
|
+
function parseNoteId(args, action) {
|
|
85
|
+
const noteId = readFlag(args, '--id')?.trim();
|
|
86
|
+
return noteId ? noteId : error(usage(action));
|
|
87
87
|
}
|
|
88
|
-
function
|
|
89
|
-
if (!input) {
|
|
90
|
-
return undefined;
|
|
91
|
-
}
|
|
92
|
-
const unique = new Map();
|
|
93
|
-
for (const chunk of input.split(',')) {
|
|
94
|
-
const cleaned = chunk.trim().replace(/^#+/, '').replace(/\s+/g, ' ');
|
|
95
|
-
if (!cleaned) {
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
const normalized = cleaned.toLowerCase();
|
|
99
|
-
if (!unique.has(normalized)) {
|
|
100
|
-
unique.set(normalized, cleaned);
|
|
101
|
-
}
|
|
102
|
-
if (unique.size === 8) {
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return Array.from(unique.values());
|
|
107
|
-
}
|
|
108
|
-
function toCliPost(post) {
|
|
88
|
+
function toCliNote(note) {
|
|
109
89
|
return {
|
|
110
|
-
id:
|
|
111
|
-
title:
|
|
112
|
-
rawText:
|
|
113
|
-
finalText:
|
|
114
|
-
visibility:
|
|
115
|
-
tags:
|
|
116
|
-
folder:
|
|
117
|
-
isPinned:
|
|
118
|
-
isStarred:
|
|
119
|
-
createdAt:
|
|
120
|
-
updatedAt:
|
|
90
|
+
id: note.id,
|
|
91
|
+
title: note.title,
|
|
92
|
+
rawText: note.rawText,
|
|
93
|
+
finalText: note.finalText,
|
|
94
|
+
visibility: note.visibility,
|
|
95
|
+
tags: note.tags,
|
|
96
|
+
folder: note.folder ?? null,
|
|
97
|
+
isPinned: note.isPinned,
|
|
98
|
+
isStarred: note.isStarred,
|
|
99
|
+
createdAt: note.createdAt,
|
|
100
|
+
updatedAt: note.updatedAt,
|
|
121
101
|
};
|
|
122
102
|
}
|
|
123
103
|
async function handleMutationAction(config, action, args) {
|
|
124
104
|
if (action === 'create') {
|
|
125
|
-
const content = (readFlag(args, '--content') ?? stripKnownFlags(args, ['--content'])[0])?.trim();
|
|
105
|
+
const content = (readFlag(args, '--content') ?? stripKnownFlags(args, ['--content', '--title'])[0])?.trim();
|
|
126
106
|
if (!content) {
|
|
127
107
|
return error(usage('create'));
|
|
128
108
|
}
|
|
129
|
-
|
|
109
|
+
const title = readFlag(args, '--title')?.trim();
|
|
110
|
+
return executeNoteAction(config, { action: 'create', content, ...(title ? { title } : {}) });
|
|
130
111
|
}
|
|
131
|
-
const
|
|
132
|
-
if (typeof
|
|
133
|
-
return
|
|
112
|
+
const noteId = parseNoteId(args, action);
|
|
113
|
+
if (typeof noteId !== 'string') {
|
|
114
|
+
return noteId;
|
|
134
115
|
}
|
|
135
|
-
return
|
|
116
|
+
return executeNoteAction(config, { action, noteId });
|
|
136
117
|
}
|
|
137
118
|
async function handleList(config, args) {
|
|
138
119
|
const { client, error: clientError } = requireUserClient(config);
|
|
@@ -149,36 +130,36 @@ async function handleList(config, args) {
|
|
|
149
130
|
return error(`--starred and --deleted cannot be used together.\n${usage('list')}`);
|
|
150
131
|
}
|
|
151
132
|
const tag = readFlag(args, '--tag');
|
|
152
|
-
let
|
|
133
|
+
let notes;
|
|
153
134
|
let responseData;
|
|
154
135
|
if (starred) {
|
|
155
|
-
const response = await client.
|
|
136
|
+
const response = await client.listStarredNotes({ tag, sort });
|
|
156
137
|
if (!response.ok) {
|
|
157
138
|
return error(response.error.message);
|
|
158
139
|
}
|
|
159
|
-
|
|
140
|
+
notes = response.data.map((entry) => toCliNote(entry.note));
|
|
160
141
|
responseData = response.data;
|
|
161
142
|
}
|
|
162
143
|
else if (deleted) {
|
|
163
|
-
const response = await client.
|
|
144
|
+
const response = await client.listDeletedNotes({ sort });
|
|
164
145
|
if (!response.ok) {
|
|
165
146
|
return error(response.error.message);
|
|
166
147
|
}
|
|
167
|
-
|
|
148
|
+
notes = response.data.map(toCliNote);
|
|
168
149
|
responseData = response.data;
|
|
169
150
|
}
|
|
170
151
|
else {
|
|
171
|
-
const response = await client.
|
|
152
|
+
const response = await client.listNotes({ tag, sort });
|
|
172
153
|
if (!response.ok) {
|
|
173
154
|
return error(response.error.message);
|
|
174
155
|
}
|
|
175
|
-
|
|
156
|
+
notes = response.data.map(toCliNote);
|
|
176
157
|
responseData = response.data;
|
|
177
158
|
}
|
|
178
|
-
const title = starred ? 'Starred
|
|
159
|
+
const title = starred ? 'Starred notes' : deleted ? 'Deleted notes' : 'Notes';
|
|
179
160
|
return {
|
|
180
161
|
ok: true,
|
|
181
|
-
message:
|
|
162
|
+
message: formatNoteCollection(title, notes),
|
|
182
163
|
data: responseData,
|
|
183
164
|
};
|
|
184
165
|
}
|
|
@@ -191,13 +172,13 @@ async function handleSearch(config, args) {
|
|
|
191
172
|
if (!query) {
|
|
192
173
|
return error(usage('search'));
|
|
193
174
|
}
|
|
194
|
-
const response = await client.
|
|
175
|
+
const response = await client.searchNotes(query);
|
|
195
176
|
if (!response.ok) {
|
|
196
177
|
return error(response.error.message);
|
|
197
178
|
}
|
|
198
179
|
return {
|
|
199
180
|
ok: true,
|
|
200
|
-
message:
|
|
181
|
+
message: formatNoteCollection(`Search results for "${query}"`, response.data.map(toCliNote)),
|
|
201
182
|
data: response.data,
|
|
202
183
|
};
|
|
203
184
|
}
|
|
@@ -206,17 +187,17 @@ async function handleShow(config, args) {
|
|
|
206
187
|
if (!client) {
|
|
207
188
|
return clientError;
|
|
208
189
|
}
|
|
209
|
-
const
|
|
210
|
-
if (typeof
|
|
211
|
-
return
|
|
190
|
+
const noteId = parseNoteId(args, 'show');
|
|
191
|
+
if (typeof noteId !== 'string') {
|
|
192
|
+
return noteId;
|
|
212
193
|
}
|
|
213
|
-
const response = await client.
|
|
194
|
+
const response = await client.getNote(noteId);
|
|
214
195
|
if (!response.ok) {
|
|
215
196
|
return error(response.error.message);
|
|
216
197
|
}
|
|
217
198
|
return {
|
|
218
199
|
ok: true,
|
|
219
|
-
message:
|
|
200
|
+
message: formatNoteDetail(toCliNote(response.data)),
|
|
220
201
|
data: response.data,
|
|
221
202
|
};
|
|
222
203
|
}
|
|
@@ -225,33 +206,53 @@ async function handleUpdate(config, args) {
|
|
|
225
206
|
if (!client) {
|
|
226
207
|
return clientError;
|
|
227
208
|
}
|
|
228
|
-
const
|
|
229
|
-
if (typeof
|
|
230
|
-
return
|
|
209
|
+
const noteId = parseNoteId(args, 'update');
|
|
210
|
+
if (typeof noteId !== 'string') {
|
|
211
|
+
return noteId;
|
|
231
212
|
}
|
|
232
213
|
const content = readFlag(args, '--content')?.trim();
|
|
233
|
-
if (!content) {
|
|
234
|
-
return error(usage('update'));
|
|
235
|
-
}
|
|
236
214
|
const visibility = readFlag(args, '--visibility');
|
|
237
215
|
if (visibility && visibility !== 'public' && visibility !== 'private') {
|
|
238
216
|
return error(`Invalid --visibility value. ${usage('update')}`);
|
|
239
217
|
}
|
|
240
218
|
const title = readFlag(args, '--title');
|
|
241
219
|
const tags = readFlag(args, '--tags');
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
if (!
|
|
249
|
-
return error(
|
|
220
|
+
if (tags !== undefined) {
|
|
221
|
+
return error('Tags are now derived from note content. Add #tag directly in --content.');
|
|
222
|
+
}
|
|
223
|
+
if (!content && title !== undefined) {
|
|
224
|
+
return error(`--title requires --content. ${usage('update')}`);
|
|
225
|
+
}
|
|
226
|
+
if (!content && !visibility) {
|
|
227
|
+
return error(usage('update'));
|
|
228
|
+
}
|
|
229
|
+
let updatedNote;
|
|
230
|
+
if (content) {
|
|
231
|
+
const response = await client.updateNote(noteId, {
|
|
232
|
+
rawText: content,
|
|
233
|
+
...(title !== undefined ? { title: title || null } : {}),
|
|
234
|
+
});
|
|
235
|
+
if (!response.ok) {
|
|
236
|
+
return error(response.error.message);
|
|
237
|
+
}
|
|
238
|
+
updatedNote = response.data;
|
|
239
|
+
}
|
|
240
|
+
if (visibility === 'public' || visibility === 'private') {
|
|
241
|
+
const response = await client.updateNoteMetadata(noteId, {
|
|
242
|
+
visibility,
|
|
243
|
+
});
|
|
244
|
+
if (!response.ok) {
|
|
245
|
+
return error(response.error.message);
|
|
246
|
+
}
|
|
247
|
+
updatedNote = response.data;
|
|
248
|
+
}
|
|
249
|
+
if (!updatedNote) {
|
|
250
|
+
return error(usage('update'));
|
|
250
251
|
}
|
|
251
252
|
return {
|
|
252
253
|
ok: true,
|
|
253
|
-
message: `
|
|
254
|
-
data:
|
|
254
|
+
message: `Note updated.\n\n${formatNoteDetail(toCliNote(updatedNote))}`,
|
|
255
|
+
data: updatedNote,
|
|
255
256
|
};
|
|
256
257
|
}
|
|
257
258
|
async function handleStar(config, args) {
|
|
@@ -259,17 +260,19 @@ async function handleStar(config, args) {
|
|
|
259
260
|
if (!client) {
|
|
260
261
|
return clientError;
|
|
261
262
|
}
|
|
262
|
-
const
|
|
263
|
-
if (typeof
|
|
264
|
-
return
|
|
263
|
+
const noteId = parseNoteId(args, 'star');
|
|
264
|
+
if (typeof noteId !== 'string') {
|
|
265
|
+
return noteId;
|
|
265
266
|
}
|
|
266
|
-
const response = await client.toggleStar(
|
|
267
|
+
const response = await client.toggleStar(noteId);
|
|
267
268
|
if (!response.ok) {
|
|
268
269
|
return error(response.error.message);
|
|
269
270
|
}
|
|
270
271
|
return {
|
|
271
272
|
ok: true,
|
|
272
|
-
message: response.data.isStarred
|
|
273
|
+
message: response.data.isStarred
|
|
274
|
+
? `Note starred. ${response.data.id}`
|
|
275
|
+
: `Note unstarred. ${response.data.id}`,
|
|
273
276
|
data: response.data,
|
|
274
277
|
};
|
|
275
278
|
}
|
|
@@ -278,24 +281,26 @@ async function handlePin(config, args) {
|
|
|
278
281
|
if (!client) {
|
|
279
282
|
return clientError;
|
|
280
283
|
}
|
|
281
|
-
const
|
|
282
|
-
if (typeof
|
|
283
|
-
return
|
|
284
|
+
const noteId = parseNoteId(args, 'pin');
|
|
285
|
+
if (typeof noteId !== 'string') {
|
|
286
|
+
return noteId;
|
|
284
287
|
}
|
|
285
|
-
const response = await client.togglePin(
|
|
288
|
+
const response = await client.togglePin(noteId);
|
|
286
289
|
if (!response.ok) {
|
|
287
290
|
return error(response.error.message);
|
|
288
291
|
}
|
|
289
292
|
return {
|
|
290
293
|
ok: true,
|
|
291
|
-
message: response.data.isPinned
|
|
294
|
+
message: response.data.isPinned
|
|
295
|
+
? `Note pinned. ${response.data.id}`
|
|
296
|
+
: `Note unpinned. ${response.data.id}`,
|
|
292
297
|
data: response.data,
|
|
293
298
|
};
|
|
294
299
|
}
|
|
295
|
-
export async function
|
|
300
|
+
export async function runNoteCommand(config, args) {
|
|
296
301
|
const action = args[1];
|
|
297
302
|
if (!action || !actions.has(action)) {
|
|
298
|
-
return error('Usage: kiipu
|
|
303
|
+
return error('Usage: kiipu note <create|delete|restore|purge|list|search|show|update|star|pin> [options]');
|
|
299
304
|
}
|
|
300
305
|
const actionArgs = args.slice(2);
|
|
301
306
|
switch (action) {
|
|
@@ -317,5 +322,5 @@ export async function runPostCommand(config, args) {
|
|
|
317
322
|
case 'pin':
|
|
318
323
|
return handlePin(config, actionArgs);
|
|
319
324
|
}
|
|
320
|
-
return error('Unsupported
|
|
325
|
+
return error('Unsupported note action.');
|
|
321
326
|
}
|
package/dist/config/load-env.js
CHANGED
|
@@ -13,7 +13,10 @@ function parseEnvFile(content) {
|
|
|
13
13
|
if (!key || process.env[key] !== undefined) {
|
|
14
14
|
continue;
|
|
15
15
|
}
|
|
16
|
-
const value = line
|
|
16
|
+
const value = line
|
|
17
|
+
.slice(separatorIndex + 1)
|
|
18
|
+
.trim()
|
|
19
|
+
.replace(/^['"]|['"]$/g, '');
|
|
17
20
|
process.env[key] = value;
|
|
18
21
|
}
|
|
19
22
|
}
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { runAskCommand } from './commands/ask.js';
|
|
|
5
5
|
import { runAuthCommand } from './commands/auth.js';
|
|
6
6
|
import { runDoctorCommand } from './commands/doctor.js';
|
|
7
7
|
import { getHelpResult } from './commands/help.js';
|
|
8
|
-
import {
|
|
8
|
+
import { runNoteCommand } from './commands/note.js';
|
|
9
9
|
import { runSkillsCommand } from './commands/skills.js';
|
|
10
10
|
import { readFlag, hasFlag } from './utils/args.js';
|
|
11
11
|
import { CLI_VERSION } from './version.js';
|
|
@@ -86,8 +86,8 @@ async function main() {
|
|
|
86
86
|
result = await runDoctorCommand(await loadKiipuConfig());
|
|
87
87
|
return printResult(result, asJson);
|
|
88
88
|
}
|
|
89
|
-
if (command === '
|
|
90
|
-
result = await
|
|
89
|
+
if (command === 'note') {
|
|
90
|
+
result = await runNoteCommand(config, commandArgs);
|
|
91
91
|
return printResult(result, asJson);
|
|
92
92
|
}
|
|
93
93
|
if (command === 'ask') {
|
package/dist/lib/ask-client.js
CHANGED
|
@@ -45,14 +45,14 @@ function parseSource(input) {
|
|
|
45
45
|
if (!isRecord(input)) {
|
|
46
46
|
return null;
|
|
47
47
|
}
|
|
48
|
-
const
|
|
48
|
+
const noteId = typeof input.noteId === 'string' ? input.noteId : '';
|
|
49
49
|
const snippet = typeof input.snippet === 'string' ? input.snippet : '';
|
|
50
|
-
if (!
|
|
50
|
+
if (!noteId || !snippet) {
|
|
51
51
|
return null;
|
|
52
52
|
}
|
|
53
53
|
return {
|
|
54
54
|
index: typeof input.index === 'number' ? input.index : 0,
|
|
55
|
-
|
|
55
|
+
noteId,
|
|
56
56
|
title: typeof input.title === 'string' ? input.title : null,
|
|
57
57
|
snippet,
|
|
58
58
|
score: typeof input.score === 'number' ? input.score : 0,
|
|
@@ -60,7 +60,9 @@ function parseSource(input) {
|
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
function parseSources(input) {
|
|
63
|
-
return Array.isArray(input)
|
|
63
|
+
return Array.isArray(input)
|
|
64
|
+
? input.map(parseSource).filter((source) => Boolean(source))
|
|
65
|
+
: [];
|
|
64
66
|
}
|
|
65
67
|
function parseAskEvent(input) {
|
|
66
68
|
if (!isRecord(input) || typeof input.type !== 'string') {
|
|
@@ -24,7 +24,7 @@ function truncate(value, maxLength) {
|
|
|
24
24
|
function formatSourceLine(source) {
|
|
25
25
|
const title = source.title?.trim() || '(untitled)';
|
|
26
26
|
const score = Number.isFinite(source.score) ? ` score ${source.score.toFixed(3)}` : '';
|
|
27
|
-
return `[${source.index}] ${title} (${source.
|
|
27
|
+
return `[${source.index}] ${title} (${source.noteId})${score}`;
|
|
28
28
|
}
|
|
29
29
|
export function formatAskFooter(input) {
|
|
30
30
|
const lines = ['', 'Sources:'];
|
|
@@ -9,40 +9,46 @@ function buildError(requestId, message, code = 'request_failed') {
|
|
|
9
9
|
},
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
|
-
function
|
|
12
|
+
function parseCreateNoteRequest(input) {
|
|
13
13
|
const requestId = typeof input.requestId === 'string' ? input.requestId : randomUUID();
|
|
14
14
|
const rawText = typeof input.rawText === 'string' ? input.rawText.trim() : '';
|
|
15
15
|
if (!rawText) {
|
|
16
16
|
throw new Error('rawText is required.');
|
|
17
17
|
}
|
|
18
18
|
const visibility = input.visibility;
|
|
19
|
-
const tags = Array.isArray(input.tags)
|
|
19
|
+
const tags = Array.isArray(input.tags)
|
|
20
|
+
? input.tags.filter((tag) => typeof tag === 'string')
|
|
21
|
+
: [];
|
|
20
22
|
return {
|
|
21
23
|
requestId,
|
|
22
24
|
requestedAt: typeof input.requestedAt === 'string' ? input.requestedAt : undefined,
|
|
23
25
|
traceId: typeof input.traceId === 'string' ? input.traceId : undefined,
|
|
24
26
|
rawText,
|
|
25
|
-
sourceType: input.sourceType === 'manual' ||
|
|
27
|
+
sourceType: input.sourceType === 'manual' ||
|
|
28
|
+
input.sourceType === 'imported' ||
|
|
29
|
+
input.sourceType === 'skill_command'
|
|
26
30
|
? input.sourceType
|
|
27
31
|
: 'skill_command',
|
|
28
32
|
finalText: typeof input.finalText === 'string' ? input.finalText : undefined,
|
|
29
|
-
visibility: visibility === 'unlisted' || visibility === 'private' || visibility === 'public'
|
|
33
|
+
visibility: visibility === 'unlisted' || visibility === 'private' || visibility === 'public'
|
|
34
|
+
? visibility
|
|
35
|
+
: 'public',
|
|
30
36
|
sourceMessageId: typeof input.sourceMessageId === 'string' ? input.sourceMessageId : undefined,
|
|
31
37
|
title: typeof input.title === 'string' ? input.title : input.title === null ? null : undefined,
|
|
32
38
|
tags,
|
|
33
39
|
};
|
|
34
40
|
}
|
|
35
|
-
function
|
|
41
|
+
function parseNoteMutationRequest(input) {
|
|
36
42
|
const requestId = typeof input.requestId === 'string' ? input.requestId : randomUUID();
|
|
37
|
-
const
|
|
38
|
-
if (!
|
|
39
|
-
throw new Error('
|
|
43
|
+
const noteId = typeof input.noteId === 'string' ? input.noteId.trim() : '';
|
|
44
|
+
if (!noteId) {
|
|
45
|
+
throw new Error('noteId is required.');
|
|
40
46
|
}
|
|
41
47
|
return {
|
|
42
48
|
requestId,
|
|
43
49
|
requestedAt: typeof input.requestedAt === 'string' ? input.requestedAt : undefined,
|
|
44
50
|
traceId: typeof input.traceId === 'string' ? input.traceId : undefined,
|
|
45
|
-
|
|
51
|
+
noteId,
|
|
46
52
|
};
|
|
47
53
|
}
|
|
48
54
|
export class KiipuIntegrationApiClient {
|
|
@@ -87,19 +93,19 @@ export class KiipuIntegrationApiClient {
|
|
|
87
93
|
? payload.code
|
|
88
94
|
: 'request_failed');
|
|
89
95
|
}
|
|
90
|
-
|
|
91
|
-
return this.request('/integrations/
|
|
96
|
+
createNote(input) {
|
|
97
|
+
return this.request('/integrations/notes', 'POST', parseCreateNoteRequest(input));
|
|
92
98
|
}
|
|
93
|
-
|
|
94
|
-
const body =
|
|
95
|
-
return this.request(`/integrations/
|
|
99
|
+
deleteNote(input) {
|
|
100
|
+
const body = parseNoteMutationRequest(input);
|
|
101
|
+
return this.request(`/integrations/notes/${body.noteId}/delete`, 'POST', body);
|
|
96
102
|
}
|
|
97
|
-
|
|
98
|
-
const body =
|
|
99
|
-
return this.request(`/integrations/
|
|
103
|
+
restoreNote(input) {
|
|
104
|
+
const body = parseNoteMutationRequest(input);
|
|
105
|
+
return this.request(`/integrations/notes/${body.noteId}/restore`, 'POST', body);
|
|
100
106
|
}
|
|
101
|
-
|
|
102
|
-
const body =
|
|
103
|
-
return this.request(`/integrations/
|
|
107
|
+
permanentDeleteNote(input) {
|
|
108
|
+
const body = parseNoteMutationRequest(input);
|
|
109
|
+
return this.request(`/integrations/notes/${body.noteId}/permanent-delete`, 'POST', body);
|
|
104
110
|
}
|
|
105
111
|
}
|
|
@@ -72,34 +72,40 @@ export class KiipuUserApiClient {
|
|
|
72
72
|
body: JSON.stringify(input),
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
|
-
|
|
76
|
-
return this.request(this.buildPath('/
|
|
75
|
+
listNotes(input) {
|
|
76
|
+
return this.request(this.buildPath('/notes/me', input));
|
|
77
77
|
}
|
|
78
|
-
|
|
79
|
-
return this.request(this.buildPath('/
|
|
78
|
+
searchNotes(query) {
|
|
79
|
+
return this.request(this.buildPath('/notes/me/search', { q: query }));
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
return this.request(this.buildPath('/
|
|
81
|
+
listStarredNotes(input) {
|
|
82
|
+
return this.request(this.buildPath('/notes/me/starred', input));
|
|
83
83
|
}
|
|
84
|
-
|
|
85
|
-
return this.request(this.buildPath('/
|
|
84
|
+
listDeletedNotes(input) {
|
|
85
|
+
return this.request(this.buildPath('/notes/me/deleted', input));
|
|
86
86
|
}
|
|
87
|
-
|
|
88
|
-
return this.request(`/
|
|
87
|
+
getNote(id) {
|
|
88
|
+
return this.request(`/notes/${id}`);
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
return this.request(`/
|
|
90
|
+
updateNote(id, input) {
|
|
91
|
+
return this.request(`/notes/${id}/content`, {
|
|
92
|
+
method: 'PATCH',
|
|
93
|
+
body: JSON.stringify(input),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
updateNoteMetadata(id, input) {
|
|
97
|
+
return this.request(`/notes/${id}/metadata`, {
|
|
92
98
|
method: 'PATCH',
|
|
93
99
|
body: JSON.stringify(input),
|
|
94
100
|
});
|
|
95
101
|
}
|
|
96
102
|
toggleStar(id) {
|
|
97
|
-
return this.request(`/
|
|
103
|
+
return this.request(`/notes/${id}/star`, {
|
|
98
104
|
method: 'PATCH',
|
|
99
105
|
});
|
|
100
106
|
}
|
|
101
107
|
togglePin(id) {
|
|
102
|
-
return this.request(`/
|
|
108
|
+
return this.request(`/notes/${id}/pin`, {
|
|
103
109
|
method: 'PATCH',
|
|
104
110
|
});
|
|
105
111
|
}
|
|
@@ -3,7 +3,7 @@ import { KiipuIntegrationApiClient } from './kiipu-integration-client.js';
|
|
|
3
3
|
function formatRequestFailed(message, code) {
|
|
4
4
|
return `Request failed: ${message} (${code}).`;
|
|
5
5
|
}
|
|
6
|
-
function
|
|
6
|
+
function getNoteApiClient(config) {
|
|
7
7
|
const apiKey = config.apiKey ?? process.env.KIIPU_API_KEY ?? '';
|
|
8
8
|
if (!apiKey) {
|
|
9
9
|
return {
|
|
@@ -20,21 +20,21 @@ function getPostApiClient(config) {
|
|
|
20
20
|
}),
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
export async function
|
|
24
|
-
const { client, error } =
|
|
23
|
+
export async function executeNoteAction(config, input) {
|
|
24
|
+
const { client, error } = getNoteApiClient(config);
|
|
25
25
|
if (!client) {
|
|
26
26
|
return error;
|
|
27
27
|
}
|
|
28
28
|
if (input.action === 'create') {
|
|
29
|
-
const response = await client.
|
|
29
|
+
const response = await client.createNote({
|
|
30
30
|
requestId: randomUUID(),
|
|
31
31
|
requestedAt: new Date().toISOString(),
|
|
32
|
-
traceId: `${input.traceIdPrefix ?? '
|
|
32
|
+
traceId: `${input.traceIdPrefix ?? 'note'}-${Date.now()}`,
|
|
33
33
|
rawText: input.content,
|
|
34
|
+
...(input.title ? { title: input.title } : {}),
|
|
34
35
|
sourceType: 'skill_command',
|
|
35
36
|
sourceMessageId: input.sourceMessageId ?? `local-${Date.now()}`,
|
|
36
37
|
visibility: 'public',
|
|
37
|
-
tags: ['kiipu'],
|
|
38
38
|
});
|
|
39
39
|
if (!response.ok) {
|
|
40
40
|
return {
|
|
@@ -44,18 +44,18 @@ export async function executePostAction(config, input) {
|
|
|
44
44
|
}
|
|
45
45
|
return {
|
|
46
46
|
ok: true,
|
|
47
|
-
message: `
|
|
47
|
+
message: `Note created. Note id ${String(response.data.id)} is now visible in the feed.`,
|
|
48
48
|
data: response.data,
|
|
49
49
|
requestId: response.requestId,
|
|
50
|
-
|
|
50
|
+
noteId: String(response.data.id),
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
if (input.action === 'delete') {
|
|
54
|
-
const response = await client.
|
|
54
|
+
const response = await client.deleteNote({
|
|
55
55
|
requestId: randomUUID(),
|
|
56
56
|
requestedAt: new Date().toISOString(),
|
|
57
57
|
traceId: `${input.traceIdPrefix ?? 'delete'}-${Date.now()}`,
|
|
58
|
-
|
|
58
|
+
noteId: input.noteId,
|
|
59
59
|
});
|
|
60
60
|
if (!response.ok) {
|
|
61
61
|
return {
|
|
@@ -65,18 +65,18 @@ export async function executePostAction(config, input) {
|
|
|
65
65
|
}
|
|
66
66
|
return {
|
|
67
67
|
ok: true,
|
|
68
|
-
message: '
|
|
68
|
+
message: 'Note deleted. The current note is no longer visible in the feed.',
|
|
69
69
|
data: response.data,
|
|
70
70
|
requestId: response.requestId,
|
|
71
|
-
|
|
71
|
+
noteId: null,
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
if (input.action === 'restore') {
|
|
75
|
-
const response = await client.
|
|
75
|
+
const response = await client.restoreNote({
|
|
76
76
|
requestId: randomUUID(),
|
|
77
77
|
requestedAt: new Date().toISOString(),
|
|
78
78
|
traceId: `${input.traceIdPrefix ?? 'restore'}-${Date.now()}`,
|
|
79
|
-
|
|
79
|
+
noteId: input.noteId,
|
|
80
80
|
});
|
|
81
81
|
if (!response.ok) {
|
|
82
82
|
return {
|
|
@@ -86,17 +86,17 @@ export async function executePostAction(config, input) {
|
|
|
86
86
|
}
|
|
87
87
|
return {
|
|
88
88
|
ok: true,
|
|
89
|
-
message: `
|
|
89
|
+
message: `Note restored. Note id ${input.noteId} is now back in the feed.`,
|
|
90
90
|
data: response.data,
|
|
91
91
|
requestId: response.requestId,
|
|
92
|
-
|
|
92
|
+
noteId: input.noteId,
|
|
93
93
|
};
|
|
94
94
|
}
|
|
95
|
-
const response = await client.
|
|
95
|
+
const response = await client.permanentDeleteNote({
|
|
96
96
|
requestId: randomUUID(),
|
|
97
97
|
requestedAt: new Date().toISOString(),
|
|
98
98
|
traceId: `${input.traceIdPrefix ?? 'purge'}-${Date.now()}`,
|
|
99
|
-
|
|
99
|
+
noteId: input.noteId,
|
|
100
100
|
});
|
|
101
101
|
if (!response.ok) {
|
|
102
102
|
return {
|
|
@@ -106,9 +106,9 @@ export async function executePostAction(config, input) {
|
|
|
106
106
|
}
|
|
107
107
|
return {
|
|
108
108
|
ok: true,
|
|
109
|
-
message: `
|
|
109
|
+
message: `Note permanently deleted. Note id ${input.noteId} has been removed from the database.`,
|
|
110
110
|
data: response.data,
|
|
111
111
|
requestId: response.requestId,
|
|
112
|
-
|
|
112
|
+
noteId: null,
|
|
113
113
|
};
|
|
114
114
|
}
|
|
@@ -23,52 +23,55 @@ function formatTags(tags, limit) {
|
|
|
23
23
|
: [];
|
|
24
24
|
return normalized.length > 0 ? normalized.map((tag) => `#${tag}`).join(', ') : 'none';
|
|
25
25
|
}
|
|
26
|
-
function
|
|
27
|
-
const content = (
|
|
26
|
+
function getNotePreview(note) {
|
|
27
|
+
const content = (note.title?.trim() ||
|
|
28
|
+
note.finalText?.trim() ||
|
|
29
|
+
note.rawText?.trim() ||
|
|
30
|
+
'').replace(/\s+/g, ' ');
|
|
28
31
|
if (!content) {
|
|
29
32
|
return '(empty)';
|
|
30
33
|
}
|
|
31
34
|
return content.length > 100 ? `${content.slice(0, 97)}...` : content;
|
|
32
35
|
}
|
|
33
|
-
function getStatusFlags(
|
|
36
|
+
function getStatusFlags(note) {
|
|
34
37
|
const flags = [];
|
|
35
|
-
if (
|
|
38
|
+
if (note.isPinned) {
|
|
36
39
|
flags.push('pinned');
|
|
37
40
|
}
|
|
38
|
-
if (
|
|
41
|
+
if (note.isStarred) {
|
|
39
42
|
flags.push('starred');
|
|
40
43
|
}
|
|
41
44
|
return flags.length > 0 ? flags.join(', ') : 'none';
|
|
42
45
|
}
|
|
43
|
-
export function
|
|
46
|
+
export function formatNoteCollection(title, notes) {
|
|
44
47
|
const lines = [title, ''];
|
|
45
|
-
if (
|
|
46
|
-
lines.push('No
|
|
48
|
+
if (notes.length === 0) {
|
|
49
|
+
lines.push('No notes found.');
|
|
47
50
|
return lines.join('\n');
|
|
48
51
|
}
|
|
49
|
-
for (const
|
|
50
|
-
lines.push(`${
|
|
51
|
-
lines.push(` flags: ${getStatusFlags(
|
|
52
|
-
lines.push(` ${
|
|
52
|
+
for (const note of notes) {
|
|
53
|
+
lines.push(`${note.id} ${formatTimestamp(note.updatedAt ?? note.createdAt)}`);
|
|
54
|
+
lines.push(` flags: ${getStatusFlags(note)} visibility: ${note.visibility} tags: ${formatTags(note.tags, 3)}`);
|
|
55
|
+
lines.push(` ${getNotePreview(note)}`);
|
|
53
56
|
lines.push('');
|
|
54
57
|
}
|
|
55
58
|
return lines.slice(0, -1).join('\n');
|
|
56
59
|
}
|
|
57
|
-
export function
|
|
58
|
-
const title =
|
|
59
|
-
const body =
|
|
60
|
+
export function formatNoteDetail(note) {
|
|
61
|
+
const title = note.title?.trim() || '(untitled)';
|
|
62
|
+
const body = note.finalText?.trim() || note.rawText?.trim() || '(empty)';
|
|
60
63
|
return [
|
|
61
64
|
title,
|
|
62
65
|
'',
|
|
63
66
|
body,
|
|
64
67
|
'',
|
|
65
|
-
`id: ${
|
|
66
|
-
`visibility: ${
|
|
67
|
-
`created: ${formatTimestamp(
|
|
68
|
-
`updated: ${formatTimestamp(
|
|
69
|
-
`tags: ${formatTags(
|
|
70
|
-
`folder: ${
|
|
71
|
-
`pinned: ${
|
|
72
|
-
`starred: ${
|
|
68
|
+
`id: ${note.id}`,
|
|
69
|
+
`visibility: ${note.visibility}`,
|
|
70
|
+
`created: ${formatTimestamp(note.createdAt)}`,
|
|
71
|
+
`updated: ${formatTimestamp(note.updatedAt)}`,
|
|
72
|
+
`tags: ${formatTags(note.tags)}`,
|
|
73
|
+
`folder: ${note.folder ? `${note.folder.name} (${note.folder.id})` : 'none'}`,
|
|
74
|
+
`pinned: ${note.isPinned ? 'yes' : 'no'}`,
|
|
75
|
+
`starred: ${note.isStarred ? 'yes' : 'no'}`,
|
|
73
76
|
].join('\n');
|
|
74
77
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kiipu/cli",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Kiipu CLI for local authentication, doctor checks, and direct
|
|
3
|
+
"version": "0.0.10",
|
|
4
|
+
"description": "Kiipu CLI for local authentication, doctor checks, and direct note actions.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|