loopctl-mcp-server 2.26.0 → 2.28.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.
Files changed (2) hide show
  1. package/index.js +101 -2
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1050,6 +1050,29 @@ async function knowledgeConflicts({ limit, offset }) {
1050
1050
  return toContent(result);
1051
1051
  }
1052
1052
 
1053
+ async function knowledgeResolveConflict({
1054
+ source_article_id,
1055
+ target_article_id,
1056
+ disposition,
1057
+ authoritative_article_id,
1058
+ classification,
1059
+ evidence,
1060
+ confidence,
1061
+ }) {
1062
+ const payload = { source_article_id, target_article_id, disposition };
1063
+ if (authoritative_article_id) payload.authoritative_article_id = authoritative_article_id;
1064
+ if (classification) payload.classification = classification;
1065
+ if (evidence) payload.evidence = evidence;
1066
+ if (confidence) payload.confidence = confidence;
1067
+ const result = await apiCall(
1068
+ "POST",
1069
+ "/api/v1/knowledge/conflicts/resolve",
1070
+ payload,
1071
+ process.env.LOOPCTL_AGENT_KEY,
1072
+ );
1073
+ return toContent(result);
1074
+ }
1075
+
1053
1076
  async function knowledgeLint({ project_id, stale_days, min_coverage, max_per_category }) {
1054
1077
  const params = new URLSearchParams();
1055
1078
  if (stale_days != null) params.set("stale_days", String(stale_days));
@@ -2444,7 +2467,9 @@ const TOOLS = [
2444
2467
  "a keyword and a semantic sub-search, each capped at 100, so up to ~200), or filtered_set " +
2445
2468
  "(list mode: the full set). Do NOT use a relevance-mode total_count to size the wiki — use " +
2446
2469
  "list mode or knowledge_stats. " +
2447
- "Pass story_id when working on a loopctl story so reads attribute correctly.",
2470
+ "Pass story_id when working on a loopctl story so reads attribute correctly. " +
2471
+ "When you knowledge_get a result and it carries `potential_conflicts`, resolve it if it's " +
2472
+ "material to your task (see knowledge_get / the conflict-resolution wiki playbook).",
2448
2473
  inputSchema: {
2449
2474
  type: "object",
2450
2475
  properties: {
@@ -2497,7 +2522,12 @@ const TOOLS = [
2497
2522
  name: "knowledge_get",
2498
2523
  description:
2499
2524
  "Get full article content by ID. Use after search to read an article in detail. " +
2500
- "Pass story_id when working on a loopctl story so reads attribute correctly.",
2525
+ "Pass story_id when working on a loopctl story so reads attribute correctly. " +
2526
+ "If the response carries a non-empty `potential_conflicts` array AND the conflict is " +
2527
+ "material to your current task, act on it: read the peer, judge redundant/complementary/" +
2528
+ "contradictory against the live system, and knowledge_resolve_conflict (dismiss a false " +
2529
+ "positive, supersede when one clearly wins, merge when both should combine). If you can't " +
2530
+ "tell which is right, leave it. See the 'Resolving knowledge conflicts' wiki playbook.",
2501
2531
  inputSchema: {
2502
2532
  type: "object",
2503
2533
  properties: {
@@ -2925,6 +2955,72 @@ const TOOLS = [
2925
2955
  required: [],
2926
2956
  },
2927
2957
  },
2958
+ {
2959
+ name: "knowledge_resolve_conflict",
2960
+ description:
2961
+ "Record YOUR verdict on a potential-conflict pair (from knowledge_conflicts or an " +
2962
+ "article's potential_conflicts). You have the live context the KB lacks — it never " +
2963
+ "re-judges, it acts on what you record. Dispositions: 'dismiss' (a false positive — " +
2964
+ "the two don't actually conflict; drops out of the queue immediately); 'supersede' " +
2965
+ "(one article wins — pass authoritative_article_id, the winner; the nightly executor " +
2966
+ "creates a supersedes link and retires the loser, but ONLY at confidence:\"high\" — " +
2967
+ "reversible and audited); 'merge' (at confidence:\"high\" the nightly executor has an LLM " +
2968
+ "synthesize the two into ONE new DRAFT — both sources preserved, never auto-published, " +
2969
+ "for you/a human to review and publish). Non-destructive " +
2970
+ "at agent role — you record intent; the privileged nightly job executes it. " +
2971
+ "Last-write-wins per pair, so re-recording with fresher ground truth overrides. " +
2972
+ "Resolve only conflicts material to your current task; adjudicate against the actual " +
2973
+ "system, and if you can't tell which is right, leave it (or record low confidence) " +
2974
+ "rather than guessing.",
2975
+ inputSchema: {
2976
+ type: "object",
2977
+ properties: {
2978
+ source_article_id: {
2979
+ type: "string",
2980
+ description: "One article of the conflict pair (UUID). Order does not matter.",
2981
+ },
2982
+ target_article_id: {
2983
+ type: "string",
2984
+ description: "The other article of the conflict pair (UUID).",
2985
+ },
2986
+ disposition: {
2987
+ type: "string",
2988
+ enum: ["dismiss", "supersede", "merge"],
2989
+ description:
2990
+ "dismiss = false positive; supersede = one wins (set authoritative_article_id); " +
2991
+ "merge = combine both into one new DRAFT (LLM-synthesized by the nightly executor " +
2992
+ "at high confidence; sources preserved, never auto-published).",
2993
+ },
2994
+ authoritative_article_id: {
2995
+ type: "string",
2996
+ description:
2997
+ "For supersede/merge: the WINNING article (must be one of the pair). The other " +
2998
+ "is the loser to retire/merge into the winner.",
2999
+ },
3000
+ classification: {
3001
+ type: "string",
3002
+ enum: ["redundant", "complementary", "contradictory"],
3003
+ description:
3004
+ "Your judgment of the relationship: redundant (same claim), complementary (same " +
3005
+ "topic, different facets — usually a dismiss), or contradictory (can't both be true).",
3006
+ },
3007
+ evidence: {
3008
+ type: "string",
3009
+ description:
3010
+ "Why you're sure — ideally a ground-truth reference (commit, file:line, URL, or the " +
3011
+ "observed behavior). Recorded for audit and for a human reviewing low-confidence calls.",
3012
+ },
3013
+ confidence: {
3014
+ type: "string",
3015
+ enum: ["high", "medium", "low"],
3016
+ description:
3017
+ "high, medium, or low. supersede auto-executes only at 'high'; lower confidence is " +
3018
+ "recorded but left for review. Default medium.",
3019
+ },
3020
+ },
3021
+ required: ["source_article_id", "target_article_id", "disposition"],
3022
+ },
3023
+ },
2928
3024
  {
2929
3025
  name: "knowledge_lint",
2930
3026
  description:
@@ -3548,6 +3644,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3548
3644
  case "knowledge_conflicts":
3549
3645
  return await knowledgeConflicts(args);
3550
3646
 
3647
+ case "knowledge_resolve_conflict":
3648
+ return await knowledgeResolveConflict(args);
3649
+
3551
3650
  case "knowledge_lint":
3552
3651
  return await knowledgeLint(args);
3553
3652
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loopctl-mcp-server",
3
- "version": "2.26.0",
3
+ "version": "2.28.0",
4
4
  "description": "MCP server for loopctl — structural trust for AI development loops",
5
5
  "type": "module",
6
6
  "main": "index.js",