opencode-archiver 0.1.0

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,9 @@
1
+ import type { Session, Message, Part, ArchiverConfig } from "./types.js";
2
+ export declare function getArchiveDir(config: ArchiverConfig): string;
3
+ export declare function generateFilename(session: Session): string;
4
+ export declare function shouldArchive(agent: string | undefined, config: ArchiverConfig): boolean;
5
+ export declare function archiveSession(session: Session, messages: {
6
+ info: Message;
7
+ parts: Part[];
8
+ }[], config: ArchiverConfig, agent?: string): Promise<void>;
9
+ //# sourceMappingURL=archiver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archiver.d.ts","sourceRoot":"","sources":["../src/archiver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAcxE,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAE5D;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAWzD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAGxF;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,EAAE,EAC5C,MAAM,EAAE,cAAc,EACtB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAyBf"}
@@ -0,0 +1,49 @@
1
+ import { promises as fs } from "fs";
2
+ import * as path from "path";
3
+ import * as os from "os";
4
+ import { isArchived, markArchived } from "./tracking.js";
5
+ import { sessionToMarkdown } from "./formatter.js";
6
+ function expandPath(p) {
7
+ if (p.startsWith("~")) {
8
+ return path.join(os.homedir(), p.slice(1));
9
+ }
10
+ return p;
11
+ }
12
+ export function getArchiveDir(config) {
13
+ return expandPath(config.outputDir);
14
+ }
15
+ export function generateFilename(session) {
16
+ const date = session.time?.created
17
+ ? new Date(session.time.created)
18
+ : new Date();
19
+ const dateStr = date.toISOString()
20
+ .slice(0, 16)
21
+ .replace("T", "_")
22
+ .replace(":", "-");
23
+ return `${dateStr}_${session.id}.md`;
24
+ }
25
+ export function shouldArchive(agent, config) {
26
+ if (!agent)
27
+ return false;
28
+ return config.agents.includes(agent);
29
+ }
30
+ export async function archiveSession(session, messages, config, agent) {
31
+ const archiveDir = getArchiveDir(config);
32
+ const trackingPath = path.join(archiveDir, ".archive_tracking.json");
33
+ if (await isArchived(trackingPath, session.id)) {
34
+ return;
35
+ }
36
+ await fs.mkdir(archiveDir, { recursive: true });
37
+ const filename = generateFilename(session);
38
+ const filepath = path.join(archiveDir, filename);
39
+ const partsByMessage = new Map();
40
+ const messageList = [];
41
+ for (const msg of messages) {
42
+ messageList.push(msg.info);
43
+ partsByMessage.set(msg.info.id, msg.parts);
44
+ }
45
+ const markdown = sessionToMarkdown(session, messageList, partsByMessage, config, agent);
46
+ await fs.writeFile(filepath, markdown);
47
+ await markArchived(trackingPath, session.id);
48
+ }
49
+ //# sourceMappingURL=archiver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archiver.js","sourceRoot":"","sources":["../src/archiver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAA;AACnC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAElD,SAAS,UAAU,CAAC,CAAS;IAC3B,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IAC5C,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAsB;IAClD,OAAO,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO;QAChC,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;QAChC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAA;IAEd,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE;SAC/B,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC;SACjB,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IAEpB,OAAO,GAAG,OAAO,IAAI,OAAO,CAAC,EAAE,KAAK,CAAA;AACtC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAyB,EAAE,MAAsB;IAC7E,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IACxB,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AACtC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAgB,EAChB,QAA4C,EAC5C,MAAsB,EACtB,KAAc;IAEd,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;IACxC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAA;IAEpE,IAAI,MAAM,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;QAC/C,OAAM;IACR,CAAC;IAED,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAE/C,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;IAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;IAEhD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAA;IAChD,MAAM,WAAW,GAAc,EAAE,CAAA;IAEjC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAC1B,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,CAAA;IAC5C,CAAC;IAED,MAAM,QAAQ,GAAG,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;IAEvF,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IACtC,MAAM,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,EAAE,CAAC,CAAA;AAC9C,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ArchiverConfig } from "./types.js";
2
+ export declare const defaultConfig: Partial<ArchiverConfig>;
3
+ export declare function loadConfig(userConfig: unknown): ArchiverConfig;
4
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,eAAO,MAAM,aAAa,EAAE,OAAO,CAAC,cAAc,CAIjD,CAAA;AAED,wBAAgB,UAAU,CAAC,UAAU,EAAE,OAAO,GAAG,cAAc,CAG9D"}
package/dist/config.js ADDED
@@ -0,0 +1,10 @@
1
+ export const defaultConfig = {
2
+ outputDir: "~/.opencode-archives",
3
+ includeTools: true,
4
+ includeThinking: false,
5
+ };
6
+ export function loadConfig(userConfig) {
7
+ const config = { ...defaultConfig, ...userConfig };
8
+ return config;
9
+ }
10
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAA4B;IACpD,SAAS,EAAE,sBAAsB;IACjC,YAAY,EAAE,IAAI;IAClB,eAAe,EAAE,KAAK;CACvB,CAAA;AAED,MAAM,UAAU,UAAU,CAAC,UAAmB;IAC5C,MAAM,MAAM,GAAG,EAAE,GAAG,aAAa,EAAE,GAAI,UAAsC,EAAoB,CAAA;IACjG,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { Session, Message, Part, ArchiverConfig } from "./types.js";
2
+ export declare function formatSessionHeader(session: Session, agent?: string): string;
3
+ export declare function formatMessage(message: Message, parts: Part[], config: ArchiverConfig): string;
4
+ export declare function sessionToMarkdown(session: Session, messages: Message[], partsByMessage: Map<string, Part[]>, config: ArchiverConfig, agent?: string): string;
5
+ //# sourceMappingURL=formatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAqC,MAAM,YAAY,CAAA;AAE3G,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAc5E;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAS7F;AAqCD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAU5J"}
@@ -0,0 +1,62 @@
1
+ export function formatSessionHeader(session, agent) {
2
+ const date = session.time?.created
3
+ ? new Date(session.time.created).toISOString().slice(0, 16).replace("T", " ")
4
+ : "Unknown";
5
+ return `# ${session.title ?? "Untitled Session"}
6
+
7
+ | Field | Value |
8
+ |-------|-------|
9
+ | Session ID | ${session.id} |
10
+ | Agent | ${agent ?? "unknown"} |
11
+ | Created | ${date} |
12
+ | Directory | ${session.directory ?? "N/A"} |
13
+ `;
14
+ }
15
+ export function formatMessage(message, parts, config) {
16
+ const role = message.role === "user" ? "User" : "Assistant";
17
+ let content = `### ${role}\n\n`;
18
+ for (const part of parts) {
19
+ content += formatPart(part, config);
20
+ }
21
+ return content;
22
+ }
23
+ function formatPart(part, config) {
24
+ switch (part.type) {
25
+ case "text":
26
+ return formatTextPart(part);
27
+ case "tool":
28
+ return config.includeTools ? formatToolPart(part) : "";
29
+ case "reasoning":
30
+ return config.includeThinking ? formatReasoningPart(part) : "";
31
+ default:
32
+ return "";
33
+ }
34
+ }
35
+ function formatTextPart(part) {
36
+ return `${part.text}\n\n`;
37
+ }
38
+ function formatToolPart(part) {
39
+ const state = part.state;
40
+ let result = `**Tool: ${part.tool}**\n\n`;
41
+ if (state.status === "completed") {
42
+ result += `Input:\n\`\`\`json\n${JSON.stringify(state.input, null, 2)}\n\`\`\`\n\n`;
43
+ result += `Output:\n\`\`\`\n${state.output}\n\`\`\`\n\n`;
44
+ }
45
+ else if (state.status === "error") {
46
+ result += `Error: ${state.error}\n\n`;
47
+ }
48
+ return result;
49
+ }
50
+ function formatReasoningPart(part) {
51
+ return `> **Reasoning:**\n> ${part.text.split("\n").join("\n> ")}\n\n`;
52
+ }
53
+ export function sessionToMarkdown(session, messages, partsByMessage, config, agent) {
54
+ let markdown = formatSessionHeader(session, agent);
55
+ markdown += "\n---\n\n## Conversation\n\n";
56
+ for (const message of messages) {
57
+ const parts = partsByMessage.get(message.id) ?? [];
58
+ markdown += formatMessage(message, parts, config);
59
+ }
60
+ return markdown;
61
+ }
62
+ //# sourceMappingURL=formatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+ export declare const ConversationArchiver: Plugin;
3
+ export default ConversationArchiver;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAA;AAc9D,eAAO,MAAM,oBAAoB,EAAE,MAgBlC,CAAA;AAuDD,eAAe,oBAAoB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,68 @@
1
+ import { loadConfig } from "./config.js";
2
+ import { shouldArchive, archiveSession } from "./archiver.js";
3
+ export const ConversationArchiver = async ({ client }) => {
4
+ const configResult = await client.config.get();
5
+ const opencodeConfig = configResult.data;
6
+ const config = loadConfig(opencodeConfig?.archiver ?? {});
7
+ return {
8
+ event: async ({ event }) => {
9
+ if (event.type === "tui.command.execute" && event.properties.command === "session.new") {
10
+ await archiveCurrentSession(client, config);
11
+ }
12
+ if (event.type === "server.connected") {
13
+ await archiveAllSessions(client, config);
14
+ }
15
+ },
16
+ };
17
+ };
18
+ async function archiveCurrentSession(client, config) {
19
+ try {
20
+ const sessionsResult = await client.session.list({});
21
+ if (sessionsResult.data && sessionsResult.data.length > 0) {
22
+ const session = sessionsResult.data[0];
23
+ const messagesResult = await client.session.messages({ path: { id: session.id } });
24
+ if (messagesResult.data && messagesResult.data.length > 0) {
25
+ const messages = messagesResult.data;
26
+ const agent = extractAgentFromMessages(messages);
27
+ if (shouldArchive(agent, config)) {
28
+ await archiveSession(session, messages, config, agent);
29
+ }
30
+ }
31
+ }
32
+ }
33
+ catch (error) {
34
+ console.error("Failed to archive session:", error);
35
+ }
36
+ }
37
+ async function archiveAllSessions(client, config) {
38
+ try {
39
+ const sessionsResult = await client.session.list({});
40
+ if (sessionsResult.data) {
41
+ for (const sessionData of sessionsResult.data) {
42
+ const session = sessionData;
43
+ const messagesResult = await client.session.messages({ path: { id: session.id } });
44
+ if (messagesResult.data && messagesResult.data.length > 0) {
45
+ const messages = messagesResult.data;
46
+ const agent = extractAgentFromMessages(messages);
47
+ if (shouldArchive(agent, config)) {
48
+ await archiveSession(session, messages, config, agent);
49
+ }
50
+ }
51
+ }
52
+ }
53
+ }
54
+ catch (error) {
55
+ console.error("Failed to archive sessions on startup:", error);
56
+ }
57
+ }
58
+ function extractAgentFromMessages(messages) {
59
+ if (messages.length === 0)
60
+ return undefined;
61
+ const firstMessage = messages[0];
62
+ if (firstMessage && "agent" in firstMessage.info) {
63
+ return firstMessage.info.agent;
64
+ }
65
+ return undefined;
66
+ }
67
+ export default ConversationArchiver;
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function isArchived(trackingPath: string, sessionId: string): Promise<boolean>;
2
+ export declare function markArchived(trackingPath: string, sessionId: string): Promise<void>;
3
+ //# sourceMappingURL=tracking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracking.d.ts","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"AAIA,wBAAsB,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1F;AAED,wBAAsB,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYzF"}
@@ -0,0 +1,34 @@
1
+ import { promises as fs } from "fs";
2
+ import * as path from "path";
3
+ export async function isArchived(trackingPath, sessionId) {
4
+ try {
5
+ const data = await readTrackingFile(trackingPath);
6
+ return data.archivedSessions.includes(sessionId);
7
+ }
8
+ catch {
9
+ return false;
10
+ }
11
+ }
12
+ export async function markArchived(trackingPath, sessionId) {
13
+ let data;
14
+ try {
15
+ data = await readTrackingFile(trackingPath);
16
+ }
17
+ catch {
18
+ data = { version: 1, archivedSessions: [] };
19
+ }
20
+ if (!data.archivedSessions.includes(sessionId)) {
21
+ data.archivedSessions.push(sessionId);
22
+ await writeTrackingFile(trackingPath, data);
23
+ }
24
+ }
25
+ async function readTrackingFile(trackingPath) {
26
+ const content = await fs.readFile(trackingPath, "utf-8");
27
+ return JSON.parse(content);
28
+ }
29
+ async function writeTrackingFile(trackingPath, data) {
30
+ const dir = path.dirname(trackingPath);
31
+ await fs.mkdir(dir, { recursive: true });
32
+ await fs.writeFile(trackingPath, JSON.stringify(data, null, 2));
33
+ }
34
+ //# sourceMappingURL=tracking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracking.js","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAA;AACnC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAE5B,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,YAAoB,EAAE,SAAiB;IACtE,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,YAAY,CAAC,CAAA;QACjD,OAAO,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,YAAoB,EAAE,SAAiB;IACxE,IAAI,IAAkB,CAAA;IACtB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,gBAAgB,CAAC,YAAY,CAAC,CAAA;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAA;IAC7C,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/C,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACrC,MAAM,iBAAiB,CAAC,YAAY,EAAE,IAAI,CAAC,CAAA;IAC7C,CAAC;AACH,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,YAAoB;IAClD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IACxD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAiB,CAAA;AAC5C,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,YAAoB,EAAE,IAAkB;IACvE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;IACtC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACxC,MAAM,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;AACjE,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { Session, Message, Part, TextPart, ToolPart, ReasoningPart } from "@opencode-ai/sdk";
2
+ export type { Session, Message, Part, TextPart, ToolPart, ReasoningPart };
3
+ export interface ArchiverConfig {
4
+ agents: string[];
5
+ outputDir: string;
6
+ includeTools: boolean;
7
+ includeThinking: boolean;
8
+ }
9
+ export interface TrackingData {
10
+ version: number;
11
+ archivedSessions: string[];
12
+ }
13
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAEjG,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAA;AAEzE,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,OAAO,CAAA;IACrB,eAAe,EAAE,OAAO,CAAA;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB,EAAE,MAAM,EAAE,CAAA;CAC3B"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "opencode-archiver",
3
+ "version": "0.1.0",
4
+ "description": "OpenCode plugin that automatically archives conversations to markdown files",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "typecheck": "tsc --noEmit"
14
+ },
15
+ "keywords": [
16
+ "opencode",
17
+ "plugin",
18
+ "archive",
19
+ "conversation"
20
+ ],
21
+ "license": "MIT",
22
+ "peerDependencies": {
23
+ "@opencode-ai/plugin": "^1.2.10"
24
+ },
25
+ "devDependencies": {
26
+ "@opencode-ai/plugin": "^1.2.10",
27
+ "@types/node": "^25.3.0",
28
+ "typescript": "^5.7.0"
29
+ }
30
+ }