@pathmode/mcp-server 1.4.5 → 1.6.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/README.md +23 -0
- package/dist/api-client.d.ts +18 -0
- package/dist/api-client.d.ts.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +521 -21
- package/dist/install-skills.d.ts +13 -0
- package/dist/install-skills.d.ts.map +1 -0
- package/dist/intent-compiler.d.ts +33 -0
- package/dist/intent-compiler.d.ts.map +1 -1
- package/manifest.json +2 -2
- package/package.json +5 -2
- package/skills/README.md +77 -0
- package/skills/compile-intent/SKILL.md +57 -0
- package/skills/grill-intent/SKILL.md +45 -0
- package/skills/handoff-intent/SKILL.md +51 -0
- package/skills/review-against-intent/SKILL.md +52 -0
- package/skills/setup-pathmode-workflow/SKILL.md +81 -0
- package/skills/split-intent-to-issues/SKILL.md +92 -0
- package/skills/verify-intent/SKILL.md +76 -0
package/dist/index.js
CHANGED
|
@@ -25565,7 +25565,7 @@ class Ajv {
|
|
|
25565
25565
|
constructor(opts = {}) {
|
|
25566
25566
|
this.schemas = {};
|
|
25567
25567
|
this.refs = {};
|
|
25568
|
-
this.formats =
|
|
25568
|
+
this.formats = Object.create(null);
|
|
25569
25569
|
this._compilations = new Set();
|
|
25570
25570
|
this._loading = {};
|
|
25571
25571
|
this._cache = new Map();
|
|
@@ -28017,6 +28017,7 @@ exports["default"] = def;
|
|
|
28017
28017
|
|
|
28018
28018
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
28019
28019
|
const code_1 = __nccwpck_require__(8484);
|
|
28020
|
+
const util_1 = __nccwpck_require__(4464);
|
|
28020
28021
|
const codegen_1 = __nccwpck_require__(1436);
|
|
28021
28022
|
const error = {
|
|
28022
28023
|
message: ({ schemaCode }) => (0, codegen_1.str) `must match pattern "${schemaCode}"`,
|
|
@@ -28029,11 +28030,19 @@ const def = {
|
|
|
28029
28030
|
$data: true,
|
|
28030
28031
|
error,
|
|
28031
28032
|
code(cxt) {
|
|
28032
|
-
const { data, $data, schema, schemaCode, it } = cxt;
|
|
28033
|
-
// TODO regexp should be wrapped in try/catchs
|
|
28033
|
+
const { gen, data, $data, schema, schemaCode, it } = cxt;
|
|
28034
28034
|
const u = it.opts.unicodeRegExp ? "u" : "";
|
|
28035
|
-
|
|
28036
|
-
|
|
28035
|
+
if ($data) {
|
|
28036
|
+
const { regExp } = it.opts.code;
|
|
28037
|
+
const regExpCode = regExp.code === "new RegExp" ? (0, codegen_1._) `new RegExp` : (0, util_1.useFunc)(gen, regExp);
|
|
28038
|
+
const valid = gen.let("valid");
|
|
28039
|
+
gen.try(() => gen.assign(valid, (0, codegen_1._) `${regExpCode}(${schemaCode}, ${u}).test(${data})`), () => gen.assign(valid, false));
|
|
28040
|
+
cxt.fail$data((0, codegen_1._) `!${valid}`);
|
|
28041
|
+
}
|
|
28042
|
+
else {
|
|
28043
|
+
const regExp = (0, code_1.usePattern)(cxt, schema);
|
|
28044
|
+
cxt.fail$data((0, codegen_1._) `!${regExp}.test(${data})`);
|
|
28045
|
+
}
|
|
28037
28046
|
},
|
|
28038
28047
|
};
|
|
28039
28048
|
exports["default"] = def;
|
|
@@ -33680,6 +33689,198 @@ class PathmodeClient {
|
|
|
33680
33689
|
exports.PathmodeClient = PathmodeClient;
|
|
33681
33690
|
|
|
33682
33691
|
|
|
33692
|
+
/***/ }),
|
|
33693
|
+
|
|
33694
|
+
/***/ 3783:
|
|
33695
|
+
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
|
33696
|
+
|
|
33697
|
+
"use strict";
|
|
33698
|
+
|
|
33699
|
+
/**
|
|
33700
|
+
* Pathmode MCP Install Skills Command
|
|
33701
|
+
*
|
|
33702
|
+
* Copies the bundled Claude Code skill pack into .claude/skills/
|
|
33703
|
+
* (project-local, default) or ~/.claude/skills/ (global, with --global).
|
|
33704
|
+
*
|
|
33705
|
+
* Usage:
|
|
33706
|
+
* npx @pathmode/mcp-server install-skills
|
|
33707
|
+
* npx @pathmode/mcp-server install-skills --global
|
|
33708
|
+
* npx @pathmode/mcp-server install-skills --force
|
|
33709
|
+
*/
|
|
33710
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
33711
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
33712
|
+
};
|
|
33713
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
33714
|
+
exports.isInstallSkillsCommand = isInstallSkillsCommand;
|
|
33715
|
+
exports.runInstallSkills = runInstallSkills;
|
|
33716
|
+
const fs_1 = __importDefault(__nccwpck_require__(9896));
|
|
33717
|
+
const path_1 = __importDefault(__nccwpck_require__(6928));
|
|
33718
|
+
const os_1 = __importDefault(__nccwpck_require__(857));
|
|
33719
|
+
const BOLD = '\x1b[1m';
|
|
33720
|
+
const DIM = '\x1b[2m';
|
|
33721
|
+
const GREEN = '\x1b[32m';
|
|
33722
|
+
const RED = '\x1b[31m';
|
|
33723
|
+
const YELLOW = '\x1b[33m';
|
|
33724
|
+
const CYAN = '\x1b[36m';
|
|
33725
|
+
const RESET = '\x1b[0m';
|
|
33726
|
+
function log(msg) { console.log(msg); }
|
|
33727
|
+
function success(msg) { console.log(` ${GREEN}✓${RESET} ${msg}`); }
|
|
33728
|
+
function warn(msg) { console.log(` ${YELLOW}!${RESET} ${msg}`); }
|
|
33729
|
+
function fail(msg) { console.log(` ${RED}✗${RESET} ${msg}`); }
|
|
33730
|
+
function isInstallSkillsCommand(argv = process.argv) {
|
|
33731
|
+
return argv.includes('install-skills');
|
|
33732
|
+
}
|
|
33733
|
+
function getInstallSkillsArgs(argv = process.argv) {
|
|
33734
|
+
const idx = argv.findIndex(a => a === 'install-skills');
|
|
33735
|
+
return idx === -1 ? [] : argv.slice(idx + 1);
|
|
33736
|
+
}
|
|
33737
|
+
function shortenPath(p) {
|
|
33738
|
+
const home = os_1.default.homedir();
|
|
33739
|
+
return p.startsWith(home) ? '~' + p.slice(home.length) : p;
|
|
33740
|
+
}
|
|
33741
|
+
/**
|
|
33742
|
+
* Locate the bundled skills/ directory.
|
|
33743
|
+
*
|
|
33744
|
+
* When installed via npm, this file is at <pkg>/dist/install-skills.js
|
|
33745
|
+
* and skills/ sits at <pkg>/skills/. When running from source (ts-node),
|
|
33746
|
+
* this file is at <pkg>/src/install-skills.ts and skills/ is at <pkg>/skills/.
|
|
33747
|
+
*
|
|
33748
|
+
* Either way, going up one level from __dirname and joining "skills" finds it.
|
|
33749
|
+
*/
|
|
33750
|
+
function findSkillsDir() {
|
|
33751
|
+
const candidates = [
|
|
33752
|
+
path_1.default.resolve(__dirname, '..', 'skills'), // npm install layout (dist/ -> ../skills)
|
|
33753
|
+
path_1.default.resolve(__dirname, '..', '..', 'skills'), // edge case if bundled deeper
|
|
33754
|
+
];
|
|
33755
|
+
for (const c of candidates) {
|
|
33756
|
+
if (!fs_1.default.existsSync(c) || !fs_1.default.statSync(c).isDirectory())
|
|
33757
|
+
continue;
|
|
33758
|
+
// Sanity check: directory must contain at least one <name>/SKILL.md
|
|
33759
|
+
const entries = fs_1.default.readdirSync(c);
|
|
33760
|
+
const hasSkill = entries.some(e => fs_1.default.existsSync(path_1.default.join(c, e, 'SKILL.md')));
|
|
33761
|
+
if (hasSkill)
|
|
33762
|
+
return c;
|
|
33763
|
+
}
|
|
33764
|
+
return null;
|
|
33765
|
+
}
|
|
33766
|
+
function copyRecursive(src, dest) {
|
|
33767
|
+
if (!fs_1.default.existsSync(dest)) {
|
|
33768
|
+
fs_1.default.mkdirSync(dest, { recursive: true });
|
|
33769
|
+
}
|
|
33770
|
+
const entries = fs_1.default.readdirSync(src, { withFileTypes: true });
|
|
33771
|
+
for (const e of entries) {
|
|
33772
|
+
const srcPath = path_1.default.join(src, e.name);
|
|
33773
|
+
const destPath = path_1.default.join(dest, e.name);
|
|
33774
|
+
if (e.isDirectory()) {
|
|
33775
|
+
copyRecursive(srcPath, destPath);
|
|
33776
|
+
}
|
|
33777
|
+
else {
|
|
33778
|
+
fs_1.default.copyFileSync(srcPath, destPath);
|
|
33779
|
+
}
|
|
33780
|
+
}
|
|
33781
|
+
}
|
|
33782
|
+
async function runInstallSkills() {
|
|
33783
|
+
const args = getInstallSkillsArgs();
|
|
33784
|
+
const isGlobal = args.includes('--global');
|
|
33785
|
+
const isForce = args.includes('--force');
|
|
33786
|
+
const wantsHelp = args.includes('--help') || args.includes('-h');
|
|
33787
|
+
log('');
|
|
33788
|
+
log(`${BOLD}Pathmode Skills Install${RESET}`);
|
|
33789
|
+
log(`${DIM}──────────────────────${RESET}`);
|
|
33790
|
+
log('');
|
|
33791
|
+
if (wantsHelp) {
|
|
33792
|
+
log(`Usage: ${CYAN}npx @pathmode/mcp-server install-skills${RESET} [options]`);
|
|
33793
|
+
log('');
|
|
33794
|
+
log('Options:');
|
|
33795
|
+
log(` ${BOLD}--global${RESET} Install into ~/.claude/skills/ instead of ./.claude/skills/`);
|
|
33796
|
+
log(` ${BOLD}--force${RESET} Overwrite existing skill directories`);
|
|
33797
|
+
log(` ${BOLD}--help${RESET} Show this message`);
|
|
33798
|
+
log('');
|
|
33799
|
+
log('Copies the bundled Claude Code skill pack into your skills directory.');
|
|
33800
|
+
log('Skills auto-trigger when your natural-language request matches their description —');
|
|
33801
|
+
log('no slash commands needed.');
|
|
33802
|
+
log('');
|
|
33803
|
+
return;
|
|
33804
|
+
}
|
|
33805
|
+
const skillsDir = findSkillsDir();
|
|
33806
|
+
if (!skillsDir) {
|
|
33807
|
+
fail('Could not locate the bundled skills/ directory.');
|
|
33808
|
+
log(` Expected to find it at <package-root>/skills/ relative to ${shortenPath(__dirname)}.`);
|
|
33809
|
+
log(` If you cloned this repo and are running from source, ensure ${BOLD}skills/${RESET} exists at the package root.`);
|
|
33810
|
+
log('');
|
|
33811
|
+
process.exit(1);
|
|
33812
|
+
}
|
|
33813
|
+
const targetDir = isGlobal
|
|
33814
|
+
? path_1.default.join(os_1.default.homedir(), '.claude', 'skills')
|
|
33815
|
+
: path_1.default.join(process.cwd(), '.claude', 'skills');
|
|
33816
|
+
log(` Source: ${DIM}${shortenPath(skillsDir)}${RESET}`);
|
|
33817
|
+
log(` Target: ${DIM}${shortenPath(targetDir)}${RESET}`);
|
|
33818
|
+
log('');
|
|
33819
|
+
if (!fs_1.default.existsSync(targetDir)) {
|
|
33820
|
+
try {
|
|
33821
|
+
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
33822
|
+
}
|
|
33823
|
+
catch (err) {
|
|
33824
|
+
fail(`Could not create target directory: ${err.message}`);
|
|
33825
|
+
log('');
|
|
33826
|
+
process.exit(1);
|
|
33827
|
+
}
|
|
33828
|
+
}
|
|
33829
|
+
const entries = fs_1.default.readdirSync(skillsDir, { withFileTypes: true });
|
|
33830
|
+
let installed = 0;
|
|
33831
|
+
let skipped = 0;
|
|
33832
|
+
let overwritten = 0;
|
|
33833
|
+
for (const e of entries) {
|
|
33834
|
+
if (!e.isDirectory())
|
|
33835
|
+
continue;
|
|
33836
|
+
// Only copy directories that contain a SKILL.md
|
|
33837
|
+
const skillFile = path_1.default.join(skillsDir, e.name, 'SKILL.md');
|
|
33838
|
+
if (!fs_1.default.existsSync(skillFile))
|
|
33839
|
+
continue;
|
|
33840
|
+
const destPath = path_1.default.join(targetDir, e.name);
|
|
33841
|
+
const existsAlready = fs_1.default.existsSync(destPath);
|
|
33842
|
+
if (existsAlready && !isForce) {
|
|
33843
|
+
warn(`${e.name} ${DIM}(already installed — pass --force to overwrite)${RESET}`);
|
|
33844
|
+
skipped++;
|
|
33845
|
+
continue;
|
|
33846
|
+
}
|
|
33847
|
+
try {
|
|
33848
|
+
if (existsAlready && isForce) {
|
|
33849
|
+
fs_1.default.rmSync(destPath, { recursive: true, force: true });
|
|
33850
|
+
}
|
|
33851
|
+
copyRecursive(path_1.default.join(skillsDir, e.name), destPath);
|
|
33852
|
+
success(e.name);
|
|
33853
|
+
if (existsAlready)
|
|
33854
|
+
overwritten++;
|
|
33855
|
+
else
|
|
33856
|
+
installed++;
|
|
33857
|
+
}
|
|
33858
|
+
catch (err) {
|
|
33859
|
+
fail(`${e.name} — ${err.message}`);
|
|
33860
|
+
}
|
|
33861
|
+
}
|
|
33862
|
+
log('');
|
|
33863
|
+
const lines = [];
|
|
33864
|
+
if (installed > 0)
|
|
33865
|
+
lines.push(`${BOLD}${installed}${RESET} installed`);
|
|
33866
|
+
if (overwritten > 0)
|
|
33867
|
+
lines.push(`${BOLD}${overwritten}${RESET} updated`);
|
|
33868
|
+
if (skipped > 0)
|
|
33869
|
+
lines.push(`${BOLD}${skipped}${RESET} skipped`);
|
|
33870
|
+
log(` ${lines.join(' · ') || 'No skills processed.'}`);
|
|
33871
|
+
log('');
|
|
33872
|
+
if (installed + overwritten > 0) {
|
|
33873
|
+
log(` ${BOLD}Next:${RESET} Restart Claude Code so the new skills register at session start.`);
|
|
33874
|
+
log(` Then try: ${CYAN}"help me write a spec for [your problem]"${RESET}`);
|
|
33875
|
+
log('');
|
|
33876
|
+
}
|
|
33877
|
+
else if (skipped > 0) {
|
|
33878
|
+
log(` All skills already installed. Run with ${BOLD}--force${RESET} to overwrite.`);
|
|
33879
|
+
log('');
|
|
33880
|
+
}
|
|
33881
|
+
}
|
|
33882
|
+
|
|
33883
|
+
|
|
33683
33884
|
/***/ }),
|
|
33684
33885
|
|
|
33685
33886
|
/***/ 6488:
|
|
@@ -33702,6 +33903,7 @@ exports.getCompileIntentPrompt = getCompileIntentPrompt;
|
|
|
33702
33903
|
exports.formatIntentMd = formatIntentMd;
|
|
33703
33904
|
exports.formatCursorRules = formatCursorRules;
|
|
33704
33905
|
exports.formatClaudeMdSection = formatClaudeMdSection;
|
|
33906
|
+
exports.formatOutcomeRubric = formatOutcomeRubric;
|
|
33705
33907
|
/** Extract text from a string or structured outcome. */
|
|
33706
33908
|
function getOutcomeText(o) {
|
|
33707
33909
|
return typeof o === 'string' ? o : o.text;
|
|
@@ -34057,6 +34259,224 @@ function formatClaudeMdSection(spec) {
|
|
|
34057
34259
|
sections.push('<!-- PATHMODE:END -->');
|
|
34058
34260
|
return sections.join('\n\n');
|
|
34059
34261
|
}
|
|
34262
|
+
function getOutcomePriority(o) {
|
|
34263
|
+
return typeof o === 'string' ? undefined : o.priority;
|
|
34264
|
+
}
|
|
34265
|
+
/** Map an outcome priority to grader-facing strength language. */
|
|
34266
|
+
function priorityToBar(priority) {
|
|
34267
|
+
switch (priority) {
|
|
34268
|
+
case 'should': return 'Expected';
|
|
34269
|
+
case 'could': return 'Optional — note if present, do not fail if absent';
|
|
34270
|
+
case 'must':
|
|
34271
|
+
default: return 'Required';
|
|
34272
|
+
}
|
|
34273
|
+
}
|
|
34274
|
+
/**
|
|
34275
|
+
* Dedup constraint strings by a normalized key — strips trailing parenthetical
|
|
34276
|
+
* refs (e.g. "(CON-2)"), trailing punctuation, and case, so an intent constraint
|
|
34277
|
+
* and its constitution twin collapse to one. Keeps the first (more specific) form.
|
|
34278
|
+
*/
|
|
34279
|
+
function dedupeByNormalized(items) {
|
|
34280
|
+
const seen = new Set();
|
|
34281
|
+
const out = [];
|
|
34282
|
+
for (const raw of items) {
|
|
34283
|
+
const item = raw?.trim();
|
|
34284
|
+
if (!item)
|
|
34285
|
+
continue;
|
|
34286
|
+
const key = item
|
|
34287
|
+
.toLowerCase()
|
|
34288
|
+
.replace(/\s*\([^)]*\)\s*/g, ' ')
|
|
34289
|
+
.replace(/[.\s]+$/g, '')
|
|
34290
|
+
.replace(/\s+/g, ' ')
|
|
34291
|
+
.trim();
|
|
34292
|
+
if (seen.has(key))
|
|
34293
|
+
continue;
|
|
34294
|
+
seen.add(key);
|
|
34295
|
+
out.push(item);
|
|
34296
|
+
}
|
|
34297
|
+
return out;
|
|
34298
|
+
}
|
|
34299
|
+
/**
|
|
34300
|
+
* Build the writer-facing task description — what the agent reads. Maps to the
|
|
34301
|
+
* `description` field of a `user.define_outcome` event.
|
|
34302
|
+
*/
|
|
34303
|
+
function buildWriterDescription(spec) {
|
|
34304
|
+
const sections = [];
|
|
34305
|
+
sections.push(`Deliver: ${spec.title || 'Untitled intent'}.`);
|
|
34306
|
+
if (spec.objective) {
|
|
34307
|
+
sections.push('');
|
|
34308
|
+
sections.push(`Why this matters: ${spec.objective}`);
|
|
34309
|
+
}
|
|
34310
|
+
if (spec.scope?.inScope?.length) {
|
|
34311
|
+
sections.push('');
|
|
34312
|
+
sections.push('In scope:');
|
|
34313
|
+
for (const item of spec.scope.inScope)
|
|
34314
|
+
sections.push(`- ${item}`);
|
|
34315
|
+
}
|
|
34316
|
+
if (spec.scope?.outOfScope?.length) {
|
|
34317
|
+
sections.push('');
|
|
34318
|
+
sections.push('Out of scope — do not do these:');
|
|
34319
|
+
for (const item of spec.scope.outOfScope)
|
|
34320
|
+
sections.push(`- ${item}`);
|
|
34321
|
+
}
|
|
34322
|
+
const ic = spec.implementationContext;
|
|
34323
|
+
if (ic?.relevantAreas?.length) {
|
|
34324
|
+
sections.push('');
|
|
34325
|
+
sections.push('Relevant areas of the codebase:');
|
|
34326
|
+
for (const a of ic.relevantAreas) {
|
|
34327
|
+
if (a?.path?.trim())
|
|
34328
|
+
sections.push(`- ${a.path}${a.reason?.trim() ? ` — ${a.reason}` : ''}`);
|
|
34329
|
+
}
|
|
34330
|
+
}
|
|
34331
|
+
if (ic?.currentBehavior?.trim()) {
|
|
34332
|
+
sections.push('');
|
|
34333
|
+
sections.push(`Current behavior: ${ic.currentBehavior.trim()}`);
|
|
34334
|
+
}
|
|
34335
|
+
sections.push('');
|
|
34336
|
+
sections.push('Write your deliverables to /mnt/session/outputs/. Iterate until the grader is satisfied.');
|
|
34337
|
+
return sections.join('\n');
|
|
34338
|
+
}
|
|
34339
|
+
/**
|
|
34340
|
+
* Build the grader-facing rubric — what the independent grader reads. Maps to the
|
|
34341
|
+
* `rubric` field of a `user.define_outcome` event. Every criterion is written to
|
|
34342
|
+
* force the grader to find concrete evidence rather than trust the writer.
|
|
34343
|
+
*/
|
|
34344
|
+
function buildGraderRubric(spec, opts = {}) {
|
|
34345
|
+
const sections = [];
|
|
34346
|
+
sections.push(`You are grading the artifact(s) in /mnt/session/outputs/ for: "${spec.title || 'Untitled intent'}".`);
|
|
34347
|
+
sections.push('Score each criterion below independently. PASS a criterion only when you can point to concrete evidence in the artifact — a measured value, a visible state, a passing test, or a quoted line. The writer asserting it is done is NOT evidence.');
|
|
34348
|
+
// Outcomes → coverage criteria
|
|
34349
|
+
const outcomes = (spec.outcomes ?? []).filter((o) => getOutcomeText(o)?.trim());
|
|
34350
|
+
if (outcomes.length) {
|
|
34351
|
+
sections.push('');
|
|
34352
|
+
sections.push('## Outcomes (coverage)');
|
|
34353
|
+
let i = 1;
|
|
34354
|
+
for (const o of outcomes) {
|
|
34355
|
+
const bar = priorityToBar(getOutcomePriority(o));
|
|
34356
|
+
sections.push(`${i}. [${bar}] ${getOutcomeText(o)}`);
|
|
34357
|
+
sections.push(' - Evidence: point to the specific value, state, or test result in the artifact that proves this. If you cannot, mark FAIL.');
|
|
34358
|
+
i++;
|
|
34359
|
+
}
|
|
34360
|
+
}
|
|
34361
|
+
// Edge cases → must-handle criteria (structured edge cases + implementation-context risks)
|
|
34362
|
+
const edgeCases = (spec.edgeCases ?? []).filter((ec) => ec.scenario?.trim() || ec.expectedBehavior?.trim());
|
|
34363
|
+
const risks = (spec.implementationContext?.risks ?? []).filter((r) => r?.trim());
|
|
34364
|
+
if (edgeCases.length || risks.length) {
|
|
34365
|
+
sections.push('');
|
|
34366
|
+
sections.push('## Edge cases (must handle)');
|
|
34367
|
+
for (const ec of edgeCases) {
|
|
34368
|
+
sections.push(`- ${ec.scenario} → expected: ${ec.expectedBehavior}`);
|
|
34369
|
+
sections.push(` - Evidence: locate or construct the "${ec.scenario}" condition and confirm the expected behavior occurs. Absent handling = FAIL.`);
|
|
34370
|
+
}
|
|
34371
|
+
for (const r of risks) {
|
|
34372
|
+
sections.push(`- Guard against: ${r}`);
|
|
34373
|
+
sections.push(' - Evidence: show the artifact handles this failure mode — a test, a guard clause, or a visible safe state. Unaddressed = FAIL.');
|
|
34374
|
+
}
|
|
34375
|
+
}
|
|
34376
|
+
// Constraints + constitution + out-of-scope → out-of-bounds / no-fire list
|
|
34377
|
+
const outOfBounds = [];
|
|
34378
|
+
if (spec.constraints?.length)
|
|
34379
|
+
outOfBounds.push(...spec.constraints);
|
|
34380
|
+
if (opts.constitutionRules?.length)
|
|
34381
|
+
outOfBounds.push(...opts.constitutionRules);
|
|
34382
|
+
if (spec.scope?.outOfScope?.length) {
|
|
34383
|
+
for (const item of spec.scope.outOfScope)
|
|
34384
|
+
outOfBounds.push(`Stays out of scope: ${item}`);
|
|
34385
|
+
}
|
|
34386
|
+
const cleanedBounds = dedupeByNormalized(outOfBounds);
|
|
34387
|
+
if (cleanedBounds.length) {
|
|
34388
|
+
sections.push('');
|
|
34389
|
+
sections.push('## Constraints (out of bounds — FAIL the artifact if any are violated)');
|
|
34390
|
+
for (const c of cleanedBounds)
|
|
34391
|
+
sections.push(`- ${c}`);
|
|
34392
|
+
}
|
|
34393
|
+
// Verification → procedures the grader must run to produce evidence
|
|
34394
|
+
const v = spec.verification;
|
|
34395
|
+
const checks = [];
|
|
34396
|
+
for (const t of v?.e2eTests ?? [])
|
|
34397
|
+
if (t?.trim())
|
|
34398
|
+
checks.push(`[e2e] ${t}`);
|
|
34399
|
+
for (const t of v?.unitTests ?? [])
|
|
34400
|
+
if (t?.trim())
|
|
34401
|
+
checks.push(`[unit] ${t}`);
|
|
34402
|
+
for (const t of v?.manualChecks ?? [])
|
|
34403
|
+
if (t?.trim())
|
|
34404
|
+
checks.push(`[manual] ${t}`);
|
|
34405
|
+
for (const t of spec.implementationContext?.verificationSuggestions ?? [])
|
|
34406
|
+
if (t?.trim())
|
|
34407
|
+
checks.push(`[suggested] ${t}`);
|
|
34408
|
+
if (checks.length) {
|
|
34409
|
+
sections.push('');
|
|
34410
|
+
sections.push('## Checks to run (produce the evidence yourself)');
|
|
34411
|
+
for (const c of checks)
|
|
34412
|
+
sections.push(`- ${c}`);
|
|
34413
|
+
}
|
|
34414
|
+
// Health metrics → observable-signal criteria
|
|
34415
|
+
const metrics = (spec.healthMetrics ?? []).filter((m) => m?.trim());
|
|
34416
|
+
if (metrics.length) {
|
|
34417
|
+
sections.push('');
|
|
34418
|
+
sections.push('## Observable signals');
|
|
34419
|
+
for (const m of metrics) {
|
|
34420
|
+
sections.push(`- The artifact leaves a way to measure: ${m}`);
|
|
34421
|
+
}
|
|
34422
|
+
}
|
|
34423
|
+
// Grader output format — proven shape from the Outcomes cookbook
|
|
34424
|
+
sections.push('');
|
|
34425
|
+
sections.push('## Output format');
|
|
34426
|
+
sections.push('Line 1: a scoreboard — "Outcomes X/Y met. Constraints OK|VIOLATED. Edge cases X/Y."');
|
|
34427
|
+
sections.push('Then one bullet per FAILED item only: "<section> <item> — FAIL. <what is missing and what to change>." One sentence per bullet.');
|
|
34428
|
+
sections.push('Do not fail the artifact for style preferences, pre-existing issues outside this intent, or anything not listed above.');
|
|
34429
|
+
return sections.join('\n');
|
|
34430
|
+
}
|
|
34431
|
+
/**
|
|
34432
|
+
* Generate a Claude Managed Agents "Outcomes" rubric document for an intent.
|
|
34433
|
+
*
|
|
34434
|
+
* Outcomes (`user.define_outcome`) takes two separate fields: a `description`
|
|
34435
|
+
* the writer agent reads, and a `rubric` the independent grader reads. This
|
|
34436
|
+
* exporter renders both — clearly separated so they paste straight into the API —
|
|
34437
|
+
* turning the intent's outcomes, edge cases, constraints, constitution rules and
|
|
34438
|
+
* verification into checkable, evidence-forcing grader criteria.
|
|
34439
|
+
*
|
|
34440
|
+
* Docs: https://platform.claude.com/docs/en/managed-agents/define-outcomes
|
|
34441
|
+
*/
|
|
34442
|
+
function formatOutcomeRubric(spec, opts = {}) {
|
|
34443
|
+
const maxIterations = opts.maxIterations ?? 5;
|
|
34444
|
+
const description = buildWriterDescription(spec);
|
|
34445
|
+
const rubric = buildGraderRubric(spec, opts);
|
|
34446
|
+
const hasVerification = [
|
|
34447
|
+
...(spec.verification?.e2eTests ?? []),
|
|
34448
|
+
...(spec.verification?.unitTests ?? []),
|
|
34449
|
+
...(spec.verification?.manualChecks ?? []),
|
|
34450
|
+
].some((t) => t?.trim());
|
|
34451
|
+
const doc = [];
|
|
34452
|
+
doc.push('<!-- Pathmode → Claude Managed Agents: Outcomes rubric -->');
|
|
34453
|
+
doc.push(`<!-- Generated ${new Date().toISOString()} | pathmode.io -->`);
|
|
34454
|
+
doc.push('');
|
|
34455
|
+
doc.push('# How to use this');
|
|
34456
|
+
doc.push('');
|
|
34457
|
+
doc.push('Send a `user.define_outcome` event to a Managed Agents session (beta header `managed-agents-2026-04-01`):');
|
|
34458
|
+
doc.push('- Put **Writer Description** into the `description` field (the agent reads this).');
|
|
34459
|
+
doc.push('- Put **Grader Rubric** into the `rubric` field (the independent grader reads this).');
|
|
34460
|
+
doc.push(`- Suggested \`max_iterations\`: ${maxIterations} (Outcomes default 3, max 20).`);
|
|
34461
|
+
if (!hasVerification) {
|
|
34462
|
+
doc.push('');
|
|
34463
|
+
doc.push('> ⚠ This intent has no verification defined, so grader criteria fall back to generic evidence requirements. Run `verify-intent` to sharpen the checks.');
|
|
34464
|
+
}
|
|
34465
|
+
doc.push('');
|
|
34466
|
+
doc.push('---');
|
|
34467
|
+
doc.push('');
|
|
34468
|
+
doc.push('# Writer Description');
|
|
34469
|
+
doc.push('');
|
|
34470
|
+
doc.push(description);
|
|
34471
|
+
doc.push('');
|
|
34472
|
+
doc.push('---');
|
|
34473
|
+
doc.push('');
|
|
34474
|
+
doc.push('# Grader Rubric');
|
|
34475
|
+
doc.push('');
|
|
34476
|
+
doc.push(rubric);
|
|
34477
|
+
doc.push('');
|
|
34478
|
+
return doc.join('\n');
|
|
34479
|
+
}
|
|
34060
34480
|
|
|
34061
34481
|
|
|
34062
34482
|
/***/ }),
|
|
@@ -64125,9 +64545,11 @@ var exports = __webpack_exports__;
|
|
|
64125
64545
|
* Connects Claude Code, Cursor, and other AI agents to your Intent Layer.
|
|
64126
64546
|
*
|
|
64127
64547
|
* Usage:
|
|
64128
|
-
* npx @pathmode/mcp-server
|
|
64129
|
-
* npx @pathmode/mcp-server --local
|
|
64130
|
-
* npx @pathmode/mcp-server setup pm_live_xxx
|
|
64548
|
+
* npx @pathmode/mcp-server # Cloud mode (uses ~/.pathmode/config.json)
|
|
64549
|
+
* npx @pathmode/mcp-server --local # Local mode (reads intent.md from cwd)
|
|
64550
|
+
* npx @pathmode/mcp-server setup pm_live_xxx # Auto-configure your tools
|
|
64551
|
+
* npx @pathmode/mcp-server install-skills # Copy the skill pack into .claude/skills/
|
|
64552
|
+
* npx @pathmode/mcp-server install-skills --global # Install into ~/.claude/skills/ instead
|
|
64131
64553
|
*
|
|
64132
64554
|
* The Intent Compiler (compile-intent prompt, intent_save, intent_export tools)
|
|
64133
64555
|
* works without an API key — zero-config intent spec building in Claude Code.
|
|
@@ -64166,16 +64588,23 @@ const api_client_1 = __nccwpck_require__(7475);
|
|
|
64166
64588
|
const local_reader_1 = __nccwpck_require__(3518);
|
|
64167
64589
|
const intent_compiler_1 = __nccwpck_require__(6488);
|
|
64168
64590
|
const setup_1 = __nccwpck_require__(8294);
|
|
64591
|
+
const install_skills_1 = __nccwpck_require__(3783);
|
|
64169
64592
|
// ─── Subcommand routing ───────────────────────────────────────
|
|
64170
|
-
// `setup`
|
|
64171
|
-
// before StdioServerTransport claims stdout for JSON-RPC.
|
|
64172
|
-
// We call startMcpServer() only when NOT in
|
|
64593
|
+
// Human-readable subcommands (`setup`, `install-skills`) use stdout
|
|
64594
|
+
// and must run before StdioServerTransport claims stdout for JSON-RPC.
|
|
64595
|
+
// We call startMcpServer() only when NOT in a subcommand.
|
|
64173
64596
|
if ((0, setup_1.isSetupCommand)()) {
|
|
64174
64597
|
(0, setup_1.runSetup)().then(() => process.exit(0)).catch((err) => {
|
|
64175
64598
|
console.error(err);
|
|
64176
64599
|
process.exit(1);
|
|
64177
64600
|
});
|
|
64178
64601
|
}
|
|
64602
|
+
else if ((0, install_skills_1.isInstallSkillsCommand)()) {
|
|
64603
|
+
(0, install_skills_1.runInstallSkills)().then(() => process.exit(0)).catch((err) => {
|
|
64604
|
+
console.error(err);
|
|
64605
|
+
process.exit(1);
|
|
64606
|
+
});
|
|
64607
|
+
}
|
|
64179
64608
|
else {
|
|
64180
64609
|
startMcpServer();
|
|
64181
64610
|
}
|
|
@@ -64199,7 +64628,7 @@ function startMcpServer() {
|
|
|
64199
64628
|
// ============================================================
|
|
64200
64629
|
const server = new mcp_js_1.McpServer({
|
|
64201
64630
|
name: 'pathmode',
|
|
64202
|
-
version: '1.4.
|
|
64631
|
+
version: '1.4.5',
|
|
64203
64632
|
});
|
|
64204
64633
|
// Annotation presets
|
|
64205
64634
|
const READ_ONLY = { readOnlyHint: true, openWorldHint: true };
|
|
@@ -64214,6 +64643,28 @@ function startMcpServer() {
|
|
|
64214
64643
|
class CloudClientError extends Error {
|
|
64215
64644
|
constructor() { super(CLOUD_REQUIRED_MSG); this.name = 'CloudClientError'; }
|
|
64216
64645
|
}
|
|
64646
|
+
function normalizeText(value) {
|
|
64647
|
+
return (value || '').trim();
|
|
64648
|
+
}
|
|
64649
|
+
// Keep this selection heuristic aligned with the canonical readiness rules in
|
|
64650
|
+
// /Users/jannelammi/code/Pathmode/lib/intentReadiness.ts. The MCP package cannot
|
|
64651
|
+
// import app/lib code directly, so this mirrors only the minimum logic needed
|
|
64652
|
+
// for get_current_intent fallback behavior.
|
|
64653
|
+
function isPlaceholderIntent(intent) {
|
|
64654
|
+
const title = normalizeText(intent.title).toLowerCase();
|
|
64655
|
+
const objective = normalizeText(intent.objective);
|
|
64656
|
+
const outcomeCount = (intent.outcomes || []).filter((outcome) => normalizeText(typeof outcome === 'string' ? outcome : outcome?.text).length > 0).length;
|
|
64657
|
+
return title === 'new intent' || title === 'untitled intent' || objective.length < 15 || outcomeCount === 0;
|
|
64658
|
+
}
|
|
64659
|
+
function pickCurrentIntent(intents) {
|
|
64660
|
+
const approvedReady = intents.find((intent) => intent.status === 'approved' && !isPlaceholderIntent(intent));
|
|
64661
|
+
if (approvedReady)
|
|
64662
|
+
return approvedReady;
|
|
64663
|
+
const anyReady = intents.find((intent) => !isPlaceholderIntent(intent));
|
|
64664
|
+
if (anyReady)
|
|
64665
|
+
return anyReady;
|
|
64666
|
+
return intents[0];
|
|
64667
|
+
}
|
|
64217
64668
|
// Note: The MCP SDK catches errors thrown in tool handlers and returns them as
|
|
64218
64669
|
// error text results. CloudClientError thrown by requireCloudClient() will
|
|
64219
64670
|
// surface its message to the client without crashing the server.
|
|
@@ -64222,14 +64673,14 @@ function startMcpServer() {
|
|
|
64222
64673
|
// ============================================================
|
|
64223
64674
|
server.registerTool('get_current_intent', {
|
|
64224
64675
|
title: 'Get Current Intent',
|
|
64225
|
-
description: 'Get the currently active intent
|
|
64676
|
+
description: 'Get the currently active intent, preferring approved intents with real objective/outcome content over empty stubs. Returns the full IntentSpec with objective, outcomes, constraints, and edge cases.',
|
|
64226
64677
|
inputSchema: { status: zod_1.z.string().optional().describe('Filter by status: draft, validated, approved, shipped, verified') },
|
|
64227
64678
|
annotations: READ_ONLY,
|
|
64228
64679
|
}, async ({ status }) => {
|
|
64229
64680
|
if (isLocalMode) {
|
|
64230
64681
|
const intents = (0, local_reader_1.readLocalIntents)();
|
|
64231
64682
|
const filtered = status ? intents.filter(i => i.status === status) : intents;
|
|
64232
|
-
const current = filtered
|
|
64683
|
+
const current = pickCurrentIntent(filtered);
|
|
64233
64684
|
if (!current) {
|
|
64234
64685
|
return { content: [{ type: 'text', text: 'No intents found locally.' }] };
|
|
64235
64686
|
}
|
|
@@ -64242,9 +64693,9 @@ function startMcpServer() {
|
|
|
64242
64693
|
if (allIntents.length === 0) {
|
|
64243
64694
|
return { content: [{ type: 'text', text: 'No intents found in workspace.' }] };
|
|
64244
64695
|
}
|
|
64245
|
-
return { content: [{ type: 'text', text: JSON.stringify(allIntents
|
|
64696
|
+
return { content: [{ type: 'text', text: JSON.stringify(pickCurrentIntent(allIntents), null, 2) }] };
|
|
64246
64697
|
}
|
|
64247
|
-
return { content: [{ type: 'text', text: JSON.stringify(intents
|
|
64698
|
+
return { content: [{ type: 'text', text: JSON.stringify(pickCurrentIntent(intents), null, 2) }] };
|
|
64248
64699
|
});
|
|
64249
64700
|
server.registerTool('list_intents', {
|
|
64250
64701
|
title: 'List Intents',
|
|
@@ -64540,11 +64991,27 @@ function startMcpServer() {
|
|
|
64540
64991
|
return { content: [{ type: 'text', text: `Graph analysis failed: ${e.message}` }] };
|
|
64541
64992
|
}
|
|
64542
64993
|
});
|
|
64994
|
+
/** Map a cloud ApiIntent into the IntentFields shape the formatters consume. */
|
|
64995
|
+
function apiIntentToFields(intent) {
|
|
64996
|
+
const v = (intent.verification || {});
|
|
64997
|
+
return {
|
|
64998
|
+
id: intent.id,
|
|
64999
|
+
title: intent.title,
|
|
65000
|
+
objective: intent.objective,
|
|
65001
|
+
outcomes: intent.outcomes ?? [],
|
|
65002
|
+
constraints: intent.constraints,
|
|
65003
|
+
edgeCases: (intent.edgeCases ?? []).map((e) => ({ scenario: e.scenario, expectedBehavior: e.expectedBehavior })),
|
|
65004
|
+
healthMetrics: intent.healthMetrics,
|
|
65005
|
+
scope: intent.scope,
|
|
65006
|
+
verification: { manualChecks: v.manualChecks, unitTests: v.unitTests, e2eTests: v.e2eTests },
|
|
65007
|
+
implementationContext: intent.implementationContext ?? undefined,
|
|
65008
|
+
};
|
|
65009
|
+
}
|
|
64543
65010
|
server.registerTool('export_context', {
|
|
64544
65011
|
title: 'Export Context',
|
|
64545
|
-
description: 'Export workspace context as a formatted file. Use "claude-md" for CLAUDE.md (full workspace context), "cursorrules" for Cursor AI rules,
|
|
65012
|
+
description: 'Export workspace context as a formatted file. Use "claude-md" for CLAUDE.md (full workspace context), "cursorrules" for Cursor AI rules, "intent-md" for a single intent specification file, or "outcome-rubric" for a Claude Managed Agents Outcomes rubric (writer description + evidence-forcing grader rubric) derived from the resolved intent, its implementation context, and the workspace constitution. For cursorrules/intent-md/outcome-rubric, product context is always derived from the resolved intent. For claude-md, pass productId to select a specific product, otherwise the first active product is used.',
|
|
64546
65013
|
inputSchema: {
|
|
64547
|
-
format: zod_1.z.enum(['claude-md', 'cursorrules', 'intent-md']).describe('Export format'),
|
|
65014
|
+
format: zod_1.z.enum(['claude-md', 'cursorrules', 'intent-md', 'outcome-rubric']).describe('Export format'),
|
|
64548
65015
|
intentId: zod_1.z.string().optional().describe('Intent ID (optional, for cursorrules and intent-md)'),
|
|
64549
65016
|
productId: zod_1.z.string().optional().describe('Product ID (optional, only used for claude-md format to select a specific product)'),
|
|
64550
65017
|
},
|
|
@@ -64554,6 +65021,25 @@ function startMcpServer() {
|
|
|
64554
65021
|
return { content: [{ type: 'text', text: 'Export requires cloud mode. Use PATHMODE_API_KEY to connect.' }] };
|
|
64555
65022
|
}
|
|
64556
65023
|
try {
|
|
65024
|
+
if (format === 'outcome-rubric') {
|
|
65025
|
+
const cloud = requireCloudClient();
|
|
65026
|
+
const intent = intentId
|
|
65027
|
+
? await cloud.getIntent(intentId)
|
|
65028
|
+
: (pickCurrentIntent(await cloud.listIntents('approved')) || pickCurrentIntent(await cloud.listIntents()));
|
|
65029
|
+
if (!intent) {
|
|
65030
|
+
return { content: [{ type: 'text', text: 'No intent found to export. Pass an intentId, or create an approved intent first.' }] };
|
|
65031
|
+
}
|
|
65032
|
+
let constitutionRules = [];
|
|
65033
|
+
try {
|
|
65034
|
+
const constitution = await cloud.getConstitution();
|
|
65035
|
+
constitutionRules = (constitution?.rules ?? [])
|
|
65036
|
+
.filter((r) => r?.isActive !== false && r?.text?.trim())
|
|
65037
|
+
.map((r) => r.text.trim());
|
|
65038
|
+
}
|
|
65039
|
+
catch { /* constitution is optional context */ }
|
|
65040
|
+
const rubric = (0, intent_compiler_1.formatOutcomeRubric)(apiIntentToFields(intent), { constitutionRules });
|
|
65041
|
+
return { content: [{ type: 'text', text: rubric }] };
|
|
65042
|
+
}
|
|
64557
65043
|
const content = await requireCloudClient().exportContext(format, intentId, productId);
|
|
64558
65044
|
return { content: [{ type: 'text', text: content }] };
|
|
64559
65045
|
}
|
|
@@ -64937,8 +65423,8 @@ function startMcpServer() {
|
|
|
64937
65423
|
}],
|
|
64938
65424
|
};
|
|
64939
65425
|
});
|
|
64940
|
-
server.tool('intent_export', 'Export an intent spec as .cursorrules
|
|
64941
|
-
format: zod_1.z.enum(['cursorrules', 'claude-md']).describe('Export format'),
|
|
65426
|
+
server.tool('intent_export', 'Export an intent spec as .cursorrules, a CLAUDE.md section, or a Claude Managed Agents Outcomes rubric for AI agent consumption.', {
|
|
65427
|
+
format: zod_1.z.enum(['cursorrules', 'claude-md', 'outcome-rubric']).describe('Export format'),
|
|
64942
65428
|
spec: zod_1.z.object(intentSpecSchema),
|
|
64943
65429
|
path: zod_1.z.string().optional().describe('Output file path. Defaults to .cursorrules or CLAUDE.md'),
|
|
64944
65430
|
}, async ({ format, spec, path }) => {
|
|
@@ -64953,6 +65439,17 @@ function startMcpServer() {
|
|
|
64953
65439
|
}],
|
|
64954
65440
|
};
|
|
64955
65441
|
}
|
|
65442
|
+
else if (format === 'outcome-rubric') {
|
|
65443
|
+
const content = (0, intent_compiler_1.formatOutcomeRubric)(spec);
|
|
65444
|
+
const filePath = (0, path_1.resolve)(process.cwd(), path || 'outcome-rubric.md');
|
|
65445
|
+
(0, fs_1.writeFileSync)(filePath, content, 'utf-8');
|
|
65446
|
+
return {
|
|
65447
|
+
content: [{
|
|
65448
|
+
type: 'text',
|
|
65449
|
+
text: `✓ Exported Outcomes rubric to ${filePath}\n\nPaste the Writer Description into the \`description\` field and the Grader Rubric into the \`rubric\` field of a Managed Agents \`user.define_outcome\` event. Docs: https://platform.claude.com/docs/en/managed-agents/define-outcomes`,
|
|
65450
|
+
}],
|
|
65451
|
+
};
|
|
65452
|
+
}
|
|
64956
65453
|
else {
|
|
64957
65454
|
const section = (0, intent_compiler_1.formatClaudeMdSection)(spec);
|
|
64958
65455
|
const filePath = (0, path_1.resolve)(process.cwd(), path || 'CLAUDE.md');
|
|
@@ -64962,7 +65459,10 @@ function startMcpServer() {
|
|
|
64962
65459
|
existing = (0, fs_1.readFileSync)(filePath, 'utf-8');
|
|
64963
65460
|
}
|
|
64964
65461
|
catch { /* file doesn't exist yet */ }
|
|
64965
|
-
|
|
65462
|
+
// Tolerant of the suffixed start marker emitted by formatClaudeMdSection
|
|
65463
|
+
// (`<!-- PATHMODE:START - Do not edit... -->`), so re-exports replace
|
|
65464
|
+
// the block instead of appending a duplicate.
|
|
65465
|
+
const marker = /<!-- PATHMODE:START[\s\S]*?-->[\s\S]*?<!-- PATHMODE:END -->/;
|
|
64966
65466
|
const updated = marker.test(existing)
|
|
64967
65467
|
? existing.replace(marker, section)
|
|
64968
65468
|
: existing ? existing + '\n\n' + section : section;
|