getprismo 0.1.58 → 0.1.60
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/docs/manual.md
CHANGED
|
@@ -1055,6 +1055,15 @@ npx getprismo context backend
|
|
|
1055
1055
|
|
|
1056
1056
|
use these as the starting point for coding sessions instead of letting agents explore the whole repo.
|
|
1057
1057
|
|
|
1058
|
+
Each generated context pack also gets a machine-readable receipt next to it, for example:
|
|
1059
|
+
|
|
1060
|
+
```text
|
|
1061
|
+
.prismo/frontend-context.md
|
|
1062
|
+
.prismo/frontend-context.receipt.json
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
Receipts record the repo ref, intended agent surfaces, source roots, omitted context classes, a digest of the source-file slice, estimated pack tokens, stale-after policy, and the command to regenerate/verify the pack. They do not include raw prompts or source contents; the digest is an audit handle so later sessions and the dashboard can tell what a pack represents without treating it as opaque markdown.
|
|
1066
|
+
|
|
1058
1067
|
---
|
|
1059
1068
|
|
|
1060
1069
|
## tracking modes
|
|
@@ -1081,8 +1090,13 @@ no api keys. no intercepted prompts. no data uploaded.
|
|
|
1081
1090
|
├── architecture-summary.md
|
|
1082
1091
|
├── backend-summary.md
|
|
1083
1092
|
├── frontend-summary.md
|
|
1093
|
+
├── architecture-summary.receipt.json
|
|
1094
|
+
├── backend-summary.receipt.json
|
|
1095
|
+
├── frontend-summary.receipt.json
|
|
1084
1096
|
├── frontend-context.md
|
|
1097
|
+
├── frontend-context.receipt.json
|
|
1085
1098
|
├── backend-context.md
|
|
1099
|
+
├── backend-context.receipt.json
|
|
1086
1100
|
├── recommended-CLAUDE.boilerplate.md
|
|
1087
1101
|
├── recommended-AGENTS.boilerplate.md
|
|
1088
1102
|
├── recommended-.claudeignore
|
package/lib/prismo-dev/agent.js
CHANGED
package/lib/prismo-dev/cli.js
CHANGED
|
@@ -135,6 +135,7 @@ function createCli(deps) {
|
|
|
135
135
|
buildMultiSessionTimeline: _timeline,
|
|
136
136
|
buildSyncPayload,
|
|
137
137
|
loadConfig,
|
|
138
|
+
registerSelfRepair,
|
|
138
139
|
buildReceipt: _receipt,
|
|
139
140
|
buildReplay: _replay,
|
|
140
141
|
runFirewall: _firewall,
|
|
@@ -841,15 +842,37 @@ function createCli(deps) {
|
|
|
841
842
|
else console.log(renderPlannerTerminal(result));
|
|
842
843
|
return;
|
|
843
844
|
}
|
|
845
|
+
const tier = (tierIndex >= 0 ? ownArgs[tierIndex + 1] : null) || "mild";
|
|
844
846
|
const result = await runRepair(target, cause, {
|
|
845
847
|
limit: parsePositiveInt(limitIndex >= 0 ? ownArgs[limitIndex + 1] : null, 5),
|
|
846
848
|
tokenBudget: parseTokenBudget(budgetIndex >= 0 ? ownArgs[budgetIndex + 1] : null),
|
|
847
849
|
scope: scopeIndex >= 0 ? ownArgs[scopeIndex + 1] : null,
|
|
848
|
-
tier
|
|
850
|
+
tier,
|
|
849
851
|
commandArgs,
|
|
850
852
|
});
|
|
851
|
-
|
|
852
|
-
|
|
853
|
+
// Register a completed manual repair with the cloud so the backend
|
|
854
|
+
// verification loop measures it like a dashboard-queued repair —
|
|
855
|
+
// otherwise hand-run repairs never become verified savings.
|
|
856
|
+
let registered = false;
|
|
857
|
+
if (result.status === "completed" && typeof registerSelfRepair === "function") {
|
|
858
|
+
const config = loadConfig();
|
|
859
|
+
if (config && config.token) {
|
|
860
|
+
registered = await registerSelfRepair(config, {
|
|
861
|
+
generatedAt: result.generatedAt,
|
|
862
|
+
decision: { cause: result.cause, tier },
|
|
863
|
+
outcome: {
|
|
864
|
+
status: result.status,
|
|
865
|
+
statusMessage: result.statusMessage,
|
|
866
|
+
generatedFiles: (result.result && result.result.generatedFiles) || [],
|
|
867
|
+
},
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
if (json) console.log(JSON.stringify({ ...result, registered }, null, 2));
|
|
872
|
+
else {
|
|
873
|
+
console.log(renderRepairTerminal(result));
|
|
874
|
+
if (registered) console.log("\nReported to Prismo Cloud — savings will verify as new sessions come in.");
|
|
875
|
+
}
|
|
853
876
|
if (result.status === "failed") process.exitCode = 1;
|
|
854
877
|
return;
|
|
855
878
|
}
|
|
@@ -386,7 +386,9 @@ module.exports = function createCloudSync(deps) {
|
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
async function runSync(rootDir = process.cwd(), options = {}) {
|
|
389
|
-
|
|
389
|
+
// Allow an embedding host (the editor extension) to inject its own
|
|
390
|
+
// connection instead of reading the CLI's on-disk config.
|
|
391
|
+
const config = options.config || loadConfig();
|
|
390
392
|
const payload = buildSyncPayload(rootDir, {
|
|
391
393
|
limit: options.limit || config?.sync?.defaultLimit || 20,
|
|
392
394
|
tool: options.tool || "all",
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
module.exports = function createContextOptimize(deps) {
|
|
2
|
+
const crypto = require("crypto");
|
|
3
|
+
const { spawnSync } = require("child_process");
|
|
2
4
|
const {
|
|
3
5
|
fs,
|
|
4
6
|
path,
|
|
@@ -125,6 +127,111 @@ function proseList(items, empty = "none detected") {
|
|
|
125
127
|
return items && items.length ? items.join(", ") : empty;
|
|
126
128
|
}
|
|
127
129
|
|
|
130
|
+
function estimateTokens(text) {
|
|
131
|
+
return Math.ceil(String(text || "").length / 4);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function sha256(value) {
|
|
135
|
+
return `sha256:${crypto.createHash("sha256").update(String(value)).digest("hex")}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function gitValue(root, args) {
|
|
139
|
+
try {
|
|
140
|
+
const result = spawnSync("git", args, { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
141
|
+
return result.status === 0 ? result.stdout.trim() || null : null;
|
|
142
|
+
} catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function repoRef(root) {
|
|
148
|
+
return {
|
|
149
|
+
branch: gitValue(root, ["rev-parse", "--abbrev-ref", "HEAD"]),
|
|
150
|
+
commit: gitValue(root, ["rev-parse", "--short=12", "HEAD"]),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function isContextPackFile(name) {
|
|
155
|
+
return name === "architecture-summary.md" ||
|
|
156
|
+
name === "backend-summary.md" ||
|
|
157
|
+
name === "frontend-summary.md" ||
|
|
158
|
+
name.endsWith("-context.md");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function rootsFromPaths(paths) {
|
|
162
|
+
return Array.from(new Set((paths || [])
|
|
163
|
+
.map((p) => String(p || "").replace(/\\/g, "/").split("/").filter(Boolean).slice(0, 2).join("/"))
|
|
164
|
+
.filter(Boolean)))
|
|
165
|
+
.sort();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function sourceRootsForPack(ctx, name) {
|
|
169
|
+
if (name === "backend-summary.md") {
|
|
170
|
+
return rootsFromPaths([
|
|
171
|
+
...ctx.backend.api,
|
|
172
|
+
...ctx.backend.services,
|
|
173
|
+
...ctx.backend.models,
|
|
174
|
+
...ctx.backend.db,
|
|
175
|
+
...ctx.backend.auth,
|
|
176
|
+
...ctx.backend.config,
|
|
177
|
+
]);
|
|
178
|
+
}
|
|
179
|
+
if (name === "frontend-summary.md") {
|
|
180
|
+
return rootsFromPaths([
|
|
181
|
+
...ctx.frontend.app,
|
|
182
|
+
...ctx.frontend.components,
|
|
183
|
+
...ctx.frontend.apiClient,
|
|
184
|
+
...ctx.frontend.state,
|
|
185
|
+
...ctx.frontend.styling,
|
|
186
|
+
]);
|
|
187
|
+
}
|
|
188
|
+
if (name.endsWith("-context.md")) {
|
|
189
|
+
return rootsFromPaths(scopedRelevantFiles(ctx, name.replace(/-context\.md$/, "")));
|
|
190
|
+
}
|
|
191
|
+
return ctx.folders.slice(0, 30);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function matchingSourceFiles(ctx, roots) {
|
|
195
|
+
if (!roots || !roots.length) return ctx.scan.files.slice(0, 250);
|
|
196
|
+
return ctx.scan.files.filter((file) => roots.some((root) => file.path === root || file.path.startsWith(`${root}/`)));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function renderPackReceipt(ctx, name, contents) {
|
|
200
|
+
const packPath = `.prismo/${name}`;
|
|
201
|
+
const packId = name.replace(/\.md$/, "");
|
|
202
|
+
const sourceRoots = sourceRootsForPack(ctx, name);
|
|
203
|
+
const sourceFiles = matchingSourceFiles(ctx, sourceRoots)
|
|
204
|
+
.filter((file) => file.kind !== "binary")
|
|
205
|
+
.map((file) => ({
|
|
206
|
+
path: file.path,
|
|
207
|
+
size: file.size,
|
|
208
|
+
kind: file.kind,
|
|
209
|
+
tokens: file.tokens,
|
|
210
|
+
}))
|
|
211
|
+
.sort((a, b) => a.path.localeCompare(b.path));
|
|
212
|
+
return JSON.stringify({
|
|
213
|
+
schema_version: 1,
|
|
214
|
+
pack_id: packId,
|
|
215
|
+
pack_path: packPath,
|
|
216
|
+
receipt_path: `.prismo/${packId}.receipt.json`,
|
|
217
|
+
generated_at: ctx.generatedAt,
|
|
218
|
+
prismo_version: require("../../package.json").version,
|
|
219
|
+
repo_ref: repoRef(ctx.root),
|
|
220
|
+
target_agents: ["claude-code", "cursor", "codex", "mcp"],
|
|
221
|
+
source_roots: sourceRoots,
|
|
222
|
+
source_globs_digest: sha256(JSON.stringify({
|
|
223
|
+
pack_id: packId,
|
|
224
|
+
source_roots: sourceRoots,
|
|
225
|
+
files: sourceFiles,
|
|
226
|
+
})),
|
|
227
|
+
omitted_classes: ["lockfiles", "build-output", "large-logs", "coverage", "dependency-vendor", "binary-media"],
|
|
228
|
+
token_budget: 12000,
|
|
229
|
+
estimated_pack_tokens: estimateTokens(contents),
|
|
230
|
+
stale_if_older_than_ms: 86400000,
|
|
231
|
+
verify_command: `${NPX_COMMAND} optimize${ctx.scope ? ` ${ctx.scope}` : ""}`,
|
|
232
|
+
}, null, 2) + "\n";
|
|
233
|
+
}
|
|
234
|
+
|
|
128
235
|
function renderArchitectureSummary(ctx) {
|
|
129
236
|
const apiLayer = ctx.backend.api.slice(0, 6);
|
|
130
237
|
const dbLayer = ctx.backend.db.slice(0, 6);
|
|
@@ -429,9 +536,9 @@ function writeOptimizeGeneratedFile(root, relPath, contents) {
|
|
|
429
536
|
return { path: relPath, backupPath: null, replaced: existed };
|
|
430
537
|
}
|
|
431
538
|
|
|
432
|
-
function
|
|
539
|
+
function scopedRelevantFiles(ctx, scope) {
|
|
433
540
|
const scopeLower = scope.toLowerCase();
|
|
434
|
-
|
|
541
|
+
return ctx.scan.files
|
|
435
542
|
.filter((file) => {
|
|
436
543
|
const rel = file.path.toLowerCase();
|
|
437
544
|
if (scopeLower === "frontend") return rel.includes("frontend/") || rel.includes("src/app/") || rel.includes("src/components/");
|
|
@@ -442,6 +549,10 @@ function renderScopedContext(ctx, scope) {
|
|
|
442
549
|
.filter((file) => file.kind !== "binary")
|
|
443
550
|
.slice(0, 60)
|
|
444
551
|
.map((file) => file.path);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function renderScopedContext(ctx, scope) {
|
|
555
|
+
const relevant = scopedRelevantFiles(ctx, scope);
|
|
445
556
|
|
|
446
557
|
return [
|
|
447
558
|
`# ${scope.charAt(0).toUpperCase()}${scope.slice(1)} Context`,
|
|
@@ -544,7 +655,11 @@ function runOptimize(rootDir = process.cwd(), options = {}) {
|
|
|
544
655
|
const pending = getOptimizePendingFiles(ctx);
|
|
545
656
|
|
|
546
657
|
if (options.dryRun) {
|
|
547
|
-
const generatedFiles = pending.
|
|
658
|
+
const generatedFiles = pending.flatMap(([name]) => {
|
|
659
|
+
const files = [path.join(".prismo", name)];
|
|
660
|
+
if (isContextPackFile(name)) files.push(path.join(".prismo", `${name.replace(/\.md$/, "")}.receipt.json`));
|
|
661
|
+
return files;
|
|
662
|
+
});
|
|
548
663
|
generatedFiles.push(".prismo/optimize-report.md");
|
|
549
664
|
return {
|
|
550
665
|
root: ctx.root,
|
|
@@ -564,6 +679,12 @@ function runOptimize(rootDir = process.cwd(), options = {}) {
|
|
|
564
679
|
for (const [name, contents] of pending) {
|
|
565
680
|
const written = writeOptimizeGeneratedFile(ctx.root, path.join(".prismo", name), contents);
|
|
566
681
|
generated.push(written.path);
|
|
682
|
+
if (isContextPackFile(name)) {
|
|
683
|
+
const receipt = renderPackReceipt(ctx, name, contents);
|
|
684
|
+
const receiptName = `${name.replace(/\.md$/, "")}.receipt.json`;
|
|
685
|
+
const writtenReceipt = writeOptimizeGeneratedFile(ctx.root, path.join(".prismo", receiptName), receipt);
|
|
686
|
+
generated.push(writtenReceipt.path);
|
|
687
|
+
}
|
|
567
688
|
}
|
|
568
689
|
const report = renderOptimizeReport(ctx, [...generated, ".prismo/optimize-report.md"]);
|
|
569
690
|
const writtenReport = writeOptimizeReport(ctx.root, path.join(".prismo", "optimize-report.md"), report);
|
package/lib/prismo-dev-scan.js
CHANGED
|
@@ -383,6 +383,7 @@ const {
|
|
|
383
383
|
const {
|
|
384
384
|
renderAgentTerminal,
|
|
385
385
|
runAgent,
|
|
386
|
+
registerSelfRepair,
|
|
386
387
|
VALID_MODES: AGENT_VALID_MODES,
|
|
387
388
|
} = require("./prismo-dev/agent")({
|
|
388
389
|
fs,
|
|
@@ -479,6 +480,7 @@ const { runCli } = require("./prismo-dev/cli")({
|
|
|
479
480
|
NPX_COMMAND,
|
|
480
481
|
DEFAULT_PRISMO_PROXY_URL,
|
|
481
482
|
AGENT_VALID_MODES,
|
|
483
|
+
registerSelfRepair,
|
|
482
484
|
openUrl,
|
|
483
485
|
printStep,
|
|
484
486
|
getPositionals,
|
|
@@ -586,7 +588,6 @@ const { runCli } = require("./prismo-dev/cli")({
|
|
|
586
588
|
buildMultiAgentView,
|
|
587
589
|
buildSyncPayload,
|
|
588
590
|
loadConfig,
|
|
589
|
-
runFirewall,
|
|
590
591
|
});
|
|
591
592
|
|
|
592
593
|
module.exports = {
|
package/package.json
CHANGED