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.
- package/index.js +101 -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
|
|