@minhpnq1807/contextos 0.5.19 → 0.5.21

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.21
4
+
5
+ - Makes prompt hooks fall back to direct scoring when the `ctx-mcp` bridge socket is missing, stale, or unavailable, avoiding empty `hook context` output.
6
+ - Prioritizes imperative and code-review-graph rules in injected critical context so `IMPORTANT` project rules appear before generic semantic matches.
7
+ - Improves workflow detection for CI, deployment, server, runtime, debugging, and issue-analysis prompts.
8
+
9
+ ## 0.5.20
10
+
11
+ - Refreshes stale `ctx-mcp` Ruler entries that point at temporary paths such as `/tmp/contextos/...`.
12
+ - Keeps `ctx install` fast by skipping large skill/workflow discovery embedding warmup unless `CONTEXTOS_INSTALL_WARM_DISCOVERY=1` is set.
13
+
3
14
  ## 0.5.19
4
15
 
5
16
  - Makes `ctx-mcp` startup non-mutating: the MCP server now verifies the local embedding model without warming or rewriting `embeddings.db` during agent initialization.
package/bin/ctx.js CHANGED
@@ -299,16 +299,21 @@ async function warmInstallEmbeddings() {
299
299
  dataDir,
300
300
  allowRemote: !modelReady
301
301
  });
302
- const skillResult = await warmSkillEmbeddings({
303
- cwd: process.cwd(),
304
- dataDir,
305
- allowRemote: !modelReady
306
- });
307
- const workflowResult = await warmWorkflowEmbeddings({
308
- cwd: process.cwd(),
309
- dataDir,
310
- allowRemote: !modelReady
311
- });
302
+ const warmDiscovery = process.env.CONTEXTOS_INSTALL_WARM_DISCOVERY === "1";
303
+ const skillResult = warmDiscovery
304
+ ? await warmSkillEmbeddings({
305
+ cwd: process.cwd(),
306
+ dataDir,
307
+ allowRemote: !modelReady
308
+ })
309
+ : { count: 0 };
310
+ const workflowResult = warmDiscovery
311
+ ? await warmWorkflowEmbeddings({
312
+ cwd: process.cwd(),
313
+ dataDir,
314
+ allowRemote: !modelReady
315
+ })
316
+ : { count: 0 };
312
317
  return { ...result, modelAlreadyCached: modelReady, fileCount: fileResult.count, skillCount: skillResult.count, workflowCount: workflowResult.count };
313
318
  }
314
319
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minhpnq1807/contextos",
3
- "version": "0.5.19",
3
+ "version": "0.5.21",
4
4
  "description": "Task-aware AGENTS.md context injection and compliance reporting for Codex, Claude Code, and Antigravity.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -259,7 +259,7 @@ export function scoreRules(rules, task, openFiles = []) {
259
259
 
260
260
  const lowerRule = rule.content.toLowerCase();
261
261
  if (IMPORTANT_WORDS.some((word) => lowerRule.includes(word))) {
262
- score += 0.4;
262
+ score += 0.5;
263
263
  reasons.push("imperative");
264
264
  }
265
265
 
@@ -2,6 +2,7 @@ import { scheduleContext } from "./scheduler.js";
2
2
  import { appendJsonLine, writeJsonFile } from "./fs-utils.js";
3
3
  import { callCtxScoreContext } from "./ctx-mcp-client.js";
4
4
  import { resolveHookCwd } from "./hook-io.js";
5
+ import { scoreContext as scoreContextDirect } from "./score-context.js";
5
6
  import path from "node:path";
6
7
 
