@oro.ad/nuxt-claude-devtools 1.2.0 → 1.3.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 (115) hide show
  1. package/README.md +105 -13
  2. package/dist/client/200.html +1 -1
  3. package/dist/client/404.html +1 -1
  4. package/dist/client/_nuxt/B8uzckkK.js +1 -0
  5. package/dist/client/_nuxt/{CPQSDiF7.js → BAb1fJOF.js} +4 -4
  6. package/dist/client/_nuxt/BB1-kxmm.js +1 -0
  7. package/dist/client/_nuxt/BMZIbUUD.js +1 -0
  8. package/dist/client/_nuxt/BSF2Vz9o.js +1 -0
  9. package/dist/client/_nuxt/BSVkH7b6.js +1 -0
  10. package/dist/client/_nuxt/{08Mb3FOO.js → BYp73eMl.js} +1 -1
  11. package/dist/client/_nuxt/B_BoWmnX.js +1 -0
  12. package/dist/client/_nuxt/BcZxFXBD.js +1 -0
  13. package/dist/client/_nuxt/BflmC3YB.js +1 -0
  14. package/dist/client/_nuxt/BnXQTjo-.js +1 -0
  15. package/dist/client/_nuxt/C--9REmc.js +1 -0
  16. package/dist/client/_nuxt/CDQtmRaX.js +1 -0
  17. package/dist/client/_nuxt/CHeJJZL9.js +1 -0
  18. package/dist/client/_nuxt/{CSlPuO5s.js → COus5Ssl.js} +1 -1
  19. package/dist/client/_nuxt/{o1jjB-UO.js → CPA0s6N9.js} +1 -1
  20. package/dist/client/_nuxt/CRkq21kc.js +1 -0
  21. package/dist/client/_nuxt/Cgba93Y9.js +1 -0
  22. package/dist/client/_nuxt/D2l4TRxW.js +1 -0
  23. package/dist/client/_nuxt/DC_XB519.js +1 -0
  24. package/dist/client/_nuxt/DEys9N1G.js +1 -0
  25. package/dist/client/_nuxt/{b4Upel01.js → DGQ4s7ae.js} +1 -1
  26. package/dist/client/_nuxt/DH8Ugy8E.js +1 -0
  27. package/dist/client/_nuxt/DSt96JPY.js +4 -0
  28. package/dist/client/_nuxt/DbJLoP3G.js +1 -0
  29. package/dist/client/_nuxt/DeGmaFBY.js +1 -0
  30. package/dist/client/_nuxt/DgfRwrFR.js +1 -0
  31. package/dist/client/_nuxt/{CLKqRoht.js → DolUcBed.js} +1 -1
  32. package/dist/client/_nuxt/M6QPYocW.js +1 -0
  33. package/dist/client/_nuxt/QumocfwJ.js +1 -0
  34. package/dist/client/_nuxt/TQi6eIO6.js +1 -0
  35. package/dist/client/_nuxt/V4UvAcd3.js +1 -0
  36. package/dist/client/_nuxt/builds/latest.json +1 -1
  37. package/dist/client/_nuxt/builds/meta/2be12f06-336a-4fdd-b982-2f6c682c14a6.json +1 -0
  38. package/dist/client/_nuxt/d8BPa19J.js +1 -0
  39. package/dist/client/_nuxt/entry.BMxUr06A.css +1 -0
  40. package/dist/client/_nuxt/qbS8UemQ.js +1 -0
  41. package/dist/client/_nuxt/wDw60tEC.js +10 -0
  42. package/dist/client/_nuxt/xEjB6ozD.js +1 -0
  43. package/dist/client/agents/index.html +1 -1
  44. package/dist/client/commands/index.html +1 -1
  45. package/dist/client/docs/index.html +1 -1
  46. package/dist/client/index.html +1 -1
  47. package/dist/client/mcp/index.html +1 -1
  48. package/dist/client/plugins/index.html +1 -0
  49. package/dist/client/settings/index.html +1 -0
  50. package/dist/client/skills/index.html +1 -1
  51. package/dist/module.d.mts +12 -1
  52. package/dist/module.json +1 -1
  53. package/dist/module.mjs +27 -5
  54. package/dist/runtime/constants.d.ts +29 -0
  55. package/dist/runtime/constants.js +5 -0
  56. package/dist/runtime/overlay/components/ChatOverlay.d.vue.ts +7 -0
  57. package/dist/runtime/overlay/components/ChatOverlay.vue +893 -0
  58. package/dist/runtime/overlay/components/ChatOverlay.vue.d.ts +7 -0
  59. package/dist/runtime/overlay/components/MarkdownContent.d.vue.ts +6 -0
  60. package/dist/runtime/overlay/components/MarkdownContent.vue +31 -0
  61. package/dist/runtime/overlay/components/MarkdownContent.vue.d.ts +6 -0
  62. package/dist/runtime/overlay/components/ToolCallBlock.d.vue.ts +8 -0
  63. package/dist/runtime/overlay/components/ToolCallBlock.vue +77 -0
  64. package/dist/runtime/overlay/components/ToolCallBlock.vue.d.ts +8 -0
  65. package/dist/runtime/overlay/plugin.client.d.ts +6 -0
  66. package/dist/runtime/overlay/plugin.client.js +29 -0
  67. package/dist/runtime/server/agents-manager.d.ts +17 -4
  68. package/dist/runtime/server/agents-manager.js +38 -109
  69. package/dist/runtime/server/base-resource-manager.d.ts +90 -0
  70. package/dist/runtime/server/base-resource-manager.js +201 -0
  71. package/dist/runtime/server/claude-session.d.ts +11 -1
  72. package/dist/runtime/server/claude-session.js +246 -27
  73. package/dist/runtime/server/commands-manager.d.ts +12 -4
  74. package/dist/runtime/server/commands-manager.js +25 -100
  75. package/dist/runtime/server/constants.d.ts +94 -0
  76. package/dist/runtime/server/constants.js +18 -0
  77. package/dist/runtime/server/docs-manager.d.ts +7 -0
  78. package/dist/runtime/server/docs-manager.js +112 -3
  79. package/dist/runtime/server/history-manager.d.ts +1 -0
  80. package/dist/runtime/server/history-manager.js +25 -3
  81. package/dist/runtime/server/plugins/socket.io.js +5 -3
  82. package/dist/runtime/server/plugins-manager.d.ts +84 -0
  83. package/dist/runtime/server/plugins-manager.js +338 -0
  84. package/dist/runtime/server/settings-manager.d.ts +17 -0
  85. package/dist/runtime/server/settings-manager.js +70 -0
  86. package/dist/runtime/server/share-manager.d.ts +24 -0
  87. package/dist/runtime/server/share-manager.js +96 -0
  88. package/dist/runtime/server/skills-manager.d.ts +18 -4
  89. package/dist/runtime/server/skills-manager.js +32 -159
  90. package/dist/runtime/shared/composables/useClaudeChat.d.ts +35 -0
  91. package/dist/runtime/shared/composables/useClaudeChat.js +264 -0
  92. package/dist/runtime/shared/composables/useShare.d.ts +42 -0
  93. package/dist/runtime/shared/composables/useShare.js +192 -0
  94. package/dist/runtime/shared/composables/useVoiceInput.d.ts +8 -0
  95. package/dist/runtime/shared/composables/useVoiceInput.js +77 -0
  96. package/dist/runtime/shared/constants.d.ts +28 -0
  97. package/dist/runtime/shared/constants.js +6 -0
  98. package/dist/runtime/shared/index.d.ts +9 -0
  99. package/dist/runtime/shared/index.js +5 -0
  100. package/dist/runtime/shared/types.d.ts +48 -0
  101. package/dist/runtime/shared/types.js +0 -0
  102. package/dist/runtime/types.d.ts +2 -0
  103. package/dist/types.d.mts +1 -1
  104. package/package.json +5 -3
  105. package/dist/client/_nuxt/BMi2eT6G.js +0 -1
  106. package/dist/client/_nuxt/BiBLVxWh.js +0 -1
  107. package/dist/client/_nuxt/BnRGpZsC.js +0 -8
  108. package/dist/client/_nuxt/C2GhPw5d.js +0 -7
  109. package/dist/client/_nuxt/D8683igF.js +0 -7
  110. package/dist/client/_nuxt/DBIw6BGF.js +0 -1
  111. package/dist/client/_nuxt/DCgjfr8H.js +0 -9
  112. package/dist/client/_nuxt/bl5iU4Kz.js +0 -1
  113. package/dist/client/_nuxt/builds/meta/35284331-5e85-46e0-9058-988fea05336c.json +0 -1
  114. package/dist/client/_nuxt/entry.BhHeZ_Nj.css +0 -1
  115. package/dist/client/_nuxt/nKfsBgPE.js +0 -4
