kontexted 0.1.5 → 0.1.6

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.
@@ -45,6 +45,54 @@ async function executeNoteById(client, workspaceSlug, notePublicId) {
45
45
  }
46
46
  return response.json();
47
47
  }
48
+ /**
49
+ * Execute create-folder skill via the API
50
+ */
51
+ async function executeCreateFolder(client, workspaceSlug, name, displayName, parentPublicId) {
52
+ const body = { workspaceSlug, name, displayName };
53
+ if (parentPublicId) {
54
+ body.parentPublicId = parentPublicId;
55
+ }
56
+ const response = await client.post("/api/skill/create-folder", body);
57
+ if (!response.ok) {
58
+ const errorText = await response.text();
59
+ throw new Error(`Create folder skill failed: ${response.status} ${errorText}`);
60
+ }
61
+ return response.json();
62
+ }
63
+ /**
64
+ * Execute create-note skill via the API
65
+ */
66
+ async function executeCreateNote(client, workspaceSlug, name, title, folderPublicId, content) {
67
+ const body = { workspaceSlug, name, title };
68
+ if (folderPublicId) {
69
+ body.folderPublicId = folderPublicId;
70
+ }
71
+ if (content !== undefined) {
72
+ body.content = content;
73
+ }
74
+ const response = await client.post("/api/skill/create-note", body);
75
+ if (!response.ok) {
76
+ const errorText = await response.text();
77
+ throw new Error(`Create note skill failed: ${response.status} ${errorText}`);
78
+ }
79
+ return response.json();
80
+ }
81
+ /**
82
+ * Execute update-note-content skill via the API
83
+ */
84
+ async function executeUpdateNoteContent(client, workspaceSlug, notePublicId, content) {
85
+ const response = await client.post("/api/skill/update-note-content", {
86
+ workspaceSlug,
87
+ notePublicId,
88
+ content,
89
+ });
90
+ if (!response.ok) {
91
+ const errorText = await response.text();
92
+ throw new Error(`Update note content skill failed: ${response.status} ${errorText}`);
93
+ }
94
+ return response.json();
95
+ }
48
96
  /**
49
97
  * Helper function to create an API client from a profile alias
50
98
  */
@@ -142,6 +190,90 @@ export function registerSkillCommand(program) {
142
190
  process.exit(1);
143
191
  }
144
192
  });
