openfleet 0.3.2 → 0.3.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.
@@ -0,0 +1,2 @@
1
+ import type { AgentConfig } from "@opencode-ai/sdk";
2
+ export declare const actorAgent: AgentConfig;
@@ -0,0 +1,2 @@
1
+ import type { AgentConfig } from "@opencode-ai/sdk";
2
+ export declare const housekeepingAgent: AgentConfig;
@@ -0,0 +1,13 @@
1
+ export declare const agents: {
2
+ "Zeus (Orchestrator)": import("@opencode-ai/sdk").AgentConfig;
3
+ "Hera (Read-only Orchestrator)": import("@opencode-ai/sdk").AgentConfig;
4
+ "[Openfleet] Athena (Scout)": import("@opencode-ai/sdk").AgentConfig;
5
+ "[Openfleet] Apollo (Planner)": import("@opencode-ai/sdk").AgentConfig;
6
+ "[Openfleet] Hercules (Actor)": import("@opencode-ai/sdk").AgentConfig;
7
+ "[Openfleet] Chiron (Reviewer)": import("@opencode-ai/sdk").AgentConfig;
8
+ "[Openfleet] Mnemosyne (Reflector)": import("@opencode-ai/sdk").AgentConfig;
9
+ "[Openfleet] Hermes (Housekeeping)": import("@opencode-ai/sdk").AgentConfig;
10
+ };
11
+ export declare function configureAgents(config: {
12
+ agent?: Record<string, unknown>;
13
+ }): void;
@@ -0,0 +1,10 @@
1
+ export declare const AGENT_NAMES: {
2
+ readonly ORCHESTRATOR: "Zeus (Orchestrator)";
3
+ readonly READ_ONLY_ORCHESTRATOR: "Hera (Read-only Orchestrator)";
4
+ readonly SCOUT: "[Openfleet] Athena (Scout)";
5
+ readonly PLANNER: "[Openfleet] Apollo (Planner)";
6
+ readonly ACTOR: "[Openfleet] Hercules (Actor)";
7
+ readonly REVIEWER: "[Openfleet] Chiron (Reviewer)";
8
+ readonly REFLECTOR: "[Openfleet] Mnemosyne (Reflector)";
9
+ readonly HOUSEKEEPING: "[Openfleet] Hermes (Housekeeping)";
10
+ };
@@ -0,0 +1,2 @@
1
+ import type { AgentConfig } from "@opencode-ai/sdk";
2
+ export declare const orchestratorAgent: AgentConfig;
@@ -0,0 +1,2 @@
1
+ import type { AgentConfig } from "@opencode-ai/sdk";
2
+ export declare const plannerAgent: AgentConfig;
@@ -0,0 +1,2 @@
1
+ import type { AgentConfig } from "@opencode-ai/sdk";
2
+ export declare const readonlyOrchestratorAgent: AgentConfig;
@@ -0,0 +1,2 @@
1
+ import type { AgentConfig } from "@opencode-ai/sdk";
2
+ export declare const reflectorAgent: AgentConfig;
@@ -0,0 +1,2 @@
1
+ import type { AgentConfig } from "@opencode-ai/sdk";
2
+ export declare const reviewerAgent: AgentConfig;
@@ -0,0 +1,2 @@
1
+ import type { AgentConfig } from "@opencode-ai/sdk";
2
+ export declare const scoutAgent: AgentConfig;
@@ -0,0 +1,29 @@
1
+ export declare const OPENFLEET_DIR: string;
2
+ export declare const PATHS: {
3
+ readonly agentsMd: string;
4
+ readonly root: string;
5
+ readonly statusFile: string;
6
+ readonly templates: string;
7
+ readonly agents: string;
8
+ readonly agentZeus: string;
9
+ readonly agentAthena: string;
10
+ readonly agentApollo: string;
11
+ readonly agentHercules: string;
12
+ readonly agentChiron: string;
13
+ readonly agentMnemosyne: string;
14
+ readonly sessions: string;
15
+ readonly stories: string;
16
+ readonly docs: string;
17
+ readonly experience: string;
18
+ readonly runbooks: string;
19
+ readonly troubleshooting: string;
20
+ readonly lessons: string;
21
+ readonly blunders: string;
22
+ readonly standards: string;
23
+ readonly reviews: string;
24
+ readonly transcripts: string;
25
+ readonly logFile: string;
26
+ };
27
+ export declare function getCurrentWeek(): string;
28
+ export declare function getTodayDate(): string;
29
+ export declare function getFullDate(): string;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- import { Plugin } from '@opencode-ai/plugin';
2
-
1
+ import type { Plugin } from "@opencode-ai/plugin";
3
2
  declare const OpenfleetPlugin: Plugin;
