@loom-framework/core 0.1.0-alpha.39 → 0.1.0-alpha.40
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/dist/backend/ai/button-resolver.d.ts +17 -0
- package/dist/backend/ai/button-resolver.d.ts.map +1 -0
- package/dist/backend/ai/button-resolver.js +40 -0
- package/dist/backend/ai/button-resolver.js.map +1 -0
- package/dist/backend/ai/engine.d.ts +51 -0
- package/dist/backend/ai/engine.d.ts.map +1 -0
- package/dist/backend/ai/engine.js +188 -0
- package/dist/backend/ai/engine.js.map +1 -0
- package/dist/backend/ai/index.d.ts +11 -0
- package/dist/backend/ai/index.d.ts.map +1 -0
- package/dist/backend/ai/index.js +8 -0
- package/dist/backend/ai/index.js.map +1 -0
- package/dist/backend/ai/output-parser.d.ts +29 -0
- package/dist/backend/ai/output-parser.d.ts.map +1 -0
- package/dist/backend/ai/output-parser.js +247 -0
- package/dist/backend/ai/output-parser.js.map +1 -0
- package/dist/backend/ai/session-manager.d.ts +102 -0
- package/dist/backend/ai/session-manager.d.ts.map +1 -0
- package/dist/backend/ai/session-manager.js +291 -0
- package/dist/backend/ai/session-manager.js.map +1 -0
- package/dist/backend/index.d.ts +61 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +160 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/observe/index.d.ts +6 -0
- package/dist/backend/observe/index.d.ts.map +1 -0
- package/dist/backend/observe/index.js +5 -0
- package/dist/backend/observe/index.js.map +1 -0
- package/dist/backend/observe/logger.d.ts +28 -0
- package/dist/backend/observe/logger.d.ts.map +1 -0
- package/dist/backend/observe/logger.js +80 -0
- package/dist/backend/observe/logger.js.map +1 -0
- package/dist/backend/observe/types.d.ts +26 -0
- package/dist/backend/observe/types.d.ts.map +1 -0
- package/dist/backend/observe/types.js +7 -0
- package/dist/backend/observe/types.js.map +1 -0
- package/dist/backend/routes/chat.d.ts +31 -0
- package/dist/backend/routes/chat.d.ts.map +1 -0
- package/dist/backend/routes/chat.js +426 -0
- package/dist/backend/routes/chat.js.map +1 -0
- package/dist/backend/routes/data.d.ts +13 -0
- package/dist/backend/routes/data.d.ts.map +1 -0
- package/dist/backend/routes/data.js +129 -0
- package/dist/backend/routes/data.js.map +1 -0
- package/dist/backend/routes/health.d.ts +7 -0
- package/dist/backend/routes/health.d.ts.map +1 -0
- package/dist/backend/routes/health.js +15 -0
- package/dist/backend/routes/health.js.map +1 -0
- package/dist/backend/routes/index.d.ts +9 -0
- package/dist/backend/routes/index.d.ts.map +1 -0
- package/dist/backend/routes/index.js +8 -0
- package/dist/backend/routes/index.js.map +1 -0
- package/dist/backend/routes/upload.d.ts +24 -0
- package/dist/backend/routes/upload.d.ts.map +1 -0
- package/dist/backend/routes/upload.js +67 -0
- package/dist/backend/routes/upload.js.map +1 -0
- package/dist/bin.d.ts +8 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +12 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/commands/build.d.ts +11 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +170 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/data.d.ts +11 -0
- package/dist/cli/commands/data.d.ts.map +1 -0
- package/dist/cli/commands/data.js +137 -0
- package/dist/cli/commands/data.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +9 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +114 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/generate-capabilities.d.ts +8 -0
- package/dist/cli/commands/generate-capabilities.d.ts.map +1 -0
- package/dist/cli/commands/generate-capabilities.js +40 -0
- package/dist/cli/commands/generate-capabilities.js.map +1 -0
- package/dist/cli/commands/generate-cli-command.d.ts +8 -0
- package/dist/cli/commands/generate-cli-command.d.ts.map +1 -0
- package/dist/cli/commands/generate-cli-command.js +64 -0
- package/dist/cli/commands/generate-cli-command.js.map +1 -0
- package/dist/cli/commands/generate-page.d.ts +9 -0
- package/dist/cli/commands/generate-page.d.ts.map +1 -0
- package/dist/cli/commands/generate-page.js +325 -0
- package/dist/cli/commands/generate-page.js.map +1 -0
- package/dist/cli/commands/generate-skill.d.ts +8 -0
- package/dist/cli/commands/generate-skill.d.ts.map +1 -0
- package/dist/cli/commands/generate-skill.js +75 -0
- package/dist/cli/commands/generate-skill.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +6 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +17 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/init.d.ts +8 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +400 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/observe.d.ts +9 -0
- package/dist/cli/commands/observe.d.ts.map +1 -0
- package/dist/cli/commands/observe.js +142 -0
- package/dist/cli/commands/observe.js.map +1 -0
- package/dist/cli/commands/skill.d.ts +9 -0
- package/dist/cli/commands/skill.d.ts.map +1 -0
- package/dist/cli/commands/skill.js +186 -0
- package/dist/cli/commands/skill.js.map +1 -0
- package/dist/cli/helpers/duration.d.ts +5 -0
- package/dist/cli/helpers/duration.d.ts.map +1 -0
- package/dist/cli/helpers/duration.js +19 -0
- package/dist/cli/helpers/duration.js.map +1 -0
- package/dist/cli/helpers/field-template.d.ts +9 -0
- package/dist/cli/helpers/field-template.d.ts.map +1 -0
- package/dist/cli/helpers/field-template.js +59 -0
- package/dist/cli/helpers/field-template.js.map +1 -0
- package/dist/cli/helpers/naming.d.ts +6 -0
- package/dist/cli/helpers/naming.d.ts.map +1 -0
- package/dist/cli/helpers/naming.js +14 -0
- package/dist/cli/helpers/naming.js.map +1 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +33 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils.d.ts +10 -0
- package/dist/cli/utils.d.ts.map +1 -0
- package/dist/cli/utils.js +31 -0
- package/dist/cli/utils.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/server-bin.d.ts +12 -0
- package/dist/server-bin.d.ts.map +1 -0
- package/dist/server-bin.js +75 -0
- package/dist/server-bin.js.map +1 -0
- package/package.json +17 -5
- package/templates/skill/SKILL.md +91 -0
- package/templates/skill/references/README.md +67 -0
- package/templates/skill/references/data-model.md +87 -0
- package/templates/skill/references/skill-development.md +46 -0
- package/templates/skill/references/troubleshooting.md +35 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* loom generate page <name>
|
|
3
|
+
*
|
|
4
|
+
* With --model: generate capabilities → CRUD page → auto-wire App.tsx
|
|
5
|
+
* Without --model: generate minimal skeleton page
|
|
6
|
+
*/
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { promises as fs } from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { loadConfig, getModelSchema, generateCapabilities } from '../../index.js';
|
|
11
|
+
import { resolveProjectRoot } from '../utils.js';
|
|
12
|
+
import { toPascalCase, toCamelCase } from '../helpers/naming.js';
|
|
13
|
+
import { fieldToFormItem } from '../helpers/field-template.js';
|
|
14
|
+
/** Generate table columns from model fields */
|
|
15
|
+
function generateColumns(fields, modelName) {
|
|
16
|
+
const cols = fields
|
|
17
|
+
.filter((f) => f.name !== 'id') // skip id column in form
|
|
18
|
+
.map((f) => {
|
|
19
|
+
const label = f.description || f.name;
|
|
20
|
+
const renders = [];
|
|
21
|
+
if (f.type === 'boolean') {
|
|
22
|
+
renders.push(` render: (val: boolean) => val ? '是' : '否',`);
|
|
23
|
+
}
|
|
24
|
+
else if (f.type === 'string[]') {
|
|
25
|
+
renders.push(` render: (val: string[]) => val?.join(', '),`);
|
|
26
|
+
}
|
|
27
|
+
else if (f.type === 'number[]') {
|
|
28
|
+
renders.push(` render: (val: number[]) => val?.join(', '),`);
|
|
29
|
+
}
|
|
30
|
+
return ` {
|
|
31
|
+
title: '${label}',
|
|
32
|
+
dataIndex: '${f.name}',
|
|
33
|
+
key: '${f.name}',
|
|
34
|
+
${renders.join('\n')} },`;
|
|
35
|
+
})
|
|
36
|
+
.join('\n');
|
|
37
|
+
// Add action column
|
|
38
|
+
return `${cols}
|
|
39
|
+
{
|
|
40
|
+
title: '操作',
|
|
41
|
+
key: 'action',
|
|
42
|
+
render: (_: unknown, record: ${toPascalCase(modelName)}Record) => (
|
|
43
|
+
<Space>
|
|
44
|
+
<Button type="link" onClick={() => handleEdit(record)}>编辑</Button>
|
|
45
|
+
<Popconfirm title="确认删除?" onConfirm={() => handleDelete(record.id)}>
|
|
46
|
+
<Button type="link" danger>删除</Button>
|
|
47
|
+
</Popconfirm>
|
|
48
|
+
</Space>
|
|
49
|
+
),
|
|
50
|
+
},`;
|
|
51
|
+
}
|
|
52
|
+
/** Generate CRUD page template from model schema */
|
|
53
|
+
function crudPageTemplate(model) {
|
|
54
|
+
const pascalName = toPascalCase(model.name);
|
|
55
|
+
const camelName = toCamelCase(model.name);
|
|
56
|
+
const recordType = `${pascalName}Record`;
|
|
57
|
+
// Build TypeScript interface from fields
|
|
58
|
+
const fieldDefs = model.fields.map((f) => {
|
|
59
|
+
const tsType = f.type === 'string[]' ? 'string[]'
|
|
60
|
+
: f.type === 'number[]' ? 'number[]'
|
|
61
|
+
: f.type === 'json' ? 'Record<string, unknown>'
|
|
62
|
+
: f.type === 'number' ? 'number'
|
|
63
|
+
: f.type === 'boolean' ? 'boolean'
|
|
64
|
+
: f.type === 'date' ? 'string'
|
|
65
|
+
: 'string';
|
|
66
|
+
return ` ${f.name}${f.required ? '' : '?'}: ${tsType};`;
|
|
67
|
+
}).join('\n');
|
|
68
|
+
const columns = generateColumns(model.fields, model.name);
|
|
69
|
+
const formItems = model.fields
|
|
70
|
+
.filter((f) => f.name !== 'id') // id is auto-generated
|
|
71
|
+
.map((f) => fieldToFormItem(f, model.name))
|
|
72
|
+
.join('\n');
|
|
73
|
+
return `import React, { useEffect, useState } from 'react';
|
|
74
|
+
import { Table, Button, Modal, Form, Input, InputNumber, Select, Switch, DatePicker, Space, Popconfirm, message } from 'antd';
|
|
75
|
+
import { PlusOutlined } from '@ant-design/icons';
|
|
76
|
+
import { useData } from '@loom-framework/frontend-antd';
|
|
77
|
+
|
|
78
|
+
interface ${recordType} {
|
|
79
|
+
${fieldDefs}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function ${pascalName}Page(): React.ReactElement {
|
|
83
|
+
const { list, create, update, remove, loading, refresh } = useData<${recordType}>('${model.name}');
|
|
84
|
+
const [form] = Form.useForm();
|
|
85
|
+
const [modalOpen, setModalOpen] = useState(false);
|
|
86
|
+
const [editingRecord, setEditingRecord] = useState<${recordType} | null>(null);
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
refresh();
|
|
90
|
+
}, [refresh]);
|
|
91
|
+
|
|
92
|
+
const handleAdd = () => {
|
|
93
|
+
setEditingRecord(null);
|
|
94
|
+
form.resetFields();
|
|
95
|
+
setModalOpen(true);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const handleEdit = (record: ${recordType}) => {
|
|
99
|
+
setEditingRecord(record);
|
|
100
|
+
form.setFieldsValue(record);
|
|
101
|
+
setModalOpen(true);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const handleDelete = async (id: string) => {
|
|
105
|
+
try {
|
|
106
|
+
await remove(id);
|
|
107
|
+
message.success('删除成功');
|
|
108
|
+
refresh();
|
|
109
|
+
} catch {
|
|
110
|
+
message.error('删除失败');
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const handleSave = async () => {
|
|
115
|
+
try {
|
|
116
|
+
const values = await form.validateFields();
|
|
117
|
+
if (editingRecord?.id) {
|
|
118
|
+
await update(editingRecord.id, values);
|
|
119
|
+
message.success('更新成功');
|
|
120
|
+
} else {
|
|
121
|
+
await create(values);
|
|
122
|
+
message.success('创建成功');
|
|
123
|
+
}
|
|
124
|
+
setModalOpen(false);
|
|
125
|
+
refresh();
|
|
126
|
+
} catch {
|
|
127
|
+
// form validation failed
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const columns = [
|
|
132
|
+
${columns}
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<div style={{ padding: 24 }}>
|
|
137
|
+
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between' }}>
|
|
138
|
+
<h2>${model.description || pascalName}管理</h2>
|
|
139
|
+
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
|
|
140
|
+
新增
|
|
141
|
+
</Button>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<Table
|
|
145
|
+
columns={columns}
|
|
146
|
+
dataSource={list}
|
|
147
|
+
rowKey="id"
|
|
148
|
+
loading={loading}
|
|
149
|
+
/>
|
|
150
|
+
|
|
151
|
+
<Modal
|
|
152
|
+
title={editingRecord ? '编辑' : '新增'}
|
|
153
|
+
open={modalOpen}
|
|
154
|
+
onOk={handleSave}
|
|
155
|
+
onCancel={() => setModalOpen(false)}
|
|
156
|
+
destroyOnClose
|
|
157
|
+
>
|
|
158
|
+
<Form form={form} layout="vertical">
|
|
159
|
+
${formItems}
|
|
160
|
+
</Form>
|
|
161
|
+
</Modal>
|
|
162
|
+
</div>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export default ${pascalName}Page;
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
/** Minimal skeleton page template (no model) */
|
|
170
|
+
const SKELETON_TEMPLATE = (pascalName) => `import React from 'react';
|
|
171
|
+
|
|
172
|
+
export interface ${pascalName}Props {
|
|
173
|
+
// TODO: Define page props
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function ${pascalName}(props: ${pascalName}Props): React.ReactElement {
|
|
177
|
+
return (
|
|
178
|
+
<div className="${pascalName.toLowerCase()}-page">
|
|
179
|
+
<h1>${pascalName}</h1>
|
|
180
|
+
<p>TODO: Implement this page.</p>
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export default ${pascalName};
|
|
186
|
+
`;
|
|
187
|
+
export function registerGeneratePageCommand(program) {
|
|
188
|
+
program
|
|
189
|
+
.command('page <name>')
|
|
190
|
+
.description('Create a new frontend page component with PascalCase naming')
|
|
191
|
+
.option('--model <model>', 'Data model name from loom.config.ts to generate CRUD page (auto-generates capabilities and wires App.tsx)')
|
|
192
|
+
.action(async (name, cmdOptions) => {
|
|
193
|
+
try {
|
|
194
|
+
const projectRoot = await resolveProjectRoot();
|
|
195
|
+
const pascalName = toPascalCase(name);
|
|
196
|
+
const pageDir = path.join(projectRoot, 'frontend', 'src', 'components', 'pages');
|
|
197
|
+
const pagePath = path.join(pageDir, `${pascalName}.tsx`);
|
|
198
|
+
// Check if page already exists
|
|
199
|
+
try {
|
|
200
|
+
await fs.access(pagePath);
|
|
201
|
+
console.error(chalk.red(`Page "${pascalName}" already exists at ${pagePath}`));
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// File does not exist, proceed
|
|
206
|
+
}
|
|
207
|
+
let template;
|
|
208
|
+
let modelLabel = pascalName;
|
|
209
|
+
if (cmdOptions.model) {
|
|
210
|
+
// Load config and find model schema
|
|
211
|
+
const config = await loadConfig(projectRoot);
|
|
212
|
+
const modelSchema = getModelSchema(config, cmdOptions.model);
|
|
213
|
+
if (!modelSchema) {
|
|
214
|
+
console.error(chalk.red(`Model "${cmdOptions.model}" not found in loom.config.ts`));
|
|
215
|
+
console.error(chalk.dim(`Available models: ${config.data.models.map((m) => m.name).join(', ')}`));
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
modelLabel = modelSchema.description || pascalName;
|
|
219
|
+
// Step 1: Auto-generate capabilities (MCP Server + CLI data commands)
|
|
220
|
+
const capResult = await generateCapabilities(projectRoot, config);
|
|
221
|
+
console.log(chalk.green('Capabilities generated'), chalk.dim(`(${capResult.filesWritten.length} files)`));
|
|
222
|
+
// Step 2: Generate CRUD page
|
|
223
|
+
template = crudPageTemplate(modelSchema);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
template = SKELETON_TEMPLATE(pascalName);
|
|
227
|
+
}
|
|
228
|
+
await fs.mkdir(pageDir, { recursive: true });
|
|
229
|
+
await fs.writeFile(pagePath, template, 'utf-8');
|
|
230
|
+
console.log(chalk.green('Page created successfully!'));
|
|
231
|
+
console.log();
|
|
232
|
+
console.log(chalk.bold(' Page:'), pascalName);
|
|
233
|
+
console.log(chalk.bold(' Path:'), path.join('frontend', 'src', 'components', 'pages', `${pascalName}.tsx`));
|
|
234
|
+
if (cmdOptions.model) {
|
|
235
|
+
console.log(chalk.bold(' Model:'), cmdOptions.model);
|
|
236
|
+
}
|
|
237
|
+
// Step 3: Auto-wire App.tsx when using --model
|
|
238
|
+
if (cmdOptions.model) {
|
|
239
|
+
const wired = await wireAppTsxAutomatic(projectRoot, pascalName, cmdOptions.model, modelLabel);
|
|
240
|
+
if (wired) {
|
|
241
|
+
console.log(chalk.green(' App.tsx wired automatically'));
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
console.log(chalk.yellow(' Could not auto-wire App.tsx — add the page import, navItem, and switch case manually'));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
console.log();
|
|
249
|
+
console.log(chalk.dim(' Use --model <name> to generate a CRUD page from a data model.'));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
254
|
+
console.error(chalk.red('Failed to create page:'), message);
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Auto-wire App.tsx: add import, navItem, and switch case for a new page.
|
|
261
|
+
* Returns true if successful, false if App.tsx structure is unexpected.
|
|
262
|
+
*/
|
|
263
|
+
async function wireAppTsxAutomatic(projectRoot, pascalName, modelKey, modelLabel) {
|
|
264
|
+
const appPath = path.join(projectRoot, 'frontend', 'src', 'App.tsx');
|
|
265
|
+
let content;
|
|
266
|
+
try {
|
|
267
|
+
content = await fs.readFile(appPath, 'utf-8');
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
// Check if this page is already wired (avoid duplicates)
|
|
273
|
+
if (content.includes(`from './components/pages/${pascalName}'`) || content.includes(`<${pascalName}Page`)) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
let modified = content;
|
|
277
|
+
// 1. Add import after the last import line
|
|
278
|
+
const importLine = `import ${pascalName}Page from './components/pages/${pascalName}';`;
|
|
279
|
+
const lastImportEnd = modified.lastIndexOf('\n', modified.indexOf('\nexport'));
|
|
280
|
+
if (lastImportEnd === -1)
|
|
281
|
+
return false;
|
|
282
|
+
modified = modified.slice(0, lastImportEnd + 1) + importLine + '\n' + modified.slice(lastImportEnd + 1);
|
|
283
|
+
// 2. Add navItem (before the closing ] of navItems array)
|
|
284
|
+
const navItemLine = ` { key: '${modelKey}', label: '${modelLabel}' },`;
|
|
285
|
+
const navItemsEnd = modified.indexOf('// Add more nav items');
|
|
286
|
+
if (navItemsEnd !== -1) {
|
|
287
|
+
// Insert before the comment placeholder
|
|
288
|
+
const lineStart = modified.lastIndexOf('\n', navItemsEnd) + 1;
|
|
289
|
+
modified = modified.slice(0, lineStart) + navItemLine + '\n' + modified.slice(lineStart);
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
// Fallback: find the closing ] of navItems array
|
|
293
|
+
const navArrayMatch = modified.match(/const navItems\s*=\s*\[/);
|
|
294
|
+
if (!navArrayMatch)
|
|
295
|
+
return false;
|
|
296
|
+
const arrayStart = navArrayMatch.index + navArrayMatch[0].length;
|
|
297
|
+
// Find the matching ]
|
|
298
|
+
let depth = 1;
|
|
299
|
+
let pos = arrayStart;
|
|
300
|
+
while (pos < modified.length && depth > 0) {
|
|
301
|
+
if (modified[pos] === '[')
|
|
302
|
+
depth++;
|
|
303
|
+
else if (modified[pos] === ']')
|
|
304
|
+
depth--;
|
|
305
|
+
pos++;
|
|
306
|
+
}
|
|
307
|
+
const closingBracket = pos - 1;
|
|
308
|
+
modified = modified.slice(0, closingBracket) + navItemLine + '\n' + modified.slice(closingBracket);
|
|
309
|
+
}
|
|
310
|
+
// 3. Add switch case (before the default case)
|
|
311
|
+
const caseLine = ` case '${modelKey}': return <${pascalName}Page />;`;
|
|
312
|
+
const defaultCase = modified.indexOf('default:');
|
|
313
|
+
if (defaultCase === -1)
|
|
314
|
+
return false;
|
|
315
|
+
// Find the line start of the default case
|
|
316
|
+
const defaultLineStart = modified.lastIndexOf('\n', defaultCase) + 1;
|
|
317
|
+
modified = modified.slice(0, defaultLineStart) + caseLine + '\n' + modified.slice(defaultLineStart);
|
|
318
|
+
// 4. Ensure baseUrl="" on AppShell (prevent /api/v1/api/v1 double prefix)
|
|
319
|
+
if (modified.includes('<AppShell') && !modified.includes('baseUrl=')) {
|
|
320
|
+
modified = modified.replace('<AppShell', '<AppShell\n baseUrl=""');
|
|
321
|
+
}
|
|
322
|
+
await fs.writeFile(appPath, modified, 'utf-8');
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
//# sourceMappingURL=generate-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-page.js","sourceRoot":"","sources":["../../../src/cli/commands/generate-page.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* loom generate skill <name>
|
|
3
|
+
*
|
|
4
|
+
* Create .claude/skills/<name>/SKILL.md skeleton and references/ directory.
|
|
5
|
+
*/
|
|
6
|
+
import type { Command } from 'commander';
|
|
7
|
+
export declare function registerGenerateSkillCommand(program: Command): void;
|
|
8
|
+
//# sourceMappingURL=generate-skill.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-skill.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/generate-skill.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgCzC,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyCnE"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* loom generate skill <name>
|
|
3
|
+
*
|
|
4
|
+
* Create .claude/skills/<name>/SKILL.md skeleton and references/ directory.
|
|
5
|
+
*/
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { promises as fs } from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { resolveProjectRoot } from '../utils.js';
|
|
10
|
+
const SKILL_MD_TEMPLATE = (name) => `---
|
|
11
|
+
name: ${name}
|
|
12
|
+
version: 0.1.0
|
|
13
|
+
description: ""
|
|
14
|
+
tags: []
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# ${name}
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
|
|
21
|
+
TODO: Describe what this skill does.
|
|
22
|
+
|
|
23
|
+
## Instructions
|
|
24
|
+
|
|
25
|
+
TODO: Step-by-step instructions for the skill.
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
TODO: Example inputs and outputs.
|
|
30
|
+
|
|
31
|
+
## References
|
|
32
|
+
|
|
33
|
+
TODO: List reference files in the references/ directory.
|
|
34
|
+
`;
|
|
35
|
+
export function registerGenerateSkillCommand(program) {
|
|
36
|
+
program
|
|
37
|
+
.command('skill <name>')
|
|
38
|
+
.description('Create a new skill skeleton with SKILL.md and references directory')
|
|
39
|
+
.action(async (name) => {
|
|
40
|
+
try {
|
|
41
|
+
const projectRoot = await resolveProjectRoot();
|
|
42
|
+
const skillDir = path.join(projectRoot, '.claude', 'skills', name);
|
|
43
|
+
const skillMdPath = path.join(skillDir, 'SKILL.md');
|
|
44
|
+
const referencesDir = path.join(skillDir, 'references');
|
|
45
|
+
// Check if skill already exists
|
|
46
|
+
try {
|
|
47
|
+
await fs.access(skillDir);
|
|
48
|
+
console.error(chalk.red(`Skill "${name}" already exists at ${skillDir}`));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Directory does not exist, proceed
|
|
53
|
+
}
|
|
54
|
+
await fs.mkdir(referencesDir, { recursive: true });
|
|
55
|
+
await fs.writeFile(skillMdPath, SKILL_MD_TEMPLATE(name), 'utf-8');
|
|
56
|
+
// Create a .gitkeep in references so the directory is tracked
|
|
57
|
+
await fs.writeFile(path.join(referencesDir, '.gitkeep'), '', 'utf-8');
|
|
58
|
+
console.log(chalk.green('Skill created successfully!'));
|
|
59
|
+
console.log();
|
|
60
|
+
console.log(chalk.bold(' Skill:'), name);
|
|
61
|
+
console.log(chalk.bold(' Path:'), skillDir);
|
|
62
|
+
console.log(chalk.bold(' Files:'));
|
|
63
|
+
console.log(chalk.gray(' -'), 'SKILL.md');
|
|
64
|
+
console.log(chalk.gray(' -'), 'references/');
|
|
65
|
+
console.log();
|
|
66
|
+
console.log(chalk.dim(` Edit ${path.join('.claude/skills', name, 'SKILL.md')} to define the skill.`));
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
70
|
+
console.error(chalk.red('Failed to create skill:'), message);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=generate-skill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-skill.js","sourceRoot":"","sources":["../../../src/cli/commands/generate-skill.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAS9D"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* loom generate - Code generation commands
|
|
3
|
+
*/
|
|
4
|
+
import { registerGenerateCapabilitiesCommand } from './generate-capabilities.js';
|
|
5
|
+
import { registerGenerateSkillCommand } from './generate-skill.js';
|
|
6
|
+
import { registerGeneratePageCommand } from './generate-page.js';
|
|
7
|
+
import { registerGenerateCliCommandCommand } from './generate-cli-command.js';
|
|
8
|
+
export function registerGenerateCommand(program) {
|
|
9
|
+
const generate = program
|
|
10
|
+
.command('generate')
|
|
11
|
+
.description('Generate code from config and templates');
|
|
12
|
+
registerGenerateCapabilitiesCommand(generate);
|
|
13
|
+
registerGenerateSkillCommand(generate);
|
|
14
|
+
registerGeneratePageCommand(generate);
|
|
15
|
+
registerGenerateCliCommandCommand(generate);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,mCAAmC,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,iCAAiC,EAAE,MAAM,2BAA2B,CAAC;AAE9E,MAAM,UAAU,uBAAuB,CAAC,OAAgB;IACtD,MAAM,QAAQ,GAAG,OAAO;SACrB,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,yCAAyC,CAAC,CAAC;IAE1D,mCAAmC,CAAC,QAAQ,CAAC,CAAC;IAC9C,4BAA4B,CAAC,QAAQ,CAAC,CAAC;IACvC,2BAA2B,CAAC,QAAQ,CAAC,CAAC;IACtC,iCAAiC,CAAC,QAAQ,CAAC,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* loom init <name>
|
|
3
|
+
*
|
|
4
|
+
* Create a new Loom project. All options via CLI flags, no interactive prompts.
|
|
5
|
+
*/
|
|
6
|
+
import type { Command } from 'commander';
|
|
7
|
+
export declare function registerInitCommand(program: Command): void;
|
|
8
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAe1D"}
|