@pathmode/mcp-server 1.20.0 → 1.20.1
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/index.js
CHANGED
|
@@ -39615,6 +39615,9 @@ async function exchangeCode(code, origin, existing) {
|
|
|
39615
39615
|
}
|
|
39616
39616
|
/** Add cloud identity to the existing frontmatter without reformatting the user's spec body. */
|
|
39617
39617
|
function patchAdoptedIntentFrontmatter(filePath, values) {
|
|
39618
|
+
if (fs_1.default.lstatSync(filePath).isSymbolicLink()) {
|
|
39619
|
+
throw new Error('Refusing to update a symbolic-link intent.md. Use a regular file inside the repository.');
|
|
39620
|
+
}
|
|
39618
39621
|
const raw = fs_1.default.readFileSync(filePath, 'utf8');
|
|
39619
39622
|
const newline = raw.includes('\r\n') ? '\r\n' : '\n';
|
|
39620
39623
|
if (!raw.startsWith(`---${newline}`)) {
|
|
@@ -39646,6 +39649,9 @@ async function runAdopt(argv = process.argv) {
|
|
|
39646
39649
|
if (!code)
|
|
39647
39650
|
throw new Error('Usage: npx @pathmode/mcp-server@latest adopt pm_adopt_…');
|
|
39648
39651
|
const filePath = path_1.default.join(process.cwd(), 'intent.md');
|
|
39652
|
+
if (fs_1.default.existsSync(filePath) && fs_1.default.lstatSync(filePath).isSymbolicLink()) {
|
|
39653
|
+
throw new Error('Refusing to adopt a symbolic-link intent.md. Use a regular file inside the repository.');
|
|
39654
|
+
}
|
|
39649
39655
|
const intent = (0, local_reader_1.readIntentFile)(filePath);
|
|
39650
39656
|
const meta = (0, local_reader_1.readIntentMeta)(filePath);
|
|
39651
39657
|
if (!intent || !meta?.id) {
|
|
@@ -39654,7 +39660,7 @@ async function runAdopt(argv = process.argv) {
|
|
|
39654
39660
|
console.log('');
|
|
39655
39661
|
console.log('Pathmode repo adoption');
|
|
39656
39662
|
console.log('──────────────────────');
|
|
39657
|
-
console.log(`
|
|
39663
|
+
console.log(` ✓ Read intent.md — ${intent.title}`);
|
|
39658
39664
|
process.stdout.write(' Connecting…');
|
|
39659
39665
|
const origin = apiOrigin(argv);
|
|
39660
39666
|
const current = (0, api_client_1.loadConfig)();
|
|
@@ -39674,6 +39680,8 @@ async function runAdopt(argv = process.argv) {
|
|
|
39674
39680
|
throw error;
|
|
39675
39681
|
}
|
|
39676
39682
|
}
|
|
39683
|
+
console.log(' ✓');
|
|
39684
|
+
console.log(` ✓ Destination confirmed — ${exchange.workspaceName} / ${exchange.productName}`);
|
|
39677
39685
|
const apiKey = exchange.apiKey || current?.apiKey;
|
|
39678
39686
|
if (!apiKey)
|
|
39679
39687
|
throw new Error('No repository credential is available after adoption exchange.');
|
|
@@ -39686,7 +39694,10 @@ async function runAdopt(argv = process.argv) {
|
|
|
39686
39694
|
// credential remains recoverable from this repo without minting a duplicate key.
|
|
39687
39695
|
(0, project_config_1.saveProjectConnection)(config);
|
|
39688
39696
|
(0, project_config_1.ensureProjectMcpConfig)();
|
|
39697
|
+
console.log(' ✓ Credential stored outside the repository');
|
|
39698
|
+
console.log(' ✓ Pathmode project entries added without the credential');
|
|
39689
39699
|
const client = new api_client_1.PathmodeClient(config);
|
|
39700
|
+
console.log(' • Creating and linking the hosted intent…');
|
|
39690
39701
|
const pushed = await (0, push_spec_1.pushSpec)({
|
|
39691
39702
|
client,
|
|
39692
39703
|
id: meta.id,
|
|
@@ -39717,11 +39728,17 @@ async function runAdopt(argv = process.argv) {
|
|
|
39717
39728
|
if (!pushed.ok)
|
|
39718
39729
|
throw new Error(pushed.error);
|
|
39719
39730
|
await client.completeRepoOnboarding(exchange.sessionId, pushed.canonicalId);
|
|
39720
|
-
console.log('
|
|
39721
|
-
console.log(` Adopted into ${exchange.workspaceName} / ${exchange.productName}`);
|
|
39731
|
+
console.log(' ✓ Hosted intent linked');
|
|
39722
39732
|
console.log(` ${pushed.sourceUrl}`);
|
|
39723
39733
|
console.log('');
|
|
39724
|
-
console.log('
|
|
39734
|
+
console.log('What changed');
|
|
39735
|
+
console.log(' • intent.md remains authoritative; only its cloud identity frontmatter changed');
|
|
39736
|
+
console.log(' • Project connection files contain no Pathmode credential');
|
|
39737
|
+
console.log(' • The hosted record is ready for human review and authorization');
|
|
39738
|
+
console.log('');
|
|
39739
|
+
console.log('Next');
|
|
39740
|
+
console.log(' Keep the browser page open; it finishes automatically.');
|
|
39741
|
+
console.log(' Restart your coding tool once so it reloads .mcp.json and discovers Pathmode.');
|
|
39725
39742
|
console.log('');
|
|
39726
39743
|
}
|
|
39727
39744
|
|
|
@@ -41490,9 +41507,11 @@ function extractVerification(sections) {
|
|
|
41490
41507
|
const result = {};
|
|
41491
41508
|
let currentKind = null;
|
|
41492
41509
|
let currentLegacy = null;
|
|
41510
|
+
let sawLabel = false;
|
|
41493
41511
|
for (const line of lines) {
|
|
41494
41512
|
const label = parseVerificationLabel(line);
|
|
41495
41513
|
if (label) {
|
|
41514
|
+
sawLabel = true;
|
|
41496
41515
|
const kind = VERIFICATION_LABEL_TO_KIND[label.toLowerCase()];
|
|
41497
41516
|
if (kind) {
|
|
41498
41517
|
currentKind = kind;
|
|
@@ -41531,6 +41550,11 @@ function extractVerification(sections) {
|
|
|
41531
41550
|
else if (currentLegacy) {
|
|
41532
41551
|
result[currentLegacy].push(text);
|
|
41533
41552
|
}
|
|
41553
|
+
else if (!sawLabel) {
|
|
41554
|
+
// IntentSpec labels are optional. Treat an ungrouped list as manual checks so a valid
|
|
41555
|
+
// flat `## Verification` section survives adoption instead of silently disappearing.
|
|
41556
|
+
(result.manualChecks ||= []).push(text);
|
|
41557
|
+
}
|
|
41534
41558
|
}
|
|
41535
41559
|
return result;
|
|
41536
41560
|
}
|
|
@@ -42216,6 +42240,7 @@ exports.ensureProjectMcpConfig = ensureProjectMcpConfig;
|
|
|
42216
42240
|
const fs_1 = __importDefault(__nccwpck_require__(9896));
|
|
42217
42241
|
const os_1 = __importDefault(__nccwpck_require__(857));
|
|
42218
42242
|
const path_1 = __importDefault(__nccwpck_require__(6928));
|
|
42243
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
42219
42244
|
const homeWorkspaceFile = (workspaceId, homeDir = os_1.default.homedir()) => path_1.default.join(homeDir, '.pathmode', 'workspaces', `${workspaceId}.json`);
|
|
42220
42245
|
function projectBindingFile(cwd = process.cwd()) {
|
|
42221
42246
|
return path_1.default.join(cwd, '.pathmode', 'config.json');
|
|
@@ -42228,32 +42253,73 @@ function readJson(filePath) {
|
|
|
42228
42253
|
return null;
|
|
42229
42254
|
}
|
|
42230
42255
|
}
|
|
42256
|
+
function normalizeApiUrl(raw) {
|
|
42257
|
+
if (typeof raw !== 'string' || !raw.trim())
|
|
42258
|
+
return null;
|
|
42259
|
+
try {
|
|
42260
|
+
const url = new URL(raw.trim());
|
|
42261
|
+
if (url.protocol !== 'https:' && url.protocol !== 'http:')
|
|
42262
|
+
return null;
|
|
42263
|
+
return url.origin;
|
|
42264
|
+
}
|
|
42265
|
+
catch {
|
|
42266
|
+
return null;
|
|
42267
|
+
}
|
|
42268
|
+
}
|
|
42269
|
+
/** Repository files are attacker-controlled when a project or branch is first opened. */
|
|
42270
|
+
function assertSafeProjectWrite(cwd, filePath) {
|
|
42271
|
+
const root = path_1.default.resolve(cwd);
|
|
42272
|
+
const target = path_1.default.resolve(filePath);
|
|
42273
|
+
const relative = path_1.default.relative(root, target);
|
|
42274
|
+
if (!relative || relative.startsWith('..') || path_1.default.isAbsolute(relative)) {
|
|
42275
|
+
throw new Error(`Refusing to write outside the repository: ${filePath}`);
|
|
42276
|
+
}
|
|
42277
|
+
let current = root;
|
|
42278
|
+
for (const segment of relative.split(path_1.default.sep)) {
|
|
42279
|
+
current = path_1.default.join(current, segment);
|
|
42280
|
+
if (fs_1.default.existsSync(current) && fs_1.default.lstatSync(current).isSymbolicLink()) {
|
|
42281
|
+
throw new Error(`Refusing to write through a symbolic link: ${current}`);
|
|
42282
|
+
}
|
|
42283
|
+
}
|
|
42284
|
+
}
|
|
42285
|
+
function writeProjectFile(cwd, filePath, contents) {
|
|
42286
|
+
assertSafeProjectWrite(cwd, filePath);
|
|
42287
|
+
fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
|
|
42288
|
+
// Re-check after creating a missing parent so a checked-in symlink is never followed.
|
|
42289
|
+
assertSafeProjectWrite(cwd, filePath);
|
|
42290
|
+
fs_1.default.writeFileSync(filePath, contents, 'utf8');
|
|
42291
|
+
}
|
|
42231
42292
|
function loadProjectConnection(cwd = process.cwd(), homeDir = os_1.default.homedir()) {
|
|
42232
42293
|
const binding = readJson(projectBindingFile(cwd));
|
|
42233
42294
|
const workspaceId = typeof binding?.workspaceId === 'string' ? binding.workspaceId.trim() : '';
|
|
42234
|
-
if (!workspaceId)
|
|
42295
|
+
if (!UUID_RE.test(workspaceId))
|
|
42235
42296
|
return null;
|
|
42236
42297
|
const secret = readJson(homeWorkspaceFile(workspaceId, homeDir));
|
|
42237
42298
|
const apiKey = typeof secret?.apiKey === 'string' ? secret.apiKey.trim() : '';
|
|
42238
42299
|
if (!apiKey)
|
|
42239
42300
|
return null;
|
|
42240
|
-
|
|
42241
|
-
|
|
42242
|
-
|
|
42301
|
+
// The repository chooses only which workspace to use. The protected home secret
|
|
42302
|
+
// chooses where that credential may be sent, so a malicious branch cannot redirect it.
|
|
42303
|
+
const apiUrl = normalizeApiUrl(secret?.apiUrl);
|
|
42304
|
+
if (!apiUrl)
|
|
42305
|
+
return null;
|
|
42243
42306
|
return { apiKey, apiUrl, workspaceId };
|
|
42244
42307
|
}
|
|
42245
42308
|
function saveProjectConnection(config, cwd = process.cwd(), homeDir = os_1.default.homedir()) {
|
|
42309
|
+
if (!UUID_RE.test(config.workspaceId))
|
|
42310
|
+
throw new Error('Cannot save a project connection with an invalid workspace id.');
|
|
42311
|
+
const apiUrl = normalizeApiUrl(config.apiUrl);
|
|
42312
|
+
if (!apiUrl)
|
|
42313
|
+
throw new Error('Cannot save a project connection with an invalid API URL.');
|
|
42246
42314
|
const bindingPath = projectBindingFile(cwd);
|
|
42247
|
-
|
|
42248
|
-
fs_1.default.writeFileSync(bindingPath, JSON.stringify({
|
|
42315
|
+
writeProjectFile(cwd, bindingPath, JSON.stringify({
|
|
42249
42316
|
workspaceId: config.workspaceId,
|
|
42250
|
-
|
|
42251
|
-
}, null, 2) + '\n', 'utf8');
|
|
42317
|
+
}, null, 2) + '\n');
|
|
42252
42318
|
const secretPath = homeWorkspaceFile(config.workspaceId, homeDir);
|
|
42253
42319
|
fs_1.default.mkdirSync(path_1.default.dirname(secretPath), { recursive: true });
|
|
42254
42320
|
fs_1.default.writeFileSync(secretPath, JSON.stringify({
|
|
42255
42321
|
apiKey: config.apiKey,
|
|
42256
|
-
apiUrl
|
|
42322
|
+
apiUrl,
|
|
42257
42323
|
}, null, 2) + '\n', { encoding: 'utf8', mode: 0o600 });
|
|
42258
42324
|
try {
|
|
42259
42325
|
fs_1.default.chmodSync(secretPath, 0o600);
|
|
@@ -42264,6 +42330,7 @@ function saveProjectConnection(config, cwd = process.cwd(), homeDir = os_1.defau
|
|
|
42264
42330
|
* from the project binding at runtime and is never written into a committable file. */
|
|
42265
42331
|
function ensureProjectMcpConfig(cwd = process.cwd()) {
|
|
42266
42332
|
const configPath = path_1.default.join(cwd, '.mcp.json');
|
|
42333
|
+
assertSafeProjectWrite(cwd, configPath);
|
|
42267
42334
|
const existing = fs_1.default.existsSync(configPath) ? readJson(configPath) : {};
|
|
42268
42335
|
if (!existing)
|
|
42269
42336
|
throw new Error(`${configPath} is not valid JSON; fix it before adopting.`);
|
|
@@ -42271,7 +42338,7 @@ function ensureProjectMcpConfig(cwd = process.cwd()) {
|
|
|
42271
42338
|
? existing.mcpServers
|
|
42272
42339
|
: {};
|
|
42273
42340
|
servers.pathmode = { command: 'npx', args: ['@pathmode/mcp-server'] };
|
|
42274
|
-
|
|
42341
|
+
writeProjectFile(cwd, configPath, JSON.stringify({ ...existing, mcpServers: servers }, null, 2) + '\n');
|
|
42275
42342
|
}
|
|
42276
42343
|
|
|
42277
42344
|
|
|
@@ -71946,7 +72013,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"$schema":"http://json-schema.org/dra
|
|
|
71946
72013
|
/***/ ((module) => {
|
|
71947
72014
|
|
|
71948
72015
|
"use strict";
|
|
71949
|
-
module.exports = /*#__PURE__*/JSON.parse('{"name":"@pathmode/mcp-server","version":"1.20.
|
|
72016
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"@pathmode/mcp-server","version":"1.20.1","publishConfig":{"access":"public"},"mcpName":"io.github.pathmodeio/mcp-server","description":"Deterministic intent preflight before your agent builds: six calibrated gates, keyless, no model call. Draft and sharpen specs in conversation, or connect a Pathmode workspace to sync intent and evidence across a team.","main":"dist/index.js","bin":{"pathmode-mcp":"dist/index.js"},"files":["dist/","manifest.json","icon.svg","README.md","skills/"],"scripts":{"build":"rm -rf dist && ncc build src/index.ts -o dist","dev":"ts-node src/index.ts","prepublishOnly":"npm run build"},"keywords":["pathmode","mcp","model-context-protocol","claude-code","claude-code-skills","agent-skills","cursor","windsurf","intent-engineering","intent-compiler","ai-agents","product-development","dependency-graph","strategic-planning"],"author":"Pathmode","license":"MIT","type":"commonjs","engines":{"node":">=18.0.0"},"homepage":"https://pathmode.io","dependencies":{"@modelcontextprotocol/sdk":"^1.12.1","gray-matter":"^4.0.3","zod":"^3.24.0"},"devDependencies":{"@types/node":"^25.1.0","@vercel/ncc":"^0.38.4","ts-node":"^10.9.2","typescript":"^5.9.3"}}');
|
|
71950
72017
|
|
|
71951
72018
|
/***/ })
|
|
71952
72019
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adopt.d.ts","sourceRoot":"","sources":["file:///Users/jannelammi/code/Pathmode/packages/mcp-server/src/adopt.ts"],"names":[],"mappings":"AAoBA,wBAAgB,cAAc,CAAC,IAAI,WAAe,GAAG,OAAO,CAE3D;AA8BD,gGAAgG;AAChG,wBAAgB,6BAA6B,CACzC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC1J,IAAI,
|
|
1
|
+
{"version":3,"file":"adopt.d.ts","sourceRoot":"","sources":["file:///Users/jannelammi/code/Pathmode/packages/mcp-server/src/adopt.ts"],"names":[],"mappings":"AAoBA,wBAAgB,cAAc,CAAC,IAAI,WAAe,GAAG,OAAO,CAE3D;AA8BD,gGAAgG;AAChG,wBAAgB,6BAA6B,CACzC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC1J,IAAI,CA6BN;AAED,wBAAsB,QAAQ,CAAC,IAAI,WAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAkGjE"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { PathmodeConfig } from './api-client';
|
|
2
2
|
export interface ProjectBinding {
|
|
3
3
|
workspaceId: string;
|
|
4
|
-
apiUrl: string;
|
|
5
4
|
}
|
|
6
5
|
export declare function projectBindingFile(cwd?: string): string;
|
|
7
6
|
export declare function loadProjectConnection(cwd?: string, homeDir?: string): PathmodeConfig | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-config.d.ts","sourceRoot":"","sources":["file:///Users/jannelammi/code/Pathmode/packages/mcp-server/src/project-config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,cAAc;IAC3B,WAAW,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"project-config.d.ts","sourceRoot":"","sources":["file:///Users/jannelammi/code/Pathmode/packages/mcp-server/src/project-config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,cAAc;IAC3B,WAAW,EAAE,MAAM,CAAC;CACvB;AAOD,wBAAgB,kBAAkB,CAAC,GAAG,SAAgB,GAAG,MAAM,CAE9D;AA+CD,wBAAgB,qBAAqB,CAAC,GAAG,SAAgB,EAAE,OAAO,SAAe,GAAG,cAAc,GAAG,IAAI,CAcxG;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,SAAgB,EAAE,OAAO,SAAe,GAAG,IAAI,CAiB/G;AAED;uFACuF;AACvF,wBAAgB,sBAAsB,CAAC,GAAG,SAAgB,GAAG,IAAI,CAUhE"}
|
package/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": "0.3",
|
|
3
3
|
"name": "pathmode",
|
|
4
4
|
"display_name": "Pathmode",
|
|
5
|
-
"version": "1.20.
|
|
5
|
+
"version": "1.20.1",
|
|
6
6
|
"description": "Deterministic preflight for the intent you hand to a coding agent: six calibrated gates, the exact blockers named, no model call, no key needed.",
|
|
7
7
|
"long_description": "Pathmode MCP Server runs a deterministic preflight before your coding agent builds: check_intent_readiness scores an intent spec against six calibrated gates (title, objective, outcomes, constraints, edge cases, verification) and names the exact blockers, with no model call and no account. Keyless local mode works out of the box; specs live in intent.md in your repo, plain markdown you own, and skills for drafting, pressure-testing, and handing off intent ride along. Connect a Pathmode workspace with an API key to sync intent and evidence across a team, analyze dependency graphs, and verify pull requests against the outcomes you agreed to.",
|
|
8
8
|
"author": {
|