@virsanghavi/axis-server 1.1.1 → 1.2.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.
@@ -827,6 +827,7 @@ var NerveCenter = class {
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
831
  await this.appendToNotepad(`
831
832
  - [FORCE UNLOCK] ${filePath} unlocked by admin. Reason: ${reason}`);
832
833
  return `File ${filePath} has been forcibly unlocked.`;
@@ -849,6 +850,31 @@ ${lockSummary || "No active locks."}
849
850
  ## Live Notepad
850
851
  ${notepad}`;
851
852
  }
853
+ // --- Lock Event Logging ---
854
+ async logLockEvent(eventType, filePath, requestingAgent, blockingAgent, intent) {
855
+ try {
856
+ if (this.contextManager.apiUrl) {
857
+ await this.callCoordination("lock-events", "POST", {
858
+ eventType,
859
+ filePath,
860
+ requestingAgent,
861
+ blockingAgent: blockingAgent || null,
862
+ intent: intent || null
863
+ });
864
+ } else if (this.useSupabase && this.supabase && this._projectId) {
865
+ await this.supabase.from("lock_events").insert({
866
+ project_id: this._projectId,
867
+ event_type: eventType,
868
+ file_path: filePath,
869
+ requesting_agent: requestingAgent,
870
+ blocking_agent: blockingAgent || null,
871
+ intent: intent || null
872
+ });
873
+ }
874
+ } catch (e) {
875
+ logger.warn(`[logLockEvent] Failed to log ${eventType} event: ${e.message}`);
876
+ }
877
+ }
852
878
  // --- Decision & Orchestration ---
853
879
  async proposeFileAccess(agentId, filePath, intent, userPrompt) {
854
880
  return await this.mutex.runExclusive(async () => {
@@ -864,6 +890,7 @@ ${notepad}`;
864
890
  });
