@hasna/todos 0.10.10 → 0.10.13
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/dist/cli/index.js +192 -8
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/project-agent-roles.d.ts +34 -0
- package/dist/db/project-agent-roles.d.ts.map +1 -0
- package/dist/index.js +15 -1
- package/dist/mcp/index.js +192 -8
- package/dist/server/index.js +15 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2897,6 +2897,20 @@ var init_database = __esm(() => {
|
|
|
2897
2897
|
ALTER TABLE agents ADD COLUMN status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'archived'));
|
|
2898
2898
|
CREATE INDEX IF NOT EXISTS idx_agents_status ON agents(status);
|
|
2899
2899
|
INSERT OR IGNORE INTO _migrations (id) VALUES (30);
|
|
2900
|
+
`,
|
|
2901
|
+
`
|
|
2902
|
+
CREATE TABLE IF NOT EXISTS project_agent_roles (
|
|
2903
|
+
id TEXT PRIMARY KEY,
|
|
2904
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
2905
|
+
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
2906
|
+
role TEXT NOT NULL,
|
|
2907
|
+
is_lead INTEGER NOT NULL DEFAULT 0,
|
|
2908
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2909
|
+
UNIQUE(project_id, agent_id, role)
|
|
2910
|
+
);
|
|
2911
|
+
CREATE INDEX IF NOT EXISTS idx_project_agent_roles_project ON project_agent_roles(project_id);
|
|
2912
|
+
CREATE INDEX IF NOT EXISTS idx_project_agent_roles_agent ON project_agent_roles(agent_id);
|
|
2913
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (31);
|
|
2900
2914
|
`,
|
|
2901
2915
|
`
|
|
2902
2916
|
CREATE TABLE IF NOT EXISTS file_locks (
|
|
@@ -2910,7 +2924,7 @@ var init_database = __esm(() => {
|
|
|
2910
2924
|
CREATE INDEX IF NOT EXISTS idx_file_locks_path ON file_locks(path);
|
|
2911
2925
|
CREATE INDEX IF NOT EXISTS idx_file_locks_agent ON file_locks(agent_id);
|
|
2912
2926
|
CREATE INDEX IF NOT EXISTS idx_file_locks_expires ON file_locks(expires_at);
|
|
2913
|
-
INSERT OR IGNORE INTO _migrations (id) VALUES (
|
|
2927
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (32);
|
|
2914
2928
|
`
|
|
2915
2929
|
];
|
|
2916
2930
|
});
|
|
@@ -10432,6 +10446,81 @@ var init_kg = __esm(() => {
|
|
|
10432
10446
|
init_database();
|
|
10433
10447
|
});
|
|
10434
10448
|
|
|
10449
|
+
// src/db/project-agent-roles.ts
|
|
10450
|
+
var exports_project_agent_roles = {};
|
|
10451
|
+
__export(exports_project_agent_roles, {
|
|
10452
|
+
setProjectAgentRole: () => setProjectAgentRole,
|
|
10453
|
+
removeProjectAgentRole: () => removeProjectAgentRole,
|
|
10454
|
+
listProjectAgentRoles: () => listProjectAgentRoles,
|
|
10455
|
+
getProjectOrgChart: () => getProjectOrgChart,
|
|
10456
|
+
getAgentProjectRoles: () => getAgentProjectRoles
|
|
10457
|
+
});
|
|
10458
|
+
function rowToRole(row) {
|
|
10459
|
+
return { ...row, is_lead: row.is_lead === 1 };
|
|
10460
|
+
}
|
|
10461
|
+
function setProjectAgentRole(projectId, agentId, role, isLead = false, db) {
|
|
10462
|
+
const d = db || getDatabase();
|
|
10463
|
+
const existing = d.query("SELECT * FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?").get(projectId, agentId, role);
|
|
10464
|
+
if (existing) {
|
|
10465
|
+
d.run("UPDATE project_agent_roles SET is_lead = ? WHERE id = ?", [isLead ? 1 : 0, existing.id]);
|
|
10466
|
+
return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(existing.id));
|
|
10467
|
+
}
|
|
10468
|
+
const id = uuid();
|
|
10469
|
+
d.run("INSERT INTO project_agent_roles (id, project_id, agent_id, role, is_lead, created_at) VALUES (?, ?, ?, ?, ?, ?)", [id, projectId, agentId, role, isLead ? 1 : 0, now()]);
|
|
10470
|
+
return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(id));
|
|
10471
|
+
}
|
|
10472
|
+
function removeProjectAgentRole(projectId, agentId, role, db) {
|
|
10473
|
+
const d = db || getDatabase();
|
|
10474
|
+
if (role) {
|
|
10475
|
+
return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?", [projectId, agentId, role]).changes;
|
|
10476
|
+
}
|
|
10477
|
+
return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ?", [projectId, agentId]).changes;
|
|
10478
|
+
}
|
|
10479
|
+
function listProjectAgentRoles(projectId, db) {
|
|
10480
|
+
const d = db || getDatabase();
|
|
10481
|
+
return d.query("SELECT * FROM project_agent_roles WHERE project_id = ? ORDER BY role, created_at").all(projectId).map(rowToRole);
|
|
10482
|
+
}
|
|
10483
|
+
function getAgentProjectRoles(agentId, db) {
|
|
10484
|
+
const d = db || getDatabase();
|
|
10485
|
+
return d.query("SELECT * FROM project_agent_roles WHERE agent_id = ? ORDER BY project_id, role").all(agentId).map(rowToRole);
|
|
10486
|
+
}
|
|
10487
|
+
function getProjectOrgChart(projectId, opts, db) {
|
|
10488
|
+
const d = db || getDatabase();
|
|
10489
|
+
const globalTree = getOrgChart(d);
|
|
10490
|
+
const projectRoles = listProjectAgentRoles(projectId, d);
|
|
10491
|
+
const rolesByAgent = new Map;
|
|
10492
|
+
for (const pr of projectRoles) {
|
|
10493
|
+
if (!rolesByAgent.has(pr.agent_id))
|
|
10494
|
+
rolesByAgent.set(pr.agent_id, { roles: [], isLead: false });
|
|
10495
|
+
const entry = rolesByAgent.get(pr.agent_id);
|
|
10496
|
+
entry.roles.push(pr.role);
|
|
10497
|
+
if (pr.is_lead)
|
|
10498
|
+
entry.isLead = true;
|
|
10499
|
+
}
|
|
10500
|
+
function augmentTree(nodes) {
|
|
10501
|
+
return nodes.map((n) => {
|
|
10502
|
+
const override = rolesByAgent.get(n.agent.id);
|
|
10503
|
+
return {
|
|
10504
|
+
...n,
|
|
10505
|
+
reports: augmentTree(n.reports),
|
|
10506
|
+
project_roles: override?.roles ?? [],
|
|
10507
|
+
is_project_lead: override?.isLead ?? false
|
|
10508
|
+
};
|
|
10509
|
+
}).filter((n) => {
|
|
10510
|
+
if (!opts?.filter_to_project)
|
|
10511
|
+
return true;
|
|
10512
|
+
const hasRole = n.project_roles.length > 0;
|
|
10513
|
+
const hasDescendant = n.reports.length > 0;
|
|
10514
|
+
return hasRole || hasDescendant;
|
|
10515
|
+
});
|
|
10516
|
+
}
|
|
10517
|
+
return augmentTree(globalTree);
|
|
10518
|
+
}
|
|
10519
|
+
var init_project_agent_roles = __esm(() => {
|
|
10520
|
+
init_database();
|
|
10521
|
+
init_agents();
|
|
10522
|
+
});
|
|
10523
|
+
|
|
10435
10524
|
// src/db/patrol.ts
|
|
10436
10525
|
var exports_patrol = {};
|
|
10437
10526
|
__export(exports_patrol, {
|
|
@@ -10836,8 +10925,6 @@ var init_mcp = __esm(() => {
|
|
|
10836
10925
|
"heartbeat"
|
|
10837
10926
|
]);
|
|
10838
10927
|
STANDARD_EXCLUDED = new Set([
|
|
10839
|
-
"get_org_chart",
|
|
10840
|
-
"set_reports_to",
|
|
10841
10928
|
"rename_agent",
|
|
10842
10929
|
"delete_agent",
|
|
10843
10930
|
"unarchive_agent",
|
|
@@ -12282,14 +12369,32 @@ In Progress:`);
|
|
|
12282
12369
|
});
|
|
12283
12370
|
}
|
|
12284
12371
|
if (shouldRegisterTool("get_org_chart")) {
|
|
12285
|
-
server.tool("get_org_chart", "Get agent org chart showing reporting hierarchy
|
|
12372
|
+
server.tool("get_org_chart", "Get agent org chart showing reporting hierarchy with roles, titles, capabilities, and activity status.", {
|
|
12373
|
+
format: exports_external.enum(["text", "json"]).optional().describe("Output format (default: text)"),
|
|
12374
|
+
role: exports_external.string().optional().describe("Filter by agent role (e.g. 'lead', 'developer')"),
|
|
12375
|
+
active_only: exports_external.coerce.boolean().optional().describe("Only show agents active in last 30 min")
|
|
12376
|
+
}, async ({ format, role, active_only }) => {
|
|
12286
12377
|
try {
|
|
12287
|
-
let
|
|
12378
|
+
let filterTree = function(nodes) {
|
|
12379
|
+
return nodes.map((n) => ({ ...n, reports: filterTree(n.reports) })).filter((n) => {
|
|
12380
|
+
if (role && n.agent.role !== role)
|
|
12381
|
+
return false;
|
|
12382
|
+
if (active_only) {
|
|
12383
|
+
const lastSeen = new Date(n.agent.last_seen_at).getTime();
|
|
12384
|
+
if (now2 - lastSeen > ACTIVE_MS)
|
|
12385
|
+
return false;
|
|
12386
|
+
}
|
|
12387
|
+
return true;
|
|
12388
|
+
});
|
|
12389
|
+
}, render = function(nodes, indent = 0) {
|
|
12288
12390
|
return nodes.map((n) => {
|
|
12289
12391
|
const prefix = " ".repeat(indent);
|
|
12290
12392
|
const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
|
|
12291
|
-
const level = n.agent.level ? `
|
|
12292
|
-
const
|
|
12393
|
+
const level = n.agent.level ? ` [${n.agent.level}]` : "";
|
|
12394
|
+
const caps = n.agent.capabilities?.length > 0 ? ` {${n.agent.capabilities.join(", ")}}` : "";
|
|
12395
|
+
const lastSeen = new Date(n.agent.last_seen_at).getTime();
|
|
12396
|
+
const active = now2 - lastSeen < ACTIVE_MS ? " \u25CF" : " \u25CB";
|
|
12397
|
+
const line = `${prefix}${active} ${n.agent.name}${title}${level}${caps}`;
|
|
12293
12398
|
const children = n.reports.length > 0 ? `
|
|
12294
12399
|
` + render(n.reports, indent + 1) : "";
|
|
12295
12400
|
return line + children;
|
|
@@ -12297,7 +12402,14 @@ In Progress:`);
|
|
|
12297
12402
|
`);
|
|
12298
12403
|
};
|
|
12299
12404
|
const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
12300
|
-
|
|
12405
|
+
let tree = getOrgChart2();
|
|
12406
|
+
const now2 = Date.now();
|
|
12407
|
+
const ACTIVE_MS = 1800000;
|
|
12408
|
+
if (role || active_only)
|
|
12409
|
+
tree = filterTree(tree);
|
|
12410
|
+
if (format === "json") {
|
|
12411
|
+
return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
|
|
12412
|
+
}
|
|
12301
12413
|
const text = tree.length > 0 ? render(tree) : "No agents registered.";
|
|
12302
12414
|
return { content: [{ type: "text", text }] };
|
|
12303
12415
|
} catch (e) {
|
|
@@ -13745,6 +13857,78 @@ ${lines.join(`
|
|
|
13745
13857
|
}
|
|
13746
13858
|
});
|
|
13747
13859
|
}
|
|
13860
|
+
if (shouldRegisterTool("set_project_agent_role")) {
|
|
13861
|
+
server.tool("set_project_agent_role", "Assign an agent a role on a specific project (client, lead, developer, qa, reviewer, etc.). Per-project roles extend the global org chart.", {
|
|
13862
|
+
project_id: exports_external.string().describe("Project ID"),
|
|
13863
|
+
agent_name: exports_external.string().describe("Agent name"),
|
|
13864
|
+
role: exports_external.string().describe("Role on this project (e.g. 'lead', 'developer', 'qa')"),
|
|
13865
|
+
is_lead: exports_external.coerce.boolean().optional().describe("Whether this agent is the project lead for this role")
|
|
13866
|
+
}, async ({ project_id, agent_name, role, is_lead }) => {
|
|
13867
|
+
try {
|
|
13868
|
+
const { setProjectAgentRole: setProjectAgentRole2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
|
|
13869
|
+
const agent = getAgentByName(agent_name);
|
|
13870
|
+
if (!agent)
|
|
13871
|
+
return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
|
|
13872
|
+
const pid = resolveId(project_id, "projects");
|
|
13873
|
+
const result = setProjectAgentRole2(pid, agent.id, role, is_lead ?? false);
|
|
13874
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
13875
|
+
} catch (e) {
|
|
13876
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
13877
|
+
}
|
|
13878
|
+
});
|
|
13879
|
+
}
|
|
13880
|
+
if (shouldRegisterTool("get_project_org_chart")) {
|
|
13881
|
+
server.tool("get_project_org_chart", "Get org chart scoped to a project \u2014 global hierarchy with per-project role overrides merged in.", {
|
|
13882
|
+
project_id: exports_external.string().describe("Project ID"),
|
|
13883
|
+
format: exports_external.enum(["text", "json"]).optional().describe("Output format (default: text)"),
|
|
13884
|
+
filter_to_project: exports_external.coerce.boolean().optional().describe("Only show agents with a role on this project")
|
|
13885
|
+
}, async ({ project_id, format, filter_to_project }) => {
|
|
13886
|
+
try {
|
|
13887
|
+
let render = function(nodes, indent = 0) {
|
|
13888
|
+
return nodes.map((n) => {
|
|
13889
|
+
const prefix = " ".repeat(indent);
|
|
13890
|
+
const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
|
|
13891
|
+
const globalRole = n.agent.role ? ` [${n.agent.role}]` : "";
|
|
13892
|
+
const projectRoles = n.project_roles.length > 0 ? ` <${n.project_roles.join(", ")}>` : "";
|
|
13893
|
+
const lead = n.is_project_lead ? " \u2605" : "";
|
|
13894
|
+
const lastSeen = new Date(n.agent.last_seen_at).getTime();
|
|
13895
|
+
const active = now2 - lastSeen < ACTIVE_MS ? " \u25CF" : " \u25CB";
|
|
13896
|
+
const line = `${prefix}${active} ${n.agent.name}${title}${globalRole}${projectRoles}${lead}`;
|
|
13897
|
+
const children = n.reports.length > 0 ? `
|
|
13898
|
+
` + render(n.reports, indent + 1) : "";
|
|
13899
|
+
return line + children;
|
|
13900
|
+
}).join(`
|
|
13901
|
+
`);
|
|
13902
|
+
};
|
|
13903
|
+
const { getProjectOrgChart: getProjectOrgChart2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
|
|
13904
|
+
const pid = resolveId(project_id, "projects");
|
|
13905
|
+
const tree = getProjectOrgChart2(pid, { filter_to_project });
|
|
13906
|
+
if (format === "json") {
|
|
13907
|
+
return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
|
|
13908
|
+
}
|
|
13909
|
+
const now2 = Date.now();
|
|
13910
|
+
const ACTIVE_MS = 30 * 60 * 1000;
|
|
13911
|
+
const text = tree.length > 0 ? render(tree) : "No agents in this project's org chart.";
|
|
13912
|
+
return { content: [{ type: "text", text }] };
|
|
13913
|
+
} catch (e) {
|
|
13914
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
13915
|
+
}
|
|
13916
|
+
});
|
|
13917
|
+
}
|
|
13918
|
+
if (shouldRegisterTool("list_project_agent_roles")) {
|
|
13919
|
+
server.tool("list_project_agent_roles", "List all agent role assignments for a project.", {
|
|
13920
|
+
project_id: exports_external.string().describe("Project ID")
|
|
13921
|
+
}, async ({ project_id }) => {
|
|
13922
|
+
try {
|
|
13923
|
+
const { listProjectAgentRoles: listProjectAgentRoles2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
|
|
13924
|
+
const pid = resolveId(project_id, "projects");
|
|
13925
|
+
const roles = listProjectAgentRoles2(pid);
|
|
13926
|
+
return { content: [{ type: "text", text: JSON.stringify(roles, null, 2) }] };
|
|
13927
|
+
} catch (e) {
|
|
13928
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
13929
|
+
}
|
|
13930
|
+
});
|
|
13931
|
+
}
|
|
13748
13932
|
if (shouldRegisterTool("get_capable_agents")) {
|
|
13749
13933
|
server.tool("get_capable_agents", "Find agents that match given capabilities, sorted by match score.", {
|
|
13750
13934
|
capabilities: exports_external.array(exports_external.string()).describe("Required capabilities to match against"),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,eAAO,MAAM,mBAAmB,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,eAAO,MAAM,mBAAmB,KAAK,CAAC;AA4iBtC,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAkBrD;AAyRD,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAK9D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,SAAa,GAAG,MAAM,CAG3D;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAGpD;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA0B9F"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import type { OrgNode } from "./agents.js";
|
|
3
|
+
export interface ProjectAgentRole {
|
|
4
|
+
id: string;
|
|
5
|
+
project_id: string;
|
|
6
|
+
agent_id: string;
|
|
7
|
+
role: string;
|
|
8
|
+
is_lead: boolean;
|
|
9
|
+
created_at: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ProjectAgentRoleRow {
|
|
12
|
+
id: string;
|
|
13
|
+
project_id: string;
|
|
14
|
+
agent_id: string;
|
|
15
|
+
role: string;
|
|
16
|
+
is_lead: number;
|
|
17
|
+
created_at: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function setProjectAgentRole(projectId: string, agentId: string, role: string, isLead?: boolean, db?: Database): ProjectAgentRole;
|
|
20
|
+
export declare function removeProjectAgentRole(projectId: string, agentId: string, role?: string, db?: Database): number;
|
|
21
|
+
export declare function listProjectAgentRoles(projectId: string, db?: Database): ProjectAgentRole[];
|
|
22
|
+
export declare function getAgentProjectRoles(agentId: string, db?: Database): ProjectAgentRole[];
|
|
23
|
+
export interface ProjectOrgNode extends OrgNode {
|
|
24
|
+
project_roles: string[];
|
|
25
|
+
is_project_lead: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get org chart scoped to a project. Returns global org chart with per-project
|
|
29
|
+
* role overrides merged in. Agents not in the project are excluded when filter=true.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getProjectOrgChart(projectId: string, opts?: {
|
|
32
|
+
filter_to_project?: boolean;
|
|
33
|
+
}, db?: Database): ProjectOrgNode[];
|
|
34
|
+
//# sourceMappingURL=project-agent-roles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-agent-roles.d.ts","sourceRoot":"","sources":["../../src/db/project-agent-roles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,UAAQ,EACd,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,CAoBlB;AAED,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,EACb,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CAYR;AAED,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,EAAE,CAK1F;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,EAAE,CAKvF;AAED,MAAM,WAAW,cAAe,SAAQ,OAAO;IAC7C,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;IAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAE,EACtC,EAAE,CAAC,EAAE,QAAQ,GACZ,cAAc,EAAE,CAkClB"}
|
package/dist/index.js
CHANGED
|
@@ -704,6 +704,20 @@ var MIGRATIONS = [
|
|
|
704
704
|
INSERT OR IGNORE INTO _migrations (id) VALUES (30);
|
|
705
705
|
`,
|
|
706
706
|
`
|
|
707
|
+
CREATE TABLE IF NOT EXISTS project_agent_roles (
|
|
708
|
+
id TEXT PRIMARY KEY,
|
|
709
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
710
|
+
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
711
|
+
role TEXT NOT NULL,
|
|
712
|
+
is_lead INTEGER NOT NULL DEFAULT 0,
|
|
713
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
714
|
+
UNIQUE(project_id, agent_id, role)
|
|
715
|
+
);
|
|
716
|
+
CREATE INDEX IF NOT EXISTS idx_project_agent_roles_project ON project_agent_roles(project_id);
|
|
717
|
+
CREATE INDEX IF NOT EXISTS idx_project_agent_roles_agent ON project_agent_roles(agent_id);
|
|
718
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (31);
|
|
719
|
+
`,
|
|
720
|
+
`
|
|
707
721
|
CREATE TABLE IF NOT EXISTS file_locks (
|
|
708
722
|
id TEXT PRIMARY KEY,
|
|
709
723
|
path TEXT NOT NULL UNIQUE,
|
|
@@ -715,7 +729,7 @@ var MIGRATIONS = [
|
|
|
715
729
|
CREATE INDEX IF NOT EXISTS idx_file_locks_path ON file_locks(path);
|
|
716
730
|
CREATE INDEX IF NOT EXISTS idx_file_locks_agent ON file_locks(agent_id);
|
|
717
731
|
CREATE INDEX IF NOT EXISTS idx_file_locks_expires ON file_locks(expires_at);
|
|
718
|
-
INSERT OR IGNORE INTO _migrations (id) VALUES (
|
|
732
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (32);
|
|
719
733
|
`
|
|
720
734
|
];
|
|
721
735
|
var _db = null;
|
package/dist/mcp/index.js
CHANGED
|
@@ -942,6 +942,20 @@ var init_database = __esm(() => {
|
|
|
942
942
|
ALTER TABLE agents ADD COLUMN status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'archived'));
|
|
943
943
|
CREATE INDEX IF NOT EXISTS idx_agents_status ON agents(status);
|
|
944
944
|
INSERT OR IGNORE INTO _migrations (id) VALUES (30);
|
|
945
|
+
`,
|
|
946
|
+
`
|
|
947
|
+
CREATE TABLE IF NOT EXISTS project_agent_roles (
|
|
948
|
+
id TEXT PRIMARY KEY,
|
|
949
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
950
|
+
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
951
|
+
role TEXT NOT NULL,
|
|
952
|
+
is_lead INTEGER NOT NULL DEFAULT 0,
|
|
953
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
954
|
+
UNIQUE(project_id, agent_id, role)
|
|
955
|
+
);
|
|
956
|
+
CREATE INDEX IF NOT EXISTS idx_project_agent_roles_project ON project_agent_roles(project_id);
|
|
957
|
+
CREATE INDEX IF NOT EXISTS idx_project_agent_roles_agent ON project_agent_roles(agent_id);
|
|
958
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (31);
|
|
945
959
|
`,
|
|
946
960
|
`
|
|
947
961
|
CREATE TABLE IF NOT EXISTS file_locks (
|
|
@@ -955,7 +969,7 @@ var init_database = __esm(() => {
|
|
|
955
969
|
CREATE INDEX IF NOT EXISTS idx_file_locks_path ON file_locks(path);
|
|
956
970
|
CREATE INDEX IF NOT EXISTS idx_file_locks_agent ON file_locks(agent_id);
|
|
957
971
|
CREATE INDEX IF NOT EXISTS idx_file_locks_expires ON file_locks(expires_at);
|
|
958
|
-
INSERT OR IGNORE INTO _migrations (id) VALUES (
|
|
972
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (32);
|
|
959
973
|
`
|
|
960
974
|
];
|
|
961
975
|
});
|
|
@@ -1977,6 +1991,81 @@ var init_kg = __esm(() => {
|
|
|
1977
1991
|
init_database();
|
|
1978
1992
|
});
|
|
1979
1993
|
|
|
1994
|
+
// src/db/project-agent-roles.ts
|
|
1995
|
+
var exports_project_agent_roles = {};
|
|
1996
|
+
__export(exports_project_agent_roles, {
|
|
1997
|
+
setProjectAgentRole: () => setProjectAgentRole,
|
|
1998
|
+
removeProjectAgentRole: () => removeProjectAgentRole,
|
|
1999
|
+
listProjectAgentRoles: () => listProjectAgentRoles,
|
|
2000
|
+
getProjectOrgChart: () => getProjectOrgChart,
|
|
2001
|
+
getAgentProjectRoles: () => getAgentProjectRoles
|
|
2002
|
+
});
|
|
2003
|
+
function rowToRole(row) {
|
|
2004
|
+
return { ...row, is_lead: row.is_lead === 1 };
|
|
2005
|
+
}
|
|
2006
|
+
function setProjectAgentRole(projectId, agentId, role, isLead = false, db) {
|
|
2007
|
+
const d = db || getDatabase();
|
|
2008
|
+
const existing = d.query("SELECT * FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?").get(projectId, agentId, role);
|
|
2009
|
+
if (existing) {
|
|
2010
|
+
d.run("UPDATE project_agent_roles SET is_lead = ? WHERE id = ?", [isLead ? 1 : 0, existing.id]);
|
|
2011
|
+
return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(existing.id));
|
|
2012
|
+
}
|
|
2013
|
+
const id = uuid();
|
|
2014
|
+
d.run("INSERT INTO project_agent_roles (id, project_id, agent_id, role, is_lead, created_at) VALUES (?, ?, ?, ?, ?, ?)", [id, projectId, agentId, role, isLead ? 1 : 0, now()]);
|
|
2015
|
+
return rowToRole(d.query("SELECT * FROM project_agent_roles WHERE id = ?").get(id));
|
|
2016
|
+
}
|
|
2017
|
+
function removeProjectAgentRole(projectId, agentId, role, db) {
|
|
2018
|
+
const d = db || getDatabase();
|
|
2019
|
+
if (role) {
|
|
2020
|
+
return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ? AND role = ?", [projectId, agentId, role]).changes;
|
|
2021
|
+
}
|
|
2022
|
+
return d.run("DELETE FROM project_agent_roles WHERE project_id = ? AND agent_id = ?", [projectId, agentId]).changes;
|
|
2023
|
+
}
|
|
2024
|
+
function listProjectAgentRoles(projectId, db) {
|
|
2025
|
+
const d = db || getDatabase();
|
|
2026
|
+
return d.query("SELECT * FROM project_agent_roles WHERE project_id = ? ORDER BY role, created_at").all(projectId).map(rowToRole);
|
|
2027
|
+
}
|
|
2028
|
+
function getAgentProjectRoles(agentId, db) {
|
|
2029
|
+
const d = db || getDatabase();
|
|
2030
|
+
return d.query("SELECT * FROM project_agent_roles WHERE agent_id = ? ORDER BY project_id, role").all(agentId).map(rowToRole);
|
|
2031
|
+
}
|
|
2032
|
+
function getProjectOrgChart(projectId, opts, db) {
|
|
2033
|
+
const d = db || getDatabase();
|
|
2034
|
+
const globalTree = getOrgChart(d);
|
|
2035
|
+
const projectRoles = listProjectAgentRoles(projectId, d);
|
|
2036
|
+
const rolesByAgent = new Map;
|
|
2037
|
+
for (const pr of projectRoles) {
|
|
2038
|
+
if (!rolesByAgent.has(pr.agent_id))
|
|
2039
|
+
rolesByAgent.set(pr.agent_id, { roles: [], isLead: false });
|
|
2040
|
+
const entry = rolesByAgent.get(pr.agent_id);
|
|
2041
|
+
entry.roles.push(pr.role);
|
|
2042
|
+
if (pr.is_lead)
|
|
2043
|
+
entry.isLead = true;
|
|
2044
|
+
}
|
|
2045
|
+
function augmentTree(nodes) {
|
|
2046
|
+
return nodes.map((n) => {
|
|
2047
|
+
const override = rolesByAgent.get(n.agent.id);
|
|
2048
|
+
return {
|
|
2049
|
+
...n,
|
|
2050
|
+
reports: augmentTree(n.reports),
|
|
2051
|
+
project_roles: override?.roles ?? [],
|
|
2052
|
+
is_project_lead: override?.isLead ?? false
|
|
2053
|
+
};
|
|
2054
|
+
}).filter((n) => {
|
|
2055
|
+
if (!opts?.filter_to_project)
|
|
2056
|
+
return true;
|
|
2057
|
+
const hasRole = n.project_roles.length > 0;
|
|
2058
|
+
const hasDescendant = n.reports.length > 0;
|
|
2059
|
+
return hasRole || hasDescendant;
|
|
2060
|
+
});
|
|
2061
|
+
}
|
|
2062
|
+
return augmentTree(globalTree);
|
|
2063
|
+
}
|
|
2064
|
+
var init_project_agent_roles = __esm(() => {
|
|
2065
|
+
init_database();
|
|
2066
|
+
init_agents();
|
|
2067
|
+
});
|
|
2068
|
+
|
|
1980
2069
|
// src/db/patrol.ts
|
|
1981
2070
|
var exports_patrol = {};
|
|
1982
2071
|
__export(exports_patrol, {
|
|
@@ -8441,8 +8530,6 @@ var MINIMAL_TOOLS = new Set([
|
|
|
8441
8530
|
"heartbeat"
|
|
8442
8531
|
]);
|
|
8443
8532
|
var STANDARD_EXCLUDED = new Set([
|
|
8444
|
-
"get_org_chart",
|
|
8445
|
-
"set_reports_to",
|
|
8446
8533
|
"rename_agent",
|
|
8447
8534
|
"delete_agent",
|
|
8448
8535
|
"unarchive_agent",
|
|
@@ -10010,14 +10097,32 @@ In Progress:`);
|
|
|
10010
10097
|
});
|
|
10011
10098
|
}
|
|
10012
10099
|
if (shouldRegisterTool("get_org_chart")) {
|
|
10013
|
-
server.tool("get_org_chart", "Get agent org chart showing reporting hierarchy
|
|
10100
|
+
server.tool("get_org_chart", "Get agent org chart showing reporting hierarchy with roles, titles, capabilities, and activity status.", {
|
|
10101
|
+
format: exports_external.enum(["text", "json"]).optional().describe("Output format (default: text)"),
|
|
10102
|
+
role: exports_external.string().optional().describe("Filter by agent role (e.g. 'lead', 'developer')"),
|
|
10103
|
+
active_only: exports_external.coerce.boolean().optional().describe("Only show agents active in last 30 min")
|
|
10104
|
+
}, async ({ format, role, active_only }) => {
|
|
10014
10105
|
try {
|
|
10015
|
-
let
|
|
10106
|
+
let filterTree = function(nodes) {
|
|
10107
|
+
return nodes.map((n) => ({ ...n, reports: filterTree(n.reports) })).filter((n) => {
|
|
10108
|
+
if (role && n.agent.role !== role)
|
|
10109
|
+
return false;
|
|
10110
|
+
if (active_only) {
|
|
10111
|
+
const lastSeen = new Date(n.agent.last_seen_at).getTime();
|
|
10112
|
+
if (now2 - lastSeen > ACTIVE_MS)
|
|
10113
|
+
return false;
|
|
10114
|
+
}
|
|
10115
|
+
return true;
|
|
10116
|
+
});
|
|
10117
|
+
}, render = function(nodes, indent = 0) {
|
|
10016
10118
|
return nodes.map((n) => {
|
|
10017
10119
|
const prefix = " ".repeat(indent);
|
|
10018
10120
|
const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
|
|
10019
|
-
const level = n.agent.level ? `
|
|
10020
|
-
const
|
|
10121
|
+
const level = n.agent.level ? ` [${n.agent.level}]` : "";
|
|
10122
|
+
const caps = n.agent.capabilities?.length > 0 ? ` {${n.agent.capabilities.join(", ")}}` : "";
|
|
10123
|
+
const lastSeen = new Date(n.agent.last_seen_at).getTime();
|
|
10124
|
+
const active = now2 - lastSeen < ACTIVE_MS ? " \u25CF" : " \u25CB";
|
|
10125
|
+
const line = `${prefix}${active} ${n.agent.name}${title}${level}${caps}`;
|
|
10021
10126
|
const children = n.reports.length > 0 ? `
|
|
10022
10127
|
` + render(n.reports, indent + 1) : "";
|
|
10023
10128
|
return line + children;
|
|
@@ -10025,7 +10130,14 @@ if (shouldRegisterTool("get_org_chart")) {
|
|
|
10025
10130
|
`);
|
|
10026
10131
|
};
|
|
10027
10132
|
const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
10028
|
-
|
|
10133
|
+
let tree = getOrgChart2();
|
|
10134
|
+
const now2 = Date.now();
|
|
10135
|
+
const ACTIVE_MS = 1800000;
|
|
10136
|
+
if (role || active_only)
|
|
10137
|
+
tree = filterTree(tree);
|
|
10138
|
+
if (format === "json") {
|
|
10139
|
+
return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
|
|
10140
|
+
}
|
|
10029
10141
|
const text = tree.length > 0 ? render(tree) : "No agents registered.";
|
|
10030
10142
|
return { content: [{ type: "text", text }] };
|
|
10031
10143
|
} catch (e) {
|
|
@@ -11473,6 +11585,78 @@ ${lines.join(`
|
|
|
11473
11585
|
}
|
|
11474
11586
|
});
|
|
11475
11587
|
}
|
|
11588
|
+
if (shouldRegisterTool("set_project_agent_role")) {
|
|
11589
|
+
server.tool("set_project_agent_role", "Assign an agent a role on a specific project (client, lead, developer, qa, reviewer, etc.). Per-project roles extend the global org chart.", {
|
|
11590
|
+
project_id: exports_external.string().describe("Project ID"),
|
|
11591
|
+
agent_name: exports_external.string().describe("Agent name"),
|
|
11592
|
+
role: exports_external.string().describe("Role on this project (e.g. 'lead', 'developer', 'qa')"),
|
|
11593
|
+
is_lead: exports_external.coerce.boolean().optional().describe("Whether this agent is the project lead for this role")
|
|
11594
|
+
}, async ({ project_id, agent_name, role, is_lead }) => {
|
|
11595
|
+
try {
|
|
11596
|
+
const { setProjectAgentRole: setProjectAgentRole2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
|
|
11597
|
+
const agent = getAgentByName(agent_name);
|
|
11598
|
+
if (!agent)
|
|
11599
|
+
return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
|
|
11600
|
+
const pid = resolveId(project_id, "projects");
|
|
11601
|
+
const result = setProjectAgentRole2(pid, agent.id, role, is_lead ?? false);
|
|
11602
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
11603
|
+
} catch (e) {
|
|
11604
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
11605
|
+
}
|
|
11606
|
+
});
|
|
11607
|
+
}
|
|
11608
|
+
if (shouldRegisterTool("get_project_org_chart")) {
|
|
11609
|
+
server.tool("get_project_org_chart", "Get org chart scoped to a project \u2014 global hierarchy with per-project role overrides merged in.", {
|
|
11610
|
+
project_id: exports_external.string().describe("Project ID"),
|
|
11611
|
+
format: exports_external.enum(["text", "json"]).optional().describe("Output format (default: text)"),
|
|
11612
|
+
filter_to_project: exports_external.coerce.boolean().optional().describe("Only show agents with a role on this project")
|
|
11613
|
+
}, async ({ project_id, format, filter_to_project }) => {
|
|
11614
|
+
try {
|
|
11615
|
+
let render = function(nodes, indent = 0) {
|
|
11616
|
+
return nodes.map((n) => {
|
|
11617
|
+
const prefix = " ".repeat(indent);
|
|
11618
|
+
const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
|
|
11619
|
+
const globalRole = n.agent.role ? ` [${n.agent.role}]` : "";
|
|
11620
|
+
const projectRoles = n.project_roles.length > 0 ? ` <${n.project_roles.join(", ")}>` : "";
|
|
11621
|
+
const lead = n.is_project_lead ? " \u2605" : "";
|
|
11622
|
+
const lastSeen = new Date(n.agent.last_seen_at).getTime();
|
|
11623
|
+
const active = now2 - lastSeen < ACTIVE_MS ? " \u25CF" : " \u25CB";
|
|
11624
|
+
const line = `${prefix}${active} ${n.agent.name}${title}${globalRole}${projectRoles}${lead}`;
|
|
11625
|
+
const children = n.reports.length > 0 ? `
|
|
11626
|
+
` + render(n.reports, indent + 1) : "";
|
|
11627
|
+
return line + children;
|
|
11628
|
+
}).join(`
|
|
11629
|
+
`);
|
|
11630
|
+
};
|
|
11631
|
+
const { getProjectOrgChart: getProjectOrgChart2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
|
|
11632
|
+
const pid = resolveId(project_id, "projects");
|
|
11633
|
+
const tree = getProjectOrgChart2(pid, { filter_to_project });
|
|
11634
|
+
if (format === "json") {
|
|
11635
|
+
return { content: [{ type: "text", text: JSON.stringify(tree, null, 2) }] };
|
|
11636
|
+
}
|
|
11637
|
+
const now2 = Date.now();
|
|
11638
|
+
const ACTIVE_MS = 30 * 60 * 1000;
|
|
11639
|
+
const text = tree.length > 0 ? render(tree) : "No agents in this project's org chart.";
|
|
11640
|
+
return { content: [{ type: "text", text }] };
|
|
11641
|
+
} catch (e) {
|
|
11642
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
11643
|
+
}
|
|
11644
|
+
});
|
|
11645
|
+
}
|
|
11646
|
+
if (shouldRegisterTool("list_project_agent_roles")) {
|
|
11647
|
+
server.tool("list_project_agent_roles", "List all agent role assignments for a project.", {
|
|
11648
|
+
project_id: exports_external.string().describe("Project ID")
|
|
11649
|
+
}, async ({ project_id }) => {
|
|
11650
|
+
try {
|
|
11651
|
+
const { listProjectAgentRoles: listProjectAgentRoles2 } = (init_project_agent_roles(), __toCommonJS(exports_project_agent_roles));
|
|
11652
|
+
const pid = resolveId(project_id, "projects");
|
|
11653
|
+
const roles = listProjectAgentRoles2(pid);
|
|
11654
|
+
return { content: [{ type: "text", text: JSON.stringify(roles, null, 2) }] };
|
|
11655
|
+
} catch (e) {
|
|
11656
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
11657
|
+
}
|
|
11658
|
+
});
|
|
11659
|
+
}
|
|
11476
11660
|
if (shouldRegisterTool("get_capable_agents")) {
|
|
11477
11661
|
server.tool("get_capable_agents", "Find agents that match given capabilities, sorted by match score.", {
|
|
11478
11662
|
capabilities: exports_external.array(exports_external.string()).describe("Required capabilities to match against"),
|
package/dist/server/index.js
CHANGED
|
@@ -858,6 +858,20 @@ var init_database = __esm(() => {
|
|
|
858
858
|
ALTER TABLE agents ADD COLUMN status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'archived'));
|
|
859
859
|
CREATE INDEX IF NOT EXISTS idx_agents_status ON agents(status);
|
|
860
860
|
INSERT OR IGNORE INTO _migrations (id) VALUES (30);
|
|
861
|
+
`,
|
|
862
|
+
`
|
|
863
|
+
CREATE TABLE IF NOT EXISTS project_agent_roles (
|
|
864
|
+
id TEXT PRIMARY KEY,
|
|
865
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
866
|
+
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
867
|
+
role TEXT NOT NULL,
|
|
868
|
+
is_lead INTEGER NOT NULL DEFAULT 0,
|
|
869
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
870
|
+
UNIQUE(project_id, agent_id, role)
|
|
871
|
+
);
|
|
872
|
+
CREATE INDEX IF NOT EXISTS idx_project_agent_roles_project ON project_agent_roles(project_id);
|
|
873
|
+
CREATE INDEX IF NOT EXISTS idx_project_agent_roles_agent ON project_agent_roles(agent_id);
|
|
874
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (31);
|
|
861
875
|
`,
|
|
862
876
|
`
|
|
863
877
|
CREATE TABLE IF NOT EXISTS file_locks (
|
|
@@ -871,7 +885,7 @@ var init_database = __esm(() => {
|
|
|
871
885
|
CREATE INDEX IF NOT EXISTS idx_file_locks_path ON file_locks(path);
|
|
872
886
|
CREATE INDEX IF NOT EXISTS idx_file_locks_agent ON file_locks(agent_id);
|
|
873
887
|
CREATE INDEX IF NOT EXISTS idx_file_locks_expires ON file_locks(expires_at);
|
|
874
|
-
INSERT OR IGNORE INTO _migrations (id) VALUES (
|
|
888
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (32);
|
|
875
889
|
`
|
|
876
890
|
];
|
|
877
891
|
});
|