awel 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.
Files changed (85) hide show
  1. package/LICENSE +200 -0
  2. package/README.md +98 -0
  3. package/babel-plugin-awel-source.cjs +79 -0
  4. package/bin/awel.js +2 -0
  5. package/dist/cli/agent.d.ts +6 -0
  6. package/dist/cli/agent.js +266 -0
  7. package/dist/cli/babel-setup.d.ts +1 -0
  8. package/dist/cli/babel-setup.js +180 -0
  9. package/dist/cli/comment-popup.d.ts +2 -0
  10. package/dist/cli/comment-popup.js +206 -0
  11. package/dist/cli/config.d.ts +14 -0
  12. package/dist/cli/config.js +29 -0
  13. package/dist/cli/devserver.d.ts +17 -0
  14. package/dist/cli/devserver.js +43 -0
  15. package/dist/cli/index.d.ts +1 -0
  16. package/dist/cli/index.js +34 -0
  17. package/dist/cli/inspector.d.ts +2 -0
  18. package/dist/cli/inspector.js +117 -0
  19. package/dist/cli/logger.d.ts +10 -0
  20. package/dist/cli/logger.js +40 -0
  21. package/dist/cli/plan-store.d.ts +14 -0
  22. package/dist/cli/plan-store.js +18 -0
  23. package/dist/cli/providers/registry.d.ts +17 -0
  24. package/dist/cli/providers/registry.js +112 -0
  25. package/dist/cli/providers/types.d.ts +17 -0
  26. package/dist/cli/providers/types.js +1 -0
  27. package/dist/cli/providers/vercel.d.ts +4 -0
  28. package/dist/cli/providers/vercel.js +483 -0
  29. package/dist/cli/proxy.d.ts +5 -0
  30. package/dist/cli/proxy.js +72 -0
  31. package/dist/cli/server.d.ts +7 -0
  32. package/dist/cli/server.js +104 -0
  33. package/dist/cli/session.d.ts +32 -0
  34. package/dist/cli/session.js +77 -0
  35. package/dist/cli/skills/react-best-practices.md +2934 -0
  36. package/dist/cli/skills/skills/react-best-practices.md +2934 -0
  37. package/dist/cli/sse.d.ts +17 -0
  38. package/dist/cli/sse.js +51 -0
  39. package/dist/cli/subprocess.d.ts +30 -0
  40. package/dist/cli/subprocess.js +163 -0
  41. package/dist/cli/tools/ask-user.d.ts +11 -0
  42. package/dist/cli/tools/ask-user.js +28 -0
  43. package/dist/cli/tools/bash.d.ts +4 -0
  44. package/dist/cli/tools/bash.js +30 -0
  45. package/dist/cli/tools/code-search.d.ts +4 -0
  46. package/dist/cli/tools/code-search.js +70 -0
  47. package/dist/cli/tools/edit.d.ts +6 -0
  48. package/dist/cli/tools/edit.js +37 -0
  49. package/dist/cli/tools/glob.d.ts +4 -0
  50. package/dist/cli/tools/glob.js +29 -0
  51. package/dist/cli/tools/grep.d.ts +5 -0
  52. package/dist/cli/tools/grep.js +146 -0
  53. package/dist/cli/tools/index.d.ts +86 -0
  54. package/dist/cli/tools/index.js +41 -0
  55. package/dist/cli/tools/ls.d.ts +3 -0
  56. package/dist/cli/tools/ls.js +31 -0
  57. package/dist/cli/tools/multi-edit.d.ts +8 -0
  58. package/dist/cli/tools/multi-edit.js +53 -0
  59. package/dist/cli/tools/propose-plan.d.ts +4 -0
  60. package/dist/cli/tools/propose-plan.js +21 -0
  61. package/dist/cli/tools/react-best-practices.d.ts +3 -0
  62. package/dist/cli/tools/react-best-practices.js +55 -0
  63. package/dist/cli/tools/read.d.ts +3 -0
  64. package/dist/cli/tools/read.js +24 -0
  65. package/dist/cli/tools/restart-dev-server.d.ts +3 -0
  66. package/dist/cli/tools/restart-dev-server.js +18 -0
  67. package/dist/cli/tools/todo.d.ts +8 -0
  68. package/dist/cli/tools/todo.js +59 -0
  69. package/dist/cli/tools/web-fetch.d.ts +5 -0
  70. package/dist/cli/tools/web-fetch.js +116 -0
  71. package/dist/cli/tools/web-search.d.ts +5 -0
  72. package/dist/cli/tools/web-search.js +74 -0
  73. package/dist/cli/tools/write.d.ts +4 -0
  74. package/dist/cli/tools/write.js +26 -0
  75. package/dist/cli/types.d.ts +16 -0
  76. package/dist/cli/types.js +2 -0
  77. package/dist/cli/undo.d.ts +49 -0
  78. package/dist/cli/undo.js +212 -0
  79. package/dist/cli/verbose.d.ts +7 -0
  80. package/dist/cli/verbose.js +60 -0
  81. package/dist/dashboard/assets/index-Bk--q3wu.js +313 -0
  82. package/dist/dashboard/assets/index-DkWV03So.css +1 -0
  83. package/dist/dashboard/index.html +16 -0
  84. package/dist/host/host.js +274 -0
  85. package/package.json +67 -0
