next-ai-editor 0.1.3 → 0.2.1

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 (63) hide show
  1. package/dist/AIEditorProvider-CKA2K_g2.js +1428 -0
  2. package/dist/AIEditorProvider-CKA2K_g2.js.map +1 -0
  3. package/dist/AIEditorProvider-C_zRSAuV.cjs +1427 -0
  4. package/dist/AIEditorProvider-C_zRSAuV.cjs.map +1 -0
  5. package/dist/client/AIEditorProvider.d.ts +1 -33
  6. package/dist/client/AIEditorProvider.d.ts.map +1 -1
  7. package/dist/client/components/ChatPanel.d.ts +18 -0
  8. package/dist/client/components/ChatPanel.d.ts.map +1 -0
  9. package/dist/client/components/ControlPill.d.ts +8 -0
  10. package/dist/client/components/ControlPill.d.ts.map +1 -0
  11. package/dist/client/components/MessageItem.d.ts +7 -0
  12. package/dist/client/components/MessageItem.d.ts.map +1 -0
  13. package/dist/client/components/MessageList.d.ts +7 -0
  14. package/dist/client/components/MessageList.d.ts.map +1 -0
  15. package/dist/client/components/TaskHistoryPanel.d.ts +10 -0
  16. package/dist/client/components/TaskHistoryPanel.d.ts.map +1 -0
  17. package/dist/client/components/index.d.ts +11 -0
  18. package/dist/client/components/index.d.ts.map +1 -0
  19. package/dist/client/hooks/index.d.ts +3 -0
  20. package/dist/client/hooks/index.d.ts.map +1 -0
  21. package/dist/client/hooks/useChatStream.d.ts +66 -0
  22. package/dist/client/hooks/useChatStream.d.ts.map +1 -0
  23. package/dist/client/hooks/useHotReload.d.ts +10 -0
  24. package/dist/client/hooks/useHotReload.d.ts.map +1 -0
  25. package/dist/client/index.d.ts +3 -0
  26. package/dist/client/index.d.ts.map +1 -1
  27. package/dist/client.cjs +7 -1
  28. package/dist/client.cjs.map +1 -1
  29. package/dist/client.js +8 -2
  30. package/dist/client.js.map +1 -1
  31. package/dist/{index-DrmEf13c.js → comments-D3m0RsOO.js} +440 -15
  32. package/dist/comments-D3m0RsOO.js.map +1 -0
  33. package/dist/{index-CNJqd4EQ.cjs → comments-Daur80r4.cjs} +426 -1
  34. package/dist/comments-Daur80r4.cjs.map +1 -0
  35. package/dist/index.cjs +34 -27
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.js +22 -15
  38. package/dist/index.js.map +1 -1
  39. package/dist/next-ai-editor.css +880 -0
  40. package/dist/server/agent/sdk-client.d.ts +54 -0
  41. package/dist/server/agent/sdk-client.d.ts.map +1 -0
  42. package/dist/server/agent/session-store.d.ts +101 -0
  43. package/dist/server/agent/session-store.d.ts.map +1 -0
  44. package/dist/server/handlers/chat.d.ts +6 -0
  45. package/dist/server/handlers/chat.d.ts.map +1 -0
  46. package/dist/server/handlers/comments.d.ts +10 -0
  47. package/dist/server/handlers/comments.d.ts.map +1 -0
  48. package/dist/server/handlers/index.d.ts +2 -1
  49. package/dist/server/handlers/index.d.ts.map +1 -1
  50. package/dist/server/index.d.ts +1 -0
  51. package/dist/server/index.d.ts.map +1 -1
  52. package/dist/server.cjs +27 -26
  53. package/dist/server.cjs.map +1 -1
  54. package/dist/server.js +14 -13
  55. package/dist/shared/comment-types.d.ts +140 -0
  56. package/dist/shared/comment-types.d.ts.map +1 -0
  57. package/package.json +13 -4
  58. package/dist/AIEditorProvider-BGHm2xyU.cjs +0 -2167
  59. package/dist/AIEditorProvider-BGHm2xyU.cjs.map +0 -1
  60. package/dist/AIEditorProvider-CxdGjdLL.js +0 -2168
  61. package/dist/AIEditorProvider-CxdGjdLL.js.map +0 -1
  62. package/dist/index-CNJqd4EQ.cjs.map +0 -1
  63. package/dist/index-DrmEf13c.js.map +0 -1
@@ -10,6 +10,8 @@ const t = require("@babel/types");
10
10
  const sourceMap = require("@jridgewell/source-map");
