@agentprojectcontext/apx 1.58.0 → 1.59.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.
@@ -1,16 +1,44 @@
1
1
  import { http } from "../http";
2
2
 
3
+ export type SkillSource = "builtin" | "global" | "project" | string;
4
+
3
5
  export type SkillEntry = {
4
6
  slug: string;
5
- source: "bundled" | "user" | "project" | string;
7
+ source: SkillSource;
6
8
  description: string;
9
+ /** Effective enabled state for the requested scope. */
10
+ enabled?: boolean;
11
+ /** Built-in APX skill — always active, never disableable. */
12
+ private?: boolean;
13
+ /** Whether THIS scope holds an explicit override (vs. inherited). */
14
+ overridden?: boolean;
7
15
  };
8
16
 
9
17
  export type SkillsList = {
10
18
  count: number;
19
+ /** Echoed scope key: "default" (super-agent) or a project path. */
20
+ scope?: string;
11
21
  skills: SkillEntry[];
12
22
  };
13
23
 
24
+ export interface SkillDetail {
25
+ slug: string;
26
+ source: SkillSource;
27
+ description: string;
28
+ frontmatter: Record<string, string>;
29
+ body: string;
30
+ file: string;
31
+ enabled: boolean;
32
+ private: boolean;
33
+ overridden: boolean;
34
+ }
35
+
36
+ export interface CreateResult {
37
+ ok: boolean;
38
+ slug: string;
39
+ source: string;
40
+ }
41
+
14
42
  export interface InspectorConfig {
15
43
  enabled: boolean;
16
44
  load_threshold: number;
@@ -62,16 +90,59 @@ export interface InspectResult {
62
90
 
63
91
  export const Skills = {
64
92
  /**
65
- * List installed skills (bundled + user + optional project-scoped). The
66
- * description is already condensed for one-line rendering.
93
+ * List installed skills (built-in + user + optional project-scoped), each
94
+ * annotated with `enabled`/`private` for the requested scope. Pass a project
95
+ * path to both scan that project's skills AND resolve enabled-state against
96
+ * it; omit it for the super-agent ("default") scope.
67
97
  */
68
- list: (projectPath?: string) =>
69
- http.get<SkillsList>(
70
- projectPath
71
- ? `/skills?project_path=${encodeURIComponent(projectPath)}`
72
- : "/skills",
98
+ list: (projectPath?: string) => {
99
+ const qs = new URLSearchParams();
100
+ if (projectPath) {
101
+ qs.set("project_path", projectPath);
102
+ qs.set("scope", projectPath);
103
+ }
104
+ const q = qs.toString();
105
+ return http.get<SkillsList>(q ? `/skills?${q}` : "/skills");
106
+ },
107
+
108
+ /** Full body + frontmatter of a skill, for the viewer. */
109
+ detail: (slug: string, projectPath?: string) => {
110
+ const qs = projectPath ? `?project_path=${encodeURIComponent(projectPath)}` : "";
111
+ return http.get<SkillDetail>(`/skills/${encodeURIComponent(slug)}/detail${qs}`);
112
+ },
113
+
114
+ /**
115
+ * Enable/disable a skill for a scope. `enabled: null` clears the override so
116
+ * the skill inherits the super-agent default again. `scope` is "default" or a
117
+ * project path.
118
+ */
119
+ setEnabled: (body: { slug: string; enabled: boolean | null; scope?: string }) =>
120
+ http.put<{ ok: boolean; slug: string; scope: string; enabled: boolean | null }>(
121
+ "/skills/enabled",
122
+ body,
73
123
  ),
74
124
 
125
+ /**
126
+ * Create a user skill from the online editor. With `project_path` it lands in
127
+ * that project's .apc/skills/; otherwise in ~/.apx/skills/.
128
+ */
129
+ create: (body: { slug: string; description?: string; body?: string; project_path?: string }) =>
130
+ http.post<CreateResult>("/skills", body),
131
+
132
+ /** Import a skill from an uploaded .zip (base64-encoded). */
133
+ importZip: (body: { data: string; project_path?: string }) =>
134
+ http.post<CreateResult>("/skills/import/zip", body),
135
+
136
+ /** Import a skill by cloning a git repo. */
137
+ importRepo: (body: { url: string; project_path?: string }) =>
138
+ http.post<CreateResult>("/skills/import/repo", body),
139
+
140
+ /** Delete a user skill (global or project-scoped). */
141
+ remove: (slug: string, projectPath?: string) => {
142
+ const qs = projectPath ? `?project_path=${encodeURIComponent(projectPath)}` : "";
143
+ return http.del<{ ok: boolean; slug: string }>(`/skills/${encodeURIComponent(slug)}${qs}`);
144
+ },
145
+
75
146
  /** Skill Inspector config + index status. */
76
147
  inspector: () => http.get<InspectorState>("/skills/inspector"),
77
148
 
@@ -4,7 +4,7 @@ import {
4
4
  Bot, Heart, Zap, Puzzle, FolderKanban, Settings,
5
5
  MessagesSquare, Send, KeyRound,
6
6
  LayoutDashboard, Boxes, Cpu, ScrollText, History, Brain, FileCode2,
7
- Building2, FileText, FolderTree,
7
+ Building2, FileText, FolderTree, Sparkles,
8
8
  } from "lucide-react";
9
9
  import { useNavCollapse, type TabSection } from "../components/common/TabNav";
10
10
  import { TabLayout } from "../components/common/TabLayout";
@@ -34,11 +34,12 @@ import { AgentDetailScreen } from "./project/AgentDetailScreen";
34
34
  import { StructureTab } from "./project/StructureTab";
35
35
  import { DocsTab } from "./project/DocsTab";
36
36
  import { FilesTab } from "./project/FilesTab";
37
+ import { SkillsTab } from "./project/SkillsTab";
37
38
 
38
39
  type NavKey =
39
40
  | "" | "chat" | "config" | "telegram"
40
41
  | "agents" | "routines" | "tasks" | "mcps" | "vars" | "logs" | "memories" | "artifacts"
41
- | "structure" | "docs" | "files";
42
+ | "structure" | "docs" | "files" | "skills";
42
43
 
43
44
  export function ProjectScreen() {
44
45
  const navigate = useNavigate();
@@ -75,6 +76,7 @@ export function ProjectScreen() {
75
76
  items: [
76
77
  { key: "agents", label: t("project.nav.agents"), icon: Bot },
77
78
  { key: "memories", label: t("project.nav.memories"), icon: Brain },
79
+ { key: "skills", label: t("skills_page.title"), icon: Sparkles },
78
80
  { key: "routines", label: t("project.nav.routines"), icon: Heart },
79
81
  { key: "mcps", label: t("project.nav.mcps"), icon: Puzzle },
80
82
  { key: "vars", label: t("project.nav.vars"), icon: KeyRound },
@@ -97,6 +99,7 @@ export function ProjectScreen() {
97
99
  { key: "agents", label: t("project.nav.agents"), icon: Bot },
98
100
  ...(isCompany ? [{ key: "structure", label: t("project.nav.structure"), icon: Building2 }] : []),
99
101
  { key: "memories", label: t("project.nav.memories"), icon: Brain },
102
+ { key: "skills", label: t("skills_page.title"), icon: Sparkles },
100
103
  ],
101
104
  },
102
105
  {
@@ -175,6 +178,7 @@ export function ProjectScreen() {
175
178
  <Route path="docs" element={<DocsTab pid={pid} />} />
176
179
  <Route path="files" element={<FilesTab pid={pid} />} />
177
180
  <Route path="memories" element={<MemoriesTab pid={pid} />} />
181
+ <Route path="skills" element={<SkillsTab pid={pid} />} />
178
182
  <Route path="routines" element={<RoutinesTab pid={pid} />} />
179
183
  <Route path="tasks" element={isBase ? <GlobalTasksTab /> : <TasksTab pid={pid} />} />
180
184
  <Route path="mcps" element={<McpsTab pid={pid} />} />
@@ -8,7 +8,7 @@ import { TabLayout } from "../components/common/TabLayout";
8
8
  import { IdentityPanel } from "../components/settings/IdentityPanel";
9
9
  import { SuperAgentPanel } from "../components/settings/SuperAgentPanel";
10
10
  import { MemoryPanel } from "../components/settings/MemoryPanel";
11
- import { SkillsInspectorPanel } from "../components/settings/SkillsInspectorPanel";
11
+ import { SkillsSettings } from "../components/settings/SkillsSettings";
12
12
  import { ModelsTab } from "./base/ModelsTab";
13
13
  import { TelegramSettingsTabs } from "../components/settings/TelegramSettingsTabs";
14
14
  import { DevicesPanel } from "../components/settings/DevicesPanel";
@@ -37,7 +37,7 @@ const SECTIONS: TabSection[] = [
37
37
  { key: "super_agent", label: t("settings.tabs.super_agent"), icon: Bot },
38
38
  { key: "engines", label: t("settings.tabs.engines"), icon: Cpu },
39
39
  { key: "memory", label: "Memory (RAG)", icon: Database },
40
- { key: "skills", label: "Skills (RAG)", icon: Sparkles },
40
+ { key: "skills", label: t("skills_page.title"), icon: Sparkles },
41
41
  ],
42
42
  },
43
43
  {
@@ -75,7 +75,7 @@ const PANELS: Record<TabKey, () => ReactElement> = {
75
75
  super_agent: () => <SuperAgentPanel />,
76
76
  engines: () => <ModelsTab />,
77
77
  memory: () => <MemoryPanel />,
78
- skills: () => <SkillsInspectorPanel />,
78
+ skills: () => <SkillsSettings />,
79
79
  telegram: () => <TelegramSettingsTabs />,
80
80
  devices: () => <DevicesPanel />,
81
81
  voice: () => <VoiceScreen />,
@@ -0,0 +1,13 @@
1
+ import { useProject } from "../../hooks/useProjects";
2
+ import { Loading } from "../../components/ui";
3
+ import { SkillsManager } from "../../components/settings/SkillsManager";
4
+
5
+ // Per-project skills view: the Claude-Desktop-style manager locked to THIS
6
+ // project's scope so you can enable/disable and add skills while working in it.
7
+ // The base project (pid "0" = super-agent admin) manages the "default" scope.
8
+ export function SkillsTab({ pid }: { pid: string }) {
9
+ const { project } = useProject(pid);
10
+ const scope = pid === "0" ? "default" : project?.path;
11
+ if (!scope) return <Loading />;
12
+ return <SkillsManager scope={scope} />;
13
+ }