@@ -0,0 +1,26 @@
1
+ import { tool } from 'ai';
2
+ import { z } from 'zod';
3
+ import { writeFileSync, mkdirSync } from 'fs';
4
+ import { resolve, dirname } from 'path';
5
+ import { pushSnapshot } from '../undo.js';
6
+ export function createWriteTool(cwd) {
7
+ return tool({
8
+ description: 'Write content to a file. Creates the file and any parent directories if they do not exist. Overwrites existing files.',
9
+ inputSchema: z.object({
10
+ file_path: z.string().describe('The path to the file to write (absolute or relative to project root)'),
11
+ content: z.string().describe('The content to write to the file'),
12
+ }),
13
+ execute: async ({ file_path, content }) => {
14
+ const fullPath = file_path.startsWith('/') ? file_path : resolve(cwd, file_path);
15
+ try {
16
+ pushSnapshot(fullPath);
17
+ mkdirSync(dirname(fullPath), { recursive: true });
18
+ writeFileSync(fullPath, content, 'utf-8');
19
+ return `Successfully wrote to ${file_path}`;
20
+ }
21
+ catch (err) {
22
+ return `Error writing file: ${err instanceof Error ? err.message : String(err)}`;
23
+ }
24
+ },
25
+ });
26
+ }
@@ -0,0 +1,16 @@
1
+ export interface SelectedElement {
2
+ tag: string;
3
+ component: string | null;
4
+ source: string | null;
5
+ text: string;
6
+ className: string;
7
+ line: number | null;
8
+ column: number | null;
9
+ props: Record<string, string> | null;
10
+ componentChain: string[] | null;
11
+ attributes: Record<string, string> | null;
12
+ comment?: string;
13
+ mode?: 'attach';
14
+ sourceSnippet?: string | null;
15
+ propsTypeDefinition?: string | null;
16
+ }
@@ -0,0 +1,2 @@
1
+ // ─── Inspector Types ─────────────────────────────────────────
2
+ export {};
@@ -0,0 +1,49 @@
1
+ import { Hono } from 'hono';
2
+ /**
3
+ * Starts a new undo session. All file snapshots captured after this call
4
+ * (and before endUndoSession) will be grouped together.
5
+ * @returns The new session ID
6
+ */
7
+ export declare function startUndoSession(): string;
8
+ /**
9
+ * Ends the current undo session. If snapshots were captured, the session
10
+ * is added to the session stack for later undo. Otherwise, the empty session is discarded.
11
+ */
12
+ export declare function endUndoSession(): void;
13
+ /**
14
+ * Captures the current content of a file before it's modified.
15
+ * For new files (that don't exist yet), stores empty string so undo can delete them.
16
+ * Only captures if there's an active session.
17
+ */
18
+ export declare function pushSnapshot(filePath: string): void;
19
+ /**
20
+ * Pops the most recent session and restores all files to their previous states.
21
+ * If a snapshot content is empty and the file was newly created, deletes it.
22
+ * @returns Array of restored file paths, or null if nothing to undo
23
+ */
24
+ export declare function popAndRestoreSession(): string[] | null;
25
+ /**
26
+ * Reads the most recent session from the stack (without popping it) and pairs
27
+ * each snapshot's original content with the current file content on disk.
28
+ */
29
+ export declare function getLatestSessionDiffs(projectCwd: string): {
30
+ relativePath: string;
31
+ originalContent: string;
32
+ currentContent: string;
33
+ existed: boolean;
34
+ existsNow: boolean;
35
+ }[] | null;
36
+ /**
37
+ * Returns lightweight stats for the currently active (not yet ended) session.
38
+ * Call this before endUndoSession() to include stats in the result event.
39
+ */
40
+ export declare function getCurrentSessionStats(projectCwd: string): {
41
+ relativePath: string;
42
+ additions: number;
43
+ deletions: number;
44
+ isNew: boolean;
45
+ }[] | null;
46
+ /**
47
+ * Creates Hono routes for the undo API.
48
+ */
49
+ export declare function createUndoRoute(projectCwd: string): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
@@ -0,0 +1,212 @@
1
+ import { Hono } from 'hono';
2
+ import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'fs';
3
+ import { relative } from 'path';
4
+ /**
5
+ * Session-based undo groups.
6
+ * Each session ID maps to a list of file snapshots made during that session.
7
+ */
8
+ const undoGroups = new Map();
9
+ /**
10
+ * Stack of session IDs in LIFO order for undo operations.
11
+ */
12
+ const sessionStack = [];
13
+ /**
14
+ * The currently active session ID. Snapshots are only captured when a session is active.
15
+ */
16
+ let currentSessionId = null;
17
+ /**
18
+ * Generates a unique session ID.
19
+ */
20
+ function generateSessionId() {
21
+ return `session-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
22
+ }
23
+ /**
24
+ * Starts a new undo session. All file snapshots captured after this call
25
+ * (and before endUndoSession) will be grouped together.
26
+ * @returns The new session ID
27
+ */
28
+ export function startUndoSession() {
29
+ const sessionId = generateSessionId();
30
+ currentSessionId = sessionId;
31
+ undoGroups.set(sessionId, []);
32
+ return sessionId;
33
+ }
34
+ /**
35
+ * Ends the current undo session. If snapshots were captured, the session
36
+ * is added to the session stack for later undo. Otherwise, the empty session is discarded.
37
+ */
38
+ export function endUndoSession() {
39
+ if (currentSessionId) {
40
+ const snapshots = undoGroups.get(currentSessionId);
41
+ if (snapshots && snapshots.length > 0) {
42
+ sessionStack.push(currentSessionId);
43
+ }
44
+ else {
45
+ // No snapshots captured, discard empty session
46
+ undoGroups.delete(currentSessionId);
47
+ }
48
+ }
49
+ currentSessionId = null;
50
+ }
51
+ /**
52
+ * Captures the current content of a file before it's modified.
53
+ * For new files (that don't exist yet), stores empty string so undo can delete them.
54
+ * Only captures if there's an active session.
55
+ */
56
+ export function pushSnapshot(filePath) {
57
+ if (!currentSessionId) {
58
+ // No active session, skip snapshot
59
+ return;
60
+ }
61
+ const snapshots = undoGroups.get(currentSessionId);
62
+ if (!snapshots) {
63
+ return;
64
+ }
65
+ // Check if we already have a snapshot for this file in this session
66
+ // (we only want to capture the original state, not intermediate states)
67
+ const alreadySnapshotted = snapshots.some(s => s.filePath === filePath);
68
+ if (alreadySnapshotted) {
69
+ return;
70
+ }
71
+ const existed = existsSync(filePath);
72
+ const content = existed ? readFileSync(filePath, 'utf-8') : '';
73
+ snapshots.push({ filePath, content, timestamp: Date.now(), existed });
74
+ }
75
+ /**
76
+ * Pops the most recent session and restores all files to their previous states.
77
+ * If a snapshot content is empty and the file was newly created, deletes it.
78
+ * @returns Array of restored file paths, or null if nothing to undo
79
+ */
80
+ export function popAndRestoreSession() {
81
+ const sessionId = sessionStack.pop();
82
+ if (!sessionId)
83
+ return null;
84
+ const snapshots = undoGroups.get(sessionId);
85
+ if (!snapshots || snapshots.length === 0) {
86
+ undoGroups.delete(sessionId);
87
+ return null;
88
+ }
89
+ const restoredPaths = [];
90
+ // Restore in reverse order (last modified first)
91
+ for (let i = snapshots.length - 1; i >= 0; i--) {
92
+ const snapshot = snapshots[i];
93
+ try {
94
+ if (!snapshot.existed && existsSync(snapshot.filePath)) {
95
+ // File was newly created — undo means delete it
96
+ unlinkSync(snapshot.filePath);
97
+ }
98
+ else {
99
+ writeFileSync(snapshot.filePath, snapshot.content, 'utf-8');
100
+ }
101
+ restoredPaths.push(snapshot.filePath);
102
+ }
103
+ catch (err) {
104
+ console.error(`Failed to restore ${snapshot.filePath}:`, err);
105
+ }
106
+ }
107
+ undoGroups.delete(sessionId);
108
+ return restoredPaths;
109
+ }
110
+ /**
111
+ * Reads the most recent session from the stack (without popping it) and pairs
112
+ * each snapshot's original content with the current file content on disk.
113
+ */
114
+ export function getLatestSessionDiffs(projectCwd) {
115
+ if (sessionStack.length === 0)
116
+ return null;
117
+ const sessionId = sessionStack[sessionStack.length - 1];
118
+ const snapshots = undoGroups.get(sessionId);
119
+ if (!snapshots || snapshots.length === 0)
120
+ return null;
121
+ return snapshots.map((snapshot) => {
122
+ const relativePath = relative(projectCwd, snapshot.filePath);
123
+ const existsNow = existsSync(snapshot.filePath);
124
+ const currentContent = existsNow ? readFileSync(snapshot.filePath, 'utf-8') : '';
125
+ return {
126
+ relativePath,
127
+ originalContent: snapshot.content,
128
+ currentContent,
129
+ existed: snapshot.existed,
130
+ existsNow,
131
+ };
132
+ });
133
+ }
134
+ /**
135
+ * Computes +/- line stats for two strings using bag-based line matching.
136
+ */
137
+ function countLineStats(original, current) {
138
+ const oldLines = original.split('\n');
139
+ const newLines = current.split('\n');
140
+ const oldBag = new Map();
141
+ for (const line of oldLines) {
142
+ oldBag.set(line, (oldBag.get(line) || 0) + 1);
143
+ }
144
+ let matched = 0;
145
+ for (const line of newLines) {
146
+ const count = oldBag.get(line);
147
+ if (count && count > 0) {
148
+ oldBag.set(line, count - 1);
149
+ matched++;
150
+ }
151
+ }
152
+ return {
153
+ additions: newLines.length - matched,
154
+ deletions: oldLines.length - matched,
155
+ };
156
+ }
157
+ /**
158
+ * Returns lightweight stats for the currently active (not yet ended) session.
159
+ * Call this before endUndoSession() to include stats in the result event.
160
+ */
161
+ export function getCurrentSessionStats(projectCwd) {
162
+ if (!currentSessionId)
163
+ return null;
164
+ const snapshots = undoGroups.get(currentSessionId);
165
+ if (!snapshots || snapshots.length === 0)
166
+ return null;
167
+ return snapshots.map((snapshot) => {
168
+ const relativePath = relative(projectCwd, snapshot.filePath);
169
+ const existsNow = existsSync(snapshot.filePath);
170
+ const currentContent = existsNow ? readFileSync(snapshot.filePath, 'utf-8') : '';
171
+ const isNew = !snapshot.existed && existsNow;
172
+ const { additions, deletions } = countLineStats(snapshot.content, currentContent);
173
+ return { relativePath, additions, deletions, isNew };
174
+ });
175
+ }
176
+ /**
177
+ * Creates Hono routes for the undo API.
178
+ */
179
+ export function createUndoRoute(projectCwd) {
180
+ const undo = new Hono();
181
+ undo.post('/api/undo', async (c) => {
182
+ const restoredPaths = popAndRestoreSession();
183
+ if (restoredPaths && restoredPaths.length > 0) {
184
+ return c.json({
185
+ success: true,
186
+ restored: restoredPaths.map(p => relative(projectCwd, p)),
187
+ });
188
+ }
189
+ return c.json({ success: false, error: 'Nothing to undo' }, 400);
190
+ });
191
+ undo.get('/api/undo/diff', async (c) => {
192
+ const diffs = getLatestSessionDiffs(projectCwd);
193
+ if (!diffs) {
194
+ return c.json({ success: false, error: 'No session to diff' }, 400);
195
+ }
196
+ return c.json({ success: true, diffs });
197
+ });
198
+ undo.get('/api/undo/stack', async (c) => {
199
+ const sessions = sessionStack.map(sessionId => {
200
+ const snapshots = undoGroups.get(sessionId) || [];
201
+ return {
202
+ sessionId,
203
+ files: snapshots.map(s => ({
204
+ file: relative(projectCwd, s.filePath),
205
+ timestamp: s.timestamp,
206
+ })),
207
+ };
208
+ });
209
+ return c.json(sessions);
210
+ });
211
+ return undo;
212
+ }
@@ -0,0 +1,7 @@
1
+ export declare function setVerbose(enabled: boolean): void;
2
+ export declare function isVerbose(): boolean;
3
+ /**
4
+ * Log a verbose event to the terminal. No-op if verbose mode is off.
5
+ * Outputs human-readable, color-coded lines to stderr.
6
+ */
7
+ export declare function logEvent(eventType: string, detail?: string): void;
@@ -0,0 +1,60 @@
1
+ // ─── Verbose Mode ────────────────────────────────────────────
2
+ // Module-level toggle for CLI verbose logging.
3
+ let _verbose = false;
4
+ export function setVerbose(enabled) {
5
+ _verbose = enabled;
6
+ }
7
+ export function isVerbose() {
8
+ return _verbose;
9
+ }
10
+ // ANSI 256-color helpers — darker shades that stay visible on light backgrounds
11
+ const dim = (s) => `\x1b[2m${s}\x1b[22m`;
12
+ const cyan = (s) => `\x1b[38;5;30m${s}\x1b[39m`;
13
+ const yellow = (s) => `\x1b[38;5;136m${s}\x1b[39m`;
14
+ const green = (s) => `\x1b[38;5;34m${s}\x1b[39m`;
15
+ const red = (s) => `\x1b[38;5;160m${s}\x1b[39m`;
16
+ const magenta = (s) => `\x1b[38;5;127m${s}\x1b[39m`;
17
+ const bold = (s) => `\x1b[1m${s}\x1b[22m`;
18
+ function timestamp() {
19
+ const now = new Date();
20
+ const h = String(now.getHours()).padStart(2, '0');
21
+ const m = String(now.getMinutes()).padStart(2, '0');
22
+ const s = String(now.getSeconds()).padStart(2, '0');
23
+ const ms = String(now.getMilliseconds()).padStart(3, '0');
24
+ return dim(`[${h}:${m}:${s}.${ms}]`);
25
+ }
26
+ function truncate(s, max = 120) {
27
+ const oneLine = s.replace(/\n/g, '\\n');
28
+ if (oneLine.length <= max)
29
+ return oneLine;
30
+ return oneLine.slice(0, max) + dim('…');
31
+ }
32
+ function colorForEvent(eventType) {
33
+ if (eventType.startsWith('stream:'))
34
+ return cyan;
35
+ switch (eventType) {
36
+ case 'text-delta': return dim;
37
+ case 'tool-call': return yellow;
38
+ case 'tool-result': return green;
39
+ case 'plan': return magenta;
40
+ case 'question': return magenta;
41
+ case 'finish-step': return dim;
42
+ case 'error': return red;
43
+ case 'abort': return red;
44
+ default: return (s) => s;
45
+ }
46
+ }
47
+ /**
48
+ * Log a verbose event to the terminal. No-op if verbose mode is off.
49
+ * Outputs human-readable, color-coded lines to stderr.
50
+ */
51
+ export function logEvent(eventType, detail) {
52
+ if (!_verbose)
53
+ return;
54
+ const color = colorForEvent(eventType);
55
+ const tag = color(bold(eventType.padEnd(14)));
56
+ const parts = [timestamp(), tag];
57
+ if (detail)
58
+ parts.push(truncate(detail, 200));
59
+ process.stderr.write(parts.join(' ') + '\n');
60
+ }