@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.
@@ -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
+ }
@@ -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
+ }