polymath-society 0.2.4 → 0.2.6
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/dist/cli.js +3132 -1694
- package/dist/engine/ingest-export.js +14 -8
- package/dist/engine/public-report.js +14 -8
- package/dist/index.js +1506 -57
- package/dist/pipeline/coding-agglomerate.js +439 -26
- package/dist/pipeline/coding-aggregate.js +313 -9
- package/dist/pipeline/coding-build.js +361 -8
- package/dist/pipeline/coding-coaching.js +274 -22
- package/dist/pipeline/coding-day-digest.js +351 -33
- package/dist/pipeline/coding-delegation.js +369 -22
- package/dist/pipeline/coding-expertise.js +456 -49
- package/dist/pipeline/coding-flow.js +308 -4
- package/dist/pipeline/coding-focus.js +16306 -0
- package/dist/pipeline/coding-frontier-detail.js +444 -26
- package/dist/pipeline/coding-frontier.js +356 -1
- package/dist/pipeline/coding-gap-dist.js +308 -4
- package/dist/pipeline/coding-gap.js +141 -40
- package/dist/pipeline/coding-grade.js +216 -26
- package/dist/pipeline/coding-nutshell.js +347 -17
- package/dist/pipeline/coding-projects.js +387 -7
- package/dist/pipeline/coding-walkthrough.js +324 -19
- package/dist/web/app.js +539 -113
- package/dist/web/styles.css +1 -1
- package/package.json +2 -2
|
@@ -134,9 +134,8 @@ var require_secure_json_parse = __commonJS({
|
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
// ../../scripts/coding-gap.mts
|
|
137
|
-
import { promises as
|
|
138
|
-
import
|
|
139
|
-
import path12 from "path";
|
|
137
|
+
import { promises as fs11 } from "fs";
|
|
138
|
+
import path13 from "path";
|
|
140
139
|
|
|
141
140
|
// ../../lib/agents/shared/agent.ts
|
|
142
141
|
import { readFileSync } from "fs";
|
|
@@ -2151,8 +2150,8 @@ function getErrorMap() {
|
|
|
2151
2150
|
|
|
2152
2151
|
// ../../node_modules/zod/v3/helpers/parseUtil.js
|
|
2153
2152
|
var makeIssue = (params) => {
|
|
2154
|
-
const { data, path:
|
|
2155
|
-
const fullPath = [...
|
|
2153
|
+
const { data, path: path14, errorMaps, issueData } = params;
|
|
2154
|
+
const fullPath = [...path14, ...issueData.path || []];
|
|
2156
2155
|
const fullIssue = {
|
|
2157
2156
|
...issueData,
|
|
2158
2157
|
path: fullPath
|
|
@@ -2268,11 +2267,11 @@ var errorUtil;
|
|
|
2268
2267
|
|
|
2269
2268
|
// ../../node_modules/zod/v3/types.js
|
|
2270
2269
|
var ParseInputLazyPath = class {
|
|
2271
|
-
constructor(parent, value,
|
|
2270
|
+
constructor(parent, value, path14, key) {
|
|
2272
2271
|
this._cachedPath = [];
|
|
2273
2272
|
this.parent = parent;
|
|
2274
2273
|
this.data = value;
|
|
2275
|
-
this._path =
|
|
2274
|
+
this._path = path14;
|
|
2276
2275
|
this._key = key;
|
|
2277
2276
|
}
|
|
2278
2277
|
get path() {
|
|
@@ -15059,39 +15058,39 @@ function createOpenAI(options = {}) {
|
|
|
15059
15058
|
});
|
|
15060
15059
|
const createChatModel = (modelId, settings = {}) => new OpenAIChatLanguageModel(modelId, settings, {
|
|
15061
15060
|
provider: `${providerName}.chat`,
|
|
15062
|
-
url: ({ path:
|
|
15061
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
15063
15062
|
headers: getHeaders,
|
|
15064
15063
|
compatibility,
|
|
15065
15064
|
fetch: options.fetch
|
|
15066
15065
|
});
|
|
15067
15066
|
const createCompletionModel = (modelId, settings = {}) => new OpenAICompletionLanguageModel(modelId, settings, {
|
|
15068
15067
|
provider: `${providerName}.completion`,
|
|
15069
|
-
url: ({ path:
|
|
15068
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
15070
15069
|
headers: getHeaders,
|
|
15071
15070
|
compatibility,
|
|
15072
15071
|
fetch: options.fetch
|
|
15073
15072
|
});
|
|
15074
15073
|
const createEmbeddingModel = (modelId, settings = {}) => new OpenAIEmbeddingModel(modelId, settings, {
|
|
15075
15074
|
provider: `${providerName}.embedding`,
|
|
15076
|
-
url: ({ path:
|
|
15075
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
15077
15076
|
headers: getHeaders,
|
|
15078
15077
|
fetch: options.fetch
|
|
15079
15078
|
});
|
|
15080
15079
|
const createImageModel = (modelId, settings = {}) => new OpenAIImageModel(modelId, settings, {
|
|
15081
15080
|
provider: `${providerName}.image`,
|
|
15082
|
-
url: ({ path:
|
|
15081
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
15083
15082
|
headers: getHeaders,
|
|
15084
15083
|
fetch: options.fetch
|
|
15085
15084
|
});
|
|
15086
15085
|
const createTranscriptionModel = (modelId) => new OpenAITranscriptionModel(modelId, {
|
|
15087
15086
|
provider: `${providerName}.transcription`,
|
|
15088
|
-
url: ({ path:
|
|
15087
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
15089
15088
|
headers: getHeaders,
|
|
15090
15089
|
fetch: options.fetch
|
|
15091
15090
|
});
|
|
15092
15091
|
const createSpeechModel = (modelId) => new OpenAISpeechModel(modelId, {
|
|
15093
15092
|
provider: `${providerName}.speech`,
|
|
15094
|
-
url: ({ path:
|
|
15093
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
15095
15094
|
headers: getHeaders,
|
|
15096
15095
|
fetch: options.fetch
|
|
15097
15096
|
});
|
|
@@ -15112,7 +15111,7 @@ function createOpenAI(options = {}) {
|
|
|
15112
15111
|
const createResponsesModel = (modelId) => {
|
|
15113
15112
|
return new OpenAIResponsesLanguageModel(modelId, {
|
|
15114
15113
|
provider: `${providerName}.responses`,
|
|
15115
|
-
url: ({ path:
|
|
15114
|
+
url: ({ path: path14 }) => `${baseURL}${path14}`,
|
|
15116
15115
|
headers: getHeaders,
|
|
15117
15116
|
fetch: options.fetch
|
|
15118
15117
|
});
|
|
@@ -16271,7 +16270,7 @@ function computeWorkstyle(inp) {
|
|
|
16271
16270
|
|
|
16272
16271
|
// ../../lib/calibration/index.ts
|
|
16273
16272
|
var DEFAULT_CALIBRATION = {
|
|
16274
|
-
version: "2026-07-
|
|
16273
|
+
version: "2026-07-14.2",
|
|
16275
16274
|
// = RUBRIC_FINGERPRINT of the shipped npm rubric (packages/coding-analyzer/
|
|
16276
16275
|
// src/grade/criteria.ts). A unit test fails loud when the rubric changes
|
|
16277
16276
|
// without this being updated (and re-pushed to the server).
|
|
@@ -16360,7 +16359,7 @@ var DEFAULT_CALIBRATION = {
|
|
|
16360
16359
|
}
|
|
16361
16360
|
],
|
|
16362
16361
|
codingTiers: {
|
|
16363
|
-
push: { median: 300, sigma:
|
|
16362
|
+
push: { median: 300, sigma: 0.91, tag: "estimated \u2014 log-normal fit", source: "Anthropic 2026 Claude Code study (400k sessions): median engaged user ~8 prompts/day at ~35 words \u2014 ~300 directed words/day; tail: a 1-in-1,000 user AVERAGES ~5k directed words/day (near-daily heavy dictation, clearly below the ~10k single-day record). AVERAGE over substantial days, cumulative not spiky." },
|
|
16364
16363
|
focus: { median: 0.08, sigma: 0.559, tag: "estimated \u2014 proxy-anchored", source: "median: Anthropic 20h/week engaged-user figure implies ~1h/day of true rapid exchange against an 8h working day; ceiling anchored to the ~4h/day deep-work limit (45% share = 1-in-1,000). AVERAGE share, cumulative not spiky." },
|
|
16365
16364
|
orchestration: { tag: "estimated \u2014 behavior bands", source: "no published concurrency distribution exists; ceiling anchored to the Claude Code lead's documented 10-15 parallel sessions" }
|
|
16366
16365
|
},
|
|
@@ -16393,12 +16392,18 @@ var DEFAULT_CALIBRATION = {
|
|
|
16393
16392
|
line: "the Claude Code lead runs 10\u201315 sessions at once; his #1 tip is 3\u20135 git worktrees in parallel"
|
|
16394
16393
|
},
|
|
16395
16394
|
throughput: {
|
|
16396
|
-
//
|
|
16397
|
-
//
|
|
16398
|
-
//
|
|
16399
|
-
//
|
|
16400
|
-
|
|
16401
|
-
|
|
16395
|
+
// Two DIFFERENT kinds of line (2026-07-14.2): topAvg is a PERCENTILE
|
|
16396
|
+
// claim — what a 1-in-1,000 user AVERAGES over substantial days (5k/day:
|
|
16397
|
+
// near-daily heavy dictation; a measured top dictating power user
|
|
16398
|
+
// averages ~3.9k). topPeak is NOT a percentile — it is the CEILING
|
|
16399
|
+
// reference, the wildest single days on record (~10k: a 14-16h
|
|
16400
|
+
// launch-day marathon), rendered as "wildest single days", never
|
|
16401
|
+
// "top X% peak". An avg-percentile and a peak-percentile can't both be
|
|
16402
|
+
// shown at a same-ish value (avg≈peak reads broken — user call), and a
|
|
16403
|
+
// peak PERCENTILE line is unknowable anyway; the record framing is
|
|
16404
|
+
// honest and keeps the visual gap.
|
|
16405
|
+
topAvgWordsPerDay: 5e3,
|
|
16406
|
+
topPeakWordsPerDay: 1e4,
|
|
16402
16407
|
benchLabel: "top 0.1%",
|
|
16403
16408
|
// No published "words typed/day" for heavy users — the real story is OUTPUT.
|
|
16404
16409
|
loopsHeadline: ">1,000,000 lines of Rust at 99.8% tests passing",
|
|
@@ -16733,7 +16738,7 @@ async function compileCodingProfile() {
|
|
|
16733
16738
|
sRow(
|
|
16734
16739
|
"How hard you push",
|
|
16735
16740
|
aggregate?.throughput?.score ?? tCeiling,
|
|
16736
|
-
`you peak at ~${peakWords.toLocaleString()} words of pure direction in a single day \u2014 the very top sustain ~${BEST.throughput.topAvgWordsPerDay.toLocaleString()} every day, week after week
|
|
16741
|
+
`you peak at ~${peakWords.toLocaleString()} words of pure direction in a single day \u2014 the very top sustain ~${BEST.throughput.topAvgWordsPerDay.toLocaleString()} every day, week after week; the wildest single days on record push ~${BEST.throughput.topPeakWordsPerDay.toLocaleString()}`
|
|
16737
16742
|
),
|
|
16738
16743
|
sRow(
|
|
16739
16744
|
"How well you use AI",
|
|
@@ -16809,6 +16814,10 @@ async function readCodingAsset(name17) {
|
|
|
16809
16814
|
);
|
|
16810
16815
|
}
|
|
16811
16816
|
|
|
16817
|
+
// ../../lib/agents/coding/docsDir.ts
|
|
16818
|
+
import { promises as fs9 } from "fs";
|
|
16819
|
+
import path12 from "path";
|
|
16820
|
+
|
|
16812
16821
|
// ../../lib/agents/coding/materialize.ts
|
|
16813
16822
|
import { promises as fs8 } from "fs";
|
|
16814
16823
|
var SYSTEM_REMINDER_RE2 = /<system-reminder>[\s\S]*?<\/system-reminder>/g;
|
|
@@ -16945,8 +16954,95 @@ async function materializeSession(file2) {
|
|
|
16945
16954
|
return { text: lines.join("\n"), humanTurns, humanChars };
|
|
16946
16955
|
}
|
|
16947
16956
|
|
|
16957
|
+
// ../../lib/agents/coding/docsDir.ts
|
|
16958
|
+
async function writeScoreboard(codingDir, docs) {
|
|
16959
|
+
const gradesDir = path12.join(codingDir, "grades");
|
|
16960
|
+
let files = [];
|
|
16961
|
+
try {
|
|
16962
|
+
files = (await fs9.readdir(gradesDir)).filter((f) => f.endsWith(".json")).sort();
|
|
16963
|
+
} catch {
|
|
16964
|
+
return;
|
|
16965
|
+
}
|
|
16966
|
+
const byCriterion = /* @__PURE__ */ new Map();
|
|
16967
|
+
for (const f of files) {
|
|
16968
|
+
let g;
|
|
16969
|
+
try {
|
|
16970
|
+
g = JSON.parse(await fs9.readFile(path12.join(gradesDir, f), "utf8"));
|
|
16971
|
+
} catch {
|
|
16972
|
+
continue;
|
|
16973
|
+
}
|
|
16974
|
+
for (const c of g.criteria ?? []) {
|
|
16975
|
+
const s = c.score ?? c.bestEstimate;
|
|
16976
|
+
if (s == null) continue;
|
|
16977
|
+
const line = `L${s}${c.score == null ? "~" : ""} \xB7 ${(g.date ?? "").slice(0, 10)} \xB7 ${(g.title ?? g.sessionId ?? "").replace(/\n.*/s, "").slice(0, 60)} \xB7 ${g.sessionId} \u2014 ${(c.instance ?? "").slice(0, 110)}`;
|
|
16978
|
+
(byCriterion.get(c.key) ?? byCriterion.set(c.key, []).get(c.key)).push({ s, line });
|
|
16979
|
+
}
|
|
16980
|
+
}
|
|
16981
|
+
if (!byCriterion.size) return;
|
|
16982
|
+
const blocks = [...byCriterion.entries()].sort(([a], [b]) => a < b ? -1 : 1).map(
|
|
16983
|
+
([key, rows]) => `## ${key}
|
|
16984
|
+
${rows.sort((a, b) => b.s - a.s).map((r) => r.line).join("\n")}`
|
|
16985
|
+
);
|
|
16986
|
+
await fs9.writeFile(
|
|
16987
|
+
path12.join(docs, "_scoreboard.md"),
|
|
16988
|
+
`# Scoreboard \u2014 every graded session per criterion, best first.
|
|
16989
|
+
# Lx~ = estimate. Format: level \xB7 date \xB7 title \xB7 sessionId \u2014 graded instance.
|
|
16990
|
+
|
|
16991
|
+
${blocks.join("\n\n")}
|
|
16992
|
+
`
|
|
16993
|
+
);
|
|
16994
|
+
}
|
|
16995
|
+
async function ensureCodingDocs(codingDir, sessions) {
|
|
16996
|
+
const docs = path12.join(codingDir, "docs");
|
|
16997
|
+
await fs9.mkdir(docs, { recursive: true });
|
|
16998
|
+
const user = sessions.filter((s) => s.sessionId && !s.duplicateOf && (!("klass" in s) || s.klass === "interactive"));
|
|
16999
|
+
let written = 0;
|
|
17000
|
+
for (const s of user) {
|
|
17001
|
+
const out = path12.join(docs, `${s.sessionId}.txt`);
|
|
17002
|
+
try {
|
|
17003
|
+
await fs9.access(out);
|
|
17004
|
+
continue;
|
|
17005
|
+
} catch {
|
|
17006
|
+
}
|
|
17007
|
+
const mat = await materializeSession(s.file).catch(() => null);
|
|
17008
|
+
if (!mat?.text) continue;
|
|
17009
|
+
await fs9.writeFile(out, mat.text);
|
|
17010
|
+
written++;
|
|
17011
|
+
}
|
|
17012
|
+
const rows = user.sort((a, b) => (a.start ?? "") < (b.start ?? "") ? -1 : 1).map((s) => `${(s.start ?? "").slice(0, 10)} \xB7 ${(s.title ?? "").replace(/\n.*/s, "").slice(0, 80)} \xB7 ${s.sessionId}`);
|
|
17013
|
+
await fs9.writeFile(path12.join(docs, "_map.md"), rows.join("\n") + "\n");
|
|
17014
|
+
await writeScoreboard(codingDir, docs);
|
|
17015
|
+
if (written) console.log(`[docs] materialized ${written} new transcript(s) \u2192 ${docs}`);
|
|
17016
|
+
return docs;
|
|
17017
|
+
}
|
|
17018
|
+
function sourceLookupNote() {
|
|
17019
|
+
return `
|
|
17020
|
+
|
|
17021
|
+
SOURCE LOOKUP \u2014 you have Read/Grep/Glob over the working directory, which holds EVERYTHING known about this person's coding work. MAPS FIRST, grep second (the engine rule): docs/_scoreboard.md ranks every graded session per criterion, best first \u2014 one glance shows where the spikes/flair are, so start there when hunting peaks or checking whether a high mark is consistent or a one-off; day-digest.json is the day-by-day story of what they did in order \u2014 read the relevant days to get the arc before searching; docs/_map.md lists every session (date \xB7 title \xB7 sessionId). Then drill down: walkthroughs/<sessionId>.json is that session's phase-by-phase account (cheap, already distilled), grades/<sessionId>.json its graded findings with quotes, and docs/<sessionId>.txt the FULL transcript (">> " = the person, "[AI]" = the model) \u2014 the ground truth, read it when the distilled layers can't settle it. Other artifacts (flow.json, delegation/, expertise/, aggregate.json) are there too if a claim depends on them. The digest you were given is the spine \u2014 use lookups when a CLAIM-MOVING question cannot be settled from it (a moment you are about to headline, a promotion/depth call, an ambiguous engagement, whether detail was vision or boilerplate). VERIFY every verbatim quote you cite by finding it in the transcript first; if you cannot find it, do not use it. Look up what changes the output \u2014 do not re-read the whole corpus.`;
|
|
17022
|
+
}
|
|
17023
|
+
|
|
17024
|
+
// ../../lib/agents/coding/promptCache.ts
|
|
17025
|
+
import { createHash as createHash2 } from "crypto";
|
|
17026
|
+
import { promises as fs10 } from "fs";
|
|
17027
|
+
function promptSha(parts) {
|
|
17028
|
+
const h = createHash2("sha256");
|
|
17029
|
+
for (const p of parts) {
|
|
17030
|
+
h.update(p ?? "");
|
|
17031
|
+
h.update("\0");
|
|
17032
|
+
}
|
|
17033
|
+
return h.digest("hex").slice(0, 16);
|
|
17034
|
+
}
|
|
17035
|
+
async function reusableOutput(outPath, sha, valid, key = "promptSha") {
|
|
17036
|
+
try {
|
|
17037
|
+
const o = JSON.parse(await fs10.readFile(outPath, "utf8"));
|
|
17038
|
+
if (o[key] === sha && valid(o)) return o;
|
|
17039
|
+
} catch {
|
|
17040
|
+
}
|
|
17041
|
+
return null;
|
|
17042
|
+
}
|
|
17043
|
+
|
|
16948
17044
|
// ../../scripts/coding-gap.mts
|
|
16949
|
-
var CODING =
|
|
17045
|
+
var CODING = CODING_DIR;
|
|
16950
17046
|
var CONFIG = {
|
|
16951
17047
|
interface: "Claude Code INSIDE the Claude Desktop Mac app (NOT a raw terminal) \u2014 so terminal-only advice like `claude -p` does not fit; give desktop-app actions.",
|
|
16952
17048
|
permissions: "`--dangerously-skip-permissions` is ON. They are NOT worried about destructive actions (never happened; they just revert). Do NOT raise auto mode or any permissions change \u2014 it is not in the catalog.",
|
|
@@ -16958,7 +17054,7 @@ var CLAUDE_AUTO_CAP = /* @__PURE__ */ new Set(["queue-ahead", "deferred-tools",
|
|
|
16958
17054
|
var CLAUDE_AUTO_MCP = /^(visualize|Claude[_ ]?Preview|ccd[_ ]?session|ccd[_ ]?session[_ ]?mgmt|mcp[_ ]?registry)$/i;
|
|
16959
17055
|
async function entrypointOf(file2) {
|
|
16960
17056
|
try {
|
|
16961
|
-
const head = (await
|
|
17057
|
+
const head = (await fs11.readFile(file2, "utf8")).slice(0, 2e4);
|
|
16962
17058
|
if (/[\/."]\.?conductor/i.test(head)) return "conductor";
|
|
16963
17059
|
const m = /"entrypoint":"([^"]+)"/.exec(head);
|
|
16964
17060
|
return m ? m[1] : "unknown";
|
|
@@ -16980,14 +17076,14 @@ var MOBILE_WEB = /^claude-(web|mobile|ios|android)$/;
|
|
|
16980
17076
|
function sumTool(recs, name17) {
|
|
16981
17077
|
return recs.reduce((a, s) => a + (s.toolCounts?.[name17] ?? 0), 0);
|
|
16982
17078
|
}
|
|
16983
|
-
var LOCK =
|
|
17079
|
+
var LOCK = path13.join(CODING, ".gap.lock");
|
|
16984
17080
|
var lockHeld = false;
|
|
16985
17081
|
async function acquireLock() {
|
|
16986
17082
|
try {
|
|
16987
|
-
await
|
|
17083
|
+
await fs11.writeFile(LOCK, String(process.pid), { flag: "wx" });
|
|
16988
17084
|
lockHeld = true;
|
|
16989
17085
|
} catch {
|
|
16990
|
-
const pid = await
|
|
17086
|
+
const pid = await fs11.readFile(LOCK, "utf8").catch(() => "?");
|
|
16991
17087
|
throw new Error(`another coding-gap run appears active (pid ${pid}, ${LOCK}). Wait for it or delete the lock if it's stale \u2014 concurrent runs overwrite each other's gap.json.`);
|
|
16992
17088
|
}
|
|
16993
17089
|
}
|
|
@@ -16996,9 +17092,9 @@ async function main() {
|
|
|
16996
17092
|
const p = await compileCodingProfile();
|
|
16997
17093
|
const techniquesAsset = await readCodingAsset("TECHNIQUES.md");
|
|
16998
17094
|
const techniques = techniquesAsset.text;
|
|
16999
|
-
console.log(` rubric: ${techniquesAsset.path} (${(await
|
|
17095
|
+
console.log(` rubric: ${techniquesAsset.path} (${(await fs11.stat(techniquesAsset.path)).mtime.toISOString()})`);
|
|
17000
17096
|
const writing = (await readCodingAsset("WRITING.md")).text;
|
|
17001
|
-
const all = JSON.parse(await
|
|
17097
|
+
const all = JSON.parse(await fs11.readFile(path13.join(CODING, "sessions.json"), "utf8"));
|
|
17002
17098
|
const interactive = all.filter((s) => s.klass === "interactive" && s.start);
|
|
17003
17099
|
const latestMs = Math.max(...interactive.map((s) => Date.parse(s.start)));
|
|
17004
17100
|
const cutoffMs = latestMs - 14 * 864e5;
|
|
@@ -17055,7 +17151,7 @@ async function main() {
|
|
|
17055
17151
|
return m ? `--- "${s.title}" \xB7 ${(s.start || "").slice(0, 10)} ---
|
|
17056
17152
|
${m.text.slice(0, 3e3)}` : "";
|
|
17057
17153
|
}))).filter(Boolean);
|
|
17058
|
-
const SYS = "You are a Claude Code power-user coach. You judge ONE person against the COMPLETE catalog of best ways to use Claude Code (the rubric below, sourced from Boris Cherny + first-party Claude Code features) and write a comprehensive, honest gap analysis, ORDERED most-difference-making \u2192 least. Rules: (1) Go through the whole catalog and surface the techniques they are NOT doing (or under-using) that would genuinely help \u2014 respecting each tip's [EMPHASIZE]/[DE-EMPHASIZE]/[GATE] curation. (2) ORDER by differenceRank (1 = biggest unlock for THIS person). Follow the rubric's PRIME DIRECTIVE: if usage.parallelism.alreadyParallel is FALSE \u2192 'Do more in parallel' is rank 1; else \u2192 'Invest in your CLAUDE.md / tune your context' is rank 1. Then Plan mode, then 'Give Claude test cases so it can auto-verify and run longer' (Boris's 'most important thing' \u2014 rank it high, right after plan mode), then subagents, and the rest per the rubric order, skipping any that don't apply. (3) GATE the conditional tips on the STRUCTURAL usage facts, NOT on keywords \u2014 and a tip whose gate FAILS is OMITTED from recommendations entirely, never included as a softened/conditional card (recommending something their data says doesn't apply, or that they already do, reads as not having looked): loops require real PR/branch volume in usage.collaboration, else OMIT; the Chrome tip requires ~zero browser-driving in usage.chrome, else OMIT and credit in alreadyStrong; forking uses the RECENT count in usage.forking \u2014 if they fork in the recent window they already do it, OMIT and credit; remote/mobile/EC2 fires only when usage.surface.workstationBound is TRUE; plan mode by usage.planMode; subagents by usage.subagents. If they already do a thing, it goes in alreadyStrong, NOT in recommendations. (4) \u26D4 CRITICAL \u2014 this person BUILDS Claude Code tooling, so their transcripts are full of the words 'worktree', 'pull request', 'fork', 'plan mode', 'by the way', 'teleport'. A keyword in the transcript is NOT evidence they use the feature. Rely on the STRUCTURAL facts only. (5) Ground every recommendation in a RECEIPT \u2014 a structural fact from their usage or a concrete session pain point \u2014 and CONVINCE with a specific case for how it helps THIS person, not a generic benefit. (6) Tailor every action to the Claude Desktop Mac app; NEVER give terminal-only `claude -p` advice. (7) Honor config.deliberateChoices (they stay on `main` on purpose \u2014 do NOT tell them to hand-juggle worktrees; parallel SESSIONS instead) and config.claudeAutoTools (never tell them to 'use' a tool Claude drives itself; for 'explain with visuals' the user just ASKS for a visual \u2014 do not name the tool). (8) Also name what they ALREADY do well. (9) SELF-CONTAINED but not verbose \u2014 lead with the PROBLEM, then the SOLUTION, then HOW to use it in their context. Plain language, no jargon, no enumerating multiple dates. An essay-like rec is a failed rec; so is a cryptic one. Follow this writing rubric strictly:\n\n" + writing;
|
|
17154
|
+
const SYS = "You are a Claude Code power-user coach. You judge ONE person against the COMPLETE catalog of best ways to use Claude Code (the rubric below, sourced from Boris Cherny + first-party Claude Code features) and write a comprehensive, honest gap analysis, ORDERED most-difference-making \u2192 least. Rules: (1) Go through the whole catalog and surface the techniques they are NOT doing (or under-using) that would genuinely help \u2014 respecting each tip's [EMPHASIZE]/[DE-EMPHASIZE]/[GATE] curation. (2) ORDER by differenceRank (1 = biggest unlock for THIS person). Follow the rubric's PRIME DIRECTIVE: if usage.parallelism.alreadyParallel is FALSE \u2192 'Do more in parallel' is rank 1; else \u2192 'Invest in your CLAUDE.md / tune your context' is rank 1. Then Plan mode, then 'Give Claude test cases so it can auto-verify and run longer' (Boris's 'most important thing' \u2014 rank it high, right after plan mode), then subagents, and the rest per the rubric order, skipping any that don't apply. (3) GATE the conditional tips on the STRUCTURAL usage facts, NOT on keywords \u2014 and a tip whose gate FAILS is OMITTED from recommendations entirely, never included as a softened/conditional card (recommending something their data says doesn't apply, or that they already do, reads as not having looked): loops require real PR/branch volume in usage.collaboration, else OMIT; the Chrome tip requires ~zero browser-driving in usage.chrome, else OMIT and credit in alreadyStrong; forking uses the RECENT count in usage.forking \u2014 if they fork in the recent window they already do it, OMIT and credit; remote/mobile/EC2 fires only when usage.surface.workstationBound is TRUE; plan mode by usage.planMode; subagents by usage.subagents. If they already do a thing, it goes in alreadyStrong, NOT in recommendations. (4) \u26D4 CRITICAL \u2014 this person BUILDS Claude Code tooling, so their transcripts are full of the words 'worktree', 'pull request', 'fork', 'plan mode', 'by the way', 'teleport'. A keyword in the transcript is NOT evidence they use the feature. Rely on the STRUCTURAL facts only. (5) Ground every recommendation in a RECEIPT \u2014 a structural fact from their usage or a concrete session pain point \u2014 and CONVINCE with a specific case for how it helps THIS person, not a generic benefit. (6) Tailor every action to the Claude Desktop Mac app; NEVER give terminal-only `claude -p` advice. (7) Honor config.deliberateChoices (they stay on `main` on purpose \u2014 do NOT tell them to hand-juggle worktrees; parallel SESSIONS instead) and config.claudeAutoTools (never tell them to 'use' a tool Claude drives itself; for 'explain with visuals' the user just ASKS for a visual \u2014 do not name the tool). (8) Also name what they ALREADY do well. (9) SELF-CONTAINED but not verbose \u2014 lead with the PROBLEM, then the SOLUTION, then HOW to use it in their context. Plain language, no jargon, no enumerating multiple dates. An essay-like rec is a failed rec; so is a cryptic one. Follow this writing rubric strictly:\n\n" + writing + sourceLookupNote();
|
|
17059
17155
|
const prompt = `THE RUBRIC \u2014 the complete catalog of best ways to use Claude Code. Judge the person against ALL of it, and ORDER by differenceRank per its PRIME DIRECTIVE:
|
|
17060
17156
|
|
|
17061
17157
|
${techniques}
|
|
@@ -17076,18 +17172,23 @@ Produce the gap analysis. Be thorough: cover every relevant catalog item they're
|
|
|
17076
17172
|
}
|
|
17077
17173
|
\`\`\`
|
|
17078
17174
|
\`differenceRank\` is a strict 1..N ordering (1 = highest). Assign it per the PRIME DIRECTIVE and the rubric's general order; the report renders recommendations sorted by it. \`payoff\` (1-5) is a secondary strength cue. Recommend ONLY from the catalog.`;
|
|
17079
|
-
|
|
17175
|
+
const sha = promptSha([SYS, prompt, "sonnet"]);
|
|
17176
|
+
if (!process.argv.includes("--refresh")) {
|
|
17177
|
+
const prev = await reusableOutput(path13.join(CODING, "gap.json"), sha, (o) => Array.isArray(o.recommendations) && o.recommendations.length > 0);
|
|
17178
|
+
if (prev) {
|
|
17179
|
+
console.log(`[gap] inputs unchanged \u2014 reusing output from ${prev.generatedAt} (--refresh to regenerate)`);
|
|
17180
|
+
return;
|
|
17181
|
+
}
|
|
17182
|
+
}
|
|
17183
|
+
await ensureCodingDocs(CODING, all);
|
|
17184
|
+
console.log("gap \u2014 1 Sonnet pass over the full TECHNIQUES.md catalog + structural usage (+source lookups)");
|
|
17080
17185
|
console.log(` signals: alreadyParallel=${alreadyParallel} planModeCalls=${planModeUses} forks=${forkCopies}/${all.length} featureBranches=${featureBranches.length} workstationBound=${workstationBound} chrome=${chromeToolUse}`);
|
|
17081
|
-
const sandbox = await fs9.mkdtemp(path12.join(os2.tmpdir(), "gap-"));
|
|
17082
17186
|
let out = { recommendations: [], alreadyStrong: [] };
|
|
17083
17187
|
try {
|
|
17084
|
-
const run2 = await invokeAgent({ prompt, systemPrompt: SYS, addDir:
|
|
17188
|
+
const run2 = await invokeAgent({ prompt, systemPrompt: SYS, addDir: CODING, allowedTools: ["Read", "Grep", "Glob"], maxTurns: 10, timeoutMs: 6e5, model: "sonnet" });
|
|
17085
17189
|
out = run2.json || out;
|
|
17086
17190
|
} catch (e) {
|
|
17087
17191
|
console.log("ERR", e.message.slice(0, 160));
|
|
17088
|
-
} finally {
|
|
17089
|
-
await fs9.rm(sandbox, { recursive: true, force: true }).catch(() => {
|
|
17090
|
-
});
|
|
17091
17192
|
}
|
|
17092
17193
|
if (Array.isArray(out.recommendations)) {
|
|
17093
17194
|
out.recommendations = out.recommendations.map((r, i) => ({ r, i })).sort((a, b) => (a.r.differenceRank ?? 99) - (b.r.differenceRank ?? 99) || (b.r.payoff ?? 0) - (a.r.payoff ?? 0) || a.i - b.i).map((x) => x.r);
|
|
@@ -17095,14 +17196,14 @@ Produce the gap analysis. Be thorough: cover every relevant catalog item they're
|
|
|
17095
17196
|
if (!Array.isArray(out.recommendations) || out.recommendations.length === 0) {
|
|
17096
17197
|
throw new Error("gap run produced 0 recommendations \u2014 refusing to overwrite the existing gap.json. Re-run the builder.");
|
|
17097
17198
|
}
|
|
17098
|
-
const result = { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), ...out };
|
|
17099
|
-
await
|
|
17199
|
+
const result = { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), promptSha: sha, ...out };
|
|
17200
|
+
await fs11.writeFile(path13.join(CODING, "gap.json"), JSON.stringify(result, null, 2));
|
|
17100
17201
|
console.log(`WROTE ${CODING}/gap.json \u2014 recommendations:${(out.recommendations || []).length} \xB7 alreadyStrong:${(out.alreadyStrong || []).length}`);
|
|
17101
17202
|
}
|
|
17102
17203
|
main().catch((e) => {
|
|
17103
17204
|
console.error(e);
|
|
17104
17205
|
process.exitCode = 1;
|
|
17105
17206
|
}).finally(() => {
|
|
17106
|
-
if (lockHeld)
|
|
17207
|
+
if (lockHeld) fs11.rm(LOCK, { force: true }).catch(() => {
|
|
17107
17208
|
});
|
|
17108
17209
|
});
|