slait.dev 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +149 -0
  2. package/dist/commands/config.d.ts +2 -0
  3. package/dist/commands/config.js +22 -0
  4. package/dist/commands/config.js.map +1 -0
  5. package/dist/commands/hooks.d.ts +17 -0
  6. package/dist/commands/hooks.js +372 -0
  7. package/dist/commands/hooks.js.map +1 -0
  8. package/dist/commands/init.d.ts +3 -0
  9. package/dist/commands/init.js +41 -0
  10. package/dist/commands/init.js.map +1 -0
  11. package/dist/commands/setup.d.ts +7 -0
  12. package/dist/commands/setup.js +85 -0
  13. package/dist/commands/setup.js.map +1 -0
  14. package/dist/commands/status.d.ts +1 -0
  15. package/dist/commands/status.js +29 -0
  16. package/dist/commands/status.js.map +1 -0
  17. package/dist/commands/upload.d.ts +6 -0
  18. package/dist/commands/upload.js +75 -0
  19. package/dist/commands/upload.js.map +1 -0
  20. package/dist/index.d.ts +2 -0
  21. package/dist/index.js +89 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/lib/api.d.ts +62 -0
  24. package/dist/lib/api.js +92 -0
  25. package/dist/lib/api.js.map +1 -0
  26. package/dist/lib/claude-artifacts.d.ts +18 -0
  27. package/dist/lib/claude-artifacts.js +181 -0
  28. package/dist/lib/claude-artifacts.js.map +1 -0
  29. package/dist/lib/claude-hooks.d.ts +13 -0
  30. package/dist/lib/claude-hooks.js +87 -0
  31. package/dist/lib/claude-hooks.js.map +1 -0
  32. package/dist/lib/config.d.ts +10 -0
  33. package/dist/lib/config.js +52 -0
  34. package/dist/lib/config.js.map +1 -0
  35. package/dist/lib/cursor-artifacts.d.ts +13 -0
  36. package/dist/lib/cursor-artifacts.js +83 -0
  37. package/dist/lib/cursor-artifacts.js.map +1 -0
  38. package/dist/lib/cursor-hooks.d.ts +14 -0
  39. package/dist/lib/cursor-hooks.js +112 -0
  40. package/dist/lib/cursor-hooks.js.map +1 -0
  41. package/dist/lib/discover.d.ts +8 -0
  42. package/dist/lib/discover.js +65 -0
  43. package/dist/lib/discover.js.map +1 -0
  44. package/dist/lib/format.d.ts +17 -0
  45. package/dist/lib/format.js +134 -0
  46. package/dist/lib/format.js.map +1 -0
  47. package/dist/lib/project-link.d.ts +11 -0
  48. package/dist/lib/project-link.js +32 -0
  49. package/dist/lib/project-link.js.map +1 -0
  50. package/dist/lib/session-events.d.ts +8 -0
  51. package/dist/lib/session-events.js +44 -0
  52. package/dist/lib/session-events.js.map +1 -0
  53. package/dist/lib/state.d.ts +11 -0
  54. package/dist/lib/state.js +31 -0
  55. package/dist/lib/state.js.map +1 -0
  56. package/dist/lib/transcript.d.ts +15 -0
  57. package/dist/lib/transcript.js +44 -0
  58. package/dist/lib/transcript.js.map +1 -0
  59. package/package.json +40 -0