4
-
5
- export { OpenfleetPlugin as default };
3
+ export default OpenfleetPlugin;
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ // @bun
1
2
  // src/config.ts
2
3
  import * as path from "path";
3
4
  var OPENFLEET_DIR = path.join(process.cwd(), ".openfleet");
@@ -548,7 +549,7 @@ In this example we see the following:
548
549
  8. this blocker did not stop us from completing the story, with a note to come
549
550
  back to token rotation afterwards
550
551
  9. we implemented a similar approach, and resolved the token rotation story
551
- 10. we raise the PR for review \u{1F973}
552
+ 10. we raise the PR for review \uD83E\uDD73
552
553
 
553
554
  And note that there can be MANY layers of task nesting (5 or more) and that's
554
555
  OK! It reflects the nature of software engineering, even when a task is well
@@ -644,7 +645,7 @@ git branch -d feat/<story>/<task>/<branch> # \u2190 DELETE merged branch
644
645
  **Keep these branches:**
645
646
  - active branches (currently working on)
646
647
  - parent branches (story/task not yet complete)
647
- - branches marked as \`\u23F8\uFE0F paused\` or \`\u{1F6A7} blocked\` (may resume later)
648
+ - branches marked as \`\u23F8\uFE0F paused\` or \`\uD83D\uDEA7 blocked\` (may resume later)
648
649
 
649
650
  **Delete these branches:**
650
651
  - branches marked as \`\u2705 merged\` in task tree
@@ -660,8 +661,8 @@ A story/task tree should show:
660
661
  - full hierarchy with proper indentation (task \u2192 subtask \u2192 branches)
661
662
  - current position: \`\u2190 YOU ARE HERE\`
662
663
  - active agents: \`\u2190 Hercules working\`
663
- - phase progress: R\u2705 H\u2705 L\u{1F504} I\u23F3
664
- - branch status: \u2705 merged, \u{1F6A7} blocked, \u23F8\uFE0F paused
664
+ - phase progress: R\u2705 H\u2705 L\uD83D\uDD04 I\u23F3
665
+ - branch status: \u2705 merged, \uD83D\uDEA7 blocked, \u23F8\uFE0F paused
665
666
  - git branch names
666
667
  - timestamps for key events
667
668
 
@@ -1090,15 +1091,13 @@ import { join as join2 } from "path";
1090
1091
  var LOG_FILE = join2(OPENFLEET_DIR, "openfleet.log");
1091
1092
  var dirVerified = false;
1092
1093
  function writeLog(level, msg, ...args) {
1093
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1094
+ const timestamp = new Date().toISOString();
1094
1095
  const formattedArgs = args.length > 0 ? ` ${JSON.stringify(args)}` : "";
1095
1096
  const logLine = `[${timestamp}] [${level.toUpperCase()}] ${msg}${formattedArgs}
