@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.
- package/README.md +105 -13
- package/dist/client/200.html +1 -1
- package/dist/client/404.html +1 -1
- package/dist/client/_nuxt/B8uzckkK.js +1 -0
- package/dist/client/_nuxt/{CPQSDiF7.js → BAb1fJOF.js} +4 -4
- package/dist/client/_nuxt/BB1-kxmm.js +1 -0
- package/dist/client/_nuxt/BMZIbUUD.js +1 -0
- package/dist/client/_nuxt/BSF2Vz9o.js +1 -0
- package/dist/client/_nuxt/BSVkH7b6.js +1 -0
- package/dist/client/_nuxt/{08Mb3FOO.js → BYp73eMl.js} +1 -1
- package/dist/client/_nuxt/B_BoWmnX.js +1 -0
- package/dist/client/_nuxt/BcZxFXBD.js +1 -0
- package/dist/client/_nuxt/BflmC3YB.js +1 -0
- package/dist/client/_nuxt/BnXQTjo-.js +1 -0
- package/dist/client/_nuxt/C--9REmc.js +1 -0
- package/dist/client/_nuxt/CDQtmRaX.js +1 -0
- package/dist/client/_nuxt/CHeJJZL9.js +1 -0
- package/dist/client/_nuxt/{CSlPuO5s.js → COus5Ssl.js} +1 -1
- package/dist/client/_nuxt/{o1jjB-UO.js → CPA0s6N9.js} +1 -1
- package/dist/client/_nuxt/CRkq21kc.js +1 -0
- package/dist/client/_nuxt/Cgba93Y9.js +1 -0
- package/dist/client/_nuxt/D2l4TRxW.js +1 -0
- package/dist/client/_nuxt/DC_XB519.js +1 -0
- package/dist/client/_nuxt/DEys9N1G.js +1 -0
- package/dist/client/_nuxt/{b4Upel01.js → DGQ4s7ae.js} +1 -1
- package/dist/client/_nuxt/DH8Ugy8E.js +1 -0
- package/dist/client/_nuxt/DSt96JPY.js +4 -0
- package/dist/client/_nuxt/DbJLoP3G.js +1 -0
- package/dist/client/_nuxt/DeGmaFBY.js +1 -0
- package/dist/client/_nuxt/DgfRwrFR.js +1 -0
- package/dist/client/_nuxt/{CLKqRoht.js → DolUcBed.js} +1 -1
- package/dist/client/_nuxt/M6QPYocW.js +1 -0
- package/dist/client/_nuxt/QumocfwJ.js +1 -0
- package/dist/client/_nuxt/TQi6eIO6.js +1 -0
- package/dist/client/_nuxt/V4UvAcd3.js +1 -0
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/2be12f06-336a-4fdd-b982-2f6c682c14a6.json +1 -0
- package/dist/client/_nuxt/d8BPa19J.js +1 -0
- package/dist/client/_nuxt/entry.BMxUr06A.css +1 -0
- package/dist/client/_nuxt/qbS8UemQ.js +1 -0
- package/dist/client/_nuxt/wDw60tEC.js +10 -0
- package/dist/client/_nuxt/xEjB6ozD.js +1 -0
- package/dist/client/agents/index.html +1 -1
- package/dist/client/commands/index.html +1 -1
- package/dist/client/docs/index.html +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/mcp/index.html +1 -1
- package/dist/client/plugins/index.html +1 -0
- package/dist/client/settings/index.html +1 -0
- package/dist/client/skills/index.html +1 -1
- package/dist/module.d.mts +12 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +27 -5
- package/dist/runtime/constants.d.ts +29 -0
- package/dist/runtime/constants.js +5 -0
- package/dist/runtime/overlay/components/ChatOverlay.d.vue.ts +7 -0
- package/dist/runtime/overlay/components/ChatOverlay.vue +893 -0
- package/dist/runtime/overlay/components/ChatOverlay.vue.d.ts +7 -0
- package/dist/runtime/overlay/components/MarkdownContent.d.vue.ts +6 -0
- package/dist/runtime/overlay/components/MarkdownContent.vue +31 -0
- package/dist/runtime/overlay/components/MarkdownContent.vue.d.ts +6 -0
- package/dist/runtime/overlay/components/ToolCallBlock.d.vue.ts +8 -0
- package/dist/runtime/overlay/components/ToolCallBlock.vue +77 -0
- package/dist/runtime/overlay/components/ToolCallBlock.vue.d.ts +8 -0
- package/dist/runtime/overlay/plugin.client.d.ts +6 -0
- package/dist/runtime/overlay/plugin.client.js +29 -0
- package/dist/runtime/server/agents-manager.d.ts +17 -4
- package/dist/runtime/server/agents-manager.js +38 -109
- package/dist/runtime/server/base-resource-manager.d.ts +90 -0
- package/dist/runtime/server/base-resource-manager.js +201 -0
- package/dist/runtime/server/claude-session.d.ts +11 -1
- package/dist/runtime/server/claude-session.js +246 -27
- package/dist/runtime/server/commands-manager.d.ts +12 -4
- package/dist/runtime/server/commands-manager.js +25 -100
- package/dist/runtime/server/constants.d.ts +94 -0
- package/dist/runtime/server/constants.js +18 -0
- package/dist/runtime/server/docs-manager.d.ts +7 -0
- package/dist/runtime/server/docs-manager.js +112 -3
- package/dist/runtime/server/history-manager.d.ts +1 -0
- package/dist/runtime/server/history-manager.js +25 -3
- package/dist/runtime/server/plugins/socket.io.js +5 -3
- package/dist/runtime/server/plugins-manager.d.ts +84 -0
- package/dist/runtime/server/plugins-manager.js +338 -0
- package/dist/runtime/server/settings-manager.d.ts +17 -0
- package/dist/runtime/server/settings-manager.js +70 -0
- package/dist/runtime/server/share-manager.d.ts +24 -0
- package/dist/runtime/server/share-manager.js +96 -0
- package/dist/runtime/server/skills-manager.d.ts +18 -4
- package/dist/runtime/server/skills-manager.js +32 -159
- package/dist/runtime/shared/composables/useClaudeChat.d.ts +35 -0
- package/dist/runtime/shared/composables/useClaudeChat.js +264 -0
- package/dist/runtime/shared/composables/useShare.d.ts +42 -0
- package/dist/runtime/shared/composables/useShare.js +192 -0
- package/dist/runtime/shared/composables/useVoiceInput.d.ts +8 -0
- package/dist/runtime/shared/composables/useVoiceInput.js +77 -0
- package/dist/runtime/shared/constants.d.ts +28 -0
- package/dist/runtime/shared/constants.js +6 -0
- package/dist/runtime/shared/index.d.ts +9 -0
- package/dist/runtime/shared/index.js +5 -0
- package/dist/runtime/shared/types.d.ts +48 -0
- package/dist/runtime/shared/types.js +0 -0
- package/dist/runtime/types.d.ts +2 -0
- package/dist/types.d.mts +1 -1
- package/package.json +5 -3
- package/dist/client/_nuxt/BMi2eT6G.js +0 -1
- package/dist/client/_nuxt/BiBLVxWh.js +0 -1
- package/dist/client/_nuxt/BnRGpZsC.js +0 -8
- package/dist/client/_nuxt/C2GhPw5d.js +0 -7
- package/dist/client/_nuxt/D8683igF.js +0 -7
- package/dist/client/_nuxt/DBIw6BGF.js +0 -1
- package/dist/client/_nuxt/DCgjfr8H.js +0 -9
- package/dist/client/_nuxt/bl5iU4Kz.js +0 -1
- package/dist/client/_nuxt/builds/meta/35284331-5e85-46e0-9058-988fea05336c.json +0 -1
- package/dist/client/_nuxt/entry.BhHeZ_Nj.css +0 -1
- package/dist/client/_nuxt/nKfsBgPE.js +0 -4
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
/** Custom socket URL (for tunnel) */
|
|
3
|
+
socketUrl?: string;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
|
+
declare const _default: typeof __VLS_export;
|
|
7
|
+
export default _default;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
content: string;
|
|
3
|
+
};
|
|
4
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
5
|
+
declare const _default: typeof __VLS_export;
|
|
6
|
+
export default _default;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { marked } from "marked";
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
content: { type: String, required: true }
|
|
6
|
+
});
|
|
7
|
+
marked.setOptions({
|
|
8
|
+
breaks: true,
|
|
9
|
+
gfm: true
|
|
10
|
+
});
|
|
11
|
+
const renderedContent = computed(() => {
|
|
12
|
+
if (!props.content) return "";
|
|
13
|
+
try {
|
|
14
|
+
return marked.parse(props.content);
|
|
15
|
+
} catch (e) {
|
|
16
|
+
console.error("Markdown parsing error:", e);
|
|
17
|
+
return props.content;
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div
|
|
24
|
+
class="markdown-content"
|
|
25
|
+
v-html="renderedContent"
|
|
26
|
+
/>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<style>
|
|
30
|
+
.markdown-content{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:14px;line-height:1.625;word-break:break-word}.markdown-content h1{font-size:1.25rem;font-weight:700;margin-bottom:.5rem;margin-top:1rem}.markdown-content h2{font-size:1.125rem;font-weight:700;margin-bottom:.5rem;margin-top:.75rem}.markdown-content h3{font-size:1rem;font-weight:600;margin-bottom:.25rem;margin-top:.5rem}.markdown-content p{margin:.5rem 0}.markdown-content ol,.markdown-content ul{margin:.5rem 0 .5rem 1rem}.markdown-content ul{list-style-type:disc}.markdown-content ol{list-style-type:decimal}.markdown-content li{margin:.25rem 0}.markdown-content code{background:hsla(0,0%,100%,.1);border-radius:4px;font-family:SF Mono,Monaco,Cascadia Code,Consolas,monospace;font-size:.75rem;padding:.125rem .375rem}.markdown-content pre{background:rgba(0,0,0,.3);border-radius:8px;margin:.5rem 0;overflow-x:auto;padding:.75rem}.markdown-content pre code{background:transparent;font-size:.8rem;line-height:1.5;padding:0}.markdown-content blockquote{border-left:4px solid hsla(0,0%,100%,.2);color:var(--claude-text-muted,#a0a0a0);font-style:italic;margin:.5rem 0;padding-left:1rem}.markdown-content a{color:#3b82f6;text-decoration:none}.markdown-content a:hover{text-decoration:underline}.markdown-content table{border-collapse:collapse;font-size:.875rem;margin:.5rem 0;width:100%}.markdown-content td,.markdown-content th{border:1px solid hsla(0,0%,100%,.15);padding:.5rem .75rem;text-align:left}.markdown-content th{background:hsla(0,0%,100%,.05);font-weight:600}.markdown-content tr:nth-child(2n){background:hsla(0,0%,100%,.02)}.markdown-content hr{border:none;border-top:1px solid hsla(0,0%,100%,.15);margin:1rem 0}.markdown-content img{border-radius:8px;height:auto;max-width:100%}.markdown-content>:first-child{margin-top:0}.markdown-content>:last-child{margin-bottom:0}
|
|
31
|
+
</style>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
content: string;
|
|
3
|
+
};
|
|
4
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
5
|
+
declare const _default: typeof __VLS_export;
|
|
6
|
+
export default _default;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ContentBlock } from '../../shared/types.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
block: ContentBlock;
|
|
4
|
+
result?: ContentBlock;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
7
|
+
declare const _default: typeof __VLS_export;
|
|
8
|
+
export default _default;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, ref } from "vue";
|
|
3
|
+
const props = defineProps({
|
|
4
|
+
block: { type: Object, required: true },
|
|
5
|
+
result: { type: Object, required: false }
|
|
6
|
+
});
|
|
7
|
+
const isExpanded = ref(false);
|
|
8
|
+
const statusIcon = computed(() => {
|
|
9
|
+
if (!props.result) return "\u23F3";
|
|
10
|
+
if (props.result.is_error) return "\u274C";
|
|
11
|
+
return "\u2713";
|
|
12
|
+
});
|
|
13
|
+
const statusColor = computed(() => {
|
|
14
|
+
if (!props.result) return "var(--claude-text-muted)";
|
|
15
|
+
if (props.result.is_error) return "#ef4444";
|
|
16
|
+
return "#22c55e";
|
|
17
|
+
});
|
|
18
|
+
const resultPreview = computed(() => {
|
|
19
|
+
if (!props.result?.content) return "";
|
|
20
|
+
const content = typeof props.result.content === "string" ? props.result.content : JSON.stringify(props.result.content, null, 2);
|
|
21
|
+
if (content.length > 200) {
|
|
22
|
+
return content.slice(0, 200) + "...";
|
|
23
|
+
}
|
|
24
|
+
return content;
|
|
25
|
+
});
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<div class="claude-tool-block">
|
|
30
|
+
<button
|
|
31
|
+
class="claude-tool-header"
|
|
32
|
+
@click="isExpanded = !isExpanded"
|
|
33
|
+
>
|
|
34
|
+
<span
|
|
35
|
+
class="claude-tool-icon"
|
|
36
|
+
:style="{ color: statusColor }"
|
|
37
|
+
>{{ statusIcon }}</span>
|
|
38
|
+
<span class="claude-tool-name">{{ block.name }}</span>
|
|
39
|
+
<span class="claude-tool-expand">{{ isExpanded ? "\u25BC" : "\u25B6" }}</span>
|
|
40
|
+
</button>
|
|
41
|
+
|
|
42
|
+
<div
|
|
43
|
+
v-if="isExpanded"
|
|
44
|
+
class="claude-tool-details"
|
|
45
|
+
>
|
|
46
|
+
<!-- Input -->
|
|
47
|
+
<div
|
|
48
|
+
v-if="block.input"
|
|
49
|
+
class="claude-tool-section"
|
|
50
|
+
>
|
|
51
|
+
<div class="claude-tool-section-title">
|
|
52
|
+
Input
|
|
53
|
+
</div>
|
|
54
|
+
<pre class="claude-tool-code">{{ JSON.stringify(block.input, null, 2) }}</pre>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<!-- Result -->
|
|
58
|
+
<div
|
|
59
|
+
v-if="result"
|
|
60
|
+
class="claude-tool-section"
|
|
61
|
+
>
|
|
62
|
+
<div class="claude-tool-section-title">
|
|
63
|
+
Result
|
|
64
|
+
<span
|
|
65
|
+
v-if="result.is_error"
|
|
66
|
+
class="claude-tool-error-badge"
|
|
67
|
+
>Error</span>
|
|
68
|
+
</div>
|
|
69
|
+
<pre class="claude-tool-code">{{ resultPreview }}</pre>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</template>
|
|
74
|
+
|
|
75
|
+
<style>
|
|
76
|
+
.claude-tool-block{background:rgba(0,0,0,.2);border-radius:6px;margin:8px 0;overflow:hidden}.claude-tool-header{align-items:center;background:transparent;border:none;color:var(--claude-text);cursor:pointer;display:flex;font-size:13px;gap:8px;padding:8px 12px;text-align:left;transition:background .15s;width:100%}.claude-tool-header:hover{background:hsla(0,0%,100%,.05)}.claude-tool-icon{font-size:12px}.claude-tool-name{color:var(--claude-primary);flex:1;font-family:monospace}.claude-tool-expand{color:var(--claude-text-muted);font-size:10px}.claude-tool-details{padding:0 12px 12px}.claude-tool-section{margin-top:8px}.claude-tool-section-title{align-items:center;color:var(--claude-text-muted);display:flex;font-size:11px;gap:8px;margin-bottom:4px}.claude-tool-error-badge{background:#ef4444;border-radius:4px;color:#fff;font-size:10px;padding:1px 6px}.claude-tool-code{background:#0d0d0d;border-radius:4px;font-family:SF Mono,Monaco,monospace;font-size:11px;margin:0;max-height:200px;overflow-x:auto;overflow-y:auto;padding:8px;white-space:pre-wrap;word-break:break-all}
|
|
77
|
+
</style>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ContentBlock } from '../../shared/types.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
block: ContentBlock;
|
|
4
|
+
result?: ContentBlock;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
7
|
+
declare const _default: typeof __VLS_export;
|
|
8
|
+
export default _default;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createApp, h } from "vue";
|
|
2
|
+
import { defineNuxtPlugin, useRuntimeConfig } from "#app";
|
|
3
|
+
import ChatOverlay from "./components/ChatOverlay.vue";
|
|
4
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
5
|
+
if (import.meta.server) return;
|
|
6
|
+
const config = useRuntimeConfig();
|
|
7
|
+
const mountPoint = document.createElement("div");
|
|
8
|
+
mountPoint.id = "claude-chat-overlay-root";
|
|
9
|
+
document.body.appendChild(mountPoint);
|
|
10
|
+
const tunnelOrigin = config.public.claudeDevtools?.tunnelOrigin;
|
|
11
|
+
const overlayApp = createApp({
|
|
12
|
+
render() {
|
|
13
|
+
return h(ChatOverlay, {
|
|
14
|
+
socketUrl: tunnelOrigin || void 0
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
overlayApp.config.globalProperties = nuxtApp.vueApp.config.globalProperties;
|
|
19
|
+
overlayApp.mount(mountPoint);
|
|
20
|
+
nuxtApp.hook("app:beforeMount", () => {
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
provide: {
|
|
24
|
+
claudeOverlay: {
|
|
25
|
+
// Could expose methods to control overlay programmatically
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MarkdownResourceManager } from './base-resource-manager.js';
|
|
1
2
|
export interface Agent {
|
|
2
3
|
name: string;
|
|
3
4
|
description: string;
|
|
@@ -9,12 +10,23 @@ export interface Agent {
|
|
|
9
10
|
permissionMode?: 'default' | 'acceptEdits' | 'dontAsk' | 'bypassPermissions' | 'plan';
|
|
10
11
|
skills?: string[];
|
|
11
12
|
updatedAt: string;
|
|
13
|
+
/** Source of the agent: 'project' for local .claude/, or plugin name */
|
|
14
|
+
source?: string;
|
|
12
15
|
}
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
interface AgentFrontmatter {
|
|
17
|
+
name?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
tools?: string;
|
|
20
|
+
disallowedTools?: string;
|
|
21
|
+
model?: string;
|
|
22
|
+
permissionMode?: string;
|
|
23
|
+
skills?: string[];
|
|
24
|
+
}
|
|
25
|
+
export declare class AgentsManager extends MarkdownResourceManager<Agent, AgentFrontmatter> {
|
|
15
26
|
constructor(projectPath: string);
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
protected parseYaml(yaml: string): AgentFrontmatter;
|
|
28
|
+
protected buildFrontmatter(agent: Partial<Agent>): string;
|
|
29
|
+
protected toResource(name: string, frontmatter: AgentFrontmatter, body: string, rawContent: string, updatedAt: string): Agent;
|
|
18
30
|
getAgents(): Agent[];
|
|
19
31
|
getAgent(name: string): Agent | null;
|
|
20
32
|
saveAgent(agent: {
|
|
@@ -29,3 +41,4 @@ export declare class AgentsManager {
|
|
|
29
41
|
}): Agent;
|
|
30
42
|
deleteAgent(name: string): boolean;
|
|
31
43
|
}
|
|
44
|
+
export {};
|
|
@@ -1,25 +1,13 @@
|
|
|
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 { AGENTS_SUBDIR } from "./constants.js";
|
|
4
4
|
const log = createLogger("agents", { timestamp: true });
|
|
5
|
-
export class AgentsManager {
|
|
6
|
-
agentsDir;
|
|
5
|
+
export class AgentsManager extends MarkdownResourceManager {
|
|
7
6
|
constructor(projectPath) {
|
|
8
|
-
|
|
9
|
-
if (!existsSync(this.agentsDir)) {
|
|
10
|
-
mkdirSync(this.agentsDir, { recursive: true });
|
|
11
|
-
log("Created agents directory", { path: this.agentsDir });
|
|
12
|
-
}
|
|
7
|
+
super(projectPath, AGENTS_SUBDIR, log);
|
|
13
8
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
|
|
17
|
-
const match = content.match(frontmatterRegex);
|
|
18
|
-
if (!match) {
|
|
19
|
-
return { frontmatter: {}, body: content.trim() };
|
|
20
|
-
}
|
|
21
|
-
const [, yaml, body] = match;
|
|
22
|
-
const frontmatter = {};
|
|
9
|
+
parseYaml(yaml) {
|
|
10
|
+
const result = {};
|
|
23
11
|
let inSkillsArray = false;
|
|
24
12
|
const skillsList = [];
|
|
25
13
|
for (const line of yaml.split("\n")) {
|
|
@@ -40,26 +28,26 @@ export class AgentsManager {
|
|
|
40
28
|
const value = line.slice(colonIndex + 1).trim();
|
|
41
29
|
switch (key) {
|
|
42
30
|
case "name":
|
|
43
|
-
|
|
31
|
+
result.name = value;
|
|
44
32
|
break;
|
|
45
33
|
case "description":
|
|
46
|
-
|
|
34
|
+
result.description = value;
|
|
47
35
|
break;
|
|
48
36
|
case "tools":
|
|
49
|
-
|
|
37
|
+
result.tools = value;
|
|
50
38
|
break;
|
|
51
39
|
case "disallowedTools":
|
|
52
|
-
|
|
40
|
+
result.disallowedTools = value;
|
|
53
41
|
break;
|
|
54
42
|
case "model":
|
|
55
|
-
|
|
43
|
+
result.model = value;
|
|
56
44
|
break;
|
|
57
45
|
case "permissionMode":
|
|
58
|
-
|
|
46
|
+
result.permissionMode = value;
|
|
59
47
|
break;
|
|
60
48
|
case "skills":
|
|
61
49
|
if (value) {
|
|
62
|
-
|
|
50
|
+
result.skills = value.split(",").map((s) => s.trim()).filter((s) => s);
|
|
63
51
|
} else {
|
|
64
52
|
inSkillsArray = true;
|
|
65
53
|
}
|
|
@@ -67,77 +55,24 @@ export class AgentsManager {
|
|
|
67
55
|
}
|
|
68
56
|
}
|
|
69
57
|
if (skillsList.length > 0) {
|
|
70
|
-
|
|
58
|
+
result.skills = skillsList;
|
|
71
59
|
}
|
|
72
|
-
return
|
|
60
|
+
return result;
|
|
73
61
|
}
|
|
74
|
-
// Build frontmatter string
|
|
75
62
|
buildFrontmatter(agent) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
if (agent.disallowedTools && agent.disallowedTools.length > 0) {
|
|
87
|
-
lines.push(`disallowedTools: ${agent.disallowedTools.join(", ")}`);
|
|
88
|
-
}
|
|
89
|
-
if (agent.model) {
|
|
90
|
-
lines.push(`model: ${agent.model}`);
|
|
91
|
-
}
|
|
92
|
-
if (agent.permissionMode) {
|
|
93
|
-
lines.push(`permissionMode: ${agent.permissionMode}`);
|
|
94
|
-
}
|
|
95
|
-
if (agent.skills && agent.skills.length > 0) {
|
|
96
|
-
lines.push("skills:");
|
|
97
|
-
for (const skill of agent.skills) {
|
|
98
|
-
lines.push(` - ${skill}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
lines.push("---");
|
|
102
|
-
return lines.join("\n");
|
|
63
|
+
return this.buildFrontmatterWithArrays([
|
|
64
|
+
{ key: "name", value: agent.name },
|
|
65
|
+
{ key: "description", value: agent.description },
|
|
66
|
+
{ key: "tools", value: agent.tools },
|
|
67
|
+
{ key: "disallowedTools", value: agent.disallowedTools },
|
|
68
|
+
{ key: "model", value: agent.model },
|
|
69
|
+
{ key: "permissionMode", value: agent.permissionMode },
|
|
70
|
+
{ key: "skills", value: agent.skills, multiline: true }
|
|
71
|
+
]);
|
|
103
72
|
}
|
|
104
|
-
|
|
105
|
-
getAgents() {
|
|
106
|
-
const agents = [];
|
|
107
|
-
if (!existsSync(this.agentsDir)) return agents;
|
|
108
|
-
const entries = readdirSync(this.agentsDir);
|
|
109
|
-
for (const entry of entries) {
|
|
110
|
-
if (!entry.endsWith(".md")) continue;
|
|
111
|
-
const fullPath = join(this.agentsDir, entry);
|
|
112
|
-
const stat = statSync(fullPath);
|
|
113
|
-
if (stat.isDirectory()) continue;
|
|
114
|
-
const rawContent = readFileSync(fullPath, "utf-8");
|
|
115
|
-
const { frontmatter, body } = this.parseFrontmatter(rawContent);
|
|
116
|
-
agents.push({
|
|
117
|
-
name: frontmatter.name || basename(entry, ".md"),
|
|
118
|
-
description: frontmatter.description || "",
|
|
119
|
-
prompt: body,
|
|
120
|
-
rawContent,
|
|
121
|
-
tools: frontmatter.tools ? frontmatter.tools.split(",").map((s) => s.trim()) : void 0,
|
|
122
|
-
disallowedTools: frontmatter.disallowedTools ? frontmatter.disallowedTools.split(",").map((s) => s.trim()) : void 0,
|
|
123
|
-
model: frontmatter.model,
|
|
124
|
-
permissionMode: frontmatter.permissionMode,
|
|
125
|
-
skills: frontmatter.skills,
|
|
126
|
-
updatedAt: stat.mtime.toISOString()
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
return agents.sort((a, b) => a.name.localeCompare(b.name));
|
|
130
|
-
}
|
|
131
|
-
// Get single agent
|
|
132
|
-
getAgent(name) {
|
|
133
|
-
const fileName = name.endsWith(".md") ? name : `${name}.md`;
|
|
134
|
-
const fullPath = join(this.agentsDir, fileName);
|
|
135
|
-
if (!existsSync(fullPath)) return null;
|
|
136
|
-
const stat = statSync(fullPath);
|
|
137
|
-
const rawContent = readFileSync(fullPath, "utf-8");
|
|
138
|
-
const { frontmatter, body } = this.parseFrontmatter(rawContent);
|
|
73
|
+
toResource(name, frontmatter, body, rawContent, updatedAt) {
|
|
139
74
|
return {
|
|
140
|
-
name: frontmatter.name ||
|
|
75
|
+
name: frontmatter.name || name,
|
|
141
76
|
description: frontmatter.description || "",
|
|
142
77
|
prompt: body,
|
|
143
78
|
rawContent,
|
|
@@ -146,14 +81,18 @@ export class AgentsManager {
|
|
|
146
81
|
model: frontmatter.model,
|
|
147
82
|
permissionMode: frontmatter.permissionMode,
|
|
148
83
|
skills: frontmatter.skills,
|
|
149
|
-
updatedAt
|
|
84
|
+
updatedAt
|
|
150
85
|
};
|
|
151
86
|
}
|
|
152
|
-
//
|
|
87
|
+
// Public API methods
|
|
88
|
+
getAgents() {
|
|
89
|
+
return this.getAll();
|
|
90
|
+
}
|
|
91
|
+
getAgent(name) {
|
|
92
|
+
return this.getOne(name);
|
|
93
|
+
}
|
|
153
94
|
saveAgent(agent) {
|
|
154
|
-
const safeName = agent.name
|
|
155
|
-
const fileName = `${safeName}.md`;
|
|
156
|
-
const fullPath = join(this.agentsDir, fileName);
|
|
95
|
+
const safeName = this.sanitizeName(agent.name);
|
|
157
96
|
const frontmatter = this.buildFrontmatter({
|
|
158
97
|
name: safeName,
|
|
159
98
|
description: agent.description,
|
|
@@ -163,11 +102,7 @@ export class AgentsManager {
|
|
|
163
102
|
permissionMode: agent.permissionMode,
|
|
164
103
|
skills: agent.skills
|
|
165
104
|
});
|
|
166
|
-
const rawContent =
|
|
167
|
-
|
|
168
|
-
${agent.prompt}`;
|
|
169
|
-
writeFileSync(fullPath, rawContent, "utf-8");
|
|
170
|
-
log("Saved agent", { name: safeName, path: fullPath });
|
|
105
|
+
const { rawContent, updatedAt } = this.saveResource(safeName, frontmatter, agent.prompt);
|
|
171
106
|
return {
|
|
172
107
|
name: safeName,
|
|
173
108
|
description: agent.description,
|
|
@@ -178,16 +113,10 @@ ${agent.prompt}`;
|
|
|
178
113
|
model: agent.model,
|
|
179
114
|
permissionMode: agent.permissionMode,
|
|
180
115
|
skills: agent.skills,
|
|
181
|
-
updatedAt
|
|
116
|
+
updatedAt
|
|
182
117
|
};
|
|
183
118
|
}
|
|
184
|
-
// Delete agent
|
|
185
119
|
deleteAgent(name) {
|
|
186
|
-
|
|
187
|
-
const fullPath = join(this.agentsDir, fileName);
|
|
188
|
-
if (!existsSync(fullPath)) return false;
|
|
189
|
-
unlinkSync(fullPath);
|
|
190
|
-
log("Deleted agent", { name });
|
|
191
|
-
return true;
|
|
120
|
+
return this.delete(name);
|
|
192
121
|
}
|
|
193
122
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
type Logger = (message: string, data?: unknown) => void;
|
|
2
|
+
/**
|
|
3
|
+
* Base interface for all markdown-based resources
|
|
4
|
+
*/
|
|
5
|
+
export interface BaseResource {
|
|
6
|
+
name: string;
|
|
7
|
+
rawContent: string;
|
|
8
|
+
updatedAt: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Configuration for resource storage
|
|
12
|
+
*/
|
|
13
|
+
export interface ResourceStorageConfig {
|
|
14
|
+
/** Use subdirectories for each resource (like skills: .claude/skills/<name>/SKILL.md) */
|
|
15
|
+
useSubdirectories?: boolean;
|
|
16
|
+
/** Filename when using subdirectories (default: SKILL.md) */
|
|
17
|
+
subdirectoryFilename?: string;
|
|
18
|
+
/** File extension (default: .md) */
|
|
19
|
+
extension?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Base class for managing markdown resources with YAML frontmatter
|
|
23
|
+
* Handles common file operations, frontmatter parsing, and CRUD operations
|
|
24
|
+
*/
|
|
25
|
+
export declare abstract class MarkdownResourceManager<TResource extends BaseResource, TFrontmatter> {
|
|
26
|
+
protected readonly resourceDir: string;
|
|
27
|
+
protected readonly log: Logger;
|
|
28
|
+
protected readonly config: Required<ResourceStorageConfig>;
|
|
29
|
+
constructor(projectPath: string, subPath: string, log: Logger, config?: ResourceStorageConfig);
|
|
30
|
+
/**
|
|
31
|
+
* Parse YAML frontmatter from markdown content
|
|
32
|
+
*/
|
|
33
|
+
protected parseFrontmatter(content: string): {
|
|
34
|
+
frontmatter: TFrontmatter;
|
|
35
|
+
body: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Parse simple YAML - override for complex parsing (arrays, nested objects)
|
|
39
|
+
*/
|
|
40
|
+
protected parseYaml(yaml: string): TFrontmatter;
|
|
41
|
+
/**
|
|
42
|
+
* Build YAML frontmatter string from resource data
|
|
43
|
+
*/
|
|
44
|
+
protected abstract buildFrontmatter(resource: Partial<TResource>): string;
|
|
45
|
+
/**
|
|
46
|
+
* Convert frontmatter + body to resource object
|
|
47
|
+
*/
|
|
48
|
+
protected abstract toResource(name: string, frontmatter: TFrontmatter, body: string, rawContent: string, updatedAt: string): TResource;
|
|
49
|
+
/**
|
|
50
|
+
* Get all resources
|
|
51
|
+
*/
|
|
52
|
+
getAll(): TResource[];
|
|
53
|
+
/**
|
|
54
|
+
* Get single resource by name
|
|
55
|
+
*/
|
|
56
|
+
getOne(name: string): TResource | null;
|
|
57
|
+
/**
|
|
58
|
+
* Save (create or update) a resource
|
|
59
|
+
*/
|
|
60
|
+
protected saveResource(name: string, frontmatter: string, body: string): {
|
|
61
|
+
rawContent: string;
|
|
62
|
+
updatedAt: string;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Delete a resource
|
|
66
|
+
*/
|
|
67
|
+
delete(name: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Sanitize name for filesystem (kebab-case)
|
|
70
|
+
*/
|
|
71
|
+
protected sanitizeName(name: string): string;
|
|
72
|
+
/**
|
|
73
|
+
* Helper to build frontmatter lines
|
|
74
|
+
*/
|
|
75
|
+
protected buildFrontmatterLines(fields: Array<{
|
|
76
|
+
key: string;
|
|
77
|
+
value: unknown;
|
|
78
|
+
}>): string;
|
|
79
|
+
/**
|
|
80
|
+
* Helper to build frontmatter with multiline arrays
|
|
81
|
+
*/
|
|
82
|
+
protected buildFrontmatterWithArrays(fields: Array<{
|
|
83
|
+
key: string;
|
|
84
|
+
value: unknown;
|
|
85
|
+
multiline?: boolean;
|
|
86
|
+
}>): string;
|
|
87
|
+
private readFromFile;
|
|
88
|
+
private readFromSubdirectory;
|
|
89
|
+
}
|
|
90
|
+
export {};
|