11
11
  const pathUtils = require("./path-utils-DYzEWUGy.cjs");
12
12
  const crypto = require("crypto");
13
+ const claudeAgentSdk = require("@anthropic-ai/claude-agent-sdk");
14
+ const fs$1 = require("fs");
13
15
  function _interopNamespaceDefault(e) {
14
16
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
15
17
  if (e) {
@@ -1533,6 +1535,361 @@ Generate 6-8 initial suggestions:`;
1533
1535
  return null;
1534
1536
  }
1535
1537
  }
1538
+ class AIEditorAgent {
1539
+ constructor(session, projectRoot) {
1540
+ this.session = session;
1541
+ this.projectRoot = projectRoot;
1542
+ }
1543
+ /**
1544
+ * Create a new agent instance
1545
+ */
1546
+ static async create(config) {
1547
+ const sessionOptions = {
1548
+ model: "claude-sonnet-4-5-20250929",
1549
+ // Enable auto-apply for edits without permission prompts
1550
+ permissionMode: "acceptEdits",
1551
+ env: {
1552
+ ...process.env,
1553
+ ANTHROPIC_API_KEY: config.apiKey,
1554
+ // Set working directory
1555
+ PWD: config.projectRoot
1556
+ },
1557
+ // Explicitly allow core development tools
1558
+ allowedTools: config.allowedTools || ["Read", "Edit", "Glob", "Grep"],
1559
+ disallowedTools: config.disallowedTools || []
1560
+ };
1561
+ let session;
1562
+ if (config.sessionId) {
1563
+ session = claudeAgentSdk.unstable_v2_resumeSession(config.sessionId, sessionOptions);
1564
+ } else {
1565
+ session = claudeAgentSdk.unstable_v2_createSession(sessionOptions);
1566
+ }
1567
+ return new AIEditorAgent(session, config.projectRoot);
1568
+ }
1569
+ /**
1570
+ * Send a message to the agent and stream responses
1571
+ */
1572
+ async *sendMessage(message, componentContext) {
1573
+ let fullMessage = `<response_style>
1574
+ KEEP RESPONSES EXTREMELY BRIEF AND STATUS-LIKE.
1575
+ - Use terse status messages: "Reading file.tsx", "Changing color", "Done"
1576
+ - NO conversational language (avoid: "I'll", "Let me", "Sure", "Great", "I can see")
1577
+ - NO explanations unless explicitly asked
1578
+ - State ONLY what you're doing, nothing more
1579
+ - Format: Action + target (e.g., "Updating StatsCard.tsx:24")
1580
+ </response_style>
1581
+
1582
+ `;
1583
+ if (componentContext) {
1584
+ fullMessage += `[Component: ${componentContext.componentName} at ${componentContext.filePath}:${componentContext.lineNumber}]
1585
+
1586
+ `;
1587
+ }
1588
+ fullMessage += message;
1589
+ await this.session.send(fullMessage);
1590
+ for await (const event of this.session.stream()) {
1591
+ yield event;
1592
+ }
1593
+ }
1594
+ /**
1595
+ * Get the session ID
1596
+ */
1597
+ getSessionId() {
1598
+ return this.session.sessionId;
1599
+ }
1600
+ /**
1601
+ * Close the session
1602
+ */
1603
+ close() {
1604
+ this.session.close();
1605
+ }
1606
+ /**
1607
+ * Async disposal support
1608
+ */
1609
+ async [Symbol.asyncDispose]() {
1610
+ await this.session[Symbol.asyncDispose]();
1611
+ }
1612
+ }
1613
+ class AgentSessionStore {
1614
+ // 5 minutes
1615
+ constructor() {
1616
+ this.sessions = /* @__PURE__ */ new Map();
1617
+ this.SESSION_TIMEOUT = 1e3 * 60 * 30;
1618
+ this.CLEANUP_INTERVAL = 1e3 * 60 * 5;
1619
+ this.startCleanupTimer();
1620
+ }
1621
+ /**
1622
+ * Create or resume a session
1623
+ */
1624
+ async createSession(config) {
1625
+ const sessionId = config.sessionId || this.generateSessionId();
1626
+ const existing = this.sessions.get(sessionId);
1627
+ if (existing) {
1628
+ existing.lastActive = Date.now();
1629
+ existing.threadId = config.threadId;
1630
+ return existing;
1631
+ }
1632
+ const agent = await AIEditorAgent.create({
1633
+ apiKey: config.apiKey,
1634
+ projectRoot: config.projectRoot,
1635
+ sessionId: config.sessionId,
1636
+ allowedTools: config.allowedTools,
1637
+ disallowedTools: config.disallowedTools
1638
+ });
1639
+ const session = {
1640
+ sessionId,
1641
+ threadId: config.threadId,
1642
+ agent,
1643
+ createdAt: Date.now(),
1644
+ lastActive: Date.now(),
1645
+ isLocked: false
1646
+ };
1647
+ this.sessions.set(sessionId, session);
1648
+ return session;
1649
+ }
1650
+ /**
1651
+ * Get a session by ID
1652
+ */
1653
+ getSession(sessionId) {
1654
+ const session = this.sessions.get(sessionId);
1655
+ if (session) {
1656
+ session.lastActive = Date.now();
1657
+ }
1658
+ return session;
1659
+ }
1660
+ /**
1661
+ * Acquire a lock on a session to prevent concurrent edits
1662
+ * Returns true if lock was acquired, false if already locked
1663
+ */
1664
+ acquireLock(sessionId) {
1665
+ const session = this.sessions.get(sessionId);
1666
+ if (!session) {
1667
+ throw new Error(`Session ${sessionId} not found`);
1668
+ }
1669
+ if (session.isLocked) {
1670
+ return false;
1671
+ }
1672
+ session.isLocked = true;
1673
+ session.lastActive = Date.now();
1674
+ return true;
1675
+ }
1676
+ /**
1677
+ * Release a lock on a session
1678
+ */
1679
+ releaseLock(sessionId) {
1680
+ const session = this.sessions.get(sessionId);
1681
+ if (session) {
1682
+ session.isLocked = false;
1683
+ session.lastActive = Date.now();
1684
+ }
1685
+ }
1686
+ /**
1687
+ * Check if any session is currently locked (to prevent concurrent edits)
1688
+ */
1689
+ hasActiveLock() {
1690
+ for (const session of this.sessions.values()) {
1691
+ if (session.isLocked) {
1692
+ return true;
1693
+ }
1694
+ }
1695
+ return false;
1696
+ }
1697
+ /**
1698
+ * Get all active sessions for a thread
1699
+ */
1700
+ getSessionsByThread(threadId) {
1701
+ const sessions = [];
1702
+ for (const session of this.sessions.values()) {
1703
+ if (session.threadId === threadId) {
1704
+ sessions.push(session);
1705
+ }
1706
+ }
1707
+ return sessions;
1708
+ }
1709
+ /**
1710
+ * Delete a session
1711
+ */
1712
+ deleteSession(sessionId) {
1713
+ const session = this.sessions.get(sessionId);
1714
+ if (session) {
1715
+ session.agent.close();
1716
+ this.sessions.delete(sessionId);
1717
+ }
1718
+ }
1719
+ /**
1720
+ * Delete all sessions for a thread
1721
+ */
1722
+ deleteSessionsByThread(threadId) {
1723
+ for (const [sessionId, session] of this.sessions.entries()) {
1724
+ if (session.threadId === threadId) {
1725
+ session.agent.close();
1726
+ this.sessions.delete(sessionId);
1727
+ }
1728
+ }
1729
+ }
1730
+ /**
1731
+ * Get all active sessions
1732
+ */
1733
+ getAllSessions() {
1734
+ return Array.from(this.sessions.values());
1735
+ }
1736
+ /**
1737
+ * Clean up expired sessions
1738
+ */
1739
+ cleanupExpiredSessions() {
1740
+ const now = Date.now();
1741
+ const expiredSessions = [];
1742
+ for (const [sessionId, session] of this.sessions.entries()) {
1743
+ const inactiveTime = now - session.lastActive;
1744
+ if (inactiveTime > this.SESSION_TIMEOUT && !session.isLocked) {
1745
+ expiredSessions.push(sessionId);
1746
+ }
1747
+ }
1748
+ for (const sessionId of expiredSessions) {
1749
+ console.log(
1750
+ `[AgentSessionStore] Cleaning up expired session: ${sessionId}`
1751
+ );
1752
+ this.deleteSession(sessionId);
1753
+ }
1754
+ if (expiredSessions.length > 0) {
1755
+ console.log(
1756
+ `[AgentSessionStore] Cleaned up ${expiredSessions.length} expired sessions`
1757
+ );
1758
+ }
1759
+ }
1760
+ /**
1761
+ * Start periodic cleanup timer
1762
+ */
1763
+ startCleanupTimer() {
1764
+ setInterval(() => {
1765
+ this.cleanupExpiredSessions();
1766
+ }, this.CLEANUP_INTERVAL);
1767
+ }
1768
+ /**
1769
+ * Generate a unique session ID
1770
+ */
1771
+ generateSessionId() {
1772
+ return `session-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
1773
+ }
1774
+ /**
1775
+ * Get statistics about the session store
1776
+ */
1777
+ getStats() {
1778
+ return {
1779
+ totalSessions: this.sessions.size,
1780
+ lockedSessions: Array.from(this.sessions.values()).filter(
1781
+ (s) => s.isLocked
1782
+ ).length,
1783
+ sessions: Array.from(this.sessions.values()).map((s) => ({
1784
+ sessionId: s.sessionId,
1785
+ threadId: s.threadId,
1786
+ isLocked: s.isLocked,
1787
+ ageMinutes: Math.floor((Date.now() - s.createdAt) / 1e3 / 60),
1788
+ inactiveMinutes: Math.floor((Date.now() - s.lastActive) / 1e3 / 60)
1789
+ }))
1790
+ };
1791
+ }
1792
+ }
1793
+ let sessionStore = null;
1794
+ function getSessionStore() {
1795
+ if (!sessionStore) {
1796
+ sessionStore = new AgentSessionStore();
1797
+ }
1798
+ return sessionStore;
1799
+ }
1800
+ async function handleChat(req) {
1801
+ const devModeError = validateDevMode();
1802
+ if (devModeError) return devModeError;
1803
+ try {
1804
+ const body = await req.json();
1805
+ const { sessionId, threadId, message, componentContext } = body;
1806
+ if (!threadId || !message) {
1807
+ return server.NextResponse.json(
1808
+ { error: "threadId and message are required" },
1809
+ { status: 400 }
1810
+ );
1811
+ }
1812
+ const apiKey = process.env.ANTHROPIC_API_KEY;
1813
+ if (!apiKey) {
1814
+ return server.NextResponse.json(
1815
+ { error: "ANTHROPIC_API_KEY not configured" },
1816
+ { status: 500 }
1817
+ );
1818
+ }
1819
+ const sessionStore2 = getSessionStore();
1820
+ const projectRoot = process.cwd();
1821
+ const session = await sessionStore2.createSession({
1822
+ sessionId,
1823
+ threadId,
1824
+ apiKey,
1825
+ projectRoot
1826
+ });
1827
+ if (!sessionStore2.acquireLock(session.sessionId)) {
1828
+ return server.NextResponse.json(
1829
+ {
1830
+ error: "Another operation is in progress. Please wait for it to complete."
1831
+ },
1832
+ { status: 409 }
1833
+ );
1834
+ }
1835
+ const encoder = new TextEncoder();
1836
+ const stream = new ReadableStream({
1837
+ async start(controller) {
1838
+ try {
1839
+ controller.enqueue(
1840
+ encoder.encode(
1841
+ `data: ${JSON.stringify({ type: "connected", sessionId: session.sessionId })}
1842
+
1843
+ `
1844
+ )
1845
+ );
1846
+ for await (const event of session.agent.sendMessage(
1847
+ message,
1848
+ componentContext
1849
+ )) {
1850
+ console.log("[AGENT EVENT]", event.type, JSON.stringify(event, null, 2));
1851
+ controller.enqueue(
1852
+ encoder.encode(`data: ${JSON.stringify(event)}
1853
+
1854
+ `)
1855
+ );
1856
+ }
1857
+ controller.enqueue(
1858
+ encoder.encode(`data: ${JSON.stringify({ type: "done" })}
1859
+
1860
+ `)
1861
+ );
1862
+ controller.close();
1863
+ } catch (error) {
1864
+ console.error("[handleChat] Error:", error);
1865
+ controller.enqueue(
1866
+ encoder.encode(
1867
+ `data: ${JSON.stringify({ type: "error", error: error.message || String(error) })}
1868
+
1869
+ `
1870
+ )
1871
+ );
1872
+ controller.close();
1873
+ } finally {
1874
+ sessionStore2.releaseLock(session.sessionId);
1875
+ }
1876
+ }
1877
+ });
1878
+ return new Response(stream, {
1879
+ headers: {
1880
+ "Content-Type": "text/event-stream",
1881
+ "Cache-Control": "no-cache",
1882
+ Connection: "keep-alive"
1883
+ }
1884
+ });
1885
+ } catch (error) {
1886
+ console.error("[handleChat] Request error:", error);
1887
+ return server.NextResponse.json(
1888
+ { error: String(error) },
1889
+ { status: 500 }
1890
+ );
1891
+ }
1892
+ }
1536
1893
  async function handleAIEditorRequest(req, context) {
1537
1894
  const { path: path2 } = await context.params;
1538
1895
  const endpoint = path2[0];
@@ -1559,12 +1916,79 @@ async function handleAIEditorRequest(req, context) {
1559
1916
  case "suggestions":
1560
1917
  if (method === "GET") return handleSuggestions(req);
1561
1918
  break;
1919
+ case "chat":
1920
+ if (method === "POST") return handleChat(req);
1921
+ break;
1562
1922
  }
1563
1923
  return server.NextResponse.json(
1564
1924
  { error: `Unknown endpoint: ${endpoint}` },
1565
1925
  { status: 404 }
1566
1926
  );
1567
1927
  }
