@dinoxx/dinox-cli 1.0.18 → 1.0.20

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 (118) hide show
  1. package/README.md +807 -418
  2. package/dist/auth/userInfo.d.ts +2 -0
  3. package/dist/auth/userInfo.js +28 -11
  4. package/dist/cli.js +15 -1
  5. package/dist/commands/auth/index.d.ts +2 -0
  6. package/dist/commands/auth/index.js +217 -163
  7. package/dist/commands/boxes/index.d.ts +2 -0
  8. package/dist/commands/boxes/index.js +119 -75
  9. package/dist/commands/boxes/repo.d.ts +8 -0
  10. package/dist/commands/boxes/repo.js +41 -0
  11. package/dist/commands/config/index.d.ts +2 -0
  12. package/dist/commands/config/index.js +85 -36
  13. package/dist/commands/descriptor/register.d.ts +8 -0
  14. package/dist/commands/descriptor/register.js +113 -0
  15. package/dist/commands/descriptor/types.d.ts +37 -0
  16. package/dist/commands/descriptor/types.js +17 -0
  17. package/dist/commands/graph/index.d.ts +2 -0
  18. package/dist/commands/graph/index.js +143 -73
  19. package/dist/commands/info/index.d.ts +2 -0
  20. package/dist/commands/info/index.js +41 -14
  21. package/dist/commands/mcp/index.js +11 -10
  22. package/dist/commands/mcp/server.js +8 -4
  23. package/dist/commands/mcp/tools.js +30 -104
  24. package/dist/commands/mcp/types.d.ts +8 -1
  25. package/dist/commands/meta/index.d.ts +2 -0
  26. package/dist/commands/meta/index.js +64 -33
  27. package/dist/commands/meta/repo.d.ts +6 -0
  28. package/dist/commands/meta/repo.js +6 -0
  29. package/dist/commands/notes/actions.d.ts +10 -0
  30. package/dist/commands/notes/actions.js +403 -0
  31. package/dist/commands/notes/descriptors.d.ts +4 -0
  32. package/dist/commands/notes/descriptors.js +250 -0
  33. package/dist/commands/notes/dryRun.d.ts +31 -0
  34. package/dist/commands/notes/dryRun.js +130 -0
  35. package/dist/commands/notes/index.d.ts +2 -51
  36. package/dist/commands/notes/index.js +2 -581
  37. package/dist/commands/notes/repo.d.ts +9 -0
  38. package/dist/commands/notes/repo.js +56 -10
  39. package/dist/commands/notes/runtime.d.ts +28 -0
  40. package/dist/commands/notes/runtime.js +185 -0
  41. package/dist/commands/notes/searchOutput.d.ts +10 -2
  42. package/dist/commands/notes/searchOutput.js +14 -2
  43. package/dist/commands/notes/searchResponse.d.ts +51 -0
  44. package/dist/commands/notes/searchResponse.js +56 -0
  45. package/dist/commands/notes/searchSql.js +2 -0
  46. package/dist/commands/prompt/index.d.ts +2 -0
  47. package/dist/commands/prompt/index.js +122 -72
  48. package/dist/commands/prompt/repo.d.ts +7 -0
  49. package/dist/commands/prompt/repo.js +46 -0
  50. package/dist/commands/schema/commandMetadata.d.ts +2 -0
  51. package/dist/commands/schema/commandMetadata.js +18 -0
  52. package/dist/commands/schema/globalOptions.d.ts +3 -0
  53. package/dist/commands/schema/globalOptions.js +3 -0
  54. package/dist/commands/schema/index.d.ts +4 -0
  55. package/dist/commands/schema/index.js +109 -0
  56. package/dist/commands/schema/renderSkills.d.ts +6 -0
  57. package/dist/commands/schema/renderSkills.js +216 -0
  58. package/dist/commands/schema/spec.d.ts +6 -0
  59. package/dist/commands/schema/spec.js +40 -0
  60. package/dist/commands/schema/types.d.ts +47 -0
  61. package/dist/commands/schema/types.js +1 -0
  62. package/dist/commands/storage/index.d.ts +4 -0
  63. package/dist/commands/storage/index.js +292 -0
  64. package/dist/commands/storage/repo.d.ts +162 -0
  65. package/dist/commands/storage/repo.js +770 -0
  66. package/dist/commands/sync.d.ts +2 -0
  67. package/dist/commands/sync.js +41 -27
  68. package/dist/commands/tags/index.d.ts +2 -0
  69. package/dist/commands/tags/index.js +121 -79
  70. package/dist/commands/tags/repo.d.ts +7 -0
  71. package/dist/commands/tags/repo.js +52 -0
  72. package/dist/commands/todo/dryRun.d.ts +23 -0
  73. package/dist/commands/todo/dryRun.js +133 -0
  74. package/dist/commands/todo/index.d.ts +2 -0
  75. package/dist/commands/todo/index.js +285 -135
  76. package/dist/commands/todo/repo.d.ts +11 -0
  77. package/dist/commands/todo/repo.js +37 -9
  78. package/dist/commands/update.d.ts +3 -0
  79. package/dist/commands/update.js +35 -13
  80. package/dist/commands/writeDryRun.d.ts +62 -0
  81. package/dist/commands/writeDryRun.js +131 -0
  82. package/dist/config/paths.d.ts +2 -1
  83. package/dist/config/paths.js +11 -1
  84. package/dist/config/resolve.js +7 -5
  85. package/dist/constants/links.d.ts +1 -1
  86. package/dist/constants/links.js +1 -1
  87. package/dist/daemon/ensure.js +3 -2
  88. package/dist/daemon/index.js +17 -15
  89. package/dist/dinox.js +9 -13
  90. package/dist/powersync/runtime.d.ts +1 -1
  91. package/dist/powersync/runtime.js +5 -3
  92. package/dist/powersync/schema/index.d.ts +27 -0
  93. package/dist/powersync/schema/index.js +2 -0
  94. package/dist/powersync/schema/note.d.ts +1 -0
  95. package/dist/powersync/schema/note.js +1 -0
  96. package/dist/powersync/schema/projects.d.ts +3 -0
  97. package/dist/powersync/schema/projects.js +3 -0
  98. package/dist/powersync/schema/storage.d.ts +24 -0
  99. package/dist/powersync/schema/storage.js +24 -0
  100. package/dist/skills/check.d.ts +6 -0
  101. package/dist/skills/check.js +254 -0
  102. package/dist/utils/bidlinkCanonicalization.d.ts +2 -0
  103. package/dist/utils/bidlinkCanonicalization.js +161 -0
  104. package/dist/utils/dryRun.d.ts +13 -0
  105. package/dist/utils/dryRun.js +69 -0
  106. package/dist/utils/output.d.ts +19 -0
  107. package/dist/utils/output.js +155 -1
  108. package/dist/utils/terminalSanitization.d.ts +2 -0
  109. package/dist/utils/terminalSanitization.js +33 -0
  110. package/dist/utils/tiptapMarkdown.d.ts +4 -1
  111. package/dist/utils/tiptapMarkdown.js +285 -113
  112. package/dist/utils/tiptapMarkdownSchema.d.ts +37 -0
  113. package/dist/utils/tiptapMarkdownSchema.js +577 -0
  114. package/dist/utils/tiptapMarkdownSchemaUtils.d.ts +74 -0
  115. package/dist/utils/tiptapMarkdownSchemaUtils.js +394 -0
  116. package/dist/utils/updateNotice.d.ts +7 -0
  117. package/dist/utils/updateNotice.js +64 -0
  118. package/package.json +25 -3
