chore: remove the Raycast extension (#1818)

This commit is contained in:
Mahesh Sanikommu 2026-10-14 14:03:45 -07:00 • committed by GitHub
parent b7534f9c50
commit 92f511df9b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 0 additions and 4051 deletions

View file

@ -48,7 +48,6 @@ supermemory/
│ ├── web/ # Redirects to the console
│ ├── mcp/ # Model Context Protocol server
│ ├── docs/ # Documentation site
│ ├── raycast-extension/ # Raycast extension
│ ├── memory-graph-playground/ # Demo app for the memory graph component
│ └── sdk-playground/ # Playground for the SDK integrations
├── packages/

View file

@ -1,14 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
# Raycast specific files
raycast-env.d.ts
.raycast-swift-build
.swiftpm
compiled_raycast_swift
compiled_raycast_rust
# misc
.DS_Store

View file

@ -1,19 +0,0 @@
# Supermemory Changelog
## [Quick Add from Selection] - 2025-11-05
- Select text anywhere and quickly add it as a memory - the content field is automatically filled
- Select text and search memories instantly - the search field is automatically filled
- Save time by selecting text before opening commands
## [Search Projects + Enhancements] - 2025-10-13
- Added command to search and add projects
- Removed `useEffect`
- Simplified `getApiKey` since `Preferences` will be enforced for presence and trim automatically
- Moved API Key check into its own HoC
## [Initial Version] - 2025-10-02
- Added Supermemory integration with Add Memory and Search Memories commands
- Added project organization support for memories

View file

@ -1,35 +0,0 @@
# Raycast Extension for Supermemory
A Raycast extension that lets you add memories and search through your Supermemory collection directly from Raycast.
## Setup
1. Install the extension in Raycast
2. Get your API key from [app.supermemory.ai](https://app.supermemory.ai)
3. Open the extension preferences and enter your API key
## Features
### Add Memory
- Add new memories to your Supermemory collection
- Organize memories by project
- Add optional titles and URLs
- Keyboard shortcut: Cmd+Enter to submit
### Search Memories
- Search through your entire Supermemory collection
- Real-time search with debouncing
- View detailed memory information
- Copy content or open related URLs
- Shows relevance scores and creation dates
## Commands
- `Add Memory` - Add a new memory to your collection
- `Search Memories` - Search through your existing memories
## Authentication
This extension requires a Supermemory API key. You can get your API key from [supermemory.link/raycast](https://supermemory.link/raycast).
The API key is stored securely in Raycast preferences and is required for all operations.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View file

@ -1,4 +0,0 @@
const { defineConfig } = require("eslint/config")
const raycastConfig = require("@raycast/eslint-config")
module.exports = defineConfig([...raycastConfig])

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

File diff suppressed because it is too large Load diff

View file

@ -1,74 +0,0 @@
{
"$schema": "https://www.raycast.com/schemas/extension.json",
"name": "supermemory",
"title": "Supermemory",
"description": "Add and search memories with your personal AI-powered knowledge base",
"icon": "extension-icon.png",
"author": "supermemory",
"contributors": [
"xmok",
"maheshthedev"
],
"platforms": [
"macOS",
"Windows"
],
"categories": [
"Productivity",
"Web"
],
"license": "MIT",
"commands": [
{
"name": "add-memory",
"title": "Add Memory",
"subtitle": "Supermemory",
"description": "Add text, URLs, or documents to your supermemory knowledge base",
"mode": "view"
},
{
"name": "search-memories",
"title": "Search Memories",
"subtitle": "Supermemory",
"description": "Search through your saved memories and find relevant information",
"mode": "view"
},
{
"name": "search-projects",
"title": "Search Projects",
"subtitle": "Supermemory",
"description": "Search through your saved projects and find relevant information",
"mode": "view"
}
],
"preferences": [
{
"name": "apiKey",
"type": "password",
"required": true,
"title": "API Key",
"description": "Your Supermemory API Key. Get it from https://supermemory.link/raycast",
"placeholder": "Enter your Supermemory API Key"
}
],
"dependencies": {
"@raycast/api": "^1.103.3",
"@raycast/utils": "^2.2.1"
},
"devDependencies": {
"@raycast/eslint-config": "^2.0.4",
"@types/node": "22.13.10",
"@types/react": "19.0.10",
"eslint": "^9.22.0",
"prettier": "^3.5.3",
"typescript": "^5.8.2"
},
"scripts": {
"build": "ray build",
"dev": "ray develop",
"fix-lint": "ray lint --fix",
"lint": "ray lint",
"prepublishOnly": "echo \"\\n\\nIt seems like you are trying to publish the Raycast extension to npm.\\n\\nIf you did intend to publish it to npm, remove the \\`prepublishOnly\\` script and rerun \\`npm publish\\` again.\\nIf you wanted to publish it to the Raycast Store instead, use \\`npm run publish\\` instead.\\n\\n\" && exit 1",
"publish": "npx @raycast/api@latest publish"
}
}

View file

@ -1,114 +0,0 @@
import {
Form,
ActionPanel,
Action,
showToast,
Toast,
useNavigation,
Icon,
getSelectedText,
} from "@raycast/api"
import { useState, useEffect } from "react"
import { addMemory, fetchProjects } from "./api"
import { usePromise } from "@raycast/utils"
import { withSupermemory } from "./withSupermemory"
interface FormValues {
content: string
project: string
}
export default withSupermemory(Command)
function Command() {
const [isSubmitting, setIsSubmitting] = useState(false)
const [initialContent, setInitialContent] = useState("")
const { pop } = useNavigation()
const { isLoading, data: projects = [] } = usePromise(fetchProjects)
useEffect(() => {
async function loadSelectedText() {
try {
const selectedText = await getSelectedText()
if (selectedText) {
setInitialContent(selectedText)
}
} catch {
// No text selected or error getting selected text - silently fail
// User can still manually enter content
}
}
loadSelectedText()
}, [])
async function handleSubmit(values: FormValues) {
if (!values.content.trim()) {
await showToast({
style: Toast.Style.Failure,
title: "Content Required",
message: "Please enter some content for the memory",
})
return
}
try {
setIsSubmitting(true)
const containerTags = values.project ? [values.project] : undefined
await addMemory({
content: values.content.trim(),
containerTags,
})
pop()
} catch (error) {
console.error("Failed to add memory:", error)
} finally {
setIsSubmitting(false)
}
}
return (
<Form
isLoading={isLoading || isSubmitting}
actions={
!isLoading && (
<ActionPanel>
<Action.SubmitForm
icon={Icon.Plus}
title="Add Memory"
onSubmit={handleSubmit}
/>
</ActionPanel>
)
}
>
<Form.TextArea
id="content"
title="Content"
value={initialContent}
placeholder="Enter the memory content..."
info="The main content of your memory. This is required."
onChange={(value) => setInitialContent(value)}
/>
<Form.Separator />
<Form.Dropdown
id="project"
title="Project"
info="Select a project to organize this memory"
storeValue
>
<Form.Dropdown.Item value="" title="No Project" />
{projects.map((project) => (
<Form.Dropdown.Item
key={project.id}
value={project.containerTag}
title={project.name}
/>
))}
</Form.Dropdown>
</Form>
)
}

View file

@ -1,222 +0,0 @@
import { getPreferenceValues, showToast, Toast } from "@raycast/api"
export interface Project {
id: string
name: string
containerTag: string
description?: string
}
export interface Memory {
id: string
content: string
title?: string
url?: string
containerTag?: string
createdAt: string
}
export interface SearchResult {
documentId: string
chunks: unknown[]
title?: string
metadata: Record<string, unknown>
score?: number
createdAt: string
updatedAt: string
type: string
}
export interface AddMemoryRequest {
content: string
containerTags?: string[]
title?: string
url?: string
metadata?: Record<string, unknown>
}
interface AddProjectRequest {
name: string
}
export interface SearchRequest {
q: string
containerTags?: string[]
limit?: number
}
export interface SearchResponse {
results: SearchResult[]
timing: number
total: number
}
const API_BASE_URL = "https://api.supermemory.ai"
class SupermemoryAPIError extends Error {
constructor(
message: string,
public status?: number,
) {
super(message)
this.name = "SupermemoryAPIError"
}
}
class AuthenticationError extends Error {
constructor(message: string) {
super(message)
this.name = "AuthenticationError"
}
}
function getApiKey(): string {
const { apiKey } = getPreferenceValues<Preferences>()
return apiKey
}
async function makeAuthenticatedRequest<T>(
endpoint: string,
options: RequestInit = {},
): Promise<T> {
const apiKey = getApiKey()
const url = `${API_BASE_URL}${endpoint}`
try {
const response = await fetch(url, {
...options,
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
...options.headers,
},
})
if (!response.ok) {
if (response.status === 401) {
throw new AuthenticationError(
"Invalid API key. Please check your API key in preferences. Get a new one from https://supermemory.link/raycast",
)
}
let errorMessage = `API request failed: ${response.statusText}`
try {
const errorBody = (await response.json()) as { message?: string }
if (errorBody.message) {
errorMessage = errorBody.message
}
} catch {
// Ignore JSON parsing errors, use default message
}
throw new SupermemoryAPIError(errorMessage, response.status)
}
if (!response.headers.get("content-type")?.includes("application/json")) {
throw new SupermemoryAPIError("Invalid response format from API")
}
const data = (await response.json()) as T
return data
} catch (err) {
if (
err instanceof AuthenticationError ||
err instanceof SupermemoryAPIError
) {
throw err
}
// Handle network errors or other fetch errors
throw new SupermemoryAPIError(
`Network error: ${err instanceof Error ? err.message : "Unknown error"}`,
)
}
}
export async function fetchProjects(): Promise<Project[]> {
try {
const response = await makeAuthenticatedRequest<{ projects: Project[] }>(
"/v3/projects",
)
return response.projects || []
} catch (error) {
await showToast({
style: Toast.Style.Failure,
title: "Failed to fetch projects",
message:
error instanceof Error ? error.message : "Unknown error occurred",
})
throw error
}
}
export async function addProject(request: AddProjectRequest): Promise<Project> {
const response = await makeAuthenticatedRequest<Project>("/v3/projects", {
method: "POST",
body: JSON.stringify(request),
})
await showToast({
style: Toast.Style.Success,
title: "Project Added",
message: "Successfully added project to Supermemory",
})
return response
}
export async function addMemory(request: AddMemoryRequest): Promise<Memory> {
try {
const response = await makeAuthenticatedRequest<Memory>("/v3/documents", {
method: "POST",
body: JSON.stringify(request),
})
await showToast({
style: Toast.Style.Success,
title: "Memory Added",
message: "Successfully added memory to Supermemory",
})
return response
} catch (error) {
await showToast({
style: Toast.Style.Failure,
title: "Failed to add memory",
message:
error instanceof Error ? error.message : "Unknown error occurred",
})
throw error
}
}
export async function searchMemories(
request: SearchRequest,
): Promise<SearchResult[]> {
try {
const response = await makeAuthenticatedRequest<SearchResponse>(
"/v3/search",
{
method: "POST",
body: JSON.stringify(request),
},
)
return response.results || []
} catch (error) {
await showToast({
style: Toast.Style.Failure,
title: "Failed to search memories",
message:
error instanceof Error ? error.message : "Unknown error occurred",
})
throw error
}
}
// Helper function to check if API key is configured and valid
export async function fetchSettings(): Promise<object> {
const response = await makeAuthenticatedRequest<object>("/v3/settings")
return response
}

View file

@ -1,226 +0,0 @@
import {
ActionPanel,
Detail,
List,
Action,
Icon,
showToast,
Toast,
getSelectedText,
} from "@raycast/api"
import { useState, useEffect } from "react"
import { searchMemories, type SearchResult } from "./api"
import { usePromise } from "@raycast/utils"
import { withSupermemory } from "./withSupermemory"
const extractContent = (memory: SearchResult) => {
if (memory.chunks && memory.chunks.length > 0) {
return memory.chunks
.map((chunk: unknown) => {
if (typeof chunk === "string") return chunk
if (
chunk &&
typeof chunk === "object" &&
"content" in chunk &&
typeof chunk.content === "string"
)
return chunk.content
if (
chunk &&
typeof chunk === "object" &&
"text" in chunk &&
typeof chunk.text === "string"
)
return chunk.text
return ""
})
.filter(Boolean)
.join(" ")
}
return "No content available"
}
// metadata.url comes from ingested content, so only http(s) reaches the OS opener.
const extractUrl = (memory: SearchResult) => {
if (memory.metadata?.url && typeof memory.metadata.url === "string") {
const url = memory.metadata.url
try {
const parsed = new URL(url)
if (parsed.protocol === "https:" || parsed.protocol === "http:") {
return url
}
} catch {
return null
}
}
return null
}
const truncateContent = (content: string, maxLength = 100) => {
if (content.length <= maxLength) return content
return `${content.substring(0, maxLength)}...`
}
export default withSupermemory(Command)
function Command() {
const [searchText, setSearchText] = useState("")
useEffect(() => {
async function loadSelectedText() {
try {
const selectedText = await getSelectedText()
if (selectedText) {
setSearchText(selectedText)
}
} catch {
// No text selected or error getting selected text - silently fail
}
}
loadSelectedText()
}, [])
const { isLoading, data: searchResults = [] } = usePromise(
async (query: string) => {
const q = query.trim()
if (!q) return []
const results = await searchMemories({
q,
limit: 50,
})
if (!results.length) {
await showToast({
style: Toast.Style.Success,
title: "Search Complete",
message: "No memories found for your query",
})
}
return results
},
[searchText],
)
const formatDate = (dateString: string) => {
try {
return new Date(dateString).toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
})
} catch {
return "Unknown date"
}
}
const hasSearched = !isLoading && !searchResults.length
return (
<List
isLoading={isLoading}
onSearchTextChange={setSearchText}
searchText={searchText}
searchBarPlaceholder="Search your memories..."
throttle
>
{hasSearched && !searchText.trim() ? (
<List.EmptyView
icon={Icon.MagnifyingGlass}
title="Search Your Memories"
description="Type to search through your Supermemory collection"
/>
) : hasSearched ? (
<List.EmptyView
icon={Icon.Document}
title="No Memories Found"
description={`No memories found for "${searchText}"`}
/>
) : isLoading && searchText.trim() ? (
<List.EmptyView
icon={Icon.MagnifyingGlass}
title="Searching Your Memories"
/>
) : (
searchResults.map((memory) => {
const content = extractContent(memory)
const url = extractUrl(memory)
return (
<List.Item
key={memory.documentId}
icon={url ? Icon.Link : Icon.Document}
title={memory.title || "Untitled Memory"}
subtitle={{ value: truncateContent(content), tooltip: content }}
accessories={[
{ text: formatDate(memory.createdAt) },
...(memory.score
? [{ text: `${Math.round(memory.score * 100)}%` }]
: []),
]}
actions={
<ActionPanel>
<Action.Push
title="View Details"
target={<MemoryDetail memory={memory} />}
icon={Icon.Eye}
/>
<Action.CopyToClipboard
title="Copy Content"
shortcut={{ modifiers: ["cmd"], key: "c" }}
content={content}
/>
{url && (
<Action.OpenInBrowser
title="Open URL"
url={url}
shortcut={{ modifiers: ["cmd"], key: "o" }}
/>
)}
</ActionPanel>
}
/>
)
})
)}
</List>
)
}
function MemoryDetail({ memory }: { memory: SearchResult }) {
const content = extractContent(memory)
const url = extractUrl(memory)
const markdown = `
# ${memory.title || "Untitled Memory"}
${content}
---
**Created:** ${new Date(memory.createdAt).toLocaleString()}
${url ? `**URL:** ${url}` : ""}
${memory.score ? `**Relevance:** ${Math.round(memory.score * 100)}%` : ""}
`
return (
<Detail
markdown={markdown}
actions={
<ActionPanel>
<Action.CopyToClipboard
title="Copy Content"
shortcut={{ modifiers: ["cmd"], key: "c" }}
content={content}
/>
{url && (
<Action.OpenInBrowser
title="Open URL"
url={url}
shortcut={{ modifiers: ["cmd"], key: "o" }}
/>
)}
</ActionPanel>
}
/>
)
}