7
8
  export async function handlePromptPayload(
@@ -21,15 +22,31 @@ export async function handlePromptPayload(
21
22
  const openFiles = payload.openFiles || payload.open_files || payload.files || [];
22
23
  const dataDir = dataPath ? path.dirname(dataPath) : undefined;
23
24
 
24
- const scored = await scoreContextClient({
25
- cwd,
26
- prompt,
27
- openFiles,
28
- maxFiles: 3
29
- }, {
30
- dataDir: mcpDataDir || dataDir,
31
- timeoutMs: Number(process.env.CONTEXTOS_MCP_BRIDGE_TIMEOUT_MS || 1000)
32
- });
25
+ let scored;
26
+ try {
27
+ scored = await scoreContextClient({
28
+ cwd,
29
+ prompt,
30
+ openFiles,
31
+ maxFiles: 3
32
+ }, {
33
+ dataDir: mcpDataDir || dataDir,
34
+ timeoutMs: Number(process.env.CONTEXTOS_MCP_BRIDGE_TIMEOUT_MS || 1000)
35
+ });
36
+ } catch (error) {
37
+ scored = await scoreContextDirect({
38
+ cwd,
39
+ prompt,
40
+ openFiles,
41
+ maxFiles: 3,
42
+ dataDir: mcpDataDir || dataDir
43
+ });
44
+ scored.telemetry = {
45
+ ...(scored.telemetry || {}),
46
+ bridgeStatus: "fallback",
47
+ bridgeError: error?.message || String(error)
48
+ };
49
+ }
33
50
 
34
51
  if (scored.error) throw new Error(scored.error);
35
52
  const scoredRules = scored.scoredRules || [];
@@ -268,6 +268,10 @@ function readRulerMcpServers({ tomlPath } = {}) {
268
268
  return servers;
269
269
  }
270
270
 
271
+ function readRulerMcpServer({ tomlPath, name } = {}) {
272
+ return readRulerMcpServers({ tomlPath }).find((server) => server.name === name) || null;
273
+ }
274
+
271
275
  function antigravityMcpConfigPaths() {
272
276
  const home = process.env.HOME || process.cwd();
273
277
  return [
@@ -415,7 +419,12 @@ export function injectCtxMcp({ tomlPath, mcpServerPath, agents = DEFAULT_AGENTS,
415
419
 
416
420
  let content = fs.readFileSync(tomlPath, "utf8");
417
421
  const sectionExists = hasTomlSection(content, `mcp_servers.${CTX_MCP_NAME}`);
418
- if (sectionExists && !force) return { changed: false, existed: true };
422
+ if (sectionExists && !force) {
423
+ const existingServer = readRulerMcpServer({ tomlPath, name: CTX_MCP_NAME });
424
+ const existingPath = existingServer?.command === "node" ? existingServer.args?.[0] : existingServer?.command;
425
+ if (existingPath && isRunnableMcpCommand(existingPath)) return { changed: false, existed: true };
426
+ force = true;
427
+ }
419
428
 
420
429
  if (force) {
421
430
  content = removeTomlSection(content, "mcp");
@@ -1,9 +1,10 @@
1
1
  const MAX_CONTEXT_CHARS = 4000;
2
2
 
3
3
  export function scheduleContext({ rules = [], relevantFiles = [], suggestedSkills = [], suggestedWorkflows = [], maxChars = MAX_CONTEXT_CHARS } = {}) {
4
- const high = rules.filter((rule) => rule.score >= 0.5);
5
- const mid = rules.filter((rule) => rule.score >= 0.1 && rule.score < 0.5);
6
- const dropped = rules.filter((rule) => rule.score < 0.1);
4
+ const orderedRules = [...rules].sort(compareRulesForContext);
5
+ const high = orderedRules.filter((rule) => rule.score >= 0.5);
6
+ const mid = orderedRules.filter((rule) => rule.score >= 0.1 && rule.score < 0.5);
7
+ const dropped = orderedRules.filter((rule) => rule.score < 0.1);
7
8
 
8
9
  const sections = [];
9
10
  if (high.length) {
@@ -37,6 +38,20 @@ export function scheduleContext({ rules = [], relevantFiles = [], suggestedSkill
37
38
  };
38
39
  }
39
40
 
41
+ function compareRulesForContext(a, b) {
42
+ return rulePriority(b) - rulePriority(a)
43
+ || Number(b.score || 0) - Number(a.score || 0)
44
+ || Number(a.originalOrder || 0) - Number(b.originalOrder || 0);
45
+ }
46
+
47
+ function rulePriority(rule) {
48
+ const content = String(rule.content || "").toLowerCase();
49
+ let priority = 0;
50
+ if (/\b(important|always|must|required|mandatory|strictly|never)\b/.test(content)) priority += 10;
51
+ if (/\b(code-review-graph|query_graph|get_minimal_context|detect_changes|semantic_search_nodes)\b/.test(content)) priority += 4;
52
+ return priority;
53
+ }
54
+
40
55
  function section(title, lines) {
41
56
  if (!lines.length) return "";
42
57
  return `## ${title}\n${lines.join("\n")}`;
@@ -320,12 +320,12 @@ function scoreWorkflowsByKeyword({ prompt, workflows }) {
320
320
 
321
321
  function actionIntentBonus(normalizedPrompt, workflow) {
322
322
  const name = normalize(`${workflow.name} ${workflow.title} ${workflow.description}`);
323
- const implementationIntent = /\b(implement|feature|build|create|fix|debug|test|ci|failing)\b/.test(normalizedPrompt);
323
+ const implementationIntent = /\b(implement|feature|build|create|fix|debug|test|ci|cd|pipeline|failing|failure|error|issue|bug|analyze|analyse|solution|server|runtime|deploy|fly|flyio|loi|phan tich|giai phap)\b/.test(normalizedPrompt);
324
324
  const docsIntent = /\b(doc|docs|documentation|readme|changelog|roadmap)\b/.test(normalizedPrompt);
325
325
  const orchestrationIntent = /\b(parallel|sequential|chain|delegate|agent|subagent|orchestrat)\b/.test(normalizedPrompt);
326
- if (implementationIntent && /\b(primary|implementation|testing|debugging|quality)\b/.test(name)) return 0.35;
327
- if (docsIntent && /\b(documentation|docs|changelog|roadmap)\b/.test(name)) return 0.35;
328
- if (orchestrationIntent && /\b(orchestration|parallel|sequential|chaining)\b/.test(name)) return 0.35;
326
+ if (implementationIntent && /\b(primary|implementation|testing|debugging|quality)\b/.test(name)) return 0.42;
327
+ if (docsIntent && /\b(documentation|docs|changelog|roadmap)\b/.test(name)) return 0.42;
328
+ if (orchestrationIntent && /\b(orchestration|parallel|sequential|chaining)\b/.test(name)) return 0.42;
329
329
  return 0;
330
330
  }
331
331