@@ -0,0 +1,181 @@
1
+ import { readFileSync, existsSync } from "fs";
2
+ import { join } from "path";
3
+ import { homedir } from "os";
4
+ const MAX_FILE_SIZE = 50_000;
5
+ function safeRead(path) {
6
+ try {
7
+ const content = readFileSync(path, "utf-8");
8
+ return content.length > MAX_FILE_SIZE
9
+ ? content.slice(0, MAX_FILE_SIZE) + "\n…[truncated]"
10
+ : content;
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ }
16
+ /**
17
+ * Read CLAUDE.md from the project workspace.
18
+ * Checks both project root and .claude/ subdirectory.
19
+ */
20
+ function readClaudeMd(workspacePath) {
21
+ const paths = [
22
+ join(workspacePath, "CLAUDE.md"),
23
+ join(workspacePath, ".claude", "CLAUDE.md"),
24
+ ];
25
+ for (const p of paths) {
26
+ const content = safeRead(p);
27
+ if (content?.trim())
28
+ return content;
29
+ }
30
+ return null;
31
+ }
32
+ /**
33
+ * Parse .claude/settings.json (project-level) for enabled permissions,
34
+ * allowed tools, and MCP servers which indicate plugins in use.
35
+ */
36
+ function parseClaudeSettings(workspacePath) {
37
+ const settingsPath = join(workspacePath, ".claude", "settings.json");
38
+ try {
39
+ if (!existsSync(settingsPath))
40
+ return { mcpServers: [], allowedTools: [] };
41
+ const raw = JSON.parse(readFileSync(settingsPath, "utf-8"));
42
+ const mcpServers = [];
43
+ if (raw.mcpServers && typeof raw.mcpServers === "object") {
44
+ mcpServers.push(...Object.keys(raw.mcpServers));
45
+ }
46
+ const allowedTools = [];
47
+ if (Array.isArray(raw.allowedTools)) {
48
+ for (const tool of raw.allowedTools) {
49
+ if (typeof tool === "string")
50
+ allowedTools.push(tool);
51
+ }
52
+ }
53
+ return { mcpServers, allowedTools };
54
+ }
55
+ catch {
56
+ return { mcpServers: [], allowedTools: [] };
57
+ }
58
+ }
59
+ /**
60
+ * Parse global ~/.claude/settings.json for user-level skills and plugins.
61
+ */
62
+ function parseGlobalClaudeSettings() {
63
+ const globalPath = join(homedir(), ".claude", "settings.json");
64
+ try {
65
+ if (!existsSync(globalPath))
66
+ return { mcpServers: [], allowedTools: [] };
67
+ const raw = JSON.parse(readFileSync(globalPath, "utf-8"));
68
+ const mcpServers = [];
69
+ if (raw.mcpServers && typeof raw.mcpServers === "object") {
70
+ mcpServers.push(...Object.keys(raw.mcpServers));
71
+ }
72
+ const allowedTools = [];
73
+ if (Array.isArray(raw.allowedTools)) {
74
+ for (const tool of raw.allowedTools) {
75
+ if (typeof tool === "string")
76
+ allowedTools.push(tool);
77
+ }
78
+ }
79
+ return { mcpServers, allowedTools };
80
+ }
81
+ catch {
82
+ return { mcpServers: [], allowedTools: [] };
83
+ }
84
+ }
85
+ /**
86
+ * Extract skills/plugins used from a Claude Code JSONL transcript.
87
+ * Looks for tool_use blocks referencing known skill patterns.
88
+ */
89
+ export function extractSkillsFromTranscript(rawJsonl) {
90
+ const skills = new Set();
91
+ const skillPatterns = [
92
+ /SKILL\.md/i,
93
+ /skills?[/-][\w-]+/i,
94
+ /plugin[/-][\w-]+/i,
95
+ ];
96
+ const mcpPattern = /mcp__(\w+)__(\w+)/;
97
+ for (const line of rawJsonl.split("\n")) {
98
+ if (!line.trim())
99
+ continue;
100
+ try {
101
+ const parsed = JSON.parse(line);
102
+ const content = parsed?.message?.content;
103
+ if (!Array.isArray(content))
104
+ continue;
105
+ for (const block of content) {
106
+ if (block.type === "tool_use" && typeof block.name === "string") {
107
+ const mcpMatch = block.name.match(mcpPattern);
108
+ if (mcpMatch) {
109
+ skills.add(`MCP: ${mcpMatch[1]}`);
110
+ }
111
+ if (typeof block.input === "object" && block.input) {
112
+ const inputStr = JSON.stringify(block.input);
113
+ for (const pat of skillPatterns) {
114
+ const match = inputStr.match(pat);
115
+ if (match)
116
+ skills.add(match[0]);
117
+ }
118
+ }
119
+ }
120
+ if (block.type === "text" && typeof block.text === "string") {
121
+ for (const pat of skillPatterns) {
122
+ const match = block.text.match(pat);
123
+ if (match)
124
+ skills.add(match[0]);
125
+ }
126
+ }
127
+ }
128
+ }
129
+ catch {
130
+ // skip malformed lines
131
+ }
132
+ }
133
+ return [...skills];
134
+ }
135
+ /**
136
+ * Collect all Claude supplementary artifacts for a workspace.
137
+ * Returns both supplementary files (for pipeline) and config metadata.
138
+ */
139
+ export function readClaudeArtifacts(workspacePath, rawTranscript) {
140
+ const supplementaryFiles = {};
141
+ const configMetadata = {
142
+ claudeMdPresent: false,
143
+ skillsUsed: [],
144
+ pluginsUsed: [],
145
+ };
146
+ const claudeMd = readClaudeMd(workspacePath);
147
+ if (claudeMd) {
148
+ supplementaryFiles["CLAUDE.md"] = claudeMd;
149
+ configMetadata.claudeMdPresent = true;
150
+ }
151
+ const projectSettings = parseClaudeSettings(workspacePath);
152
+ const globalSettings = parseGlobalClaudeSettings();
153
+ const allMcpServers = [...new Set([...projectSettings.mcpServers, ...globalSettings.mcpServers])];
154
+ const allTools = [...new Set([...projectSettings.allowedTools, ...globalSettings.allowedTools])];
155
+ if (allMcpServers.length > 0) {
156
+ configMetadata.pluginsUsed.push(...allMcpServers.map((s) => `MCP: ${s}`));
157
+ }
158
+ if (allTools.length > 0) {
159
+ configMetadata.skillsUsed.push(...allTools.filter((t) => /skill|plugin/i.test(t)));
160
+ }
161
+ if (rawTranscript) {
162
+ const transcriptSkills = extractSkillsFromTranscript(rawTranscript);
163
+ for (const skill of transcriptSkills) {
164
+ if (!configMetadata.skillsUsed.includes(skill) && !configMetadata.pluginsUsed.includes(skill)) {
165
+ configMetadata.skillsUsed.push(skill);
166
+ }
167
+ }
168
+ }
169
+ // Include settings summary as a supplementary file if meaningful
170
+ if (allMcpServers.length > 0 || allTools.length > 0) {
171
+ const settingsSummary = [
172
+ allMcpServers.length > 0 ? `MCP Servers: ${allMcpServers.join(", ")}` : "",
173
+ allTools.length > 0 ? `Allowed Tools: ${allTools.join(", ")}` : "",
174
+ ]
175
+ .filter(Boolean)
176
+ .join("\n");
177
+ supplementaryFiles["Claude Settings Summary"] = settingsSummary;
178
+ }
179
+ return { supplementaryFiles, configMetadata };
180
+ }
181
+ //# sourceMappingURL=claude-artifacts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-artifacts.js","sourceRoot":"","sources":["../../src/lib/claude-artifacts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Install slait hooks into Claude Code settings.
3
+ * Uses project-level .claude/settings.json if projectRoot is provided.
4
+ */
5
+ export declare function installClaudeHooks(projectRoot: string): number;
6
+ /**
7
+ * Remove slait hooks from Claude Code settings.
8
+ */
9
+ export declare function uninstallClaudeHooks(projectRoot: string): boolean;
10
+ /**
11
+ * Check if slait hooks are installed in Claude Code settings.
12
+ */
13
+ export declare function areClaudeHooksInstalled(projectRoot: string): boolean;
@@ -0,0 +1,87 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
2
+ import { join, dirname } from "path";
3
+ import { homedir } from "os";
4
+ const SLAIT_PREFIX = "slait ";
5
+ const HOOK_EVENTS = [
6
+ "PreToolUse",
7
+ "PostToolUse",
8
+ "Stop",
9
+ ];
10
+ function settingsFilePath(projectRoot) {
11
+ if (projectRoot) {
12
+ return join(projectRoot, ".claude", "settings.json");
13
+ }
14
+ return join(homedir(), ".claude", "settings.json");
15
+ }
16
+ function isSlaitCommand(cmd) {
17
+ return cmd.startsWith(SLAIT_PREFIX);
18
+ }
19
+ /**
20
+ * Install slait hooks into Claude Code settings.
21
+ * Uses project-level .claude/settings.json if projectRoot is provided.
22
+ */
23
+ export function installClaudeHooks(projectRoot) {
24
+ const settingsPath = settingsFilePath(projectRoot);
25
+ let settings = {};
26
+ if (existsSync(settingsPath)) {
27
+ try {
28
+ settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
29
+ }
30
+ catch {
31
+ // Start fresh
32
+ }
33
+ }
34
+ const hooks = (settings.hooks ?? []);
35
+ const slaitEntry = {
36
+ matcher: "",
37
+ hooks: {
38
+ Stop: [{ type: "command", command: "slait hooks handle claude stop" }],
39
+ },
40
+ };
41
+ const hasSlait = hooks.some((entry) => Object.values(entry.hooks).some((cmds) => cmds.some((c) => isSlaitCommand(c.command))));
42
+ if (hasSlait)
43
+ return 0;
44
+ hooks.push(slaitEntry);
45
+ settings.hooks = hooks;
46
+ mkdirSync(dirname(settingsPath), { recursive: true });
47
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
48
+ return 1;
49
+ }
50
+ /**
51
+ * Remove slait hooks from Claude Code settings.
52
+ */
53
+ export function uninstallClaudeHooks(projectRoot) {
54
+ const settingsPath = settingsFilePath(projectRoot);
55
+ if (!existsSync(settingsPath))
56
+ return false;
57
+ try {
58
+ const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
59
+ const hooks = (settings.hooks ?? []);
60
+ const filtered = hooks.filter((entry) => !Object.values(entry.hooks).some((cmds) => cmds.some((c) => isSlaitCommand(c.command))));
61
+ if (filtered.length === hooks.length)
62
+ return false;
63
+ settings.hooks = filtered;
64
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
65
+ return true;
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ }
71
+ /**
72
+ * Check if slait hooks are installed in Claude Code settings.
73
+ */
74
+ export function areClaudeHooksInstalled(projectRoot) {
75
+ const settingsPath = settingsFilePath(projectRoot);
76
+ if (!existsSync(settingsPath))
77
+ return false;
78
+ try {
79
+ const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
80
+ const hooks = (settings.hooks ?? []);
81
+ return hooks.some((entry) => Object.values(entry.hooks).some((cmds) => cmds.some((c) => isSlaitCommand(c.command))));
82
+ }
83
+ catch {
84
+ return false;
85
+ }
86
+ }
87
+ //# sourceMappingURL=claude-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-hooks.js","sourceRoot":"","sources":["../../src/lib/claude-hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export interface SlaitConfig {
2
+ api_key?: string;
3
+ base_url?: string;
4
+ }
5
+ export declare function getConfigDir(): string;
6
+ export declare function readConfig(): SlaitConfig;
7
+ export declare function writeConfig(config: SlaitConfig): void;
8
+ export declare function getBaseUrl(): string;
9
+ export declare function getApiKey(): string | undefined;
10
+ export declare function requireApiKey(): string;
@@ -0,0 +1,52 @@
1
+ import { readFileSync, writeFileSync, mkdirSync } from "fs";
2
+ import { join, dirname } from "path";
3
+ import { homedir } from "os";
4
+ const CONFIG_DIR = join(homedir(), ".slait");
5
+ const CONFIG_FILE = join(CONFIG_DIR, "config.json");
6
+ const DEFAULT_BASE_URL = "https://slait.dev";
7
+ const LEGACY_DEFAULT_BASE_URLS = new Set([
8
+ "http://localhost:3001",
9
+ "http://localhost:3000",
10
+ "http://127.0.0.1:3001",
11
+ "http://127.0.0.1:3000",
12
+ ]);
13
+ export function getConfigDir() {
14
+ return CONFIG_DIR;
15
+ }
16
+ export function readConfig() {
17
+ try {
18
+ return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
19
+ }
20
+ catch {
21
+ return {};
22
+ }
23
+ }
24
+ export function writeConfig(config) {
25
+ mkdirSync(dirname(CONFIG_FILE), { recursive: true });
26
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n");
27
+ }
28
+ export function getBaseUrl() {
29
+ const config = readConfig();
30
+ const configuredBaseUrl = config.base_url?.trim().replace(/\/$/, "");
31
+ if (!configuredBaseUrl) {
32
+ return DEFAULT_BASE_URL;
33
+ }
34
+ // Migrate legacy local defaults from earlier CLI versions.
35
+ if (LEGACY_DEFAULT_BASE_URLS.has(configuredBaseUrl)) {
36
+ writeConfig({ ...config, base_url: DEFAULT_BASE_URL });
37
+ return DEFAULT_BASE_URL;
38
+ }
39
+ return configuredBaseUrl;
40
+ }
41
+ export function getApiKey() {
42
+ return readConfig().api_key?.trim() || undefined;
43
+ }
44
+ export function requireApiKey() {
45
+ const key = getApiKey();
46
+ if (!key) {
47
+ console.error("API key not configured. Run: slait config set-key <your_api_key>");
48
+ process.exit(1);
49
+ }
50
+ return key;
51
+ }
52
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAA;AAC3D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAA;AAO5B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAA;AAC5C,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAA;AACnD,MAAM,gBAAgB,GAAG,mBAAmB,CAAA;AAC5C,MAAM,wBAAwB,GAAG,IAAI,GAAG,CAAC;IACvC,uBAAuB;IACvB,uBAAuB;IACvB,uBAAuB;IACvB,uBAAuB;CACxB,CAAC,CAAA;AAEF,MAAM,UAAU,YAAY;IAC1B,OAAO,UAAU,CAAA;AACnB,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAgB,CAAA;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAmB;IAC7C,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACpD,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;AACpE,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,MAAM,iBAAiB,GAAG,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAEpE,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,OAAO,gBAAgB,CAAA;IACzB,CAAC;IAED,2DAA2D;IAC3D,IAAI,wBAAwB,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACpD,WAAW,CAAC,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC,CAAA;QACtD,OAAO,gBAAgB,CAAA;IACzB,CAAC;IAED,OAAO,iBAAiB,CAAA;AAC1B,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,UAAU,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,SAAS,CAAA;AAClD,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,GAAG,GAAG,SAAS,EAAE,CAAA;IACvB,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CAAC,KAAK,CAAC,kEAAkE,CAAC,CAAA;QACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Read Cursor plan files from both project-local and Cursor's global project dir.
3
+ */
4
+ export declare function readCursorPlans(workspacePath: string): Record<string, string>;
5
+ /**
6
+ * Read Cursor rule files from the project workspace.
7
+ * Looks for .cursor/rules/, AGENTS.md, and .cursorrules.
8
+ */
9
+ export declare function readCursorRules(workspacePath: string): Record<string, string>;
10
+ /**
11
+ * Collect all Cursor supplementary artifacts for a workspace.
12
+ */
13
+ export declare function readCursorArtifacts(workspacePath: string): Record<string, string>;
@@ -0,0 +1,83 @@
1
+ import { readdirSync, readFileSync } from "fs";
2
+ import { join } from "path";
3
+ import { homedir } from "os";
4
+ import { sanitizePathForCursor } from "./transcript.js";
5
+ const MAX_FILE_SIZE = 50_000;
6
+ function safeRead(path) {
7
+ try {
8
+ const content = readFileSync(path, "utf-8");
9
+ return content.length > MAX_FILE_SIZE
10
+ ? content.slice(0, MAX_FILE_SIZE) + "\n…[truncated]"
11
+ : content;
12
+ }
13
+ catch {
14
+ return null;
15
+ }
16
+ }
17
+ function readMdFiles(dir, prefix) {
18
+ const result = {};
19
+ try {
20
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
21
+ if (!entry.isFile())
22
+ continue;
23
+ if (!/\.(md|mdc|txt)$/i.test(entry.name))
24
+ continue;
25
+ const content = safeRead(join(dir, entry.name));
26
+ if (content?.trim()) {
27
+ result[`${prefix}${entry.name}`] = content;
28
+ }
29
+ }
30
+ }
31
+ catch {
32
+ // directory doesn't exist or is inaccessible
33
+ }
34
+ return result;
35
+ }
36
+ /**
37
+ * Read Cursor plan files from both project-local and Cursor's global project dir.
38
+ */
39
+ export function readCursorPlans(workspacePath) {
40
+ const result = {};
41
+ // Project-local: <workspace>/.cursor/plans/
42
+ const localPlansDir = join(workspacePath, ".cursor", "plans");
43
+ Object.assign(result, readMdFiles(localPlansDir, "Cursor Plan - "));
44
+ // Global Cursor project dir: ~/.cursor/projects/<sanitized>/plans/
45
+ const sanitized = sanitizePathForCursor(workspacePath);
46
+ const globalPlansDir = join(homedir(), ".cursor", "projects", sanitized, "plans");
47
+ if (globalPlansDir !== localPlansDir) {
48
+ for (const [key, value] of Object.entries(readMdFiles(globalPlansDir, "Cursor Plan - "))) {
49
+ if (!(key in result))
50
+ result[key] = value;
51
+ }
52
+ }
53
+ return result;
54
+ }
55
+ /**
56
+ * Read Cursor rule files from the project workspace.
57
+ * Looks for .cursor/rules/, AGENTS.md, and .cursorrules.
58
+ */
59
+ export function readCursorRules(workspacePath) {
60
+ const result = {};
61
+ // .cursor/rules/ directory
62
+ const rulesDir = join(workspacePath, ".cursor", "rules");
63
+ Object.assign(result, readMdFiles(rulesDir, "Cursor Rule - "));
64
+ // AGENTS.md at project root
65
+ const agentsMd = safeRead(join(workspacePath, "AGENTS.md"));
66
+ if (agentsMd?.trim())
67
+ result["AGENTS.md"] = agentsMd;
68
+ // .cursorrules at project root (legacy format)
69
+ const cursorrules = safeRead(join(workspacePath, ".cursorrules"));
70
+ if (cursorrules?.trim())
71
+ result[".cursorrules"] = cursorrules;
72
+ return result;
73
+ }
74
+ /**
75
+ * Collect all Cursor supplementary artifacts for a workspace.
76
+ */
77
+ export function readCursorArtifacts(workspacePath) {
78
+ return {
79
+ ...readCursorPlans(workspacePath),
80
+ ...readCursorRules(workspacePath),
81
+ };
82
+ }
83
+ //# sourceMappingURL=cursor-artifacts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-artifacts.js","sourceRoot":"","sources":["../../src/lib/cursor-artifacts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Install slait hooks into .cursor/hooks.json.
3
+ * Preserves existing hooks and unknown fields via round-trip JSON.
4
+ */
5
+ export declare function installCursorHooks(projectRoot: string): number;
6
+ /**
7
+ * Remove slait hooks from .cursor/hooks.json.
8
+ * Preserves non-slait hooks and unknown fields.
9
+ */
10
+ export declare function uninstallCursorHooks(projectRoot: string): boolean;
11
+ /**
12
+ * Check if slait hooks are installed in .cursor/hooks.json.
13
+ */
14
+ export declare function areCursorHooksInstalled(projectRoot: string): boolean;
@@ -0,0 +1,112 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
2
+ import { join, dirname } from "path";
3
+ const SLAIT_PREFIX = "slait ";
4
+ const HOOK_EVENTS = [
5
+ "sessionStart",
6
+ "sessionEnd",
7
+ "stop",
8
+ "afterFileEdit",
9
+ "afterShellExecution",
10
+ "postToolUse",
11
+ "postToolUseFailure",
12
+ ];
13
+ function isSlaitHook(entry) {
14
+ return entry.command.startsWith(SLAIT_PREFIX);
15
+ }
16
+ function hooksFilePath(projectRoot) {
17
+ return join(projectRoot, ".cursor", "hooks.json");
18
+ }
19
+ /**
20
+ * Install slait hooks into .cursor/hooks.json.
21
+ * Preserves existing hooks and unknown fields via round-trip JSON.
22
+ */
23
+ export function installCursorHooks(projectRoot) {
24
+ const hooksPath = hooksFilePath(projectRoot);
25
+ let rawFile = { version: 1 };
26
+ let rawHooks = {};
27
+ if (existsSync(hooksPath)) {
28
+ try {
29
+ rawFile = JSON.parse(readFileSync(hooksPath, "utf-8"));
30
+ if (rawFile.hooks && typeof rawFile.hooks === "object") {
31
+ rawHooks = rawFile.hooks;
32
+ }
33
+ }
34
+ catch {
35
+ // Start fresh if parse fails
36
+ }
37
+ }
38
+ if (!rawFile.version)
39
+ rawFile.version = 1;
40
+ const commands = {
41
+ sessionStart: "slait hooks handle cursor session-start",
42
+ sessionEnd: "slait hooks handle cursor session-end",
43
+ stop: "slait hooks handle cursor stop",
44
+ afterFileEdit: "slait hooks handle cursor after-file-edit",
45
+ afterShellExecution: "slait hooks handle cursor after-shell-execution",
46
+ postToolUse: "slait hooks handle cursor post-tool-use",
47
+ postToolUseFailure: "slait hooks handle cursor post-tool-use-failure",
48
+ };
49
+ let count = 0;
50
+ for (const event of HOOK_EVENTS) {
51
+ const existing = rawHooks[event] ?? [];
52
+ const cmd = commands[event];
53
+ if (!existing.some((e) => e.command === cmd)) {
54
+ rawHooks[event] = [...existing, { command: cmd }];
55
+ count++;
56
+ }
57
+ }
58
+ if (count === 0)
59
+ return 0;
60
+ rawFile.hooks = rawHooks;
61
+ mkdirSync(dirname(hooksPath), { recursive: true });
62
+ writeFileSync(hooksPath, JSON.stringify(rawFile, null, 2) + "\n");
63
+ return count;
64
+ }
65
+ /**
66
+ * Remove slait hooks from .cursor/hooks.json.
67
+ * Preserves non-slait hooks and unknown fields.
68
+ */
69
+ export function uninstallCursorHooks(projectRoot) {
70
+ const hooksPath = hooksFilePath(projectRoot);
71
+ if (!existsSync(hooksPath))
72
+ return false;
73
+ let rawFile;
74
+ try {
75
+ rawFile = JSON.parse(readFileSync(hooksPath, "utf-8"));
76
+ }
77
+ catch {
78
+ return false;
79
+ }
80
+ const rawHooks = (rawFile.hooks ?? {});
81
+ let removed = false;
82
+ for (const event of Object.keys(rawHooks)) {
83
+ const before = rawHooks[event].length;
84
+ rawHooks[event] = rawHooks[event].filter((e) => !isSlaitHook(e));
85
+ if (rawHooks[event].length < before)
86
+ removed = true;
87
+ if (rawHooks[event].length === 0)
88
+ delete rawHooks[event];
89
+ }
90
+ rawFile.hooks = rawHooks;
91
+ writeFileSync(hooksPath, JSON.stringify(rawFile, null, 2) + "\n");
92
+ return removed;
93
+ }
94
+ /**
95
+ * Check if slait hooks are installed in .cursor/hooks.json.
96
+ */
97
+ export function areCursorHooksInstalled(projectRoot) {
98
+ const hooksPath = hooksFilePath(projectRoot);
99
+ if (!existsSync(hooksPath))
100
+ return false;
101
+ try {
102
+ const rawFile = JSON.parse(readFileSync(hooksPath, "utf-8"));
103
+ const hooks = rawFile.hooks;
104
+ if (!hooks)
105
+ return false;
106
+ return Object.values(hooks).some((entries) => entries.some((e) => isSlaitHook(e)));
107
+ }
108
+ catch {
109
+ return false;
110
+ }
111
+ }
112
+ //# sourceMappingURL=cursor-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-hooks.js","sourceRoot":"","sources":["../../src/lib/cursor-hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface DiscoveredFile {
2
+ path: string;
3
+ source: "cursor" | "claude";
4
+ mtime: Date;
5
+ sessionId?: string;
6
+ }
7
+ export declare function discoverCursor(projectPath?: string): DiscoveredFile[];
8
+ export declare function discoverClaude(): DiscoveredFile[];