@tdsoft-tech/aikit 0.1.16 → 0.1.18

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/dist/cli.js CHANGED
@@ -122,6 +122,34 @@ var init_paths = __esm({
122
122
  */
123
123
  memory(configPath) {
124
124
  return join(configPath, "memory");
125
+ },
126
+ /**
127
+ * Get the Claude Code CLI configuration directory
128
+ */
129
+ claudeConfig(scope) {
130
+ if (scope === "project") {
131
+ return join(process.cwd(), ".claude");
132
+ }
133
+ const base = process.platform === "win32" ? process.env.APPDATA || join(homedir(), "AppData", "Roaming") : join(homedir(), ".claude");
134
+ return base;
135
+ },
136
+ /**
137
+ * Get Claude Code CLI commands directory
138
+ */
139
+ claudeCommands(project) {
140
+ return join(this.claudeConfig(project ? "project" : "user"), "commands");
141
+ },
142
+ /**
143
+ * Get Claude Code CLI skills directory
144
+ */
145
+ claudeSkills(project) {
146
+ return join(this.claudeConfig(project ? "project" : "user"), "skills");
147
+ },
148
+ /**
149
+ * Get Claude Code CLI agents directory
150
+ */
151
+ claudeAgents(project) {
152
+ return join(this.claudeConfig(project ? "project" : "user"), "agents");
125
153
  }
126
154
  };
127
155
  }
@@ -375,8 +403,8 @@ var init_memory = __esm({
375
403
  for (const subDir of subDirs) {
376
404
  const dirPath = join6(memoryPath, subDir);
377
405
  try {
378
- const { readdir: readdir10 } = await import("fs/promises");
379
- const files = await readdir10(dirPath);
406
+ const { readdir: readdir11 } = await import("fs/promises");
407
+ const files = await readdir11(dirPath);
380
408
  for (const file of files) {
381
409
  if (!file.endsWith(".md")) continue;
382
410
  const content = await readFile4(join6(dirPath, file), "utf-8");
@@ -1113,6 +1141,7 @@ __export(beads_exports, {
1113
1141
  BeadsIntegration: () => BeadsIntegration
1114
1142
  });
1115
1143
  import { readFile as readFile6, writeFile as writeFile5, readdir as readdir4, access as access3, constants as constants3, mkdir as mkdir5 } from "fs/promises";
1144
+ import { existsSync as existsSync4 } from "fs";
1116
1145
  import { join as join9 } from "path";
1117
1146
  import { exec } from "child_process";
1118
1147
  import { promisify } from "util";
@@ -1227,6 +1256,68 @@ bd init
1227
1256
  return false;
1228
1257
  }
1229
1258
  }
1259
+ /**
1260
+ * Setup git hooks
1261
+ */
1262
+ async setupGitHooks() {
1263
+ try {
1264
+ const gitHooksDir = join9(this.projectPath, ".git", "hooks");
1265
+ if (!existsSync4(gitHooksDir)) {
1266
+ logger.info("Not a git repository, skipping git hooks setup");
1267
+ return true;
1268
+ }
1269
+ const preCommitHook = join9(gitHooksDir, "pre-commit");
1270
+ const hookContent = `#!/bin/sh
1271
+ #
1272
+ # bd (beads) pre-commit hook
1273
+ #
1274
+ # This hook ensures that any pending bd issue changes are flushed to
1275
+ # .beads/issues.jsonl before the commit is created, preventing a
1276
+ # race condition where daemon auto-flush fires after the commit.
1277
+ #
1278
+
1279
+ # Check if bd is available
1280
+ if ! command -v bd >/dev/null 2>&1; then
1281
+ echo "Warning: bd command not found, skipping pre-commit flush" >&2
1282
+ exit 0
1283
+ fi
1284
+
1285
+ # Check if we're in a bd workspace
1286
+ BEADS_DIR=""
1287
+ if [ -d ".beads" ]; then
1288
+ BEADS_DIR=".beads"
1289
+ fi
1290
+
1291
+ if [ -z "$BEADS_DIR" ]; then
1292
+ # Not a bd workspace, nothing to do
1293
+ exit 0
1294
+ fi
1295
+
1296
+ # Check if bd is actually initialized (has beads.db or config.yaml)
1297
+ # This prevents errors if .beads/ exists but bd was never initialized
1298
+ if [ ! -f "$BEADS_DIR/beads.db" ] && [ ! -f "$BEADS_DIR/config.yaml" ]; then
1299
+ # .beads/ exists but bd is not initialized, skip bd operations
1300
+ exit 0
1301
+ fi
1302
+
1303
+ # Flush pending changes to JSONL
1304
+ # Use --flush-only to skip git operations (we're already in a git hook)
1305
+ # Suppress output unless there's an error
1306
+ if ! bd sync --flush-only >/dev/null 2>&1; then
1307
+ echo "Error: Failed to flush bd changes to JSONL" >&2
1308
+ echo "Run 'bd sync --flush-only' manually to diagnose" >&2
1309
+ exit 1
1310
+ fi
1311
+
1312
+ exit 0
1313
+ `;
1314
+ await writeFile5(preCommitHook, hookContent, { mode: 493 });
1315
+ return true;
1316
+ } catch (error) {
1317
+ logger.error("Failed to setup git hooks:", error);
1318
+ return false;
1319
+ }
1320
+ }
1230
1321
  /**
1231
1322
  * Get current status
1232
1323
  */
@@ -1500,13 +1591,437 @@ type: ${type}
1500
1591
  }
1501
1592
  });
1502
1593
 
