Initial commit
This commit is contained in:
124
skills/drafts-manager/actions/README.md
Normal file
124
skills/drafts-manager/actions/README.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# Installing Drafts Actions
|
||||
|
||||
These actions must be installed in Drafts before the drafts-manager skill can work.
|
||||
|
||||
## Quick Install
|
||||
|
||||
### Geoffrey Export Inbox
|
||||
|
||||
1. Open Drafts on your Mac
|
||||
2. Go to **Drafts → Settings → Actions → Manage Actions**
|
||||
3. Click the **+** button to create a new action
|
||||
4. Set the name to: `Geoffrey Export Inbox`
|
||||
5. Click **Steps** → Add **Script** step
|
||||
6. Copy the entire contents of `geoffrey-export-inbox.js` into the script editor
|
||||
7. Click **Done** to save
|
||||
|
||||
### Geoffrey Process Draft
|
||||
|
||||
1. Create another new action
|
||||
2. Set the name to: `Geoffrey Process Draft`
|
||||
3. Click **Steps** → Add **Script** step
|
||||
4. Copy the entire contents of `geoffrey-process-draft.js` into the script editor
|
||||
5. **Important:** Click the gear icon on the action and set:
|
||||
- After Success: **Do Nothing** (don't archive the draft - the script handles this)
|
||||
6. Click **Done** to save
|
||||
|
||||
## Verification
|
||||
|
||||
Test that the export action works:
|
||||
|
||||
```bash
|
||||
open "drafts://x-callback-url/runAction?action=Geoffrey%20Export%20Inbox"
|
||||
```
|
||||
|
||||
Check for the export file:
|
||||
|
||||
```bash
|
||||
ls -la ~/Library/Mobile\ Documents/iCloud~com~agiletortoise~Drafts5/Documents/geoffrey-export.json
|
||||
```
|
||||
|
||||
If the file exists and was recently modified, the action is working.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Action not found" error
|
||||
|
||||
- Make sure the action names match exactly:
|
||||
- `Geoffrey Export Inbox`
|
||||
- `Geoffrey Process Draft`
|
||||
- Check for extra spaces or different capitalization
|
||||
|
||||
### Export file not created
|
||||
|
||||
- Open Drafts manually and run the action from the action list
|
||||
- Check for script errors in Drafts' action log
|
||||
|
||||
### OmniFocus tasks not created
|
||||
|
||||
- Make sure OmniFocus is running
|
||||
- The OmniFocus URL scheme needs the app to be open
|
||||
|
||||
### Obsidian notes not created
|
||||
|
||||
- Make sure Obsidian is running
|
||||
- Verify the vault name is "Personal_Notes"
|
||||
- Check that the folder path exists
|
||||
|
||||
## Action Configuration
|
||||
|
||||
### Template Tags
|
||||
|
||||
The process action uses Drafts template tags to receive parameters. When called via URL:
|
||||
|
||||
```
|
||||
drafts://x-callback-url/runAction?action=Geoffrey%20Process%20Draft&uuid=ABC&destination=omnifocus
|
||||
```
|
||||
|
||||
The parameters become available as template tags in the script.
|
||||
|
||||
### Customization
|
||||
|
||||
To change the Obsidian vault path, edit this line in `geoffrey-process-draft.js`:
|
||||
|
||||
```javascript
|
||||
let obsidianVault = "/Users/hagelk/Library/Mobile Documents/iCloud~md~obsidian/Documents/Personal_Notes";
|
||||
```
|
||||
|
||||
To change the default Obsidian folder, edit:
|
||||
|
||||
```javascript
|
||||
let folder = draft.getTemplateTag("folder") || "Geoffrey/Inbox";
|
||||
```
|
||||
|
||||
## URL Scheme Reference
|
||||
|
||||
### Export
|
||||
|
||||
```
|
||||
drafts://x-callback-url/runAction?action=Geoffrey%20Export%20Inbox
|
||||
```
|
||||
|
||||
### Process to OmniFocus
|
||||
|
||||
```
|
||||
drafts://x-callback-url/runAction?action=Geoffrey%20Process%20Draft&uuid=UUID&destination=omnifocus&project=Project&tags=Tag1,Tag2&dueDate=2025-11-30
|
||||
```
|
||||
|
||||
### Process to Obsidian
|
||||
|
||||
```
|
||||
drafts://x-callback-url/runAction?action=Geoffrey%20Process%20Draft&uuid=UUID&destination=obsidian&folder=Meetings
|
||||
```
|
||||
|
||||
### Archive
|
||||
|
||||
```
|
||||
drafts://x-callback-url/runAction?action=Geoffrey%20Process%20Draft&uuid=UUID&destination=archive
|
||||
```
|
||||
|
||||
### Trash
|
||||
|
||||
```
|
||||
drafts://x-callback-url/runAction?action=Geoffrey%20Process%20Draft&uuid=UUID&destination=trash
|
||||
```
|
||||
53
skills/drafts-manager/actions/geoffrey-export-inbox.js
Normal file
53
skills/drafts-manager/actions/geoffrey-export-inbox.js
Normal file
@@ -0,0 +1,53 @@
|
||||
// Geoffrey Export Inbox - Drafts Action
|
||||
// Exports all inbox drafts to JSON file for Geoffrey to analyze
|
||||
//
|
||||
// To install:
|
||||
// 1. Create new action in Drafts named "Geoffrey Export Inbox"
|
||||
// 2. Add a "Script" step
|
||||
// 3. Paste this code
|
||||
|
||||
// Query inbox drafts (not archived, not trashed)
|
||||
let drafts = Draft.query("", "inbox", [], [], "modified", true, false);
|
||||
|
||||
// Build export data
|
||||
let exportData = {
|
||||
exported: new Date().toISOString(),
|
||||
count: drafts.length,
|
||||
drafts: []
|
||||
};
|
||||
|
||||
// Process each draft
|
||||
drafts.forEach(d => {
|
||||
exportData.drafts.push({
|
||||
uuid: d.uuid,
|
||||
title: d.title || "(untitled)",
|
||||
content: d.content,
|
||||
tags: d.tags,
|
||||
createdAt: d.createdAt.toISOString(),
|
||||
modifiedAt: d.modifiedAt.toISOString(),
|
||||
isFlagged: d.isFlagged,
|
||||
wordCount: d.content.split(/\s+/).filter(w => w.length > 0).length
|
||||
});
|
||||
});
|
||||
|
||||
// Write to iCloud Documents folder
|
||||
let fm = FileManager.createCloud();
|
||||
let exportPath = "geoffrey-export.json";
|
||||
let jsonContent = JSON.stringify(exportData, null, 2);
|
||||
|
||||
let success = fm.writeString(exportPath, jsonContent);
|
||||
|
||||
if (success) {
|
||||
// Show confirmation
|
||||
app.displayInfoMessage("Exported " + drafts.length + " drafts for Geoffrey");
|
||||
|
||||
// Set draft content to summary (for callback if needed)
|
||||
draft.content = JSON.stringify({
|
||||
status: "success",
|
||||
count: drafts.length,
|
||||
path: exportPath
|
||||
});
|
||||
} else {
|
||||
app.displayErrorMessage("Failed to write export file");
|
||||
context.fail("Export failed");
|
||||
}
|
||||
185
skills/drafts-manager/actions/geoffrey-process-draft.js
Normal file
185
skills/drafts-manager/actions/geoffrey-process-draft.js
Normal file
@@ -0,0 +1,185 @@
|
||||
// Geoffrey Process Draft - Drafts Action
|
||||
// Processes a single draft based on routing instructions
|
||||
//
|
||||
// Call via URL with JSON in text parameter:
|
||||
// drafts://x-callback-url/runAction?action=Geoffrey%20Process%20Draft&text={"uuid":"...","destination":"..."}
|
||||
//
|
||||
// JSON parameters:
|
||||
// - uuid: Draft UUID to process
|
||||
// - destination: "omnifocus", "obsidian", "archive", or "trash"
|
||||
// - project: OmniFocus project name (for omnifocus destination)
|
||||
// - tags: Comma-separated OmniFocus tags (for omnifocus destination)
|
||||
// - dueDate: Due date string (for omnifocus destination)
|
||||
// - folder: Obsidian folder path (for obsidian destination)
|
||||
//
|
||||
// To install:
|
||||
// 1. Create new action in Drafts named "Geoffrey Process Draft"
|
||||
// 2. Add a "Script" step
|
||||
// 3. Paste this code
|
||||
|
||||
// Parse parameters from draft content (passed as JSON via URL text parameter)
|
||||
let params;
|
||||
try {
|
||||
params = JSON.parse(draft.content);
|
||||
} catch (e) {
|
||||
app.displayErrorMessage("Invalid JSON parameters: " + e.message);
|
||||
context.fail("Invalid JSON");
|
||||
}
|
||||
|
||||
let uuid = params.uuid;
|
||||
let destination = params.destination;
|
||||
let project = params.project || "";
|
||||
let tags = params.tags || "";
|
||||
let dueDate = params.dueDate || "";
|
||||
let folder = params.folder || "Geoffrey/Inbox";
|
||||
|
||||
// Validate required parameters
|
||||
if (!uuid) {
|
||||
app.displayErrorMessage("Missing uuid parameter");
|
||||
context.fail("Missing uuid");
|
||||
}
|
||||
|
||||
if (!destination) {
|
||||
app.displayErrorMessage("Missing destination parameter");
|
||||
context.fail("Missing destination");
|
||||
}
|
||||
|
||||
// Find the draft to process
|
||||
let targetDraft = Draft.find(uuid);
|
||||
|
||||
if (!targetDraft) {
|
||||
app.displayErrorMessage("Draft not found: " + uuid);
|
||||
context.fail("Draft not found");
|
||||
}
|
||||
|
||||
let title = targetDraft.title || "(untitled)";
|
||||
let content = targetDraft.content;
|
||||
let result = { uuid: uuid, title: title, destination: destination };
|
||||
|
||||
// Route based on destination
|
||||
switch (destination) {
|
||||
case "omnifocus":
|
||||
// Build OmniFocus URL
|
||||
let ofUrl = "omnifocus:///add?";
|
||||
let params = [];
|
||||
|
||||
// Task name is first line
|
||||
params.push("name=" + encodeURIComponent(title));
|
||||
|
||||
// Note is rest of content
|
||||
let noteLines = content.split("\n").slice(1).join("\n").trim();
|
||||
if (noteLines) {
|
||||
params.push("note=" + encodeURIComponent(noteLines));
|
||||
}
|
||||
|
||||
// Project
|
||||
if (project) {
|
||||
params.push("project=" + encodeURIComponent(project));
|
||||
}
|
||||
|
||||
// Due date
|
||||
if (dueDate) {
|
||||
params.push("due=" + encodeURIComponent(dueDate));
|
||||
}
|
||||
|
||||
// Open OmniFocus URL
|
||||
let ofCallback = CallbackURL.create();
|
||||
ofCallback.baseURL = ofUrl + params.join("&");
|
||||
|
||||
let ofSuccess = ofCallback.open();
|
||||
|
||||
if (ofSuccess) {
|
||||
result.status = "success";
|
||||
result.project = project;
|
||||
result.tags = tags;
|
||||
|
||||
// Tag the draft as processed
|
||||
targetDraft.addTag("sent-to-omnifocus");
|
||||
targetDraft.isArchived = true;
|
||||
targetDraft.update();
|
||||
|
||||
app.displayInfoMessage("Task sent to OmniFocus: " + title);
|
||||
} else {
|
||||
result.status = "failed";
|
||||
result.error = "Failed to open OmniFocus";
|
||||
app.displayErrorMessage("Failed to send to OmniFocus");
|
||||
}
|
||||
break;
|
||||
|
||||
case "obsidian":
|
||||
// Build Obsidian file path
|
||||
let obsidianVault = "/Users/hagelk/Library/Mobile Documents/iCloud~md~obsidian/Documents/Personal_Notes";
|
||||
let fileName = title.replace(/[\/\\:*?"<>|]/g, "-") + ".md";
|
||||
let filePath = folder + "/" + fileName;
|
||||
|
||||
// Build frontmatter
|
||||
let frontmatter = [
|
||||
"---",
|
||||
"created: " + new Date().toISOString().split("T")[0],
|
||||
"source: drafts",
|
||||
"draft-uuid: " + uuid,
|
||||
"tags: [from-drafts]",
|
||||
"---",
|
||||
""
|
||||
].join("\n");
|
||||
|
||||
// Full content with frontmatter
|
||||
let obsidianContent = frontmatter + content;
|
||||
|
||||
// Use Obsidian URL scheme to create file
|
||||
let obsCallback = CallbackURL.create();
|
||||
obsCallback.baseURL = "obsidian://new";
|
||||
obsCallback.addParameter("vault", "Personal_Notes");
|
||||
obsCallback.addParameter("file", folder + "/" + fileName.replace(".md", ""));
|
||||
obsCallback.addParameter("content", obsidianContent);
|
||||
obsCallback.addParameter("overwrite", "true");
|
||||
|
||||
let obsSuccess = obsCallback.open();
|
||||
|
||||
if (obsSuccess) {
|
||||
result.status = "success";
|
||||
result.folder = folder;
|
||||
result.file = fileName;
|
||||
|
||||
// Tag the draft as processed
|
||||
targetDraft.addTag("sent-to-obsidian");
|
||||
targetDraft.isArchived = true;
|
||||
targetDraft.update();
|
||||
|
||||
app.displayInfoMessage("Note saved to Obsidian: " + fileName);
|
||||
} else {
|
||||
result.status = "failed";
|
||||
result.error = "Failed to create Obsidian note";
|
||||
app.displayErrorMessage("Failed to save to Obsidian");
|
||||
}
|
||||
break;
|
||||
|
||||
case "archive":
|
||||
// Simply archive the draft
|
||||
targetDraft.addTag("archived-by-geoffrey");
|
||||
targetDraft.isArchived = true;
|
||||
targetDraft.update();
|
||||
|
||||
result.status = "success";
|
||||
app.displayInfoMessage("Archived: " + title);
|
||||
break;
|
||||
|
||||
case "trash":
|
||||
// Move to trash
|
||||
targetDraft.addTag("trashed-by-geoffrey");
|
||||
targetDraft.isTrashed = true;
|
||||
targetDraft.update();
|
||||
|
||||
result.status = "success";
|
||||
app.displayInfoMessage("Trashed: " + title);
|
||||
break;
|
||||
|
||||
default:
|
||||
result.status = "failed";
|
||||
result.error = "Unknown destination: " + destination;
|
||||
app.displayErrorMessage("Unknown destination: " + destination);
|
||||
context.fail("Unknown destination");
|
||||
}
|
||||
|
||||
// Set draft content to result (for potential callback)
|
||||
draft.content = JSON.stringify(result, null, 2);
|
||||
Reference in New Issue
Block a user