akemon 0.3.5 → 0.3.6

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/DATA_POLICY.md ADDED
@@ -0,0 +1,120 @@
1
+ # Akemon Data Policy
2
+
3
+ This document describes the intended data principles for the open-source Akemon
4
+ project and related official services. It is not a substitute for a formal
5
+ privacy notice for any hosted service that may be offered separately.
6
+
7
+ ## Core Principles
8
+
9
+ - Users own their agent memories, work memory, task history, and local runtime
10
+ data.
11
+ - Akemon should be local-first by default.
12
+ - Akemon should use plain, portable files where practical so users can inspect,
13
+ copy, back up, migrate, or delete their data without asking a service provider.
14
+ - External engines, software agents, cloud services, and relay services are
15
+ replaceable peripherals, not owners of Akemon identity or memory.
16
+ - Personality memory under `self/` is maintained by Akemon core/module logic and
17
+ should not be directly mutated by external software agents unless the user
18
+ explicitly requests ordinary file-level work.
19
+
20
+ ## Local Data
21
+
22
+ By default, Akemon stores runtime data locally under `.akemon/agents/<name>/`.
23
+ Important local areas include:
24
+
25
+ - `self/`: canonical personality and identity memory
26
+ - `work/`: user-owned work memory shared with tools such as Codex or Claude Code
27
+ - `events/`: persistent event logs
28
+ - `software-agent/`: task ledgers, context packets, session summaries, and
29
+ software-agent run metadata
30
+
31
+ Local files are user data. Users may copy them, back them up with their own
32
+ tools, place them in private storage, or delete them. Be careful with `.akemon/`
33
+ because it may contain private memories, task content, logs, and paths.
34
+
35
+ ## Work Memory and External Agents
36
+
37
+ External software agents should use `work/` as the default shared memory layer.
38
+ They may read or update work memory when the user asks or when a task explicitly
39
+ allows it.
40
+
41
+ External software agents should not receive or edit `self/` personality memory
42
+ by default. If a user explicitly names a `self/` file, that should be treated as
43
+ ordinary file inspection or editing, not as Akemon delegating personality-memory
44
+ authority.
45
+
46
+ ## Engines, Agent SDKs, and Third-Party Providers
47
+
48
+ When users configure an external model, engine, agent SDK, coding agent, MCP
49
+ server, or other provider, task content and selected context may be sent to that
50
+ provider. Those providers have their own terms, retention policies, and security
51
+ controls.
52
+
53
+ Akemon should make these boundaries visible and should avoid sending more memory
54
+ or context than the task requires. Users are responsible for choosing providers
55
+ they trust for the data they send.
56
+
57
+ ## Relay and Published Agents
58
+
59
+ Relay features send data over the network because they publish agents, route
60
+ calls, or synchronize public/remote interactions.
61
+
62
+ The intended boundary is:
63
+
64
+ - public profile, tags, status, stats, and advertised capabilities may be visible
65
+ through relay features
66
+ - task requests and responses may pass through relay when remote calls are used
67
+ - relay should not be the authority for canonical `self/` personality memory
68
+ - relay should not have reverse access to local files, configs, memories, or
69
+ private runtime data unless a user explicitly sends or publishes that data
70
+
71
+ Users should not publish secrets, private memory, credentials, or sensitive work
72
+ data through relay tasks or public profile fields.
73
+
74
+ ## Logs, Ledgers, and Redaction
75
+
76
+ Akemon records local events and software-agent task ledgers for debugging,
77
+ continuity, and audit. These records may include task goals, summaries, file
78
+ paths, command summaries, provider names, risk metadata, and selected context.
79
+
80
+ Akemon includes best-effort redaction for common secret-like values in streams
81
+ and logs, but redaction is not a guarantee. Treat logs and ledgers as potentially
82
+ sensitive local data.
83
+
84
+ ## Cloud Backup and Sync
85
+
86
+ If official cloud backup or sync is offered, it should follow these principles:
87
+
88
+ - opt in explicitly
89
+ - make clear what is backed up and where it is stored
90
+ - preserve user export and deletion paths
91
+ - avoid lock-in by keeping data formats portable where practical
92
+ - distinguish canonical local memory from cached, synced, or projected data
93
+ - publish service-specific privacy, retention, and security details before users
94
+ rely on the service for sensitive data
95
+
96
+ Users who prefer not to use official cloud backup should be able to back up local
97
+ Akemon data with their own storage provider, filesystem sync, or private archive
98
+ workflow.
99
+
100
+ ## Telemetry
101
+
102
+ The open-source CLI should not send product telemetry by default. Network traffic
103
+ is expected when users enable relay, configure remote engines, call external
104
+ agents, install integrations, or use hosted services.
105
+
106
+ If telemetry is added in the future, it should be clearly disclosed and either
107
+ opt-in or controlled by an explicit setting.
108
+
109
+ ## Data Portability
110
+
111
+ Akemon should keep user memory portable. Users should be able to:
112
+
113
+ - inspect local data with normal filesystem tools
114
+ - move memories between machines
115
+ - use external tools to read work memory
116
+ - export or back up agent memory without requiring a proprietary service
117
+ - stop using an official service without losing local ownership of memories
118
+
119
+ This portability is part of Akemon's product promise: tools and providers may
120
+ change, but user memory should remain under user control.
package/README.md CHANGED
@@ -193,6 +193,27 @@ The software-agent task ledger keeps the most recent 200 task records by default
193
193
 
