agent-stage 0.2.18 → 0.2.19

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.
@@ -4,7 +4,7 @@ import c from 'picocolors';
4
4
  const guides = {
5
5
  'quickstart': {
6
6
  title: 'Quick Start for Agents',
7
- description: 'Get started with agentstage in 3 steps',
7
+ description: 'Get started with agentstage in 4 steps',
8
8
  examples: [
9
9
  {
10
10
  scenario: '1. Initialize project (first time only)',
@@ -13,11 +13,16 @@ const guides = {
13
13
  },
14
14
  {
15
15
  scenario: '2. Create a page',
16
- command: 'agentstage page add mypage --ui \'{...}\' --state \'{...}\'',
17
- explanation: 'Creates a page with UI and initial state'
16
+ command: 'agentstage page add mypage',
17
+ explanation: 'Creates page files at pages/mypage/ui.json and pages/mypage/store.json'
18
18
  },
19
19
  {
20
- scenario: '3. Start page runtime',
20
+ scenario: '3. Generate prompt for AI UI JSON',
21
+ command: 'agentstage prompt ui --page mypage',
22
+ explanation: 'Prints a prompt you can send to AI to generate valid ui.json'
23
+ },
24
+ {
25
+ scenario: '4. Start page runtime',
21
26
  command: 'agentstage serve mypage',
22
27
  explanation: 'Starts the page runtime process'
23
28
  }
@@ -40,9 +45,9 @@ const guides = {
40
45
  explanation: 'Creates page with UI spec and initial state'
41
46
  },
42
47
  {
43
- scenario: 'Create empty page (get prompts)',
48
+ scenario: 'Create empty page then generate prompt',
44
49
  command: 'agentstage page add todo',
45
- explanation: 'Creates page with default UI and outputs prompts for AI'
50
+ explanation: 'Creates skeleton files. Then run: agentstage prompt ui --page todo'
46
51
  },
47
52
  {
48
53
  scenario: 'Create page from stdin (for large JSON)',
@@ -68,6 +73,39 @@ const guides = {
68
73
  }
69
74
  ]
70
75
  },
76
+ 'prompt-ui': {
77
+ title: 'Prompt for UI JSON',
78
+ description: 'Generate prompt text for AI to produce valid json-render ui.json',
79
+ examples: [
80
+ {
81
+ scenario: 'Generate a generic prompt',
82
+ command: 'agentstage prompt ui',
83
+ explanation: 'Outputs a reusable prompt with JSON shape and binding rules'
84
+ },
85
+ {
86
+ scenario: 'Generate with page context',
87
+ command: 'agentstage prompt ui --page todo',
88
+ explanation: 'Includes current ui.json/store.json context to guide refinement'
89
+ },
90
+ {
91
+ scenario: 'Pipe prompt into a file',
92
+ command: 'agentstage prompt ui --page todo > /tmp/todo.prompt.txt',
93
+ explanation: 'Useful for automation or LLM pipeline integration'
94
+ }
95
+ ],
96
+ commonErrors: [
97
+ {
98
+ error: 'Project not initialized',
99
+ cause: 'Using --page without initialized workspace',
100
+ fix: 'Run: agentstage init'
101
+ },
102
+ {
103
+ error: 'Page "xxx" not found',
104
+ cause: 'The target page has no pages/<id>/ui.json yet',
105
+ fix: 'Run: agentstage page add <id> first'
106
+ }
107
+ ]
108
+ },
71
109
  'ui-json': {
72
110
  title: 'UI JSON Format',
73
111
  description: 'Complete guide to json-render UI specification',
@@ -201,7 +239,7 @@ push/pop: Navigate forward/back`
201
239
  };
202
240
  export const guideCommand = new Command('guide')
203
241
  .description('Get guidance for using agentstage (for AI Agents)')
204
- .argument('[topic]', 'Topic to get guidance on (quickstart, page-add, ui-json, state-management, validate)')
242
+ .argument('[topic]', 'Topic to get guidance on (quickstart, page-add, prompt-ui, ui-json, state-management, validate)')
205
243
  .option('--list', 'List all available topics')
206
244
  .action(async (topic, options) => {
207
245
  if (options.list || !topic) {
@@ -216,6 +254,7 @@ export const guideCommand = new Command('guide')
216
254
  console.log();
217
255
  console.log(c.dim('Example:'));
218
256
  console.log(` ${c.cyan('agentstage guide quickstart')}`);
257
+ console.log(` ${c.cyan('agentstage guide prompt-ui')}`);
219
258
  console.log(` ${c.cyan('agentstage guide ui-json')}`);
220
259
  console.log();
221
260
  return;
@@ -39,6 +39,7 @@ export const pageAddCommand = new Command('add')
39
39
  await mkdir(pagesDir, { recursive: true });
40
40
  // 处理 UI
41
41
  let uiContent;
42
+ const hasCustomUi = options.uiStdin || options.ui;
42
43
  if (options.uiStdin) {
43
44
  // 从 stdin 读取 UI
44
45
  const input = await readStdin();
@@ -108,30 +109,20 @@ export const pageAddCommand = new Command('add')
108
109
  }
109
110
  // 输出结果
110
111
  const port = config?.port || 3000;
111
- if (options.ui) {
112
- // 提供了完整 UI
113
- printAgentSuccess(`Page "${name}" created with custom UI and state`, [
112
+ if (hasCustomUi) {
113
+ printAgentSuccess(`Page "${name}" created with custom UI`, [
114
114
  `Start runtime: agentstage serve ${name}`,
115
115
  `Open http://localhost:${port} to see your page`,
116
116
  `Update state: agentstage run set-state ${name} '{"key": "value"}'`
117
117
  ]);
118
118
  }
119
119
  else {
120
- // 默认 UI,输出 prompts
121
120
  consola.success(`Page "${name}" created`);
122
121
  console.log(` UI: ${c.cyan(`pages/${name}/ui.json`)}`);
123
122
  console.log(` Store: ${c.cyan(`pages/${name}/store.json`)}`);
124
123
  console.log(` URL: ${c.cyan(`http://localhost:${port}/${name}`)}`);
125
124
  console.log();
126
- console.log(c.bold('─'.repeat(60)));
127
- console.log(c.bold('🤖 AI Prompts'));
128
- console.log(c.bold('─'.repeat(60)));
129
- console.log();
130
- console.log(c.dim('Send this to AI to generate UI:'));
131
- console.log();
132
- console.log(generatePromptContent(name));
133
- console.log(c.bold('─'.repeat(60)));
134
- console.log();
125
+ printAgentHint(`Generate prompt: agentstage prompt ui --page ${name}`);
135
126
  printAgentHint(`Or provide UI directly: agentstage page add ${name} --ui '{...}' --state '{...}'`);
136
127
  }
137
128
  }
@@ -182,46 +173,3 @@ function generateDefaultState(name) {
182
173
  function toTitleCase(str) {
183
174
  return str.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
184
175
  }
185
- function generatePromptContent(name) {
186
- const titleName = toTitleCase(name);
187
- return `# Generate UI for "${titleName}" page
188
-
189
- Output JSON format:
190
- \`\`\`json
191
- {
192
- "root": "main",
193
- "elements": {
194
- "element-id": {
195
- "type": "ComponentName",
196
- "props": { ... },
197
- "children": ["child-id-1"],
198
- "on": { "press": { "action": "setState", "params": {...} } }
199
- }
200
- }
201
- }
202
- \`\`\`
203
-
204
- ## Components
205
-
206
- **Layout**: Stack(direction, gap, align, justify), Card(title, description), Separator
207
- **Typography**: Heading(text, level), Text(text, variant), Badge(text, variant)
208
- **Inputs**: Input(label, name, type, placeholder), Button(label, variant)
209
- **Data**: Table(columns, rows), Tabs(tabs), Dialog(title, openPath)
210
-
211
- ## State Bindings
212
- - Read: \`{ "$state": "/path" }\`
213
- - Write: \`{ "$bindState": "/path" }\`
214
- - List item: \`{ "$item": "field" }\`
215
-
216
- ## Actions
217
- - \`setState\`: { statePath: string, value: any }
218
- - \`pushState\`: { statePath: string, value: any }
219
- - \`removeState\`: { statePath: string, index: number }
220
-
221
- ## Usage
222
- \`\`\`bash
223
- agentstage page add ${name} --ui '{"root":"main",...}' --state '{"count":0}'
224
- \`\`\`
225
-
226
- Generate UI for: [describe your page here]`;
227
- }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare const promptCommand: Command;
@@ -0,0 +1,5 @@
1
+ import { Command } from "commander";
2
+ import { promptUiCommand } from "./ui.js";
3
+ export const promptCommand = new Command("prompt")
4
+ .description("Generate prompts for AI-assisted page creation")
5
+ .addCommand(promptUiCommand);
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare const promptUiCommand: Command;
@@ -0,0 +1,60 @@
1
+ import { Command } from "commander";
2
+ import consola from "consola";
3
+ import { existsSync } from "fs";
4
+ import { readFile } from "fs/promises";
5
+ import { join } from "pathe";
6
+ import { buildUiPrompt } from "../../lib/prompt/ui-prompt.js";
7
+ import { getWorkspaceDir, isInitialized } from "../../utils/paths.js";
8
+ function parsePageId(pageId) {
9
+ if (!/^[a-z0-9-]+$/.test(pageId)) {
10
+ throw new Error("Invalid pageId. Allowed: lowercase letters, numbers, hyphen");
11
+ }
12
+ }
13
+ async function readJson(path) {
14
+ const content = await readFile(path, "utf8");
15
+ return JSON.parse(content);
16
+ }
17
+ export const promptUiCommand = new Command("ui")
18
+ .description("Print prompt text for generating json-render ui.json")
19
+ .option("--page <pageId>", "Use an existing page as context")
20
+ .option("--compact", "Hide verbose context sections", false)
21
+ .action(async (options) => {
22
+ const pageId = options.page;
23
+ const compact = options.compact === true;
24
+ try {
25
+ if (!pageId) {
26
+ process.stdout.write(`${buildUiPrompt({ compact })}\n`);
27
+ return;
28
+ }
29
+ parsePageId(pageId);
30
+ if (!isInitialized()) {
31
+ throw new Error("Project not initialized. Please run `agentstage init` first.");
32
+ }
33
+ const workspaceDir = await getWorkspaceDir();
34
+ const pageDir = join(workspaceDir, "pages", pageId);
35
+ const uiPath = join(pageDir, "ui.json");
36
+ const storePath = join(pageDir, "store.json");
37
+ if (!existsSync(uiPath)) {
38
+ throw new Error(`Page "${pageId}" not found: ${uiPath}`);
39
+ }
40
+ const uiSpec = await readJson(uiPath);
41
+ let state = undefined;
42
+ if (existsSync(storePath)) {
43
+ const store = await readJson(storePath);
44
+ if (typeof store === "object" && store !== null && "state" in store) {
45
+ state = store.state;
46
+ }
47
+ }
48
+ process.stdout.write(`${buildUiPrompt({
49
+ pageId,
50
+ uiSpec,
51
+ state,
52
+ compact,
53
+ })}\n`);
54
+ }
55
+ catch (error) {
56
+ const message = error instanceof Error ? error.message : "Failed to build ui prompt";
57
+ consola.error(message);
58
+ process.exit(1);
59
+ }
60
+ });
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ import { stopCommand } from './commands/stop.js';
12
12
  import { statusCommand } from './commands/status.js';
13
13
  import { pageCommand } from './commands/page/index.js';
14
14
  import { runCommand } from './commands/run/index.js';
15
+ import { promptCommand } from './commands/prompt/index.js';
15
16
  import { guideCommand } from './commands/guide.js';
16
17
  import { cleanupCommand } from './commands/cleanup.js';
17
18
  import { componentsCommand } from './commands/components.js';
@@ -30,6 +31,7 @@ program.addCommand(stopCommand); // stop
30
31
  program.addCommand(statusCommand); // status
31
32
  program.addCommand(pageCommand); // page add/rm/ls/manifest
32
33
  program.addCommand(runCommand); // run get-state/set-state/exec/inspect/watch
34
+ program.addCommand(promptCommand); // prompt ui
33
35
  program.addCommand(guideCommand); // guide
34
36
  program.addCommand(cleanupCommand); // cleanup
35
37
  program.addCommand(componentsCommand); // components
@@ -0,0 +1,7 @@
1
+ export interface BuildUiPromptInput {
2
+ pageId?: string;
3
+ uiSpec?: unknown;
4
+ state?: unknown;
5
+ compact?: boolean;
6
+ }
7
+ export declare function buildUiPrompt(input?: BuildUiPromptInput): string;
@@ -0,0 +1,49 @@
1
+ function toJson(value) {
2
+ return JSON.stringify(value, null, 2);
3
+ }
4
+ export function buildUiPrompt(input = {}) {
5
+ const sections = [];
6
+ const compact = input.compact === true;
7
+ const pageLabel = input.pageId ? ` for page "${input.pageId}"` : "";
8
+ sections.push(`Generate a json-render UI spec${pageLabel}.`);
9
+ sections.push("Return ONLY one valid JSON object.");
10
+ sections.push("Do not output markdown fences.");
11
+ sections.push("Do not output explanations.");
12
+ sections.push("");
13
+ sections.push("Required output shape:");
14
+ sections.push('{ "root": "element-id", "elements": { "element-id": { "type": "ComponentName", "props": {}, "children": [] } } }');
15
+ sections.push("");
16
+ sections.push("Rules:");
17
+ sections.push("1. root must reference an existing element key in elements.");
18
+ sections.push("2. Every children element id must exist in elements.");
19
+ sections.push("3. Use only registered component names.");
20
+ sections.push("4. Keep all props JSON-serializable.");
21
+ sections.push('5. State read binding: { "$state": "/path" }');
22
+ sections.push('6. Two-way binding: { "$bindState": "/path" }');
23
+ sections.push('7. List item binding: { "$item": "fieldName" }');
24
+ sections.push('8. List index binding: { "$index": true }');
25
+ sections.push("");
26
+ sections.push("Common components:");
27
+ sections.push("Layout: Stack, Card, Grid, Separator");
28
+ sections.push("Typography: Heading, Text, Badge");
29
+ sections.push("Input: Input, Button, Select, Checkbox, Switch");
30
+ sections.push("Data/Feedback: Table, Tabs, Dialog, Accordion, Alert, Progress, Skeleton, Spinner");
31
+ sections.push("");
32
+ sections.push("Actions:");
33
+ sections.push('Use event handlers like: "on": { "press": { "action": "setState", "params": { ... } } }');
34
+ sections.push("Common actions: setState, pushState, removeState");
35
+ if (input.state !== undefined) {
36
+ sections.push("");
37
+ sections.push("Current state context:");
38
+ sections.push(toJson(input.state));
39
+ }
40
+ if (input.uiSpec !== undefined && !compact) {
41
+ sections.push("");
42
+ sections.push("Existing ui.json context:");
43
+ sections.push(toJson(input.uiSpec));
44
+ sections.push("If you improve it, keep ids stable when possible.");
45
+ }
46
+ sections.push("");
47
+ sections.push("Now output the final JSON object only.");
48
+ return sections.join("\n");
49
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-stage",
3
- "version": "0.2.18",
3
+ "version": "0.2.19",
4
4
  "files": [
5
5
  "dist"
6
6
  ],