View file

@ -1,106 +0,0 @@
import {
ActionPanel,
List,
Action,
Icon,
Form,
useNavigation,
} from "@raycast/api"
import { useState } from "react"
import { fetchProjects, addProject } from "./api"
import {
FormValidation,
showFailureToast,
useCachedPromise,
useForm,
} from "@raycast/utils"
import { withSupermemory } from "./withSupermemory"
export default withSupermemory(Command)
function Command() {
const { isLoading, data: projects, mutate } = useCachedPromise(fetchProjects)
return (
<List isLoading={isLoading} searchBarPlaceholder="Search your projects">
{!isLoading && !projects?.length ? (
<List.EmptyView
title="No Projects Found"
actions={
<ActionPanel>
<Action.Push
icon={Icon.Plus}
title="Create Project"
target={<CreateProject />}
onPop={mutate}
/>
</ActionPanel>
}
/>
) : (
projects?.map((project) => (
<List.Item
key={project.id}
icon={Icon.Folder}
title={project.name}
subtitle={project.description}
accessories={[{ tag: project.containerTag }]}
actions={
<ActionPanel>
<Action.Push
icon={Icon.Plus}
title="Create Project"
target={<CreateProject />}
onPop={mutate}
/>
</ActionPanel>
}
/>
))
)}
</List>
)
}
function CreateProject() {
const { pop } = useNavigation()
const [isLoading, setIsLoading] = useState(false)
const { handleSubmit, itemProps } = useForm<{ name: string }>({
async onSubmit(values) {
setIsLoading(true)
try {
await addProject(values)
pop()
} catch (error) {
await showFailureToast(error, { title: "Failed to add project" })
} finally {
setIsLoading(false)
}
},
validation: {
name: FormValidation.Required,
},
})
return (
<Form
navigationTitle="Search Projects / Add"
isLoading={isLoading}
actions={
<ActionPanel>
<Action.SubmitForm
icon={Icon.Plus}
title="Create Project"
onSubmit={handleSubmit}
/>
</ActionPanel>
}
>
<Form.TextField
title="Name"
placeholder="My Awesome Project"
info="This will help you organize your memories"
{...itemProps.name}
/>
</Form>
)
}