@@ -1,12 +1,17 @@
1
+ import { resolveAuthIdentity } from '../../auth/userInfo.js';
1
2
  import { loadConfig } from '../../config/store.js';
2
3
  import { resolveConfig } from '../../config/resolve.js';
3
- import { DinoxError } from '../../utils/errors.js';
4
- import { printYaml } from '../../utils/output.js';
5
- import { parseSyncTimeoutMs } from '../../utils/syncTimeout.js';
6
4
  import { connectPowerSync, waitForIdleDataFlow } from '../../powersync/runtime.js';
7
5
  import { syncNoteTokenIndex } from '../../powersync/tokenIndex.js';
8
- import { resolveAuthIdentity } from '../../auth/userInfo.js';
9
- import { addPrompt, listPrompts } from './repo.js';
6
+ import { registerCommandGroup } from '../descriptor/register.js';
7
+ import { FORMAT_OPTIONS, OFFLINE_OPTIONS, SYNC_TIMEOUT_OPTIONS } from '../schema/globalOptions.js';
8
+ import { buildPromptAddDryRunPreview } from '../writeDryRun.js';
9
+ import { printDryRunPreview } from '../../utils/dryRun.js';
10
+ import { DinoxError } from '../../utils/errors.js';
11
+ import { getRequestedStructuredOutputFormat, printHumanReadableLine, printSuccess, } from '../../utils/output.js';
12
+ import { parseSyncTimeoutMs } from '../../utils/syncTimeout.js';
13
+ import { addPrompt, inspectAddPrompt, listPrompts } from './repo.js';
14
+ const PROMPT_GLOBAL_OPTIONS = [...FORMAT_OPTIONS, ...OFFLINE_OPTIONS, ...SYNC_TIMEOUT_OPTIONS];
10
15
  async function syncBeforePromptCommand(db, offline, timeoutMs) {
11
16
  if (offline) {
12
17
  return;
@@ -14,82 +19,127 @@ async function syncBeforePromptCommand(db, offline, timeoutMs) {
14
19
  await waitForIdleDataFlow(db, Math.min(timeoutMs, 10_000));
15
20
  await syncNoteTokenIndex(db);
16
21
  }
17
- async function runPromptListCommand(options, command) {
18
- const globals = command.optsWithGlobals?.() ?? {};
19
- const offline = Boolean(options.offline ?? globals.offline);
20
- const config = resolveConfig(await loadConfig());
21
- const timeoutMs = parseSyncTimeoutMs(options.syncTimeout ?? globals.syncTimeout) ?? config.sync.timeoutMs;
22
- const { db } = await connectPowerSync({ config, offline, timeoutMs });
22
+ async function withPromptDb(context, run) {
23
+ const rawConfig = await loadConfig();
24
+ const config = resolveConfig(rawConfig);
25
+ const timeoutMs = parseSyncTimeoutMs(context.globals.syncTimeout) ?? config.sync.timeoutMs;
26
+ const offline = Boolean(context.globals.offline);
27
+ const { db, stale } = await connectPowerSync({ config, offline, timeoutMs });
23
28
  try {
24
- await syncBeforePromptCommand(db, offline, timeoutMs);
25
- const prompts = await listPrompts(db);
26
- printYaml(prompts);
29
+ await run({ db, config, rawConfig, offline, timeoutMs, stale });
27
30
  }
28
31
  finally {
29
32
  await db.close().catch(() => undefined);
30
33
  }
31
34
  }
32
- export function registerPromptCommands(program) {
33
- const prompt = program.command('prompt').description('Manage prompts');
34
- prompt
35
- .command('list')
36
- .description('List prompts from c_cmd (name and cmd)')
37
- .option('--offline', 'skip connect/sync and only use local cache')
38
- .option('--sync-timeout <ms>', 'override sync/connect timeout (milliseconds)')
39
- .action(runPromptListCommand);
40
- prompt
41
- .command('add')
42
- .description('Create a new prompt in c_cmd')
43
- .option('--name <string>', 'prompt name')
44
- .option('--cmd <string>', 'prompt command/prompt text')
45
- .option('--json', 'output machine-readable YAML')
46
- .option('--offline', 'skip connect/sync and only use local cache')
47
- .option('--sync-timeout <ms>', 'override sync/connect timeout (milliseconds)')
48
- .action(async (options, command) => {
49
- const globals = command.optsWithGlobals?.() ?? {};
50
- const offline = Boolean(options.offline ?? globals.offline);
51
- const jsonOutput = Boolean(options.json ?? globals.json);
52
- const name = typeof options.name === 'string' ? options.name.trim() : '';
53
- const cmd = typeof options.cmd === 'string' ? options.cmd.trim() : '';
54
- if (!name) {
55
- throw new DinoxError('Prompt name is required. Use `dino prompt add --name <name> --cmd <cmd>`.');
35
+ async function runPromptList(context) {
36
+ await withPromptDb(context, async ({ db, offline, timeoutMs }) => {
37
+ await syncBeforePromptCommand(db, offline, timeoutMs);
38
+ printSuccess((await listPrompts(db)).map((prompt) => ({
39
+ name: prompt.name,
40
+ prompt: prompt.cmd,
41
+ })));
42
+ });
43
+ }
44
+ async function runPromptAdd(context) {
45
+ const name = typeof context.options.name === 'string' ? context.options.name.trim() : '';
46
+ const cmd = typeof context.options.prompt === 'string' ? context.options.prompt.trim() : '';
47
+ if (!name) {
48
+ throw new DinoxError('Prompt name is required. Use `dino prompt add --name <name> --prompt <text>`.');
49
+ }
50
+ if (!cmd) {
51
+ throw new DinoxError('Prompt text is required. Use `dino prompt add --name <name> --prompt <text>`.');
52
+ }
53
+ const structuredOutput = getRequestedStructuredOutputFormat() !== null;
54
+ await withPromptDb(context, async ({ db, config, rawConfig, offline, timeoutMs, stale }) => {
55
+ const identity = await resolveAuthIdentity(rawConfig, config);
56
+ await syncBeforePromptCommand(db, offline, timeoutMs);
57
+ if (Boolean(context.options.dryRun)) {
58
+ printDryRunPreview(buildPromptAddDryRunPreview({
59
+ config,
60
+ offline,
61
+ ...(await inspectAddPrompt(db, {
62
+ name,
63
+ cmd,
64
+ userId: identity.userId,
65
+ })),
66
+ }));
67
+ return;
56
68
  }
57
- if (!cmd) {
58
- throw new DinoxError('Prompt cmd is required. Use `dino prompt add --name <name> --cmd <cmd>`.');
69
+ const result = await addPrompt(db, {
70
+ name,
71
+ cmd,
72
+ userId: identity.userId,
73
+ });
74
+ if (!offline && !config.powersync.uploadBaseUrl) {
75
+ printHumanReadableLine('warning: upload disabled (powersync.uploadBaseUrl is unset); changes are local-only for now');
59
76
  }
60
- const rawConfig = await loadConfig();
61
- const config = resolveConfig(rawConfig);
62
- const timeoutMs = parseSyncTimeoutMs(options.syncTimeout ?? globals.syncTimeout) ?? config.sync.timeoutMs;
63
- const identity = await resolveAuthIdentity(rawConfig, config);
64
- const { db, stale } = await connectPowerSync({ config, offline, timeoutMs });
65
- try {
66
- await syncBeforePromptCommand(db, offline, timeoutMs);
67
- const result = await addPrompt(db, {
68
- name,
69
- cmd,
70
- userId: identity.userId,
71
- });
72
- if (!offline && !config.powersync.uploadBaseUrl) {
73
- console.log('warning: upload disabled (powersync.uploadBaseUrl is unset); changes are local-only for now');
74
- }
75
- if (!offline) {
76
- await waitForIdleDataFlow(db, Math.min(timeoutMs, 5_000));
77
- }
78
- const payload = { ok: true, stale: offline ? true : stale, ...result };
79
- if (jsonOutput) {
80
- printYaml(payload);
81
- return;
82
- }
83
- console.log(result.id);
84
- if (stale && !offline) {
85
- console.log('warning: sync timed out; results may be stale');
86
- }
77
+ if (!offline) {
78
+ await waitForIdleDataFlow(db, Math.min(timeoutMs, 5_000));
87
79
  }
88
- finally {
89
- await db.close().catch(() => undefined);
80
+ const payload = {
81
+ ok: true,
82
+ stale: offline ? true : stale,
83
+ id: result.id,
84
+ name: result.name,
85
+ prompt: result.cmd,
86
+ restored: result.restored,
87
+ };
88
+ if (structuredOutput) {
89
+ printSuccess(payload);
90
+ return;
91
+ }
92
+ printHumanReadableLine(result.id);
93
+ if (stale && !offline) {
94
+ printHumanReadableLine('warning: sync timed out; results may be stale');
90
95
  }
91
96
  });
92
- prompt.action((_options, command) => {
93
- command.outputHelp();
94
- });
97
+ }
98
+ export const promptCommandGroup = {
99
+ name: 'prompt',
100
+ description: 'Manage prompts',
101
+ commands: [
102
+ {
103
+ name: 'list',
104
+ description: 'List reusable prompts from c_cmd',
105
+ schema: {
106
+ path: 'prompt.list',
107
+ risk: 'read',
108
+ auth: 'required',
109
+ globalOptions: PROMPT_GLOBAL_OPTIONS,
110
+ output: {
111
+ type: 'structured',
112
+ summary: 'Prompt list payload',
113
+ fields: ['name', 'prompt'],
114
+ },
115
+ examples: ['dino prompt list --format json'],
116
+ },
117
+ action: runPromptList,
118
+ },
119
+ {
120
+ name: 'add',
121
+ description: 'Create a prompt template, restoring a deleted prompt when possible',
122
+ options: [
123
+ { flags: '--name <string>', description: 'Prompt name', required: true, type: 'string' },
124
+ { flags: '--prompt <string>', description: 'Prompt text', required: true, type: 'string' },
125
+ { flags: '--dry-run', description: 'Preview the write without executing it', type: 'boolean' },
126
+ ],
127
+ schema: {
128
+ path: 'prompt.add',
129
+ risk: 'write',
130
+ auth: 'required',
131
+ globalOptions: PROMPT_GLOBAL_OPTIONS,
132
+ output: {
133
+ type: 'structured',
134
+ summary: 'Prompt creation acknowledgement',
135
+ fields: ['id', 'name', 'prompt', 'restored', 'stale'],
136
+ },
137
+ examples: ['dino prompt add --name "周报助手" --prompt "请基于本周笔记输出一份简洁周报" --format json --dry-run'],
138
+ },
139
+ action: runPromptAdd,
140
+ },
141
+ ],
142
+ };
143
+ export function registerPromptCommands(program) {
144
+ registerCommandGroup(program, promptCommandGroup);
95
145
  }
@@ -14,5 +14,12 @@ export type AddPromptResult = {
14
14
  cmd: string;
15
15
  restored: boolean;
16
16
  };
17
+ export type InspectAddPromptResult = {
18
+ name: string;
19
+ cmd: string;
20
+ restored: boolean;
21
+ existingId?: string;
22
+ };
17
23
  export declare function listPrompts(db: AbstractPowerSyncDatabase): Promise<PromptListItem[]>;
18
24
  export declare function addPrompt(db: AbstractPowerSyncDatabase, input: AddPromptInput): Promise<AddPromptResult>;
25
+ export declare function inspectAddPrompt(db: AbstractPowerSyncDatabase, input: AddPromptInput): Promise<InspectAddPromptResult>;
@@ -162,3 +162,49 @@ export async function addPrompt(db, input) {
162
162
  restored: false,
163
163
  };
164
164
  }
165
+ export async function inspectAddPrompt(db, input) {
166
+ const name = normalizeText(input.name);
167
+ if (!name) {
168
+ throw new DinoxError('Prompt name is required');
169
+ }
170
+ const cmd = normalizeText(input.cmd);
171
+ if (!cmd) {
172
+ throw new DinoxError('Prompt cmd is required');
173
+ }
174
+ const userId = normalizeText(input.userId);
175
+ const nameLookup = normalizeLookup(name);
176
+ const cmdLookup = normalizeLookup(cmd);
177
+ const columnNames = await getPromptColumns(db);
178
+ const requiredColumns = ['id', 'name', 'cmd'];
179
+ const missingRequired = requiredColumns.filter((column) => !columnNames.has(column));
180
+ if (missingRequired.length > 0) {
181
+ throw new DinoxError(`c_cmd table is missing required columns: ${missingRequired.join(', ')}`);
182
+ }
183
+ if (columnNames.has('user_id') && !userId) {
184
+ throw new DinoxError('c_cmd insert requires user_id. Missing persisted userId. Run `dino auth login` first.');
185
+ }
186
+ const rows = await listPromptRowsForWrite(db, columnNames);
187
+ const samePromptRows = rows.filter((row) => {
188
+ return normalizeLookup(row.name ?? '') === nameLookup && normalizeLookup(row.cmd ?? '') === cmdLookup;
189
+ });
190
+ const activeRows = samePromptRows.filter((row) => row.is_del !== 1);
191
+ if (activeRows.length > 0) {
192
+ const ids = activeRows.map((row) => row.id?.trim() ?? '').filter(Boolean);
193
+ throw new DinoxError(`Prompt already exists: ${name}`, {
194
+ details: {
195
+ type: 'prompt_exists',
196
+ name,
197
+ cmd,
198
+ ids,
199
+ question: 'This prompt already exists. Do you want to keep using the existing prompt?',
200
+ },
201
+ });
202
+ }
203
+ const deletedRow = samePromptRows.find((row) => (row.id?.trim() ?? '').length > 0);
204
+ return {
205
+ name,
206
+ cmd,
207
+ restored: Boolean(deletedRow?.id),
208
+ ...(deletedRow?.id ? { existingId: deletedRow.id } : {}),
209
+ };
210
+ }
@@ -0,0 +1,2 @@
1
+ import type { CommandDescriptor } from '../descriptor/types.js';
2
+ export declare const schemaCommandMetadata: Omit<CommandDescriptor, 'action'>;
@@ -0,0 +1,18 @@
1
+ import { FORMAT_OPTIONS } from './globalOptions.js';
2
+ export const schemaCommandMetadata = {
3
+ name: 'schema',
4
+ description: 'Inspect Dinox CLI command schemas for agent-friendly usage',
5
+ arguments: [{ syntax: '[path]', description: 'Command schema path, e.g. note.search or todo.update' }],
6
+ schema: {
7
+ path: 'schema',
8
+ risk: 'read',
9
+ auth: 'none',
10
+ globalOptions: FORMAT_OPTIONS,
11
+ output: {
12
+ type: 'structured',
13
+ summary: 'Schema listing or one schema detail payload',
14
+ fields: ['version', 'commands', 'command'],
15
+ },
16
+ examples: ['dino schema', 'dino schema note.search --format json'],
17
+ },
18
+ };
@@ -0,0 +1,3 @@
1
+ export declare const FORMAT_OPTIONS: string[];
2
+ export declare const OFFLINE_OPTIONS: string[];
3
+ export declare const SYNC_TIMEOUT_OPTIONS: string[];
@@ -0,0 +1,3 @@
1
+ export const FORMAT_OPTIONS = ['--format <yaml|json>', '--json'];
2
+ export const OFFLINE_OPTIONS = ['--offline'];
3
+ export const SYNC_TIMEOUT_OPTIONS = ['--sync-timeout <ms>'];
@@ -0,0 +1,4 @@
1
+ import { Command } from 'commander';
2
+ import type { CommandDescriptor } from '../descriptor/types.js';
3
+ export declare const schemaCommandDescriptor: CommandDescriptor;
4
+ export declare function registerSchemaCommands(program: Command): void;
@@ -0,0 +1,109 @@
1
+ import { registerStandaloneCommand } from '../descriptor/register.js';
2
+ import { DinoxError } from '../../utils/errors.js';
3
+ import { getRequestedStructuredOutputFormat, printHumanReadableBlock, printSuccess } from '../../utils/output.js';
4
+ import { findCommandSchema, listCommandSchemas } from './spec.js';
5
+ import { schemaCommandMetadata } from './commandMetadata.js';
6
+ function renderSchemaList(schemas) {
7
+ const lines = ['Available command schemas:', ''];
8
+ for (const schema of schemas) {
9
+ lines.push(` ${schema.path.padEnd(16)} ${schema.summary}`);
10
+ }
11
+ lines.push('');
12
+ lines.push('Usage: dino schema <path>');
13
+ return lines.join('\n');
14
+ }
15
+ function renderDetailSection(title, rows) {
16
+ if (rows.length === 0) {
17
+ return [];
18
+ }
19
+ return [title, ...rows.map((row) => ` ${row}`), ''];
20
+ }
21
+ function formatOptionMetadata(option) {
22
+ if (!option) {
23
+ return '';
24
+ }
25
+ const metadata = [];
26
+ if (option.type) {
27
+ metadata.push(`type=${option.type}`);
28
+ }
29
+ if (option.defaultValue !== undefined) {
30
+ metadata.push(`default=${Array.isArray(option.defaultValue) ? JSON.stringify(option.defaultValue) : String(option.defaultValue)}`);
31
+ }
32
+ if (option.enum && option.enum.length > 0) {
33
+ metadata.push(`enum=${option.enum.join('|')}`);
34
+ }
35
+ return metadata.length > 0 ? ` [${metadata.join(', ')}]` : '';
36
+ }
37
+ function renderSchemaDetail(schema) {
38
+ const lines = [
39
+ `${schema.path}`,
40
+ '',
41
+ `Command: ${schema.command}`,
42
+ `Summary: ${schema.summary}`,
43
+ `Risk: ${schema.risk}`,
44
+ `Auth: ${schema.auth}`,
45
+ '',
46
+ ];
47
+ lines.push(...renderDetailSection('Global Options:', schema.globalOptions));
48
+ lines.push(...renderDetailSection('Arguments:', (schema.arguments ?? []).map((entry) => `${entry.name} ${entry.required ? 'required' : 'optional'} ${entry.description}`)));
49
+ lines.push(...renderDetailSection('Options:', (schema.options ?? []).map((entry) => `${entry.name}${entry.required ? ' required' : ''} ${entry.description}${formatOptionMetadata(entry)}`)));
50
+ const outputRows = [`summary ${schema.output.summary}`];
51
+ if (schema.output.entity) {
52
+ outputRows.push(`entity ${schema.output.entity}`);
53
+ }
54
+ for (const field of schema.output.fields ?? []) {
55
+ outputRows.push(`field ${field}`);
56
+ }
57
+ for (const field of schema.output.metaFields ?? []) {
58
+ outputRows.push(`meta ${field}`);
59
+ }
60
+ lines.push(...renderDetailSection('Output:', outputRows));
61
+ lines.push(...renderDetailSection('Examples:', schema.examples));
62
+ return lines.join('\n').trimEnd();
63
+ }
64
+ async function runSchemaCommand(context) {
65
+ const structuredOutput = getRequestedStructuredOutputFormat() !== null;
66
+ const path = context.args.path;
67
+ if (!path) {
68
+ const schemas = listCommandSchemas();
69
+ if (structuredOutput) {
70
+ printSuccess({
71
+ version: 1,
72
+ commands: schemas.map((schema) => ({
73
+ path: schema.path,
74
+ command: schema.command,
75
+ summary: schema.summary,
76
+ risk: schema.risk,
77
+ auth: schema.auth,
78
+ })),
79
+ });
80
+ return;
81
+ }
82
+ printHumanReadableBlock(`${renderSchemaList(schemas)}\n`);
83
+ return;
84
+ }
85
+ const normalizedPath = path.trim();
86
+ const schema = findCommandSchema(normalizedPath);
87
+ if (!schema) {
88
+ throw new DinoxError(`Unknown schema path: ${normalizedPath}`, {
89
+ code: 'SCHEMA_PATH_NOT_FOUND',
90
+ suggestion: 'Run `dino schema` to list available schema paths.',
91
+ recoverable: true,
92
+ });
93
+ }
94
+ if (structuredOutput) {
95
+ printSuccess({
96
+ version: 1,
97
+ command: schema,
98
+ });
99
+ return;
100
+ }
101
+ printHumanReadableBlock(`${renderSchemaDetail(schema)}\n`);
102
+ }
103
+ export const schemaCommandDescriptor = {
104
+ ...schemaCommandMetadata,
105
+ action: runSchemaCommand,
106
+ };
107
+ export function registerSchemaCommands(program) {
108
+ registerStandaloneCommand(program, schemaCommandDescriptor);
109
+ }
@@ -0,0 +1,6 @@
1
+ export declare function renderBundledSkillReference(): string;
2
+ export declare function renderGeneratedCommandSection(sectionKey: string): string;
3
+ export declare function replaceGeneratedSection(source: string, markerName: string, content: string): string;
4
+ export declare function syncBundledSkillReference(skillFilePath: string): void;
5
+ export declare function syncGeneratedCommandSection(skillFilePath: string, sectionKey: string): void;
6
+ export declare function resolveBundledSkillPath(repoRoot: string): string;
@@ -0,0 +1,216 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { listCommandSchemas } from './spec.js';
4
+ const SECTION_ORDER = [
5
+ { title: 'Auth', paths: ['auth.login', 'auth.logout', 'auth.status'] },
6
+ { title: 'Sync', paths: ['sync'] },
7
+ { title: 'Schema', paths: ['schema'] },
8
+ { title: 'Update CLI', paths: ['update'] },
9
+ { title: 'Notes', paths: ['note.search', 'note.get', 'note.preview', 'note.detail', 'note.create', 'note.update', 'note.star', 'note.unstar', 'note.delete'] },
10
+ { title: 'Todo', paths: ['todo.search', 'todo.append', 'todo.create', 'todo.update'] },
11
+ { title: 'Tags', paths: ['tag.list', 'tag.add'] },
12
+ { title: 'Card Boxes (Zettel Boxes)', paths: ['box.list', 'box.add'] },
13
+ { title: 'Prompts', paths: ['prompt.list', 'prompt.add'] },
14
+ { title: 'Storage', paths: ['storage.list', 'storage.test', 'storage.upload', 'storage.stats'] },
15
+ { title: 'Config', paths: ['config.get', 'config.set'] },
16
+ { title: 'Info', paths: ['info'] },
17
+ { title: 'Graph', paths: ['graph.backlinks', 'graph.outlinks', 'graph.related', 'graph.stats'] },
18
+ ];
19
+ const GLOBAL_OPTIONS = [
20
+ ['`--format <yaml|json>`', 'Structured output format. Prefer `json` for agent and script integrations.'],
21
+ ['`--json`', 'Legacy alias for machine-readable YAML output. Keep only for backward compatibility.'],
22
+ ['`--offline`', 'Skip sync, use local cache only'],
23
+ ['`--sync-timeout <ms>`', 'Override default 300000 ms (5 min) sync/connect timeout'],
24
+ ['`--verbose`', 'Enable verbose logging'],
25
+ ];
26
+ function buildCommandSignature(schema) {
27
+ const args = schema.arguments?.map((entry) => entry.name).join(' ') ?? '';
28
+ return args ? `${schema.command} ${args}` : schema.command;
29
+ }
30
+ function padRight(value, width) {
31
+ if (value.length >= width) {
32
+ return value;
33
+ }
34
+ return `${value}${' '.repeat(width - value.length)}`;
35
+ }
36
+ function renderCommand(schema) {
37
+ const lines = [`${padRight(buildCommandSignature(schema), 34)} # ${schema.summary}`];
38
+ for (const option of schema.options ?? []) {
39
+ lines.push(` ${padRight(option.name, 30)} # ${option.description}`);
40
+ }
41
+ return lines;
42
+ }
43
+ const GENERATED_COMMAND_SECTIONS = {
44
+ 'skill:dino-manage-prompts': {
45
+ title: 'Command Reference',
46
+ paths: ['prompt.list', 'prompt.add'],
47
+ intro: 'Use these commands as the canonical Dinox CLI interface for prompt management.',
48
+ notes: [
49
+ 'Prefer `--format json` for both browse and create flows.',
50
+ 'For writes, run the same command with `--dry-run` first.',
51
+ ],
52
+ },
53
+ 'skill:dino-manage-tags': {
54
+ title: 'Command Reference',
55
+ paths: ['tag.list', 'tag.add'],
56
+ intro: 'Use these commands as the canonical Dinox CLI interface for tag management.',
57
+ notes: [
58
+ 'For writes, run the same command with `--dry-run` first.',
59
+ ],
60
+ },
61
+ 'skill:dino-manage-boxes': {
62
+ title: 'Command Reference',
63
+ paths: ['box.list', 'box.add'],
64
+ intro: 'Use these commands as the canonical Dinox CLI interface for box management.',
65
+ notes: [
66
+ 'For writes, run the same command with `--dry-run` first.',
67
+ ],
68
+ },
69
+ 'skill:dino-storage': {
70
+ title: 'Command Reference',
71
+ paths: ['storage.list', 'storage.test', 'storage.upload', 'storage.stats'],
72
+ intro: 'Use these commands as the canonical Dinox CLI interface for custom storage inspection, connectivity testing, uploads, and usage stats.',
73
+ notes: [
74
+ 'Prefer `--storage-id` when you want to bypass the current active custom storage selection.',
75
+ 'For test and upload writes, run the same command with `--dry-run` first.',
76
+ ],
77
+ },
78
+ 'ref:dino-note:search-and-read': {
79
+ title: 'Command Surface',
80
+ paths: ['note.search', 'note.get', 'note.preview', 'note.detail'],
81
+ intro: 'Use these generated commands as the canonical interfaces for note search and read workflows.',
82
+ notes: [
83
+ 'Use `--boxes` for public box filters.',
84
+ 'Prefer `--fields id,title,summary,tags,created_at,boxes,is_starred` when the user only needs search metadata.',
85
+ '`--sql` remains storage-oriented and still uses the field name `zettel_boxes`.',
86
+ ],
87
+ },
88
+ 'ref:dino-note:create': {
89
+ title: 'Command Surface',
90
+ paths: ['note.create'],
91
+ intro: 'Use this generated command surface as the canonical interface for note creation.',
92
+ notes: [
93
+ 'Use `--boxes` for public box input.',
94
+ 'Prefer `--type note` unless the user explicitly wants a `crawl` note.',
95
+ 'Run the same command with `--dry-run --format json` first.',
96
+ ],
97
+ },
98
+ 'ref:dino-note:update-and-delete': {
99
+ title: 'Command Surface',
100
+ paths: ['note.update', 'note.star', 'note.unstar', 'note.delete'],
101
+ intro: 'Use these generated commands as the canonical interfaces for note mutation workflows.',
102
+ notes: [
103
+ '`--tags` and `--boxes` are full-replacement inputs.',
104
+ 'Prefer `note star` / `note unstar` for pure starring changes, and `note update` when multiple fields change together.',
105
+ 'Run the same command with `--dry-run --format json` first.',
106
+ ],
107
+ },
108
+ 'ref:dino-manage-todo:todo-search': {
109
+ title: 'Command Surface',
110
+ paths: ['todo.search'],
111
+ intro: 'Use this generated command surface as the canonical interface for todo search.',
112
+ notes: [
113
+ 'Prefer `--format json` for machine-readable search output.',
114
+ ],
115
+ },
116
+ 'ref:dino-manage-todo:todo-mutations': {
117
+ title: 'Command Surface',
118
+ paths: ['todo.append', 'todo.create', 'todo.update'],
119
+ intro: 'Use these generated commands as the canonical mutation interfaces for todo workflows.',
120
+ notes: [
121
+ 'Run the same command with `--dry-run` first.',
122
+ 'For append, prefer explicit `--note-id` unless the user confirms the default target behavior.',
123
+ ],
124
+ },
125
+ };
126
+ export function renderBundledSkillReference() {
127
+ const schemas = new Map(listCommandSchemas().map((schema) => [schema.path, schema]));
128
+ const lines = [];
129
+ lines.push('## Global Options');
130
+ lines.push('');
131
+ lines.push('| Flag | Description |');
132
+ lines.push('|------|-------------|');
133
+ for (const [flag, description] of GLOBAL_OPTIONS) {
134
+ lines.push(`| ${flag} | ${description} |`);
135
+ }
136
+ lines.push('');
137
+ lines.push('## Commands Quick Reference');
138
+ lines.push('');
139
+ for (const section of SECTION_ORDER) {
140
+ lines.push(`### ${section.title}`);
141
+ lines.push('```text');
142
+ for (const pathKey of section.paths) {
143
+ const schema = schemas.get(pathKey);
144
+ if (!schema) {
145
+ continue;
146
+ }
147
+ lines.push(...renderCommand(schema));
148
+ lines.push('');
149
+ }
150
+ if (lines[lines.length - 1] === '') {
151
+ lines.pop();
152
+ }
153
+ lines.push('```');
154
+ lines.push('');
155
+ }
156
+ return lines.join('\n').trimEnd();
157
+ }
158
+ export function renderGeneratedCommandSection(sectionKey) {
159
+ const section = GENERATED_COMMAND_SECTIONS[sectionKey];
160
+ if (!section) {
161
+ throw new Error(`No generated command section configuration for ${sectionKey}`);
162
+ }
163
+ const schemas = new Map(listCommandSchemas().map((schema) => [schema.path, schema]));
164
+ const lines = [];
165
+ lines.push(`## ${section.title}`);
166
+ lines.push('');
167
+ if (section.intro) {
168
+ lines.push(section.intro);
169
+ lines.push('');
170
+ }
171
+ lines.push('```text');
172
+ for (const pathKey of section.paths) {
173
+ const schema = schemas.get(pathKey);
174
+ if (!schema) {
175
+ throw new Error(`Unknown schema path for generated command section: ${pathKey}`);
176
+ }
177
+ lines.push(...renderCommand(schema));
178
+ lines.push('');
179
+ }
180
+ if (lines[lines.length - 1] === '') {
181
+ lines.pop();
182
+ }
183
+ lines.push('```');
184
+ if (section.notes && section.notes.length > 0) {
185
+ lines.push('');
186
+ for (const note of section.notes) {
187
+ lines.push(`- ${note}`);
188
+ }
189
+ }
190
+ return lines.join('\n').trimEnd();
191
+ }
192
+ export function replaceGeneratedSection(source, markerName, content) {
193
+ const startMarker = `<!-- BEGIN ${markerName} -->`;
194
+ const endMarker = `<!-- END ${markerName} -->`;
195
+ const start = source.indexOf(startMarker);
196
+ const end = source.indexOf(endMarker);
197
+ if (start === -1 || end === -1 || end < start) {
198
+ throw new Error(`Missing markers for ${markerName}`);
199
+ }
200
+ const prefix = source.slice(0, start + startMarker.length);
201
+ const suffix = source.slice(end);
202
+ return `${prefix}\n${content}\n${suffix}`;
203
+ }
204
+ export function syncBundledSkillReference(skillFilePath) {
205
+ const source = fs.readFileSync(skillFilePath, 'utf8');
206
+ const next = replaceGeneratedSection(source, 'GENERATED_REFERENCE', renderBundledSkillReference());
207
+ fs.writeFileSync(skillFilePath, next);
208
+ }
209
+ export function syncGeneratedCommandSection(skillFilePath, sectionKey) {
210
+ const source = fs.readFileSync(skillFilePath, 'utf8');
211
+ const next = replaceGeneratedSection(source, 'GENERATED_COMMANDS', renderGeneratedCommandSection(sectionKey));
212
+ fs.writeFileSync(skillFilePath, next);
213
+ }
214
+ export function resolveBundledSkillPath(repoRoot) {
215
+ return path.join(repoRoot, 'skills', 'dino-dinox', 'SKILL.md');
216
+ }
@@ -0,0 +1,6 @@
1
+ import type { CommandSchema } from './types.js';
2
+ export type { CommandSchema, CommandSchemaAuth, CommandSchemaMcp, CommandSchemaMcpProperty, CommandSchemaOutput, CommandSchemaRisk, } from './types.js';
3
+ export declare const COMMAND_SCHEMAS: CommandSchema[];
4
+ export declare function listCommandSchemas(): CommandSchema[];
5
+ export declare function findCommandSchema(path: string): CommandSchema | null;
6
+ export declare function listMcpCommandSchemas(): CommandSchema[];