@agentprojectcontext/apx 1.57.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.
- package/package.json +1 -1
- package/src/core/agent/skills/index.js +9 -0
- package/src/core/agent/skills/inspector.js +7 -2
- package/src/core/agent/skills/policy.js +128 -0
- package/src/core/agent/super-agent.js +8 -1
- package/src/core/agent/tools/handlers/list-skills.js +6 -3
- package/src/core/agent/tools/handlers/load-skill.js +9 -2
- package/src/core/apc/paths.js +7 -0
- package/src/core/stores/organization.js +152 -0
- package/src/core/stores/project-files.js +199 -0
- package/src/core/stores/tasks.js +36 -3
- package/src/host/daemon/api/agents.js +22 -2
- package/src/host/daemon/api/files-project.js +99 -0
- package/src/host/daemon/api/organization.js +88 -0
- package/src/host/daemon/api/shared.js +7 -0
- package/src/host/daemon/api/skills.js +301 -17
- package/src/host/daemon/api/tasks.js +14 -0
- package/src/host/daemon/api.js +4 -0
- package/src/interfaces/cli/commands/org.js +77 -0
- package/src/interfaces/cli/commands/skills.js +3 -2
- package/src/interfaces/cli/index.js +48 -0
- package/src/interfaces/web/dist/assets/index-CnQb4N6C.js +731 -0
- package/src/interfaces/web/dist/assets/index-CnQb4N6C.js.map +1 -0
- package/src/interfaces/web/dist/assets/index-Dv3X-zpx.css +1 -0
- package/src/interfaces/web/dist/index.html +2 -2
- package/src/interfaces/web/src/components/agents/AgentFormFields.tsx +123 -0
- package/src/interfaces/web/src/components/common/ConfirmDialog.tsx +51 -0
- package/src/interfaces/web/src/components/files/FileBrowser.tsx +138 -0
- package/src/interfaces/web/src/components/files/FileTree.tsx +133 -0
- package/src/interfaces/web/src/components/files/FileViewer.tsx +167 -0
- package/src/interfaces/web/src/components/files/MarkdownEditor.tsx +48 -0
- package/src/interfaces/web/src/components/files/MarkdownPreview.tsx +146 -0
- package/src/interfaces/web/src/components/files/NewFileDialog.tsx +66 -0
- package/src/interfaces/web/src/components/settings/SkillsManager.tsx +465 -0
- package/src/interfaces/web/src/components/settings/SkillsSettings.tsx +49 -0
- package/src/interfaces/web/src/components/structure/StructureDialogs.tsx +172 -0
- package/src/interfaces/web/src/components/tasks/TaskDetailPanel.tsx +142 -0
- package/src/interfaces/web/src/components/tasks/taskStatus.tsx +57 -0
- package/src/interfaces/web/src/i18n/en.ts +171 -0
- package/src/interfaces/web/src/i18n/es.ts +171 -0
- package/src/interfaces/web/src/lib/api/organization.ts +18 -0
- package/src/interfaces/web/src/lib/api/projectFiles.ts +19 -0
- package/src/interfaces/web/src/lib/api/skills.ts +79 -8
- package/src/interfaces/web/src/lib/api/tasks.ts +16 -1
- package/src/interfaces/web/src/lib/api.ts +2 -0
- package/src/interfaces/web/src/lib/slug.ts +11 -0
- package/src/interfaces/web/src/screens/ProjectScreen.tsx +25 -2
- package/src/interfaces/web/src/screens/SettingsScreen.tsx +3 -3
- package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +31 -11
- package/src/interfaces/web/src/screens/project/AgentsTab.tsx +24 -7
- package/src/interfaces/web/src/screens/project/DocsTab.tsx +13 -0
- package/src/interfaces/web/src/screens/project/FilesTab.tsx +12 -0
- package/src/interfaces/web/src/screens/project/Overview.tsx +122 -10
- package/src/interfaces/web/src/screens/project/SkillsTab.tsx +13 -0
- package/src/interfaces/web/src/screens/project/StructureTab.tsx +147 -0
- package/src/interfaces/web/src/screens/project/TasksTab.tsx +101 -62
- package/src/interfaces/web/src/types/daemon.ts +63 -0
- package/src/interfaces/web/dist/assets/index-CEI8DfVg.css +0 -1
- package/src/interfaces/web/dist/assets/index-DJ-ocXOR.js +0 -651
- package/src/interfaces/web/dist/assets/index-DJ-ocXOR.js.map +0 -1
|
@@ -33,6 +33,10 @@ export const es = {
|
|
|
33
33
|
hide: "Ocultar",
|
|
34
34
|
copy: "Copiar",
|
|
35
35
|
run: "Ejecutar",
|
|
36
|
+
refresh: "Refrescar",
|
|
37
|
+
view_all: "Ver todo",
|
|
38
|
+
saved: "Guardado.",
|
|
39
|
+
deleted: "Eliminado.",
|
|
36
40
|
pager_prev: "Anterior",
|
|
37
41
|
pager_next: "Siguiente",
|
|
38
42
|
pager_page: "Página {page} de {total}",
|
|
@@ -293,10 +297,14 @@ export const es = {
|
|
|
293
297
|
vars: "Variables",
|
|
294
298
|
logs: "Logs",
|
|
295
299
|
memories: "Memorias",
|
|
300
|
+
structure: "Estructura",
|
|
301
|
+
docs: "Docs",
|
|
302
|
+
files: "Archivos",
|
|
296
303
|
},
|
|
297
304
|
|
|
298
305
|
sections: {
|
|
299
306
|
workspace: "Workspace",
|
|
307
|
+
content: "Contenido",
|
|
300
308
|
automation: "Automatización",
|
|
301
309
|
knowledge: "Conversaciones",
|
|
302
310
|
config: "Config",
|
|
@@ -305,11 +313,18 @@ export const es = {
|
|
|
305
313
|
overview: {
|
|
306
314
|
tasks_open: "Tasks abiertas",
|
|
307
315
|
routines: "Rutinas",
|
|
316
|
+
routines_active: "Rutinas activas",
|
|
308
317
|
agents: "Agents",
|
|
309
318
|
mcps: "MCPs",
|
|
310
319
|
artifacts: "Artifacts",
|
|
311
320
|
chat: "Chat (super-agent)",
|
|
312
321
|
chat_value: "abrir",
|
|
322
|
+
roster: "Equipo",
|
|
323
|
+
no_agents: "Todavía no hay agentes.",
|
|
324
|
+
orchestrators: "Orquestadores",
|
|
325
|
+
specialists: "Especialistas",
|
|
326
|
+
recent_tasks: "Tasks recientes",
|
|
327
|
+
no_activity: "No hay tasks abiertas.",
|
|
313
328
|
},
|
|
314
329
|
|
|
315
330
|
artifacts: {
|
|
@@ -1299,6 +1314,95 @@ export const es = {
|
|
|
1299
1314
|
role_assigned: "{name} → {role}",
|
|
1300
1315
|
},
|
|
1301
1316
|
|
|
1317
|
+
agents_form: {
|
|
1318
|
+
emoji: "Emoji",
|
|
1319
|
+
area: "Área",
|
|
1320
|
+
role: "Rol",
|
|
1321
|
+
no_role: "— sin rol —",
|
|
1322
|
+
autonomy: "Autonomía",
|
|
1323
|
+
autonomy_hint: "Cuánto puede hacer el agente sin pedir confirmación.",
|
|
1324
|
+
auto_total: "Total",
|
|
1325
|
+
auto_automatico: "Auto",
|
|
1326
|
+
auto_permiso: "Permiso",
|
|
1327
|
+
},
|
|
1328
|
+
|
|
1329
|
+
structure: {
|
|
1330
|
+
title: "Estructura",
|
|
1331
|
+
subtitle: "Áreas y roles de la empresa. Las áreas agrupan agentes; los roles definen su función.",
|
|
1332
|
+
info: "Las áreas son agrupaciones opcionales. Los roles definen la función de un agente y pueden pertenecer a un área.",
|
|
1333
|
+
empty: "Todavía no hay áreas ni roles. Creá el primero arriba.",
|
|
1334
|
+
new_area: "Nueva área",
|
|
1335
|
+
new_role: "Nuevo rol",
|
|
1336
|
+
edit_area: "Editar área",
|
|
1337
|
+
edit_role: "Editar rol",
|
|
1338
|
+
create_area: "Crear área",
|
|
1339
|
+
create_role: "Crear rol",
|
|
1340
|
+
name: "Nombre",
|
|
1341
|
+
slug: "Slug",
|
|
1342
|
+
goal: "Objetivo",
|
|
1343
|
+
goal_hint: "Para qué existe esta área (opcional).",
|
|
1344
|
+
area: "Área",
|
|
1345
|
+
description: "Descripción",
|
|
1346
|
+
no_area: "— sin área —",
|
|
1347
|
+
roles: "Roles",
|
|
1348
|
+
add_role: "rol",
|
|
1349
|
+
no_roles: "sin roles",
|
|
1350
|
+
general_roles: "Roles generales",
|
|
1351
|
+
delete_area: "Borrar área",
|
|
1352
|
+
delete_role: "Borrar rol",
|
|
1353
|
+
delete_area_desc: "¿Borrar el área \"{name}\"? Sus roles quedan sin área, no se eliminan.",
|
|
1354
|
+
delete_role_desc: "¿Borrar el rol \"{name}\"?",
|
|
1355
|
+
},
|
|
1356
|
+
|
|
1357
|
+
files: {
|
|
1358
|
+
docs_label: "Docs",
|
|
1359
|
+
files_label: "Archivos",
|
|
1360
|
+
new_doc: "Nuevo documento",
|
|
1361
|
+
new_doc_hint: "Podés usar carpetas: cases/onboarding/spec.md",
|
|
1362
|
+
empty: "No hay archivos.",
|
|
1363
|
+
docs_empty: "Todavía no hay documentación. Creá el primer documento.",
|
|
1364
|
+
truncated: "Listado recortado (demasiados archivos).",
|
|
1365
|
+
select_prompt: "Elegí un archivo para verlo.",
|
|
1366
|
+
save: "Guardar",
|
|
1367
|
+
saved: "Guardado.",
|
|
1368
|
+
deleted: "Eliminado.",
|
|
1369
|
+
created: "Documento creado.",
|
|
1370
|
+
edit: "Editar",
|
|
1371
|
+
preview: "Preview",
|
|
1372
|
+
discard: "Descartar",
|
|
1373
|
+
no_preview: "No hay preview para este archivo.",
|
|
1374
|
+
too_large: "Archivo demasiado grande para mostrar.",
|
|
1375
|
+
path_label: "Ruta del archivo",
|
|
1376
|
+
path_example: "ej. cases/onboarding/spec.md",
|
|
1377
|
+
create: "Crear",
|
|
1378
|
+
},
|
|
1379
|
+
|
|
1380
|
+
tasks: {
|
|
1381
|
+
state_open: "abiertas",
|
|
1382
|
+
state_done: "hechas",
|
|
1383
|
+
state_dropped: "descartadas",
|
|
1384
|
+
status_pending: "pendiente",
|
|
1385
|
+
status_running: "corriendo",
|
|
1386
|
+
status_in_review: "en revisión",
|
|
1387
|
+
status_blocked: "bloqueada",
|
|
1388
|
+
done_label: "hecha",
|
|
1389
|
+
dropped_label: "descartada",
|
|
1390
|
+
detail_title: "Detalle de task",
|
|
1391
|
+
field_title: "Título",
|
|
1392
|
+
field_prompt: "Prompt",
|
|
1393
|
+
field_status: "Estado",
|
|
1394
|
+
field_agent: "Agente",
|
|
1395
|
+
field_creator: "Creada por",
|
|
1396
|
+
field_source: "Origen",
|
|
1397
|
+
field_created: "Creada",
|
|
1398
|
+
field_updated: "Actualizada",
|
|
1399
|
+
field_done: "Completada",
|
|
1400
|
+
prompt_ph: "Descripción / prompt de la task…",
|
|
1401
|
+
toggle_prompt: "Prompt",
|
|
1402
|
+
view_thread: "Ver conversación",
|
|
1403
|
+
mark_done: "Completar",
|
|
1404
|
+
},
|
|
1405
|
+
|
|
1302
1406
|
agents_ui: {
|
|
1303
1407
|
model_router_default: "modelo: default del router",
|
|
1304
1408
|
slug_kebab_hint: "kebab-case, ej. reviewer, my-agent, content-writer",
|
|
@@ -1579,6 +1683,73 @@ export const es = {
|
|
|
1579
1683
|
cfg_apx_storage_id: "ID de storage APX",
|
|
1580
1684
|
},
|
|
1581
1685
|
|
|
1686
|
+
skills_page: {
|
|
1687
|
+
title: "Skills",
|
|
1688
|
+
desc: "Activá o desactivá qué skills carga cada agente. Elegí el scope: el super-agent (global) o un proyecto puntual.",
|
|
1689
|
+
list_title: "Skills instaladas",
|
|
1690
|
+
list_desc: "Las privadas de APX están siempre activas y no se pueden tocar.",
|
|
1691
|
+
scope_label: "Scope",
|
|
1692
|
+
scope_super_agent: "Super-agent (global)",
|
|
1693
|
+
scope_hint: "El super-agent usa el scope global. Cada proyecto puede sobrescribir skills de forma independiente.",
|
|
1694
|
+
count_label: "{n} skills · {on} activas",
|
|
1695
|
+
empty: "No hay skills. Creá una abajo o instalá con la CLI.",
|
|
1696
|
+
source_builtin: "APX",
|
|
1697
|
+
source_global: "Global",
|
|
1698
|
+
source_project: "Proyecto",
|
|
1699
|
+
private_badge: "Privada",
|
|
1700
|
+
private_hint: "Skill interna de APX — siempre activa, no se puede desactivar ni borrar.",
|
|
1701
|
+
overridden_badge: "Override",
|
|
1702
|
+
inherited_hint: "Heredada del global",
|
|
1703
|
+
reset_to_global: "Volver al global",
|
|
1704
|
+
on: "activa",
|
|
1705
|
+
off: "inactiva",
|
|
1706
|
+
toggle_failed: "No se pudo cambiar el estado: {msg}",
|
|
1707
|
+
add_title: "Agregar skill",
|
|
1708
|
+
add_desc: "Crea una skill de usuario en ~/.apx/skills/<slug>/SKILL.md. Queda disponible para todos los scopes.",
|
|
1709
|
+
add_slug_label: "Slug",
|
|
1710
|
+
add_slug_ph: "mi-skill",
|
|
1711
|
+
add_desc_label: "Descripción",
|
|
1712
|
+
add_desc_ph: "Una línea que explique cuándo usarla",
|
|
1713
|
+
add_body_label: "Cuerpo (Markdown)",
|
|
1714
|
+
add_body_ph: "# Mi skill\n\nInstrucciones para el agente…",
|
|
1715
|
+
add_btn: "Crear skill",
|
|
1716
|
+
created_ok: "Skill \"{slug}\" creada.",
|
|
1717
|
+
create_failed: "No se pudo crear: {msg}",
|
|
1718
|
+
delete_btn: "Borrar",
|
|
1719
|
+
delete_confirm: "¿Borrar la skill \"{slug}\"? No se puede deshacer.",
|
|
1720
|
+
deleted_ok: "Skill \"{slug}\" borrada.",
|
|
1721
|
+
delete_failed: "No se pudo borrar: {msg}",
|
|
1722
|
+
inspector_section_title: "Skill Inspector (RAG por turno)",
|
|
1723
|
+
inspector_section_desc: "Config avanzada: RAG local que inyecta solo las skills que el mensaje necesita.",
|
|
1724
|
+
scope_ph: "— elegir scope —",
|
|
1725
|
+
select_a_skill: "Elegí una skill de la lista para ver su contenido.",
|
|
1726
|
+
added_by: "Agregado por",
|
|
1727
|
+
activator: "Activador",
|
|
1728
|
+
by_apx: "APX (built-in)",
|
|
1729
|
+
by_you: "Vos",
|
|
1730
|
+
activator_value: "Coincidencia semántica (RAG)",
|
|
1731
|
+
tab_preview: "Vista",
|
|
1732
|
+
tab_source: "Fuente",
|
|
1733
|
+
add_menu: "Agregar",
|
|
1734
|
+
add_online: "Crear con el editor",
|
|
1735
|
+
add_online_hint: "Escribí slug + descripción + contenido",
|
|
1736
|
+
add_zip: "Subir .zip",
|
|
1737
|
+
add_zip_hint: "Importar una skill empaquetada",
|
|
1738
|
+
add_repo: "Desde repo git",
|
|
1739
|
+
add_repo_hint: "Clonar desde una URL",
|
|
1740
|
+
create_dialog_title: "Crear skill",
|
|
1741
|
+
repo_dialog_title: "Importar desde repo git",
|
|
1742
|
+
repo_url_label: "URL del repo",
|
|
1743
|
+
repo_url_ph: "https://github.com/usuario/mi-skill.git",
|
|
1744
|
+
repo_url_hint: "El repo (o su subcarpeta) debe tener un SKILL.md.",
|
|
1745
|
+
import_btn: "Importar",
|
|
1746
|
+
imported_ok: "Skill \"{slug}\" importada.",
|
|
1747
|
+
import_failed: "No se pudo importar: {msg}",
|
|
1748
|
+
cancel: "Cancelar",
|
|
1749
|
+
manager_tab: "Skills",
|
|
1750
|
+
rag_tab: "Config (RAG)",
|
|
1751
|
+
},
|
|
1752
|
+
|
|
1582
1753
|
shared_ui: {
|
|
1583
1754
|
skill_inspector_title: "Skill Inspector ({embedder}) eligió estas skills para este turno",
|
|
1584
1755
|
tools_count: "{n} tools",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { http } from "../http";
|
|
2
|
+
import type { Organization, OrgArea, OrgRole } from "../../types/daemon";
|
|
3
|
+
|
|
4
|
+
export const Org = {
|
|
5
|
+
get: (pid: string) => http.get<Organization>(`/projects/${pid}/organization`),
|
|
6
|
+
createArea: (pid: string, body: { name: string; slug?: string; goal?: string | null }) =>
|
|
7
|
+
http.post<OrgArea>(`/projects/${pid}/organization/areas`, body),
|
|
8
|
+
updateArea: (pid: string, slug: string, patch: { name?: string; goal?: string | null }) =>
|
|
9
|
+
http.patch<OrgArea>(`/projects/${pid}/organization/areas/${encodeURIComponent(slug)}`, patch),
|
|
10
|
+
removeArea: (pid: string, slug: string) =>
|
|
11
|
+
http.del<{ ok: boolean }>(`/projects/${pid}/organization/areas/${encodeURIComponent(slug)}`),
|
|
12
|
+
createRole: (pid: string, body: { name: string; slug?: string; area?: string | null; description?: string | null }) =>
|
|
13
|
+
http.post<OrgRole>(`/projects/${pid}/organization/roles`, body),
|
|
14
|
+
updateRole: (pid: string, slug: string, patch: { name?: string; area?: string | null; description?: string | null }) =>
|
|
15
|
+
http.patch<OrgRole>(`/projects/${pid}/organization/roles/${encodeURIComponent(slug)}`, patch),
|
|
16
|
+
removeRole: (pid: string, slug: string) =>
|
|
17
|
+
http.del<{ ok: boolean }>(`/projects/${pid}/organization/roles/${encodeURIComponent(slug)}`),
|
|
18
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { http } from "../http";
|
|
2
|
+
import type { FileTreeResponse, FileContent } from "../../types/daemon";
|
|
3
|
+
|
|
4
|
+
export type FileScope = "project" | "docs";
|
|
5
|
+
|
|
6
|
+
// Project file browser + docs editor. `scope` picks the sandbox: the whole
|
|
7
|
+
// repo ("project") or the docs subfolder ("docs"). Same store, different root.
|
|
8
|
+
export const ProjectFiles = {
|
|
9
|
+
tree: (pid: string, scope: FileScope = "project") =>
|
|
10
|
+
http.get<FileTreeResponse>(`/projects/${pid}/fs/tree?scope=${scope}`),
|
|
11
|
+
read: (pid: string, path: string, scope: FileScope = "project") =>
|
|
12
|
+
http.get<FileContent>(`/projects/${pid}/fs/file?scope=${scope}&path=${encodeURIComponent(path)}`),
|
|
13
|
+
write: (pid: string, path: string, content: string, scope: FileScope = "project") =>
|
|
14
|
+
http.put<{ ok: boolean; path: string; bytes: number }>(`/projects/${pid}/fs/file`, { scope, path, content }),
|
|
15
|
+
mkdir: (pid: string, path: string, scope: FileScope = "project") =>
|
|
16
|
+
http.post<{ ok: boolean; path: string }>(`/projects/${pid}/fs/dir`, { scope, path }),
|
|
17
|
+
remove: (pid: string, path: string, scope: FileScope = "project") =>
|
|
18
|
+
http.del<{ ok: boolean }>(`/projects/${pid}/fs/entry?scope=${scope}&path=${encodeURIComponent(path)}`),
|
|
19
|
+
};
|
|
@@ -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:
|
|
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 (
|
|
66
|
-
*
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { http, unwrapPage } from "../http";
|
|
2
|
-
import type { TaskEntry } from "../../types/daemon";
|
|
2
|
+
import type { TaskEntry, TaskStatus } from "../../types/daemon";
|
|
3
3
|
|
|
4
4
|
export interface GlobalTaskEntry extends TaskEntry {
|
|
5
5
|
project_id: number;
|
|
6
6
|
project_name: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
export interface TaskSummary {
|
|
10
|
+
open: number;
|
|
11
|
+
done: number;
|
|
12
|
+
dropped: number;
|
|
13
|
+
overdue: number;
|
|
14
|
+
total: number;
|
|
15
|
+
status: Record<TaskStatus, number>;
|
|
16
|
+
}
|
|
17
|
+
|
|
9
18
|
export const Tasks = {
|
|
10
19
|
// Full sets (no pagination) — unwrapped to plain arrays for non-paged callers.
|
|
11
20
|
list: (pid: string, state: TaskEntry["state"] | "all" = "open") =>
|
|
@@ -18,9 +27,15 @@ export const Tasks = {
|
|
|
18
27
|
http.get<unknown>(`/projects/${pid}/tasks?state=${state}&limit=${limit}&offset=${offset}`).then((b) => unwrapPage<TaskEntry>(b)),
|
|
19
28
|
globalPage: ({ state, limit, offset }: { state: TaskEntry["state"] | "all"; limit: number; offset: number }) =>
|
|
20
29
|
http.get<unknown>(`/tasks?state=${state}&limit=${limit}&offset=${offset}`).then((b) => unwrapPage<GlobalTaskEntry>(b)),
|
|
30
|
+
get: (pid: string, id: string) => http.get<TaskEntry>(`/projects/${pid}/tasks/${id}`),
|
|
21
31
|
add: (pid: string, body: Partial<TaskEntry>) =>
|
|
22
32
|
http.post<TaskEntry>(`/projects/${pid}/tasks`, body),
|
|
33
|
+
patch: (pid: string, id: string, patch: Partial<TaskEntry>) =>
|
|
34
|
+
http.patch<TaskEntry>(`/projects/${pid}/tasks/${id}`, { patch }),
|
|
35
|
+
status: (pid: string, id: string, status: TaskStatus) =>
|
|
36
|
+
http.post<TaskEntry>(`/projects/${pid}/tasks/${id}/status`, { status }),
|
|
23
37
|
done: (pid: string, id: string) => http.post<TaskEntry>(`/projects/${pid}/tasks/${id}/done`),
|
|
24
38
|
drop: (pid: string, id: string) => http.post<TaskEntry>(`/projects/${pid}/tasks/${id}/drop`),
|
|
25
39
|
reopen: (pid: string, id: string) => http.post<TaskEntry>(`/projects/${pid}/tasks/${id}/reopen`),
|
|
40
|
+
summary: (pid: string) => http.get<TaskSummary>(`/projects/${pid}/tasks-summary`),
|
|
26
41
|
};
|
|
@@ -24,6 +24,8 @@ export * from "./api/deck";
|
|
|
24
24
|
export * from "./api/code";
|
|
25
25
|
export * from "./api/artifacts";
|
|
26
26
|
export * from "./api/skills";
|
|
27
|
+
export * from "./api/organization";
|
|
28
|
+
export * from "./api/projectFiles";
|
|
27
29
|
|
|
28
30
|
// Re-export the daemon types so older imports of "../lib/api" still work.
|
|
29
31
|
export type {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Kebab-case a free-text name into a slug. Mirrors the backend's slugifyName
|
|
2
|
+
// (core/stores/organization.js) so a name typed in the web produces the same
|
|
3
|
+
// slug the daemon would derive.
|
|
4
|
+
export function slugify(name: string): string {
|
|
5
|
+
return String(name || "")
|
|
6
|
+
.trim()
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
9
|
+
.replace(/^-+|-+$/g, "")
|
|
10
|
+
.replace(/-{2,}/g, "-");
|
|
11
|
+
}
|
|
@@ -4,6 +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, Sparkles,
|
|
7
8
|
} from "lucide-react";
|
|
8
9
|
import { useNavCollapse, type TabSection } from "../components/common/TabNav";
|
|
9
10
|
import { TabLayout } from "../components/common/TabLayout";
|
|
@@ -30,10 +31,15 @@ import { TelegramTab } from "./project/TelegramTab";
|
|
|
30
31
|
import { MemoriesTab } from "./project/MemoriesTab";
|
|
31
32
|
import { ArtifactsTab } from "./project/ArtifactsTab";
|
|
32
33
|
import { AgentDetailScreen } from "./project/AgentDetailScreen";
|
|
34
|
+
import { StructureTab } from "./project/StructureTab";
|
|
35
|
+
import { DocsTab } from "./project/DocsTab";
|
|
36
|
+
import { FilesTab } from "./project/FilesTab";
|
|
37
|
+
import { SkillsTab } from "./project/SkillsTab";
|
|
33
38
|
|
|
34
39
|
type NavKey =
|
|
35
40
|
| "" | "chat" | "config" | "telegram"
|
|
36
|
-
| "agents" | "routines" | "tasks" | "mcps" | "vars" | "logs" | "memories" | "artifacts"
|
|
41
|
+
| "agents" | "routines" | "tasks" | "mcps" | "vars" | "logs" | "memories" | "artifacts"
|
|
42
|
+
| "structure" | "docs" | "files" | "skills";
|
|
37
43
|
|
|
38
44
|
export function ProjectScreen() {
|
|
39
45
|
const navigate = useNavigate();
|
|
@@ -70,6 +76,7 @@ export function ProjectScreen() {
|
|
|
70
76
|
items: [
|
|
71
77
|
{ key: "agents", label: t("project.nav.agents"), icon: Bot },
|
|
72
78
|
{ key: "memories", label: t("project.nav.memories"), icon: Brain },
|
|
79
|
+
{ key: "skills", label: t("skills_page.title"), icon: Sparkles },
|
|
73
80
|
{ key: "routines", label: t("project.nav.routines"), icon: Heart },
|
|
74
81
|
{ key: "mcps", label: t("project.nav.mcps"), icon: Puzzle },
|
|
75
82
|
{ key: "vars", label: t("project.nav.vars"), icon: KeyRound },
|
|
@@ -79,6 +86,9 @@ export function ProjectScreen() {
|
|
|
79
86
|
},
|
|
80
87
|
];
|
|
81
88
|
}
|
|
89
|
+
// Structure (org roles/areas) is only meaningful for company/enterprise
|
|
90
|
+
// projects — gate it on the project kind.
|
|
91
|
+
const isCompany = project?.kind === "company";
|
|
82
92
|
return [
|
|
83
93
|
{
|
|
84
94
|
title: t("project.sections.workspace"),
|
|
@@ -87,7 +97,16 @@ export function ProjectScreen() {
|
|
|
87
97
|
{ key: "telegram", label: t("project.nav.telegram"), icon: Send },
|
|
88
98
|
{ key: "chat", label: t("project.nav.chat"), icon: MessagesSquare },
|
|
89
99
|
{ key: "agents", label: t("project.nav.agents"), icon: Bot },
|
|
100
|
+
...(isCompany ? [{ key: "structure", label: t("project.nav.structure"), icon: Building2 }] : []),
|
|
90
101
|
{ key: "memories", label: t("project.nav.memories"), icon: Brain },
|
|
102
|
+
{ key: "skills", label: t("skills_page.title"), icon: Sparkles },
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
title: t("project.sections.content"),
|
|
107
|
+
items: [
|
|
108
|
+
{ key: "docs", label: t("project.nav.docs"), icon: FileText },
|
|
109
|
+
{ key: "files", label: t("project.nav.files"), icon: FolderTree },
|
|
91
110
|
],
|
|
92
111
|
},
|
|
93
112
|
{
|
|
@@ -108,7 +127,7 @@ export function ProjectScreen() {
|
|
|
108
127
|
],
|
|
109
128
|
},
|
|
110
129
|
];
|
|
111
|
-
}, [isBase]);
|
|
130
|
+
}, [isBase, project?.kind]);
|
|
112
131
|
|
|
113
132
|
// First path segment after /p/:pid — so deep routes like agents/:slug still
|
|
114
133
|
// highlight the "agents" nav item.
|
|
@@ -155,7 +174,11 @@ export function ProjectScreen() {
|
|
|
155
174
|
<Route path="telegram" element={<TelegramTab pid={pid} />} />
|
|
156
175
|
<Route path="agents" element={<AgentsTab pid={pid} />} />
|
|
157
176
|
<Route path="agents/:slug" element={<AgentDetailScreen pid={pid} />} />
|
|
177
|
+
<Route path="structure" element={<StructureTab pid={pid} />} />
|
|
178
|
+
<Route path="docs" element={<DocsTab pid={pid} />} />
|
|
179
|
+
<Route path="files" element={<FilesTab pid={pid} />} />
|
|
158
180
|
<Route path="memories" element={<MemoriesTab pid={pid} />} />
|
|
181
|
+
<Route path="skills" element={<SkillsTab pid={pid} />} />
|
|
159
182
|
<Route path="routines" element={<RoutinesTab pid={pid} />} />
|
|
160
183
|
<Route path="tasks" element={isBase ? <GlobalTasksTab /> : <TasksTab pid={pid} />} />
|
|
161
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 {
|
|
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: "
|
|
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: () => <
|
|
78
|
+
skills: () => <SkillsSettings />,
|
|
79
79
|
telegram: () => <TelegramSettingsTabs />,
|
|
80
80
|
devices: () => <DevicesPanel />,
|
|
81
81
|
voice: () => <VoiceScreen />,
|
|
@@ -12,8 +12,11 @@ import { Badge, Button, Field, Input, Loading, Switch, Textarea } from "../../co
|
|
|
12
12
|
import { Tip } from "../../components/ui/tip";
|
|
13
13
|
import { UiSelect } from "../../components/UiSelect";
|
|
14
14
|
import { useToast } from "../../components/Toast";
|
|
15
|
+
import { ConfirmDialog } from "../../components/common/ConfirmDialog";
|
|
16
|
+
import { EmojiInput, AutonomyPicker, AreaRoleFields } from "../../components/agents/AgentFormFields";
|
|
15
17
|
import { cn } from "../../lib/cn";
|
|
16
18
|
import { t } from "../../i18n";
|
|
19
|
+
import type { AgentAutonomy } from "../../types/daemon";
|
|
17
20
|
import { AgentBrainGraph, type BrainNode } from "./AgentBrainGraph";
|
|
18
21
|
|
|
19
22
|
type TabKey = "overview" | "memories" | "records" | "sleep" | "brain" | "config";
|
|
@@ -28,7 +31,7 @@ function buildTabs(): { key: TabKey; label: string; icon: typeof Bot }[] {
|
|
|
28
31
|
];
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
function typeOptions() {
|
|
34
|
+
export function typeOptions() {
|
|
32
35
|
return [
|
|
33
36
|
{ value: "", label: t("agents_ui.type_none") },
|
|
34
37
|
{ value: "orchestrator", label: t("agents_ui.type_orchestrator"), description: t("agents_ui.type_orchestrator_desc") },
|
|
@@ -205,9 +208,11 @@ function AgentConfigForm({
|
|
|
205
208
|
onDeleted: () => void;
|
|
206
209
|
}) {
|
|
207
210
|
const toast = useToast();
|
|
211
|
+
const [emoji, setEmoji] = useState(agent.emoji || "");
|
|
208
212
|
const [type, setType] = useState(agent.type || "");
|
|
209
213
|
const [area, setArea] = useState(agent.area || "");
|
|
210
214
|
const [role, setRole] = useState(agent.role || "");
|
|
215
|
+
const [autonomy, setAutonomy] = useState<AgentAutonomy | "">(agent.autonomy || "");
|
|
211
216
|
const [model, setModel] = useState(agent.model || "");
|
|
212
217
|
const [parent, setParent] = useState(agent.parent || "");
|
|
213
218
|
const [isMaster, setIsMaster] = useState(!!agent.is_master);
|
|
@@ -216,14 +221,17 @@ function AgentConfigForm({
|
|
|
216
221
|
const [description, setDescription] = useState(agent.description || "");
|
|
217
222
|
const [system, setSystem] = useState(agent.system || "");
|
|
218
223
|
const [busy, setBusy] = useState(false);
|
|
224
|
+
const [confirmDelete, setConfirmDelete] = useState(false);
|
|
219
225
|
|
|
220
226
|
const save = async () => {
|
|
221
227
|
setBusy(true);
|
|
222
228
|
try {
|
|
223
229
|
await Agents.update(pid, agent.slug, {
|
|
230
|
+
emoji: emoji || null,
|
|
224
231
|
type: type || null,
|
|
225
232
|
area: area || null,
|
|
226
233
|
role: role || null,
|
|
234
|
+
autonomy: autonomy || null,
|
|
227
235
|
model: model || null,
|
|
228
236
|
parent: parent || null,
|
|
229
237
|
is_master: isMaster || type === "orchestrator",
|
|
@@ -239,20 +247,23 @@ function AgentConfigForm({
|
|
|
239
247
|
};
|
|
240
248
|
|
|
241
249
|
const del = async () => {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
250
|
+
await Agents.remove(pid, agent.slug);
|
|
251
|
+
toast.success(t("project.agent_detail.delete_success"));
|
|
252
|
+
onDeleted();
|
|
245
253
|
};
|
|
246
254
|
|
|
247
255
|
return (
|
|
248
256
|
<Section title={t("project.agent_detail.config_title")} description={`.apc/agents/${agent.slug}.md — ${t("agents_ui.config_def_desc")}`}>
|
|
249
257
|
<div className="space-y-3">
|
|
250
|
-
<div className="grid grid-cols-
|
|
258
|
+
<div className="grid grid-cols-[80px_1fr] gap-3">
|
|
259
|
+
<Field label={t("agents_form.emoji")}><EmojiInput value={emoji} onChange={setEmoji} /></Field>
|
|
251
260
|
<Field label={t("project.agent_detail.type_label")}><UiSelect value={type} onChange={setType} options={typeOptions()} /></Field>
|
|
252
|
-
<Field label={t("project.agent_detail.area_label")} hint={t("project.agent_detail.area_hint")}><Input value={area} onChange={(e) => setArea(e.target.value)} placeholder={t("project.agent_detail.area_ph")} /></Field>
|
|
253
261
|
</div>
|
|
262
|
+
<AreaRoleFields pid={pid} area={area} role={role} onArea={setArea} onRole={setRole} />
|
|
263
|
+
<Field label={t("agents_form.autonomy")} hint={t("agents_form.autonomy_hint")}>
|
|
264
|
+
<AutonomyPicker value={autonomy} onChange={setAutonomy} />
|
|
265
|
+
</Field>
|
|
254
266
|
<div className="grid grid-cols-2 gap-3">
|
|
255
|
-
<Field label={t("project.agent_detail.role_label")}><Input value={role} onChange={(e) => setRole(e.target.value)} placeholder="Operations Lead" /></Field>
|
|
256
267
|
<Field label={t("project.agent_detail.parent_label")}>
|
|
257
268
|
<UiSelect
|
|
258
269
|
value={parent}
|
|
@@ -261,10 +272,10 @@ function AgentConfigForm({
|
|
|
261
272
|
options={[{ value: "", label: t("project.agent_detail.none_parent") }, ...agents.filter((x) => x.slug !== agent.slug).map((x) => ({ value: x.slug, label: x.slug }))]}
|
|
262
273
|
/>
|
|
263
274
|
</Field>
|
|
275
|
+
<Field label={t("project.agent_detail.model_label")} hint={t("project.agent_detail.model_hint")}>
|
|
276
|
+
<Input value={model} onChange={(e) => setModel(e.target.value)} placeholder={t("project.agent_detail.model_ph")} />
|
|
277
|
+
</Field>
|
|
264
278
|
</div>
|
|
265
|
-
<Field label={t("project.agent_detail.model_label")} hint={t("project.agent_detail.model_hint")}>
|
|
266
|
-
<Input value={model} onChange={(e) => setModel(e.target.value)} placeholder={t("project.agent_detail.model_ph")} />
|
|
267
|
-
</Field>
|
|
268
279
|
<Field label={t("project.agent_detail.skills_label")}><Input value={skills} onChange={(e) => setSkills(e.target.value)} placeholder="skill-a, skill-b" /></Field>
|
|
269
280
|
<ToolsPicker value={tools} onChange={setTools} />
|
|
270
281
|
<Field label={t("project.agent_detail.bio_label")}><Textarea rows={2} value={description} onChange={(e) => setDescription(e.target.value)} /></Field>
|
|
@@ -274,10 +285,19 @@ function AgentConfigForm({
|
|
|
274
285
|
<Switch checked={isMaster} onChange={setIsMaster} label={t("project.agent_detail.master_label")} />
|
|
275
286
|
|
|
276
287
|
<div className="flex items-center justify-between border-t border-border pt-3">
|
|
277
|
-
<Button variant="destructive" onClick={
|
|
288
|
+
<Button variant="destructive" onClick={() => setConfirmDelete(true)}><Trash2 size={13} /> {t("project.agent_detail.delete_btn")}</Button>
|
|
278
289
|
<Button variant="primary" loading={busy} onClick={save}><Save size={13} /> {t("project.agent_detail.save_btn")}</Button>
|
|
279
290
|
</div>
|
|
280
291
|
</div>
|
|
292
|
+
|
|
293
|
+
<ConfirmDialog
|
|
294
|
+
open={confirmDelete}
|
|
295
|
+
onClose={() => setConfirmDelete(false)}
|
|
296
|
+
onConfirm={del}
|
|
297
|
+
title={t("project.agent_detail.delete_btn")}
|
|
298
|
+
description={t("project.agent_detail.delete_confirm", { slug: agent.slug })}
|
|
299
|
+
confirmLabel={t("project.agent_detail.delete_btn")}
|
|
300
|
+
/>
|
|
281
301
|
</Section>
|
|
282
302
|
);
|
|
283
303
|
}
|