@a-company/paradigm 3.14.1 → 3.16.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.
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// ../paradigm-mcp/src/tools/reindex.ts
|
|
4
|
-
import * as
|
|
5
|
-
import * as
|
|
6
|
-
import * as
|
|
4
|
+
import * as fs9 from "fs";
|
|
5
|
+
import * as path10 from "path";
|
|
6
|
+
import * as yaml8 from "js-yaml";
|
|
7
7
|
|
|
8
8
|
// ../paradigm-mcp/node_modules/.pnpm/@a-company+premise-core@0.2.0_typescript@5.9.3/node_modules/@a-company/premise-core/dist/index.js
|
|
9
9
|
import * as yaml3 from "js-yaml";
|
|
@@ -1991,8 +1991,8 @@ var SessionTracker = class {
|
|
|
1991
1991
|
* Extract resource type from URI
|
|
1992
1992
|
*/
|
|
1993
1993
|
extractResourceType(uri) {
|
|
1994
|
-
const
|
|
1995
|
-
const firstPart =
|
|
1994
|
+
const path11 = uri.replace("paradigm://", "");
|
|
1995
|
+
const firstPart = path11.split("/")[0];
|
|
1996
1996
|
return firstPart || "unknown";
|
|
1997
1997
|
}
|
|
1998
1998
|
/**
|
|
@@ -4389,8 +4389,8 @@ async function getAffectedPersonas(rootDir, symbol) {
|
|
|
4389
4389
|
}
|
|
4390
4390
|
return results;
|
|
4391
4391
|
}
|
|
4392
|
-
function deepGet(obj,
|
|
4393
|
-
const parts =
|
|
4392
|
+
function deepGet(obj, path11) {
|
|
4393
|
+
const parts = path11.split(/[.\[\]]+/).filter(Boolean);
|
|
4394
4394
|
let current = obj;
|
|
4395
4395
|
for (const part of parts) {
|
|
4396
4396
|
if (current == null || typeof current !== "object") return void 0;
|
|
@@ -4558,6 +4558,341 @@ async function validateAgainstSentinel(persona, options = {}) {
|
|
|
4558
4558
|
};
|
|
4559
4559
|
}
|
|
4560
4560
|
|
|
4561
|
+
// ../paradigm-mcp/src/utils/protocol-loader.ts
|
|
4562
|
+
import * as fs8 from "fs";
|
|
4563
|
+
import * as path9 from "path";
|
|
4564
|
+
import * as yaml7 from "js-yaml";
|
|
4565
|
+
var PROTOCOLS_DIR = ".paradigm/protocols";
|
|
4566
|
+
var INDEX_FILE2 = "index.yaml";
|
|
4567
|
+
async function loadProtocols(rootDir) {
|
|
4568
|
+
const protocolsDir = path9.join(rootDir, PROTOCOLS_DIR);
|
|
4569
|
+
if (!fs8.existsSync(protocolsDir)) {
|
|
4570
|
+
return [];
|
|
4571
|
+
}
|
|
4572
|
+
const files = fs8.readdirSync(protocolsDir).filter((f) => f.endsWith(".protocol")).sort();
|
|
4573
|
+
const protocols = [];
|
|
4574
|
+
for (const file of files) {
|
|
4575
|
+
try {
|
|
4576
|
+
const content = fs8.readFileSync(path9.join(protocolsDir, file), "utf8");
|
|
4577
|
+
const protocol = yaml7.load(content);
|
|
4578
|
+
if (protocol?.id && protocol?.name) {
|
|
4579
|
+
protocols.push(protocol);
|
|
4580
|
+
}
|
|
4581
|
+
} catch {
|
|
4582
|
+
}
|
|
4583
|
+
}
|
|
4584
|
+
return protocols;
|
|
4585
|
+
}
|
|
4586
|
+
async function loadProtocol(rootDir, id) {
|
|
4587
|
+
const slug = id.replace(/^P-/, "");
|
|
4588
|
+
const filePath = path9.join(rootDir, PROTOCOLS_DIR, `${slug}.protocol`);
|
|
4589
|
+
if (fs8.existsSync(filePath)) {
|
|
4590
|
+
try {
|
|
4591
|
+
const content = fs8.readFileSync(filePath, "utf8");
|
|
4592
|
+
return yaml7.load(content);
|
|
4593
|
+
} catch {
|
|
4594
|
+
return null;
|
|
4595
|
+
}
|
|
4596
|
+
}
|
|
4597
|
+
const protocols = await loadProtocols(rootDir);
|
|
4598
|
+
return protocols.find((p) => p.id === id) || null;
|
|
4599
|
+
}
|
|
4600
|
+
async function loadProtocolIndex(rootDir) {
|
|
4601
|
+
const indexPath = path9.join(rootDir, PROTOCOLS_DIR, INDEX_FILE2);
|
|
4602
|
+
if (!fs8.existsSync(indexPath)) {
|
|
4603
|
+
return null;
|
|
4604
|
+
}
|
|
4605
|
+
try {
|
|
4606
|
+
const content = fs8.readFileSync(indexPath, "utf8");
|
|
4607
|
+
return yaml7.load(content);
|
|
4608
|
+
} catch {
|
|
4609
|
+
return null;
|
|
4610
|
+
}
|
|
4611
|
+
}
|
|
4612
|
+
async function searchProtocols(rootDir, task, limit = 3) {
|
|
4613
|
+
const protocols = await loadProtocols(rootDir);
|
|
4614
|
+
if (protocols.length === 0) return [];
|
|
4615
|
+
const keywords = tokenize(task);
|
|
4616
|
+
if (keywords.length === 0) return [];
|
|
4617
|
+
const scored = [];
|
|
4618
|
+
for (const protocol of protocols) {
|
|
4619
|
+
let score = 0;
|
|
4620
|
+
for (const trigger of protocol.trigger) {
|
|
4621
|
+
const triggerLower = trigger.toLowerCase();
|
|
4622
|
+
for (const kw of keywords) {
|
|
4623
|
+
if (triggerLower.includes(kw)) {
|
|
4624
|
+
score += 3;
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
for (const tag of protocol.tags) {
|
|
4629
|
+
const tagLower = tag.toLowerCase();
|
|
4630
|
+
for (const kw of keywords) {
|
|
4631
|
+
if (tagLower.includes(kw) || kw.includes(tagLower)) {
|
|
4632
|
+
score += 2;
|
|
4633
|
+
}
|
|
4634
|
+
}
|
|
4635
|
+
}
|
|
4636
|
+
const nameLower = protocol.name.toLowerCase();
|
|
4637
|
+
const descLower = protocol.description.toLowerCase();
|
|
4638
|
+
for (const kw of keywords) {
|
|
4639
|
+
if (nameLower.includes(kw)) score += 1;
|
|
4640
|
+
if (descLower.includes(kw)) score += 1;
|
|
4641
|
+
}
|
|
4642
|
+
for (const step of protocol.steps) {
|
|
4643
|
+
if (step.notes) {
|
|
4644
|
+
const notesLower = step.notes.toLowerCase();
|
|
4645
|
+
for (const kw of keywords) {
|
|
4646
|
+
if (notesLower.includes(kw)) score += 0.5;
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
if (score > 0) {
|
|
4651
|
+
scored.push({ protocol, score });
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
scored.sort((a, b) => b.score - a.score);
|
|
4655
|
+
return scored.slice(0, limit);
|
|
4656
|
+
}
|
|
4657
|
+
async function recordProtocol(rootDir, protocol) {
|
|
4658
|
+
const protocolsDir = path9.join(rootDir, PROTOCOLS_DIR);
|
|
4659
|
+
if (!fs8.existsSync(protocolsDir)) {
|
|
4660
|
+
fs8.mkdirSync(protocolsDir, { recursive: true });
|
|
4661
|
+
}
|
|
4662
|
+
const slug = slugify(protocol.name);
|
|
4663
|
+
const id = `P-${slug}`;
|
|
4664
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4665
|
+
const full = {
|
|
4666
|
+
id,
|
|
4667
|
+
name: protocol.name,
|
|
4668
|
+
description: protocol.description,
|
|
4669
|
+
trigger: protocol.trigger,
|
|
4670
|
+
tags: protocol.tags,
|
|
4671
|
+
symbols: protocol.symbols || [],
|
|
4672
|
+
exemplar: protocol.exemplar,
|
|
4673
|
+
steps: protocol.steps,
|
|
4674
|
+
recorded_from: protocol.recorded_from,
|
|
4675
|
+
recorded_at: now,
|
|
4676
|
+
last_verified: now,
|
|
4677
|
+
verified_by: protocol.verified_by || "claude-opus-4-6",
|
|
4678
|
+
status: "current"
|
|
4679
|
+
};
|
|
4680
|
+
const filePath = path9.join(protocolsDir, `${slug}.protocol`);
|
|
4681
|
+
fs8.writeFileSync(filePath, yaml7.dump(full, { lineWidth: -1, noRefs: true }), "utf8");
|
|
4682
|
+
return id;
|
|
4683
|
+
}
|
|
4684
|
+
async function updateProtocol(rootDir, id, partial, refresh = false) {
|
|
4685
|
+
const protocol = await loadProtocol(rootDir, id);
|
|
4686
|
+
if (!protocol) return false;
|
|
4687
|
+
if (partial.name !== void 0) protocol.name = partial.name;
|
|
4688
|
+
if (partial.description !== void 0) protocol.description = partial.description;
|
|
4689
|
+
if (partial.trigger !== void 0) protocol.trigger = partial.trigger;
|
|
4690
|
+
if (partial.tags !== void 0) protocol.tags = partial.tags;
|
|
4691
|
+
if (partial.symbols !== void 0) protocol.symbols = partial.symbols;
|
|
4692
|
+
if (partial.exemplar !== void 0) protocol.exemplar = partial.exemplar;
|
|
4693
|
+
if (partial.steps !== void 0) protocol.steps = partial.steps;
|
|
4694
|
+
if (partial.status !== void 0) protocol.status = partial.status;
|
|
4695
|
+
if (partial.verified_by !== void 0) protocol.verified_by = partial.verified_by;
|
|
4696
|
+
if (refresh) {
|
|
4697
|
+
protocol.last_verified = (/* @__PURE__ */ new Date()).toISOString();
|
|
4698
|
+
protocol.verified_by = partial.verified_by || "claude-opus-4-6";
|
|
4699
|
+
}
|
|
4700
|
+
const slug = id.replace(/^P-/, "");
|
|
4701
|
+
const filePath = path9.join(rootDir, PROTOCOLS_DIR, `${slug}.protocol`);
|
|
4702
|
+
fs8.writeFileSync(filePath, yaml7.dump(protocol, { lineWidth: -1, noRefs: true }), "utf8");
|
|
4703
|
+
return true;
|
|
4704
|
+
}
|
|
4705
|
+
function validateProtocol(rootDir, protocol) {
|
|
4706
|
+
const issues = [];
|
|
4707
|
+
let status = "current";
|
|
4708
|
+
if (protocol.exemplar) {
|
|
4709
|
+
const exemplarPath = path9.join(rootDir, protocol.exemplar);
|
|
4710
|
+
if (!fs8.existsSync(exemplarPath)) {
|
|
4711
|
+
issues.push(`Exemplar missing: ${protocol.exemplar}`);
|
|
4712
|
+
status = "broken";
|
|
4713
|
+
} else {
|
|
4714
|
+
const stat = fs8.statSync(exemplarPath);
|
|
4715
|
+
if (stat.mtime.toISOString() > protocol.last_verified) {
|
|
4716
|
+
issues.push(`Exemplar modified since last verified: ${protocol.exemplar}`);
|
|
4717
|
+
if (status !== "broken") status = "stale";
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
for (const step of protocol.steps) {
|
|
4722
|
+
if (step.template_from) {
|
|
4723
|
+
const templatePath = path9.join(rootDir, step.template_from);
|
|
4724
|
+
if (!fs8.existsSync(templatePath)) {
|
|
4725
|
+
issues.push(`Template file missing: ${step.template_from}`);
|
|
4726
|
+
status = "broken";
|
|
4727
|
+
}
|
|
4728
|
+
}
|
|
4729
|
+
if (step.action === "modify" && step.target) {
|
|
4730
|
+
const targetPath = path9.join(rootDir, step.target);
|
|
4731
|
+
if (!step.target.includes("{") && !fs8.existsSync(targetPath)) {
|
|
4732
|
+
issues.push(`Modify target missing: ${step.target}`);
|
|
4733
|
+
status = "broken";
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4737
|
+
return { status, issues };
|
|
4738
|
+
}
|
|
4739
|
+
async function rebuildProtocolIndex(rootDir) {
|
|
4740
|
+
const protocols = await loadProtocols(rootDir);
|
|
4741
|
+
const entries = [];
|
|
4742
|
+
let current = 0;
|
|
4743
|
+
let stale = 0;
|
|
4744
|
+
let broken = 0;
|
|
4745
|
+
for (const protocol of protocols) {
|
|
4746
|
+
const validation = validateProtocol(rootDir, protocol);
|
|
4747
|
+
if (protocol.status !== validation.status) {
|
|
4748
|
+
protocol.status = validation.status;
|
|
4749
|
+
const slug = protocol.id.replace(/^P-/, "");
|
|
4750
|
+
const filePath = path9.join(rootDir, PROTOCOLS_DIR, `${slug}.protocol`);
|
|
4751
|
+
if (fs8.existsSync(filePath)) {
|
|
4752
|
+
fs8.writeFileSync(filePath, yaml7.dump(protocol, { lineWidth: -1, noRefs: true }), "utf8");
|
|
4753
|
+
}
|
|
4754
|
+
}
|
|
4755
|
+
switch (validation.status) {
|
|
4756
|
+
case "current":
|
|
4757
|
+
current++;
|
|
4758
|
+
break;
|
|
4759
|
+
case "stale":
|
|
4760
|
+
stale++;
|
|
4761
|
+
break;
|
|
4762
|
+
case "broken":
|
|
4763
|
+
broken++;
|
|
4764
|
+
break;
|
|
4765
|
+
}
|
|
4766
|
+
entries.push({
|
|
4767
|
+
id: protocol.id,
|
|
4768
|
+
name: protocol.name,
|
|
4769
|
+
status: validation.status,
|
|
4770
|
+
last_verified: protocol.last_verified,
|
|
4771
|
+
trigger: protocol.trigger,
|
|
4772
|
+
tags: protocol.tags
|
|
4773
|
+
});
|
|
4774
|
+
}
|
|
4775
|
+
const index = {
|
|
4776
|
+
version: "1.0",
|
|
4777
|
+
generated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4778
|
+
protocols: entries,
|
|
4779
|
+
health: {
|
|
4780
|
+
total: protocols.length,
|
|
4781
|
+
current,
|
|
4782
|
+
stale,
|
|
4783
|
+
broken
|
|
4784
|
+
}
|
|
4785
|
+
};
|
|
4786
|
+
const protocolsDir = path9.join(rootDir, PROTOCOLS_DIR);
|
|
4787
|
+
if (protocols.length > 0) {
|
|
4788
|
+
if (!fs8.existsSync(protocolsDir)) {
|
|
4789
|
+
fs8.mkdirSync(protocolsDir, { recursive: true });
|
|
4790
|
+
}
|
|
4791
|
+
const indexPath = path9.join(protocolsDir, INDEX_FILE2);
|
|
4792
|
+
fs8.writeFileSync(indexPath, yaml7.dump(index, { lineWidth: -1, noRefs: true }), "utf8");
|
|
4793
|
+
}
|
|
4794
|
+
return index;
|
|
4795
|
+
}
|
|
4796
|
+
function detectProtocolSuggestion(rootDir, filesCreated, filesModified) {
|
|
4797
|
+
if (!filesCreated || filesCreated.length < 2) return null;
|
|
4798
|
+
const dirGroups = {};
|
|
4799
|
+
for (const file of filesCreated) {
|
|
4800
|
+
const dir = path9.dirname(file);
|
|
4801
|
+
if (!dirGroups[dir]) dirGroups[dir] = [];
|
|
4802
|
+
dirGroups[dir].push(file);
|
|
4803
|
+
}
|
|
4804
|
+
for (const [dir, created] of Object.entries(dirGroups)) {
|
|
4805
|
+
if (created.length < 2) continue;
|
|
4806
|
+
const absDir = path9.join(rootDir, dir);
|
|
4807
|
+
if (!fs8.existsSync(absDir)) continue;
|
|
4808
|
+
const existing = fs8.readdirSync(absDir).filter((f) => {
|
|
4809
|
+
const ext = path9.extname(f);
|
|
4810
|
+
return [".ts", ".tsx", ".js", ".jsx", ".rs", ".py"].includes(ext);
|
|
4811
|
+
});
|
|
4812
|
+
const preExisting = existing.filter((f) => !created.some((c) => path9.basename(c) === f));
|
|
4813
|
+
if (preExisting.length > 0) {
|
|
4814
|
+
const exemplar = path9.join(dir, preExisting[0]);
|
|
4815
|
+
const steps = [
|
|
4816
|
+
...created.map((f) => ({ action: "create", target: f })),
|
|
4817
|
+
...filesModified.map((f) => ({ action: "modify", target: f }))
|
|
4818
|
+
];
|
|
4819
|
+
return {
|
|
4820
|
+
hint: `This session created ${created.length} new files in ${dir}/ following existing patterns. Consider recording a protocol.`,
|
|
4821
|
+
draft: {
|
|
4822
|
+
name: `Add a ${path9.basename(dir).replace(/s$/, "")}`,
|
|
4823
|
+
exemplar,
|
|
4824
|
+
steps
|
|
4825
|
+
}
|
|
4826
|
+
};
|
|
4827
|
+
}
|
|
4828
|
+
}
|
|
4829
|
+
return null;
|
|
4830
|
+
}
|
|
4831
|
+
function tokenize(text) {
|
|
4832
|
+
return text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length > 1).filter((w) => !STOP_WORDS.has(w));
|
|
4833
|
+
}
|
|
4834
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
4835
|
+
"a",
|
|
4836
|
+
"an",
|
|
4837
|
+
"the",
|
|
4838
|
+
"is",
|
|
4839
|
+
"are",
|
|
4840
|
+
"was",
|
|
4841
|
+
"were",
|
|
4842
|
+
"be",
|
|
4843
|
+
"been",
|
|
4844
|
+
"to",
|
|
4845
|
+
"of",
|
|
4846
|
+
"in",
|
|
4847
|
+
"for",
|
|
4848
|
+
"on",
|
|
4849
|
+
"with",
|
|
4850
|
+
"at",
|
|
4851
|
+
"by",
|
|
4852
|
+
"from",
|
|
4853
|
+
"it",
|
|
4854
|
+
"this",
|
|
4855
|
+
"that",
|
|
4856
|
+
"and",
|
|
4857
|
+
"or",
|
|
4858
|
+
"but",
|
|
4859
|
+
"if",
|
|
4860
|
+
"then",
|
|
4861
|
+
"so",
|
|
4862
|
+
"as",
|
|
4863
|
+
"do",
|
|
4864
|
+
"does",
|
|
4865
|
+
"did",
|
|
4866
|
+
"will",
|
|
4867
|
+
"would",
|
|
4868
|
+
"can",
|
|
4869
|
+
"could",
|
|
4870
|
+
"should",
|
|
4871
|
+
"may",
|
|
4872
|
+
"might",
|
|
4873
|
+
"must",
|
|
4874
|
+
"shall",
|
|
4875
|
+
"i",
|
|
4876
|
+
"me",
|
|
4877
|
+
"my",
|
|
4878
|
+
"we",
|
|
4879
|
+
"our",
|
|
4880
|
+
"you",
|
|
4881
|
+
"your",
|
|
4882
|
+
"he",
|
|
4883
|
+
"she",
|
|
4884
|
+
"how",
|
|
4885
|
+
"what",
|
|
4886
|
+
"when",
|
|
4887
|
+
"where",
|
|
4888
|
+
"which",
|
|
4889
|
+
"who",
|
|
4890
|
+
"whom"
|
|
4891
|
+
]);
|
|
4892
|
+
function slugify(name) {
|
|
4893
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
4894
|
+
}
|
|
4895
|
+
|
|
4561
4896
|
// ../paradigm-mcp/src/tools/reindex.ts
|
|
4562
4897
|
var SYMBOL_CATEGORIES = {
|
|
4563
4898
|
"@": { category: "features", prefix: "@" },
|
|
@@ -4646,10 +4981,10 @@ async function rebuildStaticFiles(rootDir, ctx) {
|
|
|
4646
4981
|
} else {
|
|
4647
4982
|
aggregation = await aggregateFromDirectory(rootDir);
|
|
4648
4983
|
}
|
|
4649
|
-
const projectName = ctx?.projectName ||
|
|
4650
|
-
const paradigmDir =
|
|
4651
|
-
if (!
|
|
4652
|
-
|
|
4984
|
+
const projectName = ctx?.projectName || path10.basename(rootDir);
|
|
4985
|
+
const paradigmDir = path10.join(rootDir, ".paradigm");
|
|
4986
|
+
if (!fs9.existsSync(paradigmDir)) {
|
|
4987
|
+
fs9.mkdirSync(paradigmDir, { recursive: true });
|
|
4653
4988
|
}
|
|
4654
4989
|
const scanIndex = generateScanIndex(
|
|
4655
4990
|
{
|
|
@@ -4659,22 +4994,22 @@ async function rebuildStaticFiles(rootDir, ctx) {
|
|
|
4659
4994
|
},
|
|
4660
4995
|
{ projectName }
|
|
4661
4996
|
);
|
|
4662
|
-
const scanIndexPath =
|
|
4663
|
-
|
|
4997
|
+
const scanIndexPath = path10.join(paradigmDir, "scan-index.json");
|
|
4998
|
+
fs9.writeFileSync(scanIndexPath, serializeScanIndex(scanIndex), "utf8");
|
|
4664
4999
|
filesWritten.push(".paradigm/scan-index.json");
|
|
4665
5000
|
const navigatorData = buildNavigatorData(rootDir, aggregation);
|
|
4666
|
-
const navigatorPath =
|
|
4667
|
-
|
|
5001
|
+
const navigatorPath = path10.join(paradigmDir, "navigator.yaml");
|
|
5002
|
+
fs9.writeFileSync(
|
|
4668
5003
|
navigatorPath,
|
|
4669
|
-
|
|
5004
|
+
yaml8.dump(navigatorData, { indent: 2, lineWidth: 120, noRefs: true, sortKeys: false }),
|
|
4670
5005
|
"utf8"
|
|
4671
5006
|
);
|
|
4672
5007
|
filesWritten.push(".paradigm/navigator.yaml");
|
|
4673
5008
|
const flowIndex = generateFlowIndex(rootDir, aggregation.purposeFiles);
|
|
4674
5009
|
let flowCount = 0;
|
|
4675
5010
|
if (flowIndex && Object.keys(flowIndex.flows).length > 0) {
|
|
4676
|
-
const flowIndexPath =
|
|
4677
|
-
|
|
5011
|
+
const flowIndexPath = path10.join(paradigmDir, "flow-index.json");
|
|
5012
|
+
fs9.writeFileSync(flowIndexPath, JSON.stringify(flowIndex, null, 2), "utf8");
|
|
4678
5013
|
filesWritten.push(".paradigm/flow-index.json");
|
|
4679
5014
|
flowCount = Object.keys(flowIndex.flows).length;
|
|
4680
5015
|
}
|
|
@@ -4706,6 +5041,15 @@ async function rebuildStaticFiles(rootDir, ctx) {
|
|
|
4706
5041
|
}
|
|
4707
5042
|
} catch {
|
|
4708
5043
|
}
|
|
5044
|
+
let protocolHealth;
|
|
5045
|
+
try {
|
|
5046
|
+
const protocolIndex = await rebuildProtocolIndex(rootDir);
|
|
5047
|
+
if (protocolIndex.health.total > 0) {
|
|
5048
|
+
protocolHealth = protocolIndex.health;
|
|
5049
|
+
filesWritten.push(".paradigm/protocols/index.yaml");
|
|
5050
|
+
}
|
|
5051
|
+
} catch {
|
|
5052
|
+
}
|
|
4709
5053
|
const breakdown = {};
|
|
4710
5054
|
for (const sym of aggregation.symbols) {
|
|
4711
5055
|
breakdown[sym.type] = (breakdown[sym.type] || 0) + 1;
|
|
@@ -4717,7 +5061,8 @@ async function rebuildStaticFiles(rootDir, ctx) {
|
|
|
4717
5061
|
breakdown,
|
|
4718
5062
|
flowCount,
|
|
4719
5063
|
aspectGraphStats,
|
|
4720
|
-
personaCount
|
|
5064
|
+
personaCount,
|
|
5065
|
+
protocolHealth
|
|
4721
5066
|
};
|
|
4722
5067
|
}
|
|
4723
5068
|
function buildNavigatorData(rootDir, aggregation) {
|
|
@@ -4733,7 +5078,7 @@ function buildNavigatorData(rootDir, aggregation) {
|
|
|
4733
5078
|
function buildStructure(rootDir) {
|
|
4734
5079
|
const structure = {};
|
|
4735
5080
|
for (const [category, patterns] of Object.entries(DIRECTORY_PATTERNS)) {
|
|
4736
|
-
const existingPaths = patterns.filter((p) =>
|
|
5081
|
+
const existingPaths = patterns.filter((p) => fs9.existsSync(path10.join(rootDir, p)));
|
|
4737
5082
|
if (existingPaths.length > 0) {
|
|
4738
5083
|
const symbolInfo = Object.values(SYMBOL_CATEGORIES).find((s) => s.category === category);
|
|
4739
5084
|
structure[category] = { paths: existingPaths, symbol: symbolInfo?.prefix || "@" };
|
|
@@ -4744,7 +5089,7 @@ function buildStructure(rootDir) {
|
|
|
4744
5089
|
function buildKeyFiles(rootDir) {
|
|
4745
5090
|
const keyFiles = {};
|
|
4746
5091
|
for (const [category, patterns] of Object.entries(KEY_FILE_PATTERNS)) {
|
|
4747
|
-
const existingPaths = patterns.filter((p) =>
|
|
5092
|
+
const existingPaths = patterns.filter((p) => fs9.existsSync(path10.join(rootDir, p)));
|
|
4748
5093
|
if (existingPaths.length > 0) {
|
|
4749
5094
|
keyFiles[category] = existingPaths;
|
|
4750
5095
|
}
|
|
@@ -4760,10 +5105,10 @@ function buildSkipPatterns(rootDir) {
|
|
|
4760
5105
|
unless_testing: [...DEFAULT_SKIP_PATTERNS.unless_testing],
|
|
4761
5106
|
unless_docs: [...DEFAULT_SKIP_PATTERNS.unless_docs]
|
|
4762
5107
|
};
|
|
4763
|
-
const gitignorePath =
|
|
4764
|
-
if (
|
|
5108
|
+
const gitignorePath = path10.join(rootDir, ".gitignore");
|
|
5109
|
+
if (fs9.existsSync(gitignorePath)) {
|
|
4765
5110
|
try {
|
|
4766
|
-
const content =
|
|
5111
|
+
const content = fs9.readFileSync(gitignorePath, "utf8");
|
|
4767
5112
|
const gitignorePatterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).filter(
|
|
4768
5113
|
(line) => line.endsWith("/") || line.includes("*") || ["node_modules", "dist", "build", ".cache"].some((p) => line.includes(p))
|
|
4769
5114
|
).slice(0, 20);
|
|
@@ -4812,11 +5157,11 @@ function buildSymbolMap(symbols, purposeFiles, _rootDir) {
|
|
|
4812
5157
|
symbolMap[symbolId] = symbol.filePath;
|
|
4813
5158
|
} else {
|
|
4814
5159
|
const matchingPurpose = purposeFiles.find((pf) => {
|
|
4815
|
-
const dir =
|
|
5160
|
+
const dir = path10.dirname(pf);
|
|
4816
5161
|
return dir.toLowerCase().includes(symbol.id.toLowerCase());
|
|
4817
5162
|
});
|
|
4818
5163
|
if (matchingPurpose) {
|
|
4819
|
-
symbolMap[symbolId] =
|
|
5164
|
+
symbolMap[symbolId] = path10.dirname(matchingPurpose) + "/";
|
|
4820
5165
|
}
|
|
4821
5166
|
}
|
|
4822
5167
|
}
|
|
@@ -4827,8 +5172,8 @@ function generateFlowIndex(rootDir, purposeFiles) {
|
|
|
4827
5172
|
const symbolToFlows = {};
|
|
4828
5173
|
for (const filePath of purposeFiles) {
|
|
4829
5174
|
try {
|
|
4830
|
-
const content =
|
|
4831
|
-
const data =
|
|
5175
|
+
const content = fs9.readFileSync(filePath, "utf8");
|
|
5176
|
+
const data = yaml8.load(content);
|
|
4832
5177
|
if (!data?.flows) continue;
|
|
4833
5178
|
if (Array.isArray(data.flows)) {
|
|
4834
5179
|
for (const flowItem of data.flows) {
|
|
@@ -4841,7 +5186,7 @@ function generateFlowIndex(rootDir, purposeFiles) {
|
|
|
4841
5186
|
id: flowId,
|
|
4842
5187
|
description: flow.description || "",
|
|
4843
5188
|
steps,
|
|
4844
|
-
definedIn:
|
|
5189
|
+
definedIn: path10.relative(rootDir, filePath)
|
|
4845
5190
|
};
|
|
4846
5191
|
indexFlowSymbols(flowId, steps, symbolToFlows);
|
|
4847
5192
|
}
|
|
@@ -4857,7 +5202,7 @@ function generateFlowIndex(rootDir, purposeFiles) {
|
|
|
4857
5202
|
trigger: flowDef.trigger,
|
|
4858
5203
|
steps,
|
|
4859
5204
|
validation: flowDef.validation,
|
|
4860
|
-
definedIn:
|
|
5205
|
+
definedIn: path10.relative(rootDir, filePath)
|
|
4861
5206
|
};
|
|
4862
5207
|
indexFlowSymbols(flowId, steps, symbolToFlows);
|
|
4863
5208
|
}
|
|
@@ -4965,6 +5310,14 @@ export {
|
|
|
4965
5310
|
getPersonaCoverage,
|
|
4966
5311
|
getAffectedPersonas,
|
|
4967
5312
|
validateAgainstSentinel,
|
|
5313
|
+
loadProtocols,
|
|
5314
|
+
loadProtocol,
|
|
5315
|
+
loadProtocolIndex,
|
|
5316
|
+
searchProtocols,
|
|
5317
|
+
recordProtocol,
|
|
5318
|
+
updateProtocol,
|
|
5319
|
+
validateProtocol,
|
|
5320
|
+
detectProtocolSuggestion,
|
|
4968
5321
|
getReindexToolsList,
|
|
4969
5322
|
handleReindexTool,
|
|
4970
5323
|
rebuildStaticFiles
|
package/dist/mcp.js
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
createPersona,
|
|
37
37
|
deleteLoreEntry,
|
|
38
38
|
deletePersona,
|
|
39
|
+
detectProtocolSuggestion,
|
|
39
40
|
findPurposeFiles,
|
|
40
41
|
getAffectedPersonas,
|
|
41
42
|
getAllEdgesFor,
|
|
@@ -66,6 +67,9 @@ import {
|
|
|
66
67
|
loadLoreTimeline,
|
|
67
68
|
loadPersona,
|
|
68
69
|
loadPersonas,
|
|
70
|
+
loadProtocol,
|
|
71
|
+
loadProtocolIndex,
|
|
72
|
+
loadProtocols,
|
|
69
73
|
openAspectGraph,
|
|
70
74
|
parseGateConfig,
|
|
71
75
|
parsePurposeFileDetailed,
|
|
@@ -73,7 +77,9 @@ import {
|
|
|
73
77
|
recordGlobalAntipattern,
|
|
74
78
|
recordGlobalDecision,
|
|
75
79
|
recordLoreEntry,
|
|
80
|
+
recordProtocol,
|
|
76
81
|
removeStep,
|
|
82
|
+
searchProtocols,
|
|
77
83
|
searchSymbols,
|
|
78
84
|
serializePurposeFile,
|
|
79
85
|
toolCache,
|
|
@@ -81,10 +87,12 @@ import {
|
|
|
81
87
|
trackToolCall,
|
|
82
88
|
updateLoreEntry,
|
|
83
89
|
updatePersona,
|
|
90
|
+
updateProtocol,
|
|
84
91
|
validateAgainstSentinel,
|
|
85
92
|
validatePersona,
|
|
93
|
+
validateProtocol,
|
|
86
94
|
validatePurposeFile
|
|
87
|
-
} from "./chunk-
|
|
95
|
+
} from "./chunk-D4VBBKGV.js";
|
|
88
96
|
import {
|
|
89
97
|
getPluginUpdateNotice,
|
|
90
98
|
schedulePluginUpdateCheck
|
|
@@ -8574,6 +8582,17 @@ async function handleLoreTool(name, args, ctx) {
|
|
|
8574
8582
|
};
|
|
8575
8583
|
const id = await recordLoreEntry(ctx.rootDir, entry);
|
|
8576
8584
|
getSessionTracker().setLastLoreEntryId(id);
|
|
8585
|
+
let protocol_suggestion = null;
|
|
8586
|
+
try {
|
|
8587
|
+
if (files_created && files_created.length >= 2) {
|
|
8588
|
+
protocol_suggestion = detectProtocolSuggestion(
|
|
8589
|
+
ctx.rootDir,
|
|
8590
|
+
files_created,
|
|
8591
|
+
files_modified || []
|
|
8592
|
+
);
|
|
8593
|
+
}
|
|
8594
|
+
} catch {
|
|
8595
|
+
}
|
|
8577
8596
|
return {
|
|
8578
8597
|
handled: true,
|
|
8579
8598
|
text: JSON.stringify({
|
|
@@ -8581,7 +8600,8 @@ async function handleLoreTool(name, args, ctx) {
|
|
|
8581
8600
|
id,
|
|
8582
8601
|
type,
|
|
8583
8602
|
title,
|
|
8584
|
-
message: "Lore entry recorded successfully"
|
|
8603
|
+
message: "Lore entry recorded successfully",
|
|
8604
|
+
...protocol_suggestion ? { protocol_suggestion } : {}
|
|
8585
8605
|
})
|
|
8586
8606
|
};
|
|
8587
8607
|
}
|
|
@@ -11553,6 +11573,364 @@ async function handlePersonaTool(name, args, ctx) {
|
|
|
11553
11573
|
}
|
|
11554
11574
|
}
|
|
11555
11575
|
|
|
11576
|
+
// ../paradigm-mcp/src/tools/protocols.ts
|
|
11577
|
+
function getProtocolsToolsList() {
|
|
11578
|
+
return [
|
|
11579
|
+
{
|
|
11580
|
+
name: "paradigm_protocol_search",
|
|
11581
|
+
description: "Search for protocols matching a task description. Call BEFORE exploring the codebase \u2014 if a matching protocol exists, follow its steps instead of discovering the pattern from scratch. Returns top matches with steps, exemplar, and freshness info. ~200 tokens.",
|
|
11582
|
+
inputSchema: {
|
|
11583
|
+
type: "object",
|
|
11584
|
+
properties: {
|
|
11585
|
+
task: {
|
|
11586
|
+
type: "string",
|
|
11587
|
+
description: 'Task description to search for (e.g., "add a new page", "add API route")'
|
|
11588
|
+
},
|
|
11589
|
+
limit: {
|
|
11590
|
+
type: "number",
|
|
11591
|
+
description: "Maximum results (default: 3)"
|
|
11592
|
+
}
|
|
11593
|
+
},
|
|
11594
|
+
required: ["task"]
|
|
11595
|
+
},
|
|
11596
|
+
annotations: {
|
|
11597
|
+
readOnlyHint: true,
|
|
11598
|
+
destructiveHint: false
|
|
11599
|
+
}
|
|
11600
|
+
},
|
|
11601
|
+
{
|
|
11602
|
+
name: "paradigm_protocol_get",
|
|
11603
|
+
description: "Get a specific protocol by ID with full details and freshness check. ~300 tokens.",
|
|
11604
|
+
inputSchema: {
|
|
11605
|
+
type: "object",
|
|
11606
|
+
properties: {
|
|
11607
|
+
id: {
|
|
11608
|
+
type: "string",
|
|
11609
|
+
description: 'Protocol ID (e.g., "P-add-view")'
|
|
11610
|
+
}
|
|
11611
|
+
},
|
|
11612
|
+
required: ["id"]
|
|
11613
|
+
},
|
|
11614
|
+
annotations: {
|
|
11615
|
+
readOnlyHint: true,
|
|
11616
|
+
destructiveHint: false
|
|
11617
|
+
}
|
|
11618
|
+
},
|
|
11619
|
+
{
|
|
11620
|
+
name: "paradigm_protocol_record",
|
|
11621
|
+
description: "Record a new protocol after completing repeatable work. Captures the steps you followed so future agents can skip exploration. ~100 tokens.",
|
|
11622
|
+
inputSchema: {
|
|
11623
|
+
type: "object",
|
|
11624
|
+
properties: {
|
|
11625
|
+
name: {
|
|
11626
|
+
type: "string",
|
|
11627
|
+
description: 'Protocol name (e.g., "Add a new view")'
|
|
11628
|
+
},
|
|
11629
|
+
description: {
|
|
11630
|
+
type: "string",
|
|
11631
|
+
description: "What this protocol accomplishes"
|
|
11632
|
+
},
|
|
11633
|
+
trigger: {
|
|
11634
|
+
type: "array",
|
|
11635
|
+
items: { type: "string" },
|
|
11636
|
+
description: 'Phrases that should match this protocol (e.g., ["add view", "new page"])'
|
|
11637
|
+
},
|
|
11638
|
+
tags: {
|
|
11639
|
+
type: "array",
|
|
11640
|
+
items: { type: "string" },
|
|
11641
|
+
description: 'Classification tags (e.g., ["ui", "frontend"])'
|
|
11642
|
+
},
|
|
11643
|
+
symbols: {
|
|
11644
|
+
type: "array",
|
|
11645
|
+
items: { type: "string" },
|
|
11646
|
+
description: 'Paradigm symbols involved (e.g., ["#logs-view"])'
|
|
11647
|
+
},
|
|
11648
|
+
exemplar: {
|
|
11649
|
+
type: "string",
|
|
11650
|
+
description: 'Canonical file to study for this pattern (e.g., "ui/src/views/LogsView.tsx")'
|
|
11651
|
+
},
|
|
11652
|
+
steps: {
|
|
11653
|
+
type: "array",
|
|
11654
|
+
items: {
|
|
11655
|
+
type: "object",
|
|
11656
|
+
properties: {
|
|
11657
|
+
action: {
|
|
11658
|
+
type: "string",
|
|
11659
|
+
enum: ["create", "modify", "run", "verify"],
|
|
11660
|
+
description: "Step action type"
|
|
11661
|
+
},
|
|
11662
|
+
target: {
|
|
11663
|
+
type: "string",
|
|
11664
|
+
description: "File path (supports {Name}/{name} placeholders)"
|
|
11665
|
+
},
|
|
11666
|
+
template_from: {
|
|
11667
|
+
type: "string",
|
|
11668
|
+
description: "File to use as template (for create actions)"
|
|
11669
|
+
},
|
|
11670
|
+
reference: {
|
|
11671
|
+
type: "string",
|
|
11672
|
+
description: "Where in the file to make changes (for modify actions)"
|
|
11673
|
+
},
|
|
11674
|
+
command: {
|
|
11675
|
+
type: "string",
|
|
11676
|
+
description: "Command to execute (for run actions)"
|
|
11677
|
+
},
|
|
11678
|
+
notes: {
|
|
11679
|
+
type: "string",
|
|
11680
|
+
description: "Additional guidance for this step"
|
|
11681
|
+
}
|
|
11682
|
+
},
|
|
11683
|
+
required: ["action"]
|
|
11684
|
+
},
|
|
11685
|
+
description: "Ordered steps to follow"
|
|
11686
|
+
},
|
|
11687
|
+
recorded_from: {
|
|
11688
|
+
type: "string",
|
|
11689
|
+
description: 'Lore entry ID this protocol was learned from (e.g., "L-2026-03-01-001")'
|
|
11690
|
+
}
|
|
11691
|
+
},
|
|
11692
|
+
required: ["name", "description", "trigger", "tags", "steps"]
|
|
11693
|
+
},
|
|
11694
|
+
annotations: {
|
|
11695
|
+
readOnlyHint: false,
|
|
11696
|
+
destructiveHint: false
|
|
11697
|
+
}
|
|
11698
|
+
},
|
|
11699
|
+
{
|
|
11700
|
+
name: "paradigm_protocol_update",
|
|
11701
|
+
description: "Update an existing protocol. Use refresh:true after successfully following a protocol to bump last_verified. ~100 tokens.",
|
|
11702
|
+
inputSchema: {
|
|
11703
|
+
type: "object",
|
|
11704
|
+
properties: {
|
|
11705
|
+
id: {
|
|
11706
|
+
type: "string",
|
|
11707
|
+
description: 'Protocol ID to update (e.g., "P-add-view")'
|
|
11708
|
+
},
|
|
11709
|
+
refresh: {
|
|
11710
|
+
type: "boolean",
|
|
11711
|
+
description: "Set true to bump last_verified to now (use after successfully following the protocol)"
|
|
11712
|
+
},
|
|
11713
|
+
name: { type: "string", description: "Updated name" },
|
|
11714
|
+
description: { type: "string", description: "Updated description" },
|
|
11715
|
+
trigger: {
|
|
11716
|
+
type: "array",
|
|
11717
|
+
items: { type: "string" },
|
|
11718
|
+
description: "Updated trigger phrases"
|
|
11719
|
+
},
|
|
11720
|
+
tags: {
|
|
11721
|
+
type: "array",
|
|
11722
|
+
items: { type: "string" },
|
|
11723
|
+
description: "Updated tags"
|
|
11724
|
+
},
|
|
11725
|
+
symbols: {
|
|
11726
|
+
type: "array",
|
|
11727
|
+
items: { type: "string" },
|
|
11728
|
+
description: "Updated symbols"
|
|
11729
|
+
},
|
|
11730
|
+
exemplar: { type: "string", description: "Updated exemplar path" },
|
|
11731
|
+
steps: {
|
|
11732
|
+
type: "array",
|
|
11733
|
+
items: {
|
|
11734
|
+
type: "object",
|
|
11735
|
+
properties: {
|
|
11736
|
+
action: { type: "string", enum: ["create", "modify", "run", "verify"] },
|
|
11737
|
+
target: { type: "string" },
|
|
11738
|
+
template_from: { type: "string" },
|
|
11739
|
+
reference: { type: "string" },
|
|
11740
|
+
command: { type: "string" },
|
|
11741
|
+
notes: { type: "string" }
|
|
11742
|
+
},
|
|
11743
|
+
required: ["action"]
|
|
11744
|
+
},
|
|
11745
|
+
description: "Updated steps"
|
|
11746
|
+
}
|
|
11747
|
+
},
|
|
11748
|
+
required: ["id"]
|
|
11749
|
+
},
|
|
11750
|
+
annotations: {
|
|
11751
|
+
readOnlyHint: false,
|
|
11752
|
+
destructiveHint: false
|
|
11753
|
+
}
|
|
11754
|
+
},
|
|
11755
|
+
{
|
|
11756
|
+
name: "paradigm_protocol_validate",
|
|
11757
|
+
description: "Validate protocol references \u2014 check that referenced files exist, exemplars haven't drifted. Validates all protocols if no ID given. ~200 tokens.",
|
|
11758
|
+
inputSchema: {
|
|
11759
|
+
type: "object",
|
|
11760
|
+
properties: {
|
|
11761
|
+
id: {
|
|
11762
|
+
type: "string",
|
|
11763
|
+
description: "Protocol ID to validate (omit to validate all)"
|
|
11764
|
+
}
|
|
11765
|
+
}
|
|
11766
|
+
},
|
|
11767
|
+
annotations: {
|
|
11768
|
+
readOnlyHint: true,
|
|
11769
|
+
destructiveHint: false
|
|
11770
|
+
}
|
|
11771
|
+
}
|
|
11772
|
+
];
|
|
11773
|
+
}
|
|
11774
|
+
async function handleProtocolsTool(name, args, ctx) {
|
|
11775
|
+
switch (name) {
|
|
11776
|
+
case "paradigm_protocol_search": {
|
|
11777
|
+
const task = args.task;
|
|
11778
|
+
const limit = args.limit || 3;
|
|
11779
|
+
const results = await searchProtocols(ctx.rootDir, task, limit);
|
|
11780
|
+
if (results.length === 0) {
|
|
11781
|
+
return {
|
|
11782
|
+
handled: true,
|
|
11783
|
+
text: JSON.stringify({
|
|
11784
|
+
count: 0,
|
|
11785
|
+
task,
|
|
11786
|
+
message: "No matching protocol found. Consider recording one after completing this task."
|
|
11787
|
+
})
|
|
11788
|
+
};
|
|
11789
|
+
}
|
|
11790
|
+
return {
|
|
11791
|
+
handled: true,
|
|
11792
|
+
text: JSON.stringify({
|
|
11793
|
+
count: results.length,
|
|
11794
|
+
task,
|
|
11795
|
+
matches: results.map((r) => ({
|
|
11796
|
+
id: r.protocol.id,
|
|
11797
|
+
name: r.protocol.name,
|
|
11798
|
+
description: r.protocol.description,
|
|
11799
|
+
score: r.score,
|
|
11800
|
+
status: r.protocol.status,
|
|
11801
|
+
exemplar: r.protocol.exemplar,
|
|
11802
|
+
last_verified: r.protocol.last_verified,
|
|
11803
|
+
steps: r.protocol.steps.map(summarizeStep)
|
|
11804
|
+
}))
|
|
11805
|
+
}, null, 2)
|
|
11806
|
+
};
|
|
11807
|
+
}
|
|
11808
|
+
case "paradigm_protocol_get": {
|
|
11809
|
+
const id = args.id;
|
|
11810
|
+
const protocol = await loadProtocol(ctx.rootDir, id);
|
|
11811
|
+
if (!protocol) {
|
|
11812
|
+
return {
|
|
11813
|
+
handled: true,
|
|
11814
|
+
text: JSON.stringify({ error: `Protocol not found: ${id}` })
|
|
11815
|
+
};
|
|
11816
|
+
}
|
|
11817
|
+
const validation = validateProtocol(ctx.rootDir, protocol);
|
|
11818
|
+
return {
|
|
11819
|
+
handled: true,
|
|
11820
|
+
text: JSON.stringify({
|
|
11821
|
+
...protocol,
|
|
11822
|
+
freshness: {
|
|
11823
|
+
status: validation.status,
|
|
11824
|
+
issues: validation.issues
|
|
11825
|
+
}
|
|
11826
|
+
}, null, 2)
|
|
11827
|
+
};
|
|
11828
|
+
}
|
|
11829
|
+
case "paradigm_protocol_record": {
|
|
11830
|
+
const steps = args.steps || [];
|
|
11831
|
+
const id = await recordProtocol(ctx.rootDir, {
|
|
11832
|
+
name: args.name,
|
|
11833
|
+
description: args.description,
|
|
11834
|
+
trigger: args.trigger || [],
|
|
11835
|
+
tags: args.tags || [],
|
|
11836
|
+
symbols: args.symbols || [],
|
|
11837
|
+
exemplar: args.exemplar,
|
|
11838
|
+
steps,
|
|
11839
|
+
recorded_from: args.recorded_from,
|
|
11840
|
+
verified_by: "claude-opus-4-6"
|
|
11841
|
+
});
|
|
11842
|
+
return {
|
|
11843
|
+
handled: true,
|
|
11844
|
+
text: JSON.stringify({
|
|
11845
|
+
success: true,
|
|
11846
|
+
id,
|
|
11847
|
+
name: args.name,
|
|
11848
|
+
message: "Protocol recorded successfully"
|
|
11849
|
+
})
|
|
11850
|
+
};
|
|
11851
|
+
}
|
|
11852
|
+
case "paradigm_protocol_update": {
|
|
11853
|
+
const id = args.id;
|
|
11854
|
+
const refresh = args.refresh;
|
|
11855
|
+
const partial = {};
|
|
11856
|
+
if (args.name !== void 0) partial.name = args.name;
|
|
11857
|
+
if (args.description !== void 0) partial.description = args.description;
|
|
11858
|
+
if (args.trigger !== void 0) partial.trigger = args.trigger;
|
|
11859
|
+
if (args.tags !== void 0) partial.tags = args.tags;
|
|
11860
|
+
if (args.symbols !== void 0) partial.symbols = args.symbols;
|
|
11861
|
+
if (args.exemplar !== void 0) partial.exemplar = args.exemplar;
|
|
11862
|
+
if (args.steps !== void 0) partial.steps = args.steps;
|
|
11863
|
+
const success = await updateProtocol(ctx.rootDir, id, partial, refresh === true);
|
|
11864
|
+
return {
|
|
11865
|
+
handled: true,
|
|
11866
|
+
text: JSON.stringify({
|
|
11867
|
+
success,
|
|
11868
|
+
id,
|
|
11869
|
+
refreshed: refresh === true,
|
|
11870
|
+
message: success ? refresh ? "Protocol updated and verified" : "Protocol updated" : `Protocol not found: ${id}`
|
|
11871
|
+
})
|
|
11872
|
+
};
|
|
11873
|
+
}
|
|
11874
|
+
case "paradigm_protocol_validate": {
|
|
11875
|
+
const id = args.id;
|
|
11876
|
+
if (id) {
|
|
11877
|
+
const protocol = await loadProtocol(ctx.rootDir, id);
|
|
11878
|
+
if (!protocol) {
|
|
11879
|
+
return {
|
|
11880
|
+
handled: true,
|
|
11881
|
+
text: JSON.stringify({ error: `Protocol not found: ${id}` })
|
|
11882
|
+
};
|
|
11883
|
+
}
|
|
11884
|
+
const result = validateProtocol(ctx.rootDir, protocol);
|
|
11885
|
+
return {
|
|
11886
|
+
handled: true,
|
|
11887
|
+
text: JSON.stringify({
|
|
11888
|
+
id: protocol.id,
|
|
11889
|
+
name: protocol.name,
|
|
11890
|
+
status: result.status,
|
|
11891
|
+
issues: result.issues,
|
|
11892
|
+
last_verified: protocol.last_verified
|
|
11893
|
+
}, null, 2)
|
|
11894
|
+
};
|
|
11895
|
+
}
|
|
11896
|
+
const protocols = await loadProtocols(ctx.rootDir);
|
|
11897
|
+
const results = protocols.map((p) => {
|
|
11898
|
+
const v = validateProtocol(ctx.rootDir, p);
|
|
11899
|
+
return {
|
|
11900
|
+
id: p.id,
|
|
11901
|
+
name: p.name,
|
|
11902
|
+
status: v.status,
|
|
11903
|
+
issues: v.issues,
|
|
11904
|
+
last_verified: p.last_verified
|
|
11905
|
+
};
|
|
11906
|
+
});
|
|
11907
|
+
const health = {
|
|
11908
|
+
total: results.length,
|
|
11909
|
+
current: results.filter((r) => r.status === "current").length,
|
|
11910
|
+
stale: results.filter((r) => r.status === "stale").length,
|
|
11911
|
+
broken: results.filter((r) => r.status === "broken").length
|
|
11912
|
+
};
|
|
11913
|
+
return {
|
|
11914
|
+
handled: true,
|
|
11915
|
+
text: JSON.stringify({ protocols: results, health }, null, 2)
|
|
11916
|
+
};
|
|
11917
|
+
}
|
|
11918
|
+
default:
|
|
11919
|
+
return { handled: false, text: "" };
|
|
11920
|
+
}
|
|
11921
|
+
}
|
|
11922
|
+
function summarizeStep(step) {
|
|
11923
|
+
const result = {
|
|
11924
|
+
action: step.action
|
|
11925
|
+
};
|
|
11926
|
+
if (step.target) result.target = step.target;
|
|
11927
|
+
if (step.template_from) result.template_from = step.template_from;
|
|
11928
|
+
if (step.reference) result.reference = step.reference;
|
|
11929
|
+
if (step.command) result.command = step.command;
|
|
11930
|
+
if (step.notes) result.notes = step.notes;
|
|
11931
|
+
return result;
|
|
11932
|
+
}
|
|
11933
|
+
|
|
11556
11934
|
// ../paradigm-mcp/src/tools/fallback-grep.ts
|
|
11557
11935
|
import * as path23 from "path";
|
|
11558
11936
|
import { execSync as execSync3 } from "child_process";
|
|
@@ -11844,6 +12222,8 @@ function registerTools(server, getContext2, reloadContext2) {
|
|
|
11844
12222
|
// Assessment loop tools
|
|
11845
12223
|
...getAssessmentToolsList(),
|
|
11846
12224
|
...getPersonaToolsList(),
|
|
12225
|
+
// Protocol tools
|
|
12226
|
+
...getProtocolsToolsList(),
|
|
11847
12227
|
// Plugin update check
|
|
11848
12228
|
{
|
|
11849
12229
|
name: "paradigm_plugin_check",
|
|
@@ -12226,7 +12606,7 @@ function registerTools(server, getContext2, reloadContext2) {
|
|
|
12226
12606
|
};
|
|
12227
12607
|
}
|
|
12228
12608
|
case "paradigm_status": {
|
|
12229
|
-
const text = await toolCache.getOrCompute("status", () => {
|
|
12609
|
+
const text = await toolCache.getOrCompute("status", async () => {
|
|
12230
12610
|
const counts = getSymbolCounts(ctx.index);
|
|
12231
12611
|
const total = Object.values(counts).reduce((a, b) => a + b, 0);
|
|
12232
12612
|
const examples = {};
|
|
@@ -12237,6 +12617,14 @@ function registerTools(server, getContext2, reloadContext2) {
|
|
|
12237
12617
|
const platform2 = os.platform();
|
|
12238
12618
|
const isWindows = platform2 === "win32";
|
|
12239
12619
|
const shell = isWindows ? "PowerShell/CMD" : platform2 === "darwin" ? "zsh/bash" : "bash";
|
|
12620
|
+
let protocols;
|
|
12621
|
+
try {
|
|
12622
|
+
const protocolIndex = await loadProtocolIndex(ctx.rootDir);
|
|
12623
|
+
if (protocolIndex && protocolIndex.health.total > 0) {
|
|
12624
|
+
protocols = protocolIndex.health;
|
|
12625
|
+
}
|
|
12626
|
+
} catch {
|
|
12627
|
+
}
|
|
12240
12628
|
return JSON.stringify({
|
|
12241
12629
|
project: ctx.projectName,
|
|
12242
12630
|
symbolSystem: "v2",
|
|
@@ -12251,6 +12639,7 @@ function registerTools(server, getContext2, reloadContext2) {
|
|
|
12251
12639
|
examples,
|
|
12252
12640
|
hasPortalYaml: ctx.gateConfig !== null,
|
|
12253
12641
|
purposeFiles: ctx.aggregation.purposeFiles.length,
|
|
12642
|
+
...protocols ? { protocols } : {},
|
|
12254
12643
|
note: "Symbol System v2: Use tags [feature], [state], [integration], [idea] for classification",
|
|
12255
12644
|
environment: {
|
|
12256
12645
|
os: platform2,
|
|
@@ -12465,7 +12854,7 @@ Update command:
|
|
|
12465
12854
|
trackToolCall(noWsText.length, name);
|
|
12466
12855
|
return { content: [{ type: "text", text: noWsText }] };
|
|
12467
12856
|
}
|
|
12468
|
-
const { rebuildStaticFiles: rebuildStaticFiles2 } = await import("./reindex-
|
|
12857
|
+
const { rebuildStaticFiles: rebuildStaticFiles2 } = await import("./reindex-T4N3NG73.js");
|
|
12469
12858
|
const memberResults = [];
|
|
12470
12859
|
for (const member of ctx.workspace.config.members) {
|
|
12471
12860
|
const memberAbsPath = path24.resolve(path24.dirname(ctx.workspace.workspacePath), member.path);
|
|
@@ -12648,6 +13037,15 @@ Update command:
|
|
|
12648
13037
|
};
|
|
12649
13038
|
}
|
|
12650
13039
|
}
|
|
13040
|
+
if (name.startsWith("paradigm_protocol_")) {
|
|
13041
|
+
const result = await handleProtocolsTool(name, args, ctx);
|
|
13042
|
+
if (result.handled) {
|
|
13043
|
+
trackToolCall(result.text.length, name);
|
|
13044
|
+
return {
|
|
13045
|
+
content: [{ type: "text", text: result.text }]
|
|
13046
|
+
};
|
|
13047
|
+
}
|
|
13048
|
+
}
|
|
12651
13049
|
if (name === "paradigm_reindex") {
|
|
12652
13050
|
const reload = reloadContext2 || (async () => {
|
|
12653
13051
|
});
|
|
@@ -574,10 +574,77 @@
|
|
|
574
574
|
}
|
|
575
575
|
]
|
|
576
576
|
},
|
|
577
|
+
{
|
|
578
|
+
"id": "protocols",
|
|
579
|
+
"title": "Protocols — Repeatable Patterns",
|
|
580
|
+
"content": "## Protocols — Repeatable Patterns\n\nAI agents routinely spend tens of thousands of tokens exploring codebases to reverse-engineer implementation patterns before they can start working. In a well-structured project, the answer to \"how do I add a new view?\" is the same every time: create a component file, create a store, register a route, add CSS. But this knowledge lives nowhere explicit, so every agent re-discovers it from scratch.\n\nProtocols solve this problem. They are procedural, step-by-step instructions with exact file references, learned from actual completed work. When an agent receives a task like \"add a Settings page,\" it first calls `paradigm_protocol_search` with the task description. If a matching protocol exists, the agent gets back ordered steps, an exemplar file to study, and freshness information -- all within a few hundred tokens instead of exploring for thousands.\n\n### Storage and Format\n\nProtocols are stored as `.protocol` files (YAML syntax, semantic extension) in `.paradigm/protocols/`. Each protocol has an ID, a name, a description, trigger phrases for fuzzy matching, tags for classification, an exemplar file (the canonical example to follow), and an ordered list of steps.\n\n```\n.paradigm/protocols/\n index.yaml # Auto-generated by reindex\n add-view.protocol # One file per protocol\n add-api-route.protocol\n```\n\n### Step Types\n\nProtocol steps use four action types:\n\n- **create** -- Create a new file. Specifies `target` (the file to create, with placeholders) and `template_from` (an existing file to follow as a model).\n- **modify** -- Edit an existing file. Specifies `target`, `reference` (where in the file to make changes), and `notes` explaining what to add.\n- **run** -- Execute a command. Specifies `command` and `notes`.\n- **verify** -- Confirm something works. Specifies `notes` with what to check.\n\nSteps support placeholders: `{Name}` for PascalCase and `{name}` for kebab-case. When following the protocol, the agent substitutes the actual name.\n\n### Searching for Protocols\n\nThe primary entry point is `paradigm_protocol_search`. Pass a natural language task description and it returns matching protocols ranked by relevance.\n\n```\nparadigm_protocol_search({ task: \"add a new settings page\" })\n```\n\nThe search uses weighted fuzzy matching: trigger phrases (weight 3), tags (weight 2), name and description (weight 1), and step notes (weight 0.5). The protocol set per project is small (5-30 entries), so a full scan is efficient.\n\nTo get full details for a specific protocol, use `paradigm_protocol_get`:\n\n```\nparadigm_protocol_get({ id: \"P-add-view\" })\n```\n\n### Recording Protocols\n\nProtocols are captured *after* completing work, not before. Two paths lead to a new protocol:\n\n1. **Agent-initiated** -- After implementing a repeatable task, the agent calls `paradigm_protocol_record` with the steps it followed:\n\n```\nparadigm_protocol_record({\n name: \"Add a new view\",\n description: \"Create a new page with store, route, and CSS\",\n trigger: [\"add view\", \"new page\", \"create view\"],\n tags: [\"ui\", \"frontend\"],\n exemplar: \"ui/src/views/LogsView.tsx\",\n steps: [\n { action: \"create\", target: \"ui/src/views/{Name}View.tsx\", template_from: \"ui/src/views/LogsView.tsx\" },\n { action: \"modify\", target: \"ui/src/App.tsx\", reference: \"Route elements\", notes: \"Add Route\" },\n { action: \"verify\", notes: \"Run build, check route loads\" }\n ],\n recorded_from: \"L-2026-03-01-001\"\n})\n```\n\n2. **Lore-prompted** -- When `paradigm_lore_record` is called, the response includes a `protocol_suggestion` if the session created new files following existing patterns. This nudges agents to capture repeatable work without manual intervention.\n\n### Freshness and Maintenance\n\nProtocols go stale as code evolves. Three mechanisms keep them fresh:\n\n- **Reindex validation** -- During `paradigm_reindex`, all protocol references are checked. Missing files mark the protocol as `broken`. An exemplar modified since `last_verified` marks it `stale`. All references valid means `current`.\n- **On-use refresh** -- After successfully following a protocol, the agent calls `paradigm_protocol_update` with `refresh: true` to bump `last_verified` and fix outdated references.\n- **Status visibility** -- `paradigm_status` includes protocol health (X current, Y stale, Z broken). Stale protocols surface naturally during normal workflow.\n\nYou can also explicitly validate protocols:\n\n```\nparadigm_protocol_validate({ id: \"P-add-view\" })\n```\n\n### The Workflow\n\nThe protocol workflow integrates into the Paradigm operational loop:\n\n1. **Before implementing**: Call `paradigm_protocol_search` with your task description. If a match exists, follow the steps -- skip codebase exploration.\n2. **During implementation**: Use the protocol's exemplar as a reference. Follow each step in order.\n3. **After completion**: If no protocol existed and the work was repeatable, record one. If a protocol existed and you followed it successfully, call `paradigm_protocol_update` to refresh its timestamp.\n\nThis creates a virtuous cycle: each agent session that follows a protocol validates it, and each session that does repeatable work without a protocol can create one for future agents.",
|
|
581
|
+
"keyConcepts": [
|
|
582
|
+
"Protocols as repeatable implementation patterns with exact file references",
|
|
583
|
+
"paradigm_protocol_search — find protocols by task description",
|
|
584
|
+
"paradigm_protocol_record — capture patterns after completing work",
|
|
585
|
+
"Step types: create, modify, run, verify",
|
|
586
|
+
"Freshness tracking: current, stale, broken",
|
|
587
|
+
"Lore integration: protocol_suggestion on repeatable work"
|
|
588
|
+
],
|
|
589
|
+
"quiz": [
|
|
590
|
+
{
|
|
591
|
+
"id": "q1",
|
|
592
|
+
"question": "What is the PRIMARY purpose of Paradigm protocols?",
|
|
593
|
+
"choices": {
|
|
594
|
+
"A": "To enforce coding standards through automated linting",
|
|
595
|
+
"B": "To provide step-by-step implementation patterns that save agents from re-exploring codebases",
|
|
596
|
+
"C": "To track all changes made to the codebase in a history log",
|
|
597
|
+
"D": "To generate code automatically from templates",
|
|
598
|
+
"E": "To replace .purpose files with more detailed documentation"
|
|
599
|
+
},
|
|
600
|
+
"correct": "B",
|
|
601
|
+
"explanation": "Protocols are repeatable implementation patterns with exact file references. Their primary value is saving AI agents from spending thousands of tokens exploring a codebase to reverse-engineer patterns that are the same every time. They do not enforce linting (A), track history (C), generate code (D), or replace .purpose files (E)."
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
"id": "q2",
|
|
605
|
+
"question": "When should an agent call paradigm_protocol_search?",
|
|
606
|
+
"choices": {
|
|
607
|
+
"A": "After completing a task to verify the work matches a known pattern",
|
|
608
|
+
"B": "Before implementing a task, to check if a repeatable pattern already exists",
|
|
609
|
+
"C": "Only when the user explicitly asks for protocol guidance",
|
|
610
|
+
"D": "During reindex to validate protocol references",
|
|
611
|
+
"E": "When recording lore to check for protocol suggestions"
|
|
612
|
+
},
|
|
613
|
+
"correct": "B",
|
|
614
|
+
"explanation": "paradigm_protocol_search is the agent's first stop before exploring the codebase. When receiving a task like 'add a Settings page,' the agent searches for a matching protocol BEFORE spending tokens on exploration. If a match exists, the steps and exemplar provide everything needed. After completion (A) is when you record or refresh, not search."
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
"id": "q3",
|
|
618
|
+
"question": "When are protocols recorded?",
|
|
619
|
+
"choices": {
|
|
620
|
+
"A": "Before starting work, to plan the implementation steps",
|
|
621
|
+
"B": "During implementation, as each step is completed",
|
|
622
|
+
"C": "After completing work, capturing the steps that were actually followed",
|
|
623
|
+
"D": "During reindex, by analyzing git history",
|
|
624
|
+
"E": "Only by project maintainers, never by AI agents"
|
|
625
|
+
},
|
|
626
|
+
"correct": "C",
|
|
627
|
+
"explanation": "Protocols are captured AFTER completing work, not before. This ensures the steps reflect what actually works, not what was planned. Two paths lead to recording: agent-initiated (calling paradigm_protocol_record with the steps followed) and lore-prompted (the lore_record response includes a protocol_suggestion hint when it detects repeatable patterns)."
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
"id": "q4",
|
|
631
|
+
"question": "A protocol's status is 'stale'. What does this mean?",
|
|
632
|
+
"choices": {
|
|
633
|
+
"A": "The protocol has a syntax error in its YAML",
|
|
634
|
+
"B": "The protocol's exemplar file has been modified since the protocol was last verified",
|
|
635
|
+
"C": "The protocol has never been used by any agent",
|
|
636
|
+
"D": "The protocol references files that no longer exist",
|
|
637
|
+
"E": "The protocol was recorded more than 30 days ago"
|
|
638
|
+
},
|
|
639
|
+
"correct": "B",
|
|
640
|
+
"explanation": "A 'stale' status means the protocol's exemplar or referenced files have been modified since last_verified — the protocol's steps might no longer match the actual code patterns. 'Broken' (D) means referenced files are missing entirely. Stale protocols still work but should be reviewed and refreshed after successful use."
|
|
641
|
+
}
|
|
642
|
+
]
|
|
643
|
+
},
|
|
577
644
|
{
|
|
578
645
|
"id": "operations-review",
|
|
579
646
|
"title": "Operational Excellence",
|
|
580
|
-
"content": "## Operational Excellence\n\nThis lesson brings together everything from PARA 301 into a cohesive daily workflow. Operational excellence in Paradigm is not about memorizing individual tools -- it is about building habits that keep your project healthy, your metadata accurate, and your development sessions productive.\n\n### The Operational Loop\n\nEvery development session follows a predictable pattern:\n\n**1. Orient** -- Start by calling `paradigm_status` to see the project overview: how many symbols are defined, recent changes, any outstanding issues. If this is a continuation session, call `paradigm_session_recover` to load context from the previous session.\n\n**2. Discover** -- Before touching code,
|
|
647
|
+
"content": "## Operational Excellence\n\nThis lesson brings together everything from PARA 301 into a cohesive daily workflow. Operational excellence in Paradigm is not about memorizing individual tools -- it is about building habits that keep your project healthy, your metadata accurate, and your development sessions productive.\n\n### The Operational Loop\n\nEvery development session follows a predictable pattern:\n\n**1. Orient** -- Start by calling `paradigm_status` to see the project overview: how many symbols are defined, recent changes, any outstanding issues. If this is a continuation session, call `paradigm_session_recover` to load context from the previous session.\n\n**2. Discover** -- Before touching code, check for existing protocols: call `paradigm_protocol_search` with your task description. If a match exists, follow its steps and skip exploration. Otherwise, call `paradigm_wisdom_context` with the symbols you plan to modify to learn team conventions and avoid known pitfalls. Use `paradigm_navigate` with the \"context\" intent to find all relevant files for your task.\n\n**3. Assess Risk** -- Run `paradigm_ripple` on every symbol you plan to modify to understand the blast radius. Check `paradigm_history_fragility` on anything flagged as a dependency. If the task is complex (3+ files, security + implementation), call `paradigm_orchestrate_inline` with mode=\"plan\" to get the right agent team.\n\n**4. Implement** -- Write code, updating `.purpose` files as you go. If you add routes, update `portal.yaml`. If you add signals, register them. Do not defer metadata updates to \"later\" -- later never comes.\n\n**5. Validate** -- Run `paradigm doctor` or `paradigm_purpose_validate` to catch any drift. Use `paradigm_flow_validate` if you modified flows. Record the implementation with `paradigm_history_record`.\n\n**6. Capture Knowledge** -- Did you discover an antipattern? Record it with `paradigm_wisdom_record`. Did you make an architectural decision? Record that too. Did the implementation follow a repeatable pattern? Record a protocol with `paradigm_protocol_record`. Did the implementation reveal a fragile area? Note it for the team.\n\n**7. Monitor Context** -- Check `paradigm_context_check` periodically. If approaching limits, prepare a handoff.\n\n### Common Pitfalls\n\n- **Skipping wisdom checks**: Leads to repeating mistakes the team already knows about\n- **Skipping ripple analysis**: Leads to breaking downstream dependencies\n- **Deferring metadata updates**: Leads to stale `.purpose` files and misleading navigation\n- **Ignoring fragility warnings**: Leads to cascading failures in unstable areas\n- **Not recording history**: Loses the trail that fragility analysis depends on\n\n### The Measure of Excellence\n\nA well-operated Paradigm project has: accurate `.purpose` files that match the code, a `portal.yaml` that reflects all protected routes, wisdom entries that prevent repeated mistakes, history records that enable fragility analysis, and context handoffs that allow seamless multi-session work. When all of these are in place, every AI agent session starts with full context and every change is informed by the project's complete institutional knowledge.",
|
|
581
648
|
"keyConcepts": [
|
|
582
649
|
"The operational loop: orient, discover, assess, implement, validate, capture, monitor",
|
|
583
650
|
"Combining tools for comprehensive safety",
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": "3.0",
|
|
3
3
|
"frameworkVersion": "2.0",
|
|
4
4
|
"timeLimit": 5400,
|
|
5
|
-
"totalSlots":
|
|
5
|
+
"totalSlots": 99,
|
|
6
6
|
"passThreshold": 0.8,
|
|
7
7
|
"title": "The PLSAT \u2014 Paradigm Licensure Standardized Assessment Test",
|
|
8
|
-
"description": "
|
|
8
|
+
"description": "99 questions. 90 minutes. 80% to pass. Good luck, scholar.",
|
|
9
9
|
"items": [
|
|
10
10
|
{
|
|
11
11
|
"type": "standalone",
|
|
@@ -2279,6 +2279,111 @@
|
|
|
2279
2279
|
"explanation": "Paradigm's three-layer knowledge model is: Commits (raw facts \u2014 what code changed, captured by git), Lore (session events \u2014 what happened during a work session and why, captured by the post-commit hook into `.paradigm/history/`), and Assessments (synthesized insight \u2014 what the team learned across multiple sessions, recorded into `.paradigm/assessments/`). Each layer adds interpretation: commits are mechanical diffs, lore adds narrative context, and assessments distill patterns and decisions. All three layers persist and remain valuable \u2014 none replaces or supersedes the others."
|
|
2280
2280
|
}
|
|
2281
2281
|
]
|
|
2282
|
+
},
|
|
2283
|
+
{
|
|
2284
|
+
"type": "standalone",
|
|
2285
|
+
"slot": "slot-094",
|
|
2286
|
+
"course": "para-301",
|
|
2287
|
+
"variants": [
|
|
2288
|
+
{
|
|
2289
|
+
"id": "plsat-094",
|
|
2290
|
+
"scenario": "You receive a task: \"Add a new Settings page to the application.\" The project has a `.paradigm/protocols/` directory with several `.protocol` files. You know that previous agents added similar pages (Logs, Events, Dashboard) following the same pattern each time.",
|
|
2291
|
+
"question": "What should you do FIRST before exploring the codebase?",
|
|
2292
|
+
"choices": {
|
|
2293
|
+
"A": "Read every existing page component to understand the pattern",
|
|
2294
|
+
"B": "Call `paradigm_protocol_search` with your task description to check for a matching protocol",
|
|
2295
|
+
"C": "Call `paradigm_lore_record` to document that you are starting the task",
|
|
2296
|
+
"D": "Call `paradigm_protocol_record` to create a new protocol for the Settings page",
|
|
2297
|
+
"E": "Read the `.paradigm/protocols/index.yaml` file directly to scan for relevant entries"
|
|
2298
|
+
},
|
|
2299
|
+
"correct": "B",
|
|
2300
|
+
"explanation": "paradigm_protocol_search is the agent's first stop before exploring the codebase. It takes a natural language task description and returns matching protocols with steps, exemplar files, and freshness info — typically saving thousands of exploration tokens. Reading existing pages (A) is exactly the expensive exploration that protocols prevent. Recording lore (C) is done after work, not before. Recording a protocol (D) is done after completing repeatable work. Reading index.yaml directly (E) bypasses the fuzzy search that matches task descriptions to protocols."
|
|
2301
|
+
},
|
|
2302
|
+
{
|
|
2303
|
+
"id": "plsat-094b",
|
|
2304
|
+
"scenario": "An agent needs to add a new API endpoint to the project. The project has 15 recorded protocols covering common tasks. The agent calls `paradigm_protocol_search({ task: \"add a new API endpoint\" })` and gets back a protocol with 4 steps: create route file, add handler, register route, verify with build.",
|
|
2305
|
+
"question": "What should the agent do next?",
|
|
2306
|
+
"choices": {
|
|
2307
|
+
"A": "Ignore the protocol and explore the codebase to find its own approach",
|
|
2308
|
+
"B": "Call `paradigm_protocol_get` with the protocol ID to get full details, then follow the steps using the exemplar as reference",
|
|
2309
|
+
"C": "Call `paradigm_protocol_record` to save the protocol it just found",
|
|
2310
|
+
"D": "Run `paradigm_reindex` to make sure the protocol is up to date",
|
|
2311
|
+
"E": "Call `paradigm_protocol_validate` to check all protocols before proceeding"
|
|
2312
|
+
},
|
|
2313
|
+
"correct": "B",
|
|
2314
|
+
"explanation": "After finding a matching protocol via search, the next step is paradigm_protocol_get to retrieve full details (including the exemplar file path and detailed step notes), then follow the steps. The exemplar is the canonical file to study for the pattern. Ignoring the protocol (A) wastes the lookup. Recording (C) is for new protocols. Reindex (D) and full validation (E) are maintenance operations, not implementation steps."
|
|
2315
|
+
}
|
|
2316
|
+
]
|
|
2317
|
+
},
|
|
2318
|
+
{
|
|
2319
|
+
"type": "standalone",
|
|
2320
|
+
"slot": "slot-095",
|
|
2321
|
+
"course": "para-301",
|
|
2322
|
+
"variants": [
|
|
2323
|
+
{
|
|
2324
|
+
"id": "plsat-095",
|
|
2325
|
+
"scenario": "An agent just finished adding a new Sentinel event schema — the third such schema added to the project this month. Each time, the agent followed the same steps: create a schema file, register it in the schema index, add a migration, and verify. No protocol exists for this task yet.",
|
|
2326
|
+
"question": "What should the agent do regarding protocols?",
|
|
2327
|
+
"choices": {
|
|
2328
|
+
"A": "Nothing — protocols are only created by project maintainers, not agents",
|
|
2329
|
+
"B": "Call `paradigm_protocol_record` to capture the repeatable pattern it just followed",
|
|
2330
|
+
"C": "Edit an existing protocol to add the schema steps as a sub-procedure",
|
|
2331
|
+
"D": "Wait for `paradigm_reindex` to auto-generate a protocol from git history",
|
|
2332
|
+
"E": "File an issue asking a human to write the protocol later"
|
|
2333
|
+
},
|
|
2334
|
+
"correct": "B",
|
|
2335
|
+
"explanation": "Protocols are captured AFTER completing work, by the agent that did the work. When an agent completes a repeatable task and no protocol existed, it should call paradigm_protocol_record with the steps it followed, trigger phrases, tags, and an exemplar file. This ensures the next agent that receives a similar task can skip exploration entirely. Reindex (D) validates existing protocols but does not auto-generate new ones from git history."
|
|
2336
|
+
},
|
|
2337
|
+
{
|
|
2338
|
+
"id": "plsat-095b",
|
|
2339
|
+
"scenario": "After recording lore for a session where the agent created two new view components following the existing LogsView pattern, the `paradigm_lore_record` response includes a `protocol_suggestion` field with a draft protocol.",
|
|
2340
|
+
"question": "What triggered this protocol suggestion?",
|
|
2341
|
+
"choices": {
|
|
2342
|
+
"A": "The agent explicitly asked for protocol suggestions in the lore_record call",
|
|
2343
|
+
"B": "The lore system detected that the session created new files following existing patterns in the same directory",
|
|
2344
|
+
"C": "All lore entries automatically include protocol suggestions",
|
|
2345
|
+
"D": "The protocol suggestion was cached from a previous session",
|
|
2346
|
+
"E": "The lore system runs paradigm_protocol_search on every lore entry"
|
|
2347
|
+
},
|
|
2348
|
+
"correct": "B",
|
|
2349
|
+
"explanation": "When paradigm_lore_record is called, it runs a detection heuristic: if the session created 2+ new files in a directory that already has similar files, or modified the same 'registration' files that existing protocols touch, it includes a protocol_suggestion in the response. This nudges agents to capture repeatable patterns without manual intervention. Not all lore entries trigger suggestions (C) — only those with detectable repeatable patterns."
|
|
2350
|
+
}
|
|
2351
|
+
]
|
|
2352
|
+
},
|
|
2353
|
+
{
|
|
2354
|
+
"type": "standalone",
|
|
2355
|
+
"slot": "slot-096",
|
|
2356
|
+
"course": "para-301",
|
|
2357
|
+
"variants": [
|
|
2358
|
+
{
|
|
2359
|
+
"id": "plsat-096",
|
|
2360
|
+
"scenario": "During `paradigm_reindex`, the system validates all protocols. Protocol P-add-view references `ui/src/views/LogsView.tsx` as its exemplar. The file still exists but was significantly refactored two weeks after the protocol was last verified.",
|
|
2361
|
+
"question": "What status will the reindex assign to this protocol?",
|
|
2362
|
+
"choices": {
|
|
2363
|
+
"A": "`current` — the file still exists, so the protocol is valid",
|
|
2364
|
+
"B": "`stale` — the exemplar has been modified since the protocol was last verified",
|
|
2365
|
+
"C": "`broken` — any change to a referenced file invalidates the protocol",
|
|
2366
|
+
"D": "`deprecated` — protocols older than 30 days are automatically deprecated",
|
|
2367
|
+
"E": "`unknown` — reindex cannot determine status without running the protocol"
|
|
2368
|
+
},
|
|
2369
|
+
"correct": "B",
|
|
2370
|
+
"explanation": "During reindex validation, a protocol is marked 'stale' when its exemplar or referenced files have been modified since last_verified. The file still exists (so it is not 'broken'), but the protocol's steps might no longer match the current code pattern. A 'broken' status (C) is reserved for when referenced files are missing entirely. Stale protocols still work but should be reviewed and refreshed after successful use via paradigm_protocol_update with refresh: true."
|
|
2371
|
+
},
|
|
2372
|
+
{
|
|
2373
|
+
"id": "plsat-096b",
|
|
2374
|
+
"scenario": "An agent calls `paradigm_protocol_validate({ id: \"P-add-api-route\" })`. The protocol's step 2 references the file `src/routes/index.ts`, but that file was deleted during a recent refactoring.",
|
|
2375
|
+
"question": "What status will the validation assign?",
|
|
2376
|
+
"choices": {
|
|
2377
|
+
"A": "`stale` — a referenced file has changed",
|
|
2378
|
+
"B": "`current` — the protocol itself is still syntactically valid",
|
|
2379
|
+
"C": "`broken` — a referenced file no longer exists",
|
|
2380
|
+
"D": "`warning` — the file might be temporarily missing",
|
|
2381
|
+
"E": "`archived` — protocols with missing references are auto-archived"
|
|
2382
|
+
},
|
|
2383
|
+
"correct": "C",
|
|
2384
|
+
"explanation": "A 'broken' status means one or more files referenced by the protocol (targets, exemplars, or template_from) no longer exist. This is more severe than 'stale' (where files exist but have been modified). A broken protocol cannot be reliably followed until its references are updated to point to existing files via paradigm_protocol_update."
|
|
2385
|
+
}
|
|
2386
|
+
]
|
|
2282
2387
|
}
|
|
2283
2388
|
]
|
|
2284
2389
|
}
|