opencode-swarm-plugin 0.21.0 → 0.22.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.
package/dist/plugin.js CHANGED
@@ -35359,14 +35359,16 @@ var swarm_complete = tool({
35359
35359
  retry_count: tool.schema.number().optional().describe("Number of retry attempts during task")
35360
35360
  },
35361
35361
  async execute(args) {
35362
- const projectKey = args.project_key.replace(/\//g, "-").replace(/\\/g, "-");
35363
- let agentRegistered = false;
35364
- let registrationWarning = "";
35362
+ const epicId = args.bead_id.includes(".") ? args.bead_id.split(".")[0] : args.bead_id;
35365
35363
  try {
35366
- const agent = await getAgent(projectKey, args.agent_name, args.project_key);
35367
- agentRegistered = agent !== null;
35368
- if (!agentRegistered) {
35369
- registrationWarning = `⚠️ WARNING: Agent '${args.agent_name}' was NOT registered in swarm-mail for project '${projectKey}'.
35364
+ const projectKey = args.project_key.replace(/\//g, "-").replace(/\\/g, "-");
35365
+ let agentRegistered = false;
35366
+ let registrationWarning = "";
35367
+ try {
35368
+ const agent = await getAgent(projectKey, args.agent_name, args.project_key);
35369
+ agentRegistered = agent !== null;
35370
+ if (!agentRegistered) {
35371
+ registrationWarning = `⚠️ WARNING: Agent '${args.agent_name}' was NOT registered in swarm-mail for project '${projectKey}'.
35370
35372
 
35371
35373
  This usually means you skipped the MANDATORY swarmmail_init step.
35372
35374
 
@@ -35379,150 +35381,176 @@ This usually means you skipped the MANDATORY swarmmail_init step.
35379
35381
  **Next time:** Run swarmmail_init(project_path="${args.project_key}", task_description="<task>") FIRST, before any other work.
35380
35382
 
35381
35383
  Continuing with completion, but this should be fixed for future subtasks.`;
35382
- console.warn(`[swarm_complete] ${registrationWarning}`);
35384
+ console.warn(`[swarm_complete] ${registrationWarning}`);
35385
+ }
35386
+ } catch (error45) {
35387
+ console.warn(`[swarm_complete] Could not verify agent registration:`, error45);
35388
+ registrationWarning = `ℹ️ Could not verify swarm-mail registration (database may not be available). Consider running swarmmail_init next time.`;
35383
35389
  }
35384
- } catch (error45) {
35385
- console.warn(`[swarm_complete] Could not verify agent registration:`, error45);
35386
- registrationWarning = `ℹ️ Could not verify swarm-mail registration (database may not be available). Consider running swarmmail_init next time.`;
35387
- }
35388
- let verificationResult = null;
35389
- if (!args.skip_verification && args.files_touched?.length) {
35390
- verificationResult = await runVerificationGate(args.files_touched, args.skip_ubs_scan ?? false);
35391
- if (!verificationResult.passed) {
35392
- return JSON.stringify({
35393
- success: false,
35394
- error: "Verification Gate FAILED - fix issues before completing",
35395
- verification: {
35396
- passed: false,
35397
- summary: verificationResult.summary,
35398
- blockers: verificationResult.blockers,
35399
- steps: verificationResult.steps.map((s) => ({
35400
- name: s.name,
35401
- passed: s.passed,
35402
- skipped: s.skipped,
35403
- skipReason: s.skipReason,
35404
- error: s.error?.slice(0, 200)
35405
- }))
35406
- },
35407
- hint: verificationResult.blockers.length > 0 ? `Fix these issues: ${verificationResult.blockers.map((b, i) => `${i + 1}. ${b}`).join(", ")}. Use skip_verification=true only as last resort.` : "Fix the failing checks and try again. Use skip_verification=true only as last resort.",
35408
- gate_function: "IDENTIFY → RUN → READ → VERIFY → CLAIM (you are at VERIFY, claim blocked)"
35409
- }, null, 2);
35390
+ let verificationResult = null;
35391
+ if (!args.skip_verification && args.files_touched?.length) {
35392
+ verificationResult = await runVerificationGate(args.files_touched, args.skip_ubs_scan ?? false);
35393
+ if (!verificationResult.passed) {
35394
+ return JSON.stringify({
35395
+ success: false,
35396
+ error: "Verification Gate FAILED - fix issues before completing",
35397
+ verification: {
35398
+ passed: false,
35399
+ summary: verificationResult.summary,
35400
+ blockers: verificationResult.blockers,
35401
+ steps: verificationResult.steps.map((s) => ({
35402
+ name: s.name,
35403
+ passed: s.passed,
35404
+ skipped: s.skipped,
35405
+ skipReason: s.skipReason,
35406
+ error: s.error?.slice(0, 200)
35407
+ }))
35408
+ },
35409
+ hint: verificationResult.blockers.length > 0 ? `Fix these issues: ${verificationResult.blockers.map((b, i) => `${i + 1}. ${b}`).join(", ")}. Use skip_verification=true only as last resort.` : "Fix the failing checks and try again. Use skip_verification=true only as last resort.",
35410
+ gate_function: "IDENTIFY → RUN → READ → VERIFY → CLAIM (you are at VERIFY, claim blocked)"
35411
+ }, null, 2);
35412
+ }
35410
35413
  }
35411
- }
35412
- let ubsResult = null;
35413
- if (!args.skip_verification && !verificationResult && args.files_touched?.length && !args.skip_ubs_scan) {
35414
- ubsResult = await runUbsScan(args.files_touched);
35415
- if (ubsResult && ubsResult.summary.critical > 0) {
35416
- return JSON.stringify({
35417
- success: false,
35418
- error: `UBS found ${ubsResult.summary.critical} critical bug(s) that must be fixed before completing`,
35419
- ubs_scan: {
35420
- critical_count: ubsResult.summary.critical,
35421
- bugs: ubsResult.bugs.filter((b) => b.severity === "critical")
35422
- },
35423
- hint: `Fix these critical bugs: ${ubsResult.bugs.filter((b) => b.severity === "critical").map((b) => `${b.file}:${b.line} - ${b.message}`).slice(0, 3).join("; ")}. Try: Run 'ubs scan ${args.files_touched?.join(" ") || "."} --json' for full report, fix reported issues, or use skip_ubs_scan=true to bypass (not recommended).`
35424
- }, null, 2);
35414
+ let ubsResult = null;
35415
+ if (!args.skip_verification && !verificationResult && args.files_touched?.length && !args.skip_ubs_scan) {
35416
+ ubsResult = await runUbsScan(args.files_touched);
35417
+ if (ubsResult && ubsResult.summary.critical > 0) {
35418
+ return JSON.stringify({
35419
+ success: false,
35420
+ error: `UBS found ${ubsResult.summary.critical} critical bug(s) that must be fixed before completing`,
35421
+ ubs_scan: {
35422
+ critical_count: ubsResult.summary.critical,
35423
+ bugs: ubsResult.bugs.filter((b) => b.severity === "critical")
35424
+ },
35425
+ hint: `Fix these critical bugs: ${ubsResult.bugs.filter((b) => b.severity === "critical").map((b) => `${b.file}:${b.line} - ${b.message}`).slice(0, 3).join("; ")}. Try: Run 'ubs scan ${args.files_touched?.join(" ") || "."} --json' for full report, fix reported issues, or use skip_ubs_scan=true to bypass (not recommended).`
35426
+ }, null, 2);
35427
+ }
35428
+ }
35429
+ let parsedEvaluation;
35430
+ if (args.evaluation) {
35431
+ try {
35432
+ parsedEvaluation = EvaluationSchema.parse(JSON.parse(args.evaluation));
35433
+ } catch (error45) {
35434
+ return JSON.stringify({
35435
+ success: false,
35436
+ error: "Invalid evaluation format",
35437
+ details: error45 instanceof exports_external.ZodError ? error45.issues : String(error45)
35438
+ }, null, 2);
35439
+ }
35440
+ if (!parsedEvaluation.passed) {
35441
+ return JSON.stringify({
35442
+ success: false,
35443
+ error: "Self-evaluation failed",
35444
+ retry_suggestion: parsedEvaluation.retry_suggestion,
35445
+ feedback: parsedEvaluation.overall_feedback
35446
+ }, null, 2);
35447
+ }
35448
+ }
35449
+ const closeResult = await Bun.$`bd close ${args.bead_id} --reason ${args.summary} --json`.quiet().nothrow();
35450
+ if (closeResult.exitCode !== 0) {
35451
+ throw new Error(`Failed to close bead because bd close command failed: ${closeResult.stderr.toString()}. Try: Verify bead exists and is not already closed with 'bd show ${args.bead_id}', check if bead ID is correct with 'beads_query()', or use beads_close tool directly.`);
35425
35452
  }
35426
- }
35427
- let parsedEvaluation;
35428
- if (args.evaluation) {
35429
35453
  try {
35430
- parsedEvaluation = EvaluationSchema.parse(JSON.parse(args.evaluation));
35454
+ const epicId3 = args.bead_id.includes(".") ? args.bead_id.split(".")[0] : args.bead_id;
35455
+ const durationMs2 = args.start_time ? Date.now() - args.start_time : 0;
35456
+ const event = createEvent("subtask_outcome", {
35457
+ project_key: args.project_key,
35458
+ epic_id: epicId3,
35459
+ bead_id: args.bead_id,
35460
+ planned_files: args.planned_files || [],
35461
+ actual_files: args.files_touched || [],
35462
+ duration_ms: durationMs2,
35463
+ error_count: args.error_count || 0,
35464
+ retry_count: args.retry_count || 0,
35465
+ success: true
35466
+ });
35467
+ await appendEvent(event, args.project_key);
35431
35468
  } catch (error45) {
35432
- return JSON.stringify({
35433
- success: false,
35434
- error: "Invalid evaluation format",
35435
- details: error45 instanceof exports_external.ZodError ? error45.issues : String(error45)
35436
- }, null, 2);
35469
+ console.warn("[swarm_complete] Failed to emit SubtaskOutcomeEvent:", error45);
35437
35470
  }
35438
- if (!parsedEvaluation.passed) {
35439
- return JSON.stringify({
35440
- success: false,
35441
- error: "Self-evaluation failed",
35442
- retry_suggestion: parsedEvaluation.retry_suggestion,
35443
- feedback: parsedEvaluation.overall_feedback
35444
- }, null, 2);
35471
+ let capturedStrategy;
35472
+ const durationMs = args.start_time ? Date.now() - args.start_time : 0;
35473
+ const memoryInfo = formatMemoryStoreOnSuccess(args.bead_id, args.summary, args.files_touched || [], capturedStrategy);
35474
+ let memoryStored = false;
35475
+ let memoryError;
35476
+ try {
35477
+ const memoryAvailable = await isToolAvailable("semantic-memory");
35478
+ if (memoryAvailable) {
35479
+ const storeResult = await Bun.$`semantic-memory store ${memoryInfo.information} --metadata ${memoryInfo.metadata}`.quiet().nothrow();
35480
+ if (storeResult.exitCode === 0) {
35481
+ memoryStored = true;
35482
+ console.log(`[swarm_complete] Stored learning for ${args.bead_id} in semantic-memory`);
35483
+ } else {
35484
+ memoryError = `semantic-memory store failed: ${storeResult.stderr.toString().slice(0, 200)}`;
35485
+ console.warn(`[swarm_complete] ${memoryError}`);
35486
+ }
35487
+ } else {
35488
+ memoryError = "semantic-memory not available - learning stored in-memory only";
35489
+ warnMissingTool("semantic-memory");
35490
+ }
35491
+ } catch (error45) {
35492
+ memoryError = `Failed to store memory: ${error45 instanceof Error ? error45.message : String(error45)}`;
35493
+ console.warn(`[swarm_complete] ${memoryError}`);
35494
+ }
35495
+ try {
35496
+ await releaseSwarmFiles({
35497
+ projectPath: args.project_key,
35498
+ agentName: args.agent_name
35499
+ });
35500
+ } catch (error45) {
35501
+ console.warn(`[swarm] Failed to release file reservations for ${args.agent_name}:`, error45);
35445
35502
  }
35446
- }
35447
- const closeResult = await Bun.$`bd close ${args.bead_id} --reason ${args.summary} --json`.quiet().nothrow();
35448
- if (closeResult.exitCode !== 0) {
35449
- throw new Error(`Failed to close bead because bd close command failed: ${closeResult.stderr.toString()}. Try: Verify bead exists and is not already closed with 'bd show ${args.bead_id}', check if bead ID is correct with 'beads_query()', or use beads_close tool directly.`);
35450
- }
35451
- try {
35452
35503
  const epicId2 = args.bead_id.includes(".") ? args.bead_id.split(".")[0] : args.bead_id;
35453
- const durationMs = args.start_time ? Date.now() - args.start_time : 0;
35454
- const event = createEvent("subtask_outcome", {
35455
- project_key: args.project_key,
35456
- epic_id: epicId2,
35457
- bead_id: args.bead_id,
35458
- planned_files: args.planned_files || [],
35459
- actual_files: args.files_touched || [],
35460
- duration_ms: durationMs,
35461
- error_count: args.error_count || 0,
35462
- retry_count: args.retry_count || 0,
35463
- success: true
35464
- });
35465
- await appendEvent(event, args.project_key);
35466
- } catch (error45) {
35467
- console.warn("[swarm_complete] Failed to emit SubtaskOutcomeEvent:", error45);
35468
- }
35469
- try {
35470
- await releaseSwarmFiles({
35504
+ const completionBody = [
35505
+ `## Subtask Complete: ${args.bead_id}`,
35506
+ "",
35507
+ `**Summary**: ${args.summary}`,
35508
+ "",
35509
+ parsedEvaluation ? `**Self-Evaluation**: ${parsedEvaluation.passed ? "PASSED" : "FAILED"}` : "",
35510
+ parsedEvaluation?.overall_feedback ? `**Feedback**: ${parsedEvaluation.overall_feedback}` : "",
35511
+ "",
35512
+ `**Memory Capture**: ${memoryStored ? "✓ Stored in semantic-memory" : `✗ ${memoryError || "Failed"}`}`
35513
+ ].filter(Boolean).join(`
35514
+ `);
35515
+ await sendSwarmMessage({
35471
35516
  projectPath: args.project_key,
35472
- agentName: args.agent_name
35517
+ fromAgent: args.agent_name,
35518
+ toAgents: [],
35519
+ subject: `Complete: ${args.bead_id}`,
35520
+ body: completionBody,
35521
+ threadId: epicId2,
35522
+ importance: "normal"
35473
35523
  });
35474
- } catch (error45) {
35475
- console.warn(`[swarm] Failed to release file reservations for ${args.agent_name}:`, error45);
35476
- }
35477
- const epicId = args.bead_id.includes(".") ? args.bead_id.split(".")[0] : args.bead_id;
35478
- const completionBody = [
35479
- `## Subtask Complete: ${args.bead_id}`,
35480
- "",
35481
- `**Summary**: ${args.summary}`,
35482
- "",
35483
- parsedEvaluation ? `**Self-Evaluation**: ${parsedEvaluation.passed ? "PASSED" : "FAILED"}` : "",
35484
- parsedEvaluation?.overall_feedback ? `**Feedback**: ${parsedEvaluation.overall_feedback}` : ""
35485
- ].filter(Boolean).join(`
35486
- `);
35487
- await sendSwarmMessage({
35488
- projectPath: args.project_key,
35489
- fromAgent: args.agent_name,
35490
- toAgents: [],
35491
- subject: `Complete: ${args.bead_id}`,
35492
- body: completionBody,
35493
- threadId: epicId,
35494
- importance: "normal"
35495
- });
35496
- const response = {
35497
- success: true,
35498
- bead_id: args.bead_id,
35499
- closed: true,
35500
- reservations_released: true,
35501
- message_sent: true,
35502
- agent_registration: {
35503
- verified: agentRegistered,
35504
- warning: registrationWarning || undefined
35505
- },
35506
- verification_gate: verificationResult ? {
35507
- passed: true,
35508
- summary: verificationResult.summary,
35509
- steps: verificationResult.steps.map((s) => ({
35510
- name: s.name,
35511
- passed: s.passed,
35512
- skipped: s.skipped,
35513
- skipReason: s.skipReason
35514
- }))
35515
- } : args.skip_verification ? { skipped: true, reason: "skip_verification=true" } : { skipped: true, reason: "no files_touched provided" },
35516
- ubs_scan: ubsResult ? {
35517
- ran: true,
35518
- bugs_found: ubsResult.summary.total,
35519
- summary: ubsResult.summary,
35520
- warnings: ubsResult.bugs.filter((b) => b.severity !== "critical")
35521
- } : verificationResult ? { ran: true, included_in_verification_gate: true } : {
35522
- ran: false,
35523
- reason: args.skip_ubs_scan ? "skipped" : "no files or ubs unavailable"
35524
- },
35525
- learning_prompt: `## Reflection
35524
+ const response = {
35525
+ success: true,
35526
+ bead_id: args.bead_id,
35527
+ closed: true,
35528
+ reservations_released: true,
35529
+ message_sent: true,
35530
+ agent_registration: {
35531
+ verified: agentRegistered,
35532
+ warning: registrationWarning || undefined
35533
+ },
35534
+ verification_gate: verificationResult ? {
35535
+ passed: true,
35536
+ summary: verificationResult.summary,
35537
+ steps: verificationResult.steps.map((s) => ({
35538
+ name: s.name,
35539
+ passed: s.passed,
35540
+ skipped: s.skipped,
35541
+ skipReason: s.skipReason
35542
+ }))
35543
+ } : args.skip_verification ? { skipped: true, reason: "skip_verification=true" } : { skipped: true, reason: "no files_touched provided" },
35544
+ ubs_scan: ubsResult ? {
35545
+ ran: true,
35546
+ bugs_found: ubsResult.summary.total,
35547
+ summary: ubsResult.summary,
35548
+ warnings: ubsResult.bugs.filter((b) => b.severity !== "critical")
35549
+ } : verificationResult ? { ran: true, included_in_verification_gate: true } : {
35550
+ ran: false,
35551
+ reason: args.skip_ubs_scan ? "skipped" : "no files or ubs unavailable"
35552
+ },
35553
+ learning_prompt: `## Reflection
35526
35554
 
35527
35555
  Did you learn anything reusable during this subtask? Consider:
35528
35556
 
@@ -35534,9 +35562,81 @@ Did you learn anything reusable during this subtask? Consider:
35534
35562
  If you discovered something valuable, use \`swarm_learn\` or \`skills_create\` to preserve it as a skill for future swarms.
35535
35563
 
35536
35564
  Files touched: ${args.files_touched?.join(", ") || "none recorded"}`,
35537
- memory_store: formatMemoryStoreOnSuccess(args.bead_id, args.summary, args.files_touched || [])
35538
- };
35539
- return JSON.stringify(response, null, 2);
35565
+ memory_capture: {
35566
+ attempted: true,
35567
+ stored: memoryStored,
35568
+ error: memoryError,
35569
+ information: memoryInfo.information,
35570
+ metadata: memoryInfo.metadata,
35571
+ note: memoryStored ? "Learning automatically stored in semantic-memory" : `Failed to store: ${memoryError}. Learning lost unless semantic-memory is available.`
35572
+ }
35573
+ };
35574
+ return JSON.stringify(response, null, 2);
35575
+ } catch (error45) {
35576
+ const errorMessage = error45 instanceof Error ? error45.message : String(error45);
35577
+ const errorStack = error45 instanceof Error ? error45.stack : undefined;
35578
+ let failedStep = "unknown";
35579
+ if (errorMessage.includes("verification")) {
35580
+ failedStep = "Verification Gate (UBS/typecheck/tests)";
35581
+ } else if (errorMessage.includes("UBS") || errorMessage.includes("ubs")) {
35582
+ failedStep = "UBS scan";
35583
+ } else if (errorMessage.includes("evaluation")) {
35584
+ failedStep = "Self-evaluation parsing";
35585
+ } else if (errorMessage.includes("bead") || errorMessage.includes("close")) {
35586
+ failedStep = "Bead close";
35587
+ } else if (errorMessage.includes("memory") || errorMessage.includes("semantic")) {
35588
+ failedStep = "Memory storage (non-fatal)";
35589
+ } else if (errorMessage.includes("reservation") || errorMessage.includes("release")) {
35590
+ failedStep = "File reservation release";
35591
+ } else if (errorMessage.includes("message") || errorMessage.includes("mail")) {
35592
+ failedStep = "Swarm mail notification";
35593
+ }
35594
+ const errorBody = [
35595
+ `## ⚠️ SWARM_COMPLETE FAILED`,
35596
+ "",
35597
+ `**Bead**: ${args.bead_id}`,
35598
+ `**Agent**: ${args.agent_name}`,
35599
+ `**Failed Step**: ${failedStep}`,
35600
+ "",
35601
+ `### Error Message`,
35602
+ "```",
35603
+ errorMessage,
35604
+ "```",
35605
+ "",
35606
+ errorStack ? `### Stack Trace
35607
+ \`\`\`
35608
+ ${errorStack.slice(0, 1000)}
35609
+ \`\`\`
35610
+ ` : "",
35611
+ `### Context`,
35612
+ `- **Summary**: ${args.summary}`,
35613
+ `- **Files touched**: ${args.files_touched?.length ? args.files_touched.join(", ") : "none"}`,
35614
+ `- **Skip UBS**: ${args.skip_ubs_scan ?? false}`,
35615
+ `- **Skip verification**: ${args.skip_verification ?? false}`,
35616
+ "",
35617
+ `### Recovery Actions`,
35618
+ "1. Check error message for specific issue",
35619
+ "2. Review failed step (UBS scan, typecheck, bead close, etc.)",
35620
+ "3. Fix underlying issue or use skip flags if appropriate",
35621
+ "4. Retry swarm_complete after fixing"
35622
+ ].filter(Boolean).join(`
35623
+ `);
35624
+ try {
35625
+ await sendSwarmMessage({
35626
+ projectPath: args.project_key,
35627
+ fromAgent: args.agent_name,
35628
+ toAgents: [],
35629
+ subject: `FAILED: swarm_complete for ${args.bead_id}`,
35630
+ body: errorBody,
35631
+ threadId: epicId,
35632
+ importance: "urgent"
35633
+ });
35634
+ } catch (mailError) {
35635
+ console.error(`[swarm_complete] CRITICAL: Failed to notify coordinator of failure for ${args.bead_id}:`, mailError);
35636
+ console.error(`[swarm_complete] Original error:`, error45);
35637
+ }
35638
+ throw error45;
35639
+ }
35540
35640
  }