1594
+ // src/core/sessions.ts
1595
+ var sessions_exports = {};
1596
+ __export(sessions_exports, {
1597
+ SessionManager: () => SessionManager,
1598
+ formatSession: () => formatSession
1599
+ });
1600
+ import { readFile as readFile7, writeFile as writeFile8, readdir as readdir7, mkdir as mkdir8 } from "fs/promises";
1601
+ import { join as join13 } from "path";
1602
+ import matter3 from "gray-matter";
1603
+ import { exec as exec2 } from "child_process";
1604
+ import { promisify as promisify2 } from "util";
1605
+ function formatSession(session) {
1606
+ const startDate = new Date(session.startTime);
1607
+ const endDate = session.endTime ? new Date(session.endTime) : null;
1608
+ return `
1609
+ ${session.id}
1610
+ Name: ${session.name}
1611
+ Status: ${session.status}
1612
+ Started: ${startDate.toLocaleString()}
1613
+ ${endDate ? `Ended: ${endDate.toLocaleString()}` : ""}
1614
+ Goals: ${session.goals.length}
1615
+ Updates: ${session.updates.length}
1616
+ `;
1617
+ }
1618
+ var execAsync2, SessionManager;
1619
+ var init_sessions = __esm({
1620
+ "src/core/sessions.ts"() {
1621
+ "use strict";
1622
+ init_esm_shims();
1623
+ execAsync2 = promisify2(exec2);
1624
+ SessionManager = class {
1625
+ sessionsDir;
1626
+ projectPath;
1627
+ constructor(projectPath) {
1628
+ this.projectPath = projectPath || process.cwd();
1629
+ this.sessionsDir = join13(this.projectPath, ".aikit", "sessions");
1630
+ }
1631
+ /**
1632
+ * Initialize sessions directory
1633
+ */
1634
+ async init() {
1635
+ await mkdir8(this.sessionsDir, { recursive: true });
1636
+ }
1637
+ /**
1638
+ * Start a new session
1639
+ */
1640
+ async startSession(name, goals) {
1641
+ await this.init();
1642
+ const now = /* @__PURE__ */ new Date();
1643
+ const date = now.toISOString().split("T")[0].replace(/-/g, "");
1644
+ const time = now.toTimeString().split(" ")[0].replace(/:/g, "");
1645
+ const nameSuffix = name ? `-${name.replace(/\s+/g, "-").toLowerCase()}` : "";
1646
+ const id = `${date}-${time}${nameSuffix}`;
1647
+ const session = {
1648
+ id,
1649
+ name: name || "Untitled Session",
1650
+ startTime: now.toISOString(),
1651
+ goals: goals || [],
1652
+ updates: [],
1653
+ status: "active"
1654
+ };
1655
+ const gitState = await this.getGitState();
1656
+ session.updates.push({
1657
+ timestamp: now.toISOString(),
1658
+ notes: name ? `Started session: ${name}` : "Started new session",
1659
+ gitBranch: gitState.branch,
1660
+ gitCommits: 0
1661
+ });
1662
+ await this.saveSession(session);
1663
+ await this.setActiveSession(id);
1664
+ return session;
1665
+ }
1666
+ /**
1667
+ * Update current session with notes
1668
+ */
1669
+ async updateSession(notes) {
1670
+ const sessionId = await this.getActiveSessionId();
1671
+ if (!sessionId) {
1672
+ throw new Error("No active session. Use startSession() first.");
1673
+ }
1674
+ const session = await this.getSession(sessionId);
1675
+ if (!session) {
1676
+ throw new Error(`Session not found: ${sessionId}`);
1677
+ }
1678
+ const gitState = await this.getGitState();
1679
+ const modifiedFiles = await this.getModifiedFiles();
1680
+ const update = {
1681
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1682
+ notes: notes || "Session updated",
1683
+ gitBranch: gitState.branch,
1684
+ gitCommits: gitState.commits || 0,
1685
+ modifiedFiles
1686
+ };
1687
+ const beadsTask = await this.getCurrentBeadsTask();
1688
+ if (beadsTask) {
1689
+ update.beadsTask = beadsTask;
1690
+ }
1691
+ session.updates.push(update);
1692
+ await this.saveSession(session);
1693
+ return session;
1694
+ }
1695
+ /**
1696
+ * End current session and generate summary
1697
+ */
1698
+ async endSession() {
1699
+ const sessionId = await this.getActiveSessionId();
1700
+ if (!sessionId) {
1701
+ throw new Error("No active session. Use startSession() first.");
1702
+ }
1703
+ const session = await this.getSession(sessionId);
1704
+ if (!session) {
1705
+ throw new Error(`Session not found: ${sessionId}`);
1706
+ }
1707
+ session.endTime = (/* @__PURE__ */ new Date()).toISOString();
1708
+ session.status = "ended";
1709
+ const gitState = await this.getGitState();
1710
+ const modifiedFiles = await this.getModifiedFiles();
1711
+ session.updates.push({
1712
+ timestamp: session.endTime,
1713
+ notes: "Session ended",
1714
+ gitBranch: gitState.branch,
1715
+ gitCommits: gitState.commits || 0,
1716
+ modifiedFiles
1717
+ });
1718
+ await this.saveSession(session);
1719
+ await this.clearActiveSession();
1720
+ return session;
1721
+ }
1722
+ /**
1723
+ * Get current active session
1724
+ */
1725
+ async getCurrentSession() {
1726
+ const sessionId = await this.getActiveSessionId();
1727
+ if (!sessionId) return null;
1728
+ return this.getSession(sessionId);
1729
+ }
1730
+ /**
1731
+ * Get all sessions
1732
+ */
1733
+ async listSessions() {
1734
+ try {
1735
+ const files = await readdir7(this.sessionsDir);
1736
+ const sessions = [];
1737
+ for (const file of files) {
1738
+ if (file === ".current-session" || !file.endsWith(".md")) continue;
1739
+ const sessionId = file.replace(".md", "");
1740
+ const session = await this.getSession(sessionId);
1741
+ if (session) {
1742
+ sessions.push(session);
1743
+ }
1744
+ }
1745
+ return sessions.sort(
1746
+ (a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime()
1747
+ );
1748
+ } catch {
1749
+ return [];
1750
+ }
1751
+ }
1752
+ /**
1753
+ * Get specific session
1754
+ */
1755
+ async getSession(id) {
1756
+ const filePath = join13(this.sessionsDir, `${id}.md`);
1757
+ try {
1758
+ const content = await readFile7(filePath, "utf-8");
1759
+ const { data, content: body } = matter3(content);
1760
+ const updates = this.parseUpdates(body);
1761
+ return {
1762
+ id: data.id || id,
1763
+ name: data.name || "Untitled",
1764
+ startTime: data.startTime,
1765
+ endTime: data.endTime,
1766
+ goals: data.goals || [],
1767
+ updates,
1768
+ status: data.status || "active"
1769
+ };
1770
+ } catch {
1771
+ return null;
1772
+ }
1773
+ }
1774
+ /**
1775
+ * Search sessions by keyword
1776
+ */
1777
+ async searchSessions(query) {
1778
+ const sessions = await this.listSessions();
1779
+ const lowerQuery = query.toLowerCase();
1780
+ return sessions.filter(
1781
+ (session) => session.name.toLowerCase().includes(lowerQuery) || session.id.toLowerCase().includes(lowerQuery) || session.goals.some((g) => g.toLowerCase().includes(lowerQuery)) || session.updates.some((u) => u.notes?.toLowerCase().includes(lowerQuery))
1782
+ );
1783
+ }
1784
+ /**
1785
+ * Get active session ID
1786
+ */
1787
+ async getActiveSessionId() {
1788
+ const trackerPath = join13(this.sessionsDir, ".current-session");
1789
+ try {
1790
+ const content = await readFile7(trackerPath, "utf-8");
1791
+ return content.trim();
1792
+ } catch {
1793
+ return null;
1794
+ }
1795
+ }
1796
+ /**
1797
+ * Set active session
1798
+ */
1799
+ async setActiveSession(id) {
1800
+ const trackerPath = join13(this.sessionsDir, ".current-session");
1801
+ await writeFile8(trackerPath, id);
1802
+ }
1803
+ /**
1804
+ * Clear active session
1805
+ */
1806
+ async clearActiveSession() {
1807
+ const trackerPath = join13(this.sessionsDir, ".current-session");
1808
+ try {
1809
+ await writeFile8(trackerPath, "");
1810
+ } catch {
1811
+ }
1812
+ }
1813
+ /**
1814
+ * Save session to file
1815
+ */
1816
+ async saveSession(session) {
1817
+ const filePath = join13(this.sessionsDir, `${session.id}.md`);
1818
+ const updatesMarkdown = session.updates.map((update) => {
1819
+ const date = new Date(update.timestamp);
1820
+ const dateStr = date.toLocaleString();
1821
+ const sections = [`### ${dateStr}`];
1822
+ if (update.notes) {
1823
+ sections.push(update.notes);
1824
+ }
1825
+ if (update.gitBranch) {
1826
+ sections.push(`**Git Branch:** ${update.gitBranch}`);
1827
+ }
1828
+ if (update.modifiedFiles && update.modifiedFiles.length > 0) {
1829
+ sections.push(
1830
+ `**Modified Files:** ${update.modifiedFiles.length} files`,
1831
+ update.modifiedFiles.map((f) => ` - ${f}`).join("\n")
1832
+ );
1833
+ }
1834
+ if (update.beadsTask) {
1835
+ sections.push(`**Beads Task:** ${update.beadsTask.id} (${update.beadsTask.status})`);
1836
+ }
1837
+ return sections.join("\n");
1838
+ }).join("\n\n");
1839
+ const frontmatter = {
1840
+ id: session.id,
1841
+ name: session.name,
1842
+ startTime: session.startTime,
1843
+ status: session.status,
1844
+ goals: session.goals
1845
+ };
1846
+ if (session.endTime) {
1847
+ frontmatter.endTime = session.endTime;
1848
+ }
1849
+ const content = `# Development Session - ${session.name}
1850
+
1851
+ **Started:** ${new Date(session.startTime).toLocaleString()}
1852
+ **Status:** ${session.status}
1853
+ ${session.endTime ? `**Ended:** ${new Date(session.endTime).toLocaleString()}` : ""}
1854
+
1855
+ ## Goals
1856
+ ${session.goals.map((g, i) => `- [ ] ${g}`).join("\n")}
1857
+
1858
+ ## Progress
1859
+
1860
+ ${updatesMarkdown}
1861
+
1862
+ ## Summary
1863
+ ${this.generateSummary(session)}
1864
+ `;
1865
+ const fileContent = matter3.stringify(content, frontmatter);
1866
+ await writeFile8(filePath, fileContent);
1867
+ }
1868
+ /**
1869
+ * Generate session summary
1870
+ */
1871
+ generateSummary(session) {
1872
+ if (session.status === "active") {
1873
+ return "*Session in progress...*";
1874
+ }
1875
+ const duration = session.endTime ? new Date(session.endTime).getTime() - new Date(session.startTime).getTime() : 0;
1876
+ const durationMinutes = Math.floor(duration / 6e4);
1877
+ const hours = Math.floor(durationMinutes / 60);
1878
+ const minutes = durationMinutes % 60;
1879
+ let summary = `**Duration:** ${hours}h ${minutes}m
1880
+
1881
+ `;
1882
+ const totalGoals = session.goals.length;
1883
+ summary += `**Goals:** ${totalGoals} defined
1884
+
1885
+ `;
1886
+ summary += `**Updates:** ${session.updates.length} progress notes
1887
+
1888
+ `;
1889
+ const lastUpdate = session.updates[session.updates.length - 1];
1890
+ if (lastUpdate?.gitCommits !== void 0) {
1891
+ summary += `**Git Commits:** ${lastUpdate.gitCommits} commits
1892
+
1893
+ `;
1894
+ }
1895
+ if (lastUpdate?.modifiedFiles && lastUpdate.modifiedFiles.length > 0) {
1896
+ summary += `**Files Modified:** ${lastUpdate.modifiedFiles.length} files
1897
+
1898
+ `;
1899
+ }
1900
+ summary += `**Total Updates:** ${session.updates.length}`;
1901
+ return summary;
1902
+ }
1903
+ /**
1904
+ * Parse updates from markdown body
1905
+ */
1906
+ parseUpdates(body) {
1907
+ const updates = [];
1908
+ const lines = body.split("\n");
1909
+ let currentUpdate = null;
1910
+ let notesLines = [];
1911
+ for (const line of lines) {
1912
+ const updateMatch = line.match(/^### (\d{4}-\d{2}-\d{2} \d{2}:\d{2})/);
1913
+ if (updateMatch) {
1914
+ if (currentUpdate) {
1915
+ currentUpdate.notes = notesLines.join("\n").trim();
1916
+ updates.push(currentUpdate);
1917
+ }
1918
+ currentUpdate = {
1919
+ timestamp: updateMatch[1]
1920
+ };
1921
+ notesLines = [];
1922
+ continue;
1923
+ }
1924
+ if (!currentUpdate) continue;
1925
+ const gitBranchMatch = line.match(/\*\*Git Branch:\*\* (.+)/);
1926
+ if (gitBranchMatch) {
1927
+ currentUpdate.gitBranch = gitBranchMatch[1];
1928
+ continue;
1929
+ }
1930
+ const commitsMatch = line.match(/\*\*Git Commits:\*\* (\d+)/);
1931
+ if (commitsMatch) {
1932
+ currentUpdate.gitCommits = parseInt(commitsMatch[1], 10);
1933
+ continue;
1934
+ }
1935
+ if (line.includes("**Modified Files:**")) {
1936
+ continue;
1937
+ }
1938
+ if (line.trim().startsWith("- ") && currentUpdate) {
1939
+ if (!currentUpdate.modifiedFiles) {
1940
+ currentUpdate.modifiedFiles = [];
1941
+ }
1942
+ currentUpdate.modifiedFiles.push(line.trim().substring(2));
1943
+ continue;
1944
+ }
1945
+ const beadsMatch = line.match(/\*\*Beads Task:\*\* ([\w-]+) \((\w+)\)/);
1946
+ if (beadsMatch) {
1947
+ currentUpdate.beadsTask = {
1948
+ id: beadsMatch[1],
1949
+ status: beadsMatch[2]
1950
+ };
1951
+ continue;
1952
+ }
1953
+ if (!line.startsWith("**") && !line.startsWith("#")) {
1954
+ notesLines.push(line);
1955
+ }
1956
+ }
1957
+ if (currentUpdate) {
1958
+ currentUpdate.notes = notesLines.join("\n").trim();
1959
+ updates.push(currentUpdate);
1960
+ }
1961
+ return updates;
1962
+ }
1963
+ /**
1964
+ * Get git state
1965
+ */
1966
+ async getGitState() {
1967
+ try {
1968
+ const { stdout: branch } = await execAsync2("git rev-parse --abbrev-ref HEAD");
1969
+ const { stdout: log } = await execAsync2("git log --oneline | wc -l");
1970
+ return {
1971
+ branch: branch.trim(),
1972
+ commits: parseInt(log.trim(), 10)
1973
+ };
1974
+ } catch {
1975
+ return { branch: "main" };
1976
+ }
1977
+ }
1978
+ /**
1979
+ * Get modified files
1980
+ */
1981
+ async getModifiedFiles() {
1982
+ try {
1983
+ const { stdout } = await execAsync2("git status --porcelain");
1984
+ const lines = stdout.trim().split("\n");
1985
+ return lines.filter((line) => line.trim()).map((line) => line.substring(3));
1986
+ } catch {
1987
+ return [];
1988
+ }
1989
+ }
1990
+ /**
1991
+ * Get current Beads task
1992
+ */
1993
+ async getCurrentBeadsTask() {
1994
+ const beadsDir = join13(this.projectPath, ".beads");
1995
+ try {
1996
+ const files = await readdir7(beadsDir);
1997
+ const beadFiles = files.filter((f) => f.startsWith("bead-") && f.endsWith(".md"));
1998
+ for (const file of beadFiles) {
1999
+ const content = await readFile7(join13(beadsDir, file), "utf-8");
2000
+ const statusMatch = content.match(/^status:\s*(\w+)/m);
2001
+ const id = file.replace(".md", "");
2002
+ if (statusMatch && (statusMatch[1] === "in-progress" || statusMatch[1] === "todo")) {
2003
+ return {
2004
+ id,
2005
+ status: statusMatch[1]
2006
+ };
2007
+ }
2008
+ }
2009
+ return null;
2010
+ } catch {
2011
+ return null;
2012
+ }
2013
+ }
2014
+ };
2015
+ }
2016
+ });
2017
+
1503
2018
  // src/core/tool-config.ts
1504
2019
  var tool_config_exports = {};
1505
2020
  __export(tool_config_exports, {
1506
2021
  ToolConfigManager: () => ToolConfigManager
1507
2022
  });
1508
- import { readFile as readFile12, writeFile as writeFile13, mkdir as mkdir11, access as access5, constants as constants4 } from "fs/promises";
1509
- import { join as join18 } from "path";
2023
+ import { readFile as readFile14, writeFile as writeFile16, mkdir as mkdir14, access as access7, constants as constants4 } from "fs/promises";
2024
+ import { join as join21 } from "path";
1510
2025
  import { z as z3 } from "zod";
1511
2026
  var ToolConfigSchema, REGISTERED_TOOLS, ToolConfigManager;
1512
2027
  var init_tool_config = __esm({
@@ -1534,7 +2049,7 @@ var init_tool_config = __esm({
1534
2049
  toolsConfigPath;
1535
2050
  constructor(config) {
1536
2051
  this.config = config;
1537
- this.toolsConfigPath = join18(this.config.configPath, "config", "tools.json");
2052
+ this.toolsConfigPath = join21(this.config.configPath, "config", "tools.json");
1538
2053
  }
1539
2054
  /**
1540
2055
  * Get all registered tools with their current status
@@ -1617,8 +2132,8 @@ var init_tool_config = __esm({
1617
2132
  */
1618
2133
  async loadConfigs() {
1619
2134
  try {
1620
- await access5(this.toolsConfigPath, constants4.R_OK);
1621
- const content = await readFile12(this.toolsConfigPath, "utf-8");
2135
+ await access7(this.toolsConfigPath, constants4.R_OK);
2136
+ const content = await readFile14(this.toolsConfigPath, "utf-8");
1622
2137
  return JSON.parse(content);
1623
2138
  } catch {
1624
2139
  return {};
@@ -1628,9 +2143,9 @@ var init_tool_config = __esm({
1628
2143
  * Save configurations
1629
2144
  */
1630
2145
  async saveConfigs(configs) {
1631
- const configDir = join18(this.config.configPath, "config");
1632
- await mkdir11(configDir, { recursive: true });
1633
- await writeFile13(this.toolsConfigPath, JSON.stringify(configs, null, 2));
2146
+ const configDir = join21(this.config.configPath, "config");
2147
+ await mkdir14(configDir, { recursive: true });
2148
+ await writeFile16(this.toolsConfigPath, JSON.stringify(configs, null, 2));
1634
2149
  }
1635
2150
  };
1636
2151
  }
@@ -1677,8 +2192,8 @@ var init_figma_oauth = __esm({
1677
2192
  console.log('3. Give it a name (e.g., "AIKit")');
1678
2193
  console.log("4. Copy the token (you won't see it again!)");
1679
2194
  console.log("5. Paste it here when prompted\n");
1680
- const { default: inquirer3 } = await import("inquirer");
1681
- const { token } = await inquirer3.prompt([
2195
+ const { default: inquirer4 } = await import("inquirer");
2196
+ const { token } = await inquirer4.prompt([
1682
2197
  {
1683
2198
  type: "password",
1684
2199
  name: "token",
@@ -1708,14 +2223,14 @@ var init_figma_oauth = __esm({
1708
2223
  * Alternative: Manual token input
1709
2224
  */
1710
2225
  async authenticateManual() {
1711
- const { default: inquirer3 } = await import("inquirer");
2226
+ const { default: inquirer4 } = await import("inquirer");
1712
2227
  console.log("\n\u{1F510} Figma Authentication (Manual)\n");
1713
2228
  console.log("To get your Figma Personal Access Token:");
1714
2229
  console.log("1. Visit: https://www.figma.com/developers/api#access-tokens");
1715
2230
  console.log('2. Scroll to "Personal access tokens"');
1716
2231
  console.log('3. Click "Create new token"');
1717
2232
  console.log("4. Copy the token and paste it below\n");
1718
- const { token } = await inquirer3.prompt([
2233
+ const { token } = await inquirer4.prompt([
1719
2234
  {
1720
2235
  type: "password",
1721
2236
  name: "token",
@@ -1770,7 +2285,7 @@ init_esm_shims();
1770
2285
 
1771
2286
  // src/cli/index.ts
1772
2287
  init_esm_shims();
1773
- import { Command } from "commander";
2288
+ import { Command as Command3 } from "commander";
1774
2289
 
1775
2290
  // src/index.ts
1776
2291
  init_esm_shims();
@@ -2345,12 +2860,20 @@ ${agent.delegatesTo.map((a) => `- @${a}`).join("\n")}` : ""}
2345
2860
 
2346
2861
  // src/core/commands.ts
2347
2862
  init_esm_shims();
2863
+
2864
+ // src/core/commands/index.ts
2865
+ init_esm_shims();
2348
2866
  init_paths();
2349
2867
  import { readFile as readFile3, readdir as readdir2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
2350
2868
  import { join as join5, basename as basename2, extname as extname2 } from "path";
2351
2869
  import matter2 from "gray-matter";
2352
- var DEFAULT_COMMANDS = [
2353
- // Core Workflow Commands (Beads integration)
2870
+
2871
+ // src/core/commands/types.ts
2872
+ init_esm_shims();
2873
+
2874
+ // src/core/commands/core.ts
2875
+ init_esm_shims();
2876
+ var CORE_COMMANDS = [
2354
2877
  {
2355
2878
  name: "create",
2356
2879
  description: "Create a new Beads task for tracking",
@@ -2602,8 +3125,12 @@ Task description: $ARGUMENTS
2602
3125
  \u2713 Use for straightforward tasks first
2603
3126
  \u2713 Consider /plan + /implement for complex features
2604
3127
  \u2713 Review changes before final approval`
2605
- },
2606
- // Quick Actions
3128
+ }
3129
+ ];
3130
+
3131
+ // src/core/commands/quick.ts
3132
+ init_esm_shims();
3133
+ var QUICK_COMMANDS = [
2607
3134
  {
2608
3135
  name: "fix",
2609
3136
  description: "Quick fix for an issue",
@@ -2698,7 +3225,47 @@ Optional PR title: $ARGUMENTS
2698
3225
  - Screenshots if UI changes
2699
3226
  3. Create PR via GitHub CLI or provide URL`
2700
3227
  },
2701
- // Research & Analysis
3228
+ {
3229
+ name: "refactor",
3230
+ description: "Refactor code to improve structure without changing behavior",
3231
+ category: "quick",
3232
+ usage: "/refactor [file or pattern]",
3233
+ examples: ["/refactor src/utils.ts", "/refactor duplicate code"],
3234
+ content: `Refactor code following best practices.
3235
+
3236
+ ## Workflow
3237
+
3238
+ Optional file or pattern: $ARGUMENTS
3239
+
3240
+ 1. Ensure tests are in place
3241
+ 2. Identify refactoring opportunities
3242
+ 3. Apply refactoring incrementally
3243
+ 4. Run tests after each change
3244
+ 5. Verify no behavior changes`
3245
+ },
3246
+ {
3247
+ name: "lint",
3248
+ description: "Run linter and fix issues",
3249
+ category: "quick",
3250
+ usage: "/lint [--fix]",
3251
+ examples: ["/lint", "/lint --fix"],
3252
+ content: `Run linter and optionally fix issues.
3253
+
3254
+ ## Workflow
3255
+
3256
+ Optional flags: $ARGUMENTS
3257
+
3258
+ 1. Run linter: \`npm run lint\`
3259
+ 2. Parse errors and warnings
3260
+ 3. If --fix flag, run auto-fix
3261
+ 4. Report remaining issues
3262
+ 5. Suggest manual fixes if needed`
3263
+ }
3264
+ ];
3265
+
3266
+ // src/core/commands/research.ts
3267
+ init_esm_shims();
3268
+ var RESEARCH_COMMANDS = [
2702
3269
  {
2703
3270
  name: "research",
2704
3271
  description: "Deep research on a topic",
@@ -2759,8 +3326,12 @@ Optional path: $ARGUMENTS
2759
3326
  - Test coverage gaps
2760
3327
  3. Prioritize findings
2761
3328
  4. Suggest improvements`
2762
- },
2763
- // Design & Planning
3329
+ }
3330
+ ];
3331
+
3332
+ // src/core/commands/design.ts
3333
+ init_esm_shims();
3334
+ var DESIGN_COMMANDS = [
2764
3335
  {
2765
3336
  name: "design",
2766
3337
  description: "Design a feature or system",
@@ -2802,76 +3373,6 @@ Problem to brainstorm: $ARGUMENTS
2802
3373
  3. Group related ideas
2803
3374
  4. Evaluate feasibility
2804
3375
  5. Select top candidates`
2805
- },
2806
- // Git & Version Control
2807
- {
2808
- name: "branch",
2809
- description: "Create a new feature branch",
2810
- category: "git",
2811
- usage: "/branch <name>",
2812
- examples: ["/branch feat/auth", "/branch fix/navigation-bug"],
2813
- content: `Create and switch to a new branch.
2814
-
2815
- ## Workflow
2816
-
2817
- Branch name: $ARGUMENTS
2818
-
2819
- 1. Ensure clean working directory
2820
- 2. Pull latest main/master
2821
- 3. Create branch with naming convention:
2822
- - feat/* for features
2823
- - fix/* for bug fixes
2824
- - refactor/* for refactoring
2825
- - docs/* for documentation
2826
- 4. Switch to new branch`
2827
- },
2828
- {
2829
- name: "merge",
2830
- description: "Merge current branch to target",
2831
- category: "git",
2832
- usage: "/merge [target]",
2833
- examples: ["/merge", "/merge main"],
2834
- content: `Merge current branch to target.
2835
-
2836
- ## Workflow
2837
-
2838
- Optional target branch: $ARGUMENTS
2839
-
2840
- 1. Run quality gates first
2841
- 2. Commit any pending changes
2842
- 3. Switch to target branch
2843
- 4. Pull latest
2844
- 5. Merge feature branch
2845
- 6. Resolve conflicts if any
2846
- 7. Push`
2847
- },
2848
- // Utilities
2849
- {
2850
- name: "status",
2851
- description: "Show current status overview",
2852
- category: "utility",
2853
- usage: "/status",
2854
- examples: ["/status"],
2855
- content: `Display comprehensive status.
2856
-
2857
- ## Shows
2858
- - Current task (from Beads)
2859
- - Git status
2860
- - Active branch
2861
- - Pending changes
2862
- - Test status
2863
- - Recent activity`
2864
- },
2865
- {
2866
- name: "help",
2867
- description: "Show available commands",
2868
- category: "utility",
2869
- usage: "/help [command]",
2870
- examples: ["/help", "/help plan"],
2871
- content: `Display help information.
2872
-
2873
- If no command specified, list all available commands.
2874
- If command specified, show detailed help for that command.`
2875
3376
  },
2876
3377
  {
2877
3378
  name: "analyze-figma",
@@ -3017,24 +3518,179 @@ Summarize what was extracted:
3017
3518
  - If the tool returns an error about access, verify the file is accessible with your token
3018
3519
 
3019
3520
  The analysis will be saved automatically for later reference.`
3521
+ }
3522
+ ];
3523
+
3524
+ // src/core/commands/git.ts
3525
+ init_esm_shims();
3526
+ var GIT_COMMANDS = [
3527
+ {
3528
+ name: "branch",
3529
+ description: "Create a new feature branch",
3530
+ category: "git",
3531
+ usage: "/branch <name>",
3532
+ examples: ["/branch feat/auth", "/branch fix/navigation-bug"],
3533
+ content: `Create and switch to a new branch.
3534
+
3535
+ ## Workflow
3536
+
3537
+ Branch name: $ARGUMENTS
3538
+
3539
+ 1. Ensure clean working directory
3540
+ 2. Pull latest main/master
3541
+ 3. Create branch with naming convention:
3542
+ - feat/* for features
3543
+ - fix/* for bug fixes
3544
+ - refactor/* for refactoring
3545
+ - docs/* for documentation
3546
+ 4. Switch to new branch`
3020
3547
  },
3021
3548
  {
3022
- name: "refactor",
3023
- description: "Refactor code to improve structure without changing behavior",
3024
- category: "quick",
3025
- usage: "/refactor [file or pattern]",
3026
- examples: ["/refactor src/utils.ts", "/refactor duplicate code"],
3027
- content: `Refactor code following best practices.
3549
+ name: "merge",
3550
+ description: "Merge current branch to target",
3551
+ category: "git",
3552
+ usage: "/merge [target]",
3553
+ examples: ["/merge", "/merge main"],
3554
+ content: `Merge current branch to target.
3028
3555
 
3029
3556
  ## Workflow
3030
3557
 
3031
- Optional file or pattern: $ARGUMENTS
3558
+ Optional target branch: $ARGUMENTS
3032
3559
 
3033
- 1. Ensure tests are in place
3034
- 2. Identify refactoring opportunities
3035
- 3. Apply refactoring incrementally
3036
- 4. Run tests after each change
3037
- 5. Verify no behavior changes`
3560
+ 1. Run quality gates first
3561
+ 2. Commit any pending changes
3562
+ 3. Switch to target branch
3563
+ 4. Pull latest
3564
+ 5. Merge feature branch
3565
+ 6. Resolve conflicts if any
3566
+ 7. Push`
3567
+ },
3568
+ {
3569
+ name: "git:ignore-init",
3570
+ description: "Initialize AI-safe .gitignore patterns",
3571
+ category: "git",
3572
+ usage: "/git:ignore-init",
3573
+ examples: ["/git:ignore-init"],
3574
+ content: `Initialize AI-safe .gitignore patterns to prevent AI from accessing sensitive files.
3575
+
3576
+ ## Workflow
3577
+
3578
+ 1. **Check Existing .gitignore**: See if .gitignore exists
3579
+ 2. **Backup**: Create .gitignore.backup if file exists
3580
+ 3. **Append Patterns**: Add AI-specific ignore patterns
3581
+ 4. **Verify**: Confirm patterns added successfully
3582
+
3583
+ ## Patterns Added
3584
+
3585
+ **AI-Sensitive Files:**
3586
+ \`\`\`
3587
+ # AIKit - AI Agent Protection
3588
+ .env
3589
+ .env.local
3590
+ .env.*.local
3591
+ *.key
3592
+ *.pem
3593
+ secrets/
3594
+ credentials/
3595
+ .aws/
3596
+ .ssh/
3597
+ config/secrets.json
3598
+
3599
+ # AI Working Directories
3600
+ .aikit/memory/
3601
+ .aikit/checkpoints/
3602
+ .aikit/sessions/
3603
+
3604
+ # Claude Code
3605
+ .claude/settings.json
3606
+ .claude/local_history.json
3607
+
3608
+ # OpenCode
3609
+ .opencode/config.json
3610
+ .opencode/state.json
3611
+
3612
+ # Agent-Specific
3613
+ .agentignore
3614
+ .aiignore
3615
+
3616
+ # Logs and Debug
3617
+ *.log
3618
+ logs/
3619
+ debug.log
3620
+ npm-debug.log*
3621
+ yarn-debug.log*
3622
+ yarn-error.log*
3623
+
3624
+ # Temporary Files
3625
+ *.tmp
3626
+ *.temp
3627
+ .cache/
3628
+ temp/
3629
+ tmp/
3630
+ \`\`\`
3631
+
3632
+ **Explanation of Each Pattern:**
3633
+ - **Credentials**: API keys, tokens, certificates
3634
+ - **Config**: Sensitive configuration files
3635
+ - **Memory**: AI conversation history and context
3636
+ - **Sessions**: Active AI session data
3637
+ - **Logs**: Debug and error logs
3638
+ - **Temp**: Temporary processing files
3639
+
3640
+ **Why These Patterns?**
3641
+ - Prevent AI from reading API keys and secrets
3642
+ - Avoid exposing sensitive user data
3643
+ - Reduce AI context noise (logs, cache)
3644
+ - Protect privacy
3645
+ - Comply with security best practices
3646
+
3647
+ ## Verification
3648
+
3649
+ **After Running Command:**
3650
+ \`\`\`
3651
+ \u2713 AI-safe patterns added to .gitignore
3652
+ \u2713 Backup created: .gitignore.backup
3653
+ \u2713 Protected: API keys, secrets, memory, logs
3654
+ \u2713 Safe to commit code
3655
+ \`\`\`
3656
+
3657
+ ## Notes
3658
+ - Patterns appended to existing .gitignore
3659
+ - Doesn't remove existing patterns
3660
+ - Can be manually customized after creation
3661
+ - Recommend reviewing before committing`
3662
+ }
3663
+ ];
3664
+
3665
+ // src/core/commands/utility.ts
3666
+ init_esm_shims();
3667
+ var UTILITY_COMMANDS = [
3668
+ {
3669
+ name: "status",
3670
+ description: "Show current status overview",
3671
+ category: "utility",
3672
+ usage: "/status",
3673
+ examples: ["/status"],
3674
+ content: `Display comprehensive status.
3675
+
3676
+ ## Shows
3677
+ - Current task (from Beads)
3678
+ - Git status
3679
+ - Active branch
3680
+ - Pending changes
3681
+ - Test status
3682
+ - Recent activity`
3683
+ },
3684
+ {
3685
+ name: "help",
3686
+ description: "Show available commands",
3687
+ category: "utility",
3688
+ usage: "/help [command]",
3689
+ examples: ["/help", "/help plan"],
3690
+ content: `Display help information.
3691
+
3692
+ If no command specified, list all available commands.
3693
+ If command specified, show detailed help for that command.`
3038
3694
  },
3039
3695
  {
3040
3696
  name: "test",
@@ -3053,24 +3709,6 @@ Optional pattern: $ARGUMENTS
3053
3709
  3. Show coverage if available
3054
3710
  4. Highlight failures
3055
3711
  5. Suggest fixes for failures`
3056
- },
3057
- {
3058
- name: "lint",
3059
- description: "Run linter and fix issues",
3060
- category: "quick",
3061
- usage: "/lint [--fix]",
3062
- examples: ["/lint", "/lint --fix"],
3063
- content: `Run linter and optionally fix issues.
3064
-
3065
- ## Workflow
3066
-
3067
- Optional flags: $ARGUMENTS
3068
-
3069
- 1. Run linter: \`npm run lint\`
3070
- 2. Parse errors and warnings
3071
- 3. If --fix flag, run auto-fix
3072
- 4. Report remaining issues
3073
- 5. Suggest manual fixes if needed`
3074
3712
  },
3075
3713
  {
3076
3714
  name: "deploy",
@@ -3118,15 +3756,1196 @@ Optional version: $ARGUMENTS
3118
3756
 
3119
3757
  ## Workflow
3120
3758
 
3121
- Optional flags: $ARGUMENTS
3759
+ Optional flags: $ARGUMENTS
3760
+
3761
+ 1. Determine log location
3762
+ 2. Apply filters if specified
3763
+ 3. Display logs
3764
+ 4. If --follow, stream updates
3765
+ 5. Format for readability`
3766
+ },
3767
+ {
3768
+ name: "create-agent",
3769
+ description: "Create a custom AI agent with specific capabilities",
3770
+ category: "utility",
3771
+ usage: "/create-agent <name> [description]",
3772
+ examples: ['/create-agent security "Security audit agent"', "/create-agent writer"],
3773
+ content: `Create a custom AI agent for specialized tasks.
3774
+
3775
+ ## Workflow
3776
+
3777
+ Agent name: $ARGUMENTS
3778
+
3779
+ 1. **Determine Agent Purpose**: Ask user clarifying questions:
3780
+ - What specific tasks will this agent handle?
3781
+ - What expertise should it have?
3782
+ - What tools should it have access to?
3783
+ - Any specific behaviors or constraints?
3784
+
3785
+ 2. **Generate Agent Configuration**:
3786
+ - System prompt with instructions
3787
+ - Recommended tools/skills
3788
+ - Preferred models
3789
+ - Interaction patterns
3790
+
3791
+ 3. **Create Agent File**: Save to .aikit/agents/<name>.md
3792
+ - Frontmatter with metadata
3793
+ - System prompt
3794
+ - Tool recommendations
3795
+ - Usage examples
3796
+
3797
+ 4. **Verify Creation**: Confirm agent created successfully
3798
+
3799
+ ## Agent Template
3800
+
3801
+ **Frontmatter:**
3802
+ \`\`\`yaml
3803
+ ---
3804
+ name: <agent-name>
3805
+ description: <what this agent does>
3806
+ useWhen: <when to use this agent>
3807
+ mode: subagent
3808
+ tools: [list of tools]
3809
+ skills: [list of skills]
3810
+ ---
3811
+ \`\`\`
3812
+
3813
+ **System Prompt:**
3814
+ \`\`\`
3815
+ # Agent Name
3816
+
3817
+ You are a specialized AI agent for <specific domain>.
3818
+
3819
+ ## Your Expertise
3820
+ - Expertise area 1
3821
+ - Expertise area 2
3822
+ - Expertise area 3
3823
+
3824
+ ## Your Role
3825
+ 1. Primary responsibility
3826
+ 2. Secondary tasks
3827
+ 3. Collaboration with other agents
3828
+
3829
+ ## Guidelines
3830
+ - How to approach tasks
3831
+ - What to focus on
3832
+ - What to avoid
3833
+
3834
+ ## Tools You Have Access To
3835
+ - Tool 1: description
3836
+ - Tool 2: description
3837
+
3838
+ ## Output Format
3839
+ - Preferred response format
3840
+ - Code style (if applicable)
3841
+ - Explanation style
3842
+ \`\`\`
3843
+
3844
+ ## Example Agents
3845
+
3846
+ **Security Agent:**
3847
+ - Focuses on code security vulnerabilities
3848
+ - Tools: static analysis, dependency checker
3849
+ - Skills: security-audit, code-review
3850
+
3851
+ **Performance Agent:**
3852
+ - Focuses on optimization
3853
+ - Tools: profiler, benchmark
3854
+ - Skills: performance-optimization
3855
+
3856
+ **Documentation Agent:**
3857
+ - Focuses on writing clear docs
3858
+ - Tools: markdown formatter
3859
+ - Skills: documentation
3860
+
3861
+ ## Notes
3862
+ - Agents are saved as markdown files
3863
+ - Can be customized after creation
3864
+ - Used with /agent or agent delegation
3865
+ - Appear in agent selector in OpenCode`
3866
+ },
3867
+ {
3868
+ name: "list-agents",
3869
+ description: "List all available custom agents",
3870
+ category: "utility",
3871
+ usage: "/list-agents",
3872
+ examples: ["/list-agents"],
3873
+ content: `List all available custom agents with descriptions.
3874
+
3875
+ ## Workflow
3876
+
3877
+ 1. **Scan Agents Directory**: Find all agent files in .aikit/agents/
3878
+ 2. **Parse Metadata**: Extract agent descriptions and use cases
3879
+ 3. **Categorize**: Group by type/purpose
3880
+ 4. **Display**: Show formatted list with details
3881
+
3882
+ ## Output Format
3883
+
3884
+ \`\`\`
3885
+ Available Agents:
3886
+
3887
+ ### Development Agents
3888
+ \u2022 security - Security vulnerability scanner
3889
+ Use when: Reviewing code for security issues
3890
+ Skills: security-audit, code-review
3891
+ Tools: static-analysis, dependency-checker
3892
+
3893
+ \u2022 performance - Performance optimization specialist
3894
+ Use when: Optimizing slow code or databases
3895
+ Skills: performance-optimization, database-design
3896
+ Tools: profiler, query-analyzer
3897
+
3898
+ ### Documentation Agents
3899
+ \u2022 writer - Technical documentation writer
3900
+ Use when: Creating or updating docs
3901
+ Skills: documentation
3902
+ Tools: markdown-formatter
3903
+
3904
+ ### Design Agents
3905
+ \u2022 ui-ux - UI/UX design reviewer
3906
+ Use when: Reviewing interface designs
3907
+ Skills: accessibility, frontend-aesthetics
3908
+
3909
+ Total: 4 agents
3910
+
3911
+ Create a new agent: /create-agent <name>
3912
+ \`\`\`
3913
+
3914
+ ## Agent Information Shown
3915
+ - Agent name
3916
+ - Description
3917
+ - When to use it
3918
+ - Required skills
3919
+ - Available tools
3920
+ - Category/type
3921
+
3922
+ ## Notes
3923
+ - Built-in agents (plan, build, review, etc.) also shown
3924
+ - Custom agents appear after built-in ones
3925
+ - Use /agent <name> to invoke specific agent
3926
+ - Can view full agent file for details`
3927
+ }
3928
+ ];
3929
+
3930
+ // src/core/commands/checkpoint.ts
3931
+ init_esm_shims();
3932
+ var CHECKPOINT_COMMANDS = [
3933
+ {
3934
+ name: "checkpoint:create",
3935
+ description: "Save current state as a checkpoint",
3936
+ category: "checkpoint",
3937
+ usage: "/checkpoint:create [message]",
3938
+ examples: ["/checkpoint:create", '/checkpoint:create "Before refactoring"'],
3939
+ content: `Create a checkpoint of the current working state.
3940
+
3941
+ ## Workflow
3942
+
3943
+ Optional message: $ARGUMENTS
3944
+
3945
+ 1. **Check Git Status**: Verify clean or staged state
3946
+ 2. **Create Checkpoint**:
3947
+ - Generate checkpoint ID (timestamp-based)
3948
+ - Save git diff or uncommitted changes
3949
+ - Save current branch name
3950
+ - Save list of modified files
3951
+ - Save user's message (if provided)
3952
+ 3. **Store Checkpoint**: Save to .aikit/checkpoints/checkpoint-{id}.json
3953
+ 4. **Confirm**: Display checkpoint ID and summary
3954
+
3955
+ ## Checkpoint Contents
3956
+ - Timestamp
3957
+ - Git branch
3958
+ - Git commit hash (if any)
3959
+ - Uncommitted changes (diff)
3960
+ - Modified files list
3961
+ - User message
3962
+ - Beads task state (if active)
3963
+
3964
+ ## Example Output
3965
+ \`\`\`
3966
+ Checkpoint created: checkpoint-20250102-143022
3967
+ Message: Before refactoring
3968
+ Files modified: 5 files
3969
+ \`\`\`
3970
+
3971
+ ## Notes
3972
+ - Checkpoints are stored locally in .aikit/checkpoints/
3973
+ - Git changes are preserved (not committed)
3974
+ - Use for experimentation - safely restore if things go wrong
3975
+ - Checkpoints include full state, not just git`
3976
+ },
3977
+ {
3978
+ name: "checkpoint:restore",
3979
+ description: "Restore to a previous checkpoint",
3980
+ category: "checkpoint",
3981
+ usage: "/checkpoint:restore [checkpoint-id]",
3982
+ examples: ["/checkpoint:restore", "/checkpoint:restore checkpoint-20250102-143022", "/checkpoint:restore latest"],
3983
+ content: `Restore the project state from a checkpoint.
3984
+
3985
+ ## Workflow
3986
+
3987
+ Checkpoint ID: $ARGUMENTS (default: latest)
3988
+
3989
+ 1. **Find Checkpoint**:
3990
+ - If no ID provided, find latest checkpoint
3991
+ - Verify checkpoint file exists
3992
+ 2. **Display Summary**: Show what will be restored
3993
+ 3. **Confirm with User**: Ask for confirmation before proceeding
3994
+ 4. **Restore State**:
3995
+ - Clean working directory (remove uncommitted changes)
3996
+ - Checkout git commit from checkpoint
3997
+ - Apply uncommitted changes from checkpoint
3998
+ - Restore Beads task state if available
3999
+ 5. **Verify**: Confirm restoration successful
4000
+
4001
+ ## Safety Checks
4002
+ - Warn if current changes will be lost
4003
+ - Show diff between current and checkpoint
4004
+ - Require explicit user confirmation
4005
+ - Backup current state before restoring
4006
+
4007
+ ## Example Output
4008
+ \`\`\`
4009
+ Restoring checkpoint: checkpoint-20250102-143022
4010
+ Message: Before refactoring
4011
+ Files modified: 5 files
4012
+
4013
+ \u26A0\uFE0F Warning: Uncommitted changes will be lost
4014
+ Proceed? (yes/no)
4015
+
4016
+ \u2713 Restored successfully
4017
+ \`\`\`
4018
+
4019
+ ## Notes
4020
+ - Current uncommitted changes will be lost
4021
+ - Git history is preserved
4022
+ - Beads tasks will be restored to checkpointed state`
4023
+ },
4024
+ {
4025
+ name: "checkpoint:list",
4026
+ description: "List all available checkpoints",
4027
+ category: "checkpoint",
4028
+ usage: "/checkpoint:list",
4029
+ examples: ["/checkpoint:list"],
4030
+ content: `List all saved checkpoints with details.
4031
+
4032
+ ## Workflow
4033
+
4034
+ 1. **Scan Checkpoints Directory**: Find all checkpoint files in .aikit/checkpoints/
4035
+ 2. **Sort by Date**: Most recent first
4036
+ 3. **Display Summary**: Show table/list of checkpoints
4037
+
4038
+ ## Output Format
4039
+ \`\`\`
4040
+ Available Checkpoints:
4041
+
4042
+ 1. checkpoint-20250102-143022
4043
+ Date: 2025-01-02 14:30:22
4044
+ Message: Before refactoring
4045
+ Branch: feature/user-auth
4046
+ Files: 5 modified
4047
+ Size: 2.3MB
4048
+
4049
+ 2. checkpoint-20250102-120815
4050
+ Date: 2025-01-02 12:08:15
4051
+ Message: Initial implementation
4052
+ Branch: main
4053
+ Files: 12 modified
4054
+ Size: 5.1MB
4055
+
4056
+ Total: 2 checkpoints
4057
+ \`\`\`
4058
+
4059
+ ## Filtering Options (future)
4060
+ - Filter by branch
4061
+ - Filter by date range
4062
+ - Filter by message content
4063
+ - Show only checkpoints with specific files
4064
+
4065
+ ## Notes
4066
+ - Checkpoints are stored in .aikit/checkpoints/
4067
+ - Older checkpoints can be manually deleted
4068
+ - Use /checkpoint:restore <id> to restore
4069
+ - Use /checkpoint:restore latest for most recent`
4070
+ }
4071
+ ];
4072
+
4073
+ // src/core/commands/sessions.ts
4074
+ init_esm_shims();
4075
+ var SESSION_COMMANDS = [
4076
+ {
4077
+ name: "session:start",
4078
+ description: "Start a new development session",
4079
+ category: "session",
4080
+ usage: "/session:start [name]",
4081
+ examples: [
4082
+ "/session:start",
4083
+ "/session:start authentication-refactor",
4084
+ '/session:start "Add user profile feature"'
4085
+ ],
4086
+ content: `Start a new development session to track your work.
4087
+
4088
+ ## Workflow
4089
+
4090
+ Session name: $ARGUMENTS
4091
+
4092
+ 1. **Create Session:**
4093
+ - Generate session ID with timestamp
4094
+ - Create session file in .aikit/sessions/
4095
+ - Track active session in .current-session
4096
+ - Capture initial git state
4097
+
4098
+ 2. **Set Goals:**
4099
+ - Ask user for session goals if not provided
4100
+ - Document what you want to accomplish
4101
+ - Link to Beads task if active
4102
+
4103
+ 3. **Session Started:**
4104
+ - Session ID: YYYY-MM-DD-HHMM[-name]
4105
+ - Status: active
4106
+ - Ready for updates
4107
+
4108
+ ## What Gets Tracked
4109
+ - Session start time
4110
+ - Git branch and commits
4111
+ - Modified files
4112
+ - Progress notes
4113
+ - Linked Beads task
4114
+
4115
+ ## Session File Location
4116
+ .aikit/sessions/YYYY-MM-DD-HHMM[-name].md
4117
+
4118
+ ## Examples
4119
+
4120
+ Start unnamed session:
4121
+ \`\`\`
4122
+ /session:start
4123
+ \`\`\`
4124
+
4125
+ Start with descriptive name:
4126
+ \`\`\`
4127
+ /session:start auth-refactor
4128
+ \`\`\`
4129
+
4130
+ Start with goal:
4131
+ \`\`\`
4132
+ /session:start "Implement OAuth 2.0"
4133
+ Goals:
4134
+ - Add Google OAuth
4135
+ - Add JWT token handling
4136
+ \`\`\`
4137
+
4138
+ ## Notes
4139
+ - Session files are markdown with frontmatter
4140
+ - Sessions persist across AI conversations
4141
+ - Use /session:update to add progress notes
4142
+ - Use /session:end to close and summarize`
4143
+ },
4144
+ {
4145
+ name: "session:update",
4146
+ description: "Add progress notes to current session",
4147
+ category: "session",
4148
+ usage: "/session:update [notes]",
4149
+ examples: [
4150
+ "/session:update",
4151
+ "/session:update Fixed authentication bug",
4152
+ '/session:update "Added JWT middleware"'
4153
+ ],
4154
+ content: `Update the current session with progress notes.
4155
+
4156
+ ## Workflow
4157
+
4158
+ Progress notes: $ARGUMENTS
4159
+
4160
+ 1. **Check Active Session:**
4161
+ - Verify there's an active session
4162
+ - Load session file
4163
+
4164
+ 2. **Capture Current State:**
4165
+ - Get current git branch
4166
+ - Count git commits
4167
+ - List modified files
4168
+ - Check active Beads task
4169
+
4170
+ 3. **Add Update:**
4171
+ - Add timestamped update entry
4172
+ - Include your notes (or auto-generate)
4173
+ - Include git state
4174
+ - Include Beads task if active
4175
+
4176
+ 4. **Save Session:**
4177
+ - Update session file
4178
+ - Confirm update added
4179
+
4180
+ ## What Gets Captured
4181
+ - Timestamp of update
4182
+ - Your progress notes
4183
+ - Current git branch
4184
+ - Number of commits
4185
+ - List of modified files
4186
+ - Active Beads task (if any)
4187
+
4188
+ ## Examples
4189
+
4190
+ Auto-update (no notes):
4191
+ \`\`\`
4192
+ /session:update
4193
+ \`\`\`
4194
+ *Auto-generates summary of recent work*
4195
+
4196
+ With specific notes:
4197
+ \`\`\`
4198
+ /session:update Fixed Next.js params issue
4199
+ \`\`\`
4200
+
4201
+ With detailed notes:
4202
+ \`\`\`
4203
+ /session:update "Implemented OAuth flow with Google provider. Added callback handler and token validation."
4204
+ \`\`\`
4205
+
4206
+ ## Notes
4207
+ - Must have active session first
4208
+ - Updates are timestamped
4209
+ - Git state automatically captured
4210
+ - Beads task automatically linked`
4211
+ },
4212
+ {
4213
+ name: "session:end",
4214
+ description: "End current session with summary",
4215
+ category: "session",
4216
+ usage: "/session:end",
4217
+ examples: ["/session:end"],
4218
+ content: `End the current session and generate a comprehensive summary.
4219
+
4220
+ ## Workflow
4221
+
4222
+ 1. **Check Active Session:**
4223
+ - Verify there's an active session
4224
+ - Load session data
4225
+
4226
+ 2. **Generate Summary:**
4227
+ - Calculate session duration
4228
+ - Review all progress notes
4229
+ - Check goals completion
4230
+ - Count git commits
4231
+ - List modified files
4232
+ - Identify key accomplishments
4233
+
4234
+ 3. **Create Summary Section:**
4235
+ - Duration
4236
+ - Goals status
4237
+ - Total updates
4238
+ - Git summary
4239
+ - Key accomplishments
4240
+ - Problems solved
4241
+ - Lessons learned
4242
+
4243
+ 4. **Close Session:**
4244
+ - Mark session as ended
4245
+ - Set end time
4246
+ - Save session file
4247
+ - Clear .current-session tracker
4248
+
4249
+ ## Summary Includes
4250
+
4251
+ **Session Info:**
4252
+ - Duration (hours and minutes)
4253
+ - Start and end times
4254
+ - Session name
4255
+
4256
+ **Goals:**
4257
+ - List of all goals
4258
+ - Completion status
4259
+
4260
+ **Progress:**
4261
+ - Number of updates
4262
+ - Key accomplishments
4263
+ - Problems and solutions
4264
+
4265
+ **Git Activity:**
4266
+ - Total commits
4267
+ - Files modified
4268
+ - Branch worked on
4269
+
4270
+ **Beads Task:**
4271
+ - Linked task ID and status
4272
+
4273
+ ## Example Output
4274
+ \`\`\`
4275
+ Session ended: 2025-01-02-1430-auth-refactor
4276
+ Duration: 2h 30m
4277
+
4278
+ Goals:
4279
+ - Refactor OAuth flow \u2705
4280
+ - Add JWT support \u2705
4281
+
4282
+ Updates: 5
4283
+ Commits: 3
4284
+ Files Modified: 7
4285
+
4286
+ Summary:
4287
+ Successfully refactored authentication system with OAuth 2.0
4288
+ and JWT token support. Resolved Next.js 15 async issues.
4289
+
4290
+ Lessons:
4291
+ - Next.js 15 requires await for params
4292
+ - JWT middleware order matters
4293
+ \`\`\`
4294
+
4295
+ ## Notes
4296
+ - Session cannot be updated after ending
4297
+ - Summary is automatically generated
4298
+ - Session file persists for future reference
4299
+ - Can start new session after ending`
4300
+ },
4301
+ {
4302
+ name: "session:current",
4303
+ description: "Show current active session",
4304
+ category: "session",
4305
+ usage: "/session:current",
4306
+ examples: ["/session:current"],
4307
+ content: `Display information about the current active session.
4308
+
4309
+ ## Workflow
4310
+
4311
+ 1. **Check Active Session:**
4312
+ - Read .current-session file
4313
+ - Load session data
4314
+
4315
+ 2. **Display Session Info:**
4316
+ - Session name and ID
4317
+ - How long session has been active
4318
+ - Session goals
4319
+ - Recent updates (last 3)
4320
+ - Current git state
4321
+ - Active Beads task
4322
+
4323
+ 3. **Show Actions:**
4324
+ - Available session commands
4325
+ - Quick reminder to update/end
4326
+
4327
+ ## Example Output
4328
+ \`\`\`
4329
+ \u{1F4CD} Current Session
4330
+
4331
+ Session: authentication-refactor
4332
+ ID: 2025-01-02-1430-auth-refactor
4333
+ Started: 2 hours ago
4334
+
4335
+ Goals:
4336
+ - [ ] Refactor OAuth flow
4337
+ - [x] Add JWT support
4338
+
4339
+ Recent Updates:
4340
+ 15:45 - Implemented OAuth 2.0 with Google
4341
+ 16:20 - Added JWT token generation
4342
+
4343
+ Git:
4344
+ - Branch: feature/auth
4345
+ - Commits: 3
4346
+ - Modified: 5 files
4347
+
4348
+ Beads Task:
4349
+ - bead-001 (in-progress)
4350
+
4351
+ Commands:
4352
+ /session:update [notes] - Add progress
4353
+ /session:end - Close session
4354
+ \`\`\`
4355
+
4356
+ ## Notes
4357
+ - Shows error if no active session
4358
+ - Use to quickly check session status
4359
+ - Displays recent progress`
4360
+ },
4361
+ {
4362
+ name: "session:list",
4363
+ description: "List all sessions",
4364
+ category: "session",
4365
+ usage: "/session:list",
4366
+ examples: ["/session:list"],
4367
+ content: `List all development sessions with summaries.
4368
+
4369
+ ## Workflow
4370
+
4371
+ 1. **Scan Sessions Directory:**
4372
+ - Find all .md files in .aikit/sessions/
4373
+ - Exclude .current-session
4374
+
4375
+ 2. **Sort by Date:**
4376
+ - Newest sessions first
4377
+
4378
+ 3. **Display Summary:**
4379
+ - Session ID and name
4380
+ - Start and end times
4381
+ - Status (active/ended)
4382
+ - Number of updates
4383
+ - Session goals
4384
+
4385
+ ## Example Output
4386
+ \`\`\`
4387
+ \u{1F4DA} All Sessions
4388
+
4389
+ 1. 2025-01-02-1430-auth-refactor
4390
+ Status: Active
4391
+ Started: 2 hours ago
4392
+ Updates: 5
4393
+ Goals: Refactor OAuth, Add JWT
4394
+
4395
+ 2. 2025-01-02-1200-bug-fix
4396
+ Status: Ended
4397
+ Started: Today 12:00
4398
+ Ended: Today 13:30 (1h 30m)
4399
+ Updates: 3
4400
+ Goals: Fix email bounce handling
4401
+
4402
+ 3. 2025-01-01-1530-feature-user-profile
4403
+ Status: Ended
4404
+ Started: Yesterday 15:30
4405
+ Ended: Yesterday 18:45 (3h 15m)
4406
+ Updates: 8
4407
+ Goals: Add user profile page
4408
+
4409
+ Total: 3 sessions
4410
+ \`\`\`
4411
+
4412
+ ## Notes
4413
+ - Shows both active and ended sessions
4414
+ - Most recent sessions first
4415
+ - Active session highlighted
4416
+ - Use /session:show <id> for details`
4417
+ },
4418
+ {
4419
+ name: "session:show",
4420
+ description: "Show details of a specific session",
4421
+ category: "session",
4422
+ usage: "/session:show <session-id>",
4423
+ examples: [
4424
+ "/session:show 2025-01-02-1430",
4425
+ "/session:show 2025-01-02-1430-auth-refactor"
4426
+ ],
4427
+ content: `Display full details of a specific session.
4428
+
4429
+ ## Workflow
4430
+
4431
+ Session ID: $ARGUMENTS
4432
+
4433
+ 1. **Load Session:**
4434
+ - Find session file
4435
+ - Parse session data
4436
+
4437
+ 2. **Display Details:**
4438
+ - Session metadata
4439
+ - All progress notes
4440
+ - Git activity timeline
4441
+ - Summary (if ended)
4442
+
4443
+ 3. **Format Output:**
4444
+ - Readable markdown format
4445
+ - Chronological updates
4446
+ - Key accomplishments
4447
+
4448
+ ## Example Output
4449
+ \`\`\`
4450
+ \u{1F4C4} Session: 2025-01-02-1430-auth-refactor
4451
+
4452
+ Status: Ended
4453
+ Started: 2025-01-02 14:30
4454
+ Ended: 2025-01-02 17:00
4455
+ Duration: 2h 30m
4456
+
4457
+ Goals:
4458
+ - [x] Refactor OAuth flow
4459
+ - [x] Add JWT support
4460
+
4461
+ ## Progress
4462
+
4463
+ ### 2025-01-02 14:30
4464
+ Started session: authentication-refactor
4465
+ Git Branch: feature/auth
4466
+
4467
+ ### 2025-01-02 15:45
4468
+ Implemented OAuth 2.0 flow with Google provider
4469
+ Added callback handler and token validation
4470
+ Git Branch: feature/auth
4471
+
4472
+ ### 2025-01-02 16:20
4473
+ Added JWT token generation and validation
4474
+ Created middleware for protected routes
4475
+ Git Branch: feature/auth
4476
+
4477
+ ### 2025-01-02 17:00
4478
+ Session ended
4479
+
4480
+ ## Summary
4481
+ Duration: 2h 30m
4482
+ Goals: 2
4483
+ Updates: 4
4484
+ Git Commits: 3
4485
+ Files Modified: 7
4486
+
4487
+ Successfully refactored authentication system...
4488
+ \`\`\`
4489
+
4490
+ ## Notes
4491
+ - Use session ID or partial ID
4492
+ - Shows all updates chronologically
4493
+ - Full session details displayed`
4494
+ },
4495
+ {
4496
+ name: "session:search",
4497
+ description: "Search sessions by keyword",
4498
+ category: "session",
4499
+ usage: "/session:search <query>",
4500
+ examples: [
4501
+ "/session:search oauth",
4502
+ '/session:search "jwt"',
4503
+ "/session:search authentication"
4504
+ ],
4505
+ content: `Search for sessions matching a keyword.
4506
+
4507
+ ## Workflow
4508
+
4509
+ Search query: $ARGUMENTS
4510
+
4511
+ 1. **Search Sessions:**
4512
+ - Search in session names
4513
+ - Search in session IDs
4514
+ - Search in goals
4515
+ - Search in update notes
4516
+
4517
+ 2. **Display Results:**
4518
+ - Matching sessions
4519
+ - Highlight match context
4520
+ - Sort by date
4521
+
4522
+ 3. **Show Actions:**
4523
+ - List matching sessions
4524
+ - Suggest /session:show for details
4525
+
4526
+ ## Example Output
4527
+ \`\`\`
4528
+ \u{1F50D} Search Results: "oauth"
4529
+
4530
+ Found 2 sessions:
4531
+
4532
+ 1. 2025-01-02-1430-auth-refactor
4533
+ Match: Name contains "oauth" (case-insensitive)
4534
+ Started: 2 hours ago
4535
+ Status: Active
4536
+ Goals: Refactor OAuth flow, Add JWT
4537
+
4538
+ 2. 2025-01-01-1200-oauth-fix
4539
+ Match: Goals contain "OAuth"
4540
+ Started: Yesterday
4541
+ Status: Ended
4542
+ Goals: Fix OAuth callback
4543
+
4544
+ Total: 2 matching sessions
4545
+ \`\`\`
4546
+
4547
+ ## Notes
4548
+ - Case-insensitive search
4549
+ - Searches name, goals, and notes
4550
+ - Shows matching context
4551
+ - Use partial IDs too`
4552
+ },
4553
+ {
4554
+ name: "session:resume",
4555
+ description: "Resume a past session (load context)",
4556
+ category: "session",
4557
+ usage: "/session:resume <session-id>",
4558
+ examples: [
4559
+ "/session:resume latest",
4560
+ "/session:resume 2025-01-02-1430",
4561
+ "/session:resume 2025-01-02-1430-auth-refactor"
4562
+ ],
4563
+ content: `Load context from a past session to resume work.
4564
+
4565
+ ## Workflow
4566
+
4567
+ Session ID: $ARGUMENTS (or "latest")
4568
+
4569
+ 1. **Find Session:**
4570
+ - If "latest", load most recent session
4571
+ - Otherwise, load specified session
4572
+
4573
+ 2. **Load Context:**
4574
+ - Display session summary
4575
+ - Show what was accomplished
4576
+ - Show what's remaining
4577
+ - List key decisions made
4578
+
4579
+ 3. **Suggest Next Actions:**
4580
+ - Based on session goals
4581
+ - Based on remaining tasks
4582
+ - Based on session end notes
4583
+
4584
+ 4. **Display Session:**
4585
+ - Full session details
4586
+ - All progress notes
4587
+ - Git activity
4588
+ - Summary and lessons
4589
+
4590
+ ## Example Output
4591
+ \`\`\`
4592
+ \u{1F504} Resuming Session: 2025-01-02-1430-auth-refactor
4593
+
4594
+ ## What Was Done
4595
+ \u2713 Refactored OAuth flow
4596
+ \u2713 Added JWT middleware
4597
+ \u2713 Fixed Next.js 15 issues
4598
+
4599
+ ## What Remains
4600
+ \u25CB Add refresh token support
4601
+ \u25CB Write tests for auth module
4602
+ \u25CB Update documentation
4603
+
4604
+ ## Key Decisions
4605
+ - Used next-auth for OAuth
4606
+ - JWT middleware order: auth \u2192 validate \u2192 route
4607
+ - Next.js 15 requires async params
4608
+
4609
+ ## Problems Solved
4610
+ - Next.js 15 params Promise issue
4611
+ - Cookie domain configuration
4612
+ - JWT secret from env var
4613
+
4614
+ ## Suggested Next Steps
4615
+ 1. Add refresh token rotation
4616
+ 2. Write unit tests for auth
4617
+ 3. Update API documentation
4618
+ 4. Deploy to staging for testing
4619
+
4620
+ Session context loaded! Ready to continue.
4621
+ \`\`\`
4622
+
4623
+ ## Notes
4624
+ - Use "latest" for most recent session
4625
+ - Loads full context for continuity
4626
+ - Suggests next actions based on goals
4627
+ - Great for picking up after a break`
4628
+ }
4629
+ ];
4630
+
4631
+ // src/core/commands/drawio.ts
4632
+ init_esm_shims();
4633
+ var DRAWIO_COMMANDS = [
4634
+ {
4635
+ name: "drawio-interact",
4636
+ description: "Create/edit diagrams with AI + Draw.io + auto-sync",
4637
+ category: "design",
4638
+ usage: "/drawio-interact <create|modify|list|start-sync|stop-sync> [diagram-name]",
4639
+ examples: [
4640
+ "/drawio-interact create login-flow",
4641
+ "/drawio-interact modify login-flow",
4642
+ "/drawio-interact list",
4643
+ "/drawio-interact start-sync",
4644
+ "/drawio-interact stop-sync"
4645
+ ],
4646
+ content: `Interactive diagram workflow with AI + Draw.io + background sync.
4647
+
4648
+ **User provided**: $ARGUMENTS
4649
+
4650
+ ## File Locations
4651
+
4652
+ **NEW STRUCTURE**:
4653
+ - **Mermaid files**: \`mermaid/[name].mmd\` (project root, version control)
4654
+ - **Draw.io files**: \`.aikit/assets/drawio/[name].drawio\` (visual editing)
4655
+
4656
+ **Background Sync**:
4657
+ - Auto-syncs changes between both formats
4658
+ - Runs as background service
4659
+ - Detects changes in real-time
4660
+
4661
+ ## Workflow
4662
+
4663
+ ### Step 1: Parse User Intent
4664
+
4665
+ Check if user wants to:
4666
+ - **create** - Generate new diagram and open in Draw.io
4667
+ - **modify** - Manual sync (if background sync not running)
4668
+ - **list** - Show all existing diagrams
4669
+ - **start-sync** - Start background sync service
4670
+ - **stop-sync** - Stop background sync service
4671
+
4672
+ ### Step 2: Create Diagram (if "create")
4673
+
4674
+ **Action**: Generate diagram from description, create files, open in Draw.io
4675
+
4676
+ 1. **Extract diagram name** from arguments
4677
+ - "create login-flow" \u2192 name = "login-flow"
4678
+ - If no name provided, ask user
4679
+
4680
+ 2. **Generate Mermaid code** based on description:
4681
+ - Login/auth \u2192 Flowchart with authentication
4682
+ - Order/purchase \u2192 Flowchart with payment
4683
+ - API/request \u2192 Sequence diagram
4684
+ - Generic \u2192 Basic flowchart
4685
+
4686
+ 3. **Create files**:
4687
+ - \`mermaid/[name].mmd\` - Mermaid source
4688
+ - \`.aikit/assets/drawio/[name].drawio\` - Draw.io XML
4689
+
4690
+ 4. **Open in Draw.io with auto-sync**:
4691
+ - Use lifecycle manager for automatic sync start/stop
4692
+ - Sync starts when Draw.io opens
4693
+ - Sync stops when Draw.io closes
4694
+ - No manual intervention needed!
4695
+
4696
+ **Implementation**: Use lifecycle manager
4697
+
4698
+ \`\`\`javascript
4699
+ import { openDrawioWithAutoSync } from '.aikit/tools/drawio-sync/lifecycle-manager.js';
4700
+ import { join } from 'path';
4701
+
4702
+ const projectRoot = process.cwd();
4703
+ const drawioPath = join(projectRoot, '.aikit/assets/drawio', \`\${name}.drawio\`);
4704
+
4705
+ // This will:
4706
+ // 1. Start sync service automatically
4707
+ // 2. Open Draw.io app
4708
+ // 3. Monitor Draw.io process
4709
+ // 4. Stop sync when Draw.io closes
4710
+ await openDrawioWithAutoSync(drawioPath);
4711
+ \`\`\`
4712
+
4713
+ **What happens**:
4714
+ - \u2705 Sync service starts automatically
4715
+ - \u2705 Draw.io opens
4716
+ - \u2705 Files stay in sync while editing
4717
+ - \u2705 Sync stops automatically when Draw.io closes
4718
+ - \u2705 Zero manual intervention!
4719
+
4720
+ 5. **Report success**:
4721
+ - Show generated Mermaid code
4722
+ - Confirm file locations
4723
+ - Confirm Draw.io opened with auto-sync
4724
+ - Inform user: "Sync will stop automatically when you close Draw.io"
4725
+
4726
+ ### Step 3: Modify Diagram (if "modify")
4727
+
4728
+ **Action**: Manual one-time sync (use if background sync not running)
4729
+
4730
+ 1. **Extract diagram name**
4731
+ 2. **Read files**:
4732
+ - \`mermaid/[name].mmd\`
4733
+ - \`.aikit/assets/drawio/[name].drawio\`
4734
+
4735
+ 3. **Convert Draw.io \u2192 Mermaid**
4736
+ 4. **Detect changes**
4737
+ 5. **Update Mermaid file**
4738
+ 6. **Report changes**
4739
+
4740
+ **Note**: If background sync is running, this is automatic!
4741
+
4742
+ ### Step 4: List Diagrams (if "list")
4743
+
4744
+ **Action**: Show all diagrams
4745
+
4746
+ \`\`\`bash
4747
+ ls mermaid/*.mmd 2>/dev/null | sed 's/mermaid\\///' | sed 's/.mmd$//'
4748
+ \`\`\`
4749
+
4750
+ Format as numbered list with file info.
4751
+
4752
+ ### Step 5: Start Sync (if "start-sync")
4753
+
4754
+ **Action**: Start background sync service
4755
+
4756
+ 1. **Check if service already running**
4757
+ 2. **Start Chokidar file watcher**:
4758
+ - Watch \`mermaid/*.mmd\`
4759
+ - Watch \`.aikit/assets/drawio/*.drawio\`
4760
+ 3. **Auto-sync on change**:
4761
+ - Debounce: 500ms
4762
+ - Lock files during sync to prevent loops
4763
+ - Log all changes
4764
+ 4. **Report**: "Background sync running"
4765
+
4766
+ **Implementation**: Use .aikit/tools/drawio-sync/sync-service.js
4767
+
4768
+ \`\`\`javascript
4769
+ import { startSyncService } from '.aikit/tools/drawio-sync/sync-service.js';
4770
+ startSyncService();
4771
+ \`\`\`
4772
+
4773
+ ### Step 6: Stop Sync (if "stop-sync")
4774
+
4775
+ **Action**: Stop background sync service
4776
+
4777
+ \`\`\`javascript
4778
+ import { stopSyncService } from '.aikit/tools/drawio-sync/sync-service.js';
4779
+ stopSyncService();
4780
+ \`\`\`
4781
+
4782
+ ## Conversion Tools
4783
+
4784
+ **Location**: \`.aikit/tools/drawio-sync/\`
4785
+
4786
+ **Modules**:
4787
+ - \`mermaid-to-drawio.js\` - Mermaid \u2192 Draw.io conversion
4788
+ - \`drawio-to-mermaid.js\` - Draw.io \u2192 Mermaid conversion
4789
+ - \`sync-service.js\` - Background sync service
4790
+ - \`lifecycle-manager.js\` - **NEW!** Automatic lifecycle (start/stop sync with Draw.io)
4791
+
4792
+ **Lifecycle Manager Usage** (recommended for "create" command):
4793
+ \`\`\`javascript
4794
+ import { openDrawioWithAutoSync } from '.aikit/tools/drawio-sync/lifecycle-manager.js';
4795
+ import { join } from 'path';
4796
+
4797
+ const drawioPath = join(process.cwd(), '.aikit/assets/drawio', name + '.drawio');
4798
+
4799
+ // Automatically:
4800
+ // - Starts sync when Draw.io opens
4801
+ // - Stops sync when Draw.io closes
4802
+ await openDrawioWithAutoSync(drawioPath);
4803
+ \`\`\`
4804
+
4805
+ **Manual Conversion** (for "modify" command):
4806
+ \`\`\`javascript
4807
+ import { convertMermaidToDrawio } from '.aikit/tools/drawio-sync/mermaid-to-drawio.js';
4808
+ import { convertDrawioToMermaid } from '.aikit/tools/drawio-sync/drawio-to-mermaid.js';
4809
+
4810
+ // Mermaid \u2192 Draw.io
4811
+ const result = convertMermaidToDrawio(mermaidCode, diagramName);
4812
+ fs.writeFileSync('.aikit/assets/drawio/name.drawio', result.xml, 'utf-8');
4813
+
4814
+ // Draw.io \u2192 Mermaid
4815
+ const result = convertDrawioToMermaid(drawioXML);
4816
+ fs.writeFileSync('mermaid/name.mmd', result.code, 'utf-8');
4817
+ \`\`\`
4818
+
4819
+ ## Error Handling
4820
+
4821
+ **If sync fails**:
4822
+ 1. Log error to console
4823
+ 2. Show notification: "Sync failed for [name]"
4824
+ 3. **Ask user**:
4825
+ - Ignore this error?
4826
+ - Retry sync?
4827
+ - Let AI fix it?
4828
+
4829
+ **AI Fix Option**:
4830
+ - Analyze error
4831
+ - Attempt to fix common issues:
4832
+ - Malformed Mermaid syntax
4833
+ - Invalid XML characters
4834
+ - Encoding issues
4835
+ - Missing nodes
4836
+ - Retry sync after fix
4837
+ - Report result
4838
+
4839
+ ## Example Session
4840
+
4841
+ \`\`\`
4842
+ User: /drawio-interact create user-authentication
4843
+
4844
+ AI: Creating diagram: user-authentication
4845
+
4846
+ \u{1F4C4} Generated Mermaid Code:
4847
+ graph TD
4848
+ User[User] -->|Login| Login[Login Page]
4849
+ Login -->|Success| Dashboard[Dashboard]
4850
+
4851
+ \u2705 Files created:
4852
+ - mermaid/user-authentication.mmd
4853
+ - .aikit/assets/drawio/user-authentication.drawio
4854
+
4855
+ \u{1F504} Starting background sync service...
4856
+ \u2705 Sync service running
4857
+
4858
+ \u{1F3A8} Opening Draw.io...
4859
+ \u{1F440} Monitoring Draw.io app (sync will stop when Draw.io closes)
4860
+
4861
+ ---
4862
+
4863
+ [User edits in Draw.io, adds "Password Recovery", saves]
4864
+
4865
+ \u{1F504} Background sync detected changes:
4866
+ \u2022 Added nodes: Recovery, ResetEmail
4867
+ \u2022 Modified connections: Login \u2192 Recovery
4868
+
4869
+ \u2705 Updated: mermaid/user-authentication.mmd
4870
+
4871
+ [Or user edits mermaid/user-authentication.mmd directly]
4872
+
4873
+ \u{1F504} Background sync detected changes:
4874
+ \u2022 Modified Mermaid syntax
4875
+
4876
+ \u2705 Updated: .aikit/assets/drawio/user-authentication.drawio
4877
+
4878
+ ---
4879
+
4880
+ [User closes Draw.io app]
4881
+
4882
+ \u{1F3A8} Draw.io closed - stopping sync service
4883
+ \u2705 Sync service stopped
4884
+ \`\`\`
4885
+
4886
+ **Key Point**: Sync starts automatically when Draw.io opens, and stops automatically when Draw.io closes. No manual commands needed!
4887
+
4888
+ ## Important Notes
4889
+
4890
+ - **Auto-sync lifecycle** - Sync starts when Draw.io opens, stops when it closes
4891
+ - **Zero manual intervention** - No need to run start/stop commands
4892
+ - **Draw.io app** opens automatically on "create"
4893
+ - **Both formats** stay in sync automatically while Draw.io is open
4894
+ - **Mermaid files** are in project root for version control
4895
+ - **Draw.io files** are in .aikit/assets (can be .gitignored)
4896
+ - **Manual commands** (start-sync/stop-sync) only needed for special cases
4897
+
4898
+ ## Auto-Open Commands
4899
+
4900
+ **IMPORTANT**: Always use absolute paths!
4901
+
4902
+ **Example code**:
4903
+ \`\`\`javascript
4904
+ const platform = process.platform;
4905
+ const projectRoot = process.cwd(); // Get absolute project root
4906
+ const filePath = join(projectRoot, '.aikit/assets/drawio', name + '.drawio');
4907
+
4908
+ if (platform === 'darwin') {
4909
+ // macOS: Use double quotes for path with spaces
4910
+ execSync('open -a "Draw.io" "' + filePath + '"');
4911
+ } else if (platform === 'linux') {
4912
+ // Linux
4913
+ execSync('xdg-open "' + filePath + '"');
4914
+ } else if (platform === 'win32') {
4915
+ // Windows
4916
+ execSync('start "" "' + filePath + '"', { shell: true });
4917
+ }
4918
+ \`\`\`
4919
+
4920
+ **Why absolute paths?**
4921
+ - Relative paths depend on current working directory
4922
+ - Claude/AI might execute from wrong directory
4923
+ - Absolute paths always work correctly
4924
+
4925
+ **Fallback**: If Draw.io app not found, open https://app.diagrams.net/
4926
+
4927
+ ## Directory Setup
3122
4928
 
3123
- 1. Determine log location
3124
- 2. Apply filters if specified
3125
- 3. Display logs
3126
- 4. If --follow, stream updates
3127
- 5. Format for readability`
4929
+ Ensure directories exist:
4930
+ \`\`\`bash
4931
+ mkdir -p mermaid
4932
+ mkdir -p .aikit/assets/drawio
4933
+ \`\`\``
3128
4934
  }
3129
4935
  ];
4936
+
4937
+ // src/core/commands/index.ts
4938
+ var DEFAULT_COMMANDS = [
4939
+ ...CORE_COMMANDS,
4940
+ ...QUICK_COMMANDS,
4941
+ ...RESEARCH_COMMANDS,
4942
+ ...DESIGN_COMMANDS,
4943
+ ...GIT_COMMANDS,
4944
+ ...UTILITY_COMMANDS,
4945
+ ...CHECKPOINT_COMMANDS,
4946
+ ...SESSION_COMMANDS,
4947
+ ...DRAWIO_COMMANDS
4948
+ ];
3130
4949
  var CommandRunner = class {
3131
4950
  config;
3132
4951
  commandsCache = /* @__PURE__ */ new Map();
@@ -4591,17 +6410,17 @@ export default ${toPascalCase(name)}Plugin;
4591
6410
  event: async (event) => {
4592
6411
  if (event.type === "session.idle") {
4593
6412
  try {
4594
- const { exec: exec2 } = await import("child_process");
4595
- const { promisify: promisify2 } = await import("util");
4596
- const execAsync2 = promisify2(exec2);
6413
+ const { exec: exec3 } = await import("child_process");
6414
+ const { promisify: promisify3 } = await import("util");
6415
+ const execAsync3 = promisify3(exec3);
4597
6416
  const platform = process.platform;
4598
6417
  const summary = event.properties?.summary || "Session completed";
4599
6418
  if (platform === "darwin") {
4600
- await execAsync2(`osascript -e 'display notification "${summary}" with title "OpenCode Session Complete"'`);
6419
+ await execAsync3(`osascript -e 'display notification "${summary}" with title "OpenCode Session Complete"'`);
4601
6420
  } else if (platform === "linux") {
4602
- await execAsync2(`notify-send "OpenCode Session Complete" "${summary}"`);
6421
+ await execAsync3(`notify-send "OpenCode Session Complete" "${summary}"`);
4603
6422
  } else if (platform === "win32") {
4604
- await execAsync2(`powershell -Command "New-BurntToastNotification -Text 'OpenCode Session Complete', '${summary}'"`);
6423
+ await execAsync3(`powershell -Command "New-BurntToastNotification -Text 'OpenCode Session Complete', '${summary}'"`);
4605
6424
  }
4606
6425
  } catch (error) {
4607
6426
  logger.warn(`[Notification] Failed to send notification: ${error instanceof Error ? error.message : String(error)}`);
@@ -4652,9 +6471,15 @@ init_beads();
4652
6471
  // src/utils/cli-detector.ts
4653
6472
  init_esm_shims();
4654
6473
  import { execSync } from "child_process";
4655
- import { existsSync as existsSync4 } from "fs";
6474
+ import { existsSync as existsSync5 } from "fs";
4656
6475
  import { join as join12 } from "path";
4657
6476
  import { homedir as homedir2 } from "os";
6477
+ var CliPlatform = /* @__PURE__ */ ((CliPlatform2) => {
6478
+ CliPlatform2["OPENCODE"] = "opencode";
6479
+ CliPlatform2["CLAUDE"] = "claude";
6480
+ CliPlatform2["CODEX"] = "codex";
6481
+ return CliPlatform2;
6482
+ })(CliPlatform || {});
4658
6483
  var CliDetector = class {
4659
6484
  /**
4660
6485
  * Check if OpenCode is installed
@@ -4663,14 +6488,14 @@ var CliDetector = class {
4663
6488
  try {
4664
6489
  const opencodePath = process.platform === "win32" ? process.env.APPDATA || join12(homedir2(), "AppData", "Roaming") : join12(homedir2(), ".config");
4665
6490
  const opencodeConfig = join12(opencodePath, "opencode", "opencode.json");
4666
- let installed = existsSync4(opencodeConfig);
6491
+ let installed = existsSync5(opencodeConfig);
4667
6492
  let version;
4668
6493
  if (installed) {
4669
6494
  try {
4670
6495
  execSync("opencode --version", { stdio: "ignore" });
4671
6496
  version = "installed";
4672
6497
  } catch (error) {
4673
- if (existsSync4(opencodeConfig)) {
6498
+ if (existsSync5(opencodeConfig)) {
4674
6499
  version = "installed (config exists)";
4675
6500
  } else {
4676
6501
  installed = false;
@@ -4762,6 +6587,47 @@ var CliDetector = class {
4762
6587
  static filterInstallable(tools) {
4763
6588
  return tools.filter((t) => t.detected && !t.installed);
4764
6589
  }
6590
+ /**
6591
+ * Detect available platforms
6592
+ */
6593
+ static async detectPlatforms() {
6594
+ const platforms = [];
6595
+ const opencodePath = process.platform === "win32" ? process.env.APPDATA || join12(homedir2(), "AppData", "Roaming") : join12(homedir2(), ".config");
6596
+ platforms.push({
6597
+ platform: "opencode" /* OPENCODE */,
6598
+ displayName: "OpenCode",
6599
+ installed: existsSync5(join12(opencodePath, "opencode", "opencode.json")),
6600
+ configPath: opencodePath
6601
+ });
6602
+ const claudePath = process.platform === "win32" ? process.env.APPDATA || join12(homedir2(), "AppData", "Roaming") : join12(homedir2(), ".claude");
6603
+ platforms.push({
6604
+ platform: "claude" /* CLAUDE */,
6605
+ displayName: "Claude Code CLI",
6606
+ installed: existsSync5(claudePath),
6607
+ configPath: claudePath
6608
+ });
6609
+ return platforms;
6610
+ }
6611
+ /**
6612
+ * Filter installed platforms
6613
+ */
6614
+ static filterInstalledPlatforms(platforms) {
6615
+ return platforms.filter((p) => p.installed);
6616
+ }
6617
+ /**
6618
+ * Match platform name to CliPlatform enum
6619
+ */
6620
+ static matchPlatform(name) {
6621
+ const normalized = name.toLowerCase();
6622
+ if (normalized === "opencode" || normalized === "open-code") {
6623
+ return "opencode" /* OPENCODE */;
6624
+ } else if (normalized === "claude" || normalized === "claude-code" || normalized === "claude-code-cli") {
6625
+ return "claude" /* CLAUDE */;
6626
+ } else if (normalized === "codex") {
6627
+ return "codex" /* CODEX */;
6628
+ }
6629
+ throw new Error(`Unknown platform: ${name}`);
6630
+ }
4765
6631
  };
4766
6632
 
4767
6633
  // src/cli/commands/init.ts
@@ -4770,9 +6636,9 @@ init_paths();
4770
6636
 
4771
6637
  // src/cli/helpers.ts
4772
6638
  init_esm_shims();
4773
- import { existsSync as existsSync5 } from "fs";
4774
- import { mkdir as mkdir8, writeFile as writeFile8, readFile as readFile7, access as access4 } from "fs/promises";
4775
- import { join as join13, dirname as dirname2 } from "path";
6639
+ import { existsSync as existsSync6 } from "fs";
6640
+ import { mkdir as mkdir9, writeFile as writeFile9, readFile as readFile8, access as access5 } from "fs/promises";
6641
+ import { join as join14, dirname as dirname2 } from "path";
4776
6642
  import { homedir as homedir3 } from "os";
4777
6643
  import { fileURLToPath as fileURLToPath3 } from "url";
4778
6644
  import { execSync as execSync2 } from "child_process";
@@ -4798,7 +6664,7 @@ async function initializeConfig(configDir, _isGlobal) {
4798
6664
  "memory/research"
4799
6665
  ];
4800
6666
  for (const dir of dirs) {
4801
- await mkdir8(join13(configDir, dir), { recursive: true });
6667
+ await mkdir9(join14(configDir, dir), { recursive: true });
4802
6668
  }
4803
6669
  const defaultConfig = {
4804
6670
  version: getVersion(),
@@ -4811,8 +6677,8 @@ async function initializeConfig(configDir, _isGlobal) {
4811
6677
  beads: { enabled: true },
4812
6678
  antiHallucination: { enabled: true }
4813
6679
  };
4814
- await writeFile8(
4815
- join13(configDir, "aikit.json"),
6680
+ await writeFile9(
6681
+ join14(configDir, "aikit.json"),
4816
6682
  JSON.stringify(defaultConfig, null, 2)
4817
6683
  );
4818
6684
  const agentsMd = `# AIKit Agent Rules
@@ -4835,20 +6701,20 @@ async function initializeConfig(configDir, _isGlobal) {
4835
6701
  ## Project-Specific Rules
4836
6702
  Add your project-specific rules here.
4837
6703
  `;
4838
- await writeFile8(join13(configDir, "AGENTS.md"), agentsMd);
6704
+ await writeFile9(join14(configDir, "AGENTS.md"), agentsMd);
4839
6705
  }
4840
6706
  async function configureMcpServer(projectPath) {
4841
6707
  const currentFile = fileURLToPath3(import.meta.url);
4842
6708
  const currentDir = dirname2(currentFile);
4843
- const aikitPath = join13(currentDir, "..", "..");
4844
- const mcpServerPath = join13(aikitPath, "dist", "mcp-server.js");
6709
+ const aikitPath = join14(currentDir, "..", "..");
6710
+ const mcpServerPath = join14(aikitPath, "dist", "mcp-server.js");
4845
6711
  const configLocations = [
4846
6712
  // Global config (most common)
4847
- join13(homedir3(), ".config", "opencode", "opencode.json"),
6713
+ join14(homedir3(), ".config", "opencode", "opencode.json"),
4848
6714
  // Project-level config
4849
- join13(projectPath, ".opencode", "opencode.json"),
6715
+ join14(projectPath, ".opencode", "opencode.json"),
4850
6716
  // Alternative global location
4851
- join13(homedir3(), ".opencode", "opencode.json")
6717
+ join14(homedir3(), ".opencode", "opencode.json")
4852
6718
  ];
4853
6719
  const mcpServerConfig = {
4854
6720
  type: "local",
@@ -4857,12 +6723,12 @@ async function configureMcpServer(projectPath) {
4857
6723
  };
4858
6724
  for (const configPath of configLocations) {
4859
6725
  try {
4860
- const configDir = join13(configPath, "..");
4861
- await mkdir8(configDir, { recursive: true });
6726
+ const configDir = join14(configPath, "..");
6727
+ await mkdir9(configDir, { recursive: true });
4862
6728
  let config = {};
4863
- if (existsSync5(configPath)) {
6729
+ if (existsSync6(configPath)) {
4864
6730
  try {
4865
- const existing = await readFile7(configPath, "utf-8");
6731
+ const existing = await readFile8(configPath, "utf-8");
4866
6732
  config = JSON.parse(existing);
4867
6733
  } catch {
4868
6734
  config = {};
@@ -4872,7 +6738,7 @@ async function configureMcpServer(projectPath) {
4872
6738
  config.mcp = {};
4873
6739
  }
4874
6740
  config.mcp.aikit = mcpServerConfig;
4875
- await writeFile8(configPath, JSON.stringify(config, null, 2));
6741
+ await writeFile9(configPath, JSON.stringify(config, null, 2));
4876
6742
  logger.success(`
4877
6743
  \u2705 MCP server configured: ${configPath}`);
4878
6744
  logger.info(` Server: node ${mcpServerPath}`);
@@ -4881,9 +6747,9 @@ async function configureMcpServer(projectPath) {
4881
6747
  continue;
4882
6748
  }
4883
6749
  }
4884
- const instructionsPath = join13(projectPath, ".opencode", "MCP_SETUP.md");
4885
- await mkdir8(join13(projectPath, ".opencode"), { recursive: true });
4886
- await writeFile8(instructionsPath, `# AIKit MCP Server Configuration
6750
+ const instructionsPath = join14(projectPath, ".opencode", "MCP_SETUP.md");
6751
+ await mkdir9(join14(projectPath, ".opencode"), { recursive: true });
6752
+ await writeFile9(instructionsPath, `# AIKit MCP Server Configuration
4887
6753
 
4888
6754
  ## Automatic Setup Failed
4889
6755
 
@@ -4930,13 +6796,24 @@ async function installCliTool(tool) {
4930
6796
  await installToOpenCode(paths.opencodeConfig());
4931
6797
  break;
4932
6798
  case "claude" /* CLAUDE */:
4933
- execSync2("npm install -g @anthropic-ai/claude-code", { stdio: "inherit" });
6799
+ let platform;
6800
+ try {
6801
+ platform = process.platform;
6802
+ } catch {
6803
+ platform = "darwin";
6804
+ }
6805
+ if (platform === "darwin") {
6806
+ execSync2("curl -fsSL https://claude.ai/install.sh | bash", { stdio: "inherit" });
6807
+ } else if (platform === "win32") {
6808
+ execSync2('powershell -Command "irm https://claude.ai/install.ps1 | iex"', { stdio: "inherit" });
6809
+ } else {
6810
+ execSync2("curl -fsSL https://claude.ai/install.sh | bash", { stdio: "inherit" });
6811
+ }
4934
6812
  break;
4935
6813
  case "github" /* GITHUB */:
4936
6814
  execSync2("npm install -g gh", { stdio: "inherit" });
4937
6815
  break;
4938
6816
  }
4939
- logger.success(`\u2713 ${tool.displayName} installed`);
4940
6817
  return true;
4941
6818
  } catch (error) {
4942
6819
  logger.error(`Failed to install ${tool.displayName}:`, error);
@@ -5125,26 +7002,26 @@ Report what was extracted:
5125
7002
  }
5126
7003
  async function installToOpenCode(_opencodePath) {
5127
7004
  const projectPath = process.cwd();
5128
- const opencodeCommandDir = join13(projectPath, ".opencode", "command");
5129
- const aikitDir = join13(projectPath, ".aikit");
5130
- const opencodeAgentDir = join13(paths.opencodeConfig(), "agent");
5131
- await mkdir8(opencodeCommandDir, { recursive: true });
5132
- await mkdir8(join13(aikitDir, "skills"), { recursive: true });
5133
- await mkdir8(opencodeAgentDir, { recursive: true });
7005
+ const opencodeCommandDir = join14(projectPath, ".opencode", "command");
7006
+ const aikitDir = join14(projectPath, ".aikit");
7007
+ const opencodeAgentDir = join14(paths.opencodeConfig(), "agent");
7008
+ await mkdir9(opencodeCommandDir, { recursive: true });
7009
+ await mkdir9(join14(aikitDir, "skills"), { recursive: true });
7010
+ await mkdir9(opencodeAgentDir, { recursive: true });
5134
7011
  for (const [name, content] of Object.entries(AGENT_FILES)) {
5135
- const filePath = join13(opencodeAgentDir, `${name}.md`);
7012
+ const filePath = join14(opencodeAgentDir, `${name}.md`);
5136
7013
  try {
5137
- await access4(filePath);
5138
- const existingContent = await readFile7(filePath, "utf8");
7014
+ await access5(filePath);
7015
+ const existingContent = await readFile8(filePath, "utf8");
5139
7016
  if (!existingContent.includes("mode: subagent")) {
5140
- const matter3 = await import("gray-matter");
5141
- const { data: frontmatter, content: body } = matter3.default(existingContent);
7017
+ const matter5 = await import("gray-matter");
7018
+ const { data: frontmatter, content: body } = matter5.default(existingContent);
5142
7019
  frontmatter.mode = "subagent";
5143
- const updatedContent = matter3.default.stringify(body, frontmatter);
5144
- await writeFile8(filePath, updatedContent, "utf8");
7020
+ const updatedContent = matter5.default.stringify(body, frontmatter);
7021
+ await writeFile9(filePath, updatedContent, "utf8");
5145
7022
  }
5146
7023
  } catch {
5147
- await writeFile8(filePath, content, "utf8");
7024
+ await writeFile9(filePath, content, "utf8");
5148
7025
  }
5149
7026
  }
5150
7027
  const config = await loadConfig();
@@ -5236,8 +7113,8 @@ ${cmd.content}
5236
7113
  }
5237
7114
  let count = 0;
5238
7115
  for (const [name, content] of Object.entries(opencodeCommands)) {
5239
- const filePath = join13(opencodeCommandDir, `${name}.md`);
5240
- await writeFile8(filePath, content.trim());
7116
+ const filePath = join14(opencodeCommandDir, `${name}.md`);
7117
+ await writeFile9(filePath, content.trim());
5241
7118
  logger.info(` \u2713 Created /${name} command`);
5242
7119
  count++;
5243
7120
  }
@@ -5258,10 +7135,541 @@ function groupBy(array, keyFn) {
5258
7135
  return acc;
5259
7136
  }, {});
5260
7137
  }
7138
+ async function startSession(name, goals) {
7139
+ try {
7140
+ const { SessionManager: SessionManager2 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
7141
+ const manager = new SessionManager2();
7142
+ const session = await manager.startSession(name, goals);
7143
+ logger.success("\u2713 Session started");
7144
+ console.log(` ID: ${session.id}`);
7145
+ console.log(` Name: ${session.name}`);
7146
+ console.log(` Started: ${new Date(session.startTime).toLocaleString()}`);
7147
+ if (session.goals.length > 0) {
7148
+ console.log(` Goals:`);
7149
+ session.goals.forEach((goal) => console.log(` - ${goal}`));
7150
+ }
7151
+ console.log("\nCommands:");
7152
+ console.log(" /session:update [notes] - Add progress notes");
7153
+ console.log(" /session:end - End session with summary");
7154
+ console.log(" /session:current - Show session status\n");
7155
+ } catch (error) {
7156
+ logger.error("Failed to start session:", error);
7157
+ }
7158
+ }
7159
+ async function updateSession(notes) {
7160
+ try {
7161
+ const { SessionManager: SessionManager2 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
7162
+ const manager = new SessionManager2();
7163
+ const session = await manager.updateSession(notes);
7164
+ if (session) {
7165
+ logger.success("\u2713 Session updated");
7166
+ console.log(` Session: ${session.id}`);
7167
+ console.log(` Notes: ${notes || "Auto-generated"}`);
7168
+ console.log(` Time: ${(/* @__PURE__ */ new Date()).toLocaleString()}`);
7169
+ if (session.updates[session.updates.length - 1]?.modifiedFiles) {
7170
+ const files = session.updates[session.updates.length - 1].modifiedFiles;
7171
+ console.log(` Modified: ${files?.length} files`);
7172
+ }
7173
+ console.log();
7174
+ }
7175
+ } catch (error) {
7176
+ if (error instanceof Error && error.message.includes("No active session")) {
7177
+ logger.error("No active session. Use /session:start first");
7178
+ } else {
7179
+ logger.error("Failed to update session:", error);
7180
+ }
7181
+ }
7182
+ }
7183
+ async function endSession() {
7184
+ try {
7185
+ const { SessionManager: SessionManager2 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
7186
+ const manager = new SessionManager2();
7187
+ const session = await manager.endSession();
7188
+ if (session) {
7189
+ const duration = session.endTime ? Math.floor((new Date(session.endTime).getTime() - new Date(session.startTime).getTime()) / 6e4) : 0;
7190
+ logger.success("\u2713 Session ended");
7191
+ console.log(`
7192
+ Session: ${session.id}`);
7193
+ console.log(`Name: ${session.name}`);
7194
+ console.log(`Duration: ${Math.floor(duration / 60)}h ${duration % 60}m`);
7195
+ console.log(`Updates: ${session.updates.length}`);
7196
+ if (session.goals.length > 0) {
7197
+ console.log(`
7198
+ Goals:`);
7199
+ session.goals.forEach((goal) => console.log(` - ${goal}`));
7200
+ }
7201
+ const lastUpdate = session.updates[session.updates.length - 1];
7202
+ if (lastUpdate?.gitCommits) {
7203
+ console.log(`
7204
+ Git Activity:`);
7205
+ console.log(` Commits: ${lastUpdate.gitCommits}`);
7206
+ if (lastUpdate.modifiedFiles && lastUpdate.modifiedFiles.length > 0) {
7207
+ console.log(` Files Modified: ${lastUpdate.modifiedFiles.length}`);
7208
+ }
7209
+ }
7210
+ console.log(`
7211
+ Use /session:show ${session.id} for details
7212
+ `);
7213
+ }
7214
+ } catch (error) {
7215
+ if (error instanceof Error && error.message.includes("No active session")) {
7216
+ logger.error("No active session. Use /session:start first");
7217
+ } else {
7218
+ logger.error("Failed to end session:", error);
7219
+ }
7220
+ }
7221
+ }
7222
+ async function showCurrentSession() {
7223
+ try {
7224
+ const { SessionManager: SessionManager2 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
7225
+ const manager = new SessionManager2();
7226
+ const session = await manager.getCurrentSession();
7227
+ if (!session) {
7228
+ logger.info("No active session");
7229
+ console.log("Start a session: /session:start [name]\n");
7230
+ return;
7231
+ }
7232
+ const duration = Math.floor((Date.now() - new Date(session.startTime).getTime()) / 6e4);
7233
+ console.log("\n\u{1F4CD} Current Session");
7234
+ console.log("\u2501".repeat(60));
7235
+ console.log(`
7236
+ Session: ${session.name}`);
7237
+ console.log(`ID: ${session.id}`);
7238
+ console.log(`Started: ${Math.floor(duration / 60)}h ${duration % 60}m ago`);
7239
+ if (session.goals.length > 0) {
7240
+ console.log(`
7241
+ Goals:`);
7242
+ session.goals.forEach((goal) => console.log(` - ${goal}`));
7243
+ }
7244
+ if (session.updates.length > 0) {
7245
+ console.log(`
7246
+ Recent Updates (last 3):`);
7247
+ const recent = session.updates.slice(-3);
7248
+ recent.forEach((update) => {
7249
+ const date = new Date(update.timestamp);
7250
+ console.log(` ${date.toLocaleTimeString()} - ${update.notes || "Update"}`);
7251
+ });
7252
+ }
7253
+ const lastUpdate = session.updates[session.updates.length - 1];
7254
+ if (lastUpdate?.gitBranch || lastUpdate?.gitCommits) {
7255
+ console.log(`
7256
+ Git:`);
7257
+ if (lastUpdate.gitBranch) console.log(` Branch: ${lastUpdate.gitBranch}`);
7258
+ if (lastUpdate.gitCommits) console.log(` Commits: ${lastUpdate.gitCommits}`);
7259
+ if (lastUpdate.modifiedFiles && lastUpdate.modifiedFiles.length > 0) {
7260
+ console.log(` Modified: ${lastUpdate.modifiedFiles.length} files`);
7261
+ }
7262
+ }
7263
+ if (lastUpdate?.beadsTask) {
7264
+ console.log(`
7265
+ Beads Task:`);
7266
+ console.log(` ${lastUpdate.beadsTask.id} (${lastUpdate.beadsTask.status})`);
7267
+ }
7268
+ console.log("\nCommands:");
7269
+ console.log(" /session:update [notes] - Add progress");
7270
+ console.log(" /session:end - Close session");
7271
+ console.log("\u2501".repeat(60) + "\n");
7272
+ } catch (error) {
7273
+ logger.error("Failed to show current session:", error);
7274
+ }
7275
+ }
7276
+ async function listSessions() {
7277
+ try {
7278
+ const { SessionManager: SessionManager2 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
7279
+ const manager = new SessionManager2();
7280
+ const sessions = await manager.listSessions();
7281
+ if (sessions.length === 0) {
7282
+ logger.info("No sessions found");
7283
+ console.log("Start a session: /session:start [name]\n");
7284
+ return;
7285
+ }
7286
+ console.log("\n\u{1F4DA} All Sessions");
7287
+ console.log("\u2501".repeat(60));
7288
+ sessions.forEach((session, index) => {
7289
+ const startDate = new Date(session.startTime);
7290
+ const endDate = session.endTime ? new Date(session.endTime) : null;
7291
+ const duration = endDate ? Math.floor((endDate.getTime() - startDate.getTime()) / 6e4) : null;
7292
+ console.log(`
7293
+ ${index + 1}. ${session.id}`);
7294
+ console.log(` Status: ${session.status === "active" ? "\u{1F7E2} Active" : "Ended"}`);
7295
+ console.log(` Name: ${session.name}`);
7296
+ console.log(` Started: ${startDate.toLocaleString()}`);
7297
+ if (endDate && duration) {
7298
+ console.log(` Ended: ${endDate.toLocaleString()} (${Math.floor(duration / 60)}h ${duration % 60}m)`);
7299
+ }
7300
+ if (session.goals.length > 0) {
7301
+ console.log(` Goals: ${session.goals.slice(0, 2).join(", ")}${session.goals.length > 2 ? "..." : ""}`);
7302
+ }
7303
+ console.log(` Updates: ${session.updates.length}`);
7304
+ });
7305
+ console.log("\n" + "\u2501".repeat(60));
7306
+ console.log(`
7307
+ Total: ${sessions.length} session${sessions.length > 1 ? "s" : ""}
7308
+ `);
7309
+ console.log("Commands:");
7310
+ console.log(" /session:show <id> - View session details");
7311
+ console.log(" /session:resume <id> - Resume session");
7312
+ console.log(" /session:search <query> - Search sessions\n");
7313
+ } catch (error) {
7314
+ logger.error("Failed to list sessions:", error);
7315
+ }
7316
+ }
7317
+ async function showSession(sessionId) {
7318
+ try {
7319
+ const { SessionManager: SessionManager2 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
7320
+ const manager = new SessionManager2();
7321
+ const sessions = await manager.listSessions();
7322
+ const session = sessions.find((s) => s.id.startsWith(sessionId) || s.id === sessionId);
7323
+ if (!session) {
7324
+ logger.error(`Session not found: ${sessionId}`);
7325
+ console.log("Use /session:list to see all sessions\n");
7326
+ return;
7327
+ }
7328
+ const startDate = new Date(session.startTime);
7329
+ const endDate = session.endTime ? new Date(session.endTime) : null;
7330
+ const duration = endDate ? Math.floor((endDate.getTime() - startDate.getTime()) / 6e4) : null;
7331
+ console.log("\n\u{1F4C4} Session: " + session.id);
7332
+ console.log("\u2501".repeat(60));
7333
+ console.log(`
7334
+ Status: ${session.status === "active" ? "\u{1F7E2} Active" : "Ended"}`);
7335
+ console.log(`Name: ${session.name}`);
7336
+ console.log(`Started: ${startDate.toLocaleString()}`);
7337
+ if (endDate && duration) {
7338
+ console.log(`Ended: ${endDate.toLocaleString()}`);
7339
+ console.log(`Duration: ${Math.floor(duration / 60)}h ${duration % 60}m`);
7340
+ }
7341
+ if (session.goals.length > 0) {
7342
+ console.log(`
7343
+ Goals:`);
7344
+ session.goals.forEach((goal) => console.log(` ${session.status === "ended" ? "\u2713" : "\u25CB"} ${goal}`));
7345
+ }
7346
+ console.log(`
7347
+ ## Progress (${session.updates.length} updates)`);
7348
+ session.updates.forEach((update) => {
7349
+ const date = new Date(update.timestamp);
7350
+ console.log(`
7351
+ ### ${date.toLocaleString()}`);
7352
+ if (update.notes) console.log(`${update.notes}`);
7353
+ if (update.gitBranch) console.log(`**Git Branch:** ${update.gitBranch}`);
7354
+ if (update.modifiedFiles && update.modifiedFiles.length > 0) {
7355
+ console.log(`**Modified Files:** ${update.modifiedFiles.length} files`);
7356
+ }
7357
+ if (update.beadsTask) {
7358
+ console.log(`**Beads Task:** ${update.beadsTask.id} (${update.beadsTask.status})`);
7359
+ }
7360
+ });
7361
+ if (session.status === "ended") {
7362
+ console.log(`
7363
+ ## Summary`);
7364
+ console.log(`Duration: ${duration ? Math.floor(duration / 60) + "h " + duration % 60 + "m" : "N/A"}`);
7365
+ console.log(`Updates: ${session.updates.length}`);
7366
+ if (session.updates[session.updates.length - 1]?.gitCommits) {
7367
+ console.log(`Git Commits: ${session.updates[session.updates.length - 1].gitCommits}`);
7368
+ }
7369
+ }
7370
+ console.log("\n" + "\u2501".repeat(60) + "\n");
7371
+ } catch (error) {
7372
+ logger.error("Failed to show session:", error);
7373
+ }
7374
+ }
7375
+ async function searchSessions(query) {
7376
+ try {
7377
+ const { SessionManager: SessionManager2 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
7378
+ const manager = new SessionManager2();
7379
+ const sessions = await manager.searchSessions(query);
7380
+ if (sessions.length === 0) {
7381
+ logger.info(`No sessions found matching: ${query}`);
7382
+ console.log("Use /session:list to see all sessions\n");
7383
+ return;
7384
+ }
7385
+ console.log(`
7386
+ \u{1F50D} Search Results: "${query}"`);
7387
+ console.log("\u2501".repeat(60));
7388
+ console.log(`
7389
+ Found ${sessions.length} session${sessions.length > 1 ? "s" : ""}:
7390
+ `);
7391
+ sessions.forEach((session, index) => {
7392
+ const startDate = new Date(session.startTime);
7393
+ console.log(`${index + 1}. ${session.id}`);
7394
+ console.log(` Name: ${session.name}`);
7395
+ console.log(` Started: ${startDate.toLocaleString()}`);
7396
+ console.log(` Status: ${session.status === "active" ? "\u{1F7E2} Active" : "Ended"}`);
7397
+ if (session.goals.length > 0) {
7398
+ console.log(` Goals: ${session.goals.slice(0, 2).join(", ")}${session.goals.length > 2 ? "..." : ""}`);
7399
+ }
7400
+ console.log();
7401
+ });
7402
+ console.log("\u2501".repeat(60));
7403
+ console.log(`
7404
+ Total: ${sessions.length} matching session${sessions.length > 1 ? "s" : ""}
7405
+ `);
7406
+ console.log("Commands:");
7407
+ console.log(" /session:show <id> - View session details");
7408
+ console.log(" /session:resume <id> - Resume session\n");
7409
+ } catch (error) {
7410
+ logger.error("Failed to search sessions:", error);
7411
+ }
7412
+ }
7413
+
7414
+ // src/platform/adapters.ts
7415
+ init_esm_shims();
7416
+
7417
+ // src/platform/opencode-adapter.ts
7418
+ init_esm_shims();
7419
+ init_paths();
7420
+ import { readFile as readFile9, writeFile as writeFile10, mkdir as mkdir10, access as access6 } from "fs/promises";
7421
+ import { join as join15 } from "path";
7422
+ var OpenCodeAdapter = class {
7423
+ platform = "opencode" /* OPENCODE */;
7424
+ displayName = "OpenCode";
7425
+ getCommandsDir() {
7426
+ return join15(process.cwd(), ".opencode", "command");
7427
+ }
7428
+ getSkillsDir() {
7429
+ return join15(process.cwd(), ".opencode", "skill");
7430
+ }
7431
+ getAgentsDir() {
7432
+ return join15(paths.opencodeConfig(), "agent");
7433
+ }
7434
+ async transformCommand(command) {
7435
+ const name = `ak_cm_${command.name}`;
7436
+ const content = this.generateCommandContent(command);
7437
+ return { name, content };
7438
+ }
7439
+ async transformSkill(skill) {
7440
+ const skillName = `ak_sk_${skill.name}`;
7441
+ const skillContent = this.generateSkillContent(skill);
7442
+ const result = {
7443
+ name: skillName,
7444
+ directory: "",
7445
+ files: { [`${skillName}.md`]: skillContent }
7446
+ };
7447
+ return result;
7448
+ }
7449
+ async transformAgent(agent) {
7450
+ const name = agent.name === "build" || agent.name === "planner" ? `aikit${agent.name}` : agent.name;
7451
+ const content = this.generateAgentContent(agent, name);
7452
+ return { name, content };
7453
+ }
7454
+ async installCommand(name, content) {
7455
+ const dir = this.getCommandsDir();
7456
+ await mkdir10(dir, { recursive: true });
7457
+ await writeFile10(join15(dir, `${name}.md`), content);
7458
+ }
7459
+ async installSkill(_name, directory, files) {
7460
+ const baseDir = this.getSkillsDir();
7461
+ const targetDir = directory ? join15(baseDir, directory) : baseDir;
7462
+ await mkdir10(targetDir, { recursive: true });
7463
+ for (const [filename, content] of Object.entries(files)) {
7464
+ await writeFile10(join15(targetDir, filename), content);
7465
+ }
7466
+ }
7467
+ async installAgent(name, content) {
7468
+ const dir = this.getAgentsDir();
7469
+ await mkdir10(dir, { recursive: true });
7470
+ const filePath = join15(dir, `${name}.md`);
7471
+ try {
7472
+ await access6(filePath);
7473
+ const existingContent = await readFile9(filePath, "utf-8");
7474
+ if (!existingContent.includes("mode: subagent")) {
7475
+ const matter5 = await import("gray-matter");
7476
+ const { data: frontmatter, content: body } = matter5.default(existingContent);
7477
+ frontmatter.mode = "subagent";
7478
+ const updatedContent = matter5.default.stringify(body, frontmatter);
7479
+ await writeFile10(filePath, updatedContent, "utf-8");
7480
+ }
7481
+ } catch {
7482
+ await writeFile10(filePath, content, "utf-8");
7483
+ }
7484
+ }
7485
+ generateCommandContent(command) {
7486
+ const examples = command.examples.map((e) => {
7487
+ const prefixed = e.replace(/\//g, "/ak_cm_");
7488
+ return `- \`${prefixed}\``;
7489
+ }).join("\n");
7490
+ return `# Command: /ak_cm_${command.name}
7491
+
7492
+ ## Description
7493
+ ${command.description}
7494
+
7495
+ ## Usage
7496
+ \`${command.usage.replace(/\//g, "/ak_cm_")}\`
7497
+
7498
+ ## Examples
7499
+ ${examples}
7500
+
7501
+ ## \u26A0\uFE0F CRITICAL: The User Has Already Provided Arguments!
7502
+
7503
+ **The user has provided arguments with this command!**
7504
+
7505
+ The arguments are available in this command response - look at the command workflow below, which now includes explicit instructions to use the provided arguments.
7506
+
7507
+ **YOUR JOB**:
7508
+ 1. Follow the command workflow steps
7509
+ 2. The workflow will tell you to look at "Arguments Provided" section
7510
+ 3. Use those arguments - do NOT ask the user for this information!
7511
+ 4. They have already provided it - extract and use it!
7512
+
7513
+ **Example Scenario**:
7514
+ - User runs: \`/ak_cm_${command.name} snake game with html & css\`
7515
+ - Command: \`/ak_cm_${command.name}\`
7516
+ - Arguments to use: \`snake game with html & css\`
7517
+ - You must use "snake game with html & css" as provided in the workflow!
7518
+
7519
+ **DO NOT**: Ask "Please provide a task description"
7520
+ **DO**: Follow the workflow and use the arguments provided in it!
7521
+
7522
+ ## Workflow
7523
+ ${command.content}
7524
+
7525
+ **Category**: ${command.category}`;
7526
+ }
7527
+ generateSkillContent(skill) {
7528
+ const relativePath = skill.filePath.startsWith(process.cwd()) ? skill.filePath.replace(process.cwd(), "").replace(/\\/g, "/").replace(/^\//, "") : `.aikit/skills/${skill.name}.md`;
7529
+ return `Use the **${skill.name} skill** ${skill.useWhen.toLowerCase()}.
7530
+
7531
+ READ ${relativePath}
7532
+
7533
+ ## Description
7534
+ ${skill.description}
7535
+
7536
+ ## When to Use
7537
+ ${skill.useWhen}
7538
+
7539
+ ## Workflow
7540
+ ${skill.content.split("\n").slice(0, 20).join("\n")}${skill.content.split("\n").length > 20 ? "\n\n... (see full skill file for complete workflow)" : ""}
7541
+
7542
+ **IMPORTANT**: Follow this skill's workflow step by step. Do not skip steps.
7543
+ Complete the checklist at the end of the skill.`;
7544
+ }
7545
+ generateAgentContent(agent, nameOverride) {
7546
+ return `---
7547
+ name: ${nameOverride || agent.name}
7548
+ mode: subagent
7549
+ ---
7550
+
7551
+ ${agent.systemPrompt}`;
7552
+ }
7553
+ };
7554
+
7555
+ // src/platform/claude-adapter.ts
7556
+ init_esm_shims();
7557
+ init_paths();
7558
+ import { writeFile as writeFile11, mkdir as mkdir11 } from "fs/promises";
7559
+ import { join as join16 } from "path";
7560
+ import matter4 from "gray-matter";
7561
+ var ClaudeAdapter = class {
7562
+ platform = "claude" /* CLAUDE */;
7563
+ displayName = "Claude Code CLI";
7564
+ getCommandsDir() {
7565
+ return paths.claudeCommands(true);
7566
+ }
7567
+ getSkillsDir() {
7568
+ return paths.claudeSkills(true);
7569
+ }
7570
+ getAgentsDir() {
7571
+ return paths.claudeAgents(true);
7572
+ }
7573
+ async transformCommand(command) {
7574
+ const name = command.name;
7575
+ const content = this.generateCommandContent(command);
7576
+ return { name, content };
7577
+ }
7578
+ async transformSkill(skill) {
7579
+ const name = skill.name;
7580
+ const content = this.generateSkillContent(skill);
7581
+ return {
7582
+ name,
7583
+ directory: name,
7584
+ files: { "SKILL.md": content }
7585
+ };
7586
+ }
7587
+ async transformAgent(agent) {
7588
+ const name = agent.name;
7589
+ const content = this.generateAgentContent(agent);
7590
+ return { name, content };
7591
+ }
7592
+ async installCommand(name, content) {
7593
+ const dir = this.getCommandsDir();
7594
+ await mkdir11(dir, { recursive: true });
7595
+ await writeFile11(join16(dir, `${name}.md`), content);
7596
+ }
7597
+ async installSkill(_name, directory, files) {
7598
+ const baseDir = this.getSkillsDir();
7599
+ const targetDir = join16(baseDir, directory);
7600
+ await mkdir11(targetDir, { recursive: true });
7601
+ for (const [filename, content] of Object.entries(files)) {
7602
+ await writeFile11(join16(targetDir, filename), content);
7603
+ }
7604
+ }
7605
+ async installAgent(name, content) {
7606
+ const dir = this.getAgentsDir();
7607
+ await mkdir11(dir, { recursive: true });
7608
+ await writeFile11(join16(dir, `${name}.md`), content);
7609
+ }
7610
+ generateCommandContent(command) {
7611
+ let workflow = command.content;
7612
+ workflow = workflow.replace(/## ⚠️ CRITICAL: The User Has Already Provided Arguments!.*?(?![\n])?.*/gs, "").replace(/\*\*The user has provided arguments with this command!\*\*/g, "").replace(/\*\*The arguments are available in this command response.*?\*\*/g, "").replace(/\*\*YOUR JOB\*\*:.*/gs, "").replace(/1\. Follow command workflow steps.*?\n2\. The workflow will tell you.*?\n3\. Use those arguments.*?\n4\. They have already provided it.*?\n5\. DO NOT ask.*?\n6\. DO: Follow workflow.*?\n+\**Example Scenario\*\*:.*/gs, "").replace(/\*\*User runs:.*?\*\*:\n.*?\n+- Command:.*?\n+- Arguments to use:.*?\n+- You must use.*?\n+- DO NOT:.*?\n+- DO:.*?\n+\*\*\*/g, "").replace(/\*\*\*/g, "");
7613
+ workflow = workflow.replace(/^# Command: \/ak_cm_[\s-]+\n+/g, "");
7614
+ workflow = workflow.replace(/\$ARGUMENTS/g, "$ARGUMENTS").replace(/\$1/g, "$1").replace(/\$2/g, "$2");
7615
+ const frontmatter = {
7616
+ description: command.description,
7617
+ argumentHint: command.usage.replace(/^\//, "").replace(/<[^>]+>/g, "[args]")
7618
+ };
7619
+ return matter4.stringify(workflow, frontmatter);
7620
+ }
7621
+ generateSkillContent(skill) {
7622
+ const frontmatter = {
7623
+ name: skill.name,
7624
+ description: `${skill.description}. ${skill.useWhen}`
7625
+ };
7626
+ const content = `# ${skill.name}
7627
+
7628
+ ## When to Use
7629
+ ${skill.useWhen}
7630
+
7631
+ ## Description
7632
+ ${skill.description}
7633
+
7634
+ ## Workflow
7635
+ ${skill.content}
7636
+
7637
+ ## Tips
7638
+ - Use this skill when: ${skill.useWhen.toLowerCase()}
7639
+ - Category: ${skill.category}
7640
+ - Tags: ${skill.tags.join(", ")}`;
7641
+ return matter4.stringify(content, frontmatter);
7642
+ }
7643
+ generateAgentContent(agent) {
7644
+ const frontmatter = {
7645
+ name: agent.name,
7646
+ description: agent.useWhen,
7647
+ tools: ["Read", "Edit", "Bash", "Grep", "Glob"]
7648
+ // Default tools
7649
+ };
7650
+ return matter4.stringify(agent.systemPrompt, frontmatter);
7651
+ }
7652
+ };
7653
+
7654
+ // src/platform/adapters.ts
7655
+ function createAdapter(platform) {
7656
+ switch (platform) {
7657
+ case "opencode" /* OPENCODE */:
7658
+ return new OpenCodeAdapter();
7659
+ case "claude" /* CLAUDE */:
7660
+ return new ClaudeAdapter();
7661
+ default:
7662
+ throw new Error(`Unsupported platform: ${platform}`);
7663
+ }
7664
+ }
7665
+ var SUPPORTED_PLATFORMS = [
7666
+ { platform: "opencode" /* OPENCODE */, name: "OpenCode" },
7667
+ { platform: "claude" /* CLAUDE */, name: "Claude Code CLI" }
7668
+ ];
5261
7669
 
5262
7670
  // src/cli/commands/init.ts
5263
7671
  function registerInitCommand(program2) {
5264
- program2.command("init").description("Initialize AIKit configuration").option("-g, --global", "Initialize global configuration").option("-p, --project", "Initialize project-level configuration").action(async (options) => {
7672
+ program2.command("init [platform]").description("Initialize AIKit configuration for a specific platform").option("-g, --global", "Initialize global configuration").option("-p, --project", "Initialize project-level configuration").action(async (platformArg, options) => {
5265
7673
  const configDir = options.global ? paths.globalConfig() : paths.projectConfig();
5266
7674
  console.log(chalk2.bold("\n\u{1F680} AIKit Setup\n"));
5267
7675
  logger.info(`Initializing AIKit in ${configDir}...`);
@@ -5269,91 +7677,54 @@ function registerInitCommand(program2) {
5269
7677
  await initializeConfig(configDir, options.global);
5270
7678
  logger.success("\u2713 Configuration created");
5271
7679
  if (!options.global) {
7680
+ let selectedPlatform;
7681
+ if (platformArg) {
7682
+ selectedPlatform = CliDetector.matchPlatform(platformArg);
7683
+ } else {
7684
+ const platforms = await CliDetector.detectPlatforms();
7685
+ const installed = CliDetector.filterInstalledPlatforms(platforms);
7686
+ console.log(chalk2.bold("\n\u{1F50D} Available CLI Tools\n"));
7687
+ for (const p of platforms) {
7688
+ const status = p.installed ? chalk2.green("\u2713") : chalk2.gray("\u25CB");
7689
+ console.log(` ${status} ${p.displayName}`);
7690
+ }
7691
+ const { platform } = await inquirer.prompt([
7692
+ {
7693
+ type: "list",
7694
+ name: "platform",
7695
+ message: "Which CLI tool do you want to configure AIKit for?",
7696
+ choices: platforms.map((p) => ({
7697
+ name: p.displayName,
7698
+ value: p.platform
7699
+ })),
7700
+ default: installed[0]?.platform || "opencode" /* OPENCODE */
7701
+ }
7702
+ ]);
7703
+ selectedPlatform = platform;
7704
+ }
7705
+ logger.info(`Selected platform: ${selectedPlatform}`);
5272
7706
  const config = await loadConfig();
5273
7707
  const engine = new SkillEngine(config);
5274
7708
  const result = await engine.syncSkillsToProject();
5275
7709
  if (result.count > 0) {
5276
7710
  logger.success(`\u2713 Synced ${result.count} skills`);
5277
7711
  }
5278
- console.log(chalk2.bold("\n\u{1F50D} Checking CLI tools...\n"));
5279
- const cliTools = await CliDetector.checkAll();
5280
- const installableTools = CliDetector.filterInstallable(cliTools);
5281
- for (const tool of cliTools) {
5282
- const status = tool.installed ? chalk2.green("\u2713 Installed") : chalk2.yellow("\u2717 Not installed");
5283
- const version = tool.version ? chalk2.gray(` (${tool.version})`) : "";
5284
- console.log(` ${status} ${chalk2.cyan(tool.displayName)}${version}`);
5285
- }
5286
- if (installableTools.length > 0) {
5287
- console.log();
5288
- const { action } = await inquirer.prompt([
5289
- {
5290
- type: "list",
5291
- name: "action",
5292
- message: "How would you like to proceed?",
5293
- choices: [
5294
- {
5295
- name: "all",
5296
- value: "all",
5297
- short: "a",
5298
- message: "Install all missing CLI tools"
5299
- },
5300
- {
5301
- name: "select",
5302
- value: "select",
5303
- short: "s",
5304
- message: "Select specific tools to install (use space to select, Enter to confirm)"
5305
- },
5306
- {
5307
- name: "skip",
5308
- value: "skip",
5309
- short: "n",
5310
- message: "Skip CLI tool installation"
5311
- }
5312
- ],
5313
- default: "all"
5314
- }
5315
- ]);
5316
- if (action === "skip") {
5317
- console.log();
5318
- logger.info("Skipping CLI tool installation");
5319
- } else if (action === "all") {
5320
- console.log();
5321
- logger.info(`Installing ${installableTools.length} CLI tool(s)...`);
5322
- for (const tool of installableTools) {
5323
- await installCliTool(tool);
5324
- }
5325
- console.log();
5326
- logger.success("\u2713 CLI tools installed");
5327
- } else {
5328
- const { installTools } = await inquirer.prompt([
7712
+ if (selectedPlatform === "claude" /* CLAUDE */) {
7713
+ const cliTools = await CliDetector.checkAll();
7714
+ const claudeTool = cliTools.find((t) => t.name === "claude" /* CLAUDE */);
7715
+ if (claudeTool && !claudeTool.installed) {
7716
+ const { installClaude } = await inquirer.prompt([
5329
7717
  {
5330
- type: "checkbox",
5331
- name: "installTools",
5332
- message: "Select CLI tools to install (press Enter to skip):",
5333
- choices: installableTools.map((tool) => ({
5334
- name: tool.name,
5335
- value: tool,
5336
- checked: true
5337
- // Default to install all
5338
- }))
7718
+ type: "confirm",
7719
+ name: "installClaude",
7720
+ message: "Claude Code CLI is not installed. Install now?",
7721
+ default: true
5339
7722
  }
5340
7723
  ]);
5341
- if (installTools.length > 0) {
5342
- console.log();
5343
- logger.info(`Installing ${installTools.length} CLI tool(s)...`);
5344
- for (const tool of installTools) {
5345
- await installCliTool(tool);
5346
- }
5347
- console.log();
5348
- logger.success("\u2713 CLI tools installed");
5349
- } else {
5350
- console.log();
5351
- logger.info("Skipping CLI tool installation");
7724
+ if (installClaude) {
7725
+ await installCliTool(claudeTool);
5352
7726
  }
5353
7727
  }
5354
- } else {
5355
- console.log();
5356
- logger.success("\u2713 All CLI tools already installed");
5357
7728
  }
5358
7729
  const beads = new BeadsIntegration();
5359
7730
  const beadsStatus = await beads.getStatus();
@@ -5367,18 +7738,18 @@ function registerInitCommand(program2) {
5367
7738
  } else {
5368
7739
  logger.info("Beads already initialized");
5369
7740
  }
5370
- const opencodePath = paths.opencodeConfig();
5371
- await installToOpenCode(opencodePath);
7741
+ logger.info("Setting up git hooks...");
7742
+ await beads.setupGitHooks();
7743
+ logger.success("\u2713 Git hooks configured");
7744
+ const adapter = createAdapter(selectedPlatform);
7745
+ logger.info(`Installing AIKit for ${adapter.displayName}...`);
7746
+ await installToPlatform(adapter, config);
5372
7747
  console.log(chalk2.bold("\n\u2728 AIKit is ready!\n"));
5373
- console.log("Usage in OpenCode:");
5374
- console.log(chalk2.cyan(" /skills") + " - List all available skills");
5375
- console.log(chalk2.cyan(" /plan") + " - Create implementation plan");
5376
- console.log(chalk2.cyan(" /tdd") + " - Test-driven development");
5377
- console.log(chalk2.cyan(" /debug") + " - Systematic debugging");
5378
- console.log(chalk2.cyan(" /review") + " - Code review checklist");
5379
- console.log(chalk2.cyan(" /git") + " - Git workflow");
5380
- console.log(chalk2.cyan(" /frontend-aesthetics") + " - UI/UX guidelines");
5381
- console.log("\nPress " + chalk2.bold("Ctrl+K") + " in OpenCode to see all commands.\n");
7748
+ if (selectedPlatform === "opencode" /* OPENCODE */) {
7749
+ showOpenCodeUsage();
7750
+ } else if (selectedPlatform === "claude" /* CLAUDE */) {
7751
+ showClaudeUsage();
7752
+ }
5382
7753
  }
5383
7754
  } catch (error) {
5384
7755
  logger.error("Failed to initialize AIKit:", error);
@@ -5386,18 +7757,121 @@ function registerInitCommand(program2) {
5386
7757
  }
5387
7758
  });
5388
7759
  }
7760
+ async function installToPlatform(adapter, config) {
7761
+ const skillEngine = new SkillEngine(config);
7762
+ const commandRunner = new CommandRunner(config);
7763
+ const agentManager = new AgentManager(config);
7764
+ const skills = await skillEngine.listSkills();
7765
+ const commands = await commandRunner.listCommands();
7766
+ const agents = await agentManager.listAgents();
7767
+ logger.info(`Installing ${commands.length} commands...`);
7768
+ for (const command of commands) {
7769
+ const { name, content } = await adapter.transformCommand(command);
7770
+ await adapter.installCommand(name, content);
7771
+ logger.info(` \u2713 Created ${name} command`);
7772
+ }
7773
+ logger.info(`Installing ${skills.length} skills...`);
7774
+ for (const skill of skills) {
7775
+ const { name, directory, files } = await adapter.transformSkill(skill);
7776
+ await adapter.installSkill(name, directory, files);
7777
+ logger.info(` \u2713 Created ${name} skill`);
7778
+ }
7779
+ logger.info(`Installing ${agents.length} agents...`);
7780
+ for (const agent of agents) {
7781
+ const { name, content } = await adapter.transformAgent(agent);
7782
+ await adapter.installAgent(name, content);
7783
+ logger.info(` \u2713 Created ${name} agent`);
7784
+ }
7785
+ }
7786
+ function showOpenCodeUsage() {
7787
+ console.log("Usage in OpenCode:");
7788
+ console.log(chalk2.cyan(" /skills") + " - List all available skills");
7789
+ console.log(chalk2.cyan(" /plan") + " - Create implementation plan");
7790
+ console.log(chalk2.cyan(" /tdd") + " - Test-driven development");
7791
+ console.log(chalk2.cyan(" /debug") + " - Systematic debugging");
7792
+ console.log(chalk2.cyan(" /review") + " - Code review checklist");
7793
+ console.log(chalk2.cyan(" /git") + " - Git workflow");
7794
+ console.log(chalk2.cyan(" /frontend-aesthetics") + " - UI/UX guidelines");
7795
+ console.log("\nPress " + chalk2.bold("Ctrl+K") + " in OpenCode to see all commands.\n");
7796
+ }
7797
+ function showClaudeUsage() {
7798
+ console.log("Usage in Claude Code CLI:");
7799
+ console.log(chalk2.cyan(" /help") + " - List all available commands");
7800
+ console.log(chalk2.cyan(" /plan") + " - Create implementation plan");
7801
+ console.log(chalk2.cyan(" /implement") + " - Implement a task");
7802
+ console.log(chalk2.cyan(" /test") + " - Run tests");
7803
+ console.log("\nType " + chalk2.bold('"/help"') + " in Claude to see all commands.\n");
7804
+ }
5389
7805
 
5390
7806
  // src/cli/commands/install.ts
5391
7807
  init_esm_shims();
5392
7808
  init_logger();
5393
- init_paths();
7809
+ import inquirer2 from "inquirer";
7810
+ init_config();
5394
7811
  function registerInstallCommand(program2) {
5395
- program2.command("install").description("Install AIKit to OpenCode configuration").action(async () => {
5396
- logger.info("Installing AIKit to OpenCode...");
7812
+ program2.command("install [platform]").description("Install AIKit to specific CLI tool configuration").action(async (platformArg) => {
5397
7813
  try {
5398
- const opencodePath = paths.opencodeConfig();
5399
- await installToOpenCode(opencodePath);
5400
- logger.success("AIKit installed to OpenCode!");
7814
+ let selectedPlatform;
7815
+ if (platformArg) {
7816
+ selectedPlatform = CliDetector.matchPlatform(platformArg);
7817
+ if (!selectedPlatform) {
7818
+ logger.error(`Unknown platform: ${platformArg}`);
7819
+ logger.info(`Supported platforms: ${Object.values(CliPlatform).join(", ")}`);
7820
+ process.exit(1);
7821
+ }
7822
+ } else {
7823
+ const platforms = await CliDetector.detectPlatforms();
7824
+ const installedPlatforms = CliDetector.filterInstalledPlatforms(platforms);
7825
+ const defaultPlatform = installedPlatforms.length > 0 ? installedPlatforms[0].platform : platforms[0]?.platform;
7826
+ const { platform } = await inquirer2.prompt([
7827
+ {
7828
+ type: "list",
7829
+ name: "platform",
7830
+ message: "Which CLI tool do you want to install AIKit for?",
7831
+ choices: platforms.map((p) => ({
7832
+ name: p.displayName,
7833
+ value: p.platform
7834
+ })),
7835
+ default: defaultPlatform
7836
+ }
7837
+ ]);
7838
+ selectedPlatform = platform;
7839
+ }
7840
+ logger.info(`Installing AIKit for ${selectedPlatform}...`);
7841
+ const config = await loadConfig();
7842
+ const adapter = createAdapter(selectedPlatform);
7843
+ const skillEngine = config.skills.enabled ? new SkillEngine(config) : null;
7844
+ const commandRunner = config.commands.enabled ? new CommandRunner(config) : null;
7845
+ const agentManager = config.agents.enabled ? new AgentManager(config) : null;
7846
+ if (commandRunner) {
7847
+ const commands = await commandRunner.listCommands();
7848
+ logger.info(`Installing ${commands.length} commands...`);
7849
+ for (const command of commands) {
7850
+ const { name, content } = await adapter.transformCommand(command);
7851
+ await adapter.installCommand(name, content);
7852
+ logger.info(` \u2713 Created ${name} command`);
7853
+ }
7854
+ }
7855
+ if (skillEngine) {
7856
+ const skills = await skillEngine.listSkills();
7857
+ logger.info(`Installing ${skills.length} skills...`);
7858
+ for (const skill of skills) {
7859
+ const { name, directory, files } = await adapter.transformSkill(skill);
7860
+ await adapter.installSkill(name, directory, files);
7861
+ logger.info(` \u2713 Created ${name} skill`);
7862
+ }
7863
+ }
7864
+ if (agentManager) {
7865
+ const agents = await agentManager.listAgents();
7866
+ logger.info(`Installing ${agents.length} agents...`);
7867
+ for (const agent of agents) {
7868
+ const { name, content } = await adapter.transformAgent(agent);
7869
+ await adapter.installAgent(name, content);
7870
+ logger.info(` \u2713 Created ${name} agent`);
7871
+ }
7872
+ }
7873
+ logger.success(`
7874
+ \u2713 AIKit installed to ${adapter.displayName}!`);
5401
7875
  } catch (error) {
5402
7876
  logger.error("Failed to install:", error);
5403
7877
  process.exit(1);
@@ -5412,17 +7886,17 @@ import chalk4 from "chalk";
5412
7886
 
5413
7887
  // src/core/sync-engine.ts
5414
7888
  init_esm_shims();
5415
- import { readFile as readFile11, writeFile as writeFile12, copyFile, mkdir as mkdir10 } from "fs/promises";
5416
- import { join as join17, dirname as dirname4 } from "path";
5417
- import inquirer2 from "inquirer";
7889
+ import { readFile as readFile13, writeFile as writeFile15, copyFile, mkdir as mkdir13 } from "fs/promises";
7890
+ import { join as join20, dirname as dirname4 } from "path";
7891
+ import inquirer3 from "inquirer";
5418
7892
  import chalk3 from "chalk";
5419
7893
 
5420
7894
  // src/core/version-manager.ts
5421
7895
  init_esm_shims();
5422
7896
  init_paths();
5423
7897
  init_logger();
5424
- import { readFile as readFile8, readdir as readdir7, writeFile as writeFile9, stat } from "fs/promises";
5425
- import { join as join14 } from "path";
7898
+ import { readFile as readFile10, readdir as readdir8, writeFile as writeFile12, stat } from "fs/promises";
7899
+ import { join as join17 } from "path";
5426
7900
  import { createHash } from "crypto";
5427
7901
  var VersionManager = class {
5428
7902
  config;
@@ -5433,9 +7907,9 @@ var VersionManager = class {
5433
7907
  * Get current installed version
5434
7908
  */
5435
7909
  async getCurrentVersion() {
5436
- const versionPath = join14(paths.globalConfig(), ".version.json");
7910
+ const versionPath = join17(paths.globalConfig(), ".version.json");
5437
7911
  try {
5438
- const content = await readFile8(versionPath, "utf-8");
7912
+ const content = await readFile10(versionPath, "utf-8");
5439
7913
  return JSON.parse(content);
5440
7914
  } catch {
5441
7915
  return null;
@@ -5446,7 +7920,7 @@ var VersionManager = class {
5446
7920
  */
5447
7921
  getPackageVersion() {
5448
7922
  try {
5449
- const packageJson = __require(join14(process.cwd(), "package.json"));
7923
+ const packageJson = __require(join17(process.cwd(), "package.json"));
5450
7924
  return packageJson.version || "0.0.0";
5451
7925
  } catch {
5452
7926
  return "0.0.0";
@@ -5504,9 +7978,9 @@ var VersionManager = class {
5504
7978
  const removedSkills = [];
5505
7979
  const conflicts = [];
5506
7980
  const installedSkills = /* @__PURE__ */ new Map();
5507
- const installedPath = join14(paths.globalConfig(), ".installed-skills.json");
7981
+ const installedPath = join17(paths.globalConfig(), ".installed-skills.json");
5508
7982
  try {
5509
- const installedData = await readFile8(installedPath, "utf-8");
7983
+ const installedData = await readFile10(installedPath, "utf-8");
5510
7984
  const installedList = JSON.parse(installedData);
5511
7985
  installedList.forEach((skill) => {
5512
7986
  installedSkills.set(skill.name, skill);
@@ -5554,9 +8028,9 @@ var VersionManager = class {
5554
8028
  const hashes = [];
5555
8029
  try {
5556
8030
  const loadFromDir = async (dir) => {
5557
- const files = await readdir7(dir);
8031
+ const files = await readdir8(dir);
5558
8032
  for (const file of files) {
5559
- const filePath = join14(dir, file);
8033
+ const filePath = join17(dir, file);
5560
8034
  const stats = await stat(filePath);
5561
8035
  if (stats.isDirectory()) {
5562
8036
  await loadFromDir(filePath);
@@ -5582,7 +8056,7 @@ var VersionManager = class {
5582
8056
  */
5583
8057
  async calculateSkillHash(filePath) {
5584
8058
  try {
5585
- const content = await readFile8(filePath, "utf-8");
8059
+ const content = await readFile10(filePath, "utf-8");
5586
8060
  return createHash("sha256").update(content).digest("hex");
5587
8061
  } catch {
5588
8062
  return "";
@@ -5603,9 +8077,9 @@ var VersionManager = class {
5603
8077
  * Save installed skills info
5604
8078
  */
5605
8079
  async saveInstalledSkills(skills) {
5606
- const installedPath = join14(paths.globalConfig(), ".installed-skills.json");
8080
+ const installedPath = join17(paths.globalConfig(), ".installed-skills.json");
5607
8081
  try {
5608
- await writeFile9(installedPath, JSON.stringify(skills, null, 2));
8082
+ await writeFile12(installedPath, JSON.stringify(skills, null, 2));
5609
8083
  } catch (error) {
5610
8084
  logger.error("Failed to save installed skills info:", error);
5611
8085
  }
@@ -5626,8 +8100,8 @@ var VersionManager = class {
5626
8100
  packageVersion: this.getPackageVersion(),
5627
8101
  migrationHistory: migration ? [...current.migrationHistory, migration] : current.migrationHistory
5628
8102
  };
5629
- const versionPath = join14(paths.globalConfig(), ".version.json");
5630
- await writeFile9(versionPath, JSON.stringify(updated, null, 2));
8103
+ const versionPath = join17(paths.globalConfig(), ".version.json");
8104
+ await writeFile12(versionPath, JSON.stringify(updated, null, 2));
5631
8105
  }
5632
8106
  /**
5633
8107
  * Check if migration is needed
@@ -5642,8 +8116,8 @@ var VersionManager = class {
5642
8116
  // src/core/backup-manager.ts
5643
8117
  init_esm_shims();
5644
8118
  init_logger();
5645
- import { readFile as readFile9, writeFile as writeFile10, readdir as readdir8, stat as stat2, unlink, mkdir as mkdir9 } from "fs/promises";
5646
- import { join as join15, dirname as dirname3 } from "path";
8119
+ import { readFile as readFile11, writeFile as writeFile13, readdir as readdir9, stat as stat2, unlink, mkdir as mkdir12 } from "fs/promises";
8120
+ import { join as join18, dirname as dirname3 } from "path";
5647
8121
  import { createHash as createHash2 } from "crypto";
5648
8122
  var BackupManager = class {
5649
8123
  configPath;
@@ -5651,7 +8125,7 @@ var BackupManager = class {
5651
8125
  maxBackups;
5652
8126
  constructor(configPath, maxBackups = 5) {
5653
8127
  this.configPath = configPath;
5654
- this.backupsDir = join15(configPath, ".backups");
8128
+ this.backupsDir = join18(configPath, ".backups");
5655
8129
  this.maxBackups = maxBackups;
5656
8130
  }
5657
8131
  /**
@@ -5659,10 +8133,10 @@ var BackupManager = class {
5659
8133
  */
5660
8134
  async createBackup(fromVersion, toVersion) {
5661
8135
  try {
5662
- await mkdir9(this.backupsDir, { recursive: true });
8136
+ await mkdir12(this.backupsDir, { recursive: true });
5663
8137
  const backupId = `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
5664
- const backupPath = join15(this.backupsDir, `${backupId}-v${toVersion}`);
5665
- await mkdir9(backupPath, { recursive: true });
8138
+ const backupPath = join18(this.backupsDir, `${backupId}-v${toVersion}`);
8139
+ await mkdir12(backupPath, { recursive: true });
5666
8140
  logger.info(`Creating backup: ${backupPath}`);
5667
8141
  const files = [];
5668
8142
  const backupItems = [
@@ -5683,8 +8157,8 @@ var BackupManager = class {
5683
8157
  files,
5684
8158
  success: true
5685
8159
  };
5686
- const manifestPath = join15(backupPath, "backup-manifest.json");
5687
- await writeFile10(manifestPath, JSON.stringify(manifest, null, 2));
8160
+ const manifestPath = join18(backupPath, "backup-manifest.json");
8161
+ await writeFile13(manifestPath, JSON.stringify(manifest, null, 2));
5688
8162
  await this.cleanupOldBackups();
5689
8163
  logger.success(`\u2713 Backup created: ${backupId}`);
5690
8164
  return backupId;
@@ -5697,20 +8171,20 @@ var BackupManager = class {
5697
8171
  * Backup a file or directory
5698
8172
  */
5699
8173
  async backupItem(sourceDir, item, targetDir) {
5700
- const sourcePath = join15(sourceDir, item);
5701
- const targetPath = join15(targetDir, item);
8174
+ const sourcePath = join18(sourceDir, item);
8175
+ const targetPath = join18(targetDir, item);
5702
8176
  const files = [];
5703
8177
  try {
5704
8178
  const stats = await stat2(sourcePath);
5705
8179
  if (stats.isDirectory()) {
5706
- await mkdir9(targetPath, { recursive: true });
5707
- const entries = await readdir8(sourcePath);
8180
+ await mkdir12(targetPath, { recursive: true });
8181
+ const entries = await readdir9(sourcePath);
5708
8182
  for (const entry of entries) {
5709
8183
  const entryFiles = await this.backupItem(sourcePath, entry, targetPath);
5710
8184
  files.push(...entryFiles);
5711
8185
  }
5712
8186
  } else if (stats.isFile()) {
5713
- await mkdir9(dirname3(targetPath), { recursive: true });
8187
+ await mkdir12(dirname3(targetPath), { recursive: true });
5714
8188
  await this.copyFile(sourcePath, targetPath);
5715
8189
  const hash = await this.calculateHash(targetPath);
5716
8190
  files.push({
@@ -5728,15 +8202,15 @@ var BackupManager = class {
5728
8202
  * Copy file with hash calculation
5729
8203
  */
5730
8204
  async copyFile(source, target) {
5731
- const content = await readFile9(source);
5732
- await writeFile10(target, content);
8205
+ const content = await readFile11(source);
8206
+ await writeFile13(target, content);
5733
8207
  }
5734
8208
  /**
5735
8209
  * Calculate file hash
5736
8210
  */
5737
8211
  async calculateHash(filePath) {
5738
8212
  try {
5739
- const content = await readFile9(filePath);
8213
+ const content = await readFile11(filePath);
5740
8214
  return createHash2("sha256").update(content).digest("hex");
5741
8215
  } catch {
5742
8216
  return "";
@@ -5747,13 +8221,13 @@ var BackupManager = class {
5747
8221
  */
5748
8222
  async listBackups() {
5749
8223
  try {
5750
- const entries = await readdir8(this.backupsDir);
8224
+ const entries = await readdir9(this.backupsDir);
5751
8225
  const backups = [];
5752
8226
  for (const entry of entries) {
5753
- const backupPath = join15(this.backupsDir, entry);
5754
- const manifestPath = join15(backupPath, "backup-manifest.json");
8227
+ const backupPath = join18(this.backupsDir, entry);
8228
+ const manifestPath = join18(backupPath, "backup-manifest.json");
5755
8229
  try {
5756
- const manifestContent = await readFile9(manifestPath, "utf-8");
8230
+ const manifestContent = await readFile11(manifestPath, "utf-8");
5757
8231
  const manifest = JSON.parse(manifestContent);
5758
8232
  const size = await this.calculateBackupSize(backupPath);
5759
8233
  backups.push({
@@ -5779,9 +8253,9 @@ var BackupManager = class {
5779
8253
  let totalSize = 0;
5780
8254
  try {
5781
8255
  const calculate = async (dir) => {
5782
- const entries = await readdir8(dir);
8256
+ const entries = await readdir9(dir);
5783
8257
  for (const entry of entries) {
5784
- const entryPath = join15(dir, entry);
8258
+ const entryPath = join18(dir, entry);
5785
8259
  const stats = await stat2(entryPath);
5786
8260
  if (stats.isDirectory()) {
5787
8261
  await calculate(entryPath);
@@ -5813,9 +8287,9 @@ var BackupManager = class {
5813
8287
  return false;
5814
8288
  }
5815
8289
  for (const file of backup.manifest.files) {
5816
- const sourcePath = join15(backup.path, file.path);
5817
- const targetPath = join15(this.configPath, file.path);
5818
- await mkdir9(dirname3(targetPath), { recursive: true });
8290
+ const sourcePath = join18(backup.path, file.path);
8291
+ const targetPath = join18(this.configPath, file.path);
8292
+ await mkdir12(dirname3(targetPath), { recursive: true });
5819
8293
  await this.copyFile(sourcePath, targetPath);
5820
8294
  }
5821
8295
  logger.success(`\u2713 Backup restored: ${backupId}`);
@@ -5830,10 +8304,10 @@ var BackupManager = class {
5830
8304
  */
5831
8305
  async validateBackup(backup) {
5832
8306
  try {
5833
- const manifestPath = join15(backup.path, "backup-manifest.json");
5834
- await readFile9(manifestPath, "utf-8");
8307
+ const manifestPath = join18(backup.path, "backup-manifest.json");
8308
+ await readFile11(manifestPath, "utf-8");
5835
8309
  for (const file of backup.manifest.files) {
5836
- const filePath = join15(backup.path, file.path);
8310
+ const filePath = join18(backup.path, file.path);
5837
8311
  await stat2(filePath);
5838
8312
  const currentHash = await this.calculateHash(filePath);
5839
8313
  if (currentHash !== file.hash) {
@@ -5857,9 +8331,9 @@ var BackupManager = class {
5857
8331
  if (!backup) {
5858
8332
  return false;
5859
8333
  }
5860
- const entries = await readdir8(backup.path);
8334
+ const entries = await readdir9(backup.path);
5861
8335
  for (const entry of entries) {
5862
- const entryPath = join15(backup.path, entry);
8336
+ const entryPath = join18(backup.path, entry);
5863
8337
  const stats = await stat2(entryPath);
5864
8338
  if (stats.isDirectory()) {
5865
8339
  await this.removeDirectory(entryPath);
@@ -5878,9 +8352,9 @@ var BackupManager = class {
5878
8352
  * Remove directory recursively
5879
8353
  */
5880
8354
  async removeDirectory(dirPath) {
5881
- const entries = await readdir8(dirPath);
8355
+ const entries = await readdir9(dirPath);
5882
8356
  for (const entry of entries) {
5883
- const entryPath = join15(dirPath, entry);
8357
+ const entryPath = join18(dirPath, entry);
5884
8358
  const stats = await stat2(entryPath);
5885
8359
  if (stats.isDirectory()) {
5886
8360
  await this.removeDirectory(entryPath);
@@ -5926,14 +8400,14 @@ var BackupManager = class {
5926
8400
  // src/core/migration-manager.ts
5927
8401
  init_esm_shims();
5928
8402
  init_logger();
5929
- import { readFile as readFile10, writeFile as writeFile11, readdir as readdir9 } from "fs/promises";
5930
- import { join as join16 } from "path";
8403
+ import { readFile as readFile12, writeFile as writeFile14, readdir as readdir10 } from "fs/promises";
8404
+ import { join as join19 } from "path";
5931
8405
  var MigrationManager = class {
5932
8406
  configPath;
5933
8407
  migrationsDir;
5934
8408
  constructor(configPath) {
5935
8409
  this.configPath = configPath;
5936
- this.migrationsDir = join16(process.cwd(), "src/core/migrations");
8410
+ this.migrationsDir = join19(process.cwd(), "src/core/migrations");
5937
8411
  }
5938
8412
  /**
5939
8413
  * Load all available migrations
@@ -5941,11 +8415,11 @@ var MigrationManager = class {
5941
8415
  async loadMigrations() {
5942
8416
  const migrations = [];
5943
8417
  try {
5944
- const files = await readdir9(this.migrationsDir);
8418
+ const files = await readdir10(this.migrationsDir);
5945
8419
  for (const file of files) {
5946
8420
  if (file.endsWith(".js") && file.startsWith("migrate-")) {
5947
8421
  try {
5948
- const module = await import(join16(this.migrationsDir, file));
8422
+ const module = await import(join19(this.migrationsDir, file));
5949
8423
  const migration = module.default || module.migration;
5950
8424
  if (migration) {
5951
8425
  migrations.push(migration);
@@ -5964,9 +8438,9 @@ var MigrationManager = class {
5964
8438
  * Get applied migrations
5965
8439
  */
5966
8440
  async getAppliedMigrations() {
5967
- const migrationHistoryPath = join16(this.configPath, ".migration-history.json");
8441
+ const migrationHistoryPath = join19(this.configPath, ".migration-history.json");
5968
8442
  try {
5969
- const content = await readFile10(migrationHistoryPath, "utf-8");
8443
+ const content = await readFile12(migrationHistoryPath, "utf-8");
5970
8444
  const history = JSON.parse(content);
5971
8445
  return history.filter((m) => m.status === "completed").map((m) => m.to);
5972
8446
  } catch {
@@ -6052,16 +8526,16 @@ var MigrationManager = class {
6052
8526
  * Update migration history
6053
8527
  */
6054
8528
  async updateMigrationHistory(entries) {
6055
- const historyPath = join16(this.configPath, ".migration-history.json");
8529
+ const historyPath = join19(this.configPath, ".migration-history.json");
6056
8530
  try {
6057
8531
  let history = [];
6058
8532
  try {
6059
- const content = await readFile10(historyPath, "utf-8");
8533
+ const content = await readFile12(historyPath, "utf-8");
6060
8534
  history = JSON.parse(content);
6061
8535
  } catch {
6062
8536
  }
6063
8537
  history.push(...entries);
6064
- await writeFile11(historyPath, JSON.stringify(history, null, 2));
8538
+ await writeFile14(historyPath, JSON.stringify(history, null, 2));
6065
8539
  } catch (error) {
6066
8540
  logger.error("Failed to update migration history:", error);
6067
8541
  }
@@ -6070,14 +8544,14 @@ var MigrationManager = class {
6070
8544
  * Update migration history status
6071
8545
  */
6072
8546
  async updateMigrationHistoryStatus(version, status) {
6073
- const historyPath = join16(this.configPath, ".migration-history.json");
8547
+ const historyPath = join19(this.configPath, ".migration-history.json");
6074
8548
  try {
6075
- const content = await readFile10(historyPath, "utf-8");
8549
+ const content = await readFile12(historyPath, "utf-8");
6076
8550
  const history = JSON.parse(content);
6077
8551
  const updated = history.map(
6078
8552
  (m) => m.to === version ? { ...m, status } : m
6079
8553
  );
6080
- await writeFile11(historyPath, JSON.stringify(updated, null, 2));
8554
+ await writeFile14(historyPath, JSON.stringify(updated, null, 2));
6081
8555
  } catch (error) {
6082
8556
  logger.error("Failed to update migration history status:", error);
6083
8557
  }
@@ -6086,9 +8560,9 @@ var MigrationManager = class {
6086
8560
  * Get migration history
6087
8561
  */
6088
8562
  async getMigrationHistory() {
6089
- const historyPath = join16(this.configPath, ".migration-history.json");
8563
+ const historyPath = join19(this.configPath, ".migration-history.json");
6090
8564
  try {
6091
- const content = await readFile10(historyPath, "utf-8");
8565
+ const content = await readFile12(historyPath, "utf-8");
6092
8566
  return JSON.parse(content);
6093
8567
  } catch {
6094
8568
  return [];
@@ -6176,7 +8650,7 @@ var SyncEngine = class {
6176
8650
  }
6177
8651
  await this.displayChanges(changes);
6178
8652
  if (!options.force) {
6179
- const { confirmed } = await inquirer2.prompt([{
8653
+ const { confirmed } = await inquirer3.prompt([{
6180
8654
  type: "confirm",
6181
8655
  name: "confirmed",
6182
8656
  message: "Continue with update?",
@@ -6257,7 +8731,7 @@ var SyncEngine = class {
6257
8731
  console.log(chalk3.yellow("No backups available"));
6258
8732
  return false;
6259
8733
  }
6260
- const { selectedBackup } = await inquirer2.prompt([{
8734
+ const { selectedBackup } = await inquirer3.prompt([{
6261
8735
  type: "list",
6262
8736
  name: "selectedBackup",
6263
8737
  message: "Select backup to restore:",
@@ -6330,7 +8804,7 @@ var SyncEngine = class {
6330
8804
  \u26A0\uFE0F Conflict detected: ${conflict.skillName}
6331
8805
  `));
6332
8806
  console.log(chalk3.yellow("Your version differs from official version."));
6333
- const { action } = await inquirer2.prompt([{
8807
+ const { action } = await inquirer3.prompt([{
6334
8808
  type: "list",
6335
8809
  name: "action",
6336
8810
  message: "Choose action:",
@@ -6397,19 +8871,19 @@ var SyncEngine = class {
6397
8871
  * Install a skill
6398
8872
  */
6399
8873
  async installSkill(sourceDir, skill, targetDir) {
6400
- const sourcePath = join17(sourceDir, skill.category, `${skill.name}.md`);
6401
- const targetPath = join17(targetDir, skill.category, `${skill.name}.md`);
6402
- await mkdir10(dirname4(targetPath), { recursive: true });
8874
+ const sourcePath = join20(sourceDir, skill.category, `${skill.name}.md`);
8875
+ const targetPath = join20(targetDir, skill.category, `${skill.name}.md`);
8876
+ await mkdir13(dirname4(targetPath), { recursive: true });
6403
8877
  await copyFile(sourcePath, targetPath);
6404
8878
  }
6405
8879
  /**
6406
8880
  * Archive a removed skill
6407
8881
  */
6408
8882
  async archiveSkill(targetDir, skill) {
6409
- const sourcePath = join17(targetDir, skill.category, `${skill.name}.md`);
6410
- const targetPath = join17(targetDir, skill.category, `${skill.name}-deprecated.md`);
8883
+ const sourcePath = join20(targetDir, skill.category, `${skill.name}.md`);
8884
+ const targetPath = join20(targetDir, skill.category, `${skill.name}-deprecated.md`);
6411
8885
  try {
6412
- const content = await readFile11(sourcePath, "utf-8");
8886
+ const content = await readFile13(sourcePath, "utf-8");
6413
8887
  const deprecatedNotice = `---
6414
8888
  \u26A0\uFE0F DEPRECATED: This skill has been removed
6415
8889
 
@@ -6418,8 +8892,8 @@ Reason: Check release notes for replacement
6418
8892
  ---
6419
8893
 
6420
8894
  ${content}`;
6421
- await mkdir10(dirname4(targetPath), { recursive: true });
6422
- await writeFile12(targetPath, deprecatedNotice);
8895
+ await mkdir13(dirname4(targetPath), { recursive: true });
8896
+ await writeFile15(targetPath, deprecatedNotice);
6423
8897
  } catch (error) {
6424
8898
  if (error.code === "ENOENT") {
6425
8899
  console.log(chalk3.yellow(` - ${skill.name} (not found, skipping)`));
@@ -6650,8 +9124,8 @@ async function configureToolAction(toolName) {
6650
9124
  // src/cli/commands/misc.ts
6651
9125
  init_esm_shims();
6652
9126
  import chalk6 from "chalk";
6653
- import { readFile as readFile13, writeFile as writeFile14 } from "fs/promises";
6654
- import { join as join19 } from "path";
9127
+ import { readFile as readFile15, writeFile as writeFile17 } from "fs/promises";
9128
+ import { join as join22 } from "path";
6655
9129
  init_config();
6656
9130
  init_memory();
6657
9131
  init_beads();
@@ -6714,9 +9188,9 @@ function registerModeCommand(program2) {
6714
9188
  console.log(chalk6.red(`Invalid mode. Available modes: ${validModes.join(", ")}`));
6715
9189
  return;
6716
9190
  }
6717
- const configData = JSON.parse(await readFile13(join19(configPath, "aikit.json"), "utf-8"));
9191
+ const configData = JSON.parse(await readFile15(join22(configPath, "aikit.json"), "utf-8"));
6718
9192
  configData.mode = mode;
6719
- await writeFile14(join19(configPath, "aikit.json"), JSON.stringify(configData, null, 2));
9193
+ await writeFile17(join22(configPath, "aikit.json"), JSON.stringify(configData, null, 2));
6720
9194
  console.log(chalk6.green(`\u2713 Mode set to: ${mode}`));
6721
9195
  console.log(chalk6.gray(`Configuration updated at: ${configPath}/aikit.json`));
6722
9196
  } catch (error) {
@@ -6822,8 +9296,36 @@ function registerStatusCommand(program2) {
6822
9296
  });
6823
9297
  }
6824
9298
 
9299
+ // src/cli/commands/session.ts
9300
+ init_esm_shims();
9301
+ function registerSessionCommand(program2) {
9302
+ const sessionCmd = program2.command("session").description("Manage development sessions");
9303
+ sessionCmd.command("start [name]").description("Start a new development session").option("-g, --goals <goals...>", "Session goals").action(async (name, options) => {
9304
+ await startSession(name, options.goals);
9305
+ });
9306
+ sessionCmd.command("update [notes]").description("Add progress notes to current session").action(async (notes) => {
9307
+ await updateSession(notes);
9308
+ });
9309
+ sessionCmd.command("end").description("End current session with summary").action(async () => {
9310
+ await endSession();
9311
+ });
9312
+ sessionCmd.command("current").description("Show current active session").action(async () => {
9313
+ await showCurrentSession();
9314
+ });
9315
+ sessionCmd.command("list").description("List all sessions").action(async () => {
9316
+ await listSessions();
9317
+ });
9318
+ sessionCmd.command("show <sessionId>").description("Show details of a specific session").action(async (sessionId) => {
9319
+ await showSession(sessionId);
9320
+ });
9321
+ sessionCmd.command("search <query>").description("Search sessions by keyword").action(async (query) => {
9322
+ await searchSessions(query);
9323
+ });
9324
+ return sessionCmd;
9325
+ }
9326
+
6825
9327
  // src/cli/index.ts
6826
- var program = new Command();
9328
+ var program = new Command3();
6827
9329
  program.name("aikit").description("Open-source AI coding agent toolkit for OpenCode").version(getVersion());
6828
9330
  registerInitCommand(program);
6829
9331
  registerInstallCommand(program);
@@ -6836,6 +9338,7 @@ registerToolsCommand(program);
6836
9338
  registerPluginsCommand(program);
6837
9339
  registerMemoryCommand(program);
6838
9340
  registerBeadsCommand(program);
9341
+ registerSessionCommand(program);
6839
9342
  registerStatusCommand(program);
6840
9343
  program.parse();
6841
9344
  //# sourceMappingURL=cli.js.map