praxis-agent 0.20.0 → 0.20.2
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 +14 -3
- package/dist/cli/tui/claude-command-inventory.d.ts +2 -4
- package/dist/cli/tui/claude-command-inventory.js +2 -12
- package/dist/cli.js +11 -4
- package/dist/compatibility/claude/fork.js +11 -1
- package/dist/compatibility/claude/schema.d.ts +1 -0
- package/dist/compatibility/claude/schema.js +13 -10
- package/dist/plugins/claude-plugin-runtime.js +119 -12
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -168,9 +168,20 @@ Praxis ──────┘
|
|
|
168
168
|
```
|
|
169
169
|
|
|
170
170
|
Praxis can resume Claude Code sessions, and Claude Code can resume compatible
|
|
171
|
-
sessions written by Praxis.
|
|
172
|
-
2.1.208
|
|
173
|
-
|
|
171
|
+
sessions written by Praxis. Ordinary Praxis session runtime always emits the
|
|
172
|
+
verified Claude Code 2.1.208 write profile and never derives it from an
|
|
173
|
+
installed Claude version; this fixed profile governs generated append and
|
|
174
|
+
sidechain writes. Native fork creation is a separate restricted lossless copy
|
|
175
|
+
path that preserves each existing source record's producer version, so it can
|
|
176
|
+
copy specific black-box-verified foreign shapes such as the observed Claude
|
|
177
|
+
Code 2.1.233 records; unsupported record shapes and unverified versions still
|
|
178
|
+
fail closed and remain read-only. Maintainers can prove mixed-version Claude JSONL interoperability with
|
|
179
|
+
`npm run test:cross-version-session-compat`, `test:cross-version-fork-compat`,
|
|
180
|
+
`test:cross-version-sidechain-compat`, `test:cross-version-compaction-compat`,
|
|
181
|
+
and `test:cross-version-resume-at-compat`, covering linear resume, native fork,
|
|
182
|
+
foreground sidechain, compaction, and `--resume-session-at` branch projection.
|
|
183
|
+
Each requires `PRAXIS_CLAUDE_BINARY` (Claude Code 2.1.208) and
|
|
184
|
+
`PRAXIS_CLAUDE_CROSS_VERSION_BINARY` (a different Claude Code version).
|
|
174
185
|
|
|
175
186
|
See the
|
|
176
187
|
[compatibility contract](https://github.com/Forest-Isle/Praxis/blob/main/docs/COMPATIBILITY.md)
|
|
@@ -72,9 +72,8 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
72
72
|
readonly visibility: "visible";
|
|
73
73
|
}, {
|
|
74
74
|
readonly name: "doctor";
|
|
75
|
-
readonly disposition: "
|
|
75
|
+
readonly disposition: "included";
|
|
76
76
|
readonly visibility: "conditional";
|
|
77
|
-
readonly reason: "Interactive /doctor is required; the top-level command is not a substitute.";
|
|
78
77
|
}, {
|
|
79
78
|
readonly name: "effort";
|
|
80
79
|
readonly disposition: "included";
|
|
@@ -254,9 +253,8 @@ export declare const CLAUDE_2_1_208_COMMAND_INVENTORY: readonly [{
|
|
|
254
253
|
readonly reason: "Claude subscription rate-limit purchasing is outside the authentication boundary.";
|
|
255
254
|
}, {
|
|
256
255
|
readonly name: "usage";
|
|
257
|
-
readonly disposition: "
|
|
256
|
+
readonly disposition: "included";
|
|
258
257
|
readonly visibility: "conditional";
|
|
259
|
-
readonly reason: "The source command is the Claude subscription plan-usage panel.";
|
|
260
258
|
}, {
|
|
261
259
|
readonly name: "insights";
|
|
262
260
|
readonly disposition: "deferred";
|
|
@@ -38,12 +38,7 @@ export const CLAUDE_2_1_208_COMMAND_INVENTORY = [
|
|
|
38
38
|
reason: 'The dedicated /cost contract is included; /status is not a substitute.',
|
|
39
39
|
},
|
|
40
40
|
{ name: 'diff', disposition: 'included', visibility: 'visible' },
|
|
41
|
-
{
|
|
42
|
-
name: 'doctor',
|
|
43
|
-
disposition: 'required',
|
|
44
|
-
visibility: 'conditional',
|
|
45
|
-
reason: 'Interactive /doctor is required; the top-level command is not a substitute.',
|
|
46
|
-
},
|
|
41
|
+
{ name: 'doctor', disposition: 'included', visibility: 'conditional' },
|
|
47
42
|
{ name: 'effort', disposition: 'included', visibility: 'visible' },
|
|
48
43
|
{ name: 'exit', disposition: 'included', visibility: 'visible' },
|
|
49
44
|
{
|
|
@@ -173,12 +168,7 @@ export const CLAUDE_2_1_208_COMMAND_INVENTORY = [
|
|
|
173
168
|
visibility: 'hidden',
|
|
174
169
|
reason: 'Claude subscription rate-limit purchasing is outside the authentication boundary.',
|
|
175
170
|
},
|
|
176
|
-
{
|
|
177
|
-
name: 'usage',
|
|
178
|
-
disposition: 'excluded',
|
|
179
|
-
visibility: 'conditional',
|
|
180
|
-
reason: 'The source command is the Claude subscription plan-usage panel.',
|
|
181
|
-
},
|
|
171
|
+
{ name: 'usage', disposition: 'included', visibility: 'conditional' },
|
|
182
172
|
{
|
|
183
173
|
name: 'insights',
|
|
184
174
|
disposition: 'deferred',
|
package/dist/cli.js
CHANGED
|
@@ -31,6 +31,7 @@ import { ClaudeMcpToolRegistry, } from './mcp/claude-mcp-tools.js';
|
|
|
31
31
|
import { ClaudeMcpManagement, filterDisabledMcpResources, mcpScope, } from './mcp/claude-mcp-management.js';
|
|
32
32
|
import { authenticateMcpServer, ClaudeMcpOAuthStore, mcpOAuthServerIdentity, readMcpClientSecret, } from './mcp/claude-mcp-oauth.js';
|
|
33
33
|
import { servePraxisMcpStdio } from './mcp/praxis-mcp-server.js';
|
|
34
|
+
import { VERIFIED_CLAUDE_SCHEMA_VERSION } from './compatibility/claude/schema.js';
|
|
34
35
|
import { detectInstalledClaudeVersion } from './platform/claude-version.js';
|
|
35
36
|
import { redactSensitiveText, sensitiveEnvironmentValues, } from './platform/sensitive-data.js';
|
|
36
37
|
import { AnthropicCompatibleProvider } from './providers/anthropic-compatible.js';
|
|
@@ -697,7 +698,7 @@ const consoleIO = {
|
|
|
697
698
|
const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = false, approveRecovery, approveTool, agent, model: interactiveModel, effort: interactiveEffort, permissionMode: interactivePermissionMode, isSessionActionApproved, controls = DEFAULT_CLI_CONTROLS, interactive = false, sessionKind, signal, exposeToolRegistry = false, onElicitation, askUser, approvePlan, emitToolUseSummaries = false, cwd: requestedCwd, sandboxOriginalCwd, configRoot: requestedConfigRoot, environment, providerEnvironment: requestedProviderEnvironment, }) => {
|
|
698
699
|
const runtimeEnvironment = requestedProviderEnvironment ?? process.env;
|
|
699
700
|
const sandboxEnvironment = { ...runtimeEnvironment, ...environment };
|
|
700
|
-
const claudeVersion =
|
|
701
|
+
const claudeVersion = VERIFIED_CLAUDE_SCHEMA_VERSION;
|
|
701
702
|
const cwd = requestedCwd ?? process.cwd();
|
|
702
703
|
const workspace = new WorkspaceContext(cwd);
|
|
703
704
|
const configuredRoot = runtimeEnvironment.CLAUDE_CONFIG_DIR || undefined;
|
|
@@ -4436,8 +4437,14 @@ function isDirectExecution(moduleUrl, argvPath) {
|
|
|
4436
4437
|
if (isDirectExecution(import.meta.url, process.argv[1])) {
|
|
4437
4438
|
const controller = new AbortController();
|
|
4438
4439
|
const cancel = () => controller.abort();
|
|
4439
|
-
process.
|
|
4440
|
-
process.
|
|
4441
|
-
|
|
4440
|
+
process.on('SIGINT', cancel);
|
|
4441
|
+
process.on('SIGTERM', cancel);
|
|
4442
|
+
try {
|
|
4443
|
+
process.exitCode = await run(process.argv.slice(2), consoleIO, defaultDependencies, controller.signal);
|
|
4444
|
+
}
|
|
4445
|
+
finally {
|
|
4446
|
+
process.removeListener('SIGINT', cancel);
|
|
4447
|
+
process.removeListener('SIGTERM', cancel);
|
|
4448
|
+
}
|
|
4442
4449
|
}
|
|
4443
4450
|
//# sourceMappingURL=cli.js.map
|
|
@@ -48,6 +48,7 @@ function validateNativeHistory(entries) {
|
|
|
48
48
|
}
|
|
49
49
|
const childParentUuids = new Set();
|
|
50
50
|
const externalParentUuids = new Set();
|
|
51
|
+
let externalParentReferenceCount = 0;
|
|
51
52
|
const completedToolCalls = new Set();
|
|
52
53
|
let ordinaryRootCount = 0;
|
|
53
54
|
let compactBoundary;
|
|
@@ -96,6 +97,7 @@ function validateNativeHistory(entries) {
|
|
|
96
97
|
}
|
|
97
98
|
else {
|
|
98
99
|
externalParentUuids.add(entry.parentUuid);
|
|
100
|
+
externalParentReferenceCount += 1;
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
}
|
|
@@ -120,7 +122,15 @@ function validateNativeHistory(entries) {
|
|
|
120
122
|
if (compactBoundary) {
|
|
121
123
|
throw new Error('Claude compact boundary has no adjacent summary');
|
|
122
124
|
}
|
|
123
|
-
|
|
125
|
+
const omittedSeedUserParent = externalParentUuids.size === 1 ? [...externalParentUuids][0] : undefined;
|
|
126
|
+
const firstEntry = entries[0];
|
|
127
|
+
const isOmittedSeedUserChain = externalParentUuids.size === 1 &&
|
|
128
|
+
ordinaryRootCount === 0 &&
|
|
129
|
+
externalParentReferenceCount === 1 &&
|
|
130
|
+
firstEntry?.type === 'assistant' &&
|
|
131
|
+
firstEntry?.parentUuid === omittedSeedUserParent;
|
|
132
|
+
if ((externalParentUuids.size > 0 && !isOmittedSeedUserChain) ||
|
|
133
|
+
ordinaryRootCount > 1) {
|
|
124
134
|
throw new Error('Claude fork source has a dangling parentUuid');
|
|
125
135
|
}
|
|
126
136
|
const states = new Map();
|
|
@@ -11,6 +11,7 @@ export interface ClaudeSchemaAdapter {
|
|
|
11
11
|
serializeForSidechainAppend(entry: ClaudeTranscriptEntry): string;
|
|
12
12
|
serializeForFork(entry: ClaudeTranscriptEntry): string;
|
|
13
13
|
}
|
|
14
|
+
export declare const VERIFIED_CLAUDE_SCHEMA_VERSION = "2.1.208";
|
|
14
15
|
export declare function isClaudeForkableEntryType(type: string): boolean;
|
|
15
16
|
export declare function copyClaudeEntryWithSessionId(entry: ClaudeTranscriptEntry, sessionId: string): ClaudeTranscriptEntry;
|
|
16
17
|
export declare function selectClaudeSchemaAdapter(version: string): ClaudeSchemaAdapter;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isAgentColorValue } from './agent-color.js';
|
|
2
|
-
const
|
|
2
|
+
export const VERIFIED_CLAUDE_SCHEMA_VERSION = '2.1.208';
|
|
3
3
|
const APPENDABLE_ENTRY_TYPES = new Set([
|
|
4
4
|
'agent-color',
|
|
5
5
|
'agent-name',
|
|
@@ -69,6 +69,7 @@ const FORKABLE_ATTACHMENT_TYPES = new Set([
|
|
|
69
69
|
'read_truncation_notice',
|
|
70
70
|
'skill_listing',
|
|
71
71
|
'task_reminder',
|
|
72
|
+
'total_tokens_reminder',
|
|
72
73
|
]);
|
|
73
74
|
const RAW_CLAUDE_ENTRY = Symbol('raw-claude-entry');
|
|
74
75
|
function parseEntry(line) {
|
|
@@ -775,8 +776,8 @@ function validateAppendableEntry(entry) {
|
|
|
775
776
|
throw new Error(`Claude transcript entry is missing ${field}`);
|
|
776
777
|
}
|
|
777
778
|
}
|
|
778
|
-
if (entry.version !==
|
|
779
|
-
throw new Error(`Claude transcript append must target Claude Code ${
|
|
779
|
+
if (entry.version !== VERIFIED_CLAUDE_SCHEMA_VERSION) {
|
|
780
|
+
throw new Error(`Claude transcript append must target Claude Code ${VERIFIED_CLAUDE_SCHEMA_VERSION}`);
|
|
780
781
|
}
|
|
781
782
|
if (!('parentUuid' in entry) ||
|
|
782
783
|
(entry.parentUuid !== null && !isNonEmptyString(entry.parentUuid))) {
|
|
@@ -836,8 +837,8 @@ function validateSidechainEntry(entry) {
|
|
|
836
837
|
throw new Error(`Claude sidechain entry is missing ${field}`);
|
|
837
838
|
}
|
|
838
839
|
}
|
|
839
|
-
if (entry.version !==
|
|
840
|
-
throw new Error(`Claude sidechain append must target Claude Code ${
|
|
840
|
+
if (entry.version !== VERIFIED_CLAUDE_SCHEMA_VERSION) {
|
|
841
|
+
throw new Error(`Claude sidechain append must target Claude Code ${VERIFIED_CLAUDE_SCHEMA_VERSION}`);
|
|
841
842
|
}
|
|
842
843
|
if (!('parentUuid' in entry) ||
|
|
843
844
|
(entry.parentUuid !== null && !isNonEmptyString(entry.parentUuid))) {
|
|
@@ -925,9 +926,11 @@ function validateForkableEntry(entry) {
|
|
|
925
926
|
throw new Error(`Claude transcript entry is missing ${field}`);
|
|
926
927
|
}
|
|
927
928
|
}
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
929
|
+
// A native fork is a lossless copy that retains the source producer's
|
|
930
|
+
// version as append-only provenance and changes only fork-specific
|
|
931
|
+
// session-id fields. The strict verified-writer version gate remains on
|
|
932
|
+
// serializeForAppend and serializeForSidechainAppend; the loop above
|
|
933
|
+
// already requires a nonempty producer version here.
|
|
931
934
|
if (!('parentUuid' in entry) ||
|
|
932
935
|
(entry.parentUuid !== null && !isNonEmptyString(entry.parentUuid))) {
|
|
933
936
|
throw new Error('Claude transcript entry has invalid parentUuid');
|
|
@@ -969,7 +972,7 @@ function validateForkableEntry(entry) {
|
|
|
969
972
|
validateForkAssistantMessage(entry.message);
|
|
970
973
|
}
|
|
971
974
|
class ClaudeCode21208Adapter {
|
|
972
|
-
version =
|
|
975
|
+
version = VERIFIED_CLAUDE_SCHEMA_VERSION;
|
|
973
976
|
writeMode = 'read-write';
|
|
974
977
|
parse(line) {
|
|
975
978
|
return parseEntry(line);
|
|
@@ -1019,7 +1022,7 @@ class ReadOnlyClaudeAdapter {
|
|
|
1019
1022
|
}
|
|
1020
1023
|
}
|
|
1021
1024
|
export function selectClaudeSchemaAdapter(version) {
|
|
1022
|
-
if (version ===
|
|
1025
|
+
if (version === VERIFIED_CLAUDE_SCHEMA_VERSION) {
|
|
1023
1026
|
return new ClaudeCode21208Adapter();
|
|
1024
1027
|
}
|
|
1025
1028
|
return new ReadOnlyClaudeAdapter(version);
|
|
@@ -975,6 +975,87 @@ async function writeScaffoldComponent(path, content, preserveExisting) {
|
|
|
975
975
|
throw error;
|
|
976
976
|
}
|
|
977
977
|
}
|
|
978
|
+
function nativeSkillTemplate(name) {
|
|
979
|
+
return `---
|
|
980
|
+
name: ${name}
|
|
981
|
+
description: TODO — describe WHEN Claude should use this. Include trigger phrases users
|
|
982
|
+
might say ("do X", "set up Y", "review Z"). Be specific; this string is what Claude
|
|
983
|
+
matches the user's request against.
|
|
984
|
+
---
|
|
985
|
+
|
|
986
|
+
# ${name}
|
|
987
|
+
|
|
988
|
+
TODO: what this skill does, and the steps Claude should take.
|
|
989
|
+
`;
|
|
990
|
+
}
|
|
991
|
+
function nativeChannelServerTemplate(name) {
|
|
992
|
+
return `#!/usr/bin/env bun
|
|
993
|
+
/**
|
|
994
|
+
* ${name} channel server — stdio MCP server implementing the channel contract.
|
|
995
|
+
* See https://docs.claude.com/en/docs/claude-code/channels-reference.
|
|
996
|
+
*/
|
|
997
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
|
998
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
999
|
+
import {
|
|
1000
|
+
CallToolRequestSchema,
|
|
1001
|
+
ListToolsRequestSchema,
|
|
1002
|
+
} from '@modelcontextprotocol/sdk/types.js'
|
|
1003
|
+
|
|
1004
|
+
const mcp = new Server(
|
|
1005
|
+
{ name: '${name}', version: '0.1.0' },
|
|
1006
|
+
{
|
|
1007
|
+
capabilities: {
|
|
1008
|
+
tools: {},
|
|
1009
|
+
// Required: presence of this key registers the channel notification
|
|
1010
|
+
// listener on Claude's side.
|
|
1011
|
+
experimental: { 'claude/channel': {} },
|
|
1012
|
+
},
|
|
1013
|
+
instructions:
|
|
1014
|
+
"Events from ${name} arrive as <channel source=\\"${name}\\" ...>. Anything " +
|
|
1015
|
+
"you want the sender to see must go through the reply tool — your " +
|
|
1016
|
+
"transcript output never reaches the channel.",
|
|
1017
|
+
},
|
|
1018
|
+
)
|
|
1019
|
+
|
|
1020
|
+
mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
1021
|
+
tools: [
|
|
1022
|
+
{
|
|
1023
|
+
name: 'reply',
|
|
1024
|
+
description: 'Send a message back to the ${name} channel.',
|
|
1025
|
+
inputSchema: {
|
|
1026
|
+
type: 'object',
|
|
1027
|
+
properties: { text: { type: 'string' } },
|
|
1028
|
+
required: ['text'],
|
|
1029
|
+
},
|
|
1030
|
+
},
|
|
1031
|
+
],
|
|
1032
|
+
}))
|
|
1033
|
+
|
|
1034
|
+
mcp.setRequestHandler(CallToolRequestSchema, async req => {
|
|
1035
|
+
const args = (req.params.arguments ?? {}) as Record<string, unknown>
|
|
1036
|
+
if (req.params.name === 'reply') {
|
|
1037
|
+
// TODO: deliver args.text to the external service.
|
|
1038
|
+
return { content: [{ type: 'text', text: 'sent' }] }
|
|
1039
|
+
}
|
|
1040
|
+
return { content: [{ type: 'text', text: 'unknown tool' }], isError: true }
|
|
1041
|
+
})
|
|
1042
|
+
|
|
1043
|
+
// TODO: when the external service has an inbound event, push it to Claude:
|
|
1044
|
+
//
|
|
1045
|
+
// await mcp.notification({
|
|
1046
|
+
// method: 'notifications/claude/channel',
|
|
1047
|
+
// params: {
|
|
1048
|
+
// content: 'the event body',
|
|
1049
|
+
// meta: { chat_id: '...', sender: '...' },
|
|
1050
|
+
// },
|
|
1051
|
+
// })
|
|
1052
|
+
//
|
|
1053
|
+
// Each meta key becomes an attribute on the <channel> tag. Keys must be
|
|
1054
|
+
// identifiers (letters/digits/underscores) — others are silently dropped.
|
|
1055
|
+
|
|
1056
|
+
await mcp.connect(new StdioServerTransport())
|
|
1057
|
+
`;
|
|
1058
|
+
}
|
|
978
1059
|
export async function initClaudePlugin(path, name = basename(resolve(path)), options = {}) {
|
|
979
1060
|
if (!PLUGIN_NAME.test(name))
|
|
980
1061
|
throw new Error(`Plugin name must match ${PLUGIN_NAME}`);
|
|
@@ -1029,42 +1110,68 @@ export async function initClaudePlugin(path, name = basename(resolve(path)), opt
|
|
|
1029
1110
|
};
|
|
1030
1111
|
await writeScaffoldFile(join(manifestDirectory, 'plugin.json'), `${JSON.stringify(manifest, null, 2)}\n`, options.force === true);
|
|
1031
1112
|
if (nativeLayout) {
|
|
1032
|
-
await writeScaffoldComponent(join(root, 'SKILL.md'),
|
|
1113
|
+
await writeScaffoldComponent(join(root, 'SKILL.md'), nativeSkillTemplate(name), options.force === true);
|
|
1033
1114
|
}
|
|
1034
1115
|
else {
|
|
1035
1116
|
await writeScaffoldComponent(join(root, 'commands', 'hello.md'), '# Hello\n\nDescribe the current workspace.\n', options.force === true);
|
|
1036
1117
|
}
|
|
1037
1118
|
if (components.has('skills')) {
|
|
1038
|
-
await writeScaffoldComponent(join(root, 'skills', 'example', 'SKILL.md'), '
|
|
1119
|
+
await writeScaffoldComponent(join(root, 'skills', 'example', 'SKILL.md'), nativeSkillTemplate('example'), options.force === true);
|
|
1039
1120
|
}
|
|
1040
1121
|
if (components.has('agents')) {
|
|
1041
|
-
await writeScaffoldComponent(join(root, 'agents', nativeLayout ? 'example.md' : 'reviewer.md'),
|
|
1122
|
+
await writeScaffoldComponent(join(root, 'agents', nativeLayout ? 'example.md' : 'reviewer.md'), nativeLayout
|
|
1123
|
+
? '---\nname: example\ndescription: TODO — when should Claude delegate to this subagent?\ntools:\n - Read\n - Grep\n---\n\nTODO: system prompt for the subagent.\n'
|
|
1124
|
+
: '---\nname: example\ndescription: Review changes\n---\n\nReview the current changes.\n', options.force === true);
|
|
1042
1125
|
}
|
|
1043
1126
|
if (components.has('hooks')) {
|
|
1044
|
-
|
|
1127
|
+
if (nativeLayout) {
|
|
1128
|
+
await writeScaffoldComponent(join(root, 'hooks-handlers', 'on-session-start.ts'), '#!/usr/bin/env bun\n// SessionStart hook handler. Reads the event from stdin, writes a JSON result\n// to stdout. Swap "bun" for "node" or "python3" in hooks/hooks.json if your\n// users\' environment lacks bun.\nconst input = await new Response(Bun.stdin.stream()).text()\nconst event = JSON.parse(input)\nprocess.stdout.write(JSON.stringify({}))\n', options.force === true);
|
|
1129
|
+
}
|
|
1130
|
+
await writeScaffoldComponent(join(root, 'hooks', 'hooks.json'), nativeLayout
|
|
1131
|
+
? '{\n "hooks": {\n "SessionStart": [\n {\n "hooks": [\n {\n "type": "command",\n "command": "bun ${CLAUDE_PLUGIN_ROOT}/hooks-handlers/on-session-start.ts"\n }\n ]\n }\n ]\n }\n}\n'
|
|
1132
|
+
: '{\n "hooks": {\n "SessionStart": []\n }\n}\n', options.force === true);
|
|
1045
1133
|
}
|
|
1046
1134
|
if (components.has('mcp') || components.has('channel')) {
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
[
|
|
1135
|
+
const mcpServers = components.has('channel')
|
|
1136
|
+
? {
|
|
1137
|
+
[name]: {
|
|
1138
|
+
command: 'bun',
|
|
1139
|
+
args: [
|
|
1140
|
+
'run',
|
|
1141
|
+
'--cwd',
|
|
1142
|
+
'${CLAUDE_PLUGIN_ROOT}',
|
|
1143
|
+
'--shell=bun',
|
|
1144
|
+
'--silent',
|
|
1145
|
+
'start',
|
|
1146
|
+
],
|
|
1147
|
+
},
|
|
1148
|
+
}
|
|
1149
|
+
: {
|
|
1150
|
+
'example-remote': {
|
|
1151
|
+
type: 'http',
|
|
1152
|
+
url: 'https://example.com/mcp',
|
|
1153
|
+
},
|
|
1154
|
+
'example-local': {
|
|
1050
1155
|
command: 'npx',
|
|
1051
1156
|
args: ['<your-mcp-server-package>'],
|
|
1052
1157
|
},
|
|
1053
|
-
}
|
|
1054
|
-
}, null, 2)}\n`, options.force === true);
|
|
1158
|
+
};
|
|
1159
|
+
await writeScaffoldComponent(join(root, '.mcp.json'), `${JSON.stringify({ mcpServers }, null, 2)}\n`, options.force === true);
|
|
1055
1160
|
}
|
|
1056
1161
|
if (components.has('lsp')) {
|
|
1057
|
-
await writeScaffoldComponent(join(root, '.lsp.json'), '{\n "example": {\n "command": "example-language-server",\n "args": ["--stdio"]\n }\n}\n', options.force === true);
|
|
1162
|
+
await writeScaffoldComponent(join(root, '.lsp.json'), '{\n "example": {\n "command": "example-language-server",\n "args": [\n "--stdio"\n ],\n "extensionToLanguage": {\n ".example": "example"\n }\n }\n}\n', options.force === true);
|
|
1058
1163
|
}
|
|
1059
1164
|
if (components.has('output-style')) {
|
|
1060
|
-
await writeScaffoldComponent(join(root, 'output-styles', `${name}.md`), `---\nname: ${name}\ndescription: TODO —
|
|
1165
|
+
await writeScaffoldComponent(join(root, 'output-styles', `${name}.md`), `---\nname: ${name}\ndescription: TODO — one line shown in the Output style picker in /config\nforce-for-plugin: true\nkeep-coding-instructions: true\n---\n\nTODO: the style prompt. This is appended to Claude's system prompt while the\nstyle is active. With force-for-plugin: true, the style applies automatically\nwhen this plugin is enabled.\n`, options.force === true);
|
|
1061
1166
|
}
|
|
1062
1167
|
if (components.has('channel')) {
|
|
1063
|
-
await writeScaffoldComponent(join(root, 'server.ts'),
|
|
1168
|
+
await writeScaffoldComponent(join(root, 'server.ts'), nativeChannelServerTemplate(name), options.force === true);
|
|
1064
1169
|
await writeScaffoldComponent(join(root, 'package.json'), `${JSON.stringify({
|
|
1065
1170
|
name: `claude-channel-${name}`,
|
|
1066
1171
|
version: '0.1.0',
|
|
1067
1172
|
type: 'module',
|
|
1173
|
+
scripts: { start: 'bun install --no-summary && bun server.ts' },
|
|
1174
|
+
dependencies: { '@modelcontextprotocol/sdk': '^1.0.0' },
|
|
1068
1175
|
}, null, 2)}\n`, options.force === true);
|
|
1069
1176
|
}
|
|
1070
1177
|
return validateClaudePlugin(root);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "praxis-agent",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.2",
|
|
4
4
|
"description": "Local-first, single-user general agent for the command line.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "wuqisen",
|
|
@@ -62,6 +62,11 @@
|
|
|
62
62
|
"test:mcp-resource-compat": "npm run build && node scripts/verify-mcp-resource-compatibility.mjs",
|
|
63
63
|
"test:notebook-compat": "npm run build && node scripts/verify-notebook-compatibility.mjs",
|
|
64
64
|
"test:context-compat": "npm run build && node scripts/verify-context-runtime.mjs",
|
|
65
|
+
"test:cross-version-session-compat": "npm run build && node scripts/verify-cross-version-session-compatibility.mjs",
|
|
66
|
+
"test:cross-version-resume-at-compat": "npm run build && node scripts/verify-cross-version-resume-at-compatibility.mjs",
|
|
67
|
+
"test:cross-version-fork-compat": "npm run build && node scripts/verify-cross-version-fork-compatibility.mjs",
|
|
68
|
+
"test:cross-version-sidechain-compat": "npm run build && node scripts/verify-cross-version-sidechain-compatibility.mjs",
|
|
69
|
+
"test:cross-version-compaction-compat": "npm run build && node scripts/verify-cross-version-compaction-compatibility.mjs",
|
|
65
70
|
"test:dynamic-system-compat": "npm run build && node scripts/verify-dynamic-system-prompt.mjs",
|
|
66
71
|
"test:permission-compat": "npm run build && node scripts/verify-claude-permissions.mjs",
|
|
67
72
|
"test:permission-prompt-compat": "npm run build && node scripts/verify-permission-prompt-tool.mjs",
|