@phyzess/pappra 0.1.0-beta.2 → 0.1.0-beta.4
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 +24 -7
- package/dist/app/doctor.js +2 -2
- 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 +3 -1
- package/dist/cli/commands/host.js +50 -2
- 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 +134 -7
- package/dist/hosts/detect.d.ts +1 -0
- package/dist/hosts/detect.js +1 -1
- package/dist/hosts/integration/bundle-export.d.ts +22 -0
- package/dist/hosts/integration/bundle-export.js +114 -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 +24605 -0
- package/dist/hosts/integration/pappra-hook.mjs +24723 -0
- package/dist/hosts/integration/service.d.ts +8 -0
- package/dist/hosts/integration/service.js +62 -30
- package/dist/hosts/integration/templates.d.ts +3 -3
- package/dist/hosts/integration/templates.js +15 -10
- 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
|
@@ -0,0 +1,114 @@
|
|
|
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') {
|
|
14
|
+
return {
|
|
15
|
+
path: '.claude-plugin/plugin.json',
|
|
16
|
+
value: {
|
|
17
|
+
name: 'pappra',
|
|
18
|
+
version,
|
|
19
|
+
description: 'Portable workspace knowledge and continuity for coding agents.',
|
|
20
|
+
hooks: './hooks/hooks.json',
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (host === 'codex') {
|
|
25
|
+
return {
|
|
26
|
+
path: 'plugin.json',
|
|
27
|
+
value: {
|
|
28
|
+
name: 'pappra',
|
|
29
|
+
version,
|
|
30
|
+
description: 'Portable workspace knowledge and continuity for coding agents.',
|
|
31
|
+
hooks: './hooks/hooks.json',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
path: 'package.json',
|
|
37
|
+
value: {
|
|
38
|
+
name: '@phyzess/pappra-pi',
|
|
39
|
+
version,
|
|
40
|
+
description: 'Pappra Pi package: bounded lifecycle hooks and skill.',
|
|
41
|
+
type: 'module',
|
|
42
|
+
pi: {
|
|
43
|
+
extensions: ['./extensions/pappra/index.ts'],
|
|
44
|
+
skills: ['./skills'],
|
|
45
|
+
},
|
|
46
|
+
peerDependencies: { '@earendil-works/pi-coding-agent': '*' },
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Writes a host-native plugin/package directory. This is an artifact export: it
|
|
52
|
+
* does not register the plugin with a host, and it never claims the host will
|
|
53
|
+
* load or trust it.
|
|
54
|
+
*/
|
|
55
|
+
export async function exportHostPluginBundle(options) {
|
|
56
|
+
const outDir = resolve(options.outDir);
|
|
57
|
+
const payload = await loadHostBundle(options.host, options.packageVersion, {
|
|
58
|
+
hookSource: join(getPackageRoot(), 'dist/hosts/integration/pappra-hook.mjs'),
|
|
59
|
+
hookExpression: pluginHookExpression(options.host),
|
|
60
|
+
});
|
|
61
|
+
const written = [];
|
|
62
|
+
const writeEntry = async (path, bytes) => {
|
|
63
|
+
const target = join(outDir, ...path.split('/'));
|
|
64
|
+
if (!isPathInside(outDir, target)) {
|
|
65
|
+
throw new Error(`Unsafe plugin bundle path: ${path}`);
|
|
66
|
+
}
|
|
67
|
+
await mkdir(dirname(target), { recursive: true });
|
|
68
|
+
await writeFile(target, bytes);
|
|
69
|
+
written.push(path);
|
|
70
|
+
};
|
|
71
|
+
for (const [path, bytes] of payload.contents) {
|
|
72
|
+
// Plugin manifests point at hooks/hooks.json directly; the rendered
|
|
73
|
+
// templates are only an intermediate artifact.
|
|
74
|
+
if (options.host !== 'pi' && path.startsWith('templates/'))
|
|
75
|
+
continue;
|
|
76
|
+
await writeEntry(path, bytes);
|
|
77
|
+
}
|
|
78
|
+
const expression = pluginHookExpression(options.host);
|
|
79
|
+
if (options.host === 'pi') {
|
|
80
|
+
const entry = payload.contents.get('templates/pi/entry.ts');
|
|
81
|
+
if (entry === undefined) {
|
|
82
|
+
throw new Error('Pi plugin bundle is missing its extension entry.');
|
|
83
|
+
}
|
|
84
|
+
await writeEntry('extensions/pappra/index.ts', entry);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const patch = renderHookConfigPatch(options.host, expression);
|
|
88
|
+
await writeEntry('hooks/hooks.json', Buffer.from(`${JSON.stringify(patch, null, 2)}\n`));
|
|
89
|
+
}
|
|
90
|
+
const manifest = hostManifest(options.host, options.packageVersion);
|
|
91
|
+
await writeEntry(manifest.path, Buffer.from(`${JSON.stringify(manifest.value, null, 2)}\n`));
|
|
92
|
+
return {
|
|
93
|
+
host: options.host,
|
|
94
|
+
outDir,
|
|
95
|
+
files: written.sort((left, right) => left.localeCompare(right)),
|
|
96
|
+
pluginManifest: manifest.path,
|
|
97
|
+
hookConfig: options.host === 'pi' ? null : 'hooks/hooks.json',
|
|
98
|
+
registration: options.host === 'claude'
|
|
99
|
+
? [
|
|
100
|
+
'Register the directory as a local plugin or marketplace entry; Pappra does not add it to host settings.',
|
|
101
|
+
'Claude Code substitutes ${CLAUDE_PLUGIN_ROOT} at hook execution time.',
|
|
102
|
+
]
|
|
103
|
+
: options.host === 'codex'
|
|
104
|
+
? [
|
|
105
|
+
'Package the directory as a Codex plugin; hook paths are relative to the plugin root.',
|
|
106
|
+
'Review the plugin before trusting it; Pappra does not install or trust it.',
|
|
107
|
+
]
|
|
108
|
+
: [
|
|
109
|
+
'Install the directory as a Pi package, for example `pi install ./pappra-pi`.',
|
|
110
|
+
'Pi installs package dependencies and requires project trust before loading.',
|
|
111
|
+
],
|
|
112
|
+
verification: 'unsupported',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -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,
|