@robota-sdk/agent-command 3.0.0-beta.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/LICENSE +21 -0
  2. package/dist/node/index.cjs +30 -0
  3. package/dist/node/index.d.ts +293 -0
  4. package/dist/node/index.d.ts.map +1 -0
  5. package/dist/node/index.js +31 -0
  6. package/dist/node/index.js.map +1 -0
  7. package/package.json +48 -0
  8. package/src/agent/__tests__/agent-command.test.ts +504 -0
  9. package/src/agent/agent-command-module.ts +82 -0
  10. package/src/agent/agent-command-parser.ts +180 -0
  11. package/src/agent/agent-command.ts +235 -0
  12. package/src/agent/index.ts +7 -0
  13. package/src/background/__tests__/background-command-module.test.ts +255 -0
  14. package/src/background/background-command-module.ts +53 -0
  15. package/src/background/background-command.ts +63 -0
  16. package/src/background/index.ts +6 -0
  17. package/src/compact/__tests__/compact-command-module.test.ts +162 -0
  18. package/src/compact/compact-command-module.ts +51 -0
  19. package/src/compact/compact-command.ts +21 -0
  20. package/src/compact/index.ts +6 -0
  21. package/src/context/__tests__/context-command-module.test.ts +294 -0
  22. package/src/context/context-command-module.ts +54 -0
  23. package/src/context/context-command.ts +298 -0
  24. package/src/context/index.ts +6 -0
  25. package/src/exit/__tests__/exit-command-module.test.ts +35 -0
  26. package/src/exit/exit-command-module.ts +48 -0
  27. package/src/exit/exit-command.ts +10 -0
  28. package/src/exit/index.ts +6 -0
  29. package/src/help/__tests__/help-command-module.test.ts +106 -0
  30. package/src/help/help-command-module.ts +48 -0
  31. package/src/help/help-command.ts +9 -0
  32. package/src/help/index.ts +6 -0
  33. package/src/index.ts +20 -0
  34. package/src/language/__tests__/language-command-module.test.ts +105 -0
  35. package/src/language/index.ts +6 -0
  36. package/src/language/language-command-module.ts +56 -0
  37. package/src/language/language-command.ts +22 -0
  38. package/src/memory/__tests__/memory-command-module.test.ts +272 -0
  39. package/src/memory/index.ts +6 -0
  40. package/src/memory/memory-command-module.ts +57 -0
  41. package/src/memory/memory-command.ts +234 -0
  42. package/src/mode/__tests__/mode-command-module.test.ts +143 -0
  43. package/src/mode/index.ts +6 -0
  44. package/src/mode/mode-command-module.ts +56 -0
  45. package/src/mode/mode-command.ts +34 -0
  46. package/src/model/__tests__/model-command-module.test.ts +273 -0
  47. package/src/model/index.ts +6 -0
  48. package/src/model/model-command-module.ts +68 -0
  49. package/src/model/model-command.ts +40 -0
  50. package/src/permissions/__tests__/permissions-command-module.test.ts +164 -0
  51. package/src/permissions/index.ts +6 -0
  52. package/src/permissions/permissions-command-module.ts +56 -0
  53. package/src/permissions/permissions-command.ts +45 -0
  54. package/src/plugin/__tests__/plugin-command-module.test.ts +214 -0
  55. package/src/plugin/index.ts +7 -0
  56. package/src/plugin/plugin-command-module.ts +81 -0
  57. package/src/plugin/plugin-command.ts +230 -0
  58. package/src/provider/__tests__/provider-command-module.test.ts +488 -0
  59. package/src/provider/__tests__/provider-setup-flow.test.ts +43 -0
  60. package/src/provider/index.ts +30 -0
  61. package/src/provider/provider-command-execution.ts +150 -0
  62. package/src/provider/provider-command-module.ts +65 -0
  63. package/src/provider/provider-command-profile-lifecycle.ts +211 -0
  64. package/src/provider/provider-command-profile-operations.ts +198 -0
  65. package/src/provider/provider-command-profile.ts +109 -0
  66. package/src/provider/provider-command-setup.ts +104 -0
  67. package/src/provider/provider-setup-flow.ts +309 -0
  68. package/src/reset/__tests__/reset-command-module.test.ts +63 -0
  69. package/src/reset/index.ts +2 -0
  70. package/src/reset/reset-command-module.ts +49 -0
  71. package/src/reset/reset-command.ts +10 -0
  72. package/src/rewind/__tests__/rewind-command-module.test.ts +215 -0
  73. package/src/rewind/index.ts +2 -0
  74. package/src/rewind/rewind-command-module.ts +57 -0
  75. package/src/rewind/rewind-command.ts +184 -0
  76. package/src/session/__tests__/session-command-module.test.ts +339 -0
  77. package/src/session/index.ts +17 -0
  78. package/src/session/session-command-module.ts +168 -0
  79. package/src/session/session-command.ts +74 -0
  80. package/src/settings/index.ts +7 -0
  81. package/src/settings/settings-command-module.ts +50 -0
  82. package/src/skills/__tests__/skills-command-module.test.ts +157 -0
  83. package/src/skills/index.ts +6 -0
  84. package/src/skills/skills-command-module.ts +62 -0
  85. package/src/skills/skills-command.ts +110 -0
  86. package/src/statusline/__tests__/statusline-command-module.test.ts +95 -0
  87. package/src/statusline/index.ts +6 -0
  88. package/src/statusline/statusline-command-module.ts +56 -0
  89. package/src/statusline/statusline-command.ts +79 -0
  90. package/src/user-local/__tests__/user-local-command.test.ts +145 -0
  91. package/src/user-local/index.ts +13 -0
  92. package/src/user-local/user-local-command-constants.ts +5 -0
  93. package/src/user-local/user-local-command-module.ts +67 -0
  94. package/src/user-local/user-local-command.ts +205 -0
  95. package/src/user-local/user-local-memory-command.ts +147 -0