1928
+ const STORAGE_DIR = ".ai-editor";
1929
+ const STORAGE_FILE = "comments.json";
1930
+ function getProjectRoot() {
1931
+ return process.cwd();
1932
+ }
1933
+ function getStoragePath() {
1934
+ return path.join(getProjectRoot(), STORAGE_DIR, STORAGE_FILE);
1935
+ }
1936
+ async function ensureStorageDir() {
1937
+ const dir = path.join(getProjectRoot(), STORAGE_DIR);
1938
+ if (!fs$1.existsSync(dir)) {
1939
+ await fs.mkdir(dir, { recursive: true });
1940
+ }
1941
+ }
1942
+ async function handleCommentsRequest(request) {
1943
+ if (process.env.NODE_ENV !== "development") {
1944
+ return server.NextResponse.json(
1945
+ { error: "Comments API only available in development" },
1946
+ { status: 403 }
1947
+ );
1948
+ }
1949
+ try {
1950
+ if (request.method === "GET") {
1951
+ const storagePath = getStoragePath();
1952
+ if (!fs$1.existsSync(storagePath)) {
1953
+ return server.NextResponse.json({ comments: [] });
1954
+ }
1955
+ const data = await fs.readFile(storagePath, "utf-8");
1956
+ const comments = JSON.parse(data);
1957
+ return server.NextResponse.json({ comments });
1958
+ }
1959
+ if (request.method === "POST") {
1960
+ const body = await request.json();
1961
+ const { action } = body;
1962
+ if (action === "save") {
1963
+ const { comments } = body;
1964
+ await ensureStorageDir();
1965
+ const storagePath = getStoragePath();
1966
+ await fs.writeFile(storagePath, JSON.stringify(comments, null, 2), "utf-8");
1967
+ return server.NextResponse.json({ success: true });
1968
+ }
1969
+ if (action === "clear") {
1970
+ await ensureStorageDir();
1971
+ const storagePath = getStoragePath();
1972
+ await fs.writeFile(storagePath, JSON.stringify([]), "utf-8");
1973
+ return server.NextResponse.json({ success: true });
1974
+ }
1975
+ return server.NextResponse.json(
1976
+ { error: "Invalid action" },
1977
+ { status: 400 }
1978
+ );
1979
+ }
1980
+ return server.NextResponse.json(
1981
+ { error: "Method not allowed" },
1982
+ { status: 405 }
1983
+ );
1984
+ } catch (error) {
1985
+ console.error("Comment storage error:", error);
1986
+ return server.NextResponse.json(
1987
+ { error: "Internal server error" },
1988
+ { status: 500 }
1989
+ );
1990
+ }
1991
+ }
1568
1992
  exports.extractComponentName = extractComponentName;
1569
1993
  exports.extractComponentNameFromStack = extractComponentNameFromStack;
1570
1994
  exports.fileExists = fileExists$1;
@@ -1574,6 +1998,7 @@ exports.getJSXMemberName = getJSXMemberName;
1574
1998
  exports.getOriginalPositionFromDebugStack = getOriginalPositionFromDebugStack;
1575
1999
  exports.handleAIEditorRequest = handleAIEditorRequest;
1576
2000
  exports.handleAbsolutePath = handleAbsolutePath;
2001
+ exports.handleCommentsRequest = handleCommentsRequest;
1577
2002
  exports.handleEdit = handleEdit;
1578
2003
  exports.handleRead = handleRead;
1579
2004
  exports.handleResolve = handleResolve;
@@ -1590,4 +2015,4 @@ exports.resolveOriginalPosition = resolveOriginalPosition;
1590
2015
  exports.scoreElementMatch = scoreElementMatch;
1591
2016
  exports.validateDevMode = validateDevMode;
1592
2017
  exports.validateGeneratedCode = validateGeneratedCode;
1593
- //# sourceMappingURL=index-CNJqd4EQ.cjs.map
2018
+ //# sourceMappingURL=comments-Daur80r4.cjs.map