Skip to content

Commit f954e3d

Browse files
feat: llm tools (alpha version) (#2292)
* feat: llm tools alpha version - Added 'skipAsATool' and 'essential' properties to CLI-only operation definitions to control tool catalog generation and mark essential tools. - Updated the 'buildSdkContract' function to include these new properties in the SDK contract output. - Adjusted operation categories to better reflect their usage, consolidating lifecycle operations under the session category. - Refactored parameter schemas and validation logic to accommodate new operation definitions. * docs: add AI Agents section with LLM Tools, MCP Server, and Skills documentation
1 parent 217bdce commit f954e3d

36 files changed

Lines changed: 4970 additions & 1126 deletions

apps/cli/scripts/export-sdk-contract.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,8 @@ function buildSdkContract() {
307307
entry.outputSchema = docOp.outputSchema;
308308
if (docOp.successSchema) entry.successSchema = docOp.successSchema;
309309
if (docOp.failureSchema) entry.failureSchema = docOp.failureSchema;
310+
if (docOp.skipAsATool) entry.skipAsATool = true;
311+
if (docOp.essential) entry.essential = true;
310312
} else {
311313
// CLI-only operation — metadata from canonical definitions
312314
const def = cliOnlyDef!;
@@ -315,6 +317,7 @@ function buildSdkContract() {
315317
entry.supportsTrackedMode = def.sdkMetadata.supportsTrackedMode;
316318
entry.supportsDryRun = def.sdkMetadata.supportsDryRun;
317319
entry.outputSchema = def.outputSchema;
320+
if (def.skipAsATool) entry.skipAsATool = true;
318321
}
319322

320323
// Invariant: every operation must have outputSchema

apps/cli/src/__tests__/conformance/scenarios.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -858,7 +858,7 @@ export const SUCCESS_SCENARIOS = {
858858
docPath,
859859
'--expected-revision',
860860
'0',
861-
'--atomic-json',
861+
'--atomic',
862862
'true',
863863
'--change-mode',
864864
'direct',
@@ -891,7 +891,7 @@ export const SUCCESS_SCENARIOS = {
891891
'mutations',
892892
'apply',
893893
docPath,
894-
'--atomic-json',
894+
'--atomic',
895895
'true',
896896
'--change-mode',
897897
'direct',

apps/cli/src/__tests__/help-regression.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('CLI help regression coverage', () => {
88
);
99

1010
expect(blocksDeleteCommand).toBeDefined();
11-
expect(CLI_HELP).toContain('blocks:');
11+
expect(CLI_HELP).toContain('core:');
1212
expect(CLI_HELP).toContain(blocksDeleteCommand!.key);
1313
});
1414
});

apps/cli/src/cli/cli-only-operation-definitions.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ export interface CliOnlyOperationDefinition {
3131
intentName: string;
3232
sdkMetadata: CliOnlySdkMetadata;
3333
outputSchema: Record<string, unknown>;
34+
/** When true, this operation is excluded from generated LLM tool catalogs. */
35+
skipAsATool?: boolean;
3436
}
3537

3638
// ---------------------------------------------------------------------------
@@ -39,7 +41,7 @@ export interface CliOnlyOperationDefinition {
3941

4042
export const CLI_ONLY_OPERATION_DEFINITIONS: Record<CliOnlyOperation, CliOnlyOperationDefinition> = {
4143
open: {
42-
category: 'lifecycle',
44+
category: 'session',
4345
description:
4446
'Open a document and create a persistent editing session. Optionally override the document body with contentOverride + overrideType (markdown, html, or text).',
4547
requiresDocumentContext: false,
@@ -78,7 +80,7 @@ export const CLI_ONLY_OPERATION_DEFINITIONS: Record<CliOnlyOperation, CliOnlyOpe
7880
},
7981
},
8082
save: {
81-
category: 'lifecycle',
83+
category: 'session',
8284
description: 'Save the current session to the original file or a new path.',
8385
requiresDocumentContext: false,
8486
intentName: 'save_document',
@@ -117,7 +119,7 @@ export const CLI_ONLY_OPERATION_DEFINITIONS: Record<CliOnlyOperation, CliOnlyOpe
117119
},
118120
},
119121
close: {
120-
category: 'lifecycle',
122+
category: 'session',
121123
description: 'Close the active editing session and clean up resources.',
122124
requiresDocumentContext: false,
123125
intentName: 'close_document',
@@ -144,7 +146,7 @@ export const CLI_ONLY_OPERATION_DEFINITIONS: Record<CliOnlyOperation, CliOnlyOpe
144146
},
145147
},
146148
status: {
147-
category: 'introspection',
149+
category: 'session',
148150
description: 'Show the current session status and document metadata.',
149151
requiresDocumentContext: false,
150152
intentName: 'get_status',
@@ -168,10 +170,11 @@ export const CLI_ONLY_OPERATION_DEFINITIONS: Record<CliOnlyOperation, CliOnlyOpe
168170
},
169171
},
170172
describe: {
171-
category: 'introspection',
173+
category: 'session',
172174
description: 'List all available CLI operations and contract metadata.',
173175
requiresDocumentContext: false,
174176
intentName: 'describe_commands',
177+
skipAsATool: true,
175178
sdkMetadata: { mutates: false, idempotency: 'idempotent', supportsTrackedMode: false, supportsDryRun: false },
176179
outputSchema: {
177180
type: 'object',
@@ -194,11 +197,12 @@ export const CLI_ONLY_OPERATION_DEFINITIONS: Record<CliOnlyOperation, CliOnlyOpe
194197
},
195198
},
196199
describeCommand: {
197-
category: 'introspection',
200+
category: 'session',
198201
description: 'Show detailed metadata for a single CLI operation.',
199202
requiresDocumentContext: false,
200203
tokenOverride: ['describe', 'command'],
201204
intentName: 'describe_command',
205+
skipAsATool: true,
202206
sdkMetadata: { mutates: false, idempotency: 'idempotent', supportsTrackedMode: false, supportsDryRun: false },
203207
outputSchema: {
204208
type: 'object',

apps/cli/src/cli/commands.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,17 +158,18 @@ function buildHelpText(): string {
158158
}
159159

160160
const categoryOrder = [
161-
'query',
162-
'mutation',
161+
'core',
163162
'format',
164163
'create',
165-
'blocks',
164+
'tables',
165+
'sections',
166166
'lists',
167167
'comments',
168168
'trackChanges',
169-
'lifecycle',
169+
'toc',
170+
'images',
171+
'history',
170172
'session',
171-
'introspection',
172173
];
173174

174175
for (const category of categoryOrder) {

apps/cli/src/cli/operation-params.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ const CHANGE_MODE_PARAM: CliOperationParamSpec = {
5151
kind: 'flag',
5252
flag: 'change-mode',
5353
type: 'string',
54-
schema: { oneOf: [{ const: 'direct' }, { const: 'tracked' }] } as CliTypeSpec,
55-
agentVisible: false,
54+
schema: { enum: ['direct', 'tracked'] } as CliTypeSpec,
5655
};
5756
const EXPECTED_REVISION_PARAM: CliOperationParamSpec = {
5857
name: 'expectedRevision',
@@ -79,7 +78,7 @@ const USER_EMAIL_PARAM: CliOperationParamSpec = {
7978
// ---------------------------------------------------------------------------
8079

8180
type JsonSchema = Record<string, unknown>;
82-
const AGENT_HIDDEN_PARAM_NAMES = new Set(['out', 'expectedRevision', 'changeMode', 'dryRun']);
81+
const AGENT_HIDDEN_PARAM_NAMES = new Set(['out', 'expectedRevision', 'dryRun']);
8382

8483
function resolveRef(schema: JsonSchema, $defs?: Record<string, JsonSchema>): JsonSchema {
8584
if (schema.$ref && $defs) {
@@ -288,19 +287,11 @@ const PARAM_FLAG_OVERRIDES: Partial<Record<string, Record<string, { name?: strin
288287
// ---------------------------------------------------------------------------
289288
// Per-operation param schema overrides
290289
//
291-
// Some contract schemas intentionally use broad placeholders (for example,
292-
// mutation-step arrays represented as { type: 'object' }). Validate these
293-
// payloads as generic JSON to avoid over-constraining CLI flags.
290+
// Override specific parameter schemas when the contract schema needs
291+
// adjustment for CLI metadata (e.g. simplifying or enriching).
294292
// ---------------------------------------------------------------------------
295293

296-
const PARAM_SCHEMA_OVERRIDES: Partial<Record<string, Record<string, CliTypeSpec>>> = {
297-
'doc.mutations.preview': {
298-
steps: { type: 'json' },
299-
},
300-
'doc.mutations.apply': {
301-
steps: { type: 'json' },
302-
},
303-
};
294+
const PARAM_SCHEMA_OVERRIDES: Partial<Record<string, Record<string, CliTypeSpec>>> = {};
304295

305296
// ---------------------------------------------------------------------------
306297
// Schema-derived param exclusions

apps/cli/src/cli/operation-set.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,31 @@ for (const group of REFERENCE_OPERATION_GROUPS) {
8686
}
8787
}
8888

89+
const REFERENCE_GROUP_TO_CATEGORY: Record<string, CliCategory> = {
90+
core: 'core',
91+
mutations: 'core',
92+
query: 'core',
93+
blocks: 'core',
94+
capabilities: 'core',
95+
format: 'format',
96+
'format.paragraph': 'format',
97+
styles: 'format',
98+
'styles.paragraph': 'format',
99+
create: 'create',
100+
tables: 'tables',
101+
sections: 'sections',
102+
lists: 'lists',
103+
comments: 'comments',
104+
trackChanges: 'trackChanges',
105+
toc: 'toc',
106+
images: 'images',
107+
history: 'history',
108+
};
109+
89110
function deriveCategoryFromDocApi(docApiId: OperationId): CliCategory {
90111
const group = REFERENCE_GROUP_BY_OP.get(docApiId);
91-
if (!group) return 'query';
92-
93-
if (group === 'core' || group === 'mutations') {
94-
return COMMAND_CATALOG[docApiId].mutates ? 'mutation' : 'query';
95-
}
96-
97-
return group as CliCategory;
112+
if (!group) return 'core';
113+
return REFERENCE_GROUP_TO_CATEGORY[group] ?? 'core';
98114
}
99115

100116
export function cliCategory(cliOpId: CliOperationId): CliCategory {

apps/cli/src/cli/types.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,19 +115,18 @@ export type CliOperationArgsById = {
115115
// ---------------------------------------------------------------------------
116116

117117
export type CliCategory =
118-
| 'query'
119-
| 'mutation'
118+
| 'core'
120119
| 'format'
121120
| 'create'
122-
| 'blocks'
121+
| 'tables'
122+
| 'sections'
123123
| 'lists'
124124
| 'comments'
125125
| 'trackChanges'
126-
| 'capabilities'
126+
| 'toc'
127+
| 'images'
127128
| 'history'
128-
| 'lifecycle'
129-
| 'session'
130-
| 'introspection';
129+
| 'session';
131130

132131
/** The 10 CLI-only operation identifiers (without `doc.` prefix). Single source of truth. */
133132
export const CLI_ONLY_OPERATIONS = [

apps/cli/src/lib/operation-args.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@ export function validateValueAgainstTypeSpec(value: unknown, schema: CliTypeSpec
104104

105105
if (schema.type === 'json') return;
106106

107+
if (schema.enum) {
108+
if (!schema.enum.includes(value)) {
109+
throw new CliError('VALIDATION_ERROR', `${path} must be one of: ${schema.enum.join(', ')}.`);
110+
}
111+
return;
112+
}
113+
107114
if (schema.type === 'string') {
108115
if (typeof value !== 'string') throw new CliError('VALIDATION_ERROR', `${path} must be a string.`);
109116
return;

apps/docs/docs.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,14 @@
111111
},
112112
"document-engine/sdks",
113113
"document-engine/cli",
114-
"document-engine/mcp"
114+
{
115+
"group": "AI Agents",
116+
"pages": [
117+
"document-engine/ai-agents/llm-tools",
118+
"document-engine/ai-agents/skills",
119+
"document-engine/ai-agents/mcp-server"
120+
]
121+
}
115122
]
116123
},
117124
{
@@ -307,6 +314,10 @@
307314
]
308315
},
309316
"redirects": [
317+
{
318+
"source": "/document-engine/mcp",
319+
"destination": "/document-engine/ai-agents/mcp-server"
320+
},
310321
{
311322
"source": "/core/superdoc/properties",
312323
"destination": "/core/superdoc/methods#properties"

0 commit comments

Comments
 (0)