193
+ skillCommand
194
+ .command("create-folder")
195
+ .description("Create a new folder in the workspace")
196
+ .requiredOption("--alias <name>", "Profile alias to use")
197
+ .requiredOption("--name <name>", "URL-safe folder name")
198
+ .requiredOption("--display-name <displayName>", "Human-readable display name")
199
+ .option("--parent-id <parentPublicId>", "Public ID of parent folder (for nested folders)")
200
+ .action(async (options) => {
201
+ try {
202
+ const client = await createApiClient(options.alias);
203
+ const config = await readConfig();
204
+ const profile = getProfile(config, options.alias);
205
+ if (!profile) {
206
+ console.error(`Profile not found: ${options.alias}. Run 'kontexted login' first.`);
207
+ process.exit(1);
208
+ }
209
+ if (!profile.write) {
210
+ console.error("Error: Write operations not enabled for this profile. Re-login with 'kontexted login --alias <alias> --write' to enable write access.");
211
+ process.exit(1);
212
+ }
213
+ const result = await executeCreateFolder(client, profile.workspace, options.name, options.displayName, options.parentId);
214
+ displayResult(result);
215
+ }
216
+ catch (error) {
217
+ console.error(error instanceof Error ? error.message : String(error));
218
+ process.exit(1);
219
+ }
220
+ });
221
+ skillCommand
222
+ .command("create-note")
223
+ .description("Create a new note in the workspace")
224
+ .requiredOption("--alias <name>", "Profile alias to use")
225
+ .requiredOption("--name <name>", "URL-safe note name")
226
+ .requiredOption("--title <title>", "Human-readable note title")
227
+ .option("--folder-id <folderPublicId>", "Public ID of folder (for notes in folders)")
228
+ .option("--content <content>", "Initial content for the note")
229
+ .action(async (options) => {
230
+ try {
231
+ const client = await createApiClient(options.alias);
232
+ const config = await readConfig();
233
+ const profile = getProfile(config, options.alias);
234
+ if (!profile) {
235
+ console.error(`Profile not found: ${options.alias}. Run 'kontexted login' first.`);
236
+ process.exit(1);
237
+ }
238
+ if (!profile.write) {
239
+ console.error("Error: Write operations not enabled for this profile. Re-login with 'kontexted login --alias <alias> --write' to enable write access.");
240
+ process.exit(1);
241
+ }
242
+ const result = await executeCreateNote(client, profile.workspace, options.name, options.title, options.folderId, options.content);
243
+ displayResult(result);
244
+ }
245
+ catch (error) {
246
+ console.error(error instanceof Error ? error.message : String(error));
247
+ process.exit(1);
248
+ }
249
+ });
250
+ skillCommand
251
+ .command("update-note-content")
252
+ .description("Update the content of an existing note")
253
+ .requiredOption("--alias <name>", "Profile alias to use")
254
+ .requiredOption("--note-id <notePublicId>", "Public ID of the note to update")
255
+ .requiredOption("--content <content>", "New content for the note")
256
+ .action(async (options) => {
257
+ try {
258
+ const client = await createApiClient(options.alias);
259
+ const config = await readConfig();
260
+ const profile = getProfile(config, options.alias);
261
+ if (!profile) {
262
+ console.error(`Profile not found: ${options.alias}. Run 'kontexted login' first.`);
263
+ process.exit(1);
264
+ }
265
+ if (!profile.write) {
266
+ console.error("Error: Write operations not enabled for this profile. Re-login with 'kontexted login --alias <alias> --write' to enable write access.");
267
+ process.exit(1);
268
+ }
269
+ const result = await executeUpdateNoteContent(client, profile.workspace, options.noteId, options.content);
270
+ displayResult(result);
271
+ }
272
+ catch (error) {
273
+ console.error(error instanceof Error ? error.message : String(error));
274
+ process.exit(1);
275
+ }
276
+ });
145
277
  skillCommand
146
278
  .command("init")
147
279
  .description("Initialize AI agent skills for the current project")
@@ -3,7 +3,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
3
3
  import { z } from "zod";
4
4
  import { logError } from "../lib/logger.js";
5
5
  // Tools that modify data
6
- const WRITE_TOOLS = new Set(["createFolder", "createNote", "updateNote", "deleteNote", "deleteFolder"]);
6
+ const WRITE_TOOLS = new Set(["createFolder", "createNote", "updateNoteContent", "deleteNote", "deleteFolder"]);
7
7
  /**
8
8
  * Convert JSON schema to Zod schema, removing workspaceSlug
9
9
  */
