@phyzess/pappra 0.1.0-beta.1 → 0.1.0-beta.11
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 +31 -12
- package/assets/integrations/skills/pappra/SKILL.md +7 -4
- package/assets/integrations/skills/pappra/references/hosts.md +21 -11
- package/assets/integrations/skills/pappra/references/lifecycle.md +4 -0
- package/assets/integrations/skills/pappra/references/operations.md +20 -7
- package/dist/app/collaboration.js +6 -13
- package/dist/app/doctor.js +26 -7
- package/dist/app/init.d.ts +28 -0
- package/dist/app/init.js +419 -88
- package/dist/cli/commands/docs.d.ts +1 -0
- package/dist/cli/commands/docs.js +104 -3
- package/dist/cli/commands/host.d.ts +5 -1
- package/dist/cli/commands/host.js +80 -6
- package/dist/cli/commands/init.d.ts +8 -0
- package/dist/cli/commands/init.js +186 -9
- package/dist/cli/commands/runtime.d.ts +14 -0
- package/dist/cli/commands/runtime.js +263 -0
- package/dist/cli/run.js +159 -7
- package/dist/code/service.js +14 -8
- package/dist/hosts/detect.d.ts +1 -0
- package/dist/hosts/detect.js +1 -1
- package/dist/hosts/integration/bundle-export.d.ts +24 -0
- package/dist/hosts/integration/bundle-export.js +128 -0
- package/dist/hosts/integration/bundle.d.ts +4 -1
- package/dist/hosts/integration/bundle.js +20 -2
- package/dist/hosts/integration/pappra-extension-runtime.mjs +24610 -0
- package/dist/hosts/integration/pappra-hook.mjs +24728 -0
- package/dist/hosts/integration/service.d.ts +8 -0
- package/dist/hosts/integration/service.js +71 -31
- package/dist/hosts/integration/support.d.ts +23 -0
- package/dist/hosts/integration/support.js +25 -0
- package/dist/hosts/integration/templates.d.ts +3 -3
- package/dist/hosts/integration/templates.js +15 -10
- package/dist/hosts/pi/rpc.d.ts +2 -0
- package/dist/hosts/pi/rpc.js +39 -1
- package/dist/hosts/pilot.d.ts +77 -77
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/install/runtime.d.ts +1 -1
- package/dist/install/runtime.js +117 -10
- package/dist/package-info.d.ts +2 -0
- package/dist/package-info.js +6 -0
- package/dist/schemas/host.js +4 -2
- package/dist/schemas/install.d.ts +14 -0
- package/dist/schemas/install.js +8 -0
- package/package.json +8 -2
- package/skills/pappra/SKILL.md +7 -4
- package/skills/pappra/references/hosts.md +21 -11
- package/skills/pappra/references/lifecycle.md +4 -0
- package/skills/pappra/references/operations.md +20 -7
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { getPackageRoot } from '../../package-info.js';
|
|
4
|
+
import { isPathInside } from '../../schemas/paths.js';
|
|
5
|
+
import { loadHostBundle } from './bundle.js';
|
|
6
|
+
import { renderHookConfigPatch } from './templates.js';
|
|
7
|
+
function pluginHookExpression(host) {
|
|
8
|
+
return host === 'claude'
|
|
9
|
+
? '${CLAUDE_PLUGIN_ROOT}/hooks/pappra-hook.mjs'
|
|
10
|
+
: './hooks/pappra-hook.mjs';
|
|
11
|
+
}
|
|
12
|
+
function hostManifest(host, version) {
|
|
13
|
+
if (host === 'claude' || host === 'codex') {
|
|
14
|
+
// Codex reads the Claude plugin manifest path, so one manifest serves both.
|
|
15
|
+
return {
|
|
16
|
+
path: '.claude-plugin/plugin.json',
|
|
17
|
+
value: {
|
|
18
|
+
name: 'pappra',
|
|
19
|
+
version,
|
|
20
|
+
description: 'Portable workspace knowledge and continuity for coding agents.',
|
|
21
|
+
author: { name: 'phyzess' },
|
|
22
|
+
hooks: './hooks/hooks.json',
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
path: 'package.json',
|
|
28
|
+
value: {
|
|
29
|
+
name: '@phyzess/pappra-pi',
|
|
30
|
+
version,
|
|
31
|
+
description: 'Pappra Pi package: bounded lifecycle hooks and skill.',
|
|
32
|
+
type: 'module',
|
|
33
|
+
pi: {
|
|
34
|
+
extensions: ['./extensions/pappra/index.ts'],
|
|
35
|
+
skills: ['./skills'],
|
|
36
|
+
},
|
|
37
|
+
peerDependencies: { '@earendil-works/pi-coding-agent': '*' },
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Writes a host-native plugin/package directory. This is an artifact export: it
|
|
43
|
+
* does not register the plugin with a host, and it never claims the host will
|
|
44
|
+
* load or trust it.
|
|
45
|
+
*/
|
|
46
|
+
export async function exportHostPluginBundle(options) {
|
|
47
|
+
const outRoot = resolve(options.outDir);
|
|
48
|
+
if (options.marketplace === true && options.host === 'pi') {
|
|
49
|
+
throw new Error('Pi installs a directory directly; a marketplace wrapper is not supported.');
|
|
50
|
+
}
|
|
51
|
+
const pluginPrefix = options.marketplace === true ? 'pappra/' : '';
|
|
52
|
+
const pluginRoot = options.marketplace === true ? join(outRoot, 'pappra') : outRoot;
|
|
53
|
+
const payload = await loadHostBundle(options.host, options.packageVersion, {
|
|
54
|
+
hookSource: join(getPackageRoot(), 'dist/hosts/integration/pappra-hook.mjs'),
|
|
55
|
+
hookExpression: pluginHookExpression(options.host),
|
|
56
|
+
});
|
|
57
|
+
const written = [];
|
|
58
|
+
const writeEntry = async (path, bytes) => {
|
|
59
|
+
const target = join(outRoot, ...path.split('/'));
|
|
60
|
+
if (!isPathInside(outRoot, target)) {
|
|
61
|
+
throw new Error(`Unsafe plugin bundle path: ${path}`);
|
|
62
|
+
}
|
|
63
|
+
await mkdir(dirname(target), { recursive: true });
|
|
64
|
+
await writeFile(target, bytes);
|
|
65
|
+
written.push(path);
|
|
66
|
+
};
|
|
67
|
+
for (const [path, bytes] of payload.contents) {
|
|
68
|
+
// Plugin manifests point at hooks/hooks.json directly; the rendered
|
|
69
|
+
// templates are only an intermediate artifact.
|
|
70
|
+
if (path.startsWith('templates/'))
|
|
71
|
+
continue;
|
|
72
|
+
await writeEntry(`${pluginPrefix}${path}`, bytes);
|
|
73
|
+
}
|
|
74
|
+
const expression = pluginHookExpression(options.host);
|
|
75
|
+
if (options.host === 'pi') {
|
|
76
|
+
const entry = payload.contents.get('templates/pi/entry.ts');
|
|
77
|
+
const runtime = payload.contents.get('templates/pi/runtime.mjs');
|
|
78
|
+
if (entry === undefined || runtime === undefined) {
|
|
79
|
+
throw new Error('Pi plugin bundle is missing its extension entry.');
|
|
80
|
+
}
|
|
81
|
+
// The generated entry imports ./runtime.mjs, so both files share one directory.
|
|
82
|
+
await writeEntry(`${pluginPrefix}extensions/pappra/index.ts`, entry);
|
|
83
|
+
await writeEntry(`${pluginPrefix}extensions/pappra/runtime.mjs`, runtime);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
const patch = renderHookConfigPatch(options.host, expression);
|
|
87
|
+
await writeEntry(`${pluginPrefix}hooks/hooks.json`, Buffer.from(`${JSON.stringify(patch, null, 2)}\n`));
|
|
88
|
+
}
|
|
89
|
+
const manifest = hostManifest(options.host, options.packageVersion);
|
|
90
|
+
await writeEntry(`${pluginPrefix}${manifest.path}`, Buffer.from(`${JSON.stringify(manifest.value, null, 2)}\n`));
|
|
91
|
+
if (options.marketplace === true) {
|
|
92
|
+
await writeEntry('.claude-plugin/marketplace.json', Buffer.from(`${JSON.stringify({
|
|
93
|
+
name: 'pappra-local',
|
|
94
|
+
owner: { name: 'phyzess' },
|
|
95
|
+
description: 'Local Pappra plugin marketplace exported by pappra host bundle.',
|
|
96
|
+
plugins: [
|
|
97
|
+
{
|
|
98
|
+
name: 'pappra',
|
|
99
|
+
source: './pappra',
|
|
100
|
+
description: 'Portable workspace knowledge and continuity for coding agents.',
|
|
101
|
+
author: { name: 'phyzess' },
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
}, null, 2)}\n`));
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
host: options.host,
|
|
108
|
+
outDir: pluginRoot,
|
|
109
|
+
files: written.sort((left, right) => left.localeCompare(right)),
|
|
110
|
+
pluginManifest: `${pluginPrefix}${manifest.path}`,
|
|
111
|
+
hookConfig: options.host === 'pi' ? null : `${pluginPrefix}hooks/hooks.json`,
|
|
112
|
+
registration: options.host === 'claude'
|
|
113
|
+
? [
|
|
114
|
+
'Register the directory as a local plugin or marketplace entry; Pappra does not add it to host settings.',
|
|
115
|
+
'Claude Code substitutes ${CLAUDE_PLUGIN_ROOT} at hook execution time.',
|
|
116
|
+
]
|
|
117
|
+
: options.host === 'codex'
|
|
118
|
+
? [
|
|
119
|
+
'Package the directory as a Codex plugin; hook paths are relative to the plugin root.',
|
|
120
|
+
'Review the plugin before trusting it; Pappra does not install or trust it.',
|
|
121
|
+
]
|
|
122
|
+
: [
|
|
123
|
+
'Install the directory as a Pi package, for example `pi install ./pappra-pi`.',
|
|
124
|
+
'Pi installs package dependencies and requires project trust before loading.',
|
|
125
|
+
],
|
|
126
|
+
verification: 'unsupported',
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -11,6 +11,9 @@ export declare function installPathForBundleFile(host: HostName, sourcePath: str
|
|
|
11
11
|
*/
|
|
12
12
|
export declare function loadHostBundle(host: HostName, packageVersion: string, options?: {
|
|
13
13
|
readonly assetsRoot?: string;
|
|
14
|
-
|
|
14
|
+
/** Absolute path of the bundled, dependency-free hook file. */
|
|
15
|
+
readonly hookSource?: string;
|
|
16
|
+
/** Command-line path expression used inside the host hook configuration. */
|
|
17
|
+
readonly hookExpression?: string;
|
|
15
18
|
readonly observedAt?: string;
|
|
16
19
|
}): Promise<HostBundlePayload>;
|
|
@@ -55,8 +55,26 @@ export async function loadHostBundle(host, packageVersion, options = {}) {
|
|
|
55
55
|
kind: source.endsWith('/SKILL.md') ? 'skill' : 'reference',
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
|
-
const
|
|
59
|
-
join(packageRoot(), 'dist/hosts/integration/hook
|
|
58
|
+
const hookSource = options.hookSource ??
|
|
59
|
+
join(packageRoot(), 'dist/hosts/integration/pappra-hook.mjs');
|
|
60
|
+
const hookBytes = await readFile(hookSource);
|
|
61
|
+
contents.set('hooks/pappra-hook.mjs', hookBytes);
|
|
62
|
+
files.push({
|
|
63
|
+
path: 'hooks/pappra-hook.mjs',
|
|
64
|
+
digest: sha256Digest(hookBytes),
|
|
65
|
+
kind: 'hook',
|
|
66
|
+
});
|
|
67
|
+
if (host === 'pi') {
|
|
68
|
+
const runtimePath = join(dirname(hookSource), 'pappra-extension-runtime.mjs');
|
|
69
|
+
const runtimeBytes = await readFile(runtimePath);
|
|
70
|
+
contents.set('templates/pi/runtime.mjs', runtimeBytes);
|
|
71
|
+
files.push({
|
|
72
|
+
path: 'templates/pi/runtime.mjs',
|
|
73
|
+
digest: sha256Digest(runtimeBytes),
|
|
74
|
+
kind: 'extension',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const template = renderHostTemplate(host, options.hookExpression ?? hookSource);
|
|
60
78
|
contents.set(template.path, template.bytes);
|
|
61
79
|
files.push({
|
|
62
80
|
path: template.path,
|