1096
1097
  `;
1097
1098
  if (!dirVerified) {
1098
1099
  if (!existsSync(OPENFLEET_DIR)) {
1099
- throw new Error(
1100
- `[openfleet] .openfleet directory not initialized. Call initializeDirectories() first.`
1101
- );
1100
+ throw new Error(`[openfleet] .openfleet directory not initialized. Call initializeDirectories() first.`);
1102
1101
  }
1103
1102
  dirVerified = true;
1104
1103
  }
@@ -1156,7 +1155,8 @@ function formatEntryAsMarkdown(entry) {
1156
1155
  }
1157
1156
  lines.push("---");
1158
1157
  lines.push("");
1159
- return lines.join("\n");
1158
+ return lines.join(`
1159
+ `);
1160
1160
  }
1161
1161
  function formatUserMessage(entry) {
1162
1162
  const lines = [];
@@ -1194,7 +1194,7 @@ function formatToolResult(entry) {
1194
1194
  lines.push("### Output");
1195
1195
  lines.push(...formatOutput(entry.output));
1196
1196
  lines.push("");
1197
- if (entry.metadata !== void 0) {
1197
+ if (entry.metadata !== undefined) {
1198
1198
  lines.push("### Metadata");
1199
1199
  lines.push("```json");
1200
1200
  lines.push(JSON.stringify(entry.metadata, null, 2));
@@ -1233,39 +1233,40 @@ function formatOutput(output) {
1233
1233
  }
1234
1234
 
1235
1235
  // src/transcript/recorder.ts
1236
- var MAX_CACHE_SIZE = 1e3;
1237
- var toolInputCache = /* @__PURE__ */ new Map();
1236
+ var MAX_CACHE_SIZE = 1000;
1237
+ var toolInputCache = new Map;
1238
1238
  async function recordUserMessage(session, message, parts) {
1239
1239
  const entry = {
1240
1240
  type: "user",
1241
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1241
+ timestamp: new Date().toISOString(),
1242
1242
  content: extractContentFromParts(parts),
1243
1243
  parts
1244
1244
  };
1245
1245
  await appendTranscriptEntry(session.sessionID, entry, session.parentID);
1246
1246
  }
