@virsanghavi/axis-server 1.9.0 → 1.9.2
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/mcp-server.mjs +34 -27
- package/package.json +1 -1
package/dist/mcp-server.mjs
CHANGED
|
@@ -827,7 +827,7 @@ var NerveCenter = class _NerveCenter {
|
|
|
827
827
|
delete this.state.locks[filePath];
|
|
828
828
|
await this.saveState();
|
|
829
829
|
}
|
|
830
|
-
this.logLockEvent("FORCE_UNLOCKED", filePath, "admin", void 0, reason);
|
|
830
|
+
await this.logLockEvent("FORCE_UNLOCKED", filePath, "admin", void 0, reason);
|
|
831
831
|
await this.appendToNotepad(`
|
|
832
832
|
- [FORCE UNLOCK] ${filePath} unlocked by admin. Reason: ${reason}`);
|
|
833
833
|
return `File ${filePath} has been forcibly unlocked.`;
|
|
@@ -854,6 +854,7 @@ ${notepad}`;
|
|
|
854
854
|
async logLockEvent(eventType, filePath, requestingAgent, blockingAgent, intent) {
|
|
855
855
|
try {
|
|
856
856
|
if (this.contextManager.apiUrl) {
|
|
857
|
+
logger.info(`[logLockEvent] Logging ${eventType} event via API for ${filePath} (agent: ${requestingAgent}, blocker: ${blockingAgent || "none"})`);
|
|
857
858
|
await this.callCoordination("lock-events", "POST", {
|
|
858
859
|
eventType,
|
|
859
860
|
filePath,
|
|
@@ -861,7 +862,9 @@ ${notepad}`;
|
|
|
861
862
|
blockingAgent: blockingAgent || null,
|
|
862
863
|
intent: intent || null
|
|
863
864
|
});
|
|
865
|
+
logger.info(`[logLockEvent] Successfully logged ${eventType} event`);
|
|
864
866
|
} else if (this.useSupabase && this.supabase && this._projectId) {
|
|
867
|
+
logger.info(`[logLockEvent] Logging ${eventType} event via Supabase for ${filePath}`);
|
|
865
868
|
await this.supabase.from("lock_events").insert({
|
|
866
869
|
project_id: this._projectId,
|
|
867
870
|
event_type: eventType,
|
|
@@ -870,9 +873,12 @@ ${notepad}`;
|
|
|
870
873
|
blocking_agent: blockingAgent || null,
|
|
871
874
|
intent: intent || null
|
|
872
875
|
});
|
|
876
|
+
logger.info(`[logLockEvent] Successfully logged ${eventType} event`);
|
|
877
|
+
} else {
|
|
878
|
+
logger.warn(`[logLockEvent] No persistence backend available \u2014 ${eventType} event for ${filePath} will not be recorded`);
|
|
873
879
|
}
|
|
874
880
|
} catch (e) {
|
|
875
|
-
logger.
|
|
881
|
+
logger.error(`[logLockEvent] Failed to log ${eventType} event for ${filePath}: ${e.message}`);
|
|
876
882
|
}
|
|
877
883
|
}
|
|
878
884
|
// --- Decision & Orchestration ---
|
|
@@ -972,7 +978,7 @@ ${notepad}`;
|
|
|
972
978
|
});
|
|
973
979
|
if (result.status === "DENIED") {
|
|
974
980
|
logger.info(`[proposeFileAccess] DENIED by server: ${result.message}`);
|
|
975
|
-
this.logLockEvent("BLOCKED", normalizedPath, agentId, result.current_lock?.agent_id, intent);
|
|
981
|
+
await this.logLockEvent("BLOCKED", normalizedPath, agentId, result.current_lock?.agent_id, intent);
|
|
976
982
|
return {
|
|
977
983
|
status: "REQUIRES_ORCHESTRATION",
|
|
978
984
|
message: result.message || `File '${normalizedPath}' is locked by another agent`,
|
|
@@ -984,7 +990,7 @@ ${notepad}`;
|
|
|
984
990
|
return { status: "REJECTED", message: result.message };
|
|
985
991
|
}
|
|
986
992
|
logger.info(`[proposeFileAccess] GRANTED by server`);
|
|
987
|
-
this.logLockEvent("GRANTED", normalizedPath, agentId, void 0, intent);
|
|
993
|
+
await this.logLockEvent("GRANTED", normalizedPath, agentId, void 0, intent);
|
|
988
994
|
await this.appendToNotepad(`
|
|
989
995
|
- [LOCK] ${agentId} locked ${normalizedPath}
|
|
990
996
|
Intent: ${intent}`);
|
|
@@ -1001,7 +1007,7 @@ ${notepad}`;
|
|
|
1001
1007
|
}
|
|
1002
1008
|
} catch {
|
|
1003
1009
|
}
|
|
1004
|
-
this.logLockEvent("BLOCKED", normalizedPath, agentId, blockingAgent, intent);
|
|
1010
|
+
await this.logLockEvent("BLOCKED", normalizedPath, agentId, blockingAgent, intent);
|
|
1005
1011
|
return {
|
|
1006
1012
|
status: "REQUIRES_ORCHESTRATION",
|
|
1007
1013
|
message: `File '${normalizedPath}' is locked by another agent`
|
|
@@ -1024,7 +1030,7 @@ ${notepad}`;
|
|
|
1024
1030
|
if (error) throw error;
|
|
1025
1031
|
const row = Array.isArray(data) ? data[0] : data;
|
|
1026
1032
|
if (row && row.status === "DENIED") {
|
|
1027
|
-
this.logLockEvent("BLOCKED", normalizedPath, agentId, row.owner_id, intent);
|
|
1033
|
+
await this.logLockEvent("BLOCKED", normalizedPath, agentId, row.owner_id, intent);
|
|
1028
1034
|
return {
|
|
1029
1035
|
status: "REQUIRES_ORCHESTRATION",
|
|
1030
1036
|
message: `Conflict: File '${normalizedPath}' is locked by '${row.owner_id}'`,
|
|
@@ -1036,7 +1042,7 @@ ${notepad}`;
|
|
|
1036
1042
|
}
|
|
1037
1043
|
};
|
|
1038
1044
|
}
|
|
1039
|
-
this.logLockEvent("GRANTED", normalizedPath, agentId, void 0, intent);
|
|
1045
|
+
await this.logLockEvent("GRANTED", normalizedPath, agentId, void 0, intent);
|
|
1040
1046
|
await this.appendToNotepad(`
|
|
1041
1047
|
- [LOCK] ${agentId} locked ${normalizedPath}
|
|
1042
1048
|
Intent: ${intent}`);
|
|
@@ -1048,7 +1054,7 @@ ${notepad}`;
|
|
|
1048
1054
|
const allLocks = Object.values(this.state.locks);
|
|
1049
1055
|
const conflict = this.findExactConflict(filePath, agentId, allLocks);
|
|
1050
1056
|
if (conflict) {
|
|
1051
|
-
this.logLockEvent("BLOCKED", normalizedPath, agentId, conflict.agentId, intent);
|
|
1057
|
+
await this.logLockEvent("BLOCKED", normalizedPath, agentId, conflict.agentId, intent);
|
|
1052
1058
|
return {
|
|
1053
1059
|
status: "REQUIRES_ORCHESTRATION",
|
|
1054
1060
|
message: `Conflict: File '${normalizedPath}' is locked by '${conflict.agentId}'`,
|
|
@@ -1057,7 +1063,7 @@ ${notepad}`;
|
|
|
1057
1063
|
}
|
|
1058
1064
|
this.state.locks[normalizedPath] = { agentId, filePath: normalizedPath, intent, userPrompt, timestamp: Date.now() };
|
|
1059
1065
|
await this.saveState();
|
|
1060
|
-
this.logLockEvent("GRANTED", normalizedPath, agentId, void 0, intent);
|
|
1066
|
+
await this.logLockEvent("GRANTED", normalizedPath, agentId, void 0, intent);
|
|
1061
1067
|
await this.appendToNotepad(`
|
|
1062
1068
|
- [LOCK] ${agentId} locked ${normalizedPath}
|
|
1063
1069
|
Intent: ${intent}`);
|
|
@@ -1113,8 +1119,10 @@ ${notepad}`;
|
|
|
1113
1119
|
}
|
|
1114
1120
|
async getProjectSoul() {
|
|
1115
1121
|
let soul = "## Project Soul\n";
|
|
1122
|
+
let context = "";
|
|
1123
|
+
let couldNotRead = false;
|
|
1116
1124
|
try {
|
|
1117
|
-
|
|
1125
|
+
context = await this.contextManager.readFile("context.md");
|
|
1118
1126
|
soul += `
|
|
1119
1127
|
### Context
|
|
1120
1128
|
${context}`;
|
|
@@ -1123,8 +1131,19 @@ ${context}`;
|
|
|
1123
1131
|
### Conventions
|
|
1124
1132
|
${conventions}`;
|
|
1125
1133
|
} catch (_e) {
|
|
1134
|
+
couldNotRead = true;
|
|
1126
1135
|
soul += "\n(Could not read local context files)";
|
|
1127
1136
|
}
|
|
1137
|
+
const unfilled = couldNotRead || /Describe your project|<!-- Describe|This project uses Axis/i.test(context) || context.trim().length < 450 && /# Project Context/i.test(context);
|
|
1138
|
+
if (unfilled) {
|
|
1139
|
+
soul += `
|
|
1140
|
+
|
|
1141
|
+
### MANDATORY: Project soul is not yet filled
|
|
1142
|
+
You MUST fill the project soul before doing any other work. Do not proceed with other tasks until it is filled.
|
|
1143
|
+
- If the codebase has content to search: use \`search_codebase\` to explore, then \`read_context\` and \`update_context\` to populate \`context.md\` and \`conventions.md\`.
|
|
1144
|
+
- If there is nothing in the directory to search through: you MUST ask the user what the project is about, then update the soul files with their answer.
|
|
1145
|
+
This requirement is mandatory; ignoring it means working without context.`;
|
|
1146
|
+
}
|
|
1128
1147
|
return soul;
|
|
1129
1148
|
}
|
|
1130
1149
|
// --- Billing & Usage ---
|
|
@@ -1841,30 +1860,18 @@ async function ensureFileSystem() {
|
|
|
1841
1860
|
["context.md", `# Project Context
|
|
1842
1861
|
|
|
1843
1862
|
## Overview
|
|
1844
|
-
|
|
1845
|
-
Axis provides shared context, atomic file locks, a job board, and real-time
|
|
1846
|
-
activity feeds so that multiple agents (Cursor, Claude, Windsurf, Codex, etc.)
|
|
1847
|
-
can work on the same codebase without conflicts.
|
|
1863
|
+
<!-- Describe your project's goals and what it does. If this file is still just placeholders, agents MUST fill it: use search_codebase to infer content from the repo, or if there is nothing to search, ask the user what the project is about, then update via update_context. Do not proceed with other work until the soul is filled. -->
|
|
1848
1864
|
|
|
1849
1865
|
## Architecture
|
|
1850
|
-
|
|
1851
|
-
- **Supabase Backend**: Postgres for state (locks, jobs, profiles); Realtime for live feeds.
|
|
1852
|
-
- **Frontend**: Next.js App Router + Tailwind CSS dashboard at useaxis.dev.
|
|
1853
|
-
- **npm Packages**: @virsanghavi/axis-server (runtime), @virsanghavi/axis-init (scaffolding).
|
|
1866
|
+
<!-- Stack, high-level design, and key systems -->
|
|
1854
1867
|
|
|
1855
1868
|
## Core Features
|
|
1856
|
-
|
|
1857
|
-
2. Job Board \u2014 post / claim / complete tasks with priorities and dependencies.
|
|
1858
|
-
3. Shared Context \u2014 live notepad visible to every agent in real time.
|
|
1859
|
-
4. RAG Search \u2014 vector search over the indexed codebase.
|
|
1860
|
-
5. Soul Files \u2014 context.md, conventions.md, activity.md define project identity.
|
|
1869
|
+
<!-- List main capabilities of this project -->
|
|
1861
1870
|
`],
|
|
1862
1871
|
["conventions.md", `# Coding Conventions
|
|
1863
1872
|
|
|
1864
1873
|
## Language & Style
|
|
1865
|
-
|
|
1866
|
-
- Tailwind CSS for styling; no raw CSS unless unavoidable.
|
|
1867
|
-
- Functional React components; prefer server components in Next.js App Router.
|
|
1874
|
+
<!-- Your language, framework, and styling guidelines -->
|
|
1868
1875
|
|
|
1869
1876
|
## Agent Behavioral Norms (MANDATORY)
|
|
1870
1877
|
|
|
@@ -2077,7 +2084,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
2077
2084
|
},
|
|
2078
2085
|
{
|
|
2079
2086
|
name: "get_project_soul",
|
|
2080
|
-
description: "**MANDATORY FIRST CALL**: Returns the project's goals, architecture, conventions, and active state.\n- Combines `context.md`, `conventions.md`, and other core directives into a single prompt.\n- You MUST call this as your FIRST action in every new session or task \u2014 before reading files, before responding to the user, before anything else.\n- Skipping this call means you are working without context and will make wrong decisions.",
|
|
2087
|
+
description: "**MANDATORY FIRST CALL**: Returns the project's goals, architecture, conventions, and active state.\n- Combines `context.md`, `conventions.md`, and other core directives into a single prompt.\n- You MUST call this as your FIRST action in every new session or task \u2014 before reading files, before responding to the user, before anything else.\n- If the project soul is not yet filled, you MUST fill it before any other work: use search_codebase then update_context, or if there is nothing to search, you MUST ask the user what the project is about and then update the soul files. Do not proceed with other tasks until the soul is filled.\n- Skipping this call means you are working without context and will make wrong decisions.",
|
|
2081
2088
|
inputSchema: { type: "object", properties: {}, required: [] }
|
|
2082
2089
|
},
|
|
2083
2090
|
// --- Job Board (Task Orchestration) ---
|