View file

@ -1,48 +0,0 @@
import { usePromise } from "@raycast/utils"
import { fetchSettings } from "./api"
import {
Action,
ActionPanel,
Detail,
Icon,
List,
openExtensionPreferences,
} from "@raycast/api"
import type { ComponentType } from "react"
export function withSupermemory<P extends object>(Component: ComponentType<P>) {
return function SupermemoryWrappedComponent(props: P) {
const { isLoading, data } = usePromise(fetchSettings, [], {
failureToastOptions: {
title: "Invalid API Key",
message:
"Invalid API key. Please check your API key in preferences. Get a new one from https://supermemory.link/raycast",
},
})
if (!data) {
return isLoading ? (
<Detail isLoading />
) : (
<List>
<List.EmptyView
icon={Icon.ExclamationMark}
title="API Key Required"
description="Please configure your Supermemory API key to search memories"
actions={
<ActionPanel>
<Action
title="Open Extension Preferences"
onAction={openExtensionPreferences}
icon={Icon.Gear}
/>
</ActionPanel>
}
/>
</List>
)
}
return <Component {...props} />
}
}

View file

@ -1,16 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"include": ["src/**/*", "raycast-env.d.ts"],
"compilerOptions": {
"lib": ["ES2023"],
"module": "commonjs",
"target": "ES2023",
"strict": true,
"isolatedModules": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"jsx": "react-jsx",
"resolveJsonModule": true
}
}

View file

@ -16,7 +16,6 @@
"packageManager": "bun@1.3.6",
"workspaces": [
"apps/*",
"!apps/raycast-extension",
"!tools/test/chatapp",
"packages/*"
],

View file

@ -5,7 +5,6 @@ const SEARCH_MEMORY_SHORTCUT_URL =
"https://www.icloud.com/shortcuts/b0a132cc3c0d475196bc7014aa702a5c"
const ADD_MEMORY_SHORTCUT_URL =
"https://www.icloud.com/shortcuts/0fd3e855be444845b457f94c78c2c8d9"
const RAYCAST_EXTENSION_URL = "https://www.raycast.com/supermemory/supermemory"
const CHROME_EXTENSION_URL =
"https://chromewebstore.google.com/detail/supermemory/afpgkkipfdpeaflnpoaffkcankadgjfc"
const POKE_RECIPE_URL = "https://supermemory.link/poke"
@ -16,7 +15,6 @@ export {
SHARED_TEAM_BRAIN_TAG,
SEARCH_MEMORY_SHORTCUT_URL,
ADD_MEMORY_SHORTCUT_URL,
RAYCAST_EXTENSION_URL,
CHROME_EXTENSION_URL,
POKE_RECIPE_URL,
}