1247
- async function recordToolUse(session, tool2, callID, args) {
1247
+ async function recordToolUse(session, tool, callID, args) {
1248
1248
  if (toolInputCache.size >= MAX_CACHE_SIZE) {
1249
1249
  const oldestKey = toolInputCache.keys().next().value;
1250
- if (oldestKey) toolInputCache.delete(oldestKey);
1250
+ if (oldestKey)
1251
+ toolInputCache.delete(oldestKey);
1251
1252
  }
1252
1253
  toolInputCache.set(callID, args);
1253
1254
  const entry = {
1254
1255
  type: "tool_use",
1255
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1256
- tool: tool2,
1256
+ timestamp: new Date().toISOString(),
1257
+ tool,
1257
1258
  callID,
1258
1259
  input: args
1259
1260
  };
1260
1261
  await appendTranscriptEntry(session.sessionID, entry, session.parentID);
1261
1262
  }
1262
- async function recordToolResult(session, tool2, callID, output) {
1263
+ async function recordToolResult(session, tool, callID, output) {
1263
1264
  const cachedInput = toolInputCache.get(callID);
1264
1265
  toolInputCache.delete(callID);
1265
1266
  const entry = {
1266
1267
  type: "tool_result",
1267
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1268
- tool: tool2,
1268
+ timestamp: new Date().toISOString(),
1269
+ tool,
1269
1270
  callID,
1270
1271
  input: cachedInput,
1271
1272
  output: {
@@ -1277,14 +1278,16 @@ async function recordToolResult(session, tool2, callID, output) {
1277
1278
  await appendTranscriptEntry(session.sessionID, entry, session.parentID);
1278
1279
  }
1279
1280
  function extractContentFromParts(parts) {
1280
- return parts.filter((part) => part.type === "text").map((part) => part.text).join("\n");
1281
+ return parts.filter((part) => part.type === "text").map((part) => part.text).join(`
1282
+ `);
1281
1283
  }
1282
1284
 
1283
1285
  // src/transcript/hooks.ts
1284
- var sessionInfoCache = /* @__PURE__ */ new Map();
1286
+ var sessionInfoCache = new Map;
1285
1287
  async function getSessionInfo(ctx, sessionID) {
1286
1288
  const cached = sessionInfoCache.get(sessionID);
1287
- if (cached) return cached;
1289
+ if (cached)
1290
+ return cached;
1288
1291
  try {
1289
1292
  const { data: session } = await ctx.client.session.get({
1290
1293
  path: { id: sessionID },
@@ -1318,7 +1321,6 @@ function createTranscriptHooks(ctx) {
1318
1321
  }
1319
1322
  };
1320
1323
  }
1321
-
1322
1324
  // src/tools/save-conversation/counter.ts
1323
1325
  import * as fs from "fs";
1324
1326
  import * as path3 from "path";
@@ -1346,7 +1348,8 @@ async function getNextCounter(date) {
1346
1348
  }
1347
1349
  function parseFilename(filename) {
1348
1350
  const match = filename.match(FILENAME_PATTERN);
1349
- if (!match) return null;
1351
+ if (!match)
1352
+ return null;
1350
1353
  const [, counterStr, slug] = match;
1351
1354
  const counter = parseInt(counterStr, 10);
1352
1355
  if (isNaN(counter) || counter < 1 || counter > MAX_COUNTER) {
@@ -1360,7 +1363,7 @@ function ensureDateDir(dateDir) {
1360
1363
  }
1361
1364
  }
1362
1365
  function getCurrentDate() {
1363
- const now = /* @__PURE__ */ new Date();
1366
+ const now = new Date;
1364
1367
  const year = now.getUTCFullYear();
1365
1368
  const month = String(now.getUTCMonth() + 1).padStart(2, "0");
1366
1369
  const day = String(now.getUTCDate()).padStart(2, "0");
@@ -1431,7 +1434,7 @@ grep -A 5 "^## User Message" "${entry.transcriptPath}"
1431
1434
  `;
1432
1435
  }
1433
1436
  function formatTokens(entry) {
1434
- if (entry.tokensInput !== void 0 && entry.tokensOutput !== void 0) {
1437
+ if (entry.tokensInput !== undefined && entry.tokensOutput !== undefined) {
1435
1438
  const total = entry.tokensInput + entry.tokensOutput;
1436
1439
  return `${total.toLocaleString()} (${entry.tokensInput.toLocaleString()} in, ${entry.tokensOutput.toLocaleString()} out)`;
1437
1440
  }
@@ -1444,7 +1447,7 @@ function ensureDateDir2(dateDir) {
1444
1447
  }
1445
1448
  function calculateDuration(startTime, endTime) {
1446
1449
  const diffMs = endTime.getTime() - startTime.getTime();
1447
- const diffMinutes = Math.floor(diffMs / 1e3 / 60);
1450
+ const diffMinutes = Math.floor(diffMs / 1000 / 60);
1448
1451
  if (diffMinutes < 60) {
1449
1452
  return `${diffMinutes} minute${diffMinutes !== 1 ? "s" : ""}`;
1450
1453
  }
@@ -1573,7 +1576,7 @@ The tool will:
1573
1576
  note: tool.schema.string().optional().describe("Optional note about what was accomplished")
1574
1577
  },
1575
1578
  async execute(args, context) {
1576
- const startTime = /* @__PURE__ */ new Date();
1579
+ const startTime = new Date;
1577
1580
  const { sessionID } = context;
1578
1581
  try {
1579
1582
  const { data: messages } = await ctx.client.session.messages({
@@ -1589,7 +1592,7 @@ The tool will:
1589
1592
  const title = slugToTitle(slug);
1590
1593
  const date = getCurrentDate();
1591
1594
  const counter = await getNextCounter(date);
1592
- const endTime = /* @__PURE__ */ new Date();
1595
+ const endTime = new Date;
1593
1596
  const duration = calculateDuration(startTime, endTime);
1594
1597
  const transcriptPath = getTranscriptPath(sessionID);
1595
1598
  const summary = await generateSummary(messages, slug);
@@ -1669,9 +1672,7 @@ async function generateSummary(messages, slug) {
1669
1672
  const messageCount = messages.length;
1670
1673
  const userMessages = messages.filter((m) => m.info.role === "user").length;
1671
1674
  const assistantMessages = messages.filter((m) => m.info.role === "assistant").length;
1672
- return `Work session focused on: ${slugToTitle(
1673
- slug
1674
- )}. Exchanged ${messageCount} messages (${userMessages} user, ${assistantMessages} assistant). See transcript for full details.`;
1675
+ return `Work session focused on: ${slugToTitle(slug)}. Exchanged ${messageCount} messages (${userMessages} user, ${assistantMessages} assistant). See transcript for full details.`;
1675
1676
  }
1676
1677
  function buildContextString(messages, note) {
1677
1678
  if (note) {
@@ -1691,11 +1692,7 @@ function buildContextString(messages, note) {
1691
1692
  import * as fs3 from "fs";
1692
1693
  import * as path5 from "path";
1693
1694
  import { fileURLToPath } from "url";
1694
- var TEMPLATES_DIR = path5.join(
1695
- path5.dirname(fileURLToPath(import.meta.url)),
1696
- "templates",
1697
- ".openfleet"
1698
- );
1695
+ var TEMPLATES_DIR = path5.join(path5.dirname(fileURLToPath(import.meta.url)), "templates", ".openfleet");
1699
1696
  function initializeDirectories() {
1700
1697
  if (fs3.existsSync(OPENFLEET_DIR)) {
1701
1698
  return;
@@ -1734,8 +1731,7 @@ function showSpinnerToast(ctx, options) {
1734
1731
  variant: options.variant || "info",
1735
1732
  duration: frameInterval + 50
1736
1733
  }
1737
- }).catch(() => {
1738
- });
1734
+ }).catch(() => {});
1739
1735
  await new Promise((resolve) => setTimeout(resolve, frameInterval));
1740
1736
  frameIndex++;
1741
1737
  }
@@ -1760,9 +1756,11 @@ var OpenfleetPlugin = async (ctx) => {
1760
1756
  configureAgents(config);
1761
1757
  },
1762
1758
  event: async ({ event }) => {
1763
- if (event.type !== "session.created") return;
1759
+ if (event.type !== "session.created")
1760
+ return;
1764
1761
  const props = event.properties;
1765
- if (props?.info?.parentID) return;
1762
+ if (props?.info?.parentID)
1763
+ return;
1766
1764
  setTimeout(async () => {
1767
1765
  await showFleetToast(ctx);
1768
1766
  }, 0);
@@ -1776,10 +1774,10 @@ async function showFleetToast(ctx) {
1776
1774
  message: "The Openfleet plugin is now at play.",
1777
1775
  variant: "info"
1778
1776
  });
1779
- await sleep(5e3);
1777
+ await sleep(5000);
1780
1778
  await stopSpinner();
1781
1779
  }
1782
- var index_default = OpenfleetPlugin;
1780
+ var src_default = OpenfleetPlugin;
1783
1781
  export {
1784
- index_default as default
1782
+ src_default as default
1785
1783
  };
@@ -0,0 +1,12 @@
1
+ import Anthropic from "@anthropic-ai/sdk";
2
+ /**
3
+ * Gets or creates a singleton Anthropic client instance.
4
+ *
5
+ * This ensures we reuse the same client across the application
6
+ * instead of creating multiple instances.
7
+ *
8
+ * Example:
9
+ * >>> const client = getAnthropicClient();
10
+ * >>> const response = await client.messages.create({...});
11
+ */
12
+ export declare function getAnthropicClient(): Anthropic;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Sleeps for time_ms number of milliseconds
3
+ */
4
+ export declare function sleep(time_ms: number): Promise<unknown>;
@@ -0,0 +1,8 @@
1
+ type LogLevel = "debug" | "info" | "warn" | "error";
2
+ declare const logger: {
3
+ debug: (msg: string, ...args: unknown[]) => void;
4
+ info: (msg: string, ...args: unknown[]) => void;
5
+ warn: (msg: string, ...args: unknown[]) => void;
6
+ error: (msg: string, ...args: unknown[]) => void;
7
+ };
8
+ export { logger, type LogLevel };
@@ -0,0 +1,24 @@
1
+ export declare const models: {
2
+ readonly bedrock: {
3
+ readonly sonnet: "amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0";
4
+ readonly opus: "amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0";
5
+ readonly haiku: "amazon-bedrock/anthropic.claude-haiku-4-5-20251001-v1:0";
6
+ };
7
+ readonly anthropic: {
8
+ readonly sonnet: "anthropic/claude-sonnet-4-5";
9
+ readonly opus: "anthropic/claude-opus-4-5";
10
+ readonly haiku: "anthropic/claude-haiku-4-5";
11
+ };
12
+ readonly openai: {
13
+ readonly gpt5: "openai/gpt-5.2";
14
+ readonly o4Mini: "openai/o4-mini";
15
+ readonly o3: "openai/o3";
16
+ };
17
+ readonly google: {
18
+ readonly gemini3Pro: "google/gemini-3-pro-high";
19
+ readonly gemini3Flash: "google/gemini-3-flash";
20
+ readonly gemini25Pro: "google/gemini-2.5-pro";
21
+ };
22
+ };
23
+ export declare const defaultModel: "anthropic/claude-sonnet-4-5";
24
+ export declare const smallModel: "amazon-bedrock/anthropic.claude-haiku-4-5-20251001-v1:0";
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Gets the next counter for a given date.
3
+ *
4
+ * This function:
5
+ * 1. looks inside the date subdirectory (sessions/YYYY-MM-DD/)
6
+ * 2. extracts all counters from files matching NNN_slug.md pattern
7
+ * 3. finds the highest counter
8
+ * 4. returns next counter (highest + 1), zero-padded to 3 digits
9
+ *
10
+ * Example:
11
+ * >>> const counter = await getNextCounter("2025-12-23");
12
+ * >>> counter
13
+ * '003'
14
+ */
15
+ export declare function getNextCounter(date: string): Promise<string>;
16
+ /**
17
+ * Gets current date in YYYY-MM-DD format (UTC).
18
+ */
19
+ export declare function getCurrentDate(): string;
20
+ /**
21
+ * Validates date format (YYYY-MM-DD).
22
+ */
23
+ export declare function isValidDateFormat(date: string): boolean;
24
+ /**
25
+ * Gets all session files for a specific date.
26
+ */
27
+ export declare function getSessionsForDate(date: string): string[];
28
+ /**
29
+ * Builds filename from counter and slug (NNN_slug.md format).
30
+ */
31
+ export declare function buildFilename(counter: string, slug: string): string;
@@ -0,0 +1,10 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ export declare function createSaveConversationTool(ctx: PluginInput): {
3
+ description: string;
4
+ args: {
5
+ note: import("zod").ZodOptional<import("zod").ZodString>;
6
+ };
7
+ execute(args: {
8
+ note?: string | undefined;
9
+ }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
10
+ };
@@ -0,0 +1,17 @@
1
+ import type { SessionEntry } from "./types";
2
+ /**
3
+ * Writes a session entry to the sessions directory.
4
+ *
5
+ * This function:
6
+ * 1. ensures date subdirectory exists (sessions/YYYY-MM-DD/)
7
+ * 2. builds filename from counter and slug (NNN_slug.md)
8
+ * 3. generates enhanced session content
9
+ * 4. writes file atomically
10
+ * 5. returns full file path
11
+ */
12
+ export declare function writeSession(entry: SessionEntry): string;
13
+ /**
14
+ * Calculates session duration from timestamp metadata.
15
+ * Returns formatted string like "45 minutes" or "2 hours 15 minutes".
16
+ */
17
+ export declare function calculateDuration(startTime: Date, endTime: Date): string;
@@ -0,0 +1,14 @@
1
+ import type { SlugContext } from "./types";
2
+ /**
3
+ * Generates a semantic slug using simple context string.
4
+ *
5
+ * This function takes a context string (usually from user's note or session summary)
6
+ * and generates a kebab-case slug using Claude Haiku.
7
+ *
8
+ * Example:
9
+ * >>> const slug = await generateSlug("Implemented user authentication system");
10
+ * >>> slug
11
+ * 'implement-user-auth'
12
+ */
13
+ export declare function generateSlug(contextString: string, context?: SlugContext): Promise<string>;
14
+ export declare function slugToTitle(slug: string): string;
@@ -0,0 +1,28 @@
1
+ export interface SaveConversationArgs {
2
+ note?: string;
3
+ }
4
+ export interface SessionEntry {
5
+ sessionID: string;
6
+ transcriptPath: string;
7
+ savedAt: string;
8
+ date: string;
9
+ counter: string;
10
+ slug: string;
11
+ title: string;
12
+ summary: string;
13
+ note?: string;
14
+ duration?: string;
15
+ messageCount: number;
16
+ tokensBefore: number;
17
+ tokensInput?: number;
18
+ tokensOutput?: number;
19
+ }
20
+ export interface SlugContext {
21
+ maxMessages?: number;
22
+ maxContextChars?: number;
23
+ }
24
+ export interface CounterInfo {
25
+ date: string;
26
+ counter: string;
27
+ highestFound: number;
28
+ }
@@ -0,0 +1,25 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ export declare function createTranscriptHooks(ctx: PluginInput): {
3
+ "chat.message": (input: {
4
+ sessionID: string;
5
+ }, output: {
6
+ message: unknown;
7
+ parts: unknown[];
8
+ }) => Promise<void>;
9
+ "tool.execute.before": (input: {
10
+ sessionID: string;
11
+ tool: string;
12
+ callID: string;
13
+ }, output: {
14
+ args: unknown;
15
+ }) => Promise<void>;
16
+ "tool.execute.after": (input: {
17
+ sessionID: string;
18
+ tool: string;
19
+ callID: string;
20
+ }, output: {
21
+ title: string;
22
+ output: string;
23
+ metadata?: unknown;
24
+ }) => Promise<void>;
25
+ };
@@ -0,0 +1,4 @@
1
+ export { createTranscriptHooks } from "./hooks";
2
+ export { getTranscriptPath } from "./recorder";
3
+ export type { SessionInfo } from "./recorder";
4
+ export type { TranscriptEntry, UserMessageEntry, ToolUseEntry, ToolResultEntry } from "./types";
@@ -0,0 +1,14 @@
1
+ import type { Part, UserMessage } from "@opencode-ai/sdk";
2
+ import { getTranscriptPath } from "./writer";
3
+ export interface SessionInfo {
4
+ sessionID: string;
5
+ parentID?: string;
6
+ }
7
+ export declare function recordUserMessage(session: SessionInfo, message: UserMessage, parts: Part[]): Promise<void>;
8
+ export declare function recordToolUse(session: SessionInfo, tool: string, callID: string, args: unknown): Promise<void>;
9
+ export declare function recordToolResult(session: SessionInfo, tool: string, callID: string, output: {
10
+ title: string;
11
+ output: string;
12
+ metadata?: unknown;
13
+ }): Promise<void>;
14
+ export { getTranscriptPath };
@@ -0,0 +1,23 @@
1
+ export interface BaseEntry {
2
+ timestamp: string;
3
+ }
4
+ export interface UserMessageEntry extends BaseEntry {
5
+ type: "user";
6
+ content: string;
7
+ parts: unknown[];
8
+ }
9
+ export interface ToolUseEntry extends BaseEntry {
10
+ type: "tool_use";
11
+ tool: string;
12
+ callID: string;
13
+ input: unknown;
14
+ }
15
+ export interface ToolResultEntry extends BaseEntry {
16
+ type: "tool_result";
17
+ tool: string;
18
+ callID: string;
19
+ input: unknown;
20
+ output: unknown;
21
+ metadata?: unknown;
22
+ }
23
+ export type TranscriptEntry = UserMessageEntry | ToolUseEntry | ToolResultEntry;
@@ -0,0 +1,3 @@
1
+ import type { TranscriptEntry } from "./types";
2
+ export declare function appendTranscriptEntry(sessionID: string, entry: TranscriptEntry, parentID?: string): Promise<void>;
3
+ export declare function getTranscriptPath(sessionID: string, parentID?: string): string;
@@ -0,0 +1 @@
1
+ export declare function initializeDirectories(): void;
@@ -0,0 +1,40 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ type ToastVariant = "info" | "success" | "warning" | "error";
3
+ interface ToastOptions {
4
+ title: string;
5
+ message: string;
6
+ variant: ToastVariant;
7
+ duration: number;
8
+ }
9
+ /**
10
+ * Shows a static toast notification.
11
+ */
12
+ export declare function showToast(ctx: PluginInput, options: ToastOptions): Promise<void>;
13
+ /**
14
+ * Shows an animated toast with spinner until stopped.
15
+ *
16
+ * Returns a function to stop the spinner and optionally show a final toast.
17
+ */
18
+ export declare function showSpinnerToast(ctx: PluginInput, options: {
19
+ title: string;
20
+ message: string;
21
+ variant?: ToastVariant;
22
+ }): () => Promise<void>;
23
+ /**
24
+ * Shows spinner toast until promise resolves, then shows completion toast.
25
+ */
26
+ export declare function showSpinnerUntil<T>(ctx: PluginInput, promise: Promise<T>, options: {
27
+ spinner: {
28
+ title: string;
29
+ message: string;
30
+ };
31
+ success: {
32
+ title: string;
33
+ message: string;
34
+ };
35
+ error: {
36
+ title: string;
37
+ message: string;
38
+ };
39
+ }): Promise<T>;
40
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openfleet",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "SPARR framework agents + infinite context for OpenCode",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,7 +19,7 @@
19
19
  "url": "git+https://github.com/scottsus/openfleet.git"
20
20
  },
21
21
  "scripts": {
22
- "build": "rm -rf dist && tsup src/index.ts --format esm --dts && cp -r src/templates dist/",
22
+ "build": "rm -rf dist && bun build src/index.ts --outdir dist --target bun --format esm --external @opencode-ai/plugin --external @opencode-ai/sdk --external @anthropic-ai/sdk && tsc --emitDeclarationOnly && cp -r src/templates dist/",
23
23
  "dev": "tsup src/index.ts --format esm --dts --watch",
24
24
  "typecheck": "tsc --noEmit",
25
25
  "format": "prettier --write .",
@@ -30,12 +30,15 @@
30
30
  "lint-staged": {
31
31
  "*": "prettier --write --ignore-unknown"
32
32
  },
33
- "dependencies": {
33
+ "peerDependencies": {
34
34
  "@anthropic-ai/sdk": "^0.71.2",
35
- "@opencode-ai/plugin": "^1.0.191",
36
- "@opencode-ai/sdk": "^1.0.191"
35
+ "@opencode-ai/plugin": "~1.0.224",
36
+ "@opencode-ai/sdk": "~1.0.224"
37
37
  },
38
38
  "devDependencies": {
39
+ "@anthropic-ai/sdk": "^0.71.2",
40
+ "@opencode-ai/plugin": "~1.0.224",
41
+ "@opencode-ai/sdk": "~1.0.224",
39
42
  "@ianvs/prettier-plugin-sort-imports": "^4.7.0",
40
43
  "@types/node": "^22",
41
44
  "husky": "^9.1.7",