pi-local-agents-only 0.1.3 → 0.1.5
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/CHANGELOG.md +10 -0
- package/README.md +6 -0
- package/extensions/local-agents-only.js +224 -43
- package/package.json +2 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.5 - 2026-04-07
|
|
4
|
+
|
|
5
|
+
- strip already-loaded global `AGENTS.md` / `CLAUDE.md` context reliably instead of rereading live files from disk
|
|
6
|
+
- remove the now-empty `# Project Context` section when only global context was loaded
|
|
7
|
+
- handle prompts whose custom prompt text also mentions the `# Project Context` heading
|
|
8
|
+
- make `/local-agents-only off` report when the repo is still enabled via the global allowlist or `PI_LOCAL_AGENTS_ONLY`
|
|
9
|
+
- document the repo-marker-only behavior of `/local-agents-only off`
|
|
10
|
+
- add integration tests against pi's real system prompt builder and command UX regressions
|
package/README.md
CHANGED
|
@@ -30,6 +30,8 @@ Disable for the current repo:
|
|
|
30
30
|
/local-agents-only off
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
`/local-agents-only off` clears the repo marker only. If the repo is still enabled via `/local-agents-only global-on` or `PI_LOCAL_AGENTS_ONLY=1`, it remains enabled until you also run `/local-agents-only global-off` or unset the env var.
|
|
34
|
+
|
|
33
35
|
Enable or disable via the global allowlist:
|
|
34
36
|
|
|
35
37
|
```bash
|
|
@@ -49,6 +51,8 @@ Repo opt-in uses this marker file:
|
|
|
49
51
|
.pi/local-agents-only
|
|
50
52
|
```
|
|
51
53
|
|
|
54
|
+
For git repos, marker and global allowlist activation apply across linked worktrees.
|
|
55
|
+
|
|
52
56
|
Env override for one run:
|
|
53
57
|
|
|
54
58
|
```bash
|
|
@@ -57,3 +61,5 @@ PI_LOCAL_AGENTS_ONLY=0 pi
|
|
|
57
61
|
```
|
|
58
62
|
|
|
59
63
|
This changes the prompt the model sees. It does not change pi's startup header.
|
|
64
|
+
|
|
65
|
+
If you toggle it during an existing session, start a fresh turn or `/new` for the cleanest verification.
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Purpose: Strip pi's global AGENTS.md and CLAUDE.md blocks from the effective prompt for opted-in projects.
|
|
3
|
-
* Responsibilities: Detect repo opt-in state, manage repo and global toggles, and remove matching global context blocks before model calls.
|
|
3
|
+
* Responsibilities: Detect repo and worktree opt-in state, manage repo and global toggles, add a local-only guardrail, and remove matching global context blocks before model calls.
|
|
4
4
|
* Scope: Works as a pi extension package. It changes only the prompt the model sees, not pi's startup header.
|
|
5
5
|
* Usage: Install the package, then use `/local-agents-only on|off|status|global-on|global-off`.
|
|
6
|
-
* Invariants/Assumptions: pi injects context files as `## /absolute/path\n\n<file contents>\n\n
|
|
6
|
+
* Invariants/Assumptions: pi injects context files as `## /absolute/path\n\n<file contents>\n\n`; git worktrees that share a common git dir should share local-agents-only state.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { execFileSync } from "node:child_process";
|
|
9
10
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
10
11
|
import { homedir } from "node:os";
|
|
11
12
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -15,6 +16,10 @@ const MARKER = join(".pi", COMMAND);
|
|
|
15
16
|
const GLOBAL_CONTEXT_FILES = ["AGENTS.md", "CLAUDE.md"];
|
|
16
17
|
const ENV_TRUE = ["1", "true", "yes", "on"];
|
|
17
18
|
const ENV_FALSE = ["0", "false", "no", "off"];
|
|
19
|
+
const PROJECT_CONTEXT_HEADER = "\n\n# Project Context\n\nProject-specific instructions and guidelines:\n\n";
|
|
20
|
+
const SKILLS_HEADER = "\n\nThe following skills provide specialized instructions for specific tasks.";
|
|
21
|
+
const DATE_HEADER = "\nCurrent date:";
|
|
22
|
+
const CONTEXT_BLOCK_HEADER = /^## ([^\n]+(?:AGENTS|CLAUDE)\.md)\n\n/gm;
|
|
18
23
|
|
|
19
24
|
const getAgentDir = () => {
|
|
20
25
|
const env = process.env.PI_CODING_AGENT_DIR;
|
|
@@ -29,19 +34,54 @@ const getAgentDir = () => {
|
|
|
29
34
|
const normalizePath = (path) => resolve(path).replace(/\\/g, "/");
|
|
30
35
|
const CONFIG = () => join(getAgentDir(), `${COMMAND}.json`);
|
|
31
36
|
const getMarkerPath = (projectRoot) => join(projectRoot, MARKER);
|
|
32
|
-
const
|
|
37
|
+
const uniqueSorted = (values) => [...new Set(values.map(normalizePath))].sort();
|
|
38
|
+
const walkUp = (start, predicate) => {
|
|
39
|
+
let current = resolve(start);
|
|
40
|
+
while (true) {
|
|
41
|
+
if (predicate(current)) {
|
|
42
|
+
return current;
|
|
43
|
+
}
|
|
44
|
+
const parent = dirname(current);
|
|
45
|
+
if (parent === current) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
current = parent;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const runGit = (start, args) => {
|
|
33
52
|
try {
|
|
34
|
-
|
|
35
|
-
|
|
53
|
+
return execFileSync("git", args, {
|
|
54
|
+
cwd: resolve(start),
|
|
55
|
+
encoding: "utf8",
|
|
56
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
57
|
+
}).trim();
|
|
36
58
|
} catch {
|
|
37
|
-
return
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const readConfig = (configPath = CONFIG()) => {
|
|
63
|
+
try {
|
|
64
|
+
const { projects = [], repositories = [] } = JSON.parse(readFileSync(configPath, "utf8"));
|
|
65
|
+
return {
|
|
66
|
+
projects: Array.isArray(projects) ? projects.map(normalizePath) : [],
|
|
67
|
+
repositories: Array.isArray(repositories) ? repositories.map(normalizePath) : [],
|
|
68
|
+
};
|
|
69
|
+
} catch {
|
|
70
|
+
return { projects: [], repositories: [] };
|
|
38
71
|
}
|
|
39
72
|
};
|
|
40
|
-
const
|
|
73
|
+
const writeConfig = ({ projects, repositories }, configPath = CONFIG()) => {
|
|
41
74
|
mkdirSync(dirname(configPath), { recursive: true });
|
|
42
75
|
writeFileSync(
|
|
43
76
|
configPath,
|
|
44
|
-
JSON.stringify(
|
|
77
|
+
JSON.stringify(
|
|
78
|
+
{
|
|
79
|
+
projects: uniqueSorted(projects),
|
|
80
|
+
repositories: uniqueSorted(repositories),
|
|
81
|
+
},
|
|
82
|
+
null,
|
|
83
|
+
2,
|
|
84
|
+
) + "\n",
|
|
45
85
|
);
|
|
46
86
|
};
|
|
47
87
|
const getEnvToggle = (value = process.env.PI_LOCAL_AGENTS_ONLY) => {
|
|
@@ -53,79 +93,220 @@ const getEnvToggle = (value = process.env.PI_LOCAL_AGENTS_ONLY) => {
|
|
|
53
93
|
return false;
|
|
54
94
|
}
|
|
55
95
|
};
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
96
|
+
const getGlobalContextPaths = (agentDir = getAgentDir()) => GLOBAL_CONTEXT_FILES.map((name) => join(agentDir, name));
|
|
97
|
+
const getExistingGlobalContextPaths = (agentDir = getAgentDir()) =>
|
|
98
|
+
getGlobalContextPaths(agentDir).filter((path) => existsSync(path));
|
|
99
|
+
const getContextSectionEnd = (prompt, offset) => {
|
|
100
|
+
const candidates = [prompt.indexOf(SKILLS_HEADER, offset), prompt.indexOf(DATE_HEADER, offset)].filter(
|
|
101
|
+
(index) => index !== -1,
|
|
102
|
+
);
|
|
103
|
+
return candidates.length > 0 ? Math.min(...candidates) : prompt.length;
|
|
104
|
+
};
|
|
105
|
+
const getContextBlocks = (contextSection) => {
|
|
106
|
+
const matches = [...contextSection.matchAll(CONTEXT_BLOCK_HEADER)];
|
|
107
|
+
return matches.map((match, index) => ({
|
|
108
|
+
path: match[1],
|
|
109
|
+
start: match.index,
|
|
110
|
+
end: index + 1 < matches.length ? matches[index + 1].index : contextSection.length,
|
|
111
|
+
}));
|
|
112
|
+
};
|
|
113
|
+
const stripGlobalContext = (prompt, globalPaths = getGlobalContextPaths()) => {
|
|
114
|
+
const sectionStart = prompt.lastIndexOf(PROJECT_CONTEXT_HEADER);
|
|
115
|
+
if (sectionStart === -1) {
|
|
116
|
+
return { prompt, removedPaths: [] };
|
|
117
|
+
}
|
|
118
|
+
const contextStart = sectionStart + PROJECT_CONTEXT_HEADER.length;
|
|
119
|
+
const sectionEnd = getContextSectionEnd(prompt, contextStart);
|
|
120
|
+
const contextSection = prompt.slice(contextStart, sectionEnd);
|
|
121
|
+
const blocks = getContextBlocks(contextSection);
|
|
122
|
+
if (blocks.length === 0) {
|
|
123
|
+
return { prompt, removedPaths: [] };
|
|
124
|
+
}
|
|
125
|
+
const globalPathKeys = new Set(globalPaths.map(normalizePath));
|
|
126
|
+
const keptBlocks = [];
|
|
127
|
+
const removedPaths = [];
|
|
128
|
+
for (const block of blocks) {
|
|
129
|
+
const blockText = contextSection.slice(block.start, block.end);
|
|
130
|
+
if (globalPathKeys.has(normalizePath(block.path))) {
|
|
131
|
+
removedPaths.push(block.path);
|
|
132
|
+
} else {
|
|
133
|
+
keptBlocks.push(blockText);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (removedPaths.length === 0) {
|
|
137
|
+
return { prompt, removedPaths: [] };
|
|
138
|
+
}
|
|
139
|
+
const prefix = prompt.slice(0, sectionStart);
|
|
140
|
+
const suffix = prompt.slice(sectionEnd);
|
|
141
|
+
if (keptBlocks.length === 0) {
|
|
142
|
+
return { prompt: `${prefix}${suffix}`, removedPaths: uniqueSorted(removedPaths) };
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
prompt: `${prefix}${PROJECT_CONTEXT_HEADER}${keptBlocks.join("")}${suffix}`,
|
|
146
|
+
removedPaths: uniqueSorted(removedPaths),
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
const getGitTopLevel = (start) => {
|
|
150
|
+
const topLevel = runGit(start, ["rev-parse", "--show-toplevel"]);
|
|
151
|
+
return topLevel ? normalizePath(topLevel) : undefined;
|
|
152
|
+
};
|
|
153
|
+
const getGitCommonDir = (start) => {
|
|
154
|
+
const commonDir = runGit(start, ["rev-parse", "--git-common-dir"]);
|
|
155
|
+
return commonDir ? normalizePath(resolve(start, commonDir)) : undefined;
|
|
156
|
+
};
|
|
157
|
+
const getWorktreeRoots = (start) => {
|
|
158
|
+
const list = runGit(start, ["worktree", "list", "--porcelain"]);
|
|
159
|
+
if (!list) {
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
return uniqueSorted(
|
|
163
|
+
list
|
|
164
|
+
.split(/\r?\n/u)
|
|
165
|
+
.filter((line) => line.startsWith("worktree "))
|
|
166
|
+
.map((line) => line.slice("worktree ".length)),
|
|
167
|
+
);
|
|
168
|
+
};
|
|
169
|
+
const getProjectState = (start = process.cwd()) => {
|
|
170
|
+
const normalizedStart = normalizePath(start);
|
|
171
|
+
const gitTopLevel = getGitTopLevel(normalizedStart);
|
|
172
|
+
const projectRoot =
|
|
173
|
+
gitTopLevel ||
|
|
174
|
+
walkUp(normalizedStart, (dir) => existsSync(getMarkerPath(dir))) ||
|
|
175
|
+
walkUp(normalizedStart, (dir) => existsSync(join(dir, ".pi"))) ||
|
|
176
|
+
normalizedStart;
|
|
177
|
+
const worktreeRoots = getWorktreeRoots(normalizedStart);
|
|
178
|
+
return {
|
|
179
|
+
start: normalizedStart,
|
|
180
|
+
projectRoot: normalizePath(projectRoot),
|
|
181
|
+
repoId: getGitCommonDir(normalizedStart) || normalizePath(projectRoot),
|
|
182
|
+
worktreeRoots:
|
|
183
|
+
worktreeRoots.length > 0 ? uniqueSorted([projectRoot, ...worktreeRoots]) : [normalizePath(projectRoot)],
|
|
184
|
+
};
|
|
185
|
+
};
|
|
186
|
+
const getMarkerRoots = (state) => uniqueSorted([state.projectRoot, ...state.worktreeRoots]);
|
|
187
|
+
const hasMarker = (state) => getMarkerRoots(state).some((root) => existsSync(getMarkerPath(root)));
|
|
188
|
+
const writeMarkers = (state) => {
|
|
189
|
+
for (const root of getMarkerRoots(state)) {
|
|
190
|
+
mkdirSync(dirname(getMarkerPath(root)), { recursive: true });
|
|
191
|
+
writeFileSync(getMarkerPath(root), "\n");
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
const clearMarkers = (state) => {
|
|
195
|
+
for (const root of getMarkerRoots(state)) {
|
|
196
|
+
rmSync(getMarkerPath(root), { force: true });
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const buildLocalOnlyNotice = (paths = getExistingGlobalContextPaths(getAgentDir())) => {
|
|
200
|
+
if (paths.length === 0) {
|
|
201
|
+
return "";
|
|
202
|
+
}
|
|
203
|
+
return [
|
|
204
|
+
"# Local Context Mode",
|
|
205
|
+
"This repo is in local-agents-only mode.",
|
|
206
|
+
"Ignore instructions from these global context files even if they appear in older session messages, summaries, or retries:",
|
|
207
|
+
...uniqueSorted(paths).map((path) => `- ${path}`),
|
|
208
|
+
"Follow only repo-local AGENTS.md or CLAUDE.md guidance for this project.",
|
|
209
|
+
].join("\n");
|
|
210
|
+
};
|
|
211
|
+
const applyLocalOnlyPrompt = (prompt, agentDir = getAgentDir()) => {
|
|
212
|
+
const { prompt: stripped, removedPaths } = stripGlobalContext(prompt, getGlobalContextPaths(agentDir));
|
|
213
|
+
const notice = buildLocalOnlyNotice(
|
|
214
|
+
removedPaths.length > 0 ? removedPaths : getExistingGlobalContextPaths(agentDir),
|
|
215
|
+
);
|
|
216
|
+
return notice ? `${stripped}\n\n${notice}` : stripped;
|
|
217
|
+
};
|
|
61
218
|
const setStatus = (ctx) => {
|
|
62
219
|
if (!ctx.hasUI) {
|
|
63
220
|
return;
|
|
64
221
|
}
|
|
65
|
-
const mode = getMode(
|
|
222
|
+
const mode = getMode(ctx.cwd);
|
|
66
223
|
ctx.ui.setStatus(COMMAND, mode.enabled ? `AGENTS: local-only (${mode.source})` : undefined);
|
|
67
224
|
};
|
|
225
|
+
const getProjectTarget = (state) =>
|
|
226
|
+
state.worktreeRoots.length > 1 ? `${state.projectRoot} and linked worktrees` : state.projectRoot;
|
|
227
|
+
const getOffNotification = (state) => {
|
|
228
|
+
const mode = getMode(state);
|
|
229
|
+
if (!mode.enabled) {
|
|
230
|
+
return `Disabled for ${getProjectTarget(state)}`;
|
|
231
|
+
}
|
|
232
|
+
if (mode.source === "global-config") {
|
|
233
|
+
return `Repo marker cleared for ${getProjectTarget(state)}, but local-agents-only is still enabled via global allowlist. Use /local-agents-only global-off to fully disable it.`;
|
|
234
|
+
}
|
|
235
|
+
if (mode.source === "env") {
|
|
236
|
+
return `Repo marker cleared for ${getProjectTarget(state)}, but local-agents-only is still enabled via PI_LOCAL_AGENTS_ONLY.`;
|
|
237
|
+
}
|
|
238
|
+
return `Repo marker cleared for ${getProjectTarget(state)}, but local-agents-only is still enabled via ${mode.source}.`;
|
|
239
|
+
};
|
|
68
240
|
|
|
69
241
|
export function findProjectRoot(start = process.cwd()) {
|
|
70
|
-
|
|
71
|
-
while (!existsSync(join(current, ".git"))) {
|
|
72
|
-
const parent = dirname(current);
|
|
73
|
-
if (parent === current) {
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
current = parent;
|
|
77
|
-
}
|
|
78
|
-
return current;
|
|
242
|
+
return getProjectState(start).projectRoot;
|
|
79
243
|
}
|
|
80
244
|
|
|
81
|
-
export function getMode(
|
|
245
|
+
export function getMode(start = process.cwd(), envValue = process.env.PI_LOCAL_AGENTS_ONLY, configPath = CONFIG()) {
|
|
246
|
+
const state = typeof start === "string" ? getProjectState(start) : start;
|
|
82
247
|
const envToggle = getEnvToggle(envValue);
|
|
83
248
|
if (envToggle !== undefined) {
|
|
84
249
|
return { enabled: envToggle, source: "env" };
|
|
85
250
|
}
|
|
86
|
-
if (
|
|
251
|
+
if (hasMarker(state)) {
|
|
87
252
|
return { enabled: true, source: "marker" };
|
|
88
253
|
}
|
|
89
|
-
|
|
254
|
+
const { projects, repositories } = readConfig(configPath);
|
|
255
|
+
if (repositories.includes(state.repoId)) {
|
|
256
|
+
return { enabled: true, source: "global-config" };
|
|
257
|
+
}
|
|
258
|
+
if (projects.includes(state.projectRoot) || state.worktreeRoots.some((root) => projects.includes(root))) {
|
|
90
259
|
return { enabled: true, source: "global-config" };
|
|
91
260
|
}
|
|
92
261
|
return { enabled: false, source: "default" };
|
|
93
262
|
}
|
|
94
263
|
|
|
95
|
-
export function stripGlobalBlocks(prompt,
|
|
96
|
-
return
|
|
264
|
+
export function stripGlobalBlocks(prompt, globalPaths = getGlobalContextPaths()) {
|
|
265
|
+
return stripGlobalContext(prompt, globalPaths).prompt;
|
|
97
266
|
}
|
|
98
267
|
|
|
99
268
|
export default function localAgentsOnly(pi) {
|
|
100
269
|
pi.registerCommand(COMMAND, {
|
|
101
270
|
description: "Use only repo-local AGENTS prompt context",
|
|
102
271
|
handler: async (args, ctx) => {
|
|
103
|
-
const
|
|
272
|
+
const state = getProjectState(ctx.cwd);
|
|
104
273
|
switch ((args.trim() || "status").toLowerCase()) {
|
|
105
274
|
case "on":
|
|
106
|
-
|
|
107
|
-
writeFileSync(getMarkerPath(projectRoot), "\n");
|
|
275
|
+
writeMarkers(state);
|
|
108
276
|
setStatus(ctx);
|
|
109
|
-
ctx.ui.notify(`Enabled
|
|
277
|
+
ctx.ui.notify(`Enabled for ${state.projectRoot}${state.worktreeRoots.length > 1 ? ` across ${state.worktreeRoots.length} worktrees` : ""}`, "info");
|
|
110
278
|
return;
|
|
111
279
|
case "off":
|
|
112
|
-
|
|
280
|
+
clearMarkers(state);
|
|
113
281
|
setStatus(ctx);
|
|
114
|
-
ctx.ui.notify(
|
|
282
|
+
ctx.ui.notify(getOffNotification(state), "info");
|
|
115
283
|
return;
|
|
116
|
-
case "global-on":
|
|
117
|
-
|
|
284
|
+
case "global-on": {
|
|
285
|
+
const config = readConfig();
|
|
286
|
+
writeConfig({
|
|
287
|
+
projects: [...config.projects, ...state.worktreeRoots],
|
|
288
|
+
repositories: [...config.repositories, state.repoId],
|
|
289
|
+
});
|
|
118
290
|
setStatus(ctx);
|
|
119
|
-
ctx.ui.notify(`Global allowlist enabled for ${
|
|
291
|
+
ctx.ui.notify(`Global allowlist enabled for ${state.projectRoot}`, "info");
|
|
120
292
|
return;
|
|
121
|
-
|
|
122
|
-
|
|
293
|
+
}
|
|
294
|
+
case "global-off": {
|
|
295
|
+
const config = readConfig();
|
|
296
|
+
writeConfig({
|
|
297
|
+
projects: config.projects.filter((path) => !state.worktreeRoots.includes(path)),
|
|
298
|
+
repositories: config.repositories.filter((id) => id !== state.repoId),
|
|
299
|
+
});
|
|
123
300
|
setStatus(ctx);
|
|
124
|
-
ctx.ui.notify(`Global allowlist disabled for ${
|
|
301
|
+
ctx.ui.notify(`Global allowlist disabled for ${state.projectRoot}`, "info");
|
|
125
302
|
return;
|
|
303
|
+
}
|
|
126
304
|
case "status": {
|
|
127
|
-
const mode = getMode(
|
|
128
|
-
ctx.ui.notify(
|
|
305
|
+
const mode = getMode(state);
|
|
306
|
+
ctx.ui.notify(
|
|
307
|
+
`local-agents-only: ${mode.enabled ? `enabled via ${mode.source}` : "disabled"} (${state.projectRoot})`,
|
|
308
|
+
"info",
|
|
309
|
+
);
|
|
129
310
|
return;
|
|
130
311
|
}
|
|
131
312
|
default:
|
|
@@ -145,6 +326,6 @@ export default function localAgentsOnly(pi) {
|
|
|
145
326
|
});
|
|
146
327
|
|
|
147
328
|
pi.on("before_agent_start", (event, ctx) => {
|
|
148
|
-
return getMode(
|
|
329
|
+
return getMode(ctx.cwd).enabled ? { systemPrompt: applyLocalOnlyPrompt(event.systemPrompt) } : undefined;
|
|
149
330
|
});
|
|
150
331
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-local-agents-only",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Pi extension that strips global AGENTS.md and CLAUDE.md from the effective prompt for selected projects.",
|
|
5
5
|
"author": "Mitch Fultz (https://github.com/fitchmultz)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"url": "https://github.com/fitchmultz/pi-local-agents-only/issues"
|
|
15
15
|
},
|
|
16
16
|
"homepage": "https://github.com/fitchmultz/pi-local-agents-only#readme",
|
|
17
|
-
"files": ["extensions", "README.md"],
|
|
17
|
+
"files": ["extensions", "README.md", "CHANGELOG.md", "LICENSE"],
|
|
18
18
|
"pi": {
|
|
19
19
|
"extensions": ["./extensions"]
|
|
20
20
|
},
|