Save and organize research content with structured metadata — pasted text, URLs, or existing files are stored as clean markdown with YAML frontmatter in the project's research directory.
-
Get the content. Accept content from the user. This can be:
- Pasted text — raw content to save directly
- A URL — delegate to the
download-webpage skill first to fetch and convert the page, then organize the result with save-research
- A file path — an existing file on disk to organize into the research directory
-
Ask for metadata. Ask the user for:
- Tags (required) — comma-separated tags for categorization (e.g.,
ai, machine-learning, transformers)
- Title (optional) — a descriptive title. If not provided, the script will try to extract one from existing frontmatter or use "untitled-research"
- Author (optional) — the content author
-
Determine the output directory. Default to research/ in the current project (e.g. <project>/research/). If no project is open, use ~/Notesage/research/. Always confirm the output directory with the user before proceeding.
-
If the input is a URL, run the download-webpage skill first:
execute_skill_script("download-webpage", "scripts/download.mjs", [url, output_dir, "--tags", "tag1,tag2"])
Then use the resulting file path as input to save-research to ensure consistent metadata and organization. If download-webpage already saved the file with the correct metadata, you can skip re-saving and just report the result.
-
Run the save script:
execute_skill_script("save-research", "scripts/save.mjs", [content_or_path, output_dir, "--title", "Article Title", "--tags", "tag1,tag2"])
Optional flags:
--url "https://..." — source URL for attribution
--author "Author Name" — content author
--force — overwrite an existing file with the same name or source URL
For pasted text content, pass the text as the first argument. For file paths, pass the absolute path — the script detects whether the argument is an existing file and reads from it.
Output JSON to stdout:
{
"file": "/path/to/saved/article-title.md",
"title": "Article Title",
"tags": ["tag1", "tag2"],
"status": "created"
}
status values:
"created" — new file saved
"exists" — file already exists (by filename or matching source_url), nothing was written
"overwritten" — existing file was replaced (when --force was used)
-
Handle existing files. If status is "exists", you MUST present exactly these three choices as a numbered list:
- Overwrite — re-run with
--force flag added to the arguments
- Keep both — rename the existing file by appending
-1 (or -2, etc.) to the filename, then re-run without --force
- Skip — do nothing, move on
-
Report the result. Tell the user:
- The saved file title and location
- The tags applied
- If the save failed, show the error from stderr