@a-company/paradigm 3.20.2 → 3.22.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/dist/graph-BFZXFAFL.js +160 -0
- package/dist/graph-server-BPKQYSQK.js +188 -0
- package/dist/{habits-KD4RLIN2.js → habits-7BORPC2F.js} +31 -8
- package/dist/index.js +18 -10
- package/dist/mcp.js +675 -29
- package/graph-ui/dist/assets/index-BJ499n6C.css +1 -0
- package/graph-ui/dist/assets/index-EP5AoOxo.js +63 -0
- package/graph-ui/dist/index.html +13 -0
- package/package.json +4 -2
package/dist/mcp.js
CHANGED
|
@@ -2026,7 +2026,7 @@ function registerResources(server, getContext2) {
|
|
|
2026
2026
|
|
|
2027
2027
|
// ../paradigm-mcp/src/tools/index.ts
|
|
2028
2028
|
import * as os3 from "os";
|
|
2029
|
-
import * as
|
|
2029
|
+
import * as path26 from "path";
|
|
2030
2030
|
import {
|
|
2031
2031
|
ListToolsRequestSchema,
|
|
2032
2032
|
CallToolRequestSchema
|
|
@@ -2790,7 +2790,7 @@ function navigateExplore(config, target, rootDir) {
|
|
|
2790
2790
|
}
|
|
2791
2791
|
if (result.paths.length === 0) {
|
|
2792
2792
|
const areaSymbols = Object.entries(config.symbols).filter(
|
|
2793
|
-
([sym,
|
|
2793
|
+
([sym, path27]) => sym.toLowerCase().includes(targetLower) || path27.toLowerCase().includes(targetLower)
|
|
2794
2794
|
).slice(0, 10);
|
|
2795
2795
|
result.paths = [...new Set(areaSymbols.map(([, p]) => p))];
|
|
2796
2796
|
result.symbols = areaSymbols.map(([s]) => s);
|
|
@@ -7543,8 +7543,16 @@ function loadHabitsFresh(rootDir) {
|
|
|
7543
7543
|
const home = process.env.HOME || process.env.USERPROFILE || "~";
|
|
7544
7544
|
const globalConfig = loadHabitsYaml(path18.join(home, ".paradigm", "habits.yaml"));
|
|
7545
7545
|
if (globalConfig) mergeHabits(habitsById, globalConfig);
|
|
7546
|
+
const globalHabitFiles = loadHabitFiles(path18.join(home, ".paradigm", "habits"));
|
|
7547
|
+
for (const habit of globalHabitFiles) {
|
|
7548
|
+
habitsById.set(habit.id, habit);
|
|
7549
|
+
}
|
|
7546
7550
|
const projectConfig = loadHabitsYaml(path18.join(rootDir, ".paradigm", "habits.yaml"));
|
|
7547
7551
|
if (projectConfig) mergeHabits(habitsById, projectConfig);
|
|
7552
|
+
const projectHabitFiles = loadHabitFiles(path18.join(rootDir, ".paradigm", "habits"));
|
|
7553
|
+
for (const habit of projectHabitFiles) {
|
|
7554
|
+
habitsById.set(habit.id, habit);
|
|
7555
|
+
}
|
|
7548
7556
|
return Array.from(habitsById.values());
|
|
7549
7557
|
}
|
|
7550
7558
|
function loadHabitsYaml(filePath) {
|
|
@@ -7575,6 +7583,141 @@ function mergeHabits(habitsById, config) {
|
|
|
7575
7583
|
function getHabitsByTrigger(habits, trigger) {
|
|
7576
7584
|
return habits.filter((h) => h.enabled && h.trigger === trigger);
|
|
7577
7585
|
}
|
|
7586
|
+
function invalidateHabitsCache(rootDir) {
|
|
7587
|
+
habitsCache.delete(path18.resolve(rootDir));
|
|
7588
|
+
}
|
|
7589
|
+
function loadHabitFiles(dir) {
|
|
7590
|
+
if (!fs17.existsSync(dir)) return [];
|
|
7591
|
+
try {
|
|
7592
|
+
const files = fs17.readdirSync(dir).filter((f) => f.endsWith(".habit")).sort();
|
|
7593
|
+
const habits = [];
|
|
7594
|
+
for (const file of files) {
|
|
7595
|
+
try {
|
|
7596
|
+
const content = fs17.readFileSync(path18.join(dir, file), "utf8");
|
|
7597
|
+
const habit = yaml11.load(content);
|
|
7598
|
+
if (habit?.id && habit?.name) {
|
|
7599
|
+
habits.push(habit);
|
|
7600
|
+
}
|
|
7601
|
+
} catch {
|
|
7602
|
+
}
|
|
7603
|
+
}
|
|
7604
|
+
return habits;
|
|
7605
|
+
} catch {
|
|
7606
|
+
return [];
|
|
7607
|
+
}
|
|
7608
|
+
}
|
|
7609
|
+
var VALID_CATEGORIES = [
|
|
7610
|
+
"discovery",
|
|
7611
|
+
"verification",
|
|
7612
|
+
"testing",
|
|
7613
|
+
"documentation",
|
|
7614
|
+
"collaboration",
|
|
7615
|
+
"security"
|
|
7616
|
+
];
|
|
7617
|
+
var VALID_TRIGGERS = [
|
|
7618
|
+
"preflight",
|
|
7619
|
+
"postflight",
|
|
7620
|
+
"on-commit",
|
|
7621
|
+
"on-stop"
|
|
7622
|
+
];
|
|
7623
|
+
var VALID_SEVERITIES = ["advisory", "warn", "block"];
|
|
7624
|
+
var VALID_CHECK_TYPES = [
|
|
7625
|
+
"tool-called",
|
|
7626
|
+
"file-exists",
|
|
7627
|
+
"file-modified",
|
|
7628
|
+
"lore-recorded",
|
|
7629
|
+
"symbols-registered",
|
|
7630
|
+
"gates-declared",
|
|
7631
|
+
"tests-exist",
|
|
7632
|
+
"git-clean",
|
|
7633
|
+
"commit-message-format",
|
|
7634
|
+
"flow-coverage",
|
|
7635
|
+
"context-checked",
|
|
7636
|
+
"aspect-anchored"
|
|
7637
|
+
];
|
|
7638
|
+
var KEBAB_CASE_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
7639
|
+
function validateHabitDefinition(habit) {
|
|
7640
|
+
const errors = [];
|
|
7641
|
+
if (!habit.id) errors.push("Missing required field: id");
|
|
7642
|
+
if (!habit.name) errors.push("Missing required field: name");
|
|
7643
|
+
if (!habit.description) errors.push("Missing required field: description");
|
|
7644
|
+
if (!habit.category) errors.push("Missing required field: category");
|
|
7645
|
+
if (!habit.trigger) errors.push("Missing required field: trigger");
|
|
7646
|
+
if (!habit.severity) errors.push("Missing required field: severity");
|
|
7647
|
+
if (!habit.check) errors.push("Missing required field: check");
|
|
7648
|
+
if (habit.enabled === void 0 || habit.enabled === null) errors.push("Missing required field: enabled");
|
|
7649
|
+
if (habit.id && !KEBAB_CASE_RE.test(habit.id)) {
|
|
7650
|
+
errors.push(`Invalid id format: "${habit.id}" \u2014 must be kebab-case (lowercase, hyphens only)`);
|
|
7651
|
+
}
|
|
7652
|
+
if (habit.category && !VALID_CATEGORIES.includes(habit.category)) {
|
|
7653
|
+
errors.push(`Invalid category: "${habit.category}" \u2014 must be one of: ${VALID_CATEGORIES.join(", ")}`);
|
|
7654
|
+
}
|
|
7655
|
+
if (habit.trigger && !VALID_TRIGGERS.includes(habit.trigger)) {
|
|
7656
|
+
errors.push(`Invalid trigger: "${habit.trigger}" \u2014 must be one of: ${VALID_TRIGGERS.join(", ")}`);
|
|
7657
|
+
}
|
|
7658
|
+
if (habit.severity && !VALID_SEVERITIES.includes(habit.severity)) {
|
|
7659
|
+
errors.push(`Invalid severity: "${habit.severity}" \u2014 must be one of: ${VALID_SEVERITIES.join(", ")}`);
|
|
7660
|
+
}
|
|
7661
|
+
if (habit.check) {
|
|
7662
|
+
if (!VALID_CHECK_TYPES.includes(habit.check.type)) {
|
|
7663
|
+
errors.push(`Invalid check.type: "${habit.check.type}" \u2014 must be one of: ${VALID_CHECK_TYPES.join(", ")}`);
|
|
7664
|
+
}
|
|
7665
|
+
const params = habit.check.params || {};
|
|
7666
|
+
switch (habit.check.type) {
|
|
7667
|
+
case "tool-called":
|
|
7668
|
+
if (!params.tools || !Array.isArray(params.tools) || params.tools.length === 0) {
|
|
7669
|
+
errors.push('check.type "tool-called" requires check.params.tools[] (non-empty array)');
|
|
7670
|
+
}
|
|
7671
|
+
break;
|
|
7672
|
+
case "file-exists":
|
|
7673
|
+
case "file-modified":
|
|
7674
|
+
if (!params.patterns || !Array.isArray(params.patterns) || params.patterns.length === 0) {
|
|
7675
|
+
errors.push(`check.type "${habit.check.type}" requires check.params.patterns[] (non-empty array)`);
|
|
7676
|
+
}
|
|
7677
|
+
break;
|
|
7678
|
+
case "commit-message-format":
|
|
7679
|
+
if (!params.messagePatterns || !Array.isArray(params.messagePatterns) || params.messagePatterns.length === 0) {
|
|
7680
|
+
errors.push('check.type "commit-message-format" requires check.params.messagePatterns[] (non-empty array)');
|
|
7681
|
+
}
|
|
7682
|
+
break;
|
|
7683
|
+
}
|
|
7684
|
+
}
|
|
7685
|
+
return { valid: errors.length === 0, errors };
|
|
7686
|
+
}
|
|
7687
|
+
var SEED_HABIT_IDS = new Set(SEED_HABITS.map((h) => h.id));
|
|
7688
|
+
function isSeedHabit(id) {
|
|
7689
|
+
return SEED_HABIT_IDS.has(id);
|
|
7690
|
+
}
|
|
7691
|
+
function saveHabit(rootDir, habit, scope = "project") {
|
|
7692
|
+
const baseDir = scope === "global" ? path18.join(process.env.HOME || process.env.USERPROFILE || "~", ".paradigm", "habits") : path18.join(rootDir, ".paradigm", "habits");
|
|
7693
|
+
if (!fs17.existsSync(baseDir)) {
|
|
7694
|
+
fs17.mkdirSync(baseDir, { recursive: true });
|
|
7695
|
+
}
|
|
7696
|
+
const filePath = path18.join(baseDir, `${habit.id}.habit`);
|
|
7697
|
+
const content = yaml11.dump(habit, { lineWidth: 120, noRefs: true });
|
|
7698
|
+
fs17.writeFileSync(filePath, content, "utf8");
|
|
7699
|
+
invalidateHabitsCache(rootDir);
|
|
7700
|
+
return filePath;
|
|
7701
|
+
}
|
|
7702
|
+
function removeHabit(rootDir, id) {
|
|
7703
|
+
if (isSeedHabit(id)) {
|
|
7704
|
+
return { removed: false, reason: `"${id}" is a seed habit and cannot be removed. Use overrides in habits.yaml to disable it.` };
|
|
7705
|
+
}
|
|
7706
|
+
const projectPath = path18.join(rootDir, ".paradigm", "habits", `${id}.habit`);
|
|
7707
|
+
if (fs17.existsSync(projectPath)) {
|
|
7708
|
+
fs17.unlinkSync(projectPath);
|
|
7709
|
+
invalidateHabitsCache(rootDir);
|
|
7710
|
+
return { removed: true };
|
|
7711
|
+
}
|
|
7712
|
+
const home = process.env.HOME || process.env.USERPROFILE || "~";
|
|
7713
|
+
const globalPath = path18.join(home, ".paradigm", "habits", `${id}.habit`);
|
|
7714
|
+
if (fs17.existsSync(globalPath)) {
|
|
7715
|
+
fs17.unlinkSync(globalPath);
|
|
7716
|
+
invalidateHabitsCache(rootDir);
|
|
7717
|
+
return { removed: true };
|
|
7718
|
+
}
|
|
7719
|
+
return { removed: false, reason: `No .habit file found for "${id}". It may be defined in habits.yaml \u2014 edit that file directly.` };
|
|
7720
|
+
}
|
|
7578
7721
|
function evaluateHabits(habits, trigger, context2, platform2) {
|
|
7579
7722
|
let activeHabits = getHabitsByTrigger(habits, trigger);
|
|
7580
7723
|
if (platform2) {
|
|
@@ -8786,6 +8929,7 @@ function summarizeEntry(entry) {
|
|
|
8786
8929
|
// ../paradigm-mcp/src/tools/habits.ts
|
|
8787
8930
|
import * as fs19 from "fs";
|
|
8788
8931
|
import * as path21 from "path";
|
|
8932
|
+
import * as yaml12 from "js-yaml";
|
|
8789
8933
|
import { execSync as execSync3 } from "child_process";
|
|
8790
8934
|
function getHabitsToolsList() {
|
|
8791
8935
|
return [
|
|
@@ -8897,6 +9041,128 @@ function getHabitsToolsList() {
|
|
|
8897
9041
|
readOnlyHint: true,
|
|
8898
9042
|
destructiveHint: false
|
|
8899
9043
|
}
|
|
9044
|
+
},
|
|
9045
|
+
{
|
|
9046
|
+
name: "paradigm_habits_add",
|
|
9047
|
+
description: 'Create a new custom habit as an individual .habit file. Validates all fields and checks for ID collisions with seed habits. Use scope "global" for ~/.paradigm/habits/ or "project" (default) for .paradigm/habits/. ~150 tokens.',
|
|
9048
|
+
inputSchema: {
|
|
9049
|
+
type: "object",
|
|
9050
|
+
properties: {
|
|
9051
|
+
id: { type: "string", description: 'Unique habit ID in kebab-case (e.g. "check-changelog")' },
|
|
9052
|
+
name: { type: "string", description: "Human-readable habit name" },
|
|
9053
|
+
description: { type: "string", description: "What this habit checks and why" },
|
|
9054
|
+
category: {
|
|
9055
|
+
type: "string",
|
|
9056
|
+
enum: ["discovery", "verification", "testing", "documentation", "collaboration", "security"],
|
|
9057
|
+
description: "Habit category"
|
|
9058
|
+
},
|
|
9059
|
+
trigger: {
|
|
9060
|
+
type: "string",
|
|
9061
|
+
enum: ["preflight", "postflight", "on-commit", "on-stop"],
|
|
9062
|
+
description: "When the habit is evaluated"
|
|
9063
|
+
},
|
|
9064
|
+
severity: {
|
|
9065
|
+
type: "string",
|
|
9066
|
+
enum: ["advisory", "warn", "block"],
|
|
9067
|
+
description: "How strictly to enforce (block prevents session completion)"
|
|
9068
|
+
},
|
|
9069
|
+
check: {
|
|
9070
|
+
type: "object",
|
|
9071
|
+
description: "Check definition with type and params",
|
|
9072
|
+
properties: {
|
|
9073
|
+
type: {
|
|
9074
|
+
type: "string",
|
|
9075
|
+
enum: [
|
|
9076
|
+
"tool-called",
|
|
9077
|
+
"file-exists",
|
|
9078
|
+
"file-modified",
|
|
9079
|
+
"lore-recorded",
|
|
9080
|
+
"symbols-registered",
|
|
9081
|
+
"gates-declared",
|
|
9082
|
+
"tests-exist",
|
|
9083
|
+
"git-clean",
|
|
9084
|
+
"commit-message-format",
|
|
9085
|
+
"flow-coverage",
|
|
9086
|
+
"context-checked",
|
|
9087
|
+
"aspect-anchored"
|
|
9088
|
+
]
|
|
9089
|
+
},
|
|
9090
|
+
params: { type: "object", description: "Check-specific parameters (tools[], patterns[], etc.)" }
|
|
9091
|
+
},
|
|
9092
|
+
required: ["type", "params"]
|
|
9093
|
+
},
|
|
9094
|
+
enabled: { type: "boolean", description: "Whether the habit is active (default: true)" },
|
|
9095
|
+
platforms: {
|
|
9096
|
+
type: "array",
|
|
9097
|
+
items: { type: "string" },
|
|
9098
|
+
description: 'Platforms this habit applies to (e.g. ["claude", "cursor"]). Omit for all.'
|
|
9099
|
+
},
|
|
9100
|
+
scope: {
|
|
9101
|
+
type: "string",
|
|
9102
|
+
enum: ["project", "global"],
|
|
9103
|
+
description: 'Where to save: "project" (default) or "global" (~/.paradigm/habits/)'
|
|
9104
|
+
}
|
|
9105
|
+
},
|
|
9106
|
+
required: ["id", "name", "description", "category", "trigger", "severity", "check"]
|
|
9107
|
+
},
|
|
9108
|
+
annotations: {
|
|
9109
|
+
readOnlyHint: false,
|
|
9110
|
+
destructiveHint: false
|
|
9111
|
+
}
|
|
9112
|
+
},
|
|
9113
|
+
{
|
|
9114
|
+
name: "paradigm_habits_edit",
|
|
9115
|
+
description: "Update fields on an existing custom .habit file. Cannot edit seed habits \u2014 use overrides in habits.yaml instead. Merges provided fields with existing definition and re-validates. ~150 tokens.",
|
|
9116
|
+
inputSchema: {
|
|
9117
|
+
type: "object",
|
|
9118
|
+
properties: {
|
|
9119
|
+
id: { type: "string", description: "ID of the habit to edit" },
|
|
9120
|
+
name: { type: "string", description: "Updated name" },
|
|
9121
|
+
description: { type: "string", description: "Updated description" },
|
|
9122
|
+
category: {
|
|
9123
|
+
type: "string",
|
|
9124
|
+
enum: ["discovery", "verification", "testing", "documentation", "collaboration", "security"]
|
|
9125
|
+
},
|
|
9126
|
+
trigger: {
|
|
9127
|
+
type: "string",
|
|
9128
|
+
enum: ["preflight", "postflight", "on-commit", "on-stop"]
|
|
9129
|
+
},
|
|
9130
|
+
severity: {
|
|
9131
|
+
type: "string",
|
|
9132
|
+
enum: ["advisory", "warn", "block"]
|
|
9133
|
+
},
|
|
9134
|
+
check: {
|
|
9135
|
+
type: "object",
|
|
9136
|
+
properties: {
|
|
9137
|
+
type: { type: "string" },
|
|
9138
|
+
params: { type: "object" }
|
|
9139
|
+
},
|
|
9140
|
+
required: ["type", "params"]
|
|
9141
|
+
},
|
|
9142
|
+
enabled: { type: "boolean" },
|
|
9143
|
+
platforms: { type: "array", items: { type: "string" } }
|
|
9144
|
+
},
|
|
9145
|
+
required: ["id"]
|
|
9146
|
+
},
|
|
9147
|
+
annotations: {
|
|
9148
|
+
readOnlyHint: false,
|
|
9149
|
+
destructiveHint: false
|
|
9150
|
+
}
|
|
9151
|
+
},
|
|
9152
|
+
{
|
|
9153
|
+
name: "paradigm_habits_remove",
|
|
9154
|
+
description: "Delete a custom .habit file. Cannot remove seed habits \u2014 use overrides to disable them instead. Searches both project and global habit directories. ~100 tokens.",
|
|
9155
|
+
inputSchema: {
|
|
9156
|
+
type: "object",
|
|
9157
|
+
properties: {
|
|
9158
|
+
id: { type: "string", description: "ID of the habit to remove" }
|
|
9159
|
+
},
|
|
9160
|
+
required: ["id"]
|
|
9161
|
+
},
|
|
9162
|
+
annotations: {
|
|
9163
|
+
readOnlyHint: false,
|
|
9164
|
+
destructiveHint: true
|
|
9165
|
+
}
|
|
8900
9166
|
}
|
|
8901
9167
|
];
|
|
8902
9168
|
}
|
|
@@ -8922,6 +9188,21 @@ async function handleHabitsTool(name, args, ctx) {
|
|
|
8922
9188
|
trackToolCall(result.length, name);
|
|
8923
9189
|
return { text: result, handled: true };
|
|
8924
9190
|
}
|
|
9191
|
+
case "paradigm_habits_add": {
|
|
9192
|
+
const result = handleHabitsAdd(args, ctx);
|
|
9193
|
+
trackToolCall(result.length, name);
|
|
9194
|
+
return { text: result, handled: true };
|
|
9195
|
+
}
|
|
9196
|
+
case "paradigm_habits_edit": {
|
|
9197
|
+
const result = handleHabitsEdit(args, ctx);
|
|
9198
|
+
trackToolCall(result.length, name);
|
|
9199
|
+
return { text: result, handled: true };
|
|
9200
|
+
}
|
|
9201
|
+
case "paradigm_habits_remove": {
|
|
9202
|
+
const result = handleHabitsRemove(args, ctx);
|
|
9203
|
+
trackToolCall(result.length, name);
|
|
9204
|
+
return { text: result, handled: true };
|
|
9205
|
+
}
|
|
8925
9206
|
default:
|
|
8926
9207
|
return { text: "", handled: false };
|
|
8927
9208
|
}
|
|
@@ -9111,6 +9392,127 @@ function buildRecommendations(evaluation) {
|
|
|
9111
9392
|
}
|
|
9112
9393
|
return [...new Set(recs)];
|
|
9113
9394
|
}
|
|
9395
|
+
function handleHabitsAdd(args, ctx) {
|
|
9396
|
+
const id = args.id;
|
|
9397
|
+
const scope = args.scope || "project";
|
|
9398
|
+
if (isSeedHabit(id)) {
|
|
9399
|
+
return JSON.stringify({
|
|
9400
|
+
error: true,
|
|
9401
|
+
message: `Cannot create habit "${id}" \u2014 it collides with a seed habit. Choose a different ID or use overrides in habits.yaml to customize the seed habit.`
|
|
9402
|
+
}, null, 2);
|
|
9403
|
+
}
|
|
9404
|
+
const existingHabits = loadHabits(ctx.rootDir);
|
|
9405
|
+
const existing = existingHabits.find((h) => h.id === id);
|
|
9406
|
+
if (existing) {
|
|
9407
|
+
return JSON.stringify({
|
|
9408
|
+
error: true,
|
|
9409
|
+
message: `Habit "${id}" already exists. Use paradigm_habits_edit to update it, or choose a different ID.`
|
|
9410
|
+
}, null, 2);
|
|
9411
|
+
}
|
|
9412
|
+
const habit = {
|
|
9413
|
+
id,
|
|
9414
|
+
name: args.name,
|
|
9415
|
+
description: args.description,
|
|
9416
|
+
category: args.category,
|
|
9417
|
+
trigger: args.trigger,
|
|
9418
|
+
severity: args.severity,
|
|
9419
|
+
check: args.check,
|
|
9420
|
+
enabled: args.enabled !== void 0 ? args.enabled : true
|
|
9421
|
+
};
|
|
9422
|
+
if (args.platforms) habit.platforms = args.platforms;
|
|
9423
|
+
const validation = validateHabitDefinition(habit);
|
|
9424
|
+
if (!validation.valid) {
|
|
9425
|
+
return JSON.stringify({
|
|
9426
|
+
error: true,
|
|
9427
|
+
message: "Validation failed",
|
|
9428
|
+
errors: validation.errors
|
|
9429
|
+
}, null, 2);
|
|
9430
|
+
}
|
|
9431
|
+
const filePath = saveHabit(ctx.rootDir, habit, scope);
|
|
9432
|
+
return JSON.stringify({
|
|
9433
|
+
created: true,
|
|
9434
|
+
id: habit.id,
|
|
9435
|
+
filePath,
|
|
9436
|
+
scope,
|
|
9437
|
+
message: `Habit "${habit.name}" created at ${filePath}`
|
|
9438
|
+
}, null, 2);
|
|
9439
|
+
}
|
|
9440
|
+
function handleHabitsEdit(args, ctx) {
|
|
9441
|
+
const id = args.id;
|
|
9442
|
+
if (isSeedHabit(id)) {
|
|
9443
|
+
return JSON.stringify({
|
|
9444
|
+
error: true,
|
|
9445
|
+
message: `Cannot edit "${id}" \u2014 it is a seed habit. To customize it, add an override in .paradigm/habits.yaml under the "overrides:" key.`
|
|
9446
|
+
}, null, 2);
|
|
9447
|
+
}
|
|
9448
|
+
const projectPath = path21.join(ctx.rootDir, ".paradigm", "habits", `${id}.habit`);
|
|
9449
|
+
const home = process.env.HOME || process.env.USERPROFILE || "~";
|
|
9450
|
+
const globalPath = path21.join(home, ".paradigm", "habits", `${id}.habit`);
|
|
9451
|
+
let filePath = null;
|
|
9452
|
+
let scope = "project";
|
|
9453
|
+
if (fs19.existsSync(projectPath)) {
|
|
9454
|
+
filePath = projectPath;
|
|
9455
|
+
scope = "project";
|
|
9456
|
+
} else if (fs19.existsSync(globalPath)) {
|
|
9457
|
+
filePath = globalPath;
|
|
9458
|
+
scope = "global";
|
|
9459
|
+
}
|
|
9460
|
+
if (!filePath) {
|
|
9461
|
+
return JSON.stringify({
|
|
9462
|
+
error: true,
|
|
9463
|
+
message: `No .habit file found for "${id}". It may be defined in habits.yaml \u2014 edit that file directly.`
|
|
9464
|
+
}, null, 2);
|
|
9465
|
+
}
|
|
9466
|
+
let existing;
|
|
9467
|
+
try {
|
|
9468
|
+
const content = fs19.readFileSync(filePath, "utf8");
|
|
9469
|
+
existing = yaml12.load(content);
|
|
9470
|
+
} catch {
|
|
9471
|
+
return JSON.stringify({
|
|
9472
|
+
error: true,
|
|
9473
|
+
message: `Failed to read ${filePath}`
|
|
9474
|
+
}, null, 2);
|
|
9475
|
+
}
|
|
9476
|
+
const updated = { ...existing };
|
|
9477
|
+
if (args.name !== void 0) updated.name = args.name;
|
|
9478
|
+
if (args.description !== void 0) updated.description = args.description;
|
|
9479
|
+
if (args.category !== void 0) updated.category = args.category;
|
|
9480
|
+
if (args.trigger !== void 0) updated.trigger = args.trigger;
|
|
9481
|
+
if (args.severity !== void 0) updated.severity = args.severity;
|
|
9482
|
+
if (args.check !== void 0) updated.check = args.check;
|
|
9483
|
+
if (args.enabled !== void 0) updated.enabled = args.enabled;
|
|
9484
|
+
if (args.platforms !== void 0) updated.platforms = args.platforms;
|
|
9485
|
+
const validation = validateHabitDefinition(updated);
|
|
9486
|
+
if (!validation.valid) {
|
|
9487
|
+
return JSON.stringify({
|
|
9488
|
+
error: true,
|
|
9489
|
+
message: "Validation failed after merge",
|
|
9490
|
+
errors: validation.errors
|
|
9491
|
+
}, null, 2);
|
|
9492
|
+
}
|
|
9493
|
+
saveHabit(ctx.rootDir, updated, scope);
|
|
9494
|
+
return JSON.stringify({
|
|
9495
|
+
updated: true,
|
|
9496
|
+
id: updated.id,
|
|
9497
|
+
filePath,
|
|
9498
|
+
message: `Habit "${updated.name}" updated`
|
|
9499
|
+
}, null, 2);
|
|
9500
|
+
}
|
|
9501
|
+
function handleHabitsRemove(args, ctx) {
|
|
9502
|
+
const id = args.id;
|
|
9503
|
+
const result = removeHabit(ctx.rootDir, id);
|
|
9504
|
+
if (result.removed) {
|
|
9505
|
+
return JSON.stringify({
|
|
9506
|
+
removed: true,
|
|
9507
|
+
id,
|
|
9508
|
+
message: `Habit "${id}" removed`
|
|
9509
|
+
}, null, 2);
|
|
9510
|
+
}
|
|
9511
|
+
return JSON.stringify({
|
|
9512
|
+
error: true,
|
|
9513
|
+
message: result.reason
|
|
9514
|
+
}, null, 2);
|
|
9515
|
+
}
|
|
9114
9516
|
async function handleHabitsStatus(args, ctx) {
|
|
9115
9517
|
const engineer = args.engineer;
|
|
9116
9518
|
const period = args.period || "30d";
|
|
@@ -10366,7 +10768,7 @@ async function handleTasksTool(name, args, ctx) {
|
|
|
10366
10768
|
// ../paradigm-mcp/src/utils/assessment-loader.ts
|
|
10367
10769
|
import * as fs21 from "fs";
|
|
10368
10770
|
import * as path23 from "path";
|
|
10369
|
-
import * as
|
|
10771
|
+
import * as yaml13 from "js-yaml";
|
|
10370
10772
|
var ASSESSMENTS_DIR = ".paradigm/assessments";
|
|
10371
10773
|
var ARCS_DIR = "arcs";
|
|
10372
10774
|
var INDEX_FILE = "index.yaml";
|
|
@@ -10379,7 +10781,7 @@ function computeArcStats(rootDir, arc) {
|
|
|
10379
10781
|
const files = fs21.readdirSync(entriesPath).filter((f) => f.endsWith(".yaml"));
|
|
10380
10782
|
for (const file of files) {
|
|
10381
10783
|
try {
|
|
10382
|
-
const entry =
|
|
10784
|
+
const entry = yaml13.load(fs21.readFileSync(path23.join(entriesPath, file), "utf8"));
|
|
10383
10785
|
entryCount++;
|
|
10384
10786
|
if (entry.symbols) entry.symbols.forEach((s) => symbolSet.add(s));
|
|
10385
10787
|
if (!latestDate || entry.date > latestDate) latestDate = entry.date;
|
|
@@ -10404,7 +10806,7 @@ async function loadArcs(rootDir, status) {
|
|
|
10404
10806
|
const arcFile = path23.join(arcsPath, arcDir, "arc.yaml");
|
|
10405
10807
|
if (!fs21.existsSync(arcFile)) continue;
|
|
10406
10808
|
try {
|
|
10407
|
-
const arc =
|
|
10809
|
+
const arc = yaml13.load(fs21.readFileSync(arcFile, "utf8"));
|
|
10408
10810
|
if (status && status !== "all" && arc.status !== status) continue;
|
|
10409
10811
|
arcs.push(computeArcStats(rootDir, arc));
|
|
10410
10812
|
} catch {
|
|
@@ -10421,7 +10823,7 @@ async function loadArc(rootDir, arcId) {
|
|
|
10421
10823
|
const arcFile = path23.join(rootDir, ASSESSMENTS_DIR, ARCS_DIR, arcId, "arc.yaml");
|
|
10422
10824
|
if (!fs21.existsSync(arcFile)) return null;
|
|
10423
10825
|
try {
|
|
10424
|
-
const arc =
|
|
10826
|
+
const arc = yaml13.load(fs21.readFileSync(arcFile, "utf8"));
|
|
10425
10827
|
return computeArcStats(rootDir, arc);
|
|
10426
10828
|
} catch {
|
|
10427
10829
|
return null;
|
|
@@ -10431,9 +10833,9 @@ async function closeArc(rootDir, arcId, status) {
|
|
|
10431
10833
|
const arcFile = path23.join(rootDir, ASSESSMENTS_DIR, ARCS_DIR, arcId, "arc.yaml");
|
|
10432
10834
|
if (!fs21.existsSync(arcFile)) return false;
|
|
10433
10835
|
try {
|
|
10434
|
-
const arc =
|
|
10836
|
+
const arc = yaml13.load(fs21.readFileSync(arcFile, "utf8"));
|
|
10435
10837
|
arc.status = status;
|
|
10436
|
-
fs21.writeFileSync(arcFile,
|
|
10838
|
+
fs21.writeFileSync(arcFile, yaml13.dump(arc, { lineWidth: -1, noRefs: true }));
|
|
10437
10839
|
await rebuildAssessmentIndex(rootDir);
|
|
10438
10840
|
return true;
|
|
10439
10841
|
} catch {
|
|
@@ -10447,7 +10849,7 @@ async function loadEntries(rootDir, arcId) {
|
|
|
10447
10849
|
const files = fs21.readdirSync(entriesPath).filter((f) => f.endsWith(".yaml")).sort();
|
|
10448
10850
|
for (const file of files) {
|
|
10449
10851
|
try {
|
|
10450
|
-
const entry =
|
|
10852
|
+
const entry = yaml13.load(fs21.readFileSync(path23.join(entriesPath, file), "utf8"));
|
|
10451
10853
|
entries.push(entry);
|
|
10452
10854
|
} catch {
|
|
10453
10855
|
}
|
|
@@ -10469,9 +10871,9 @@ async function loadEntry(rootDir, entryId) {
|
|
|
10469
10871
|
const entryFile = path23.join(arcsPath, arcDir, "entries", `${entryId}.yaml`);
|
|
10470
10872
|
if (fs21.existsSync(entryFile)) {
|
|
10471
10873
|
try {
|
|
10472
|
-
const entry =
|
|
10874
|
+
const entry = yaml13.load(fs21.readFileSync(entryFile, "utf8"));
|
|
10473
10875
|
const arcFile = path23.join(arcsPath, arcDir, "arc.yaml");
|
|
10474
|
-
const arc =
|
|
10876
|
+
const arc = yaml13.load(fs21.readFileSync(arcFile, "utf8"));
|
|
10475
10877
|
return { entry, arc };
|
|
10476
10878
|
} catch {
|
|
10477
10879
|
return null;
|
|
@@ -10498,7 +10900,7 @@ async function searchEntries(rootDir, filter) {
|
|
|
10498
10900
|
const files = fs21.readdirSync(entriesPath).filter((f) => f.endsWith(".yaml"));
|
|
10499
10901
|
for (const file of files) {
|
|
10500
10902
|
try {
|
|
10501
|
-
const entry =
|
|
10903
|
+
const entry = yaml13.load(fs21.readFileSync(path23.join(entriesPath, file), "utf8"));
|
|
10502
10904
|
if (filter.symbol && !(entry.symbols || []).includes(filter.symbol)) continue;
|
|
10503
10905
|
if (filter.tag && !(entry.tags || []).includes(filter.tag)) continue;
|
|
10504
10906
|
if (filter.type && entry.type !== filter.type) continue;
|
|
@@ -10529,7 +10931,7 @@ async function rebuildAssessmentIndex(rootDir) {
|
|
|
10529
10931
|
const arcFile = path23.join(arcsPath, arcDir, "arc.yaml");
|
|
10530
10932
|
if (!fs21.existsSync(arcFile)) continue;
|
|
10531
10933
|
try {
|
|
10532
|
-
const arc =
|
|
10934
|
+
const arc = yaml13.load(fs21.readFileSync(arcFile, "utf8"));
|
|
10533
10935
|
const entriesPath = path23.join(arcsPath, arcDir, "entries");
|
|
10534
10936
|
const entryCount = fs21.existsSync(entriesPath) ? fs21.readdirSync(entriesPath).filter((f) => f.endsWith(".yaml")).length : 0;
|
|
10535
10937
|
totalArcs++;
|
|
@@ -10549,7 +10951,7 @@ async function rebuildAssessmentIndex(rootDir) {
|
|
|
10549
10951
|
arcs: arcSummaries
|
|
10550
10952
|
};
|
|
10551
10953
|
fs21.mkdirSync(assessmentsPath, { recursive: true });
|
|
10552
|
-
fs21.writeFileSync(path23.join(assessmentsPath, INDEX_FILE),
|
|
10954
|
+
fs21.writeFileSync(path23.join(assessmentsPath, INDEX_FILE), yaml13.dump(index, { lineWidth: -1, noRefs: true }));
|
|
10553
10955
|
return index;
|
|
10554
10956
|
}
|
|
10555
10957
|
|
|
@@ -10886,8 +11288,8 @@ function generateRunId() {
|
|
|
10886
11288
|
var TEMPLATE_REGEX = /\{\{([^}]+)\}\}/g;
|
|
10887
11289
|
function interpolate(value, scope) {
|
|
10888
11290
|
if (typeof value === "string") {
|
|
10889
|
-
return value.replace(TEMPLATE_REGEX, (_match,
|
|
10890
|
-
const resolved = resolvePath(
|
|
11291
|
+
return value.replace(TEMPLATE_REGEX, (_match, path27) => {
|
|
11292
|
+
const resolved = resolvePath(path27.trim(), scope);
|
|
10891
11293
|
return resolved !== void 0 ? String(resolved) : _match;
|
|
10892
11294
|
});
|
|
10893
11295
|
}
|
|
@@ -10920,8 +11322,8 @@ function resolvePath(dotPath, scope) {
|
|
|
10920
11322
|
return void 0;
|
|
10921
11323
|
}
|
|
10922
11324
|
}
|
|
10923
|
-
function deepGet(obj,
|
|
10924
|
-
const parts =
|
|
11325
|
+
function deepGet(obj, path27) {
|
|
11326
|
+
const parts = path27.split(/[.\[\]]+/).filter(Boolean);
|
|
10925
11327
|
let current = obj;
|
|
10926
11328
|
for (const part of parts) {
|
|
10927
11329
|
if (current == null || typeof current !== "object") return void 0;
|
|
@@ -11157,11 +11559,11 @@ async function runPersonaObject(rootDir, persona, options) {
|
|
|
11157
11559
|
}
|
|
11158
11560
|
async function runChain(rootDir, chainId, options) {
|
|
11159
11561
|
const start = Date.now();
|
|
11160
|
-
const
|
|
11161
|
-
const
|
|
11162
|
-
const
|
|
11163
|
-
const chainPath =
|
|
11164
|
-
if (!
|
|
11562
|
+
const fs23 = await import("fs");
|
|
11563
|
+
const path27 = await import("path");
|
|
11564
|
+
const yaml14 = await import("js-yaml");
|
|
11565
|
+
const chainPath = path27.join(rootDir, ".paradigm", "personas", "chains", `${chainId}.yaml`);
|
|
11566
|
+
if (!fs23.existsSync(chainPath)) {
|
|
11165
11567
|
return {
|
|
11166
11568
|
chain_id: chainId,
|
|
11167
11569
|
status: "error",
|
|
@@ -11170,7 +11572,7 @@ async function runChain(rootDir, chainId, options) {
|
|
|
11170
11572
|
duration_ms: Date.now() - start
|
|
11171
11573
|
};
|
|
11172
11574
|
}
|
|
11173
|
-
const chain =
|
|
11575
|
+
const chain = yaml14.load(fs23.readFileSync(chainPath, "utf8"));
|
|
11174
11576
|
let permutation;
|
|
11175
11577
|
if (options.permutation && chain.permutations) {
|
|
11176
11578
|
permutation = chain.permutations.find((p) => p.id === options.permutation);
|
|
@@ -11274,8 +11676,8 @@ function validateInterpolation(persona) {
|
|
|
11274
11676
|
const serialized = JSON.stringify(step);
|
|
11275
11677
|
const templates = serialized.match(TEMPLATE_REGEX) || [];
|
|
11276
11678
|
for (const template of templates) {
|
|
11277
|
-
const
|
|
11278
|
-
const [namespace, ...rest] =
|
|
11679
|
+
const path27 = template.replace("{{", "").replace("}}", "").trim();
|
|
11680
|
+
const [namespace, ...rest] = path27.split(".");
|
|
11279
11681
|
const key = rest.join(".");
|
|
11280
11682
|
switch (namespace) {
|
|
11281
11683
|
case "fixtures":
|
|
@@ -12287,8 +12689,241 @@ function summarizeStep(step) {
|
|
|
12287
12689
|
return result;
|
|
12288
12690
|
}
|
|
12289
12691
|
|
|
12290
|
-
// ../paradigm-mcp/src/tools/
|
|
12692
|
+
// ../paradigm-mcp/src/tools/graph.ts
|
|
12693
|
+
import * as fs22 from "fs";
|
|
12291
12694
|
import * as path24 from "path";
|
|
12695
|
+
var CATEGORY_PREFIXES = {
|
|
12696
|
+
component: "#",
|
|
12697
|
+
flow: "$",
|
|
12698
|
+
gate: "^",
|
|
12699
|
+
signal: "!",
|
|
12700
|
+
aspect: "~"
|
|
12701
|
+
};
|
|
12702
|
+
var NODE_WIDTH = 200;
|
|
12703
|
+
var NODE_HEIGHT = 60;
|
|
12704
|
+
var NODE_GAP = 20;
|
|
12705
|
+
var GROUP_PADDING = 40;
|
|
12706
|
+
var GROUP_HEADER = 50;
|
|
12707
|
+
var GROUP_GAP = 60;
|
|
12708
|
+
function getGraphToolsList() {
|
|
12709
|
+
return [
|
|
12710
|
+
{
|
|
12711
|
+
name: "paradigm_graph_generate",
|
|
12712
|
+
description: "Generate a GraphState JSON document for the Paradigm Symbol Graph UI. Accepts optional symbols (filter), groups (clustering), and links (edges between groups). Returns valid GraphState ready to load. ~200 tokens.",
|
|
12713
|
+
inputSchema: {
|
|
12714
|
+
type: "object",
|
|
12715
|
+
properties: {
|
|
12716
|
+
symbols: {
|
|
12717
|
+
type: "array",
|
|
12718
|
+
items: { type: "string" },
|
|
12719
|
+
description: 'Symbol names to include (e.g. ["#auth-middleware", "^authenticated"]). Omit to include all from scan-index.'
|
|
12720
|
+
},
|
|
12721
|
+
groups: {
|
|
12722
|
+
type: "array",
|
|
12723
|
+
items: {
|
|
12724
|
+
type: "object",
|
|
12725
|
+
properties: {
|
|
12726
|
+
label: { type: "string", description: "Group display label" },
|
|
12727
|
+
symbols: {
|
|
12728
|
+
type: "array",
|
|
12729
|
+
items: { type: "string" },
|
|
12730
|
+
description: "Symbol names belonging to this group"
|
|
12731
|
+
}
|
|
12732
|
+
},
|
|
12733
|
+
required: ["label", "symbols"]
|
|
12734
|
+
},
|
|
12735
|
+
description: "Optional groupings of symbols."
|
|
12736
|
+
},
|
|
12737
|
+
links: {
|
|
12738
|
+
type: "array",
|
|
12739
|
+
items: {
|
|
12740
|
+
type: "object",
|
|
12741
|
+
properties: {
|
|
12742
|
+
source: { type: "string", description: "Source group label" },
|
|
12743
|
+
target: { type: "string", description: "Target group label" },
|
|
12744
|
+
label: { type: "string", description: "Edge label" }
|
|
12745
|
+
},
|
|
12746
|
+
required: ["source", "target"]
|
|
12747
|
+
},
|
|
12748
|
+
description: "Edges between groups (by label name)."
|
|
12749
|
+
},
|
|
12750
|
+
name: {
|
|
12751
|
+
type: "string",
|
|
12752
|
+
description: 'Graph name (default: "Generated Graph").'
|
|
12753
|
+
}
|
|
12754
|
+
}
|
|
12755
|
+
},
|
|
12756
|
+
annotations: {
|
|
12757
|
+
readOnlyHint: true,
|
|
12758
|
+
destructiveHint: false
|
|
12759
|
+
}
|
|
12760
|
+
}
|
|
12761
|
+
];
|
|
12762
|
+
}
|
|
12763
|
+
async function handleGraphTool(name, args, ctx) {
|
|
12764
|
+
if (name !== "paradigm_graph_generate") {
|
|
12765
|
+
return { handled: false, text: "" };
|
|
12766
|
+
}
|
|
12767
|
+
try {
|
|
12768
|
+
const result = buildGraphState(
|
|
12769
|
+
ctx.rootDir,
|
|
12770
|
+
args.symbols,
|
|
12771
|
+
args.groups,
|
|
12772
|
+
args.links,
|
|
12773
|
+
args.name || "Generated Graph"
|
|
12774
|
+
);
|
|
12775
|
+
const text = JSON.stringify(result, null, 2);
|
|
12776
|
+
trackToolCall(text.length, name);
|
|
12777
|
+
return { handled: true, text };
|
|
12778
|
+
} catch (err2) {
|
|
12779
|
+
const text = JSON.stringify({ error: err2.message }, null, 2);
|
|
12780
|
+
trackToolCall(text.length, name);
|
|
12781
|
+
return { handled: true, text };
|
|
12782
|
+
}
|
|
12783
|
+
}
|
|
12784
|
+
var SCAN_CATEGORY_MAP = {
|
|
12785
|
+
components: "component",
|
|
12786
|
+
flows: "flow",
|
|
12787
|
+
gates: "gate",
|
|
12788
|
+
signals: "signal",
|
|
12789
|
+
aspects: "aspect"
|
|
12790
|
+
};
|
|
12791
|
+
function loadScanIndex(rootDir) {
|
|
12792
|
+
const indexPath = path24.join(rootDir, ".paradigm", "scan-index.json");
|
|
12793
|
+
if (!fs22.existsSync(indexPath)) return [];
|
|
12794
|
+
const raw = JSON.parse(fs22.readFileSync(indexPath, "utf8"));
|
|
12795
|
+
const symbols = [];
|
|
12796
|
+
for (const [sectionKey, categoryName] of Object.entries(SCAN_CATEGORY_MAP)) {
|
|
12797
|
+
const section = raw[sectionKey];
|
|
12798
|
+
if (!section || typeof section !== "object") continue;
|
|
12799
|
+
for (const [id, sym] of Object.entries(section)) {
|
|
12800
|
+
const s = sym;
|
|
12801
|
+
symbols.push({
|
|
12802
|
+
id,
|
|
12803
|
+
name: id,
|
|
12804
|
+
category: categoryName,
|
|
12805
|
+
prefix: CATEGORY_PREFIXES[categoryName] || "#",
|
|
12806
|
+
description: s.description,
|
|
12807
|
+
path: s.path,
|
|
12808
|
+
tags: s.tags
|
|
12809
|
+
});
|
|
12810
|
+
}
|
|
12811
|
+
}
|
|
12812
|
+
return symbols;
|
|
12813
|
+
}
|
|
12814
|
+
function resolveSymbol(name, allSymbols) {
|
|
12815
|
+
const stripped = name.replace(/^[#$^!~]/, "");
|
|
12816
|
+
return allSymbols.find(
|
|
12817
|
+
(s) => s.id === stripped || s.name === stripped || s.id === name || s.name === name
|
|
12818
|
+
);
|
|
12819
|
+
}
|
|
12820
|
+
function buildGraphState(rootDir, symbolFilter, groups, links, graphName = "Generated Graph") {
|
|
12821
|
+
const allSymbols = loadScanIndex(rootDir);
|
|
12822
|
+
let included;
|
|
12823
|
+
if (symbolFilter && symbolFilter.length > 0) {
|
|
12824
|
+
included = symbolFilter.map((name) => resolveSymbol(name, allSymbols)).filter(Boolean);
|
|
12825
|
+
} else {
|
|
12826
|
+
included = allSymbols;
|
|
12827
|
+
}
|
|
12828
|
+
const nodes = [];
|
|
12829
|
+
const edges = [];
|
|
12830
|
+
const groupIdMap = /* @__PURE__ */ new Map();
|
|
12831
|
+
const assignedSymbols = /* @__PURE__ */ new Set();
|
|
12832
|
+
let nextGroupX = 0;
|
|
12833
|
+
if (groups && groups.length > 0) {
|
|
12834
|
+
for (const group of groups) {
|
|
12835
|
+
const groupId = `group-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
12836
|
+
groupIdMap.set(group.label, groupId);
|
|
12837
|
+
const memberSymbols = group.symbols.map((name) => resolveSymbol(name, included)).filter(Boolean);
|
|
12838
|
+
const cols = Math.ceil(Math.sqrt(memberSymbols.length));
|
|
12839
|
+
const rows = Math.ceil(memberSymbols.length / cols);
|
|
12840
|
+
for (let i = 0; i < memberSymbols.length; i++) {
|
|
12841
|
+
const sym = memberSymbols[i];
|
|
12842
|
+
const col = i % cols;
|
|
12843
|
+
const row = Math.floor(i / cols);
|
|
12844
|
+
const prefix = CATEGORY_PREFIXES[sym.category] || "#";
|
|
12845
|
+
nodes.push({
|
|
12846
|
+
id: `sym-${sym.id}`,
|
|
12847
|
+
type: "symbolNode",
|
|
12848
|
+
position: {
|
|
12849
|
+
x: GROUP_PADDING + col * (NODE_WIDTH + NODE_GAP),
|
|
12850
|
+
y: GROUP_HEADER + GROUP_PADDING + row * (NODE_HEIGHT + NODE_GAP)
|
|
12851
|
+
},
|
|
12852
|
+
parentId: groupId,
|
|
12853
|
+
data: {
|
|
12854
|
+
type: "symbol",
|
|
12855
|
+
symbol: sym,
|
|
12856
|
+
label: `${prefix}${sym.name}`
|
|
12857
|
+
}
|
|
12858
|
+
});
|
|
12859
|
+
assignedSymbols.add(sym.id);
|
|
12860
|
+
}
|
|
12861
|
+
const cols2 = Math.max(cols, 1);
|
|
12862
|
+
const rows2 = Math.max(rows, 1);
|
|
12863
|
+
const groupWidth = GROUP_PADDING * 2 + cols2 * NODE_WIDTH + (cols2 - 1) * NODE_GAP;
|
|
12864
|
+
const groupHeight = GROUP_HEADER + GROUP_PADDING * 2 + rows2 * NODE_HEIGHT + (rows2 - 1) * NODE_GAP;
|
|
12865
|
+
nodes.unshift({
|
|
12866
|
+
id: groupId,
|
|
12867
|
+
type: "groupNode",
|
|
12868
|
+
position: { x: nextGroupX, y: 0 },
|
|
12869
|
+
style: { width: groupWidth, height: groupHeight },
|
|
12870
|
+
data: { type: "group", label: group.label }
|
|
12871
|
+
});
|
|
12872
|
+
nextGroupX += groupWidth + GROUP_GAP;
|
|
12873
|
+
}
|
|
12874
|
+
}
|
|
12875
|
+
const ungrouped = included.filter((s) => !assignedSymbols.has(s.id));
|
|
12876
|
+
if (ungrouped.length > 0) {
|
|
12877
|
+
const startY = groups && groups.length > 0 ? 400 : 0;
|
|
12878
|
+
const cols = Math.ceil(Math.sqrt(ungrouped.length));
|
|
12879
|
+
for (let i = 0; i < ungrouped.length; i++) {
|
|
12880
|
+
const sym = ungrouped[i];
|
|
12881
|
+
const col = i % cols;
|
|
12882
|
+
const row = Math.floor(i / cols);
|
|
12883
|
+
const prefix = CATEGORY_PREFIXES[sym.category] || "#";
|
|
12884
|
+
nodes.push({
|
|
12885
|
+
id: `sym-${sym.id}`,
|
|
12886
|
+
type: "symbolNode",
|
|
12887
|
+
position: {
|
|
12888
|
+
x: col * (NODE_WIDTH + NODE_GAP),
|
|
12889
|
+
y: startY + row * (NODE_HEIGHT + NODE_GAP)
|
|
12890
|
+
},
|
|
12891
|
+
data: {
|
|
12892
|
+
type: "symbol",
|
|
12893
|
+
symbol: sym,
|
|
12894
|
+
label: `${prefix}${sym.name}`
|
|
12895
|
+
}
|
|
12896
|
+
});
|
|
12897
|
+
}
|
|
12898
|
+
}
|
|
12899
|
+
if (links && links.length > 0) {
|
|
12900
|
+
for (const link of links) {
|
|
12901
|
+
const sourceId = groupIdMap.get(link.source);
|
|
12902
|
+
const targetId = groupIdMap.get(link.target);
|
|
12903
|
+
if (sourceId && targetId) {
|
|
12904
|
+
edges.push({
|
|
12905
|
+
id: `e-${sourceId}-${targetId}`,
|
|
12906
|
+
source: sourceId,
|
|
12907
|
+
target: targetId,
|
|
12908
|
+
type: "default",
|
|
12909
|
+
label: link.label,
|
|
12910
|
+
data: { label: link.label }
|
|
12911
|
+
});
|
|
12912
|
+
}
|
|
12913
|
+
}
|
|
12914
|
+
}
|
|
12915
|
+
return {
|
|
12916
|
+
version: "1.0",
|
|
12917
|
+
name: graphName,
|
|
12918
|
+
projectId: path24.basename(rootDir),
|
|
12919
|
+
lastModified: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12920
|
+
nodes,
|
|
12921
|
+
edges
|
|
12922
|
+
};
|
|
12923
|
+
}
|
|
12924
|
+
|
|
12925
|
+
// ../paradigm-mcp/src/tools/fallback-grep.ts
|
|
12926
|
+
import * as path25 from "path";
|
|
12292
12927
|
import { execSync as execSync5 } from "child_process";
|
|
12293
12928
|
function grepForReferences(rootDir, symbol, options = {}) {
|
|
12294
12929
|
const { maxResults = 20 } = options;
|
|
@@ -12317,7 +12952,7 @@ function grepForReferences(rootDir, symbol, options = {}) {
|
|
|
12317
12952
|
const match = line.match(/^(.+?):(\d+):(.*)$/);
|
|
12318
12953
|
if (match) {
|
|
12319
12954
|
const [, filePath, lineNum, content] = match;
|
|
12320
|
-
const relativePath =
|
|
12955
|
+
const relativePath = path25.relative(rootDir, filePath);
|
|
12321
12956
|
let context2 = "unknown";
|
|
12322
12957
|
if (relativePath.includes(".purpose") || relativePath.includes("portal.yaml")) {
|
|
12323
12958
|
context2 = "purpose";
|
|
@@ -12580,6 +13215,8 @@ function registerTools(server, getContext2, reloadContext2) {
|
|
|
12580
13215
|
...getPersonaToolsList(),
|
|
12581
13216
|
// Protocol tools
|
|
12582
13217
|
...getProtocolsToolsList(),
|
|
13218
|
+
// Graph generation tool
|
|
13219
|
+
...getGraphToolsList(),
|
|
12583
13220
|
// Plugin update check
|
|
12584
13221
|
{
|
|
12585
13222
|
name: "paradigm_plugin_check",
|
|
@@ -13213,7 +13850,7 @@ Update command:
|
|
|
13213
13850
|
const { rebuildStaticFiles: rebuildStaticFiles2 } = await import("./reindex-CMZARW5K.js");
|
|
13214
13851
|
const memberResults = [];
|
|
13215
13852
|
for (const member of ctx.workspace.config.members) {
|
|
13216
|
-
const memberAbsPath =
|
|
13853
|
+
const memberAbsPath = path26.resolve(path26.dirname(ctx.workspace.workspacePath), member.path);
|
|
13217
13854
|
try {
|
|
13218
13855
|
const result = await rebuildStaticFiles2(memberAbsPath);
|
|
13219
13856
|
memberResults.push({
|
|
@@ -13402,6 +14039,15 @@ Update command:
|
|
|
13402
14039
|
};
|
|
13403
14040
|
}
|
|
13404
14041
|
}
|
|
14042
|
+
if (name === "paradigm_graph_generate") {
|
|
14043
|
+
const result = await handleGraphTool(name, args, ctx);
|
|
14044
|
+
if (result.handled) {
|
|
14045
|
+
trackToolCall(result.text.length, name);
|
|
14046
|
+
return {
|
|
14047
|
+
content: [{ type: "text", text: result.text }]
|
|
14048
|
+
};
|
|
14049
|
+
}
|
|
14050
|
+
}
|
|
13405
14051
|
if (name === "paradigm_reindex") {
|
|
13406
14052
|
const reload = reloadContext2 || (async () => {
|
|
13407
14053
|
});
|