@@ -0,0 +1,54 @@
1
+ import type {
2
+ ICommand,
3
+ ICommandModule,
4
+ ICommandSource,
5
+ ISystemCommand,
6
+ } from '@robota-sdk/agent-framework';
7
+ import { executeContextCommand } from './context-command.js';
8
+
9
+ export function createContextCommandEntry(): ICommand {
10
+ return {
11
+ name: 'context',
12
+ displayName: 'Context References',
13
+ description: 'Context window info, reference inventory, and auto-compact controls',
14
+ source: 'context',
15
+ modelInvocable: false,
16
+ argumentHint: 'list | add <path> | remove <path> | clear | auto ...',
17
+ subcommands: [
18
+ { name: 'list', description: 'List loaded context references', source: 'context' },
19
+ { name: 'add', description: 'Add a file to active context references', source: 'context' },
20
+ { name: 'remove', description: 'Remove a context reference', source: 'context' },
21
+ { name: 'clear', description: 'Clear context references', source: 'context' },
22
+ { name: 'auto', description: 'Inspect or change auto-compact policy', source: 'context' },
23
+ ],
24
+ };
25
+ }
26
+
27
+ function createContextSystemCommand(): ISystemCommand {
28
+ const entry = createContextCommandEntry();
29
+ return {
30
+ name: entry.name,
31
+ displayName: entry.displayName,
32
+ description: entry.description,
33
+ requiresPermission: false,
34
+ userInvocable: true,
35
+ modelInvocable: false,
36
+ execute: executeContextCommand,
37
+ };
38
+ }
39
+
40
+ export class ContextCommandSource implements ICommandSource {
41
+ readonly name = 'context';
42
+
43
+ getCommands(): ICommand[] {
44
+ return [createContextCommandEntry()];
45
+ }
46
+ }
47
+
48
+ export function createContextCommandModule(): ICommandModule {
49
+ return {
50
+ name: 'agent-command-context',
51
+ commandSources: [new ContextCommandSource()],
52
+ systemCommands: [createContextSystemCommand()],
53
+ };
54
+ }
@@ -0,0 +1,298 @@
1
+ import type {
2
+ ICommandHostContext,
3
+ ICommandResult,
4
+ TAutoCompactThreshold,
5
+ TAutoCompactThresholdSource,
6
+ } from '@robota-sdk/agent-framework';
7
+ import {
8
+ addCommandContextReference,
9
+ clearCommandContextReferences,
10
+ DEFAULT_AUTO_COMPACT_THRESHOLD,
11
+ listCommandContextReferences,
12
+ readAutoCompactThreshold,
13
+ readAutoCompactThresholdSource,
14
+ readCommandContextState,
15
+ removeCommandContextReference,
16
+ resetAutoCompactThresholdSetting,
17
+ setCommandAutoCompactThreshold,
18
+ writeAutoCompactThresholdSetting,
19
+ } from '@robota-sdk/agent-framework';
20
+ import type { IContextReferenceItem } from '@robota-sdk/agent-framework';
21
+
22
+ const PERCENT = 100;
23
+ const USAGE = [
24
+ 'Usage: /context [list] | add <path> | remove <path> | clear | auto on | off | <percent> | reset',
25
+ 'Examples: /context list, /context add AGENTS.md, /context remove AGENTS.md, /context auto 85%',
26
+ ].join('\n');
27
+
28
+ function formatThreshold(threshold: TAutoCompactThreshold): string {
29
+ if (threshold === false) {
30
+ return 'disabled';
31
+ }
32
+ return `${Math.round(threshold * PERCENT)}%`;
33
+ }
34
+
35
+ function formatAutoCompactLine(
36
+ threshold: TAutoCompactThreshold,
37
+ source: TAutoCompactThresholdSource,
38
+ ): string {
39
+ if (threshold === false) {
40
+ return `Auto compact: disabled (${source})`;
41
+ }
42
+ return `Auto compact: ${formatThreshold(threshold)} (${source})`;
43
+ }
44
+
45
+ function formatPersistenceSuffix(persisted: boolean): string {
46
+ return persisted ? 'settings' : 'current session only';
47
+ }
48
+
49
+ export async function executeContextCommand(
50
+ context: ICommandHostContext,
51
+ args: string,
52
+ ): Promise<ICommandResult> {
53
+ const parts = args
54
+ .trim()
55
+ .split(/\s+/)
56
+ .filter((part) => part.length > 0);
57
+
58
+ if (parts.length > 0) {
59
+ return executeContextSubcommand(context, parts);
60
+ }
61
+
62
+ const state = readCommandContextState(context);
63
+ const autoCompactThreshold = readAutoCompactThreshold(context);
64
+ const autoCompactThresholdSource = readAutoCompactThresholdSource(context);
65
+ return {
66
+ message: [
67
+ `Context: ${state.usedTokens.toLocaleString()} / ${state.maxTokens.toLocaleString()} tokens (${Math.round(state.usedPercentage)}%)`,
68
+ formatAutoCompactLine(autoCompactThreshold, autoCompactThresholdSource),
69
+ formatContextReferenceSummary(listCommandContextReferences(context)),
70
+ ].join('\n'),
71
+ success: true,
72
+ data: {
73
+ usedTokens: state.usedTokens,
74
+ maxTokens: state.maxTokens,
75
+ percentage: state.usedPercentage,
76
+ autoCompactThreshold,
77
+ autoCompactThresholdSource,
78
+ references: listCommandContextReferences(context),
79
+ },
80
+ };
81
+ }
82
+
83
+ async function executeContextSubcommand(
84
+ context: ICommandHostContext,
85
+ parts: readonly string[],
86
+ ): Promise<ICommandResult> {
87
+ const [subcommand, ...rest] = parts;
88
+ if (subcommand === 'list') {
89
+ if (rest.length > 0) return { success: false, message: USAGE };
90
+ return formatContextReferenceList(listCommandContextReferences(context));
91
+ }
92
+ if (subcommand === 'add') {
93
+ return executeAddContextReference(context, rest);
94
+ }
95
+ if (subcommand === 'remove') {
96
+ return executeRemoveContextReference(context, rest);
97
+ }
98
+ if (subcommand === 'clear') {
99
+ if (rest.length > 0) return { success: false, message: USAGE };
100
+ const result = clearCommandContextReferences(context);
101
+ return {
102
+ success: true,
103
+ message: `Context references cleared: ${result.removed.length} removed.`,
104
+ data: { removed: result.removed },
105
+ };
106
+ }
107
+ if (subcommand !== 'auto') {
108
+ return { success: false, message: USAGE };
109
+ }
110
+ return executeAutoContextSubcommand(context, rest);
111
+ }
112
+
113
+ function executeAutoContextSubcommand(
114
+ context: ICommandHostContext,
115
+ parts: readonly string[],
116
+ ): ICommandResult {
117
+ const [action, extra] = parts;
118
+ if (extra !== undefined) return { success: false, message: USAGE };
119
+ if (action === undefined) {
120
+ const threshold = readAutoCompactThreshold(context);
121
+ const source = readAutoCompactThresholdSource(context);
122
+ return {
123
+ success: true,
124
+ message: [formatAutoCompactLine(threshold, source), USAGE].join('\n'),
125
+ data: { autoCompactThreshold: threshold, autoCompactThresholdSource: source },
126
+ };
127
+ }
128
+
129
+ if (action === 'on') {
130
+ return applyAutoCompactThreshold(context, DEFAULT_AUTO_COMPACT_THRESHOLD, 'enabled');
131
+ }
132
+ if (action === 'off') {
133
+ return applyAutoCompactThreshold(context, false, 'disabled');
134
+ }
135
+ if (action === 'reset') {
136
+ const persisted = resetAutoCompactThresholdSetting(context);
137
+ setCommandAutoCompactThreshold(context, DEFAULT_AUTO_COMPACT_THRESHOLD, 'default');
138
+ return {
139
+ success: true,
140
+ message: `Auto compact reset to default: ${formatThreshold(DEFAULT_AUTO_COMPACT_THRESHOLD)} (${formatPersistenceSuffix(persisted)}).`,
141
+ data: {
142
+ autoCompactThreshold: DEFAULT_AUTO_COMPACT_THRESHOLD,
143
+ autoCompactThresholdSource: 'default',
144
+ persisted,
145
+ },
146
+ };
147
+ }
148
+
149
+ const parsed = parseThreshold(action);
150
+ if (!parsed.success) {
151
+ return { success: false, message: `${parsed.message}\n${USAGE}` };
152
+ }
153
+ return applyAutoCompactThreshold(context, parsed.threshold, 'threshold set');
154
+ }
155
+
156
+ async function executeAddContextReference(
157
+ context: ICommandHostContext,
158
+ args: readonly string[],
159
+ ): Promise<ICommandResult> {
160
+ const path = args.join(' ').trim();
161
+ if (!path) return { success: false, message: USAGE };
162
+
163
+ const result = await addCommandContextReference(context, path);
164
+ if (!result.reference) {
165
+ return {
166
+ success: false,
167
+ message: result.diagnostics.join('\n') || `Context reference not found: ${path}`,
168
+ data: { diagnostics: result.diagnostics },
169
+ };
170
+ }
171
+
172
+ return {
173
+ success: true,
174
+ message: [
175
+ `Context reference added: ${formatContextReferenceLine(result.reference)}.`,
176
+ ...(result.evicted.length > 0
177
+ ? [`Evicted ${result.evicted.length} older context reference(s).`]
178
+ : []),
179
+ ].join('\n'),
180
+ data: { reference: result.reference, evicted: result.evicted },
181
+ };
182
+ }
183
+
184
+ function executeRemoveContextReference(
185
+ context: ICommandHostContext,
186
+ args: readonly string[],
187
+ ): ICommandResult {
188
+ const path = args.join(' ').trim();
189
+ if (!path) return { success: false, message: USAGE };
190
+
191
+ const result = removeCommandContextReference(context, path);
192
+ if (!result.removed) {
193
+ return {
194
+ success: false,
195
+ message: `Context reference not found: ${path}`,
196
+ };
197
+ }
198
+
199
+ return {
200
+ success: true,
201
+ message: `Context reference removed: ${formatContextReferenceLine(result.removed)}.`,
202
+ data: { removed: result.removed },
203
+ };
204
+ }
205
+
206
+ function applyAutoCompactThreshold(
207
+ context: ICommandHostContext,
208
+ threshold: TAutoCompactThreshold,
209
+ action: 'enabled' | 'disabled' | 'threshold set',
210
+ ): ICommandResult {
211
+ const persisted = writeAutoCompactThresholdSetting(context, threshold);
212
+ const source: TAutoCompactThresholdSource = persisted ? 'settings' : 'session';
213
+ setCommandAutoCompactThreshold(context, threshold, source);
214
+
215
+ return {
216
+ success: true,
217
+ message: formatApplyMessage(action, threshold, persisted),
218
+ data: {
219
+ autoCompactThreshold: threshold,
220
+ autoCompactThresholdSource: source,
221
+ persisted,
222
+ },
223
+ };
224
+ }
225
+
226
+ function formatApplyMessage(
227
+ action: 'enabled' | 'disabled' | 'threshold set',
228
+ threshold: TAutoCompactThreshold,
229
+ persisted: boolean,
230
+ ): string {
231
+ const suffix = formatPersistenceSuffix(persisted);
232
+ if (action === 'disabled') {
233
+ return `Auto compact disabled (${suffix}).`;
234
+ }
235
+ if (action === 'enabled') {
236
+ return `Auto compact enabled at ${formatThreshold(threshold)} (${suffix}).`;
237
+ }
238
+ return `Auto compact threshold set to ${formatThreshold(threshold)} (${suffix}).`;
239
+ }
240
+
241
+ type TParseThresholdResult =
242
+ | { success: true; threshold: number }
243
+ | { success: false; message: string };
244
+
245
+ function parseThreshold(raw: string): TParseThresholdResult {
246
+ if (raw.endsWith('%')) {
247
+ const percent = Number(raw.slice(0, -1));
248
+ if (!Number.isFinite(percent) || percent <= 0 || percent > PERCENT) {
249
+ return {
250
+ success: false,
251
+ message: 'Auto compact percentage must be greater than 0% and at most 100%.',
252
+ };
253
+ }
254
+ return { success: true, threshold: percent / PERCENT };
255
+ }
256
+
257
+ if (raw.includes('.')) {
258
+ const fraction = Number(raw);
259
+ if (!Number.isFinite(fraction) || fraction <= 0 || fraction > 1) {
260
+ return {
261
+ success: false,
262
+ message: 'Auto compact fraction must be greater than 0 and at most 1.',
263
+ };
264
+ }
265
+ return { success: true, threshold: fraction };
266
+ }
267
+
268
+ return {
269
+ success: false,
270
+ message: 'Use a percentage such as 85% or a fraction such as 0.85.',
271
+ };
272
+ }
273
+
274
+ function formatContextReferenceSummary(references: readonly IContextReferenceItem[]): string {
275
+ const active = references.filter((reference) => reference.status === 'active').length;
276
+ const observed = references.filter((reference) => reference.status === 'observed').length;
277
+ return `References: ${active} active, ${observed} observed`;
278
+ }
279
+
280
+ function formatContextReferenceList(references: readonly IContextReferenceItem[]): ICommandResult {
281
+ if (references.length === 0) {
282
+ return { success: true, message: 'No context references.', data: { references } };
283
+ }
284
+
285
+ return {
286
+ success: true,
287
+ message: ['Context references:', ...references.map(formatContextReferenceLine)].join('\n'),
288
+ data: { references },
289
+ };
290
+ }
291
+
292
+ function formatContextReferenceLine(reference: IContextReferenceItem): string {
293
+ return [
294
+ reference.relativePath,
295
+ `[${reference.loadType}, ${reference.status}]`,
296
+ `${reference.byteLength.toLocaleString()} B`,
297
+ ].join(' ');
298
+ }
@@ -0,0 +1,6 @@
1
+ export {
2
+ ContextCommandSource,
3
+ createContextCommandEntry,
4
+ createContextCommandModule,
5
+ } from './context-command-module.js';
6
+ export { executeContextCommand } from './context-command.js';
@@ -0,0 +1,35 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ ExitCommandSource,
4
+ createExitCommandEntry,
5
+ createExitCommandModule,
6
+ executeExitCommand,
7
+ } from '../index.js';
8
+
9
+ describe('exit command module', () => {
10
+ it('provides command metadata and executable registration from one module', () => {
11
+ const entry = createExitCommandEntry();
12
+ const module = createExitCommandModule();
13
+
14
+ expect(entry).toEqual({
15
+ name: 'exit',
16
+ displayName: 'Exit Session',
17
+ description: 'Exit CLI',
18
+ source: 'exit',
19
+ modelInvocable: false,
20
+ });
21
+ expect(new ExitCommandSource().getCommands()).toEqual([entry]);
22
+ expect(module.systemCommands?.map((command) => command.name)).toEqual(['exit']);
23
+ expect(module.commandSources?.flatMap((source) => source.getCommands())).toEqual([entry]);
24
+ });
25
+
26
+ it('returns the session exit effect without owning process exit', () => {
27
+ const result = executeExitCommand({} as never, '');
28
+
29
+ expect(result).toEqual({
30
+ success: true,
31
+ message: 'Exit requested.',
32
+ effects: [{ type: 'session-exit-requested' }],
33
+ });
34
+ });
35
+ });
@@ -0,0 +1,48 @@
1
+ import type {
2
+ ICommand,
3
+ ICommandModule,
4
+ ICommandSource,
5
+ ISystemCommand,
6
+ } from '@robota-sdk/agent-framework';
7
+ import { EXIT_COMMAND_DESCRIPTION } from '@robota-sdk/agent-framework';
8
+ import { executeExitCommand } from './exit-command.js';
9
+
10
+ export function createExitCommandEntry(): ICommand {
11
+ return {
12
+ name: 'exit',
13
+ displayName: 'Exit Session',
14
+ description: EXIT_COMMAND_DESCRIPTION,
15
+ source: 'exit',
16
+ modelInvocable: false,
17
+ };
18
+ }
19
+
20
+ function createExitSystemCommand(): ISystemCommand {
21
+ const entry = createExitCommandEntry();
22
+ return {
23
+ name: entry.name,
24
+ displayName: entry.displayName,
25
+ description: entry.description,
26
+ requiresPermission: true,
27
+ userInvocable: true,
28
+ modelInvocable: false,
29
+ lifecycle: 'inline',
30
+ execute: executeExitCommand,
31
+ };
32
+ }
33
+
34
+ export class ExitCommandSource implements ICommandSource {
35
+ readonly name = 'exit';
36
+
37
+ getCommands(): ICommand[] {
38
+ return [createExitCommandEntry()];
39
+ }
40
+ }
41
+
42
+ export function createExitCommandModule(): ICommandModule {
43
+ return {
44
+ name: 'agent-command-exit',
45
+ commandSources: [new ExitCommandSource()],
46
+ systemCommands: [createExitSystemCommand()],
47
+ };
48
+ }
@@ -0,0 +1,10 @@
1
+ import type { ICommandHostContext, ICommandResult } from '@robota-sdk/agent-framework';
2
+ import { createSessionExitRequestedEffect } from '@robota-sdk/agent-framework';
3
+
4
+ export function executeExitCommand(_context: ICommandHostContext, _args: string): ICommandResult {
5
+ return {
6
+ success: true,
7
+ message: 'Exit requested.',
8
+ effects: [createSessionExitRequestedEffect()],
9
+ };
10
+ }
@@ -0,0 +1,6 @@
1
+ export {
2
+ ExitCommandSource,
3
+ createExitCommandEntry,
4
+ createExitCommandModule,
5
+ } from './exit-command-module.js';
6
+ export { executeExitCommand } from './exit-command.js';
@@ -0,0 +1,106 @@
1
+ import type { ICommandHostContext, ICommandSessionRuntime } from '@robota-sdk/agent-framework';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { createHelpCommandModule } from '../help-command-module.js';
4
+ import { executeHelpCommand } from '../help-command.js';
5
+
6
+ function createCommandSessionRuntime(): ICommandSessionRuntime {
7
+ return {
8
+ clearHistory: () => undefined,
9
+ compact: async () => undefined,
10
+ getContextState: () => ({
11
+ maxTokens: 100,
12
+ usedTokens: 10,
13
+ usedPercentage: 10,
14
+ remainingPercentage: 90,
15
+ }),
16
+ getPermissionMode: () => 'default',
17
+ setPermissionMode: () => undefined,
18
+ getSessionId: () => 'session_1',
19
+ getMessageCount: () => 0,
20
+ getSessionAllowedTools: () => [],
21
+ getAutoCompactThreshold: () => false,
22
+ };
23
+ }
24
+
25
+ function createCommandHostContext(): ICommandHostContext {
26
+ const checkpoint = {
27
+ id: 'checkpoint_1',
28
+ sessionId: 'session_1',
29
+ sequence: 1,
30
+ prompt: 'prompt',
31
+ createdAt: '2026-05-03T00:00:00.000Z',
32
+ fileCount: 0,
33
+ };
34
+ return {
35
+ getSession: () => createCommandSessionRuntime(),
36
+ getContextState: () => ({
37
+ maxTokens: 100,
38
+ usedTokens: 10,
39
+ usedPercentage: 10,
40
+ remainingPercentage: 90,
41
+ }),
42
+ getAutoCompactThreshold: () => 0.8,
43
+ compactContext: async () => undefined,
44
+ getCwd: () => '/workspace',
45
+ listCommands: () => [
46
+ { name: 'help', displayName: 'Help', description: 'Show available commands' },
47
+ { name: 'provider', displayName: 'Provider Setup', description: 'Manage provider profiles' },
48
+ { name: 'plugin', displayName: 'Plugins', description: 'Manage plugins' },
49
+ ],
50
+ listEditCheckpoints: () => [],
51
+ restoreEditCheckpoint: async () => ({
52
+ target: checkpoint,
53
+ restoredCheckpointCount: 0,
54
+ restoredFileCount: 0,
55
+ removedCheckpointCount: 0,
56
+ }),
57
+ rollbackEditCheckpoint: async () => ({
58
+ target: checkpoint,
59
+ restoredCheckpointCount: 0,
60
+ restoredFileCount: 0,
61
+ removedCheckpointCount: 0,
62
+ }),
63
+ getUsedMemoryReferences: () => [],
64
+ recordMemoryEvent: () => undefined,
65
+ listBackgroundTasks: () => [],
66
+ readBackgroundTaskLog: async (taskId) => ({ taskId, lines: [] }),
67
+ cancelBackgroundTask: async () => undefined,
68
+ closeBackgroundTask: async () => undefined,
69
+ };
70
+ }
71
+
72
+ describe('createHelpCommandModule', () => {
73
+ it('contributes help command metadata and executable command', () => {
74
+ const module = createHelpCommandModule();
75
+
76
+ expect(module.name).toBe('agent-command-help');
77
+ expect(module.commandSources?.[0]?.getCommands()).toEqual([
78
+ {
79
+ name: 'help',
80
+ displayName: 'Help',
81
+ description: 'Show available commands',
82
+ source: 'help',
83
+ modelInvocable: false,
84
+ },
85
+ ]);
86
+ expect(module.systemCommands?.map((command) => command.name)).toEqual(['help']);
87
+ expect(module.systemCommands?.[0]?.lifecycle).toBe('inline');
88
+ expect(module.systemCommands?.[0]?.modelInvocable).toBe(false);
89
+ });
90
+ });
91
+
92
+ describe('executeHelpCommand', () => {
93
+ it('renders the composed command list from the host context', () => {
94
+ const result = executeHelpCommand(createCommandHostContext(), '');
95
+
96
+ expect(result).toEqual({
97
+ success: true,
98
+ message: [
99
+ 'Available commands:',
100
+ ' Help (/help) — Show available commands',
101
+ ' Provider Setup (/provider) — Manage provider profiles',
102
+ ' Plugins (/plugin) — Manage plugins',
103
+ ].join('\n'),
104
+ });
105
+ });
106
+ });
@@ -0,0 +1,48 @@
1
+ import type {
2
+ ICommand,
3
+ ICommandModule,
4
+ ICommandSource,
5
+ ISystemCommand,
6
+ } from '@robota-sdk/agent-framework';
7
+ import { HELP_COMMAND_DESCRIPTION } from '@robota-sdk/agent-framework';
8
+ import { executeHelpCommand } from './help-command.js';
9
+
10
+ export function createHelpCommandEntry(): ICommand {
11
+ return {
12
+ name: 'help',
13
+ displayName: 'Help',
14
+ description: HELP_COMMAND_DESCRIPTION,
15
+ source: 'help',
16
+ modelInvocable: false,
17
+ };
18
+ }
19
+
20
+ function createHelpSystemCommand(): ISystemCommand {
21
+ const entry = createHelpCommandEntry();
22
+ return {
23
+ name: entry.name,
24
+ displayName: entry.displayName,
25
+ description: entry.description,
26
+ requiresPermission: false,
27
+ userInvocable: true,
28
+ modelInvocable: false,
29
+ lifecycle: 'inline',
30
+ execute: executeHelpCommand,
31
+ };
32
+ }
33
+
34
+ export class HelpCommandSource implements ICommandSource {
35
+ readonly name = 'help';
36
+
37
+ getCommands(): ICommand[] {
38
+ return [createHelpCommandEntry()];
39
+ }
40
+ }
41
+
42
+ export function createHelpCommandModule(): ICommandModule {
43
+ return {
44
+ name: 'agent-command-help',
45
+ commandSources: [new HelpCommandSource()],
46
+ systemCommands: [createHelpSystemCommand()],
47
+ };
48
+ }
@@ -0,0 +1,9 @@
1
+ import type { ICommandHostContext, ICommandResult } from '@robota-sdk/agent-framework';
2
+ import { formatCommandHelpMessage } from '@robota-sdk/agent-framework';
3
+
4
+ export function executeHelpCommand(context: ICommandHostContext, _args: string): ICommandResult {
5
+ return {
6
+ success: true,
7
+ message: formatCommandHelpMessage(context),
8
+ };
9
+ }
@@ -0,0 +1,6 @@
1
+ export {
2
+ createHelpCommandEntry,
3
+ createHelpCommandModule,
4
+ HelpCommandSource,
5
+ } from './help-command-module.js';
6
+ export { executeHelpCommand } from './help-command.js';
package/src/index.ts ADDED
@@ -0,0 +1,20 @@
1
+ export * from './agent/index.js';
2
+ export * from './background/index.js';
3
+ export * from './compact/index.js';
4
+ export * from './context/index.js';
5
+ export * from './exit/index.js';
6
+ export * from './help/index.js';
7
+ export * from './language/index.js';
8
+ export * from './memory/index.js';
9
+ export * from './mode/index.js';
10
+ export * from './model/index.js';
11
+ export * from './permissions/index.js';
12
+ export * from './plugin/index.js';
13
+ export * from './provider/index.js';
14
+ export * from './reset/index.js';
15
+ export * from './rewind/index.js';
16
+ export * from './session/index.js';
17
+ export * from './settings/index.js';
18
+ export * from './skills/index.js';
19
+ export * from './statusline/index.js';
20
+ export * from './user-local/index.js';