Add CONTEXT.md generation on kisync init
Creates a comprehensive context file for AI assistants (Claude Code, Codex) that explains the project structure, script execution model, execQuery usage, query file naming conventions, and available sandbox globals. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as readline from 'readline';
|
||||
import chalk from 'chalk';
|
||||
import { writeConfig, findProjectRoot } from '../config';
|
||||
@@ -91,11 +93,173 @@ export async function initCommand(): Promise<void> {
|
||||
|
||||
writeConfig({ host, token }, cwd);
|
||||
|
||||
// Write default context file for AI assistants
|
||||
writeDefaultContext(cwd);
|
||||
|
||||
console.log(chalk.green('\nProject initialized successfully!'));
|
||||
console.log(chalk.gray(`Config saved to: ${cwd}/.kisync.json`));
|
||||
console.log(chalk.gray(`AI context: ${cwd}/CONTEXT.md`));
|
||||
console.log('');
|
||||
console.log('Next steps:');
|
||||
console.log(` ${chalk.cyan('kisync pull')} — download endpoints from server`);
|
||||
console.log(` ${chalk.cyan('kisync status')} — check what changed`);
|
||||
console.log(` ${chalk.cyan('kisync push')} — upload your changes`);
|
||||
}
|
||||
|
||||
function writeDefaultContext(cwd: string): void {
|
||||
const contextPath = path.join(cwd, 'CONTEXT.md');
|
||||
if (fs.existsSync(contextPath)) return; // don't overwrite
|
||||
|
||||
const content = `# KIS API Builder — Project Context
|
||||
|
||||
This directory contains endpoint definitions synced from a KIS API Builder server via \`kisync\`.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
\`\`\`
|
||||
project-root/
|
||||
├── .kisync.json # connection config (host, token) — DO NOT COMMIT
|
||||
├── .kisync-state.json # sync state (hashes, timestamps) — DO NOT EDIT
|
||||
├── CONTEXT.md # this file
|
||||
├── FolderName/ # API folder
|
||||
│ ├── _folder.json # folder metadata (id, name, parent_id)
|
||||
│ └── EndpointName/ # single API endpoint
|
||||
│ ├── endpoint.json # endpoint metadata (method, path, params, config)
|
||||
│ ├── query.sql # SQL query (for sql-type endpoints)
|
||||
│ ├── main.js # JavaScript script (for script-type endpoints)
|
||||
│ ├── main.py # Python script (for script-type endpoints)
|
||||
│ ├── request.http # HTTP request definition (for AQL-type endpoints)
|
||||
│ └── queries/ # named queries used by scripts
|
||||
│ ├── _index.json # query index (name → file, database binding)
|
||||
│ ├── get_users.sql # SQL query file
|
||||
│ └── get_data.http # AQL/HTTP query file
|
||||
\`\`\`
|
||||
|
||||
## Script Execution Model
|
||||
|
||||
Scripts in \`main.js\` / \`main.py\` are NOT standalone programs.
|
||||
They run inside an API Builder sandbox with pre-injected globals.
|
||||
|
||||
### IMPORTANT: Do NOT wrap code in functions
|
||||
|
||||
WRONG:
|
||||
\`\`\`js
|
||||
function main() {
|
||||
const result = await execQuery('get_users');
|
||||
return result.data;
|
||||
}
|
||||
main();
|
||||
\`\`\`
|
||||
|
||||
CORRECT — write code directly at the top level:
|
||||
\`\`\`js
|
||||
const result = await execQuery('get_users');
|
||||
return result.data;
|
||||
\`\`\`
|
||||
|
||||
The server wraps the code in \`(async function() { <your code> })()\` automatically.
|
||||
A top-level \`return\` statement sets the API response body.
|
||||
|
||||
### Available Globals (JavaScript)
|
||||
|
||||
| Global | Description |
|
||||
|---|---|
|
||||
| \`params\` | Object with request parameters (query string + body). Example: \`params.user_id\` |
|
||||
| \`execQuery(name, extraParams?)\` | Execute a named query from the \`queries/\` folder. Returns \`{ success, data, rowCount, error }\` |
|
||||
| \`console.log/warn/error/info\` | Captured logs, visible in API Builder UI |
|
||||
| \`JSON, Date, Math, Array, Object, String, Number, Boolean, RegExp, Map, Set, Promise\` | Standard JS built-ins |
|
||||
| \`setTimeout(fn, ms)\` | Capped at 30 seconds |
|
||||
| \`parseInt, parseFloat, isNaN, isFinite\` | Standard utility functions |
|
||||
| \`encodeURIComponent, decodeURIComponent, encodeURI, decodeURI\` | URL encoding |
|
||||
|
||||
No \`require\`, \`import\`, \`fetch\`, \`fs\`, \`process\`, or \`Buffer\` — the sandbox is isolated.
|
||||
|
||||
### Available Globals (Python)
|
||||
|
||||
| Global | Description |
|
||||
|---|---|
|
||||
| \`params\` | Dict with request parameters. Example: \`params["user_id"]\` |
|
||||
| \`exec_query(name, additional_params=None)\` | Execute a named query. Returns dict \`{ "success", "data", "rowCount", "error" }\` |
|
||||
| \`json, sys, datetime\` | Pre-imported standard modules |
|
||||
|
||||
Use \`return\` to set the response. The code is wrapped in a function automatically.
|
||||
|
||||
### execQuery / exec_query Explained
|
||||
|
||||
The function \`execQuery\` (JS) or \`exec_query\` (Python) runs a query defined in the \`queries/\` folder.
|
||||
|
||||
Each query file is mapped via \`queries/_index.json\`:
|
||||
\`\`\`json
|
||||
[
|
||||
{
|
||||
"name": "get_users",
|
||||
"database_id": "abc-123",
|
||||
"file": "get_users.sql"
|
||||
},
|
||||
{
|
||||
"name": "get_data",
|
||||
"database_id": "def-456",
|
||||
"file": "get_data.http",
|
||||
"type": "aql"
|
||||
}
|
||||
]
|
||||
\`\`\`
|
||||
|
||||
**The \`name\` field is the key** — it's what you pass to \`execQuery("get_users")\`.
|
||||
The \`file\` field is the corresponding SQL or HTTP file in the \`queries/\` directory.
|
||||
|
||||
SQL queries use \`$paramName\` placeholders that auto-bind from \`params\`:
|
||||
\`\`\`sql
|
||||
SELECT * FROM users WHERE id = $user_id AND status = $status
|
||||
\`\`\`
|
||||
|
||||
You can also pass extra params:
|
||||
\`\`\`js
|
||||
const result = await execQuery('get_users', { status: 'active' });
|
||||
// result.data — array of rows
|
||||
// result.success — boolean
|
||||
// result.rowCount — number of rows
|
||||
\`\`\`
|
||||
|
||||
### AQL (HTTP) Queries
|
||||
|
||||
\`.http\` files define HTTP requests to external APIs:
|
||||
\`\`\`http
|
||||
POST https://example.com/api/patients
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "{{name}}",
|
||||
"age": {{age}}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### endpoint.json Fields
|
||||
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| \`id\` | Server-side UUID — do not change |
|
||||
| \`name\` | Display name of the endpoint |
|
||||
| \`method\` | HTTP method: GET, POST, PUT, DELETE |
|
||||
| \`path\` | URL path, e.g. \`/api/users\` |
|
||||
| \`execution_type\` | \`"sql"\`, \`"script"\`, or \`"aql"\` |
|
||||
| \`parameters\` | Array of \`{ name, type, required, default_value, description }\` |
|
||||
| \`database_name\` | Bound database name (for sql-type) |
|
||||
| \`database_id\` | Bound database UUID |
|
||||
| \`updated_at\` | Last server update timestamp — do not change |
|
||||
|
||||
## Sync Workflow
|
||||
|
||||
\`\`\`bash
|
||||
kisync pull # download from server → local files
|
||||
# ... edit files ...
|
||||
kisync status # see what changed locally and on server
|
||||
kisync push # upload changes (with conflict detection)
|
||||
\`\`\`
|
||||
|
||||
Conflict detection: if someone else modified an endpoint on the server since your
|
||||
last pull, \`push\` will warn you. Use \`--force\` to overwrite, or \`pull\` first to merge.
|
||||
`;
|
||||
|
||||
fs.writeFileSync(contextPath, content, 'utf-8');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user