@introspection-ai/pi-recipes 0.1.0-beta.0 → 0.1.0-beta.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/README.md +5 -1
- package/dist/cli.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/pi-extension.js +0 -2
- package/dist/recipe-agent.js +5 -6
- package/dist/recipe-dev.js +1 -2
- package/dist/recipe-package.d.ts +0 -1
- package/dist/recipe-package.js +1 -5
- package/dist/recipe-publish.d.ts +1 -0
- package/dist/recipe-publish.js +26 -0
- package/dist/recipe-store.d.ts +2 -0
- package/dist/recipe-store.js +6 -0
- package/dist/recipe-telemetry.d.ts +44 -0
- package/dist/recipe-telemetry.js +79 -0
- package/docs/pi-extension.md +8 -7
- package/docs/recipe-cli.md +40 -4
- package/docs/recipe-flow.md +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ those recipe files into the live Pi session at launch time.
|
|
|
13
13
|
`package.json` owns both recipe identity and Node dependency metadata. The
|
|
14
14
|
top-level `name`, `version`, and `description` identify the recipe, while the
|
|
15
15
|
`pi` block declares recipe resources such as agents, extensions, skills,
|
|
16
|
-
|
|
16
|
+
and prompts.
|
|
17
17
|
|
|
18
18
|
## Documentation
|
|
19
19
|
|
|
@@ -88,6 +88,10 @@ repository and pushes the local recipe:
|
|
|
88
88
|
recipes publish ./my-recipe --github owner/my-recipe --visibility private
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
Use `--visibility public` to submit the recipe's public GitHub metadata to the
|
|
92
|
+
marketplace catalog after a successful push. Catalog submissions are
|
|
93
|
+
best-effort; private publishes are not listed.
|
|
94
|
+
|
|
91
95
|
Customize an installed recipe into an editable local copy:
|
|
92
96
|
|
|
93
97
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -274,6 +274,7 @@ function printPublishedRecipe(result) {
|
|
|
274
274
|
`Repository: ${result.createdRepository ? "created" : "existing"}`,
|
|
275
275
|
`Commit: ${result.committed ? "created" : "no changes"}`,
|
|
276
276
|
"Push: ok",
|
|
277
|
+
`Catalog: ${result.catalogued ? "submitted" : "skipped"}`,
|
|
277
278
|
"",
|
|
278
279
|
"Use:",
|
|
279
280
|
` pi --recipe ${result.shortName}`,
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/pi-extension.js
CHANGED
|
@@ -481,7 +481,6 @@ export function createPiRecipesExtension(opts = {}) {
|
|
|
481
481
|
agent: resolved.agent,
|
|
482
482
|
skillPaths: packageResourcePaths(manifest, "skills"),
|
|
483
483
|
promptPaths: packageResourcePaths(manifest, "prompts"),
|
|
484
|
-
themePaths: packageResourcePaths(manifest, "themes"),
|
|
485
484
|
extensionPaths,
|
|
486
485
|
extensionsLoaded: false,
|
|
487
486
|
configured: false,
|
|
@@ -791,7 +790,6 @@ export function createPiRecipesExtension(opts = {}) {
|
|
|
791
790
|
return {
|
|
792
791
|
skillPaths: launchState.skillPaths,
|
|
793
792
|
promptPaths: launchState.promptPaths,
|
|
794
|
-
themePaths: launchState.themePaths,
|
|
795
793
|
};
|
|
796
794
|
});
|
|
797
795
|
pi.on("before_agent_start", (event, ctx) => {
|
package/dist/recipe-agent.js
CHANGED
|
@@ -37,13 +37,12 @@ function parseModel(data) {
|
|
|
37
37
|
}
|
|
38
38
|
function parseSystemInstructions(data) {
|
|
39
39
|
const raw = asRecord(data.system_instructions ?? data.systemInstructions);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return prompt ? { mode: "append", content: prompt } : undefined;
|
|
40
|
+
if (Object.hasOwn(raw, "content") && typeof raw.content === "string") {
|
|
41
|
+
const mode = raw.mode === "replace" ? "replace" : "append";
|
|
42
|
+
return { mode, content: raw.content.trim() };
|
|
44
43
|
}
|
|
45
|
-
const
|
|
46
|
-
return { mode, content };
|
|
44
|
+
const prompt = typeof data.prompt === "string" ? data.prompt.trim() : "";
|
|
45
|
+
return prompt ? { mode: "append", content: prompt } : undefined;
|
|
47
46
|
}
|
|
48
47
|
function parseExtensions(data) {
|
|
49
48
|
const raw = asRecord(data.extensions);
|
package/dist/recipe-dev.js
CHANGED
|
@@ -7,7 +7,6 @@ const RESOURCE_KEYS = [
|
|
|
7
7
|
"extensions",
|
|
8
8
|
"skills",
|
|
9
9
|
"prompts",
|
|
10
|
-
"themes",
|
|
11
10
|
];
|
|
12
11
|
function recipeNameFromTarget(target) {
|
|
13
12
|
const name = basename(resolve(target)).replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
@@ -165,7 +164,7 @@ export function createRecipePublishGuide(recipeDir) {
|
|
|
165
164
|
checklist: [
|
|
166
165
|
"Run `recipes doctor .` and fix any errors.",
|
|
167
166
|
`Run \`recipes publish . --github owner/${repositoryName} --visibility private\` to create, commit, and push a GitHub repository.`,
|
|
168
|
-
"Commit package.json, agents, prompts, skills,
|
|
167
|
+
"Commit package.json, agents, prompts, skills, extensions, and SYSTEM.md.",
|
|
169
168
|
"If extensions have runtime dependencies, commit the package lockfile.",
|
|
170
169
|
"Push the recipe to a Git repository.",
|
|
171
170
|
"Tag releases when users should install a stable version.",
|
package/dist/recipe-package.d.ts
CHANGED
package/dist/recipe-package.js
CHANGED
|
@@ -5,7 +5,6 @@ const RESOURCE_KEYS = [
|
|
|
5
5
|
"extensions",
|
|
6
6
|
"skills",
|
|
7
7
|
"prompts",
|
|
8
|
-
"themes",
|
|
9
8
|
];
|
|
10
9
|
function stringArray(value) {
|
|
11
10
|
return Array.isArray(value)
|
|
@@ -18,7 +17,6 @@ function emptyResources() {
|
|
|
18
17
|
extensions: [],
|
|
19
18
|
skills: [],
|
|
20
19
|
prompts: [],
|
|
21
|
-
themes: [],
|
|
22
20
|
};
|
|
23
21
|
}
|
|
24
22
|
function normalizeResourcePath(path) {
|
|
@@ -196,7 +194,6 @@ export function defaultPiPackageResourcePaths(pkg, key) {
|
|
|
196
194
|
agents: [join(pkg.path, "agents")],
|
|
197
195
|
skills: [join(pkg.path, "skills")],
|
|
198
196
|
prompts: [join(pkg.path, "prompts")],
|
|
199
|
-
themes: [join(pkg.path, "themes")],
|
|
200
197
|
};
|
|
201
198
|
return (defaults[key] ?? []).filter((path) => existsSync(path));
|
|
202
199
|
}
|
|
@@ -205,8 +202,7 @@ export function packageResourcePaths(pkg, key) {
|
|
|
205
202
|
return resolvePiPackageResourcePaths(pkg, key, {
|
|
206
203
|
allowEmptyGlobMatches: key === "extensions" ||
|
|
207
204
|
key === "skills" ||
|
|
208
|
-
key === "prompts"
|
|
209
|
-
key === "themes",
|
|
205
|
+
key === "prompts",
|
|
210
206
|
});
|
|
211
207
|
}
|
|
212
208
|
return defaultPiPackageResourcePaths(pkg, key);
|
package/dist/recipe-publish.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export interface PublishedRecipe {
|
|
|
25
25
|
createdRepository: boolean;
|
|
26
26
|
committed: boolean;
|
|
27
27
|
pushed: boolean;
|
|
28
|
+
catalogued: boolean;
|
|
28
29
|
}
|
|
29
30
|
export declare function publishRecipe(target: string, opts: RecipePublishOptions): Promise<PublishedRecipe>;
|
|
30
31
|
//# sourceMappingURL=recipe-publish.d.ts.map
|
package/dist/recipe-publish.js
CHANGED
|
@@ -5,6 +5,7 @@ import { promisify } from "node:util";
|
|
|
5
5
|
import { validateRecipeDirectory } from "./recipe-dev.js";
|
|
6
6
|
import { readPiPackageManifest } from "./recipe-package.js";
|
|
7
7
|
import { addRecipe, customizeRecipe, defaultRecipeStoreDir, findInstalledRecipe, recipePreferredIdentifier, recipeScopedIdentifier, } from "./recipe-store.js";
|
|
8
|
+
import { sendPublishTelemetry, telemetryDisabled } from "./recipe-telemetry.js";
|
|
8
9
|
const execFileAsync = promisify(execFile);
|
|
9
10
|
function isSafeGithubName(value) {
|
|
10
11
|
return /^[a-zA-Z0-9_.-]+$/.test(value) && value !== "." && value !== "..";
|
|
@@ -82,6 +83,14 @@ function ensureGitignore(recipeDir) {
|
|
|
82
83
|
writeFileSync(gitignorePath, `${existing.join("\n").replace(/\n*$/, "")}${prefix}${missing.join("\n")}\n`);
|
|
83
84
|
return true;
|
|
84
85
|
}
|
|
86
|
+
function resourceCounts(resources) {
|
|
87
|
+
return {
|
|
88
|
+
agents: resources.agents?.length ?? 0,
|
|
89
|
+
extensions: resources.extensions?.length ?? 0,
|
|
90
|
+
skills: resources.skills?.length ?? 0,
|
|
91
|
+
prompts: resources.prompts?.length ?? 0,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
85
94
|
function localPathForInput(input, cwd) {
|
|
86
95
|
const expanded = input === "~"
|
|
87
96
|
? process.env.HOME ?? input
|
|
@@ -176,7 +185,23 @@ export async function publishRecipe(target, opts) {
|
|
|
176
185
|
const createdRepository = await ensureGithubRepository(github, opts.visibility, recipeDir, env, opts.commandRunner);
|
|
177
186
|
await setOrigin(github, recipeDir, env, opts.commandRunner);
|
|
178
187
|
await runCommand("git", ["push", "-u", "origin", "HEAD:main"], { cwd: recipeDir, env }, opts.commandRunner);
|
|
188
|
+
const publishedManifest = readPiPackageManifest(recipeDir);
|
|
179
189
|
const recipe = await addRecipe(recipeDir, opts);
|
|
190
|
+
const catalogued = opts.visibility === "public" && !telemetryDisabled(env);
|
|
191
|
+
if (catalogued) {
|
|
192
|
+
const source = `github:${github.fullName}`;
|
|
193
|
+
await sendPublishTelemetry({
|
|
194
|
+
id: source,
|
|
195
|
+
name: publishedManifest.name,
|
|
196
|
+
version: publishedManifest.version,
|
|
197
|
+
...(publishedManifest.description ? { description: publishedManifest.description } : {}),
|
|
198
|
+
github: github.fullName,
|
|
199
|
+
source,
|
|
200
|
+
installCommand: `recipes install ${source}`,
|
|
201
|
+
homepage: `https://github.com/${github.fullName}`,
|
|
202
|
+
resources: resourceCounts(report.resources),
|
|
203
|
+
}, { env: opts.env, fetchImpl: opts.fetchImpl });
|
|
204
|
+
}
|
|
180
205
|
return {
|
|
181
206
|
recipe,
|
|
182
207
|
recipeDir,
|
|
@@ -187,6 +212,7 @@ export async function publishRecipe(target, opts) {
|
|
|
187
212
|
createdRepository,
|
|
188
213
|
committed,
|
|
189
214
|
pushed: true,
|
|
215
|
+
catalogued,
|
|
190
216
|
};
|
|
191
217
|
}
|
|
192
218
|
//# sourceMappingURL=recipe-publish.js.map
|
package/dist/recipe-store.d.ts
CHANGED
package/dist/recipe-store.js
CHANGED
|
@@ -5,6 +5,7 @@ import { basename, dirname, isAbsolute, join, relative, resolve } from "node:pat
|
|
|
5
5
|
import { execFile } from "node:child_process";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { readPiPackageManifest, validatePiPackageManifest, } from "./recipe-package.js";
|
|
8
|
+
import { sendInstallTelemetry } from "./recipe-telemetry.js";
|
|
8
9
|
const execFileAsync = promisify(execFile);
|
|
9
10
|
const STORE_LOCK_STALE_MS = 30_000;
|
|
10
11
|
const STORE_LOCK_TIMEOUT_MS = 10_000;
|
|
@@ -602,6 +603,11 @@ export async function addRecipe(input, opts = {}) {
|
|
|
602
603
|
].sort((a, b) => a.name.localeCompare(b.name));
|
|
603
604
|
writeRecipeStore(store, storeDir);
|
|
604
605
|
});
|
|
606
|
+
// Anonymous, best-effort install ping for the public directory. Only remote
|
|
607
|
+
// (github/git) installs are counted; local recipe registration is private.
|
|
608
|
+
if (source.kind === "github" || source.kind === "git") {
|
|
609
|
+
await sendInstallTelemetry(installed, { env: opts.env, fetchImpl: opts.fetchImpl });
|
|
610
|
+
}
|
|
605
611
|
return installed;
|
|
606
612
|
}
|
|
607
613
|
export function listRecipes(opts = {}) {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { InstalledRecipe } from "./recipe-store.js";
|
|
2
|
+
import type { RecipePackageResources } from "./recipe-package.js";
|
|
3
|
+
export declare const DEFAULT_TELEMETRY_ENDPOINT = "https://pi.recipes/api/installs";
|
|
4
|
+
export declare const DEFAULT_CATALOG_ENDPOINT = "https://pi.recipes/api/catalog/recipes";
|
|
5
|
+
export type FetchImpl = typeof fetch;
|
|
6
|
+
export interface InstallTelemetryOptions {
|
|
7
|
+
env?: NodeJS.ProcessEnv;
|
|
8
|
+
fetchImpl?: FetchImpl;
|
|
9
|
+
}
|
|
10
|
+
export interface PublishTelemetryOptions extends InstallTelemetryOptions {
|
|
11
|
+
endpoint?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface PublishTelemetryRecipe {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
version: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
github: string;
|
|
19
|
+
source: string;
|
|
20
|
+
installCommand: string;
|
|
21
|
+
homepage: string;
|
|
22
|
+
resources: Record<keyof RecipePackageResources, number>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Returns true when the user has opted out of recipe telemetry.
|
|
26
|
+
* Honors the cross-tool `DO_NOT_TRACK` convention plus a package-specific
|
|
27
|
+
* `PI_RECIPES_NO_TELEMETRY` escape hatch.
|
|
28
|
+
*/
|
|
29
|
+
export declare function telemetryDisabled(env: NodeJS.ProcessEnv): boolean;
|
|
30
|
+
export declare function telemetryEndpoint(env: NodeJS.ProcessEnv): string;
|
|
31
|
+
export declare function catalogEndpoint(env: NodeJS.ProcessEnv): string;
|
|
32
|
+
/**
|
|
33
|
+
* Send a single anonymous install ping. Resolves once the request settles or
|
|
34
|
+
* the timeout elapses; all network and serialization errors are swallowed so a
|
|
35
|
+
* telemetry failure can never break `recipes install`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function sendInstallTelemetry(recipe: InstalledRecipe, opts?: InstallTelemetryOptions): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Submit a public recipe for marketplace cataloguing. The backend owns
|
|
40
|
+
* trust/moderation fields such as `official`; clients submit only public
|
|
41
|
+
* package metadata derived from the GitHub repository and recipe manifest.
|
|
42
|
+
*/
|
|
43
|
+
export declare function sendPublishTelemetry(recipe: PublishTelemetryRecipe, opts?: PublishTelemetryOptions): Promise<void>;
|
|
44
|
+
//# sourceMappingURL=recipe-telemetry.d.ts.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Best-effort telemetry for the recipes directory at https://pi.recipes.
|
|
2
|
+
// Installs send only anonymous counters. Public publishes submit public package
|
|
3
|
+
// metadata so the directory can catalogue recipes. Telemetry never blocks or
|
|
4
|
+
// fails a recipe command.
|
|
5
|
+
export const DEFAULT_TELEMETRY_ENDPOINT = "https://pi.recipes/api/installs";
|
|
6
|
+
export const DEFAULT_CATALOG_ENDPOINT = "https://pi.recipes/api/catalog/recipes";
|
|
7
|
+
const TELEMETRY_TIMEOUT_MS = 1500;
|
|
8
|
+
/**
|
|
9
|
+
* Returns true when the user has opted out of recipe telemetry.
|
|
10
|
+
* Honors the cross-tool `DO_NOT_TRACK` convention plus a package-specific
|
|
11
|
+
* `PI_RECIPES_NO_TELEMETRY` escape hatch.
|
|
12
|
+
*/
|
|
13
|
+
export function telemetryDisabled(env) {
|
|
14
|
+
return isTruthy(env.DO_NOT_TRACK) || isTruthy(env.PI_RECIPES_NO_TELEMETRY);
|
|
15
|
+
}
|
|
16
|
+
export function telemetryEndpoint(env) {
|
|
17
|
+
const configured = env.PI_RECIPES_TELEMETRY_ENDPOINT?.trim();
|
|
18
|
+
return configured && configured.length > 0 ? configured : DEFAULT_TELEMETRY_ENDPOINT;
|
|
19
|
+
}
|
|
20
|
+
export function catalogEndpoint(env) {
|
|
21
|
+
const configured = env.PI_RECIPES_CATALOG_ENDPOINT?.trim();
|
|
22
|
+
return configured && configured.length > 0 ? configured : DEFAULT_CATALOG_ENDPOINT;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Send a single anonymous install ping. Resolves once the request settles or
|
|
26
|
+
* the timeout elapses; all network and serialization errors are swallowed so a
|
|
27
|
+
* telemetry failure can never break `recipes install`.
|
|
28
|
+
*/
|
|
29
|
+
export async function sendInstallTelemetry(recipe, opts = {}) {
|
|
30
|
+
await postTelemetry(telemetryEndpoint(opts.env ?? process.env), {
|
|
31
|
+
event: "install",
|
|
32
|
+
id: recipe.id,
|
|
33
|
+
name: recipe.name,
|
|
34
|
+
version: recipe.version,
|
|
35
|
+
}, opts);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Submit a public recipe for marketplace cataloguing. The backend owns
|
|
39
|
+
* trust/moderation fields such as `official`; clients submit only public
|
|
40
|
+
* package metadata derived from the GitHub repository and recipe manifest.
|
|
41
|
+
*/
|
|
42
|
+
export async function sendPublishTelemetry(recipe, opts = {}) {
|
|
43
|
+
const env = opts.env ?? process.env;
|
|
44
|
+
await postTelemetry(opts.endpoint ?? catalogEndpoint(env), {
|
|
45
|
+
event: "publish",
|
|
46
|
+
...recipe,
|
|
47
|
+
}, opts);
|
|
48
|
+
}
|
|
49
|
+
async function postTelemetry(endpoint, body, opts) {
|
|
50
|
+
const env = opts.env ?? process.env;
|
|
51
|
+
if (telemetryDisabled(env))
|
|
52
|
+
return;
|
|
53
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
54
|
+
if (typeof fetchImpl !== "function")
|
|
55
|
+
return;
|
|
56
|
+
const controller = new AbortController();
|
|
57
|
+
const timer = setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT_MS);
|
|
58
|
+
try {
|
|
59
|
+
await fetchImpl(endpoint, {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: { "content-type": "application/json" },
|
|
62
|
+
body: JSON.stringify(body),
|
|
63
|
+
signal: controller.signal,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Telemetry is best-effort. Never surface failures to the caller.
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
clearTimeout(timer);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function isTruthy(value) {
|
|
74
|
+
if (!value)
|
|
75
|
+
return false;
|
|
76
|
+
const normalized = value.trim().toLowerCase();
|
|
77
|
+
return normalized !== "" && normalized !== "0" && normalized !== "false";
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=recipe-telemetry.js.map
|
package/docs/pi-extension.md
CHANGED
|
@@ -84,7 +84,7 @@ On session startup, the extension:
|
|
|
84
84
|
6. Sets the Pi session name.
|
|
85
85
|
7. Sets the model and thinking level from the selected agent when specified.
|
|
86
86
|
8. Selects active tools from the selected agent.
|
|
87
|
-
9. Returns recipe resources for skills
|
|
87
|
+
9. Returns recipe resources for skills and prompts.
|
|
88
88
|
10. Composes the runtime system prompt from Pi defaults, `SYSTEM.md`, selected agent instructions, and recipe runtime context.
|
|
89
89
|
|
|
90
90
|
The current Pi working directory remains the user's project workspace. The
|
|
@@ -102,7 +102,7 @@ The `pi` block tells Pi which recipe-owned files should be loaded:
|
|
|
102
102
|
|
|
103
103
|
- agent definition globs
|
|
104
104
|
- extension globs
|
|
105
|
-
- skill
|
|
105
|
+
- skill and prompt paths
|
|
106
106
|
|
|
107
107
|
The same file also carries normal Node package metadata for extension
|
|
108
108
|
dependencies. Add `dependencies`, `peerDependencies`, `packageManager`, and a
|
|
@@ -171,6 +171,9 @@ Objects such as `model` and `extensions` merge by key. Arrays such as `tools`,
|
|
|
171
171
|
- `append`: append to the current prompt
|
|
172
172
|
- `replace`: replace the current prompt
|
|
173
173
|
|
|
174
|
+
Use `content: ""` when an agent intentionally adds no instructions beyond
|
|
175
|
+
`SYSTEM.md` but still needs to declare the field explicitly.
|
|
176
|
+
|
|
174
177
|
## Session Prompt
|
|
175
178
|
|
|
176
179
|
The session prompt is assembled from:
|
|
@@ -233,7 +236,7 @@ When a recipe is active, the extension registers:
|
|
|
233
236
|
- recipe directory
|
|
234
237
|
- project workspace
|
|
235
238
|
|
|
236
|
-
`/recipe reload` asks Pi to reload extensions, skills,
|
|
239
|
+
`/recipe reload` asks Pi to reload extensions, skills, and prompts, and
|
|
237
240
|
clears the cached recipe manifest and agent state first. Use it after editing a
|
|
238
241
|
local recipe's `package.json`, agent files, resources, or extension code.
|
|
239
242
|
|
|
@@ -243,17 +246,15 @@ The extension responds to Pi `resources_discover` with:
|
|
|
243
246
|
|
|
244
247
|
- `skillPaths`
|
|
245
248
|
- `promptPaths`
|
|
246
|
-
- `themePaths`
|
|
247
249
|
|
|
248
250
|
Declared manifest paths are used first. When omitted, conventional folders are
|
|
249
251
|
used if present:
|
|
250
252
|
|
|
251
253
|
- `skills`
|
|
252
254
|
- `prompts`
|
|
253
|
-
- `themes`
|
|
254
255
|
|
|
255
|
-
Skills become Pi `/skill:name` commands. Prompt templates
|
|
256
|
-
|
|
256
|
+
Skills become Pi `/skill:name` commands. Prompt templates are surfaced through
|
|
257
|
+
Pi's normal resource system.
|
|
257
258
|
|
|
258
259
|
## Subagents
|
|
259
260
|
|
package/docs/recipe-cli.md
CHANGED
|
@@ -133,6 +133,9 @@ model:
|
|
|
133
133
|
Objects such as `model` and `extensions` merge by key, while arrays such as
|
|
134
134
|
`tools`, `skills`, and `subagents` replace the inherited array.
|
|
135
135
|
|
|
136
|
+
Use `system_instructions.content: ""` when an agent intentionally adds no
|
|
137
|
+
instructions beyond `SYSTEM.md` but still needs to declare the field explicitly.
|
|
138
|
+
|
|
136
139
|
`SYSTEM.md` is optional. When present, Pi uses it as the recipe-level system
|
|
137
140
|
prompt before applying the selected agent's `system_instructions`.
|
|
138
141
|
|
|
@@ -183,7 +186,6 @@ The `pi` block declares recipe-owned resources:
|
|
|
183
186
|
- `extensions`: TypeScript extension globs
|
|
184
187
|
- `skills`: skill paths or globs
|
|
185
188
|
- `prompts`: prompt paths or globs
|
|
186
|
-
- `themes`: theme paths or globs
|
|
187
189
|
|
|
188
190
|
Normal package-manager fields such as `dependencies`, `optionalDependencies`,
|
|
189
191
|
`peerDependencies`, `devDependencies`, `packageManager`, and lockfile metadata
|
|
@@ -201,8 +203,7 @@ boundaries:
|
|
|
201
203
|
"agents": ["agents/*.yaml"],
|
|
202
204
|
"extensions": ["extensions/*.ts", "extensions/*/index.ts"],
|
|
203
205
|
"skills": ["skills/**/SKILL.md"],
|
|
204
|
-
"prompts": ["prompts"]
|
|
205
|
-
"themes": ["themes/*.json"]
|
|
206
|
+
"prompts": ["prompts"]
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
209
|
```
|
|
@@ -212,7 +213,6 @@ When entries are omitted, conventional folders are used if present:
|
|
|
212
213
|
- `agents`
|
|
213
214
|
- `skills`
|
|
214
215
|
- `prompts`
|
|
215
|
-
- `themes`
|
|
216
216
|
|
|
217
217
|
`extensions` are only loaded when declared.
|
|
218
218
|
|
|
@@ -448,6 +448,8 @@ recipes publish ./my-recipe --github owner/my-recipe --visibility public
|
|
|
448
448
|
`recipes publish` runs the same development validation as `doctor`, updates
|
|
449
449
|
`package.json#name` to `@owner/my-recipe`, commits local changes, creates the
|
|
450
450
|
GitHub repository when needed, pushes `main`, and re-registers the local recipe.
|
|
451
|
+
When `--visibility public` is used, it also submits the public package metadata
|
|
452
|
+
to the recipe catalog so the marketplace can add or refresh the listing.
|
|
451
453
|
|
|
452
454
|
For a standalone public recipe:
|
|
453
455
|
|
|
@@ -506,6 +508,40 @@ recipes install github:owner/repo/recipes/code-review
|
|
|
506
508
|
recipes install github:owner/repo/recipes/research#v1.0.0
|
|
507
509
|
```
|
|
508
510
|
|
|
511
|
+
## Telemetry
|
|
512
|
+
|
|
513
|
+
When you install a recipe from a remote source (`github:` or a Git URL),
|
|
514
|
+
`recipes install` sends a single anonymous ping to the public recipe directory
|
|
515
|
+
at [pi.recipes](https://pi.recipes) so it can rank recipes by install count.
|
|
516
|
+
|
|
517
|
+
The ping contains only the recipe's canonical id, name, and version:
|
|
518
|
+
|
|
519
|
+
```json
|
|
520
|
+
{ "event": "install", "id": "github:owner/repo", "name": "my-recipe", "version": "1.0.0" }
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
No paths, machine identifiers, or other personal data are sent. Local recipe
|
|
524
|
+
registration (`recipes install ./my-recipe`) is never reported. The ping is
|
|
525
|
+
best-effort and never blocks or fails an install.
|
|
526
|
+
|
|
527
|
+
When you publish with `--visibility public`, `recipes publish` sends the
|
|
528
|
+
recipe's public GitHub source, package name, version, description, install
|
|
529
|
+
command, homepage, and resource counts so it can appear in the catalog.
|
|
530
|
+
|
|
531
|
+
Opt out by setting either environment variable to a truthy value:
|
|
532
|
+
|
|
533
|
+
```bash
|
|
534
|
+
DO_NOT_TRACK=1 recipes install github:owner/repo
|
|
535
|
+
PI_RECIPES_NO_TELEMETRY=1 recipes install github:owner/repo
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Point install pings or catalog submissions at different collectors with:
|
|
539
|
+
|
|
540
|
+
```bash
|
|
541
|
+
PI_RECIPES_TELEMETRY_ENDPOINT=https://example.com/api/installs recipes install github:owner/repo
|
|
542
|
+
PI_RECIPES_CATALOG_ENDPOINT=https://example.com/api/catalog/recipes recipes publish . --github owner/my-recipe --visibility public
|
|
543
|
+
```
|
|
544
|
+
|
|
509
545
|
## Troubleshooting
|
|
510
546
|
|
|
511
547
|
If a private GitHub recipe fails to install with `github:owner/repo`, use SSH or
|
package/docs/recipe-flow.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Pi Recipes: Install, Customize, Publish
|
|
2
2
|
|
|
3
3
|
A recipe is a shareable Pi workflow package: it can add agents, instructions,
|
|
4
|
-
skills, prompts,
|
|
4
|
+
skills, prompts, and optional runtime extensions.
|
|
5
5
|
|
|
6
6
|
Use `recipes` to install recipes, make local edits, validate them, and
|
|
7
7
|
publish your own.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@introspection-ai/pi-recipes",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.1",
|
|
4
4
|
"description": "Local session tooling for package-based workflows.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
"dist/recipe-publish.js",
|
|
25
25
|
"dist/recipe-store.d.ts",
|
|
26
26
|
"dist/recipe-store.js",
|
|
27
|
+
"dist/recipe-telemetry.d.ts",
|
|
28
|
+
"dist/recipe-telemetry.js",
|
|
27
29
|
"docs",
|
|
28
30
|
"README.md",
|
|
29
31
|
"package.json"
|