@claudiv/cli 0.1.0
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/LICENSE +21 -0
- package/README.md +430 -0
- package/bin/claudiv.js +407 -0
- package/dist/claude-api.d.ts +20 -0
- package/dist/claude-api.js +117 -0
- package/dist/claude-cli.d.ts +18 -0
- package/dist/claude-cli.js +124 -0
- package/dist/claude-client.d.ts +16 -0
- package/dist/claude-client.js +44 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +67 -0
- package/dist/dev-server.d.ts +10 -0
- package/dist/dev-server.js +118 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +305 -0
- package/dist/updater.d.ts +29 -0
- package/dist/updater.js +79 -0
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.js +36 -0
- package/dist/watcher.d.ts +22 -0
- package/dist/watcher.js +66 -0
- package/package.json +69 -0
package/dist/updater.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XML-ish updater - inserts AI responses into spec.html
|
|
3
|
+
*/
|
|
4
|
+
import { writeFile } from 'fs/promises';
|
|
5
|
+
import { logger } from './utils/logger.js';
|
|
6
|
+
/**
|
|
7
|
+
* Strip code blocks from response and replace with reference
|
|
8
|
+
*/
|
|
9
|
+
export function stripCodeBlocks(response, hasCode) {
|
|
10
|
+
// Remove code blocks (```...```)
|
|
11
|
+
const withoutCodeBlocks = response.replace(/```[\s\S]*?```/g, '').trim();
|
|
12
|
+
// If there was code, add a reference
|
|
13
|
+
if (hasCode) {
|
|
14
|
+
return `${withoutCodeBlocks}\n\n→ Implementation in spec.code.html`;
|
|
15
|
+
}
|
|
16
|
+
return withoutCodeBlocks;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Update element with AI response: remove action attribute and add/update <ai> child
|
|
20
|
+
*/
|
|
21
|
+
export function updateElementWithResponse($, element, action, response, hasCode = false) {
|
|
22
|
+
const $element = $(element);
|
|
23
|
+
// 1. Remove the action attribute (gen/retry/undo)
|
|
24
|
+
$element.removeAttr(action);
|
|
25
|
+
logger.debug(`Removed '${action}' attribute from element`);
|
|
26
|
+
// 2. Strip code blocks and add reference if code was generated
|
|
27
|
+
const cleanResponse = stripCodeBlocks(response, hasCode);
|
|
28
|
+
// 3. Add or append <ai> child element with response
|
|
29
|
+
// Create new <ai> element
|
|
30
|
+
const aiElement = $('<ai></ai>');
|
|
31
|
+
aiElement.html(cleanResponse);
|
|
32
|
+
// Append to element
|
|
33
|
+
$element.append(aiElement);
|
|
34
|
+
logger.debug('Added <ai> element with response as child');
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Serialize cheerio DOM back to HTML string
|
|
38
|
+
*/
|
|
39
|
+
export async function serializeToHTML($) {
|
|
40
|
+
// Use $.html() to serialize the entire document
|
|
41
|
+
const html = $.html();
|
|
42
|
+
// Format with prettier for consistent indentation (disabled for now)
|
|
43
|
+
// TODO: Add prettier as optional dependency for better formatting
|
|
44
|
+
return html;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Write updated content back to spec.html safely
|
|
48
|
+
*/
|
|
49
|
+
export async function writeSpecFile(filePath, content) {
|
|
50
|
+
try {
|
|
51
|
+
await writeFile(filePath, content, 'utf-8');
|
|
52
|
+
logger.debug(`Wrote updated content to ${filePath}`);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
const err = error;
|
|
56
|
+
logger.error(`Failed to write ${filePath}: ${err.message}`);
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Grace period to prevent immediate re-trigger of watcher
|
|
62
|
+
*/
|
|
63
|
+
export function sleep(ms) {
|
|
64
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Complete update flow: remove action attribute, add AI response, serialize, write file
|
|
68
|
+
*/
|
|
69
|
+
export async function updateSpecWithResponse($, element, action, response, filePath, hasCode = false) {
|
|
70
|
+
// Update the element: remove action attribute and add <ai> child
|
|
71
|
+
updateElementWithResponse($, element, action, response, hasCode);
|
|
72
|
+
// Serialize back to HTML
|
|
73
|
+
const updatedHTML = await serializeToHTML($);
|
|
74
|
+
// Write to file
|
|
75
|
+
await writeSpecFile(filePath, updatedHTML);
|
|
76
|
+
// Grace period to prevent circular trigger
|
|
77
|
+
await sleep(500);
|
|
78
|
+
logger.success('Updated spec.html with AI response');
|
|
79
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple console logging utilities
|
|
3
|
+
*/
|
|
4
|
+
export declare const logger: {
|
|
5
|
+
info(message: string): void;
|
|
6
|
+
success(message: string): void;
|
|
7
|
+
error(message: string): void;
|
|
8
|
+
warn(message: string): void;
|
|
9
|
+
debug(message: string): void;
|
|
10
|
+
processing(message: string): void;
|
|
11
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple console logging utilities
|
|
3
|
+
*/
|
|
4
|
+
const colors = {
|
|
5
|
+
reset: '\x1b[0m',
|
|
6
|
+
bright: '\x1b[1m',
|
|
7
|
+
dim: '\x1b[2m',
|
|
8
|
+
red: '\x1b[31m',
|
|
9
|
+
green: '\x1b[32m',
|
|
10
|
+
yellow: '\x1b[33m',
|
|
11
|
+
blue: '\x1b[34m',
|
|
12
|
+
magenta: '\x1b[35m',
|
|
13
|
+
cyan: '\x1b[36m',
|
|
14
|
+
};
|
|
15
|
+
export const logger = {
|
|
16
|
+
info(message) {
|
|
17
|
+
console.log(`${colors.blue}ℹ${colors.reset} ${message}`);
|
|
18
|
+
},
|
|
19
|
+
success(message) {
|
|
20
|
+
console.log(`${colors.green}✓${colors.reset} ${message}`);
|
|
21
|
+
},
|
|
22
|
+
error(message) {
|
|
23
|
+
console.error(`${colors.red}✗${colors.reset} ${message}`);
|
|
24
|
+
},
|
|
25
|
+
warn(message) {
|
|
26
|
+
console.warn(`${colors.yellow}⚠${colors.reset} ${message}`);
|
|
27
|
+
},
|
|
28
|
+
debug(message) {
|
|
29
|
+
if (process.env.DEBUG) {
|
|
30
|
+
console.log(`${colors.dim}${message}${colors.reset}`);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
processing(message) {
|
|
34
|
+
console.log(`${colors.cyan}⚙${colors.reset} ${message}`);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File watcher for spec.html with debouncing and circular trigger prevention
|
|
3
|
+
*/
|
|
4
|
+
import { EventEmitter } from 'events';
|
|
5
|
+
import type { Config } from '@claudiv/core';
|
|
6
|
+
export declare class SpecFileWatcher extends EventEmitter {
|
|
7
|
+
private watcher;
|
|
8
|
+
private isUpdating;
|
|
9
|
+
private config;
|
|
10
|
+
constructor(config: Config);
|
|
11
|
+
start(): void;
|
|
12
|
+
stop(): void;
|
|
13
|
+
/**
|
|
14
|
+
* Signal that we're about to update the file internally
|
|
15
|
+
* This prevents circular triggers
|
|
16
|
+
*/
|
|
17
|
+
setUpdating(value: boolean): void;
|
|
18
|
+
/**
|
|
19
|
+
* Check if we're currently updating
|
|
20
|
+
*/
|
|
21
|
+
isCurrentlyUpdating(): boolean;
|
|
22
|
+
}
|
package/dist/watcher.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File watcher for spec.html with debouncing and circular trigger prevention
|
|
3
|
+
*/
|
|
4
|
+
import chokidar from 'chokidar';
|
|
5
|
+
import debounce from 'lodash.debounce';
|
|
6
|
+
import { EventEmitter } from 'events';
|
|
7
|
+
import { logger } from './utils/logger.js';
|
|
8
|
+
export class SpecFileWatcher extends EventEmitter {
|
|
9
|
+
watcher = null;
|
|
10
|
+
isUpdating = false;
|
|
11
|
+
config;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
super();
|
|
14
|
+
this.config = config;
|
|
15
|
+
}
|
|
16
|
+
start() {
|
|
17
|
+
logger.info(`Starting file watcher for ${this.config.specFile}...`);
|
|
18
|
+
// Debounced change handler
|
|
19
|
+
const handleChange = debounce((path) => {
|
|
20
|
+
// Skip if we're in the middle of updating the file ourselves
|
|
21
|
+
if (this.isUpdating) {
|
|
22
|
+
logger.debug('Skipping change event (internal update)');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
logger.processing(`Detected change in ${path}`);
|
|
26
|
+
this.emit('change', path);
|
|
27
|
+
}, this.config.debounceMs);
|
|
28
|
+
// Watch spec.html with chokidar
|
|
29
|
+
this.watcher = chokidar.watch(this.config.specFile, {
|
|
30
|
+
ignoreInitial: false, // Process on startup
|
|
31
|
+
awaitWriteFinish: {
|
|
32
|
+
stabilityThreshold: 300, // Wait for file to stabilize
|
|
33
|
+
pollInterval: 100,
|
|
34
|
+
},
|
|
35
|
+
persistent: true,
|
|
36
|
+
});
|
|
37
|
+
this.watcher.on('change', handleChange);
|
|
38
|
+
this.watcher.on('add', handleChange);
|
|
39
|
+
this.watcher.on('error', (error) => {
|
|
40
|
+
const err = error;
|
|
41
|
+
logger.error(`Watcher error: ${err.message}`);
|
|
42
|
+
});
|
|
43
|
+
logger.success('File watcher started successfully');
|
|
44
|
+
}
|
|
45
|
+
stop() {
|
|
46
|
+
if (this.watcher) {
|
|
47
|
+
logger.info('Stopping file watcher...');
|
|
48
|
+
this.watcher.close();
|
|
49
|
+
this.watcher = null;
|
|
50
|
+
logger.success('File watcher stopped');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Signal that we're about to update the file internally
|
|
55
|
+
* This prevents circular triggers
|
|
56
|
+
*/
|
|
57
|
+
setUpdating(value) {
|
|
58
|
+
this.isUpdating = value;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if we're currently updating
|
|
62
|
+
*/
|
|
63
|
+
isCurrentlyUpdating() {
|
|
64
|
+
return this.isUpdating;
|
|
65
|
+
}
|
|
66
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@claudiv/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Claude in a Div — Universal Declarative Generation Platform",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"claudiv": "./bin/claudiv.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/**/*",
|
|
12
|
+
"bin/**/*",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"dev": "tsx src/index.ts",
|
|
18
|
+
"dev:cli": "MODE=cli tsx src/index.ts",
|
|
19
|
+
"dev:api": "MODE=api tsx src/index.ts",
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"clean": "rm -rf dist",
|
|
22
|
+
"start": "node dist/index.js",
|
|
23
|
+
"watch": "tsx watch src/index.ts",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@claudiv/core": "^0.1.0",
|
|
28
|
+
"@anthropic-ai/sdk": "^0.32.1",
|
|
29
|
+
"cheerio": "^1.0.0",
|
|
30
|
+
"chokidar": "^5.0.0",
|
|
31
|
+
"dotenv": "^16.4.7",
|
|
32
|
+
"htmlparser2": "^9.1.0",
|
|
33
|
+
"lodash.debounce": "^4.0.8",
|
|
34
|
+
"vite": "^6.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/lodash.debounce": "^4.0.9",
|
|
38
|
+
"@types/node": "^20.0.0",
|
|
39
|
+
"tsx": "^4.0.0",
|
|
40
|
+
"typescript": "^5.0.0"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/claudiv-ai/cli.git"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"claudiv",
|
|
51
|
+
"claude",
|
|
52
|
+
"ai",
|
|
53
|
+
"cdml",
|
|
54
|
+
"code-generation",
|
|
55
|
+
"universal",
|
|
56
|
+
"declarative",
|
|
57
|
+
"reverse-engineering",
|
|
58
|
+
"cli"
|
|
59
|
+
],
|
|
60
|
+
"author": "Amir Guterman",
|
|
61
|
+
"license": "MIT",
|
|
62
|
+
"bugs": {
|
|
63
|
+
"url": "https://github.com/claudiv-ai/claudiv/issues"
|
|
64
|
+
},
|
|
65
|
+
"homepage": "https://claudiv.org",
|
|
66
|
+
"engines": {
|
|
67
|
+
"node": ">=20.0.0"
|
|
68
|
+
}
|
|
69
|
+
}
|