synomem 0.1.0 → 0.2.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/ARCHITECTURE.md +2 -2
- package/CHANGELOG.md +37 -1
- package/README.md +28 -15
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +311 -70
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +96 -17
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +333 -76
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.js +8 -8
- package/dist/import.d.ts +207 -13
- package/dist/import.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +156 -31
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp-server.js +54 -8
- package/dist/mcp-server.js.map +1 -1
- package/dist/ports/repository.d.ts +18 -1
- package/dist/ports/repository.d.ts.map +1 -1
- package/dist/projections.d.ts +18 -1
- package/dist/projections.d.ts.map +1 -1
- package/dist/projections.js +137 -44
- package/dist/projections.js.map +1 -1
- package/dist/remote.d.ts +57 -9
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +43 -2
- package/dist/remote.js.map +1 -1
- package/dist/schemas.d.ts +302 -21
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +142 -26
- package/dist/schemas.js.map +1 -1
- package/dist/service.d.ts +57 -10
- package/dist/service.d.ts.map +1 -1
- package/dist/skill-install.d.ts +8 -2
- package/dist/skill-install.d.ts.map +1 -1
- package/dist/skill-install.js +6 -11
- package/dist/skill-install.js.map +1 -1
- package/dist/storage.d.ts +41 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +254 -36
- package/dist/storage.js.map +1 -1
- package/dist/types.d.ts +213 -38
- package/dist/types.d.ts.map +1 -1
- package/docs/cli.md +53 -20
- package/docs/examples.md +4 -4
- package/docs/mcp.md +12 -4
- package/docs/skill.md +10 -7
- package/docs/storage-format.md +4 -4
- package/openapi/synomem-v1.yaml +24 -24
- package/package.json +1 -1
- package/skills/synomem/SKILL.md +24 -8
- package/skills/synomem/agents/openai.yaml +1 -1
- package/skills/synomem/references/examples.md +1 -1
- package/src/cli.ts +572 -173
- package/src/client.ts +391 -79
- package/src/config.ts +8 -8
- package/src/index.ts +11 -1
- package/src/mcp/index.ts +189 -30
- package/src/mcp-server.ts +58 -8
- package/src/ports/repository.ts +16 -0
- package/src/projections.ts +139 -44
- package/src/remote.ts +118 -8
- package/src/schemas.ts +147 -26
- package/src/service.ts +59 -7
- package/src/skill-install.ts +14 -17
- package/src/storage.ts +326 -34
- package/src/types.ts +228 -39
package/src/cli.ts
CHANGED
|
@@ -34,7 +34,7 @@ import type {
|
|
|
34
34
|
KudosSummary,
|
|
35
35
|
ItemListInput,
|
|
36
36
|
ItemSummary,
|
|
37
|
-
|
|
37
|
+
TaskDue,
|
|
38
38
|
} from './types.js';
|
|
39
39
|
import { packageVersion } from './version.js';
|
|
40
40
|
import type { SynomemService, SynomemServiceFactory } from './service.js';
|
|
@@ -109,11 +109,27 @@ function actor(kind: string, id: string, displayName?: string): ActorIdentity {
|
|
|
109
109
|
};
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
/**
|
|
113
|
+
* The acting identity for commands that do not take an explicit actor.
|
|
114
|
+
* Local storage accepts any actor, so these fall back to the historical CLI defaults; a remote
|
|
115
|
+
* backend binds the credential to one actor, so SYNOMEM_ACTOR_ID/KIND/NAME must be able to
|
|
116
|
+
* override them or those commands cannot authenticate.
|
|
117
|
+
*/
|
|
118
|
+
function defaultActor(
|
|
119
|
+
env: NodeJS.ProcessEnv,
|
|
120
|
+
fallbackKind: string,
|
|
121
|
+
fallbackId: string,
|
|
122
|
+
): ActorIdentity {
|
|
123
|
+
const id = env.SYNOMEM_ACTOR_ID?.trim();
|
|
124
|
+
if (!id) return actor(fallbackKind, fallbackId);
|
|
125
|
+
return actor(env.SYNOMEM_ACTOR_KIND?.trim() || fallbackKind, id, env.SYNOMEM_ACTOR_NAME?.trim());
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function taskDue(options: {
|
|
113
129
|
dueDate?: string;
|
|
114
130
|
dueAt?: string;
|
|
115
131
|
timeZone?: string;
|
|
116
|
-
}):
|
|
132
|
+
}): TaskDue | undefined {
|
|
117
133
|
if (options.dueDate && options.dueAt)
|
|
118
134
|
throw new SynomemError('INVALID_INPUT', 'Use due-date or due-at, not both.');
|
|
119
135
|
if (options.dueDate) return { kind: 'date', date: options.dueDate };
|
|
@@ -386,7 +402,7 @@ export function createCli(
|
|
|
386
402
|
if (persisted?.backend.kind === 'remote') {
|
|
387
403
|
throw new SynomemError('INVALID_INPUT', 'The init command requires a local backend.');
|
|
388
404
|
}
|
|
389
|
-
await withClient(options.home,
|
|
405
|
+
await withClient(options.home, defaultActor(env, 'system', 'cli'), async (client) => {
|
|
390
406
|
const info = await client.info();
|
|
391
407
|
if (info.backend !== 'local') {
|
|
392
408
|
throw new SynomemError('INVALID_INPUT', 'The init command requires a local backend.');
|
|
@@ -595,13 +611,16 @@ export function createCli(
|
|
|
595
611
|
command: Command,
|
|
596
612
|
) => {
|
|
597
613
|
const global = globals(command);
|
|
598
|
-
const profile = await withClient(
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
614
|
+
const profile = await withClient(
|
|
615
|
+
global.home,
|
|
616
|
+
defaultActor(env, 'system', 'cli'),
|
|
617
|
+
(client) =>
|
|
618
|
+
client.agents.create({
|
|
619
|
+
id,
|
|
620
|
+
displayName: options.name,
|
|
621
|
+
...(options.alias.length ? { aliases: options.alias } : {}),
|
|
622
|
+
...(options.description ? { description: options.description } : {}),
|
|
623
|
+
}),
|
|
605
624
|
);
|
|
606
625
|
output(io, global.json, profile, `Created ${profile.displayName} (${profile.id})`);
|
|
607
626
|
},
|
|
@@ -618,29 +637,73 @@ export function createCli(
|
|
|
618
637
|
.option('--yes', 'apply the displayed plan', false)
|
|
619
638
|
.option('--force', 'replace a conflicting synomem directory', false)
|
|
620
639
|
.option('--link', 'symlink to the packaged skill instead of copying it', false)
|
|
621
|
-
.option('--
|
|
622
|
-
.option('--actor-name <name>', 'display name used in MCP registration commands')
|
|
640
|
+
.option('--agent <id-or-alias>', 'bind this installation to an agent')
|
|
623
641
|
.action(
|
|
624
|
-
(
|
|
642
|
+
async (
|
|
625
643
|
options: {
|
|
626
644
|
runtime: string[];
|
|
627
645
|
yes: boolean;
|
|
628
646
|
force: boolean;
|
|
629
647
|
link: boolean;
|
|
630
|
-
|
|
631
|
-
actorName?: string;
|
|
648
|
+
agent?: string;
|
|
632
649
|
},
|
|
633
650
|
command: Command,
|
|
634
651
|
) => {
|
|
635
652
|
const global = globals(command);
|
|
653
|
+
const runtimes = skillRuntimes(options.runtime);
|
|
654
|
+
|
|
655
|
+
/*
|
|
656
|
+
* The agent is resolved BEFORE anything is written. An ambiguous or
|
|
657
|
+
* unknown name then stops the command with a name to fix, rather than
|
|
658
|
+
* leaving a skill installed and pointed at an agent that does not
|
|
659
|
+
* exist.
|
|
660
|
+
*/
|
|
661
|
+
let agentId: string | undefined;
|
|
662
|
+
if (options.agent) {
|
|
663
|
+
agentId = await withClient(
|
|
664
|
+
global.home,
|
|
665
|
+
defaultActor(env, 'system', 'cli'),
|
|
666
|
+
async (client) => {
|
|
667
|
+
const resolution = await client.agents.resolve(options.agent!);
|
|
668
|
+
if (!resolution.match) {
|
|
669
|
+
throw new SynomemError(
|
|
670
|
+
'AGENT_NOT_FOUND',
|
|
671
|
+
resolution.candidates.length
|
|
672
|
+
? `"${options.agent}" matches ${resolution.candidates.length} agents: ${resolution.candidates
|
|
673
|
+
.map((candidate) => candidate.id)
|
|
674
|
+
.join(', ')}. Name one of them.`
|
|
675
|
+
: `Unknown agent: ${options.agent}`,
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
return resolution.match.id;
|
|
679
|
+
},
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
|
|
636
683
|
const result = installSkill({
|
|
637
|
-
runtimes:
|
|
684
|
+
...(runtimes ? { runtimes } : {}),
|
|
638
685
|
apply: options.yes,
|
|
639
686
|
force: options.force,
|
|
640
687
|
link: options.link,
|
|
641
|
-
|
|
642
|
-
actorName: options.actorName,
|
|
688
|
+
...(agentId ? { agentId } : {}),
|
|
643
689
|
});
|
|
690
|
+
|
|
691
|
+
// Bindings follow what was actually installed, and only on a real run:
|
|
692
|
+
// a dry run must not claim a binding it did not make, and a runtime
|
|
693
|
+
// whose harness is not present here is not somewhere this agent runs.
|
|
694
|
+
if (agentId && options.yes) {
|
|
695
|
+
const installed = result.locations
|
|
696
|
+
.filter((location) => location.state !== 'unavailable')
|
|
697
|
+
.map((location) => location.runtime);
|
|
698
|
+
if (installed.length) {
|
|
699
|
+
await withClient(global.home, defaultActor(env, 'system', 'cli'), async (client) => {
|
|
700
|
+
for (const runtime of installed) {
|
|
701
|
+
await client.agents.bindRuntime({ agentId, runtime });
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
644
707
|
output(io, global.json, result, formatSkillResult(result, 'install'));
|
|
645
708
|
},
|
|
646
709
|
);
|
|
@@ -649,19 +712,15 @@ export function createCli(
|
|
|
649
712
|
.command('status')
|
|
650
713
|
.description('Show installed, stale, missing, or conflicting skill copies')
|
|
651
714
|
.option('--runtime <runtime>', `${skillRuntimeHelp} (repeatable)`, collect, [])
|
|
652
|
-
.option('--
|
|
653
|
-
.
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
});
|
|
662
|
-
output(io, global.json, result, formatSkillResult(result, 'status'));
|
|
663
|
-
},
|
|
664
|
-
);
|
|
715
|
+
.option('--agent <id>', 'print the registration command for this agent')
|
|
716
|
+
.action((options: { runtime: string[]; agent?: string }, command: Command) => {
|
|
717
|
+
const global = globals(command);
|
|
718
|
+
const result = skillStatus({
|
|
719
|
+
runtimes: skillRuntimes(options.runtime),
|
|
720
|
+
...(options.agent ? { agentId: options.agent } : {}),
|
|
721
|
+
});
|
|
722
|
+
output(io, global.json, result, formatSkillResult(result, 'status'));
|
|
723
|
+
});
|
|
665
724
|
|
|
666
725
|
skillCommand
|
|
667
726
|
.command('uninstall')
|
|
@@ -684,7 +743,7 @@ export function createCli(
|
|
|
684
743
|
.description('List known agent identities')
|
|
685
744
|
.action(async (_options, command: Command) => {
|
|
686
745
|
const global = globals(command);
|
|
687
|
-
const agents = await withClient(global.home,
|
|
746
|
+
const agents = await withClient(global.home, defaultActor(env, 'system', 'cli'), (client) =>
|
|
688
747
|
client.agents.list(),
|
|
689
748
|
);
|
|
690
749
|
const human = agents.length
|
|
@@ -703,7 +762,7 @@ export function createCli(
|
|
|
703
762
|
.description('Show one agent profile, resolving aliases')
|
|
704
763
|
.action(async (id: string, _options, command: Command) => {
|
|
705
764
|
const global = globals(command);
|
|
706
|
-
const profile = await withClient(global.home,
|
|
765
|
+
const profile = await withClient(global.home, defaultActor(env, 'system', 'cli'), (client) =>
|
|
707
766
|
client.agents.get(id),
|
|
708
767
|
);
|
|
709
768
|
output(
|
|
@@ -729,17 +788,141 @@ export function createCli(
|
|
|
729
788
|
) => {
|
|
730
789
|
const global = globals(command);
|
|
731
790
|
const hasAliases = options.clearAliases || options.alias.length > 0;
|
|
732
|
-
const profile = await withClient(
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
791
|
+
const profile = await withClient(
|
|
792
|
+
global.home,
|
|
793
|
+
defaultActor(env, 'system', 'cli'),
|
|
794
|
+
(client) =>
|
|
795
|
+
client.agents.update(id, {
|
|
796
|
+
...(options.name ? { displayName: options.name } : {}),
|
|
797
|
+
...(hasAliases ? { aliases: options.clearAliases ? [] : options.alias } : {}),
|
|
798
|
+
...(options.description !== undefined ? { description: options.description } : {}),
|
|
799
|
+
}),
|
|
738
800
|
);
|
|
739
801
|
output(io, global.json, profile, `Updated ${profile.displayName} (${profile.id})`);
|
|
740
802
|
},
|
|
741
803
|
);
|
|
742
804
|
|
|
805
|
+
agentCommand
|
|
806
|
+
.command('resolve <name>')
|
|
807
|
+
.description('Resolve a name or alias to one agent, or list the candidates')
|
|
808
|
+
.action(async (name: string, _options, command: Command) => {
|
|
809
|
+
const global = globals(command);
|
|
810
|
+
const resolution = await withClient(
|
|
811
|
+
global.home,
|
|
812
|
+
defaultActor(env, 'system', 'cli'),
|
|
813
|
+
(client) => client.agents.resolve(name),
|
|
814
|
+
);
|
|
815
|
+
// An ambiguous name is a question, not a failure: exit zero and show the
|
|
816
|
+
// candidates so the caller can pick one.
|
|
817
|
+
const human = resolution.match
|
|
818
|
+
? `${resolution.match.displayName} (${resolution.match.id})`
|
|
819
|
+
: resolution.candidates.length
|
|
820
|
+
? `"${resolution.query}" is ambiguous. Candidates:\n${resolution.candidates
|
|
821
|
+
.map((profile) => ` ${profile.id} ${profile.displayName}`)
|
|
822
|
+
.join('\n')}`
|
|
823
|
+
: `No agent answers to "${resolution.query}".`;
|
|
824
|
+
output(io, global.json, resolution, human);
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
agentCommand
|
|
828
|
+
.command('directory')
|
|
829
|
+
.description('List agents with their runtime bindings')
|
|
830
|
+
.action(async (_options, command: Command) => {
|
|
831
|
+
const global = globals(command);
|
|
832
|
+
const entries = await withClient(global.home, defaultActor(env, 'system', 'cli'), (client) =>
|
|
833
|
+
client.agents.directory(),
|
|
834
|
+
);
|
|
835
|
+
const human = entries.length
|
|
836
|
+
? entries
|
|
837
|
+
.map((entry) => {
|
|
838
|
+
const runtimes = entry.runtimeBindings.length
|
|
839
|
+
? entry.runtimeBindings
|
|
840
|
+
.map(
|
|
841
|
+
(binding) =>
|
|
842
|
+
` ${binding.runtime}${binding.profile ? `/${binding.profile}` : ''}` +
|
|
843
|
+
// Last-seen is advisory, so it is labelled as an
|
|
844
|
+
// observation rather than a status.
|
|
845
|
+
`${binding.lastSeenAt ? ` last seen ${binding.lastSeenAt}` : ' not yet seen'}`,
|
|
846
|
+
)
|
|
847
|
+
.join('\n')
|
|
848
|
+
: ' no runtime bindings';
|
|
849
|
+
return `${entry.profile.id} ${entry.profile.displayName}\n${runtimes}`;
|
|
850
|
+
})
|
|
851
|
+
.join('\n')
|
|
852
|
+
: 'No agents configured.';
|
|
853
|
+
output(io, global.json, { entries }, human);
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
const runtimeCommand = agentCommand.command('runtime').description('Record where an agent runs');
|
|
857
|
+
|
|
858
|
+
runtimeCommand
|
|
859
|
+
.command('bind <agent>')
|
|
860
|
+
.description('Bind an agent to a runtime')
|
|
861
|
+
.requiredOption('--runtime <name>', 'runtime family, e.g. claude-code')
|
|
862
|
+
.option('--profile <name>', 'named configuration within the runtime')
|
|
863
|
+
.option('--installation <id>', 'hosted installation this binding belongs to')
|
|
864
|
+
.action(
|
|
865
|
+
async (
|
|
866
|
+
agent: string,
|
|
867
|
+
options: { runtime: string; profile?: string; installation?: string },
|
|
868
|
+
command: Command,
|
|
869
|
+
) => {
|
|
870
|
+
const global = globals(command);
|
|
871
|
+
const binding = await withClient(
|
|
872
|
+
global.home,
|
|
873
|
+
defaultActor(env, 'system', 'cli'),
|
|
874
|
+
(client) =>
|
|
875
|
+
client.agents.bindRuntime({
|
|
876
|
+
agentId: agent,
|
|
877
|
+
runtime: options.runtime,
|
|
878
|
+
...(options.profile ? { profile: options.profile } : {}),
|
|
879
|
+
...(options.installation ? { installationId: options.installation } : {}),
|
|
880
|
+
}),
|
|
881
|
+
);
|
|
882
|
+
output(
|
|
883
|
+
io,
|
|
884
|
+
global.json,
|
|
885
|
+
binding,
|
|
886
|
+
`Bound ${binding.agentId} to ${binding.runtime}${binding.profile ? `/${binding.profile}` : ''} (${binding.id})`,
|
|
887
|
+
);
|
|
888
|
+
},
|
|
889
|
+
);
|
|
890
|
+
|
|
891
|
+
runtimeCommand
|
|
892
|
+
.command('list <agent>')
|
|
893
|
+
.description('List an agent runtime bindings')
|
|
894
|
+
.action(async (agent: string, _options, command: Command) => {
|
|
895
|
+
const global = globals(command);
|
|
896
|
+
const bindings = await withClient(global.home, defaultActor(env, 'system', 'cli'), (client) =>
|
|
897
|
+
client.agents.bindings(agent),
|
|
898
|
+
);
|
|
899
|
+
const human = bindings.length
|
|
900
|
+
? bindings
|
|
901
|
+
.map(
|
|
902
|
+
(binding) =>
|
|
903
|
+
`${binding.id} ${binding.runtime}${binding.profile ? `/${binding.profile}` : ''} bound ${binding.boundAt}`,
|
|
904
|
+
)
|
|
905
|
+
.join('\n')
|
|
906
|
+
: 'No runtime bindings.';
|
|
907
|
+
output(io, global.json, { bindings }, human);
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
runtimeCommand
|
|
911
|
+
.command('unbind <binding-id>')
|
|
912
|
+
.description('Remove a runtime binding')
|
|
913
|
+
.action(async (bindingId: string, _options, command: Command) => {
|
|
914
|
+
const global = globals(command);
|
|
915
|
+
const removed = await withClient(global.home, defaultActor(env, 'system', 'cli'), (client) =>
|
|
916
|
+
client.agents.unbindRuntime(bindingId),
|
|
917
|
+
);
|
|
918
|
+
output(
|
|
919
|
+
io,
|
|
920
|
+
global.json,
|
|
921
|
+
{ removed },
|
|
922
|
+
removed ? `Removed binding ${bindingId}.` : `No binding ${bindingId}.`,
|
|
923
|
+
);
|
|
924
|
+
});
|
|
925
|
+
|
|
743
926
|
const kudosCommand = program.command('kudos').description('Give and manage agent recognition');
|
|
744
927
|
|
|
745
928
|
kudosCommand
|
|
@@ -797,7 +980,7 @@ export function createCli(
|
|
|
797
980
|
|
|
798
981
|
program
|
|
799
982
|
.command('inbox [agent]')
|
|
800
|
-
.description('Show pending kudos, memos, and
|
|
983
|
+
.description('Show pending kudos, memos, and tasks for an agent')
|
|
801
984
|
.option('--as <agent-id>', 'defaults to the positional agent')
|
|
802
985
|
.option('--limit <number>', 'maximum results (default 10, maximum 50)', '10')
|
|
803
986
|
.option('--cursor <cursor>', 'opaque cursor returned by the previous page')
|
|
@@ -835,8 +1018,10 @@ export function createCli(
|
|
|
835
1018
|
addListOptions(kudosCommand.command('list').description('List and filter kudos')).action(
|
|
836
1019
|
async (options: Record<string, string>, command: Command) => {
|
|
837
1020
|
const global = globals(command);
|
|
838
|
-
const page = await withClient(
|
|
839
|
-
|
|
1021
|
+
const page = await withClient(
|
|
1022
|
+
global.home,
|
|
1023
|
+
defaultActor(env, 'human', 'local-cli'),
|
|
1024
|
+
(client) => client.kudos.list(listInput(options)),
|
|
840
1025
|
);
|
|
841
1026
|
output(
|
|
842
1027
|
io,
|
|
@@ -852,7 +1037,7 @@ export function createCli(
|
|
|
852
1037
|
program
|
|
853
1038
|
.command('list')
|
|
854
1039
|
.description('List compact summaries across all record types')
|
|
855
|
-
.option('--kind <kind>', 'kudos, memo, note, or
|
|
1040
|
+
.option('--kind <kind>', 'kudos, memo, note, or task (repeatable)', collect, [])
|
|
856
1041
|
.option('--participant <agent>')
|
|
857
1042
|
.option('--actor <id>')
|
|
858
1043
|
.option('--tag <tag>')
|
|
@@ -863,8 +1048,10 @@ export function createCli(
|
|
|
863
1048
|
.option('--offset <number>', 'deprecated offset', '0')
|
|
864
1049
|
.action(async (options: Record<string, string | string[]>, command: Command) => {
|
|
865
1050
|
const global = globals(command);
|
|
866
|
-
const page = await withClient(
|
|
867
|
-
|
|
1051
|
+
const page = await withClient(
|
|
1052
|
+
global.home,
|
|
1053
|
+
defaultActor(env, 'human', 'local-cli'),
|
|
1054
|
+
(client) => client.items.list(itemListInput(options)),
|
|
868
1055
|
);
|
|
869
1056
|
output(
|
|
870
1057
|
io,
|
|
@@ -885,12 +1072,15 @@ export function createCli(
|
|
|
885
1072
|
.action(
|
|
886
1073
|
async (options: { after?: string; limit: string; kind: string[] }, command: Command) => {
|
|
887
1074
|
const global = globals(command);
|
|
888
|
-
const page = await withClient(
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
1075
|
+
const page = await withClient(
|
|
1076
|
+
global.home,
|
|
1077
|
+
defaultActor(env, 'human', 'local-cli'),
|
|
1078
|
+
(client) =>
|
|
1079
|
+
client.items.changes({
|
|
1080
|
+
limit: Number(options.limit),
|
|
1081
|
+
...(options.kind.length ? { kinds: options.kind as ItemListInput['kinds'] } : {}),
|
|
1082
|
+
...(options.after ? { after: options.after } : {}),
|
|
1083
|
+
}),
|
|
894
1084
|
);
|
|
895
1085
|
const human = page.items.length
|
|
896
1086
|
? `${page.items
|
|
@@ -964,21 +1154,26 @@ export function createCli(
|
|
|
964
1154
|
command: Command,
|
|
965
1155
|
) => {
|
|
966
1156
|
const global = globals(command);
|
|
967
|
-
const page = await withClient(
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1157
|
+
const page = await withClient(
|
|
1158
|
+
global.home,
|
|
1159
|
+
defaultActor(env, 'human', 'local-cli'),
|
|
1160
|
+
(client) =>
|
|
1161
|
+
client.memos.list({
|
|
1162
|
+
...(options.participant ? { participantAgentId: options.participant } : {}),
|
|
1163
|
+
...(options.status ? { status: options.status } : {}),
|
|
1164
|
+
limit: Number(options.limit),
|
|
1165
|
+
...(options.cursor ? { cursor: options.cursor } : {}),
|
|
1166
|
+
}),
|
|
974
1167
|
);
|
|
975
1168
|
output(io, global.json, page, page.items.map(lineForItem).join('\n') || 'No memos found.');
|
|
976
1169
|
},
|
|
977
1170
|
);
|
|
978
1171
|
memoCommand.command('show <memo-id>').action(async (id: string, _options, command: Command) => {
|
|
979
1172
|
const global = globals(command);
|
|
980
|
-
const record = await withClient(
|
|
981
|
-
|
|
1173
|
+
const record = await withClient(
|
|
1174
|
+
global.home,
|
|
1175
|
+
defaultActor(env, 'human', 'local-cli'),
|
|
1176
|
+
(client) => client.memos.get(id),
|
|
982
1177
|
);
|
|
983
1178
|
output(
|
|
984
1179
|
io,
|
|
@@ -1068,20 +1263,25 @@ export function createCli(
|
|
|
1068
1263
|
.action(
|
|
1069
1264
|
async (options: { owner?: string; status?: string; limit: string }, command: Command) => {
|
|
1070
1265
|
const global = globals(command);
|
|
1071
|
-
const page = await withClient(
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1266
|
+
const page = await withClient(
|
|
1267
|
+
global.home,
|
|
1268
|
+
defaultActor(env, 'human', 'local-cli'),
|
|
1269
|
+
(client) =>
|
|
1270
|
+
client.notes.list({
|
|
1271
|
+
...(options.owner ? { participantAgentId: options.owner } : {}),
|
|
1272
|
+
...(options.status ? { status: options.status } : {}),
|
|
1273
|
+
limit: Number(options.limit),
|
|
1274
|
+
}),
|
|
1077
1275
|
);
|
|
1078
1276
|
output(io, global.json, page, page.items.map(lineForItem).join('\n') || 'No notes found.');
|
|
1079
1277
|
},
|
|
1080
1278
|
);
|
|
1081
1279
|
noteCommand.command('show <note-id>').action(async (id: string, _options, command: Command) => {
|
|
1082
1280
|
const global = globals(command);
|
|
1083
|
-
const record = await withClient(
|
|
1084
|
-
|
|
1281
|
+
const record = await withClient(
|
|
1282
|
+
global.home,
|
|
1283
|
+
defaultActor(env, 'human', 'local-cli'),
|
|
1284
|
+
(client) => client.notes.get(id),
|
|
1085
1285
|
);
|
|
1086
1286
|
output(
|
|
1087
1287
|
io,
|
|
@@ -1155,8 +1355,163 @@ export function createCli(
|
|
|
1155
1355
|
},
|
|
1156
1356
|
);
|
|
1157
1357
|
|
|
1158
|
-
const todoCommand = program
|
|
1358
|
+
const todoCommand = program
|
|
1359
|
+
.command('todo')
|
|
1360
|
+
.description('Create and manage your own private reminders');
|
|
1159
1361
|
todoCommand
|
|
1362
|
+
.command('create')
|
|
1363
|
+
.requiredOption('--as <actor-id>')
|
|
1364
|
+
.option('--actor-kind <kind>', 'human, agent, or system', 'agent')
|
|
1365
|
+
.requiredOption('--title <title>')
|
|
1366
|
+
.option('--details <text>', 'private working detail')
|
|
1367
|
+
.option('--priority <number>', '1 highest, 4 lowest', '3')
|
|
1368
|
+
.option('--due-date <date>')
|
|
1369
|
+
.option('--due-at <datetime>')
|
|
1370
|
+
.option('--time-zone <iana-zone>')
|
|
1371
|
+
.option('--tag <tag>', 'tag (repeatable)', collect, [])
|
|
1372
|
+
.option('--idempotency-key <key>')
|
|
1373
|
+
.action(
|
|
1374
|
+
async (
|
|
1375
|
+
options: {
|
|
1376
|
+
as: string;
|
|
1377
|
+
actorKind: string;
|
|
1378
|
+
title: string;
|
|
1379
|
+
details?: string;
|
|
1380
|
+
priority: string;
|
|
1381
|
+
dueDate?: string;
|
|
1382
|
+
dueAt?: string;
|
|
1383
|
+
timeZone?: string;
|
|
1384
|
+
tag: string[];
|
|
1385
|
+
idempotencyKey?: string;
|
|
1386
|
+
},
|
|
1387
|
+
command: Command,
|
|
1388
|
+
) => {
|
|
1389
|
+
const global = globals(command);
|
|
1390
|
+
const result = await withClient(
|
|
1391
|
+
global.home,
|
|
1392
|
+
actor(options.actorKind, options.as),
|
|
1393
|
+
(client) =>
|
|
1394
|
+
client.todos.create({
|
|
1395
|
+
title: options.title,
|
|
1396
|
+
...(options.details ? { details: options.details } : {}),
|
|
1397
|
+
priority: Number(options.priority) as 1 | 2 | 3 | 4,
|
|
1398
|
+
...((due) => (due ? { due } : {}))(taskDue(options)),
|
|
1399
|
+
...(options.tag.length ? { tags: options.tag } : {}),
|
|
1400
|
+
...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),
|
|
1401
|
+
}),
|
|
1402
|
+
);
|
|
1403
|
+
output(
|
|
1404
|
+
io,
|
|
1405
|
+
global.json,
|
|
1406
|
+
result,
|
|
1407
|
+
`${result.deduplicated ? 'Found existing' : 'Created'} private todo\nTitle: ${result.record.current.title}\nID: ${result.record.event.id}`,
|
|
1408
|
+
);
|
|
1409
|
+
},
|
|
1410
|
+
);
|
|
1411
|
+
todoCommand
|
|
1412
|
+
.command('list')
|
|
1413
|
+
.requiredOption('--as <actor-id>')
|
|
1414
|
+
.option('--actor-kind <kind>', 'human, agent, or system', 'agent')
|
|
1415
|
+
.option('--status <status>')
|
|
1416
|
+
.option('--limit <number>', 'maximum results', '10')
|
|
1417
|
+
.action(
|
|
1418
|
+
async (
|
|
1419
|
+
options: { as: string; actorKind: string; status?: string; limit: string },
|
|
1420
|
+
command: Command,
|
|
1421
|
+
) => {
|
|
1422
|
+
const global = globals(command);
|
|
1423
|
+
const page = await withClient(global.home, actor(options.actorKind, options.as), (client) =>
|
|
1424
|
+
client.todos.list({
|
|
1425
|
+
...(options.status ? { status: options.status } : {}),
|
|
1426
|
+
limit: Number(options.limit),
|
|
1427
|
+
}),
|
|
1428
|
+
);
|
|
1429
|
+
output(
|
|
1430
|
+
io,
|
|
1431
|
+
global.json,
|
|
1432
|
+
page,
|
|
1433
|
+
page.items.length
|
|
1434
|
+
? page.items
|
|
1435
|
+
.map((item) => `${item.id} ${item.status.padEnd(9)} ${item.title}`)
|
|
1436
|
+
.join('\n')
|
|
1437
|
+
: 'No todos.',
|
|
1438
|
+
);
|
|
1439
|
+
},
|
|
1440
|
+
);
|
|
1441
|
+
todoCommand
|
|
1442
|
+
.command('show <todo-id>')
|
|
1443
|
+
.requiredOption('--as <actor-id>')
|
|
1444
|
+
.option('--actor-kind <kind>', 'human, agent, or system', 'agent')
|
|
1445
|
+
.action(async (id: string, options: { as: string; actorKind: string }, command: Command) => {
|
|
1446
|
+
const global = globals(command);
|
|
1447
|
+
const record = await withClient(global.home, actor(options.actorKind, options.as), (client) =>
|
|
1448
|
+
client.todos.get(id),
|
|
1449
|
+
);
|
|
1450
|
+
output(
|
|
1451
|
+
io,
|
|
1452
|
+
global.json,
|
|
1453
|
+
record,
|
|
1454
|
+
`${record.current.title}\nStatus: ${record.status}\nPriority: ${record.current.priority}\nVersion: ${record.current.version}${record.current.details ? `\n\n${record.current.details}` : ''}`,
|
|
1455
|
+
);
|
|
1456
|
+
});
|
|
1457
|
+
for (const operation of ['complete', 'reopen', 'cancel', 'archive'] as const) {
|
|
1458
|
+
todoCommand
|
|
1459
|
+
.command(`${operation} <todo-id>`)
|
|
1460
|
+
.requiredOption('--as <actor-id>')
|
|
1461
|
+
.option('--actor-kind <kind>', 'human, agent, or system', 'agent')
|
|
1462
|
+
.option('--note <text>')
|
|
1463
|
+
.option('--reason <text>')
|
|
1464
|
+
.option('--idempotency-key <key>')
|
|
1465
|
+
.action(
|
|
1466
|
+
async (
|
|
1467
|
+
id: string,
|
|
1468
|
+
options: {
|
|
1469
|
+
as: string;
|
|
1470
|
+
actorKind: string;
|
|
1471
|
+
note?: string;
|
|
1472
|
+
reason?: string;
|
|
1473
|
+
idempotencyKey?: string;
|
|
1474
|
+
},
|
|
1475
|
+
command: Command,
|
|
1476
|
+
) => {
|
|
1477
|
+
const global = globals(command);
|
|
1478
|
+
const record = await withClient(
|
|
1479
|
+
global.home,
|
|
1480
|
+
actor(options.actorKind, options.as),
|
|
1481
|
+
(client) =>
|
|
1482
|
+
operation === 'complete'
|
|
1483
|
+
? client.todos.complete({
|
|
1484
|
+
todoId: id,
|
|
1485
|
+
...(options.note ? { note: options.note } : {}),
|
|
1486
|
+
...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),
|
|
1487
|
+
})
|
|
1488
|
+
: operation === 'cancel'
|
|
1489
|
+
? client.todos.cancel({
|
|
1490
|
+
todoId: id,
|
|
1491
|
+
...(options.reason ? { reason: options.reason } : {}),
|
|
1492
|
+
...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),
|
|
1493
|
+
})
|
|
1494
|
+
: operation === 'archive'
|
|
1495
|
+
? client.todos.archive({
|
|
1496
|
+
todoId: id,
|
|
1497
|
+
...(options.idempotencyKey
|
|
1498
|
+
? { idempotencyKey: options.idempotencyKey }
|
|
1499
|
+
: {}),
|
|
1500
|
+
})
|
|
1501
|
+
: client.todos.reopen({
|
|
1502
|
+
todoId: id,
|
|
1503
|
+
...(options.idempotencyKey
|
|
1504
|
+
? { idempotencyKey: options.idempotencyKey }
|
|
1505
|
+
: {}),
|
|
1506
|
+
}),
|
|
1507
|
+
);
|
|
1508
|
+
output(io, global.json, record, `Todo ${id} is now ${record.status}.`);
|
|
1509
|
+
},
|
|
1510
|
+
);
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
const taskCommand = program.command('task').description('Create and manage agent tasks');
|
|
1514
|
+
taskCommand
|
|
1160
1515
|
.command('create <assignee>')
|
|
1161
1516
|
.requiredOption('--from <actor-id>')
|
|
1162
1517
|
.option('--actor-kind <kind>', 'human, agent, or system', 'agent')
|
|
@@ -1192,12 +1547,12 @@ export function createCli(
|
|
|
1192
1547
|
global.home,
|
|
1193
1548
|
actor(options.actorKind, options.from),
|
|
1194
1549
|
(client) =>
|
|
1195
|
-
client.
|
|
1550
|
+
client.tasks.create({
|
|
1196
1551
|
assigneeAgentId: assignee,
|
|
1197
1552
|
title: options.title,
|
|
1198
1553
|
...(options.description ? { description: options.description } : {}),
|
|
1199
1554
|
priority: Number(options.priority) as 1 | 2 | 3 | 4,
|
|
1200
|
-
...((due) => (due ? { due } : {}))(
|
|
1555
|
+
...((due) => (due ? { due } : {}))(taskDue(options)),
|
|
1201
1556
|
...(options.tag.length ? { tags: options.tag } : {}),
|
|
1202
1557
|
visibility: options.visibility,
|
|
1203
1558
|
...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),
|
|
@@ -1207,11 +1562,11 @@ export function createCli(
|
|
|
1207
1562
|
io,
|
|
1208
1563
|
global.json,
|
|
1209
1564
|
result,
|
|
1210
|
-
`${result.deduplicated ? 'Found existing' : 'Created'}
|
|
1565
|
+
`${result.deduplicated ? 'Found existing' : 'Created'} task for ${result.record.event.assigneeDisplayName}\nTitle: ${result.record.current.title}\nID: ${result.record.event.id}`,
|
|
1211
1566
|
);
|
|
1212
1567
|
},
|
|
1213
1568
|
);
|
|
1214
|
-
|
|
1569
|
+
taskCommand
|
|
1215
1570
|
.command('list')
|
|
1216
1571
|
.option('--assignee <agent>')
|
|
1217
1572
|
.option('--status <status>')
|
|
@@ -1219,20 +1574,25 @@ export function createCli(
|
|
|
1219
1574
|
.action(
|
|
1220
1575
|
async (options: { assignee?: string; status?: string; limit: string }, command: Command) => {
|
|
1221
1576
|
const global = globals(command);
|
|
1222
|
-
const page = await withClient(
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1577
|
+
const page = await withClient(
|
|
1578
|
+
global.home,
|
|
1579
|
+
defaultActor(env, 'human', 'local-cli'),
|
|
1580
|
+
(client) =>
|
|
1581
|
+
client.tasks.list({
|
|
1582
|
+
...(options.assignee ? { participantAgentId: options.assignee } : {}),
|
|
1583
|
+
...(options.status ? { status: options.status } : {}),
|
|
1584
|
+
limit: Number(options.limit),
|
|
1585
|
+
}),
|
|
1228
1586
|
);
|
|
1229
|
-
output(io, global.json, page, page.items.map(lineForItem).join('\n') || 'No
|
|
1587
|
+
output(io, global.json, page, page.items.map(lineForItem).join('\n') || 'No tasks found.');
|
|
1230
1588
|
},
|
|
1231
1589
|
);
|
|
1232
|
-
|
|
1590
|
+
taskCommand.command('show <task-id>').action(async (id: string, _options, command: Command) => {
|
|
1233
1591
|
const global = globals(command);
|
|
1234
|
-
const record = await withClient(
|
|
1235
|
-
|
|
1592
|
+
const record = await withClient(
|
|
1593
|
+
global.home,
|
|
1594
|
+
defaultActor(env, 'human', 'local-cli'),
|
|
1595
|
+
(client) => client.tasks.get(id),
|
|
1236
1596
|
);
|
|
1237
1597
|
output(
|
|
1238
1598
|
io,
|
|
@@ -1241,8 +1601,8 @@ export function createCli(
|
|
|
1241
1601
|
`${record.current.title}\nID: ${record.event.id}\nStatus: ${record.status}\nVersion: ${record.current.version}`,
|
|
1242
1602
|
);
|
|
1243
1603
|
});
|
|
1244
|
-
|
|
1245
|
-
.command('update <
|
|
1604
|
+
taskCommand
|
|
1605
|
+
.command('update <task-id>')
|
|
1246
1606
|
.requiredOption('--as <actor-id>')
|
|
1247
1607
|
.option('--actor-kind <kind>', 'agent or human', 'agent')
|
|
1248
1608
|
.requiredOption('--expected-version <number>')
|
|
@@ -1275,13 +1635,13 @@ export function createCli(
|
|
|
1275
1635
|
command: Command,
|
|
1276
1636
|
) => {
|
|
1277
1637
|
const global = globals(command);
|
|
1278
|
-
const parsedDue =
|
|
1638
|
+
const parsedDue = taskDue(options);
|
|
1279
1639
|
const record = await withClient(
|
|
1280
1640
|
global.home,
|
|
1281
1641
|
actor(options.actorKind, options.as),
|
|
1282
1642
|
(client) =>
|
|
1283
|
-
client.
|
|
1284
|
-
|
|
1643
|
+
client.tasks.update({
|
|
1644
|
+
taskId: id,
|
|
1285
1645
|
expectedVersion: Number(options.expectedVersion),
|
|
1286
1646
|
...(options.title ? { title: options.title } : {}),
|
|
1287
1647
|
...(options.description !== undefined ? { description: options.description } : {}),
|
|
@@ -1291,71 +1651,78 @@ export function createCli(
|
|
|
1291
1651
|
...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),
|
|
1292
1652
|
}),
|
|
1293
1653
|
);
|
|
1294
|
-
output(io, global.json, record, `Updated
|
|
1654
|
+
output(io, global.json, record, `Updated task ${id} to version ${record.current.version}.`);
|
|
1295
1655
|
},
|
|
1296
1656
|
);
|
|
1297
1657
|
for (const operation of ['accept', 'reject', 'complete', 'reopen', 'cancel'] as const) {
|
|
1298
|
-
|
|
1299
|
-
.command(`${operation} <
|
|
1658
|
+
const command_ = taskCommand
|
|
1659
|
+
.command(`${operation} <task-id>`)
|
|
1300
1660
|
.requiredOption('--as <actor-id>')
|
|
1301
1661
|
.option('--actor-kind <kind>', 'agent or human', 'agent')
|
|
1302
1662
|
.option('--note <text>')
|
|
1303
1663
|
.option('--reason <text>')
|
|
1304
|
-
.option('--idempotency-key <key>')
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1664
|
+
.option('--idempotency-key <key>');
|
|
1665
|
+
// Rejecting requires saying why; accepting may. Marked required at the
|
|
1666
|
+
// parser so the CLI refuses before touching the store.
|
|
1667
|
+
if (operation === 'reject') {
|
|
1668
|
+
command_.requiredOption('--response <text>', 'why the task is being refused');
|
|
1669
|
+
} else if (operation === 'accept') {
|
|
1670
|
+
command_.option('--response <text>', 'conditions, timing, or partial capability');
|
|
1671
|
+
}
|
|
1672
|
+
command_.action(
|
|
1673
|
+
async (
|
|
1674
|
+
id: string,
|
|
1675
|
+
options: {
|
|
1676
|
+
as: string;
|
|
1677
|
+
actorKind: string;
|
|
1678
|
+
note?: string;
|
|
1679
|
+
reason?: string;
|
|
1680
|
+
response?: string;
|
|
1681
|
+
idempotencyKey?: string;
|
|
1682
|
+
},
|
|
1683
|
+
command: Command,
|
|
1684
|
+
) => {
|
|
1685
|
+
const global = globals(command);
|
|
1686
|
+
const record = await withClient(
|
|
1687
|
+
global.home,
|
|
1688
|
+
actor(options.actorKind, options.as),
|
|
1689
|
+
(client) =>
|
|
1690
|
+
operation === 'accept'
|
|
1691
|
+
? client.tasks.accept({
|
|
1692
|
+
taskId: id,
|
|
1693
|
+
...(options.response ? { response: options.response } : {}),
|
|
1694
|
+
...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),
|
|
1695
|
+
})
|
|
1696
|
+
: operation === 'reject'
|
|
1697
|
+
? client.tasks.reject({
|
|
1698
|
+
taskId: id,
|
|
1699
|
+
response: options.response ?? options.reason ?? '',
|
|
1325
1700
|
...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),
|
|
1326
1701
|
})
|
|
1327
|
-
: operation === '
|
|
1328
|
-
? client.
|
|
1329
|
-
|
|
1330
|
-
...(options.
|
|
1702
|
+
: operation === 'complete'
|
|
1703
|
+
? client.tasks.complete({
|
|
1704
|
+
taskId: id,
|
|
1705
|
+
...(options.note ? { note: options.note } : {}),
|
|
1331
1706
|
...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),
|
|
1332
1707
|
})
|
|
1333
|
-
: operation === '
|
|
1334
|
-
? client.
|
|
1335
|
-
|
|
1336
|
-
...(options.
|
|
1708
|
+
: operation === 'cancel'
|
|
1709
|
+
? client.tasks.cancel({
|
|
1710
|
+
taskId: id,
|
|
1711
|
+
...(options.reason ? { reason: options.reason } : {}),
|
|
1337
1712
|
...(options.idempotencyKey
|
|
1338
1713
|
? { idempotencyKey: options.idempotencyKey }
|
|
1339
1714
|
: {}),
|
|
1340
1715
|
})
|
|
1341
|
-
:
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
...(options.idempotencyKey
|
|
1352
|
-
? { idempotencyKey: options.idempotencyKey }
|
|
1353
|
-
: {}),
|
|
1354
|
-
}),
|
|
1355
|
-
);
|
|
1356
|
-
output(io, global.json, record, `Todo ${id} is ${record.status}.`);
|
|
1357
|
-
},
|
|
1358
|
-
);
|
|
1716
|
+
: client.tasks.reopen({
|
|
1717
|
+
taskId: id,
|
|
1718
|
+
...(options.idempotencyKey
|
|
1719
|
+
? { idempotencyKey: options.idempotencyKey }
|
|
1720
|
+
: {}),
|
|
1721
|
+
}),
|
|
1722
|
+
);
|
|
1723
|
+
output(io, global.json, record, `Task ${id} is ${record.status}.`);
|
|
1724
|
+
},
|
|
1725
|
+
);
|
|
1359
1726
|
}
|
|
1360
1727
|
|
|
1361
1728
|
kudosCommand
|
|
@@ -1363,7 +1730,7 @@ export function createCli(
|
|
|
1363
1730
|
.description('Show one kudos item and its current state')
|
|
1364
1731
|
.action(async (id: string, _options, command: Command) => {
|
|
1365
1732
|
const global = globals(command);
|
|
1366
|
-
const record = await withClient(global.home,
|
|
1733
|
+
const record = await withClient(global.home, defaultActor(env, 'system', 'cli'), (client) =>
|
|
1367
1734
|
client.kudos.get(id),
|
|
1368
1735
|
);
|
|
1369
1736
|
output(io, global.json, record, showRecord(record));
|
|
@@ -1437,28 +1804,32 @@ export function createCli(
|
|
|
1437
1804
|
) => {
|
|
1438
1805
|
const global = globals(command);
|
|
1439
1806
|
if (!agentId) throw new SynomemError('INVALID_INPUT', 'Specify an agent.');
|
|
1440
|
-
const details = await withClient(
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
const
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1807
|
+
const details = await withClient(
|
|
1808
|
+
global.home,
|
|
1809
|
+
defaultActor(env, 'system', 'cli'),
|
|
1810
|
+
async (client) => {
|
|
1811
|
+
const profile = await client.agents.get(agentId);
|
|
1812
|
+
const info = await client.info();
|
|
1813
|
+
if (info.backend !== 'local') {
|
|
1814
|
+
throw new SynomemError(
|
|
1815
|
+
'INVALID_INPUT',
|
|
1816
|
+
'Generated WINS.md files are available only with the local backend.',
|
|
1817
|
+
);
|
|
1818
|
+
}
|
|
1819
|
+
const capabilities = await client.capabilities();
|
|
1820
|
+
const path = join(info.home, profile.id, 'WINS.md');
|
|
1821
|
+
if (!existsSync(path)) {
|
|
1822
|
+
const hint = capabilities.projections.writeWinsMarkdown
|
|
1823
|
+
? 'Run `synomem rebuild` to generate it.'
|
|
1824
|
+
: 'Enable projection.writeWinsMarkdown and run `synomem rebuild`.';
|
|
1825
|
+
throw new SynomemError(
|
|
1826
|
+
'INVALID_INPUT',
|
|
1827
|
+
`No generated WINS.md exists for ${profile.id}. ${hint}`,
|
|
1828
|
+
);
|
|
1829
|
+
}
|
|
1830
|
+
return { profile, path, content: readFileSync(path, 'utf8') };
|
|
1831
|
+
},
|
|
1832
|
+
);
|
|
1462
1833
|
if (options.open) {
|
|
1463
1834
|
const commandName =
|
|
1464
1835
|
process.platform === 'darwin'
|
|
@@ -1478,7 +1849,7 @@ export function createCli(
|
|
|
1478
1849
|
kudosCommand.command('stats').description('Show aggregate kudos statistics'),
|
|
1479
1850
|
).action(async (options: Record<string, string>, command: Command) => {
|
|
1480
1851
|
const global = globals(command);
|
|
1481
|
-
const stats = await withClient(global.home,
|
|
1852
|
+
const stats = await withClient(global.home, defaultActor(env, 'system', 'cli'), (client) =>
|
|
1482
1853
|
client.stats(listInput(options)),
|
|
1483
1854
|
);
|
|
1484
1855
|
output(
|
|
@@ -1494,7 +1865,7 @@ export function createCli(
|
|
|
1494
1865
|
.description('Regenerate current-state and filesystem projections from canonical events')
|
|
1495
1866
|
.action(async (_options, command: Command) => {
|
|
1496
1867
|
const global = globals(command);
|
|
1497
|
-
const result = await withClient(global.home,
|
|
1868
|
+
const result = await withClient(global.home, defaultActor(env, 'system', 'cli'), (client) =>
|
|
1498
1869
|
client.rebuild(),
|
|
1499
1870
|
);
|
|
1500
1871
|
output(
|
|
@@ -1510,7 +1881,7 @@ export function createCli(
|
|
|
1510
1881
|
.description('Create a transactionally consistent SQLite backup')
|
|
1511
1882
|
.action(async (destination: string, _options, command: Command) => {
|
|
1512
1883
|
const global = globals(command);
|
|
1513
|
-
const path = await withClient(global.home,
|
|
1884
|
+
const path = await withClient(global.home, defaultActor(env, 'system', 'cli'), (client) => {
|
|
1514
1885
|
if (!client.backup) {
|
|
1515
1886
|
throw new SynomemError(
|
|
1516
1887
|
'INVALID_INPUT',
|
|
@@ -1535,8 +1906,10 @@ export function createCli(
|
|
|
1535
1906
|
command: Command,
|
|
1536
1907
|
) => {
|
|
1537
1908
|
const global = globals(command);
|
|
1538
|
-
const content = await withClient(
|
|
1539
|
-
|
|
1909
|
+
const content = await withClient(
|
|
1910
|
+
global.home,
|
|
1911
|
+
defaultActor(env, 'system', 'cli'),
|
|
1912
|
+
(client) => client.export(options.format),
|
|
1540
1913
|
);
|
|
1541
1914
|
if (options.output) {
|
|
1542
1915
|
const destination = resolve(options.output);
|
|
@@ -1558,7 +1931,7 @@ export function createCli(
|
|
|
1558
1931
|
.description('Run safe diagnostics')
|
|
1559
1932
|
.action(async (_options, command: Command) => {
|
|
1560
1933
|
const global = globals(command);
|
|
1561
|
-
const result = await withClient(global.home,
|
|
1934
|
+
const result = await withClient(global.home, defaultActor(env, 'system', 'cli'), (client) =>
|
|
1562
1935
|
client.doctor(),
|
|
1563
1936
|
);
|
|
1564
1937
|
const human = result.diagnostics
|
|
@@ -1571,18 +1944,44 @@ export function createCli(
|
|
|
1571
1944
|
program
|
|
1572
1945
|
.command('mcp')
|
|
1573
1946
|
.description('Run the actor-bound MCP server over stdio')
|
|
1574
|
-
.
|
|
1575
|
-
.
|
|
1576
|
-
.option('--actor-
|
|
1947
|
+
.option('--agent-id <id>', 'bound agent, whose identity is read from Synomem')
|
|
1948
|
+
.option('--actor-id <id>', 'bound non-agent actor ID')
|
|
1949
|
+
.option('--actor-kind <kind>', 'human or system')
|
|
1950
|
+
.option('--actor-name <display-name>', 'display name for a non-agent actor')
|
|
1577
1951
|
.action(
|
|
1578
1952
|
async (
|
|
1579
|
-
options: { actorId
|
|
1953
|
+
options: { agentId?: string; actorId?: string; actorKind?: string; actorName?: string },
|
|
1580
1954
|
command: Command,
|
|
1581
1955
|
) => {
|
|
1582
1956
|
const global = globals(command);
|
|
1957
|
+
// An agent's name comes from its profile, never from the command line:
|
|
1958
|
+
// the name is written into every event the session appends, and a
|
|
1959
|
+
// harness must not be able to sign another agent's name to work.
|
|
1960
|
+
const bound = options.agentId
|
|
1961
|
+
? await withClient(global.home, defaultActor(env, 'system', 'cli'), async (client) => {
|
|
1962
|
+
const resolution = await client.agents.resolve(options.agentId!);
|
|
1963
|
+
if (!resolution.match) {
|
|
1964
|
+
throw new SynomemError(
|
|
1965
|
+
'AGENT_NOT_FOUND',
|
|
1966
|
+
resolution.candidates.length
|
|
1967
|
+
? `"${options.agentId}" matches ${resolution.candidates.length} agents: ${resolution.candidates
|
|
1968
|
+
.map((candidate) => candidate.id)
|
|
1969
|
+
.join(', ')}. Name one of them.`
|
|
1970
|
+
: `Unknown agent: ${options.agentId}`,
|
|
1971
|
+
);
|
|
1972
|
+
}
|
|
1973
|
+
return actor('agent', resolution.match.id, resolution.match.displayName);
|
|
1974
|
+
})
|
|
1975
|
+
: undefined;
|
|
1976
|
+
if (!bound && !(options.actorId && options.actorKind)) {
|
|
1977
|
+
throw new SynomemError(
|
|
1978
|
+
'INVALID_INPUT',
|
|
1979
|
+
'Specify --agent-id, or --actor-id with --actor-kind for a non-agent actor.',
|
|
1980
|
+
);
|
|
1981
|
+
}
|
|
1583
1982
|
await startMcpServer({
|
|
1584
1983
|
...(global.home ? { home: global.home } : {}),
|
|
1585
|
-
actor: actor(options.actorKind
|
|
1984
|
+
actor: bound ?? actor(options.actorKind!, options.actorId!, options.actorName),
|
|
1586
1985
|
});
|
|
1587
1986
|
},
|
|
1588
1987
|
);
|