194
194
  The persistent event log rotates automatically at about 10 MB per file and keeps the current `events.jsonl` plus five rotated files.
195
195
 
196
+ ## Work Memory
197
+
198
+ Akemon keeps personality memory under `.akemon/agents/<name>/self/`. External software tools such as Codex CLI and Claude Code should use the separate work-memory directory instead:
199
+
200
+ ```bash
201
+ # Print a deterministic work-memory packet for an external tool
202
+ akemon work-context --name my-agent
203
+
204
+ # Append a quick work-memory note
205
+ akemon work-note --name my-agent --source codex --kind decision "Keep Codex focused on work memory before adding more tools."
206
+ ```
207
+
208
+ Work memory lives under `.akemon/agents/<name>/work/`. Users and coding agents may read or update that directory directly, with their own grep, browsing, semantic review, or skill workflow.
209
+
210
+ When launching Codex through Akemon, work memory is passed as a directory by default. Add `--work-context` when you want Akemon to embed a bounded `work-context` packet directly in the task envelope:
211
+
212
+ ```bash
213
+ akemon software-agent --session akemon-dev --work-context "Continue the current Codex UX work."
214
+ akemon software-agent-continue akemon-dev --work-context-budget 8000 "Pick up from the last task."
215
+ ```
216
+
196
217
  ## Serve Options
197
218
 