865
891
  if (result.status === "DENIED") {
866
892
  logger.info(`[proposeFileAccess] DENIED by server: ${result.message}`);
893
+ this.logLockEvent("BLOCKED", filePath, agentId, result.current_lock?.agent_id, intent);
867
894
  return {
868
895
  status: "REQUIRES_ORCHESTRATION",
869
896
  message: result.message || `File '${filePath}' is locked by another agent`,
@@ -871,6 +898,7 @@ ${notepad}`;
871
898
  };
872
899
  }
873
900
  logger.info(`[proposeFileAccess] GRANTED by server`);
901
+ this.logLockEvent("GRANTED", filePath, agentId, void 0, intent);
874
902
  await this.appendToNotepad(`
875
903
  - [LOCK] ${agentId} locked ${filePath}
876
904
  Intent: ${intent}`);
@@ -900,6 +928,7 @@ ${notepad}`;
900
928
  if (error) throw error;
901
929
  const row = Array.isArray(data) ? data[0] : data;
902
930
  if (row && row.status === "DENIED") {
931
+ this.logLockEvent("BLOCKED", filePath, agentId, row.owner_id, intent);
903
932
  return {
904
933
  status: "REQUIRES_ORCHESTRATION",
905
934
  message: `Conflict: File '${filePath}' is locked by '${row.owner_id}'`,
@@ -911,6 +940,7 @@ ${notepad}`;
911
940
  }
912
941
  };
913
942
  }
943
+ this.logLockEvent("GRANTED", filePath, agentId, void 0, intent);
914
944
  await this.appendToNotepad(`
915
945
  - [LOCK] ${agentId} locked ${filePath}
916
946
  Intent: ${intent}`);
@@ -923,6 +953,7 @@ ${notepad}`;
923
953
  if (existing) {
924
954
  const isStale = Date.now() - existing.timestamp > this.lockTimeout;
925
955
  if (!isStale && existing.agentId !== agentId) {
956
+ this.logLockEvent("BLOCKED", filePath, agentId, existing.agentId, intent);
926
957
  return {
927
958
  status: "REQUIRES_ORCHESTRATION",
928
959
  message: `Conflict: File '${filePath}' is currently locked by '${existing.agentId}'`,
@@ -932,6 +963,7 @@ ${notepad}`;
932
963
  }
933
964
  this.state.locks[filePath] = { agentId, filePath, intent, userPrompt, timestamp: Date.now() };
934
965
  await this.saveState();
966
+ this.logLockEvent("GRANTED", filePath, agentId, void 0, intent);
935
967
  await this.appendToNotepad(`
936
968
  - [LOCK] ${agentId} locked ${filePath}
937
969
  Intent: ${intent}`);
@@ -1226,8 +1258,52 @@ async function ensureFileSystem() {
1226
1258
  await fs4.mkdir(axisInstructions, { recursive: true }).catch(() => {
1227
1259
  });
1228
1260
  const defaults = [
1229
- ["context.md", "# Project Context\n\n"],
1230
- ["conventions.md", "# Coding Conventions\n\n"],
1261
+ ["context.md", `# Project Context
1262
+
1263
+ ## Overview
1264
+ This project uses Axis \u2014 an open-source coordination layer for AI agents.
1265
+ Axis provides shared context, atomic file locks, a job board, and real-time
1266
+ activity feeds so that multiple agents (Cursor, Claude, Windsurf, Codex, etc.)
1267
+ can work on the same codebase without conflicts.
1268
+
1269
+ ## Architecture
1270
+ - **MCP Server**: Exposes tools (locks, jobs, context, search) via the Model Context Protocol.
1271
+ - **Supabase Backend**: Postgres for state (locks, jobs, profiles); Realtime for live feeds.
1272
+ - **Frontend**: Next.js App Router + Tailwind CSS dashboard at useaxis.dev.
1273
+ - **npm Packages**: @virsanghavi/axis-server (runtime), @virsanghavi/axis-init (scaffolding).
1274
+
1275
+ ## Core Features
1276
+ 1. File Locking \u2014 atomic, cross-IDE locks with 30-min TTL.
1277
+ 2. Job Board \u2014 post / claim / complete tasks with priorities and dependencies.
1278
+ 3. Shared Context \u2014 live notepad visible to every agent in real time.
1279
+ 4. RAG Search \u2014 vector search over the indexed codebase.
1280
+ 5. Soul Files \u2014 context.md, conventions.md, activity.md define project identity.
1281
+ `],
1282
+ ["conventions.md", `# Coding Conventions
1283
+
1284
+ ## Language & Style
1285
+ - TypeScript everywhere (strict mode).
1286
+ - Tailwind CSS for styling; no raw CSS unless unavoidable.
1287
+ - Functional React components; prefer server components in Next.js App Router.
1288
+
1289
+ ## Agent Behavioral Norms
1290
+
1291
+ ### Plan Before Write
1292
+ Every non-trivial task must follow: post_job -> claim_next_job -> propose_file_access -> (edit) -> complete_job.
1293
+ Skip only for single-line typo fixes.
1294
+
1295
+ ### Force-Unlock Policy
1296
+ force_unlock is a LAST RESORT. Before using it:
1297
+ 1. Verify the lock is > 25 minutes old.
1298
+ 2. Confirm the locking agent is unresponsive.
1299
+ 3. Provide a specific reason string.
1300
+ Never casually unlock files \u2014 always try propose_file_access first.
1301
+
1302
+ ### Proactive Tool Usage
1303
+ Agents must use Axis MCP tools by default \u2014 do not wait for the user to say "use Axis".
1304
+ On session start, call get_project_soul or read_context to load project state.
1305
+ After significant progress, call update_shared_context.
1306
+ `],
1231
1307
  ["activity.md", "# Activity Log\n\n"]
1232
1308
  ];
1233
1309
  for (const [file, content] of defaults) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@virsanghavi/axis-server",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Axis MCP Server CLI",
5
5
  "main": "dist/index.js",
6
6
  "bin": {