Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .continue/agents/architecture.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Architecture Review
description: Ensure changes align with single-file plugin architecture
triggers:
- pull_request

steps:
- name: Review architecture
prompt: |
Review the changes for architectural consistency:

**Project constraints:**
- Single-file plugin architecture (index.ts is the main entry)
- Event-driven hooks (tool.execute.before, event listeners)
- Plugin should remain lightweight

**Watch for:**
- Adding new files when the feature could fit in index.ts
- Introducing heavy dependencies
- Breaking the plugin's event-driven pattern

If changes violate these constraints, suggest alternatives that align with the architecture.
26 changes: 26 additions & 0 deletions .continue/agents/code-style.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Code Style Review
description: Enforce code style conventions from AGENTS.md
triggers:
- pull_request

steps:
- name: Check code style
prompt: |
Review the changed TypeScript files against these conventions from AGENTS.md:

**Imports:**
- Named imports preferred (not `import *`)
- Type imports must be separate: `import type { X }`

**Naming:**
- camelCase for variables and functions
- PascalCase for types, interfaces, classes, and exports

**Async:**
- Prefer async/await over raw promises

**Error Handling:**
- Try-catch blocks with proper cleanup
- No silent failures (empty catch blocks)

For each violation, leave a comment on the specific line explaining the issue and suggesting a fix.
24 changes: 24 additions & 0 deletions .continue/agents/commit-format.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Commit Message Format
description: Ensure commit messages follow Conventional Commits
triggers:
- pull_request

steps:
- name: Check commit messages
prompt: |
Review the commit messages in this PR for Conventional Commits format:

**Required format:** `type: description`

**Valid types:**
- `feat:` - New feature
- `fix:` - Bug fix
- `docs:` - Documentation changes
- `chore:` - Maintenance tasks
- `refactor:` - Code refactoring
- `test:` - Adding/modifying tests
- `perf:` - Performance improvements

**Breaking changes:** Use `!` suffix (e.g., `feat!: change API`)

If any commit message doesn't follow this format, leave a comment suggesting the correct format.
20 changes: 20 additions & 0 deletions .continue/agents/typecheck.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: TypeScript Type Check
description: Verify TypeScript compiles without errors
triggers:
- pull_request

steps:
- name: Run typecheck
run: npm run typecheck
fail_on_error: true

- name: Review for type issues
prompt: |
Review the changed files for TypeScript type issues:

- Check for implicit `any` types that should be explicit
- Verify return types are consistent with function signatures
- Ensure type imports use `import type { X }` syntax
- Look for potential null/undefined issues

If issues found, leave a comment on the specific line.
128 changes: 115 additions & 13 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ export const SessionPlugin: Plugin = async (ctx) => {
},
})
} catch (error) {
// Silently fail - error handling could be added here if needed
console.error(`Failed to send pending message to session ${sessionID}:`, error)
}
return
}
Expand Down Expand Up @@ -284,7 +284,7 @@ export const SessionPlugin: Plugin = async (ctx) => {
},
})
} catch (error) {
// Silently fail - error handling could be added here if needed
console.error(`Failed to send compacted message to session ${sessionID}:`, error)
}
}
}
Expand Down Expand Up @@ -321,6 +321,11 @@ ${agentList}

If omitted, continues with current agent.

NAME PARAMETER (optional):

Custom name for new or forked sessions. Helps identify sessions in the session list.
Only used in 'new' and 'fork' modes. Ignored in 'message' and 'compact' modes.

EXAMPLES:

# COLLABORATE: Multi-agent problem solving
Expand All @@ -331,13 +336,14 @@ EXAMPLES:
})
# Plan reviews, responds, can pass back to build

# HANDOFF: Clean phase transition
# HANDOFF: Clean phase transition with named session
session({
mode: "new",
agent: "researcher",
name: "API Research",
text: "Research best practices for API design"
})
# Fresh session, no baggage from previous implementation work
# Fresh session titled "API Research", no baggage from previous work

# COMPRESS: Long conversation with handoff
session({
Expand All @@ -347,18 +353,20 @@ EXAMPLES:
})
# Compacts history, adds handoff context, plan agent responds