35541
35641
  });
35542
35642
  var swarm_record_outcome = tool({
@@ -37347,6 +37447,34 @@ class InMemoryMaturityStorage {
37347
37447
  }
37348
37448
  }
37349
37449
 
37450
+ // src/storage.ts
37451
+ function getCollectionNames() {
37452
+ const base = {
37453
+ feedback: "swarm-feedback",
37454
+ patterns: "swarm-patterns",
37455
+ maturity: "swarm-maturity"
37456
+ };
37457
+ if (process.env.TEST_MEMORY_COLLECTIONS === "true") {
37458
+ return {
37459
+ feedback: `${base.feedback}-test`,
37460
+ patterns: `${base.patterns}-test`,
37461
+ maturity: `${base.maturity}-test`
37462
+ };
37463
+ }
37464
+ return base;
37465
+ }
37466
+ var DEFAULT_STORAGE_CONFIG = {
37467
+ backend: "semantic-memory",
37468
+ collections: getCollectionNames(),
37469
+ useSemanticSearch: true
37470
+ };
37471
+ var sessionStats = {
37472
+ storesCount: 0,
37473
+ queriesCount: 0,
37474
+ sessionStart: Date.now(),
37475
+ lastAlertCheck: Date.now()
37476
+ };
37477
+
37350
37478
  // src/index.ts
37351
37479
  init_skills();
37352
37480
  var SwarmPlugin = async (input) => {
@@ -0,0 +1,123 @@
1
+ # Semantic Memory CLI Syntax Reference
2
+
3
+ **CRITICAL: The semantic-memory CLI has specific JSON requirements that will fail silently if violated.**
4
+
5
+ ## Working Syntax
6
+
7
+ ### Store Memory (via Bash)
8
+
9
+ ```bash
10
+ semantic-memory store \
11
+ --information "Your learning here with full context" \
12
+ --metadata '{"tags": ["tag1", "tag2", "tag3"]}'
13
+ ```
14
+
15
+ **Key Rules:**
16
+
17
+ - `--metadata` MUST be valid JSON with single quotes wrapping the object
18
+ - Use `{"tags": [...]}` structure, NOT comma-separated strings
19
+ - Information can be plain text (no quotes needed in bash)
20
+
21
+ ### Store Memory (via MCP tool - PREFERRED)
22
+
23
+ The MCP tool `semantic-memory_store` has different syntax:
24
+
25
+ ```typescript
26
+ semantic-memory_store(
27
+ information: "Your learning here",
28
+ metadata: "tag1, tag2, tag3" // Comma-separated string, NOT JSON
29
+ )
30
+ ```
31
+
32
+ **Use the MCP tool when available - it handles JSON serialization for you.**
33
+
34
+ ## Common Mistakes
35
+
36
+ ### ❌ WRONG - Comma-separated string in CLI
37
+
38
+ ```bash
39
+ semantic-memory store \
40
+ --metadata "swarm,edge-case,workaround"
41
+ # Error: Invalid JSON in --metadata
42
+ ```
43
+
44
+ ### ❌ WRONG - Double quotes wrapping JSON
45
+
46
+ ```bash
47
+ semantic-memory store \
48
+ --metadata "{"tags": ["swarm"]}"
49
+ # Error: Shell parsing breaks on nested quotes
50
+ ```
51
+
52
+ ### ❌ WRONG - No tags wrapper
53
+
54
+ ```bash
55
+ semantic-memory store \
56
+ --metadata '["swarm", "edge-case"]'
57
+ # Error: Expected object with "tags" key
58
+ ```
59
+
60
+ ### ✅ CORRECT - JSON object with single quotes
61
+
62
+ ```bash
63
+ semantic-memory store \
64
+ --information "swarm_complete fails when files outside project_key" \
65
+ --metadata '{"tags": ["swarm", "edge-case", "workaround"]}'
66
+ ```
67
+
68
+ ## When to Use Which
69
+
70
+ | Context | Tool | Metadata Format |
71
+ | --------------------------- | ---------------------------------- | ------------------------------------- |
72
+ | **Inside OpenCode session** | `semantic-memory_store()` MCP tool | `"tag1, tag2, tag3"` (string) |
73
+ | **Direct CLI / Bash tool** | `semantic-memory store` command | `'{"tags": ["tag1", "tag2"]}'` (JSON) |
74
+ | **Scripts / automation** | CLI command | JSON object |
75
+
76
+ ## Other Commands
77
+
78
+ ### Find Memories
79
+
80
+ ```bash
81
+ # CLI
82
+ semantic-memory find --query "search terms" --limit 5
83
+
84
+ # MCP tool (preferred)
85
+ semantic-memory_find(query="search terms", limit=5)
86
+ ```
87
+
88
+ ### Validate Memory (reset decay)
89
+
90
+ ```bash
91
+ # CLI
92
+ semantic-memory validate --id "mem-uuid"
93
+
94
+ # MCP tool (preferred)
95
+ semantic-memory_validate(id="mem-uuid")
96
+ ```
97
+
98
+ ### List All
99
+
100
+ ```bash
101
+ # CLI
102
+ semantic-memory list
103
+
104
+ # MCP tool (preferred)
105
+ semantic-memory_list()
106
+ ```
107
+
108
+ ## Pro Tips
109
+
110
+ 1. **Prefer MCP tools in OpenCode sessions** - they handle serialization
111
+ 2. **Use CLI for scripts** - more control, but requires JSON knowledge
112
+ 3. **Test metadata syntax** - run `semantic-memory list` to verify storage
113
+ 4. **Keep tags focused** - 3-5 tags max, use domain/tech/pattern structure
114
+ 5. **Include error messages verbatim** - makes search more effective
115
+
116
+ ## Debugging
117
+
118
+ If storage fails:
119
+
120
+ 1. Check `--metadata` is valid JSON: `echo '{"tags": ["test"]}' | jq`
121
+ 2. Verify single quotes wrap the JSON object
122
+ 3. Ensure no shell escaping issues with information text
123
+ 4. Try MCP tool instead of CLI if in OpenCode session