@@ -1,3 +1,4 @@
1
+ import { MarkdownResourceManager } from './base-resource-manager.js';
1
2
  export interface SlashCommand {
2
3
  name: string;
3
4
  path: string;
@@ -7,12 +8,18 @@ export interface SlashCommand {
7
8
  content: string;
8
9
  rawContent: string;
9
10
  updatedAt: string;
11
+ /** Source of the command: 'project' for local .claude/, or plugin name */
12
+ source?: string;
10
13
  }
11
- export declare class CommandsManager {
12
- private commandsDir;
14
+ interface CommandFrontmatter {
15
+ 'description'?: string;
16
+ 'allowed-tools'?: string;
17
+ 'disable-model-invocation'?: boolean;
18
+ }
19
+ export declare class CommandsManager extends MarkdownResourceManager<SlashCommand, CommandFrontmatter> {
13
20
  constructor(projectPath: string);
14
- private parseFrontmatter;
15
- private buildFrontmatter;
21
+ protected buildFrontmatter(command: Partial<SlashCommand>): string;
22
+ protected toResource(name: string, frontmatter: CommandFrontmatter, body: string, rawContent: string, updatedAt: string): SlashCommand;
16
23
  getCommands(): SlashCommand[];
17
24
  getCommand(name: string): SlashCommand | null;
18
25
  saveCommand(name: string, content: string, options?: {
@@ -22,3 +29,4 @@ export declare class CommandsManager {
22
29
  }): SlashCommand;
23
30
  deleteCommand(name: string): boolean;
24
31
  }
32
+ export {};
@@ -1,132 +1,57 @@
1
- import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
2
- import { basename, join } from "node:path";
3
1
  import { createLogger } from "../logger.js";
2
+ import { MarkdownResourceManager } from "./base-resource-manager.js";
3
+ import { COMMANDS_SUBDIR } from "./constants.js";
4
4
  const log = createLogger("commands", { timestamp: true });
5
- export class CommandsManager {
6
- commandsDir;
5
+ export class CommandsManager extends MarkdownResourceManager {
7
6
  constructor(projectPath) {
8
- this.commandsDir = join(projectPath, ".claude", "commands");
9
- if (!existsSync(this.commandsDir)) {
10
- mkdirSync(this.commandsDir, { recursive: true });
11
- log("Created commands directory", { path: this.commandsDir });
12
- }
7
+ super(projectPath, COMMANDS_SUBDIR, log);
13
8
  }
14
- // Parse frontmatter from markdown content
15
- parseFrontmatter(content) {
16
- const frontmatterRegex = /^---\n((?:[^\n]*\n)*?)---\n([\s\S]*)$/;
17
- const match = content.match(frontmatterRegex);
18
- if (!match) {
19
- return { frontmatter: {}, body: content };
20
- }
21
- const [, yaml, body] = match;
22
- const frontmatter = {};
23
- for (const line of yaml.split("\n")) {
24
- const colonIndex = line.indexOf(":");
25
- if (colonIndex === -1) continue;
26
- const key = line.slice(0, colonIndex).trim();
27
- const value = line.slice(colonIndex + 1).trim();
28
- if (key === "description") {
29
- frontmatter.description = value;
30
- } else if (key === "allowed-tools") {
31
- frontmatter["allowed-tools"] = value;
32
- } else if (key === "disable-model-invocation") {
33
- frontmatter["disable-model-invocation"] = value === "true";
34
- }
35
- }
36
- return { frontmatter, body: body.trim() };
37
- }
38
- // Build frontmatter string
39
9
  buildFrontmatter(command) {
40
- const lines = ["---"];
41
- if (command.description) {
42
- lines.push(`description: ${command.description}`);
43
- }
44
- if (command.allowedTools && command.allowedTools.length > 0) {
45
- lines.push(`allowed-tools: ${command.allowedTools.join(", ")}`);
46
- }
47
- if (command.disableModelInvocation !== void 0) {
48
- lines.push(`disable-model-invocation: ${command.disableModelInvocation}`);
49
- }
50
- lines.push("---");
51
- return lines.join("\n");
52
- }
53
- // Get all slash commands
54
- getCommands() {
55
- const commands = [];
56
- if (!existsSync(this.commandsDir)) return commands;
57
- const entries = readdirSync(this.commandsDir);
58
- for (const entry of entries) {
59
- if (!entry.endsWith(".md")) continue;
60
- const fullPath = join(this.commandsDir, entry);
61
- const stat = statSync(fullPath);
62
- if (stat.isDirectory()) continue;
63
- const rawContent = readFileSync(fullPath, "utf-8");
64
- const { frontmatter, body } = this.parseFrontmatter(rawContent);
65
- commands.push({
66
- name: basename(entry, ".md"),
67
- path: entry,
68
- description: frontmatter.description,
69
- allowedTools: frontmatter["allowed-tools"] ? frontmatter["allowed-tools"].split(",").map((s) => s.trim()) : void 0,
70
- disableModelInvocation: frontmatter["disable-model-invocation"],
71
- content: body,
72
- rawContent,
73
- updatedAt: stat.mtime.toISOString()
74
- });
75
- }
76
- return commands.sort((a, b) => a.name.localeCompare(b.name));
10
+ return this.buildFrontmatterLines([
11
+ { key: "description", value: command.description },
12
+ { key: "allowed-tools", value: command.allowedTools },
13
+ { key: "disable-model-invocation", value: command.disableModelInvocation }
14
+ ]);
77
15
  }
78
- // Get single command
79
- getCommand(name) {
80
- const fileName = name.endsWith(".md") ? name : `${name}.md`;
81
- const fullPath = join(this.commandsDir, fileName);
82
- if (!existsSync(fullPath)) return null;
83
- const stat = statSync(fullPath);
84
- const rawContent = readFileSync(fullPath, "utf-8");
85
- const { frontmatter, body } = this.parseFrontmatter(rawContent);
16
+ toResource(name, frontmatter, body, rawContent, updatedAt) {
86
17
  return {
87
- name: basename(fileName, ".md"),
88
- path: fileName,
18
+ name,
19
+ path: `${name}.md`,
89
20
  description: frontmatter.description,
90
21
  allowedTools: frontmatter["allowed-tools"] ? frontmatter["allowed-tools"].split(",").map((s) => s.trim()) : void 0,
91
22
  disableModelInvocation: frontmatter["disable-model-invocation"],
92
23
  content: body,
93
24
  rawContent,
94
- updatedAt: stat.mtime.toISOString()
25
+ updatedAt
95
26
  };
96
27
  }
97
- // Create or update command
28
+ // Public API methods
29
+ getCommands() {
30
+ return this.getAll();
31
+ }
32
+ getCommand(name) {
33
+ return this.getOne(name);
34
+ }
98
35
  saveCommand(name, content, options) {
99
- const safeName = name.replace(/[^\w-]/g, "-").toLowerCase();
100
- const fileName = `${safeName}.md`;
101
- const fullPath = join(this.commandsDir, fileName);
36
+ const safeName = this.sanitizeName(name);
102
37
  const frontmatter = this.buildFrontmatter({
103
38
  description: options?.description,
104
39
  allowedTools: options?.allowedTools,
105
40
  disableModelInvocation: options?.disableModelInvocation
106
41
  });
107
- const rawContent = `${frontmatter}
108
-
109
- ${content}`;
110
- writeFileSync(fullPath, rawContent, "utf-8");
111
- log("Saved command", { name: safeName });
42
+ const { rawContent, updatedAt } = this.saveResource(safeName, frontmatter, content);
112
43
  return {
113
44
  name: safeName,
114
- path: fileName,
45
+ path: `${safeName}.md`,
115
46
  description: options?.description,
116
47
  allowedTools: options?.allowedTools,
117
48
  disableModelInvocation: options?.disableModelInvocation,
118
49
  content,
119
50
  rawContent,
120
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
51
+ updatedAt
121
52
  };
122
53
  }
123
- // Delete command
124
54
  deleteCommand(name) {
125
- const fileName = name.endsWith(".md") ? name : `${name}.md`;
126
- const fullPath = join(this.commandsDir, fileName);
127
- if (!existsSync(fullPath)) return false;
128
- unlinkSync(fullPath);
129
- log("Deleted command", { name });
130
- return true;
55
+ return this.delete(name);
131
56
  }
132
57
  }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Server-side constants for nuxt-claude-devtools
3
+ * Defines file paths, directory names, and configuration file names
4
+ */
5
+ /**
6
+ * Claude's standard configuration directory
7
+ * Contains: commands, agents, skills, docs
8
+ * Standard location recognized by Claude CLI
9
+ */
10
+ export declare const CLAUDE_DIR = ".claude";
11
+ /**
12
+ * DevTools-specific data directory
13
+ * Contains: history.json, settings.json, share.json, llms.json
14
+ * Separate from .claude to avoid conflicts with Claude CLI
15
+ */
16
+ export declare const DEVTOOLS_DATA_DIR = ".claude-devtools";
17
+ /**
18
+ * Directory for custom slash commands
19
+ * Path: .claude/commands/
20
+ */
21
+ export declare const COMMANDS_SUBDIR = "commands";
22
+ /**
23
+ * Directory for agent definitions
24
+ * Path: .claude/agents/
25
+ */
26
+ export declare const AGENTS_SUBDIR = "agents";
27
+ /**
28
+ * Directory for skill definitions
29
+ * Path: .claude/skills/
30
+ */
31
+ export declare const SKILLS_SUBDIR = "skills";
32
+ /**
33
+ * Directory for project documentation
34
+ * Path: .claude/docs/
35
+ * Claude automatically reads files from this directory
36
+ */
37
+ export declare const DOCS_SUBDIR = "docs";
38
+ /**
39
+ * Project guidelines file in repository root
40
+ * Claude reads this file for project-specific instructions
41
+ */
42
+ export declare const CLAUDE_MD_FILE = "CLAUDE.md";
43
+ /**
44
+ * Skill definition file name inside skill subdirectories
45
+ * Path: .claude/skills/<skill-name>/SKILL.md
46
+ */
47
+ export declare const SKILL_FILE = "SKILL.md";
48
+ /**
49
+ * Conversation history storage
50
+ * Path: .claude-devtools/history.json
51
+ */
52
+ export declare const HISTORY_FILE = "history.json";
53
+ /**
54
+ * DevTools settings storage
55
+ * Path: .claude-devtools/settings.json
56
+ */
57
+ export declare const SETTINGS_FILE = "settings.json";
58
+ /**
59
+ * Collaborative sharing data
60
+ * Path: .claude-devtools/share.json
61
+ */
62
+ export declare const SHARE_FILE = "share.json";
63
+ /**
64
+ * External documentation sources (llms.txt URLs)
65
+ * Path: .claude-devtools/llms.json
66
+ */
67
+ export declare const LLMS_FILE = "llms.json";
68
+ /**
69
+ * Default extension for markdown resources
70
+ */
71
+ export declare const MD_EXTENSION = ".md";
72
+ /**
73
+ * Default plugins cache directory (relative to user home)
74
+ * Path: ~/.claude/plugins/cache
75
+ */
76
+ export declare const DEFAULT_PLUGINS_CACHE_PATH = ".claude/plugins/cache";
77
+ /**
78
+ * Plugin manifest directory name inside plugin root
79
+ */
80
+ export declare const PLUGIN_MANIFEST_DIR = ".claude-plugin";
81
+ /**
82
+ * Plugin manifest file name
83
+ */
84
+ export declare const PLUGIN_MANIFEST_FILE = "plugin.json";
85
+ /**
86
+ * Claude settings file (for reading enabled plugins)
87
+ * Path: ~/.claude/settings.json (user scope)
88
+ */
89
+ export declare const CLAUDE_SETTINGS_FILE = "settings.json";
90
+ /**
91
+ * Claude local settings file (project scope, gitignored)
92
+ * Path: .claude/settings.local.json
93
+ */
94
+ export declare const CLAUDE_SETTINGS_LOCAL_FILE = "settings.local.json";
@@ -0,0 +1,18 @@
1
+ export const CLAUDE_DIR = ".claude";
2
+ export const DEVTOOLS_DATA_DIR = ".claude-devtools";
3
+ export const COMMANDS_SUBDIR = "commands";
4
+ export const AGENTS_SUBDIR = "agents";
5
+ export const SKILLS_SUBDIR = "skills";
6
+ export const DOCS_SUBDIR = "docs";
7
+ export const CLAUDE_MD_FILE = "CLAUDE.md";
8
+ export const SKILL_FILE = "SKILL.md";
9
+ export const HISTORY_FILE = "history.json";
10
+ export const SETTINGS_FILE = "settings.json";
11
+ export const SHARE_FILE = "share.json";
12
+ export const LLMS_FILE = "llms.json";
13
+ export const MD_EXTENSION = ".md";
14
+ export const DEFAULT_PLUGINS_CACHE_PATH = ".claude/plugins/cache";
15
+ export const PLUGIN_MANIFEST_DIR = ".claude-plugin";
16
+ export const PLUGIN_MANIFEST_FILE = "plugin.json";
17
+ export const CLAUDE_SETTINGS_FILE = "settings.json";
18
+ export const CLAUDE_SETTINGS_LOCAL_FILE = "settings.local.json";
@@ -1,3 +1,6 @@
1
+ export declare const CRITICAL_FILES: readonly ["nuxt.config.ts", "nuxt.config.js", "app.config.ts", "app.config.js", ".nuxtrc", "tsconfig.json"];
2
+ export declare function isCriticalFile(filePath: string): boolean;
3
+ export declare function getCriticalFileName(filePath: string): string | null;
1
4
  export interface DocFile {
2
5
  path: string;
3
6
  name: string;
@@ -36,6 +39,10 @@ export declare class DocsManager {
36
39
  exists: boolean;
37
40
  updatedAt: string;
38
41
  };
42
+ private static readonly CRITICAL_FILES_START;
43
+ private static readonly CRITICAL_FILES_END;
44
+ ensureCriticalFilesSection(autoConfirm?: boolean): void;
45
+ private generateCriticalFilesSection;
39
46
  private loadLlmsStore;
40
47
  private saveLlmsStore;
41
48
  getLlmsSources(): LlmsSource[];
@@ -1,15 +1,35 @@
1
1
  import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { basename, dirname, join, relative } from "node:path";
3
3
  import { createLogger } from "../logger.js";
4
+ import { CLAUDE_DIR, CLAUDE_MD_FILE, DEVTOOLS_DATA_DIR, DOCS_SUBDIR, LLMS_FILE, SETTINGS_FILE } from "./constants.js";
4
5
  const log = createLogger("docs", { timestamp: true });
6
+ export const CRITICAL_FILES = [
7
+ "nuxt.config.ts",
8
+ "nuxt.config.js",
9
+ "app.config.ts",
10
+ "app.config.js",
11
+ ".nuxtrc",
12
+ "tsconfig.json"
13
+ ];
14
+ export function isCriticalFile(filePath) {
15
+ const fileName = basename(filePath);
16
+ return CRITICAL_FILES.includes(fileName);
17
+ }
18
+ export function getCriticalFileName(filePath) {
19
+ const fileName = basename(filePath);
20
+ if (CRITICAL_FILES.includes(fileName)) {
21
+ return fileName;
22
+ }
23
+ return null;
24
+ }
5
25
  export class DocsManager {
6
26
  docsDir;
7
27
  llmsPath;
8
28
  projectPath;
9
29
  constructor(projectPath) {
10
30
  this.projectPath = projectPath;
11
- this.docsDir = join(projectPath, ".claude", "docs");
12
- this.llmsPath = join(projectPath, ".claude-devtools", "llms.json");
31
+ this.docsDir = join(projectPath, CLAUDE_DIR, DOCS_SUBDIR);
32
+ this.llmsPath = join(projectPath, DEVTOOLS_DATA_DIR, LLMS_FILE);
13
33
  if (!existsSync(this.docsDir)) {
14
34
  mkdirSync(this.docsDir, { recursive: true });
15
35
  log("Created docs directory", { path: this.docsDir });
@@ -84,7 +104,7 @@ export class DocsManager {
84
104
  }
85
105
  // ============ CLAUDE.md ============
86
106
  get claudeMdPath() {
87
- return join(this.projectPath, "CLAUDE.md");
107
+ return join(this.projectPath, CLAUDE_MD_FILE);
88
108
  }
89
109
  // Get CLAUDE.md content
90
110
  getClaudeMd() {
@@ -110,6 +130,95 @@ export class DocsManager {
110
130
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
111
131
  };
112
132
  }
133
+ // Section markers for auto-generated content
134
+ static CRITICAL_FILES_START = "<!-- NUXT-DEVTOOLS:CRITICAL-FILES -->";
135
+ static CRITICAL_FILES_END = "<!-- /NUXT-DEVTOOLS:CRITICAL-FILES -->";
136
+ // Ensure CLAUDE.md has the critical files warning section
137
+ // Always updates the auto-generated section on startup to ensure latest instructions
138
+ ensureCriticalFilesSection(autoConfirm = false) {
139
+ const { content, exists } = this.getClaudeMd();
140
+ const criticalFilesSection = this.generateCriticalFilesSection(autoConfirm);
141
+ if (!exists) {
142
+ const newContent = `# Project Guidelines
143
+
144
+ ${criticalFilesSection}
145
+ `;
146
+ this.saveClaudeMd(newContent);
147
+ log("Created CLAUDE.md with critical files section");
148
+ return;
149
+ }
150
+ if (content.includes(DocsManager.CRITICAL_FILES_START)) {
151
+ const startIdx = content.indexOf(DocsManager.CRITICAL_FILES_START);
152
+ const endIdx = content.indexOf(DocsManager.CRITICAL_FILES_END);
153
+ if (endIdx > startIdx) {
154
+ const before = content.substring(0, startIdx);
155
+ const after = content.substring(endIdx + DocsManager.CRITICAL_FILES_END.length);
156
+ const newContent = before + criticalFilesSection + after;
157
+ this.saveClaudeMd(newContent);
158
+ log("Updated critical files section in CLAUDE.md");
159
+ }
160
+ } else {
161
+ const newContent = content.trimEnd() + "\n\n" + criticalFilesSection + "\n";
162
+ this.saveClaudeMd(newContent);
163
+ log("Added critical files section to CLAUDE.md");
164
+ }
165
+ }
166
+ generateCriticalFilesSection(autoConfirm) {
167
+ const filesList = CRITICAL_FILES.map((f) => `- \`${f}\``).join("\n");
168
+ const settingsPath = `${DEVTOOLS_DATA_DIR}/${SETTINGS_FILE}`;
169
+ return `${DocsManager.CRITICAL_FILES_START}
170
+ ## \u26A0\uFE0F Critical Configuration Files
171
+
172
+ The following files trigger a full Nuxt restart when modified:
173
+ ${filesList}
174
+
175
+ ### \u{1F534} MANDATORY CHECK (EVERY TIME, NO EXCEPTIONS)
176
+
177
+ **BEFORE modifying ANY of these files, you MUST:**
178
+
179
+ \`\`\`
180
+ 1. READ ${settingsPath}
181
+ 2. CHECK criticalFiles.autoConfirm value
182
+ 3. IF false OR file missing \u2192 STOP and ASK user
183
+ 4. IF true \u2192 inform user, then proceed
184
+ \`\`\`
185
+
186
+ **This check is REQUIRED every single time, even if you checked before in this session.**
187
+
188
+ ### Order of Operations
189
+
190
+ 1. **Complete ALL prerequisite tasks FIRST**
191
+ - Create all new files that will be referenced
192
+ - Install all dependencies
193
+ - Write all related code
194
+
195
+ 2. **Verify prerequisites exist**
196
+ - All files referenced in config change must exist
197
+ - All imports must be valid
198
+
199
+ 3. **Check settings file** (read \`${settingsPath}\`)
200
+
201
+ 4. **Act based on autoConfirm setting**
202
+
203
+ ### Example: Adding i18n locale
204
+
205
+ \`\`\`
206
+ Step 1: Create locales/es.json \u2713 prerequisite
207
+ Step 2: Read ${settingsPath} \u2713 check flag
208
+ Step 3: If autoConfirm=false \u2192 ask user
209
+ Step 4: Update nuxt.config.ts \u2713 only after confirmation
210
+ \`\`\`
211
+
212
+ ### Current Setting
213
+
214
+ **autoConfirm: ${autoConfirm ? "ENABLED" : "DISABLED"}**
215
+
216
+ ${autoConfirm ? "\u2192 Inform user about restart, no confirmation needed." : '\u2192 MUST ask user and WAIT for explicit "yes" before proceeding.'}
217
+
218
+ ---
219
+ After restart, conversation history is preserved. User can send "continue" to resume.
220
+ ${DocsManager.CRITICAL_FILES_END}`;
221
+ }
113
222
  // ============ LLMS Sources ============
114
223
  loadLlmsStore() {
115
224
  try {
@@ -21,4 +21,5 @@ export declare class HistoryManager {
21
21
  getClaudeSessionId(): string | null;
22
22
  formatHistoryForSystemPrompt(): string | null;
23
23
  hasHistoryForRecovery(): boolean;
24
+ savePartialAssistantMessage(message: Message): void;
24
25
  }
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { createLogger } from "../logger.js";
4
+ import { DEVTOOLS_DATA_DIR, HISTORY_FILE } from "./constants.js";
4
5
  const log = createLogger("history", { timestamp: true });
5
6
  export class HistoryManager {
6
7
  storePath;
@@ -8,8 +9,8 @@ export class HistoryManager {
8
9
  projectPath;
9
10
  constructor(projectPath) {
10
11
  this.projectPath = projectPath;
11
- const historyDir = join(projectPath, ".claude-devtools");
12
- this.storePath = join(historyDir, "history.json");
12
+ const historyDir = join(projectPath, DEVTOOLS_DATA_DIR);
13
+ this.storePath = join(historyDir, HISTORY_FILE);
13
14
  if (!existsSync(historyDir)) {
14
15
  mkdirSync(historyDir, { recursive: true });
15
16
  log("Created history directory", { path: historyDir });
@@ -75,7 +76,8 @@ export class HistoryManager {
75
76
  conversation.messages.push(message);
76
77
  conversation.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
77
78
  if (!conversation.title && message.role === "user") {
78
- conversation.title = message.content.substring(0, 50) + (message.content.length > 50 ? "..." : "");
79
+ const content = message.content.replace(/^\[context\]\n[\s\S]*?\n\[\/context\]\n?/, "").trim();
80
+ conversation.title = content.substring(0, 50) + (content.length > 50 ? "..." : "");
79
81
  }
80
82
  this.saveStore();
81
83
  }
@@ -181,4 +183,24 @@ export class HistoryManager {
181
183
  const conversation = this.store.activeConversationId ? this.store.conversations.find((c) => c.id === this.store.activeConversationId) : null;
182
184
  return !!conversation && conversation.messages.length > 0;
183
185
  }
186
+ // Save partial assistant message (for preserving state before config changes)
187
+ savePartialAssistantMessage(message) {
188
+ const conversation = this.getActiveConversation();
189
+ const lastMessage = conversation.messages[conversation.messages.length - 1];
190
+ if (lastMessage && lastMessage.role === "assistant") {
191
+ lastMessage.content = message.content;
192
+ lastMessage.contentBlocks = message.contentBlocks;
193
+ lastMessage.timestamp = message.timestamp;
194
+ if (message.model) lastMessage.model = message.model;
195
+ } else {
196
+ conversation.messages.push(message);
197
+ }
198
+ conversation.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
199
+ try {
200
+ writeFileSync(this.storePath, JSON.stringify(this.store, null, 2));
201
+ log("Saved partial assistant message", { messageId: message.id });
202
+ } catch (error) {
203
+ log("Failed to save partial assistant message", { error });
204
+ }
205
+ }
184
206
  }
@@ -2,6 +2,7 @@ import { defineNitroPlugin, useRuntimeConfig } from "nitropack/runtime";
2
2
  import { Server as Engine } from "engine.io";
3
3
  import { Server } from "socket.io";
4
4
  import { defineEventHandler } from "h3";
5
+ import { SOCKET_PATH } from "../../constants.js";
5
6
  import { createLogger } from "../../logger.js";
6
7
  import { getClaudeSessionInstance, initClaudeSession } from "../claude-session.js";
7
8
  const log = createLogger("plugin");
@@ -17,7 +18,8 @@ export default defineNitroPlugin((nitroApp) => {
17
18
  command: claudeConfig.claude.command,
18
19
  args: claudeConfig.claude.args,
19
20
  rootDir: claudeConfig.rootDir,
20
- tunnelOrigin: claudeConfig.tunnelOrigin || null
21
+ tunnelOrigin: claudeConfig.tunnelOrigin || null,
22
+ pluginsCachePath: claudeConfig.pluginsCachePath || null
21
23
  });
22
24
  const engine = new Engine();
23
25
  const io = new Server();
@@ -26,7 +28,7 @@ export default defineNitroPlugin((nitroApp) => {
26
28
  if (session) {
27
29
  session.attachSocketIO(io);
28
30
  }
29
- nitroApp.router.use("/__claude_devtools_socket/", defineEventHandler({
31
+ nitroApp.router.use(`${SOCKET_PATH}/`, defineEventHandler({
30
32
  handler(event) {
31
33
  engine.handleRequest(event.node.req, event.node.res);
32
34
  event._handled = true;
@@ -44,5 +46,5 @@ export default defineNitroPlugin((nitroApp) => {
44
46
  }
45
47
  }
46
48
  }));
47
- log("Socket.IO server ready on /__claude_devtools_socket/");
49
+ log(`Socket.IO server ready on ${SOCKET_PATH}/`);
48
50
  });
@@ -0,0 +1,84 @@
1
+ export type PluginScope = 'user' | 'project' | 'local' | 'managed';
2
+ export interface PluginManifest {
3
+ name: string;
4
+ version?: string;
5
+ description?: string;
6
+ author?: {
7
+ name?: string;
8
+ email?: string;
9
+ url?: string;
10
+ };
11
+ homepage?: string;
12
+ repository?: string;
13
+ license?: string;
14
+ keywords?: string[];
15
+ }
16
+ export interface InstalledPlugin {
17
+ /** Plugin identifier (name@marketplace) */
18
+ id: string;
19
+ /** Plugin name */
20
+ name: string;
21
+ /** Marketplace name */
22
+ marketplace: string;
23
+ /** Installation scope */
24
+ scope: PluginScope;
25
+ /** Whether plugin is enabled */
26
+ enabled: boolean;
27
+ /** Plugin manifest (if available from cache) */
28
+ manifest?: PluginManifest;
29
+ /** Path to plugin in cache (if found) */
30
+ cachePath?: string;
31
+ }
32
+ export interface Marketplace {
33
+ /** Marketplace identifier */
34
+ name: string;
35
+ /** Source (GitHub repo, URL, or local path) */
36
+ source: string;
37
+ /** Whether auto-update is enabled */
38
+ autoUpdate?: boolean;
39
+ }
40
+ export interface PluginSkill {
41
+ name: string;
42
+ description: string;
43
+ content: string;
44
+ pluginName: string;
45
+ /** Full invocation name: plugin-name:skill-name */
46
+ fullName: string;
47
+ }
48
+ export interface PluginCommand {
49
+ name: string;
50
+ description?: string;
51
+ content: string;
52
+ pluginName: string;
53
+ /** Full invocation name: plugin-name:command-name */
54
+ fullName: string;
55
+ }
56
+ export interface PluginAgent {
57
+ name: string;
58
+ description: string;
59
+ prompt: string;
60
+ pluginName: string;
61
+ }
62
+ export declare class PluginsManager {
63
+ private projectPath;
64
+ private pluginsCachePath;
65
+ private userClaudeDir;
66
+ constructor(projectPath: string, customCachePath?: string | null);
67
+ private readSettingsFile;
68
+ private getUserSettings;
69
+ private getProjectSettings;
70
+ private getLocalSettings;
71
+ private parsePluginId;
72
+ private findPluginInCache;
73
+ private readPluginManifest;
74
+ private normalizePluginList;
75
+ getInstalledPlugins(): InstalledPlugin[];
76
+ getMarketplaces(): Marketplace[];
77
+ private parseFrontmatter;
78
+ getPluginSkills(pluginPath: string, pluginName: string): PluginSkill[];
79
+ getPluginCommands(pluginPath: string, pluginName: string): PluginCommand[];
80
+ getPluginAgents(pluginPath: string, pluginName: string): PluginAgent[];
81
+ getAllPluginSkills(): PluginSkill[];
82
+ getAllPluginCommands(): PluginCommand[];
83
+ getAllPluginAgents(): PluginAgent[];
84
+ }