# PARALLELIZE: Try multiple approaches
# PARALLELIZE: Named forked sessions for comparison
session({
mode: "fork",
agent: "build",
agent: "plan",
name: "Redux Architecture",
text: "Implement using Redux"
})
session({
mode: "fork",
agent: "build",
agent: "plan",
name: "Context API Architecture",
text: "Implement using Context API"
})
# Two independent sessions, compare results
# Two named sessions for easy identification, compare results
`,

args: {
Expand All @@ -374,6 +382,12 @@ EXAMPLES:
.describe(
"Primary agent name (e.g., 'build', 'plan') for agent switching",
),
name: tool.schema
.string()
.optional()
.describe(
"Custom name for the session (used in 'new' and 'fork' modes)",
),
},

async execute(args, toolCtx) {
Expand All @@ -388,11 +402,12 @@ EXAMPLES:

case "new":
// Create session via SDK for agent control
const sessionTitle = args.name || (args.agent
? `Session via ${args.agent}`
: "New session")
const newSession = await ctx.client.session.create({
body: {
title: args.agent
? `Session via ${args.agent}`
: "New session",
title: sessionTitle,
},
})

Expand All @@ -405,7 +420,9 @@ EXAMPLES:
},
})

return `New session created with ${args.agent || "build"} agent (ID: ${newSession.data.id})`
return args.name
? `New session "${args.name}" created with ${args.agent || "build"} agent (ID: ${newSession.data.id})`
: `New session created with ${args.agent || "build"} agent (ID: ${newSession.data.id})`

case "compact":
try {
Expand Down Expand Up @@ -470,6 +487,13 @@ EXAMPLES:
body: {},
})

if (args.name) {
await ctx.client.session.update({
path: { id: forkedSession.data.id },
body: { title: args.name },
})
}

// Send new message in forked session
await ctx.client.session.prompt({
path: { id: forkedSession.data.id },
Expand All @@ -479,7 +503,9 @@ EXAMPLES:
},
})

return `Forked session with ${args.agent || "build"} agent - history preserved (ID: ${forkedSession.data.id})`
return args.name
? `Forked session "${args.name}" with ${args.agent || "build"} agent - history preserved (ID: ${forkedSession.data.id})`
: `Forked session with ${args.agent || "build"} agent - history preserved (ID: ${forkedSession.data.id})`
}
} catch (error) {
const message =
Expand All @@ -498,6 +524,82 @@ EXAMPLES:
}
},
}),

session_list: tool({
description: `List all OpenCode sessions with optional filtering.

Returns a table of sessions with ID, title, and creation date.`,

args: {
limit: tool.schema
.number()
.optional()
.describe("Maximum number of sessions to return"),
from_date: tool.schema
.string()
.optional()
.describe("Filter sessions from this date (ISO 8601 format)"),
to_date: tool.schema
.string()
.optional()
.describe("Filter sessions until this date (ISO 8601 format)"),
},

async execute(args) {
try {
const response = await ctx.client.session.list()

if (!response.data) {
return "No sessions found."
}

let sessions = response.data

if (args.from_date) {
const fromDate = new Date(args.from_date).getTime()
sessions = sessions.filter((s) => {
const created = s.time?.created
? new Date(s.time.created).getTime()
: 0
return created >= fromDate
})
}

if (args.to_date) {
const toDate = new Date(args.to_date).getTime()
sessions = sessions.filter((s) => {
const created = s.time?.created
? new Date(s.time.created).getTime()
: 0
return created <= toDate
})
}

if (args.limit && args.limit > 0) {
sessions = sessions.slice(0, args.limit)
}

if (sessions.length === 0) {
return "No sessions found matching criteria."
}

const rows = sessions.map((s) => {
const id = s.id || "unknown"
const title = s.title || "Untitled"
const created = s.time?.created
? new Date(s.time.created).toISOString().split("T")[0]
: "N/A"
return `| ${id} | ${title} | ${created} |`
})

return ["| Session ID | Title | Created |", "|------------|-------|---------|", ...rows].join("\n")
} catch (error) {
const message =
error instanceof Error ? error.message : String(error)
return `Error listing sessions: ${message}`
}
},
}),
},
}
}