slyplan-mcp 1.6.2 → 1.6.3
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/commands/sly/sort-tasks.md +26 -28
- package/dist/cli.js +3 -1
- package/dist/supabase.js +25 -4
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Sort
|
|
2
|
+
description: Sort "Sort Later" tasks into the correct places in the project tree
|
|
3
3
|
argument-hint: "[project-name]"
|
|
4
4
|
allowed-tools:
|
|
5
5
|
- mcp__slyplan__list_projects
|
|
@@ -11,9 +11,10 @@ allowed-tools:
|
|
|
11
11
|
- mcp__slyplan__move_node
|
|
12
12
|
- mcp__slyplan__add_node
|
|
13
13
|
- mcp__slyplan__search
|
|
14
|
+
- mcp__supabase__execute_sql
|
|
14
15
|
---
|
|
15
16
|
|
|
16
|
-
You are a task sorting agent for SlyPlan. Your job is to take all unsorted tasks from the "
|
|
17
|
+
You are a task sorting agent for SlyPlan. Your job is to take all unsorted tasks from the "Sort Later" list (stored in project metadata) and create them as proper nodes in the correct places in the project tree for "$ARGUMENTS". Follow each step in order.
|
|
17
18
|
|
|
18
19
|
## Step 1: Resolve Project
|
|
19
20
|
|
|
@@ -22,50 +23,47 @@ You are a task sorting agent for SlyPlan. Your job is to take all unsorted tasks
|
|
|
22
23
|
3. If no reasonable match is found, output: **"No project found matching '$ARGUMENTS'"** and stop.
|
|
23
24
|
4. Call `set_project` with the matched project's ID.
|
|
24
25
|
|
|
25
|
-
## Step 2: Find
|
|
26
|
+
## Step 2: Find Sort Later Tasks
|
|
26
27
|
|
|
27
|
-
1. Call `
|
|
28
|
-
2.
|
|
29
|
-
3.
|
|
30
|
-
4.
|
|
31
|
-
5. Note down all the tasks (children of Quick Tasks) — their IDs, titles, and descriptions.
|
|
28
|
+
1. Call `get_node` on the project to read its metadata.
|
|
29
|
+
2. Look for `metadata.sortLaterTasks` — this is an array of tasks with `{ id, title, description, children: [{ id, title, description }] }`.
|
|
30
|
+
3. If the array is empty or doesn't exist, output: **"No Sort Later tasks found — nothing to sort."** and stop.
|
|
31
|
+
4. Note down all the tasks — their titles, descriptions, and children.
|
|
32
32
|
|
|
33
33
|
## Step 3: Understand the Tree
|
|
34
34
|
|
|
35
35
|
1. Call `get_tree` **once** to get the complete project hierarchy.
|
|
36
36
|
2. Analyze the existing structure: categories, phases, and plans.
|
|
37
|
-
3. For each
|
|
37
|
+
3. For each Sort Later task, determine the best placement:
|
|
38
38
|
- Search for existing categories and phases that logically match the task.
|
|
39
39
|
- Consider the task's title and description to understand its intent.
|
|
40
|
-
4. Plan ALL
|
|
40
|
+
4. Plan ALL placements before executing any.
|
|
41
41
|
|
|
42
|
-
## Step 4:
|
|
42
|
+
## Step 4: Create Nodes for Each Task
|
|
43
43
|
|
|
44
|
-
For each
|
|
44
|
+
For each Sort Later task:
|
|
45
45
|
|
|
46
|
-
1. **If an existing category > phase fits:**
|
|
47
|
-
2. **If a category exists but no fitting phase:** Create a new `phase` with `add_node`, then
|
|
48
|
-
3. **If no relevant category exists:** Create a new `category` with `add_node`, then a `phase` under it, then
|
|
49
|
-
4. **If a task could itself be a category or phase:**
|
|
50
|
-
5. **If a task
|
|
46
|
+
1. **If an existing category > phase fits:** Create the task as a `plan` node under that phase using `add_node`.
|
|
47
|
+
2. **If a category exists but no fitting phase:** Create a new `phase` with `add_node`, then create the task under it.
|
|
48
|
+
3. **If no relevant category exists:** Create a new `category` with `add_node`, then a `phase` under it, then the task.
|
|
49
|
+
4. **If a task could itself be a category or phase:** Create it with that type and restructure accordingly.
|
|
50
|
+
5. **If a task has children:** Create the parent first, then create each child as a `plan` node under it.
|
|
51
|
+
6. **If a task makes absolutely no sense:** Ask the user what they want to do with it before creating or discarding it.
|
|
51
52
|
|
|
52
53
|
Use your intelligence to group things logically. Related tasks should be near each other.
|
|
53
54
|
|
|
54
|
-
## Step 5:
|
|
55
|
+
## Step 5: Clear Sort Later List
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
2. If empty, delete it using `delete_node`.
|
|
58
|
-
3. If some tasks remain (because you asked the user and they haven't decided), leave the category.
|
|
57
|
+
After all tasks are created as nodes, clear the Sort Later list from project metadata:
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
The database uses `ON DELETE CASCADE` on `parent_id`. Deleting a parent node **permanently destroys ALL descendants**. There is no undo.
|
|
59
|
+
Use `execute_sql` to clear the sortLaterTasks from the project metadata:
|
|
60
|
+
```sql
|
|
61
|
+
UPDATE nodes SET metadata = metadata - 'sortLaterTasks' WHERE id = '<project-id>';
|
|
62
|
+
```
|
|
65
63
|
|
|
66
64
|
## Behavioral Rules
|
|
67
65
|
|
|
68
|
-
- Do NOT
|
|
66
|
+
- Do NOT modify existing nodes — only create new ones for the Sort Later tasks.
|
|
69
67
|
- Ask the user about ambiguous tasks rather than guessing wrong.
|
|
70
68
|
- Fetch `get_tree` only ONCE (Step 3). Work from the snapshot.
|
|
71
69
|
- Plan all changes before executing MCP calls.
|
|
@@ -81,7 +79,7 @@ Sort complete for "[Project Name]"
|
|
|
81
79
|
Sorted: X tasks
|
|
82
80
|
Created: Y new categories/phases
|
|
83
81
|
Skipped: Z tasks (asked user)
|
|
84
|
-
|
|
82
|
+
Sort Later list: [cleared | X tasks remaining]
|
|
85
83
|
```
|
|
86
84
|
|
|
87
85
|
Then list where each task was placed:
|
package/dist/cli.js
CHANGED
|
@@ -49,7 +49,9 @@ process.stdin.on('end', () => {
|
|
|
49
49
|
if (block.type !== 'tool_use') continue;
|
|
50
50
|
const name = block.name || '';
|
|
51
51
|
if (name.includes('set_project')) hasSetProject = true;
|
|
52
|
+
// Track add/remove — last action wins
|
|
52
53
|
if (name.includes('add_to_work_mode')) hasWorkModeNode = true;
|
|
54
|
+
if (name.includes('remove_from_work_mode')) hasWorkModeNode = false;
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
} catch {}
|
|
@@ -72,7 +74,7 @@ process.stdin.on('end', () => {
|
|
|
72
74
|
suppressOutput: true,
|
|
73
75
|
hookSpecificOutput: {
|
|
74
76
|
hookEventName: "PreToolCall",
|
|
75
|
-
additionalContext: "
|
|
77
|
+
additionalContext: "BLOCKED: No node in work mode. Call search + add_to_work_mode before doing any work. This is not optional."
|
|
76
78
|
}
|
|
77
79
|
});
|
|
78
80
|
process.stdout.write(output);
|
package/dist/supabase.js
CHANGED
|
@@ -38,13 +38,34 @@ export async function authenticate() {
|
|
|
38
38
|
const email = process.env.SLYPLAN_EMAIL;
|
|
39
39
|
const password = process.env.SLYPLAN_PASSWORD;
|
|
40
40
|
// 1. Preferred: permanent API key (never expires)
|
|
41
|
+
// Calls edge function to exchange API key → Supabase session tokens
|
|
41
42
|
if (apiKey) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
try {
|
|
44
|
+
const res = await fetch(`${SUPABASE_URL}/functions/v1/exchange-api-key`, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: { 'Content-Type': 'application/json' },
|
|
47
|
+
body: JSON.stringify({ api_key: apiKey }),
|
|
48
|
+
});
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
51
|
+
console.error(`API key authentication failed: ${err.error || res.statusText}`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
const tokens = await res.json();
|
|
55
|
+
const { error: sessionError } = await supabase.auth.setSession({
|
|
56
|
+
access_token: tokens.access_token,
|
|
57
|
+
refresh_token: tokens.refresh_token,
|
|
58
|
+
});
|
|
59
|
+
if (sessionError) {
|
|
60
|
+
console.error(`Failed to set session from API key: ${sessionError.message}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
userId = tokens.user_id;
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
console.error(`API key authentication failed: ${err.message}`);
|
|
45
67
|
process.exit(1);
|
|
46
68
|
}
|
|
47
|
-
userId = data;
|
|
48
69
|
return;
|
|
49
70
|
}
|
|
50
71
|
// 2. Refresh token (browser-based flow)
|