@agentprojectcontext/apx 1.57.0 → 1.58.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/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/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/index.js +48 -0
- package/src/interfaces/web/dist/assets/index-Cl0WXtxF.css +1 -0
- package/src/interfaces/web/dist/assets/index-DPAuXATr.js +705 -0
- package/src/interfaces/web/dist/assets/index-DPAuXATr.js.map +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/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 +104 -0
- package/src/interfaces/web/src/i18n/es.ts +104 -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/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 +21 -2
- 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/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
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Organization structure (areas + roles) per project.
|
|
2
|
+
//
|
|
3
|
+
// GET /projects/:pid/organization -> { areas, roles }
|
|
4
|
+
// POST /projects/:pid/organization/areas body { name, slug?, goal? }
|
|
5
|
+
// PATCH /projects/:pid/organization/areas/:slug body { name?, goal? }
|
|
6
|
+
// DELETE /projects/:pid/organization/areas/:slug -> { ok }
|
|
7
|
+
// POST /projects/:pid/organization/roles body { name, slug?, area?, description? }
|
|
8
|
+
// PATCH /projects/:pid/organization/roles/:slug body { name?, area?, description? }
|
|
9
|
+
// DELETE /projects/:pid/organization/roles/:slug -> { ok }
|
|
10
|
+
//
|
|
11
|
+
// Thin adapter: parse body, call core/stores/organization, shape the response.
|
|
12
|
+
import {
|
|
13
|
+
readOrganization,
|
|
14
|
+
createArea,
|
|
15
|
+
updateArea,
|
|
16
|
+
removeArea,
|
|
17
|
+
createRole,
|
|
18
|
+
updateRole,
|
|
19
|
+
removeRole,
|
|
20
|
+
} from "#core/stores/organization.js";
|
|
21
|
+
|
|
22
|
+
export function register(app, { project }) {
|
|
23
|
+
app.get("/projects/:pid/organization", (req, res) => {
|
|
24
|
+
const p = project(req, res);
|
|
25
|
+
if (!p) return;
|
|
26
|
+
res.json(readOrganization(p.path));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
app.post("/projects/:pid/organization/areas", (req, res) => {
|
|
30
|
+
const p = project(req, res);
|
|
31
|
+
if (!p) return;
|
|
32
|
+
try {
|
|
33
|
+
res.status(201).json(createArea(p.path, req.body || {}));
|
|
34
|
+
} catch (e) {
|
|
35
|
+
res.status(400).json({ error: e.message });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
app.patch("/projects/:pid/organization/areas/:slug", (req, res) => {
|
|
40
|
+
const p = project(req, res);
|
|
41
|
+
if (!p) return;
|
|
42
|
+
try {
|
|
43
|
+
const area = updateArea(p.path, req.params.slug, req.body || {});
|
|
44
|
+
if (!area) return res.status(404).json({ error: "area not found" });
|
|
45
|
+
res.json(area);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
res.status(400).json({ error: e.message });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
app.delete("/projects/:pid/organization/areas/:slug", (req, res) => {
|
|
52
|
+
const p = project(req, res);
|
|
53
|
+
if (!p) return;
|
|
54
|
+
if (!removeArea(p.path, req.params.slug))
|
|
55
|
+
return res.status(404).json({ error: "area not found" });
|
|
56
|
+
res.json({ ok: true });
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
app.post("/projects/:pid/organization/roles", (req, res) => {
|
|
60
|
+
const p = project(req, res);
|
|
61
|
+
if (!p) return;
|
|
62
|
+
try {
|
|
63
|
+
res.status(201).json(createRole(p.path, req.body || {}));
|
|
64
|
+
} catch (e) {
|
|
65
|
+
res.status(400).json({ error: e.message });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
app.patch("/projects/:pid/organization/roles/:slug", (req, res) => {
|
|
70
|
+
const p = project(req, res);
|
|
71
|
+
if (!p) return;
|
|
72
|
+
try {
|
|
73
|
+
const role = updateRole(p.path, req.params.slug, req.body || {});
|
|
74
|
+
if (!role) return res.status(404).json({ error: "role not found" });
|
|
75
|
+
res.json(role);
|
|
76
|
+
} catch (e) {
|
|
77
|
+
res.status(400).json({ error: e.message });
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
app.delete("/projects/:pid/organization/roles/:slug", (req, res) => {
|
|
82
|
+
const p = project(req, res);
|
|
83
|
+
if (!p) return;
|
|
84
|
+
if (!removeRole(p.path, req.params.slug))
|
|
85
|
+
return res.status(404).json({ error: "role not found" });
|
|
86
|
+
res.json({ ok: true });
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -210,6 +210,8 @@ export function agentToResponse(a) {
|
|
|
210
210
|
"Parent",
|
|
211
211
|
"Type",
|
|
212
212
|
"Area",
|
|
213
|
+
"Emoji",
|
|
214
|
+
"Autonomy",
|
|
213
215
|
]);
|
|
214
216
|
const extra = {};
|
|
215
217
|
for (const [k, v] of Object.entries(f)) {
|
|
@@ -229,6 +231,11 @@ export function agentToResponse(a) {
|
|
|
229
231
|
// definitional, kept in APC frontmatter.
|
|
230
232
|
type: f.Type || null,
|
|
231
233
|
area: f.Area || null,
|
|
234
|
+
// Display emoji (avatar) + autonomy (permission mode: total/automatico/
|
|
235
|
+
// permiso). Definitional, kept in APC frontmatter so they travel with the
|
|
236
|
+
// project and stay diffable.
|
|
237
|
+
emoji: f.Emoji || null,
|
|
238
|
+
autonomy: f.Autonomy || null,
|
|
232
239
|
skills: Array.isArray(f.Skills) ? f.Skills : [],
|
|
233
240
|
tools: Array.isArray(f.Tools) ? f.Tools : [],
|
|
234
241
|
extra,
|
|
@@ -14,7 +14,9 @@ import {
|
|
|
14
14
|
doneTask,
|
|
15
15
|
dropTask,
|
|
16
16
|
reopenTask,
|
|
17
|
+
setTaskStatus,
|
|
17
18
|
countTasks,
|
|
19
|
+
TASK_STATUSES,
|
|
18
20
|
} from "#core/stores/tasks.js";
|
|
19
21
|
import { pageEnvelope } from "./shared.js";
|
|
20
22
|
|
|
@@ -113,6 +115,18 @@ export function register(app, { project, projects }) {
|
|
|
113
115
|
res.json(updated);
|
|
114
116
|
});
|
|
115
117
|
|
|
118
|
+
// Move an open task through its workflow (pending → running → in_review …).
|
|
119
|
+
app.post("/projects/:pid/tasks/:id/status", (req, res) => {
|
|
120
|
+
const p = project(req, res);
|
|
121
|
+
if (!p) return;
|
|
122
|
+
const { status } = req.body || {};
|
|
123
|
+
if (!TASK_STATUSES.includes(status))
|
|
124
|
+
return res.status(400).json({ error: `status must be one of ${TASK_STATUSES.join(", ")}` });
|
|
125
|
+
const updated = setTaskStatus(p.storagePath, req.params.id, status);
|
|
126
|
+
if (!updated) return res.status(404).json({ error: "task not found" });
|
|
127
|
+
res.json(updated);
|
|
128
|
+
});
|
|
129
|
+
|
|
116
130
|
// Lightweight summary endpoint for status displays.
|
|
117
131
|
app.get("/projects/:pid/tasks-summary", (req, res) => {
|
|
118
132
|
const p = project(req, res);
|
package/src/host/daemon/api.js
CHANGED
|
@@ -33,6 +33,8 @@ import { register as registerRuntimes } from "./api/runtimes.js";
|
|
|
33
33
|
import { register as registerRoutines } from "./api/routines.js";
|
|
34
34
|
import { register as registerArtifacts } from "./api/artifacts.js";
|
|
35
35
|
import { register as registerTasks } from "./api/tasks.js";
|
|
36
|
+
import { register as registerOrganization } from "./api/organization.js";
|
|
37
|
+
import { register as registerProjectFiles } from "./api/files-project.js";
|
|
36
38
|
import { register as registerConfig } from "./api/config.js";
|
|
37
39
|
import { register as registerRun } from "./api/run.js";
|
|
38
40
|
import { register as registerTopLevel } from "./api/top-level.js";
|
|
@@ -121,6 +123,8 @@ export function buildApi({
|
|
|
121
123
|
registerRoutines(app, ctx);
|
|
122
124
|
registerArtifacts(app, ctx);
|
|
123
125
|
registerTasks(app, ctx);
|
|
126
|
+
registerOrganization(app, ctx);
|
|
127
|
+
registerProjectFiles(app, ctx);
|
|
124
128
|
registerConfig(app, ctx);
|
|
125
129
|
|
|
126
130
|
// ---- Top-level shortcuts (MCP server clients) --------------------
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// apx org — organization structure (areas + roles) for a project.
|
|
2
|
+
// Backed by /projects/:pid/organization (core/stores/organization.js).
|
|
3
|
+
//
|
|
4
|
+
// apx org show [--project X]
|
|
5
|
+
// apx org area add "<name>" [--slug s] [--goal g] [--project X]
|
|
6
|
+
// apx org area rm <slug> [--project X]
|
|
7
|
+
// apx org role add "<name>" [--slug s] [--area a] [--desc d] [--project X]
|
|
8
|
+
// apx org role rm <slug> [--project X]
|
|
9
|
+
//
|
|
10
|
+
// Thin surface over the daemon API — the web panel calls the same routes.
|
|
11
|
+
import { http } from "../http.js";
|
|
12
|
+
import { resolveProjectId } from "./project.js";
|
|
13
|
+
|
|
14
|
+
export const ORG_USAGE = {
|
|
15
|
+
show: "apx org show [--project X]",
|
|
16
|
+
areaAdd: 'apx org area add "<name>" [--slug s] [--goal g] [--project X]',
|
|
17
|
+
areaRm: "apx org area rm <slug> [--project X]",
|
|
18
|
+
roleAdd: 'apx org role add "<name>" [--slug s] [--area a] [--desc d] [--project X]',
|
|
19
|
+
roleRm: "apx org role rm <slug> [--project X]",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function fail(key, msg) {
|
|
23
|
+
console.error(`apx org: ${msg}`);
|
|
24
|
+
console.error(`Usage: ${ORG_USAGE[key]}`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function cmdOrgShow(args) {
|
|
29
|
+
const pid = await resolveProjectId(args?.flags?.project);
|
|
30
|
+
const org = await http.get(`/projects/${pid}/organization`);
|
|
31
|
+
if (!org.areas.length && !org.roles.length) {
|
|
32
|
+
console.log("(no organization structure yet)");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
console.log("Areas:");
|
|
36
|
+
for (const a of org.areas) console.log(` • ${a.name} (${a.slug})${a.goal ? ` — ${a.goal}` : ""}`);
|
|
37
|
+
console.log("Roles:");
|
|
38
|
+
for (const r of org.roles) {
|
|
39
|
+
console.log(` • ${r.name} (${r.slug})${r.area ? ` [${r.area}]` : ""}${r.description ? ` — ${r.description}` : ""}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function cmdOrgAreaAdd(args) {
|
|
44
|
+
const name = (args._ || []).slice(1).join(" ").trim();
|
|
45
|
+
if (!name) return fail("areaAdd", "name required");
|
|
46
|
+
const pid = await resolveProjectId(args?.flags?.project);
|
|
47
|
+
const area = await http.post(`/projects/${pid}/organization/areas`, {
|
|
48
|
+
name, slug: args.flags?.slug, goal: args.flags?.goal,
|
|
49
|
+
});
|
|
50
|
+
console.log(`added area ${area.name} (${area.slug})`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function cmdOrgAreaRm(args) {
|
|
54
|
+
const slug = (args._ || [])[1];
|
|
55
|
+
if (!slug) return fail("areaRm", "slug required");
|
|
56
|
+
const pid = await resolveProjectId(args?.flags?.project);
|
|
57
|
+
await http.delete(`/projects/${pid}/organization/areas/${encodeURIComponent(slug)}`);
|
|
58
|
+
console.log(`removed area ${slug}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function cmdOrgRoleAdd(args) {
|
|
62
|
+
const name = (args._ || []).slice(1).join(" ").trim();
|
|
63
|
+
if (!name) return fail("roleAdd", "name required");
|
|
64
|
+
const pid = await resolveProjectId(args?.flags?.project);
|
|
65
|
+
const role = await http.post(`/projects/${pid}/organization/roles`, {
|
|
66
|
+
name, slug: args.flags?.slug, area: args.flags?.area, description: args.flags?.desc,
|
|
67
|
+
});
|
|
68
|
+
console.log(`added role ${role.name} (${role.slug})${role.area ? ` in ${role.area}` : ""}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function cmdOrgRoleRm(args) {
|
|
72
|
+
const slug = (args._ || [])[1];
|
|
73
|
+
if (!slug) return fail("roleRm", "slug required");
|
|
74
|
+
const pid = await resolveProjectId(args?.flags?.project);
|
|
75
|
+
await http.delete(`/projects/${pid}/organization/roles/${encodeURIComponent(slug)}`);
|
|
76
|
+
console.log(`removed role ${slug}`);
|
|
77
|
+
}
|
|
@@ -140,6 +140,13 @@ import {
|
|
|
140
140
|
cmdTaskReopen,
|
|
141
141
|
cmdTaskPatch,
|
|
142
142
|
} from "./commands/task.js";
|
|
143
|
+
import {
|
|
144
|
+
cmdOrgShow,
|
|
145
|
+
cmdOrgAreaAdd,
|
|
146
|
+
cmdOrgAreaRm,
|
|
147
|
+
cmdOrgRoleAdd,
|
|
148
|
+
cmdOrgRoleRm,
|
|
149
|
+
} from "./commands/org.js";
|
|
143
150
|
|
|
144
151
|
const __filename = fileURLToPath(import.meta.url);
|
|
145
152
|
const __dirname = path.dirname(__filename);
|
|
@@ -1531,6 +1538,24 @@ const HELP_TOPICS = new Map(Object.entries({
|
|
|
1531
1538
|
usage: ["apx tasks <subcommand> [args] [--flags]"],
|
|
1532
1539
|
examples: ["apx tasks list"],
|
|
1533
1540
|
}),
|
|
1541
|
+
org: topic({
|
|
1542
|
+
title: "apx org",
|
|
1543
|
+
summary: "Organization structure (areas + roles) for a project — the org chart companies/enterprises use to group agents.",
|
|
1544
|
+
usage: ["apx org <show|area|role> [args] [--flags]"],
|
|
1545
|
+
commands: [
|
|
1546
|
+
["show | list", "Print the project's areas and roles."],
|
|
1547
|
+
["area add \"<name>\"", "Create an area. --slug, --goal optional."],
|
|
1548
|
+
["area rm <slug>", "Remove an area (its roles are detached, not deleted)."],
|
|
1549
|
+
["role add \"<name>\"", "Create a role. --slug, --area, --desc optional."],
|
|
1550
|
+
["role rm <slug>", "Remove a role."],
|
|
1551
|
+
],
|
|
1552
|
+
options: [["--project <name|id|path>", "Pin command to a specific project."]],
|
|
1553
|
+
examples: [
|
|
1554
|
+
"apx org area add \"Engineering\" --goal \"Build the product\"",
|
|
1555
|
+
"apx org role add \"Tech Lead\" --area engineering",
|
|
1556
|
+
"apx org show",
|
|
1557
|
+
],
|
|
1558
|
+
}),
|
|
1534
1559
|
"task add": topic({
|
|
1535
1560
|
title: "apx task add",
|
|
1536
1561
|
summary: "Create a task on a project's TODO list.",
|
|
@@ -2604,6 +2629,29 @@ async function dispatch(cmd, rest) {
|
|
|
2604
2629
|
break;
|
|
2605
2630
|
}
|
|
2606
2631
|
|
|
2632
|
+
case "org":
|
|
2633
|
+
case "organization": {
|
|
2634
|
+
const sub = rest[0];
|
|
2635
|
+
const a = parseArgs(rest.slice(1));
|
|
2636
|
+
// `apx org area add ...` / `apx org role rm ...` — the resource verb is
|
|
2637
|
+
// the first positional, the action the second.
|
|
2638
|
+
if (!sub || sub === "show" || sub === "list") await cmdOrgShow(a);
|
|
2639
|
+
else if (sub === "area") {
|
|
2640
|
+
const action = rest[1];
|
|
2641
|
+
const aa = parseArgs(rest.slice(1)); // keep `area` as _[0] for name parsing
|
|
2642
|
+
if (action === "add" || action === "new") await cmdOrgAreaAdd(aa);
|
|
2643
|
+
else if (action === "rm" || action === "remove" || action === "delete") await cmdOrgAreaRm(aa);
|
|
2644
|
+
else die("usage: apx org area <add|rm> ...");
|
|
2645
|
+
} else if (sub === "role") {
|
|
2646
|
+
const action = rest[1];
|
|
2647
|
+
const ra = parseArgs(rest.slice(1));
|
|
2648
|
+
if (action === "add" || action === "new") await cmdOrgRoleAdd(ra);
|
|
2649
|
+
else if (action === "rm" || action === "remove" || action === "delete") await cmdOrgRoleRm(ra);
|
|
2650
|
+
else die("usage: apx org role <add|rm> ...");
|
|
2651
|
+
} else die(`unknown org subcommand: ${sub}\nUsage: apx org <show|area|role> ...`);
|
|
2652
|
+
break;
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2607
2655
|
case "skills": {
|
|
2608
2656
|
const sub = rest[0];
|
|
2609
2657
|
const a = parseArgs(rest.slice(1));
|