198
219
  ```bash
@@ -266,6 +287,10 @@ Open [relay.akemon.dev](https://relay.akemon.dev) in any browser to see all agen
266
287
  - **No reverse access** — relay is a dumb pipe
267
288
  - **You control** — `--approve` to review tasks, `--engine human` to answer personally
268
289
 
290
+ See [DATA_POLICY.md](DATA_POLICY.md) for Akemon's local-first memory and data
291
+ ownership principles. See [TRADEMARK.md](TRADEMARK.md) for use of the Akemon
292
+ name, marks, and official service identity.
293
+
269
294
  ## Agent Stats
270
295
 
271
296
  Every agent earns stats through real work:
package/TRADEMARK.md ADDED
@@ -0,0 +1,74 @@
1
+ # Akemon Trademark Policy
2
+
3
+ This project is open source, but the open-source license for the code does not
4
+ grant a license to use Akemon names, logos, domains, or other project marks in a
5
+ way that implies official endorsement or control.
6
+
7
+ ## Project Marks
8
+
9
+ Project marks include:
10
+
11
+ - the name `Akemon`
12
+ - Akemon logos, icons, mascots, and visual brand assets
13
+ - official domains and services such as `akemon.dev` and `relay.akemon.dev`
14
+ - names or marks that are confusingly similar when used for related software or
15
+ hosted services
16
+
17
+ These marks may or may not be registered trademarks. This policy is intended to
18
+ keep the project name reliable for users.
19
+
20
+ ## Allowed Uses
21
+
22
+ You may use the Akemon name to:
23
+
24
+ - refer truthfully to the open-source project
25
+ - describe compatibility, such as "works with Akemon" or "Akemon-compatible"
26
+ - identify an unmodified copy of the upstream project
27
+ - discuss, review, document, or teach the project
28
+ - link to the official repository or official services
29
+
30
+ These uses should not imply that your project, fork, service, package, plugin,
31
+ or hosted deployment is official unless it is actually maintained or approved by
32
+ the Akemon maintainers.
33
+
34
+ ## Forks and Modified Versions
35
+
36
+ You may fork the code under its open-source license. If you distribute a
37
+ modified product, hosted service, package, or agent network, use a name and
38
+ presentation that make the difference clear.
39
+
40
+ Good examples:
41
+
42
+ - `ExampleAI, built from Akemon`
43
+ - `ExampleAI, Akemon-compatible`
44
+ - `ExampleAI fork of Akemon`
45
+
46
+ Avoid examples:
47
+
48
+ - calling a materially modified fork simply `Akemon`
49
+ - using the official logo for an unofficial service
50
+ - presenting an unofficial relay, cloud backup, or marketplace as the official
51
+ Akemon service
52
+
53
+ If your changes substantially alter memory ownership, permission behavior,
54
+ privacy boundaries, or agent identity behavior, make that especially clear to
55
+ users.
56
+
57
+ ## Official Services
58
+
59
+ Official hosted services, including relay, cloud backup, sync, marketplace, or
60
+ managed agent services, may have separate terms, privacy notices, data policies,
61
+ and brand rules. The open-source code license does not grant access to or
62
+ control over those services.
63
+
64
+ ## No Endorsement
65
+
66
+ Do not use Akemon marks in advertising, product names, company names, domains,
67
+ social accounts, package names, or app listings in a way that suggests official
68
+ endorsement, partnership, or sponsorship without written permission.
69
+
70
+ ## Questions
71
+
72
+ If a use is ambiguous, prefer clear attribution and a distinct product name.
73
+ Open an issue or contact the maintainers before relying on a use that could
74
+ confuse users about who operates the software or service.
package/dist/cli.js CHANGED
@@ -8,6 +8,8 @@ import { listAgents } from "./list.js";
8
8
  import { connect } from "./connect.js";
9
9
  import { PrivacyFilterUnavailableError, sanitizeText, } from "./privacy-filter.js";
10
10
  import { SoftwareAgentStreamCliRenderer } from "./software-agent-stream-cli.js";
11
+ import { appendWorkMemoryNote, buildWorkMemoryContext, } from "./work-memory.js";
12
+ import { renderSoftwareAgentRunResult } from "./software-agent-result-cli.js";
11
13
  import { readFileSync } from "fs";
12
14
  import { fileURLToPath } from "url";
13
15
  import { dirname, join } from "path";
@@ -76,10 +78,36 @@ function printSoftwareAgentTaskList(tasks) {
76
78
  : "no-git";
77
79
  const goal = truncateOneLine(task.envelope?.goal || "", 90);
78
80
  console.log(`${task.taskId} ${task.status}/${result} ${duration} ${git} ${task.updatedAt || task.startedAt}`);
81
+ if (task.contextSession?.sessionId)
82
+ console.log(` session: ${task.contextSession.sessionId}`);
83
+ const workMemoryDir = task.envelope?.workMemoryDir || task.result?.workMemoryDir;
84
+ if (workMemoryDir)
85
+ console.log(` work memory: ${workMemoryDir}`);
79
86
  if (goal)
80
87
  console.log(` ${goal}`);
81
88
  }
82
89
  }
90
+ function printSoftwareAgentSessionList(sessions) {
91
+ if (!sessions.length) {
92
+ console.log("No software-agent context sessions found.");
93
+ return;
94
+ }
95
+ for (const session of sessions) {
96
+ const result = session.lastResult?.success === true ? "ok" : session.lastResult?.success === false ? "error" : "pending";
97
+ const duration = typeof session.lastResult?.durationMs === "number" ? `${session.lastResult.durationMs}ms` : "-";
98
+ const updatedAt = session.updatedAt || "-";
99
+ const goal = truncateOneLine(session.lastGoal || "", 90);
100
+ console.log(`${session.sessionId} ${result} ${duration} ${updatedAt}`);
101
+ if (session.lastTaskId)
102
+ console.log(` last task: ${session.lastTaskId}`);
103
+ if (goal)
104
+ console.log(` ${goal}`);
105
+ if (session.packetPath)
106
+ console.log(` context: ${session.packetPath}`);
107
+ if (session.workMemoryDir)
108
+ console.log(` work memory: ${session.workMemoryDir}`);
109
+ }
110
+ }
83
111
  function truncateOneLine(value, max) {
84
112
  const oneLine = value.replace(/\s+/g, " ").trim();
85
113
  if (oneLine.length <= max)
@@ -176,9 +204,64 @@ async function streamLocalOwnerEndpoint(path, opts, body) {
176
204
  if (failed)
177
205
  process.exit(1);
178
206
  }
207
+ async function runSoftwareAgentCli(goalParts, opts, forcedSessionId) {
208
+ const body = {
209
+ goal: goalParts.join(" "),
210
+ roleScope: opts.roleScope,
211
+ memoryScope: opts.memoryScope,
212
+ riskLevel: opts.risk,
213
+ };
214
+ if (opts.workdir)
215
+ body.workdir = opts.workdir;
216
+ if (opts.allowOutsideWorkdir)
217
+ body.allowOutsideWorkdir = true;
218
+ if (opts.memorySummary)
219
+ body.memorySummary = opts.memorySummary;
220
+ const workContextBudget = parsePositiveIntCliOption(opts.workContextBudget, "--work-context-budget");
221
+ if (opts.workContext || workContextBudget !== undefined)
222
+ body.includeWorkMemoryContext = true;
223
+ if (workContextBudget !== undefined)
224
+ body.workMemoryContextBudget = workContextBudget;
225
+ const sessionId = forcedSessionId || opts.session;
226
+ if (sessionId)
227
+ body.contextSessionId = sessionId;
228
+ if (opts.deliverable)
229
+ body.deliverable = opts.deliverable;
230
+ if (opts.timeoutMs) {
231
+ const timeoutMs = Number(opts.timeoutMs);
232
+ if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) {
233
+ console.error("--timeout-ms must be a positive integer");
234
+ process.exit(1);
235
+ }
236
+ body.timeoutMs = timeoutMs;
237
+ }
238
+ if (opts.stream !== false) {
239
+ await streamLocalOwnerEndpoint("/self/software-agent/run-stream", opts, body);
240
+ return;
241
+ }
242
+ const res = await fetchLocalOwnerEndpoint("/self/software-agent/run", opts, {
243
+ method: "POST",
244
+ body: JSON.stringify(body),
245
+ });
246
+ const text = await res.text();
247
+ let data;
248
+ try {
249
+ data = text ? JSON.parse(text) : {};
250
+ }
251
+ catch {
252
+ data = { output: text };
253
+ }
254
+ if (!res.ok) {
255
+ console.error(data.error || text || `Request failed with HTTP ${res.status}`);
256
+ process.exit(1);
257
+ }
258
+ const failed = renderSoftwareAgentRunResult(data);
259
+ if (failed)
260
+ process.exit(1);
261
+ }
179
262
  program
180
263
  .name("akemon")
181
- .description("Agent work marketplace train your agent, let it work for others")
264
+ .description("Local AI companion runtime with memory, modules, relay sync, and software-agent control")
182
265
  .version(pkg.version);
183
266
  program
184
267
  .command("serve")
@@ -309,47 +392,34 @@ program
309
392
  .option("--memory-scope <scope>", "Memory scope: none|public|task|owner", "owner")
310
393
  .option("--risk <level>", "Risk level: low|medium|high", "medium")
311
394
  .option("--memory-summary <text>", "Pre-filtered memory/context text to include")
395
+ .option("--work-context", "Embed a bounded work-memory context packet in the task envelope")
396
+ .option("--work-context-budget <chars>", "Maximum embedded work-memory context size; also enables --work-context")
312
397
  .option("--session <id>", "Akemon-side context session id for explicit software-agent continuity")
313
398
  .option("--deliverable <text>", "Expected output shape")
314
399
  .option("--timeout-ms <ms>", "Task timeout in milliseconds")
315
400
  .option("--no-stream", "Disable local streaming and wait for the final response")
316
401
  .action(async (goalParts, opts) => {
317
- const body = {
318
- goal: goalParts.join(" "),
319
- roleScope: opts.roleScope,
320
- memoryScope: opts.memoryScope,
321
- riskLevel: opts.risk,
322
- };
323
- if (opts.workdir)
324
- body.workdir = opts.workdir;
325
- if (opts.allowOutsideWorkdir)
326
- body.allowOutsideWorkdir = true;
327
- if (opts.memorySummary)
328
- body.memorySummary = opts.memorySummary;
329
- if (opts.session)
330
- body.contextSessionId = opts.session;
331
- if (opts.deliverable)
332
- body.deliverable = opts.deliverable;
333
- if (opts.timeoutMs) {
334
- const timeoutMs = Number(opts.timeoutMs);
335
- if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) {
336
- console.error("--timeout-ms must be a positive integer");
337
- process.exit(1);
338
- }
339
- body.timeoutMs = timeoutMs;
340
- }
341
- if (opts.stream !== false) {
342
- await streamLocalOwnerEndpoint("/self/software-agent/run-stream", opts, body);
343
- return;
344
- }
345
- const data = await callLocalOwnerEndpoint("/self/software-agent/run", opts, {
346
- method: "POST",
347
- body: JSON.stringify(body),
348
- });
349
- if (data.output)
350
- console.log(data.output);
351
- else
352
- console.log(JSON.stringify(data, null, 2));
402
+ await runSoftwareAgentCli(goalParts, opts);
403
+ });
404
+ program
405
+ .command("software-agent-continue")
406
+ .description("Continue an Akemon-side software-agent context session")
407
+ .argument("<sessionId>", "Akemon-side context session id to continue")
408
+ .argument("<goal...>", "Task goal to send to the software agent")
409
+ .option("-p, --port <port>", "Local akemon serve port", "3000")
410
+ .option("-w, --workdir <path>", "Workdir for the software agent (default: serve workdir)")
411
+ .option("--allow-outside-workdir", "Allow the software agent workdir to be outside the serve workdir")
412
+ .option("--role-scope <scope>", "Role scope: owner|public|order|agent|system", "owner")
413
+ .option("--memory-scope <scope>", "Memory scope: none|public|task|owner", "owner")
414
+ .option("--risk <level>", "Risk level: low|medium|high", "medium")
415
+ .option("--memory-summary <text>", "Pre-filtered memory/context text to include")
416
+ .option("--work-context", "Embed a bounded work-memory context packet in the task envelope")
417
+ .option("--work-context-budget <chars>", "Maximum embedded work-memory context size; also enables --work-context")
418
+ .option("--deliverable <text>", "Expected output shape")
419
+ .option("--timeout-ms <ms>", "Task timeout in milliseconds")
420
+ .option("--no-stream", "Disable local streaming and wait for the final response")
421
+ .action(async (sessionId, goalParts, opts) => {
422
+ await runSoftwareAgentCli(goalParts, opts, sessionId);
353
423
  });
354
424
  program
355
425
  .command("software-agent-status")
@@ -367,20 +437,78 @@ program
367
437
  .argument("[taskId]", "Task id to inspect")
368
438
  .option("-p, --port <port>", "Local akemon serve port", "3000")
369
439
  .option("-l, --limit <n>", "Maximum recent tasks to list", "20")
440
+ .option("--session <id>", "Filter listed tasks by Akemon-side context session id")
441
+ .option("--context", "Print the task's Akemon TASK_CONTEXT.md content when inspecting one task")
370
442
  .option("--json", "Print raw JSON")
371
443
  .action(async (taskId, opts) => {
444
+ if (!taskId && opts.context) {
445
+ console.error("--context requires a taskId");
446
+ process.exit(1);
447
+ }
448
+ if (taskId && opts.session) {
449
+ console.error("--session cannot be used when inspecting a single taskId");
450
+ process.exit(1);
451
+ }
372
452
  const path = taskId
373
- ? `/self/software-agent/tasks/${encodeURIComponent(taskId)}`
374
- : `/self/software-agent/tasks?limit=${clampPositiveInt(opts.limit, 20, 100)}`;
453
+ ? `/self/software-agent/tasks/${encodeURIComponent(taskId)}${opts.context ? "?includeContext=1" : ""}`
454
+ : `/self/software-agent/tasks?limit=${clampPositiveInt(opts.limit, 20, 100)}${opts.session ? `&session=${encodeURIComponent(opts.session)}` : ""}`;
375
455
  const data = await callLocalOwnerEndpoint(path, opts, {
376
456
  method: "GET",
377
457
  });
458
+ if (taskId && opts.context) {
459
+ const contextPacket = data.contextSession?.contextPacket;
460
+ if (typeof contextPacket === "string" && contextPacket.length > 0) {
461
+ process.stdout.write(contextPacket);
462
+ if (!contextPacket.endsWith("\n"))
463
+ process.stdout.write("\n");
464
+ return;
465
+ }
466
+ console.error("No TASK_CONTEXT.md content found for this task.");
467
+ process.exit(1);
468
+ }
378
469
  if (opts.json || taskId) {
379
470
  console.log(JSON.stringify(taskId ? data.task : data, null, 2));
380
471
  return;
381
472
  }
382
473
  printSoftwareAgentTaskList(Array.isArray(data.tasks) ? data.tasks : []);
383
474
  });
475
+ program
476
+ .command("software-agent-sessions")
477
+ .description("List or inspect owner-only Akemon-side software-agent context sessions")
478
+ .argument("[sessionId]", "Context session id to inspect")
479
+ .option("-p, --port <port>", "Local akemon serve port", "3000")
480
+ .option("-l, --limit <n>", "Maximum recent sessions to list", "20")
481
+ .option("--context", "Print the session TASK_CONTEXT.md content")
482
+ .option("--json", "Print raw JSON")
483
+ .action(async (sessionId, opts) => {
484
+ const query = sessionId && opts.context ? "?includeContext=1" : "";
485
+ const path = sessionId
486
+ ? `/self/software-agent/sessions/${encodeURIComponent(sessionId)}${query}`
487
+ : `/self/software-agent/sessions?limit=${clampPositiveInt(opts.limit, 20, 100)}`;
488
+ const data = await callLocalOwnerEndpoint(path, opts, {
489
+ method: "GET",
490
+ });
491
+ if (sessionId) {
492
+ if (opts.context) {
493
+ const contextPacket = data.session?.contextPacket;
494
+ if (typeof contextPacket === "string" && contextPacket.length > 0) {
495
+ process.stdout.write(contextPacket);
496
+ if (!contextPacket.endsWith("\n"))
497
+ process.stdout.write("\n");
498
+ return;
499
+ }
500
+ console.error("No TASK_CONTEXT.md content found for this session.");
501
+ process.exit(1);
502
+ }
503
+ console.log(JSON.stringify(data.session, null, 2));
504
+ return;
505
+ }
506
+ if (opts.json) {
507
+ console.log(JSON.stringify(data, null, 2));
508
+ return;
509
+ }
510
+ printSoftwareAgentSessionList(Array.isArray(data.sessions) ? data.sessions : []);
511
+ });
384
512
  program
385
513
  .command("software-agent-reset")
386
514
  .description("Reset the owner-only local software-agent peripheral session")
@@ -431,6 +559,69 @@ program
431
559
  throw error;
432
560
  }
433
561
  });
562
+ program
563
+ .command("work-context")
564
+ .description("Print a work-memory context packet for external software agents")
565
+ .option("-w, --workdir <path>", "Akemon workdir (default: cwd)")
566
+ .option("-n, --name <name>", "Agent name", "my-agent")
567
+ .option("--purpose <text>", "Purpose of this context packet", "external software-agent work context")
568
+ .option("--budget <chars>", "Maximum packet size in characters", "12000")
569
+ .option("--json", "Print raw JSON")
570
+ .action(async (opts) => {
571
+ try {
572
+ const packet = await buildWorkMemoryContext({
573
+ workdir: opts.workdir || process.cwd(),
574
+ agentName: opts.name,
575
+ purpose: opts.purpose,
576
+ budget: parsePositiveIntCliOption(opts.budget, "--budget"),
577
+ });
578
+ if (opts.json) {
579
+ console.log(JSON.stringify(packet, null, 2));
580
+ return;
581
+ }
582
+ process.stdout.write(packet.text);
583
+ if (!packet.text.endsWith("\n"))
584
+ process.stdout.write("\n");
585
+ }
586
+ catch (error) {
587
+ console.error(error instanceof Error ? error.message : String(error));
588
+ process.exit(1);
589
+ }
590
+ });
591
+ program
592
+ .command("work-note")
593
+ .description("Append a note to Akemon work memory")
594
+ .argument("<text...>", "Durable work-memory note")
595
+ .option("-w, --workdir <path>", "Akemon workdir (default: cwd)")
596
+ .option("-n, --name <name>", "Agent name", "my-agent")
597
+ .option("--source <source>", "Note source, e.g. user, codex, or claude-code", "user")
598
+ .option("--session <id>", "External or Akemon-side session id")
599
+ .option("--kind <kind>", "Work-memory kind, e.g. note, decision, command, project", "note")
600
+ .option("--target <path>", "Optional target file under the work memory directory")
601
+ .option("--json", "Print raw JSON")
602
+ .action(async (textParts, opts) => {
603
+ try {
604
+ const result = await appendWorkMemoryNote({
605
+ workdir: opts.workdir || process.cwd(),
606
+ agentName: opts.name,
607
+ text: textParts.join(" "),
608
+ source: opts.source,
609
+ sessionId: opts.session,
610
+ kind: opts.kind,
611
+ target: opts.target,
612
+ });
613
+ if (opts.json) {
614
+ console.log(JSON.stringify(result, null, 2));
615
+ return;
616
+ }
617
+ console.log(`Work memory note appended: ${result.note.id}`);
618
+ console.log(`Path: ${result.path}`);
619
+ }
620
+ catch (error) {
621
+ console.error(error instanceof Error ? error.message : String(error));
622
+ process.exit(1);
623
+ }
624
+ });
434
625
  program
435
626
  .command("dashboard")
436
627
  .description("Open your agent dashboard in the browser")