akm-opencode 0.4.1 → 0.4.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.
Files changed (2) hide show
  1. package/index.ts +148 -59
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -92,6 +92,14 @@ type CliLogMeta = {
92
92
  sessionID?: string
93
93
  }
94
94
 
95
+ type SessionPromptBody = {
96
+ agent: string
97
+ parts: Array<{ type: "text"; text: string }>
98
+ system?: string
99
+ model?: { providerID: string; modelID: string }
100
+ tools?: Record<string, boolean>
101
+ }
102
+
95
103
  function formatCliError(error: unknown): string {
96
104
  if (error && typeof error === "object" && "code" in error && (error as { code?: unknown }).code === "ENOENT") {
97
105
  return "The 'akm' CLI was not found on PATH. Install it first from https://github.com/itlackey/akm."
@@ -729,18 +737,111 @@ async function ensureTargetSessionID(input: {
729
737
  context: { sessionID: string; directory: string }
730
738
  title: string
731
739
  client: PluginClient
740
+ logClient: LogCapableClient
741
+ toolName: string
732
742
  }): Promise<{ ok: true; sessionID: string } | CliError> {
733
743
  if (!input.useSubtask) return { ok: true, sessionID: input.context.sessionID }
734
744
 
735
- const created = await input.client.session.create({
736
- query: { directory: input.context.directory },
737
- body: { parentID: input.context.sessionID, title: input.title },
738
- })
739
- if (created.error || !created.data?.id) {
740
- const reason = created.error ? JSON.stringify(created.error) : "missing child session id"
745
+ try {
746
+ const created = await input.client.session.create({
747
+ body: { parentID: input.context.sessionID, title: input.title },
748
+ })
749
+ if (created.error || !created.data?.id) {
750
+ const reason = created.error ? JSON.stringify(created.error) : "missing child session id"
751
+ await writePluginLog(input.logClient, "error", "AKM dispatch child session failed", {
752
+ subsystem: "dispatch",
753
+ toolName: input.toolName,
754
+ sessionID: input.context.sessionID,
755
+ directory: input.context.directory,
756
+ title: input.title,
757
+ error: reason,
758
+ })
759
+ return { ok: false, error: `Failed to create child session: ${reason}` }
760
+ }
761
+ await writePluginLog(input.logClient, "info", "AKM dispatch child session created", {
762
+ subsystem: "dispatch",
763
+ toolName: input.toolName,
764
+ sessionID: input.context.sessionID,
765
+ directory: input.context.directory,
766
+ childSessionID: created.data.id,
767
+ title: input.title,
768
+ })
769
+ return { ok: true, sessionID: created.data.id }
770
+ } catch (error: unknown) {
771
+ const reason = error instanceof Error ? error.message : String(error)
772
+ await writePluginLog(input.logClient, "error", "AKM dispatch child session threw", {
773
+ subsystem: "dispatch",
774
+ toolName: input.toolName,
775
+ sessionID: input.context.sessionID,
776
+ directory: input.context.directory,
777
+ title: input.title,
778
+ error: reason,
779
+ })
741
780
  return { ok: false, error: `Failed to create child session: ${reason}` }
742
781
  }
743
- return { ok: true, sessionID: created.data.id }
782
+ }
783
+
784
+ async function promptTargetSession(input: {
785
+ client: PluginClient
786
+ logClient: LogCapableClient
787
+ toolName: string
788
+ context: { sessionID: string; directory: string }
789
+ targetSessionID: string
790
+ promptBody: SessionPromptBody
791
+ failureMessage: string
792
+ ref?: string
793
+ }): Promise<{ ok: true; data: { parts?: unknown } } | CliError> {
794
+ try {
795
+ const promptResponse = await input.client.session.prompt({
796
+ path: { id: input.targetSessionID },
797
+ body: input.promptBody,
798
+ })
799
+
800
+ if (promptResponse.error || !promptResponse.data) {
801
+ const reason = promptResponse.error ? JSON.stringify(promptResponse.error) : "empty response"
802
+ await writePluginLog(input.logClient, "error", "AKM dispatch prompt failed", {
803
+ subsystem: "dispatch",
804
+ toolName: input.toolName,
805
+ sessionID: input.context.sessionID,
806
+ directory: input.context.directory,
807
+ targetSessionID: input.targetSessionID,
808
+ dispatchAgent: input.promptBody.agent,
809
+ ref: input.ref,
810
+ error: reason,
811
+ })
812
+ return {
813
+ ok: false,
814
+ error: `${input.failureMessage}: ${reason}`,
815
+ }
816
+ }
817
+
818
+ await writePluginLog(input.logClient, "info", "AKM dispatch prompt completed", {
819
+ subsystem: "dispatch",
820
+ toolName: input.toolName,
821
+ sessionID: input.context.sessionID,
822
+ directory: input.context.directory,
823
+ targetSessionID: input.targetSessionID,
824
+ dispatchAgent: input.promptBody.agent,
825
+ ref: input.ref,
826
+ })
827
+ return { ok: true, data: promptResponse.data }
828
+ } catch (error: unknown) {
829
+ const reason = error instanceof Error ? error.message : String(error)
830
+ await writePluginLog(input.logClient, "error", "AKM dispatch prompt threw", {
831
+ subsystem: "dispatch",
832
+ toolName: input.toolName,
833
+ sessionID: input.context.sessionID,
834
+ directory: input.context.directory,
835
+ targetSessionID: input.targetSessionID,
836
+ dispatchAgent: input.promptBody.agent,
837
+ ref: input.ref,
838
+ error: reason,
839
+ })
840
+ return {
841
+ ok: false,
842
+ error: `${input.failureMessage}: ${reason}`,
843
+ }
844
+ }
744
845
  }
745
846
 
746
847
  function splitArguments(raw: string): string[] {
@@ -787,19 +888,11 @@ function createSearchArgs(input: {
787
888
  type PluginClient = {
788
889
  session: {
789
890
  create: (input: {
790
- query: { directory: string }
791
891
  body: { parentID: string; title: string }
792
892
  }) => Promise<{ data?: { id?: string }; error?: unknown }>
793
893
  prompt: (input: {
794
- query: { directory: string }
795
894
  path: { id: string }
796
- body: {
797
- agent: string
798
- parts: Array<{ type: "text"; text: string }>
799
- system?: string
800
- model?: { providerID: string; modelID: string }
801
- tools?: Record<string, boolean>
802
- }
895
+ body: SessionPromptBody
803
896
  }) => Promise<{ data?: { parts?: unknown }; error?: unknown }>
804
897
  }
805
898
  }
@@ -1203,6 +1296,8 @@ export const AkmPlugin: Plugin = async ({ client }) => {
1203
1296
  context: { sessionID: context.sessionID, directory: context.directory },
1204
1297
  title: "akm:curator",
1205
1298
  client: client as unknown as PluginClient,
1299
+ logClient,
1300
+ toolName: "akm_evolve",
1206
1301
  })
1207
1302
  if (!targetSession.ok) return JSON.stringify(targetSession)
1208
1303
 
@@ -1210,23 +1305,20 @@ export const AkmPlugin: Plugin = async ({ client }) => {
1210
1305
  ? `Review recent AKM activity with an emphasis on: ${focus.trim()}. Produce the prioritized action list described in the system prompt.`
1211
1306
  : "Review recent AKM activity and produce the prioritized action list described in the system prompt."
1212
1307
 
1213
- const promptResponse = await client.session.prompt({
1214
- query: { directory: context.directory },
1215
- path: { id: targetSession.sessionID },
1216
- body: {
1308
+ const promptResponse = await promptTargetSession({
1309
+ client: client as unknown as PluginClient,
1310
+ logClient,
1311
+ toolName: "akm_evolve",
1312
+ context: { sessionID: context.sessionID, directory: context.directory },
1313
+ targetSessionID: targetSession.sessionID,
1314
+ failureMessage: "Failed to dispatch curator",
1315
+ promptBody: {
1217
1316
  agent: targetAgent,
1218
1317
  system: CURATOR_AGENT_PROMPT,
1219
1318
  parts: [{ type: "text", text: task }],
1220
1319
  },
1221
1320
  })
1222
-
1223
- if (promptResponse.error || !promptResponse.data) {
1224
- const reason = promptResponse.error ? JSON.stringify(promptResponse.error) : "empty response"
1225
- return JSON.stringify({
1226
- ok: false,
1227
- error: `Failed to dispatch curator: ${reason}`,
1228
- })
1229
- }
1321
+ if (!promptResponse.ok) return JSON.stringify(promptResponse)
1230
1322
 
1231
1323
  return JSON.stringify({
1232
1324
  ok: true,
@@ -1286,16 +1378,12 @@ export const AkmPlugin: Plugin = async ({ client }) => {
1286
1378
  context: { sessionID: context.sessionID, directory: context.directory },
1287
1379
  title: `akm:${shown.name}`,
1288
1380
  client: client as unknown as PluginClient,
1381
+ logClient,
1382
+ toolName: "akm_agent",
1289
1383
  })
1290
1384
  if (!targetSession.ok) return JSON.stringify(targetSession)
1291
1385
 
1292
- const promptBody: {
1293
- agent: string
1294
- system: string
1295
- parts: Array<{ type: "text"; text: string }>
1296
- model?: { providerID: string; modelID: string }
1297
- tools?: Record<string, boolean>
1298
- } = {
1386
+ const promptBody: SessionPromptBody = {
1299
1387
  agent: targetAgent,
1300
1388
  system: shown.prompt,
1301
1389
  parts: [{ type: "text", text: task_prompt }],
@@ -1303,19 +1391,17 @@ export const AkmPlugin: Plugin = async ({ client }) => {
1303
1391
  if (model) promptBody.model = model
1304
1392
  if (tools) promptBody.tools = tools
1305
1393
 
1306
- const promptResponse = await client.session.prompt({
1307
- query: { directory: context.directory },
1308
- path: { id: targetSession.sessionID },
1309
- body: promptBody,
1394
+ const promptResponse = await promptTargetSession({
1395
+ client: client as unknown as PluginClient,
1396
+ logClient,
1397
+ toolName: "akm_agent",
1398
+ context: { sessionID: context.sessionID, directory: context.directory },
1399
+ targetSessionID: targetSession.sessionID,
1400
+ promptBody,
1401
+ failureMessage: `Failed to dispatch prompt for ${resolved.ref}`,
1402
+ ref: resolved.ref,
1310
1403
  })
1311
-
1312
- if (promptResponse.error || !promptResponse.data) {
1313
- const reason = promptResponse.error ? JSON.stringify(promptResponse.error) : "empty response"
1314
- return JSON.stringify({
1315
- ok: false,
1316
- error: `Failed to dispatch prompt for ${resolved.ref}: ${reason}`,
1317
- })
1318
- }
1404
+ if (!promptResponse.ok) return JSON.stringify(promptResponse)
1319
1405
 
1320
1406
  return JSON.stringify({
1321
1407
  ok: true,
@@ -1370,25 +1456,25 @@ export const AkmPlugin: Plugin = async ({ client }) => {
1370
1456
  context: { sessionID: context.sessionID, directory: context.directory },
1371
1457
  title: `akm:cmd:${shown.name}`,
1372
1458
  client: client as unknown as PluginClient,
1459
+ logClient,
1460
+ toolName: "akm_cmd",
1373
1461
  })
1374
1462
  if (!targetSession.ok) return JSON.stringify(targetSession)
1375
1463
 
1376
- const promptResponse = await client.session.prompt({
1377
- query: { directory: context.directory },
1378
- path: { id: targetSession.sessionID },
1379
- body: {
1464
+ const promptResponse = await promptTargetSession({
1465
+ client: client as unknown as PluginClient,
1466
+ logClient,
1467
+ toolName: "akm_cmd",
1468
+ context: { sessionID: context.sessionID, directory: context.directory },
1469
+ targetSessionID: targetSession.sessionID,
1470
+ failureMessage: `Failed to execute command ${resolved.ref}`,
1471
+ ref: resolved.ref,
1472
+ promptBody: {
1380
1473
  agent: targetAgent,
1381
1474
  parts: [{ type: "text", text: rendered }],
1382
1475
  },
1383
1476
  })
1384
-
1385
- if (promptResponse.error || !promptResponse.data) {
1386
- const reason = promptResponse.error ? JSON.stringify(promptResponse.error) : "empty response"
1387
- return JSON.stringify({
1388
- ok: false,
1389
- error: `Failed to execute command ${resolved.ref}: ${reason}`,
1390
- })
1391
- }
1477
+ if (!promptResponse.ok) return JSON.stringify(promptResponse)
1392
1478
 
1393
1479
  return JSON.stringify({
1394
1480
  ok: true,
@@ -1497,3 +1583,6 @@ export const AkmPlugin: Plugin = async ({ client }) => {
1497
1583
  },
1498
1584
  }
1499
1585
  }
1586
+
1587
+ export const server = AkmPlugin
1588
+ export default { server }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-opencode",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin for AKM - search, show, and manage extension assets via the akm CLI, with agentic hooks that auto-load relevant stash assets, record feedback, and harvest session memories so the stash improves every session.",
6
6
  "keywords": [