@@ -1,14 +1,24 @@
1
1
  export const kontextedCliSkill = {
2
2
  name: 'kontexted-cli',
3
- description: 'Access and manage Kontexted workspaces, search notes, and retrieve content through the kontexted CLI. Use when the user needs to explore workspace structure, find specific notes, or read note content using profile aliases.',
3
+ description: 'Access and manage Kontexted workspaces, search notes, retrieve content, and create/update notes and folders through the kontexted CLI. Use when the user needs to explore workspace structure, find specific notes, read note content, or modify workspace content using profile aliases.',
4
4
  content: String.raw `# Kontexted CLI Skill Commands
5
5
 
6
+ ## Read Commands (No --write flag needed)
7
+
6
8
  \`\`\`
7
9
  kontexted skill workspace-tree --alias <profile> # Get workspace folder/note structure
8
10
  kontexted skill search-notes --alias <profile> --query "<text>" # Search notes
9
11
  kontexted skill note-by-id --alias <profile> --note-id <id> # Get specific note
10
12
  \`\`\`
11
13
 
14
+ ## Write Commands (Require write-enabled profile)
15
+
16
+ \`\`\`
17
+ kontexted skill create-folder --alias <profile> --name <name> --display-name "<displayName>" [--parent-id <id>]
18
+ kontexted skill create-note --alias <profile> --name <name> --title "<title>" [--folder-id <id>] [--content "<content>"]
19
+ kontexted skill update-note-content --alias <profile> --note-id <id> --content "<content>"
20
+ \`\`\`
21
+
12
22
  ## Prerequisites
13
23
 
14
24
  Before using the kontexted CLI skill commands, ensure:
@@ -16,12 +26,15 @@ Before using the kontexted CLI skill commands, ensure:
16
26
  1. **kontexted CLI is installed** - Install via npm or your preferred package manager
17
27
  2. **User has authenticated** - User must have run \`kontexted login\` with a profile alias
18
28
  3. **Profile has a workspace configured** - The profile alias must be associated with an active workspace
29
+ 4. **Write operations require write-enabled profile** - To use write commands, the profile must have been created with \`kontexted login --write\`
19
30
 
20
31
  All commands require the \`--alias\` parameter to specify which profile to use. The profile must already be set up and authenticated.
21
32
 
22
33
  ## Available Tools
23
34
 
24
- ### workspace-tree
35
+ ### Read Tools
36
+
37
+ #### workspace-tree
25
38
 
26
39
  Get the complete folder and note structure of a workspace.
27
40
 
@@ -39,7 +52,7 @@ kontexted skill workspace-tree --alias <profile>
39
52
  - When exploring available notes before reading specific content
40
53
  - When building navigation paths to locate notes
41
54
 
42
- ### search-notes
55
+ #### search-notes
43
56
 
44
57
  Search for notes containing specific text content.
45
58
 
@@ -50,16 +63,16 @@ kontexted skill search-notes --alias <profile> --query "<text>" [--limit <n>]
50
63
  **Options:**
51
64
  - \`--alias\` (required): The profile alias to use for authentication
52
65
  - \`--query\` (required): The search text to find in notes
53
- - \`--limit\` (optional): Maximum number of results to return (default: 10)
66
+ - \`--limit\` (optional): Maximum number of results to return (default: 20, max: 50)
54
67
 
55
- **Returns:** JSON array of matching notes with metadata including note ID, title, and snippets
68
+ **Returns:** JSON array of matching notes with metadata including note ID, title, and relevant snippets
56
69
 
57
70
  **When to use:**
58
71
  - When you need to find notes containing specific keywords
59
72
  - When searching for content across multiple notes
60
73
  - When the user asks to find notes about a particular topic
61
74
 
62
- ### note-by-id
75
+ #### note-by-id
63
76
 
64
77
  Retrieve the complete content of a specific note by its ID.
65
78
 
@@ -78,24 +91,114 @@ kontexted skill note-by-id --alias <profile> --note-id <id>
78
91
  - After finding a note via search or workspace tree exploration
79
92
  - When the user asks to read a specific note
80
93
 
94
+ ### Write Tools
95
+
96
+ #### create-folder
97
+
98
+ Create a new folder in the workspace. Optionally nest under a parent folder.
99
+
100
+ \`\`\`bash
101
+ kontexted skill create-folder --alias <profile> --name <name> --display-name "<displayName>" [--parent-id <parentPublicId>]
102
+ \`\`\`
103
+
104
+ **Options:**
105
+ - \`--alias\` (required): The profile alias to use for authentication
106
+ - \`--name\` (required): URL-safe folder name (kebab-case, camelCase, snake_case, or PascalCase)
107
+ - \`--display-name\` (required): Human-readable display name for the folder
108
+ - \`--parent-id\` (optional): Public ID of parent folder (omit for root level)
109
+
110
+ **Returns:** JSON object containing the created folder's public ID and metadata
111
+
112
+ **When to use:**
113
+ - When creating a new folder to organize notes
114
+ - When setting up a hierarchical folder structure
115
+
116
+ **Error cases:**
117
+ - **"Folder with this name already exists"** - Choose a different name
118
+ - **"Parent folder not found"** - Verify the parent folder ID
119
+ - **"Invalid folder name"** - Use kebab-case, camelCase, snake_case, or PascalCase
120
+
121
+ #### create-note
122
+
123
+ Create a new note in the workspace. Optionally place in a folder.
124
+
125
+ \`\`\`bash
126
+ kontexted skill create-note --alias <profile> --name <name> --title "<title>" [--folder-id <folderPublicId>] [--content "<content>"]
127
+ \`\`\`
128
+
129
+ **Options:**
130
+ - \`--alias\` (required): The profile alias to use for authentication
131
+ - \`--name\` (required): URL-safe note name (kebab-case, camelCase, snake_case, or PascalCase)
132
+ - \`--title\` (required): Human-readable title for the note
133
+ - \`--folder-id\` (optional): Public ID of folder (omit for root level)
134
+ - \`--content\` (optional): Initial content for the note (defaults to empty)
135
+
136
+ **Returns:** JSON object containing the created note's public ID and metadata
137
+
138
+ **When to use:**
139
+ - When creating a new note in the workspace
140
+ - When the user asks to write or create a document/note
141
+
142
+ **Error cases:**
143
+ - **"Note with this name already exists"** - Choose a different name
144
+ - **"Folder not found"** - Verify the folder ID
145
+ - **"Invalid note name"** - Use kebab-case, camelCase, snake_case, or PascalCase
146
+
147
+ #### update-note-content
148
+
149
+ Update the content of an existing note. This creates a revision for history.
150
+
151
+ \`\`\`bash
152
+ kontexted skill update-note-content --alias <profile> --note-id <notePublicId> --content "<content>"
153
+ \`\`\`
154
+
155
+ **Options:**
156
+ - \`--alias\` (required): The profile alias to use for authentication
157
+ - \`--note-id\` (required): Public ID of the note to update
158
+ - \`--content\` (required): New content for the note
159
+
160
+ **Returns:** JSON object containing the note's public ID, revision ID, and updated timestamp
161
+
162
+ **When to use:**
163
+ - When updating the content of an existing note
164
+ - When the user asks to edit or modify a note's content
165
+ - When appending or replacing note content
166
+
167
+ **Important notes:**
168
+ - This operation replaces the entire note content
169
+ - A revision is created for history tracking
170
+ - Connected clients are notified in real-time
171
+
172
+ **Error cases:**
173
+ - **"Note not found"** - Verify the note ID
174
+ - **"Invalid note public ID"** - Check the ID format
175
+
81
176
  ## Typical Workflow
82
177
 
83
178
  The skill commands work best when combined in a logical sequence:
84
179
 
180
+ ### Read-Only Workflow
181
+
85
182
  1. **Explore** - Use \`workspace-tree\` to understand workspace structure
86
183
  2. **Search** - Use \`search-notes\` to find relevant notes by content
87
184
  3. **Read** - Use \`note-by-id\` to retrieve full content of specific notes
88
185
 
89
- **Example workflow:**
186
+ ### Write Workflow
187
+
188
+ 1. **Create structure** - Use \`create-folder\` to organize content
189
+ 2. **Create notes** - Use \`create-note\` to create new notes
190
+ 3. **Update content** - Use \`update-note-content\` to modify existing notes
191
+
192
+ **Example write workflow:**
90
193
  \`\`\`bash
91
- # First, explore the workspace structure
92
- kontexted skill workspace-tree --alias work
194
+ # Create a folder for project documentation
195
+ kontexted skill create-folder --alias work --name "project-docs" --display-name "Project Documentation"
93
196
 
94
- # Then, search for notes containing specific content
95
- kontexted skill search-notes --alias work --query "project planning" --limit 5
197
+ # Create a note in that folder (use the returned publicId)
198
+ kontexted skill create-note --alias work --name "requirements" --title "Requirements" --folder-id "FOLDER_PUBLIC_ID"
96
199
 
97
- # Finally, read the content of notes of interest
98
- kontexted skill note-by-id --alias work --note-id "abc123"
200
+ # Update the note content
201
+ kontexted skill update-note-content --alias work --note-id "NOTE_PUBLIC_ID" --content "# Requirements\n\n- Feature A\n- Feature B"
99
202
  \`\`\`
100
203
 
101
204
  ## Example Usage
@@ -124,16 +227,35 @@ kontexted skill search-notes --alias work --query "todo" --limit 3
124
227
  kontexted skill note-by-id --alias work --note-id "note-uuid-123"
125
228
  \`\`\`
126
229
 
127
- ### Combining commands in a single task
230
+ ### Creating a folder structure
231
+
232
+ \`\`\`bash
233
+ # Create a root-level folder
234
+ kontexted skill create-folder --alias work --name "meetings" --display-name "Meeting Notes"
235
+
236
+ # Create a nested folder (use the returned publicId as parent-id)
237
+ kontexted skill create-folder --alias work --name "2024" --display-name "2024 Meetings" --parent-id "PARENT_FOLDER_ID"
238
+ \`\`\`
239
+
240
+ ### Creating and populating a note
128
241
 
129
242
  \`\`\`bash
130
- # Task: Find and read notes about project requirements
131
- # Step 1: Search for relevant notes
132
- kontexted skill search-notes --alias work --query "requirements" --limit 5
243
+ # Create a note with initial content
244
+ kontexted skill create-note --alias work --name "todo" --title "Todo List" --content "# Todo\n\n- [ ] Task 1\n- [ ] Task 2"
133
245
 
134
- # Step 2: Read each matching note
135
- kontexted skill note-by-id --alias work --note-id "req-001"
136
- kontexted skill note-by-id --alias work --note-id "req-002"
246
+ # Later, update the note content
247
+ kontexted skill update-note-content --alias work --note-id "NOTE_ID" --content "# Todo\n\n- [x] Task 1\n- [ ] Task 2\n- [ ] Task 3"
248
+ \`\`\`
249
+
250
+ ### Combining read and write operations
251
+
252
+ \`\`\`bash
253
+ # Task: Find a note and update it
254
+ # Step 1: Search for the note
255
+ kontexted skill search-notes --alias work --query "meeting notes"
256
+
257
+ # Step 2: Update the found note
258
+ kontexted skill update-note-content --alias work --note-id "FOUND_NOTE_ID" --content "Updated content here"
137
259
  \`\`\`
138
260
 
139
261
  ## Error Handling
@@ -148,6 +270,14 @@ If you encounter authentication errors:
148
270
 
149
271
  3. **"No workspace configured"** - The profile is authenticated but has no workspace. Ask the user to set up a workspace with \`kontexted workspace set --alias <profile>\`.
150
272
 
273
+ ### Write operation errors
274
+
275
+ 1. **"Write operations not enabled for this profile"** - Re-login with \`kontexted login --alias <alias> --write\` to enable write access
276
+ 2. **"Folder with this name already exists"** - Use a unique name or check existing folders
277
+ 3. **"Note with this name already exists"** - Use a unique name or check existing notes
278
+ 4. **"Parent folder not found"** - Verify the parent folder ID exists
279
+ 5. **"Note not found"** - Verify the note ID is correct
280
+
151
281
  ### Other errors
152
282
 
153
283
  - **"Note not found"** - The specified note ID doesn't exist or belongs to a different workspace
@@ -160,10 +290,16 @@ When errors occur, report them clearly to the user so they can take appropriate
160
290
 
161
291
  All commands return JSON output that is easy to parse:
162
292
 
293
+ ### Read commands
163
294
  - \`workspace-tree\`: Returns nested object with folders and notes
164
295
  - \`search-notes\`: Returns array of matching notes with ID, title, and snippets
165
296
  - \`note-by-id\`: Returns complete note object with body and metadata
166
297
 
298
+ ### Write commands
299
+ - \`create-folder\`: Returns \`{ folder: { publicId, name, displayName, parentPublicId } }\`
300
+ - \`create-note\`: Returns \`{ note: { publicId, name, title, folderPublicId, content } }\`
301
+ - \`update-note-content\`: Returns \`{ note: { publicId, revisionId, updatedAt } }\`
302
+
167
303
  Use this structured output to provide clear responses to users about workspace contents and note information.
168
304
  `
169
305
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kontexted",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "CLI tool for Kontexted - MCP proxy, workspaces management, and local server",
5
5
  "type": "module",
6
6
  "bin": {
@@ -43,8 +43,8 @@
43
43
  "typescript": "^5.6.0"
44
44
  },
45
45
  "optionalDependencies": {
46
- "@kontexted/darwin-arm64": "0.1.5",
47
- "@kontexted/linux-x64": "0.1.5",
48
- "@kontexted/windows-x64": "0.1.5"
46
+ "@kontexted/darwin-arm64": "0.1.6",
47
+ "@kontexted/linux-x64": "0.1.6",
48
+ "@kontexted/windows-x64": "0.1.6"
49
49
  }
50
50
  }