@cogit/cli 0.29.0 → 0.31.0
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
CHANGED
|
@@ -11,6 +11,9 @@ asked, what came of it, and the message it answered. Your code commits point bac
|
|
|
11
11
|
thought behind them, so `git log` tells you what changed and cogit tells you why. It works with
|
|
12
12
|
Claude Code today.
|
|
13
13
|
|
|
14
|
+
It runs as a Claude Code plugin and hooks in at SessionStart, Stop and SessionEnd, so the
|
|
15
|
+
record is written while you work, not reconstructed from the diff afterwards.
|
|
16
|
+
|
|
14
17
|
<img src="docs/images/map.png" alt="The map: sessions as a git graph, each with its messages, branches, commit chips and a left off here marker">
|
|
15
18
|
|
|
16
19
|
*Your sessions as a git graph — one row per message, the decision behind it, and where you left
|
|
@@ -26,6 +29,15 @@ what the change does, and the diff.*
|
|
|
26
29
|
*Tasks — what your sessions left undone, drafted when a session closes. You accept, dismiss or
|
|
27
30
|
close each one.*
|
|
28
31
|
|
|
32
|
+
<img src="docs/images/files.png" alt="The Files view: a changed file opened, listing the messages that touched it with each message's reason and the option it rejected">
|
|
33
|
+
|
|
34
|
+
*A changed file opens its history: every message that touched it, and the reason behind each —
|
|
35
|
+
including the option that was rejected.*
|
|
36
|
+
|
|
37
|
+
**[Click through a saved map](https://erensrtky.github.io/cogit/demo/)** — no install, no server:
|
|
38
|
+
the same page `cogit open` writes, built from sample data. Read-only, so the Tasks buttons are
|
|
39
|
+
inert.
|
|
40
|
+
|
|
29
41
|
## What it does
|
|
30
42
|
|
|
31
43
|
- **Every message becomes a thought commit**: the question, the outcome, the decisions with
|
|
@@ -21206,7 +21206,7 @@ async function migrateProject(root) {
|
|
|
21206
21206
|
const next = renameGitignoreLines(text);
|
|
21207
21207
|
if (next !== text) await atomicWrite(gitignore, next).catch(() => void 0);
|
|
21208
21208
|
}
|
|
21209
|
-
const { installGitHooks: installGitHooks2 } = await import("./git-hooks-
|
|
21209
|
+
const { installGitHooks: installGitHooks2 } = await import("./git-hooks-SGXDRKNY.js");
|
|
21210
21210
|
await installGitHooks2(dir).catch(() => void 0);
|
|
21211
21211
|
return true;
|
|
21212
21212
|
}
|
|
@@ -24044,6 +24044,9 @@ var exec4 = promisify3(execFile3);
|
|
|
24044
24044
|
var THOUGHT_TRAILER = "Thought";
|
|
24045
24045
|
var THOUGHT_VALUE = /^h[0-9a-f]{12}$/;
|
|
24046
24046
|
var FULL_HASH = /^[0-9a-f]{40}$/;
|
|
24047
|
+
var THOUGHT_LINE = new RegExp(`^[ \\t]*${THOUGHT_TRAILER}:[ \\t]*(h[0-9a-f]{12})[ \\t]*$`, "gim");
|
|
24048
|
+
var COMMIT_MARK = "";
|
|
24049
|
+
var LOG_MAX_BUFFER = 256 * 1024 * 1024;
|
|
24047
24050
|
async function readable(path2) {
|
|
24048
24051
|
return access2(path2).then(
|
|
24049
24052
|
() => true,
|
|
@@ -24070,10 +24073,23 @@ async function writeTrailer(messageFile, value) {
|
|
|
24070
24073
|
messagePath(messageFile)
|
|
24071
24074
|
]);
|
|
24072
24075
|
}
|
|
24076
|
+
async function replayingCommits(dir, env) {
|
|
24077
|
+
let gitDir;
|
|
24078
|
+
try {
|
|
24079
|
+
const { stdout } = await exec4("git", ["-C", dir, "rev-parse", "--absolute-git-dir"], { env });
|
|
24080
|
+
gitDir = String(stdout).trim();
|
|
24081
|
+
} catch {
|
|
24082
|
+
return false;
|
|
24083
|
+
}
|
|
24084
|
+
const states = ["rebase-merge", "rebase-apply", "CHERRY_PICK_HEAD"];
|
|
24085
|
+
const found = await Promise.all(states.map((state) => readable(resolve3(gitDir, state))));
|
|
24086
|
+
return found.includes(true);
|
|
24087
|
+
}
|
|
24073
24088
|
async function addThoughtTrailer(opts) {
|
|
24074
24089
|
const env = opts.env ?? process.env;
|
|
24075
24090
|
if (captureDisabled(env)) return false;
|
|
24076
24091
|
if (opts.source === "merge" || opts.source === "squash") return false;
|
|
24092
|
+
if (await replayingCommits(opts.dir, env)) return false;
|
|
24077
24093
|
const agent = defaultAgent();
|
|
24078
24094
|
const named = (env[agent.commitSessionEnv] ?? "").trim();
|
|
24079
24095
|
if (named !== "") {
|
|
@@ -24101,17 +24117,14 @@ async function addThoughtTrailer(opts) {
|
|
|
24101
24117
|
}
|
|
24102
24118
|
function parseThoughtTrailers(stdout) {
|
|
24103
24119
|
const out = /* @__PURE__ */ new Map();
|
|
24104
|
-
for (const
|
|
24105
|
-
const separator =
|
|
24120
|
+
for (const record2 of stdout.split(COMMIT_MARK)) {
|
|
24121
|
+
const separator = record2.indexOf("\0");
|
|
24106
24122
|
if (separator === -1) continue;
|
|
24107
|
-
const commit =
|
|
24123
|
+
const commit = record2.slice(0, separator);
|
|
24108
24124
|
if (!FULL_HASH.test(commit)) continue;
|
|
24109
|
-
|
|
24110
|
-
|
|
24111
|
-
|
|
24112
|
-
break;
|
|
24113
|
-
}
|
|
24114
|
-
}
|
|
24125
|
+
const body = record2.slice(separator + 1);
|
|
24126
|
+
const thoughts = [...body.matchAll(THOUGHT_LINE)].map((match) => match[1]);
|
|
24127
|
+
if (thoughts.length > 0) out.set(commit, [...new Set(thoughts)]);
|
|
24115
24128
|
}
|
|
24116
24129
|
return out;
|
|
24117
24130
|
}
|
|
@@ -24122,15 +24135,17 @@ async function readThoughtTrailers(root, opts = {}) {
|
|
|
24122
24135
|
"-C",
|
|
24123
24136
|
gitRoot,
|
|
24124
24137
|
"log",
|
|
24125
|
-
"--format=%H%x00%
|
|
24138
|
+
"--format=%x01%H%x00%B",
|
|
24139
|
+
"--extended-regexp",
|
|
24140
|
+
"--regexp-ignore-case",
|
|
24141
|
+
`--grep=^[[:space:]]*${THOUGHT_TRAILER}:`,
|
|
24126
24142
|
...opts.since === void 0 ? [] : [`--since=${opts.since}`]
|
|
24127
24143
|
];
|
|
24128
24144
|
try {
|
|
24129
|
-
const { stdout } = await exec4(
|
|
24130
|
-
|
|
24131
|
-
|
|
24132
|
-
|
|
24133
|
-
);
|
|
24145
|
+
const { stdout } = await exec4("git", args, {
|
|
24146
|
+
maxBuffer: LOG_MAX_BUFFER,
|
|
24147
|
+
...opts.env === void 0 ? {} : { env: opts.env }
|
|
24148
|
+
});
|
|
24134
24149
|
return parseThoughtTrailers(String(stdout));
|
|
24135
24150
|
} catch {
|
|
24136
24151
|
return /* @__PURE__ */ new Map();
|
|
@@ -24158,19 +24173,21 @@ function earliestSessionStart(sessions) {
|
|
|
24158
24173
|
function sameCommit(recorded, commit) {
|
|
24159
24174
|
return commit.startsWith(recorded) || recorded.startsWith(commit);
|
|
24160
24175
|
}
|
|
24161
|
-
function
|
|
24162
|
-
for (const [commit,
|
|
24163
|
-
if (sameCommit(recorded, commit)) return
|
|
24176
|
+
function thoughtsOf(recorded, trailers) {
|
|
24177
|
+
for (const [commit, thoughts] of trailers) {
|
|
24178
|
+
if (sameCommit(recorded, commit)) return thoughts;
|
|
24164
24179
|
}
|
|
24165
24180
|
return void 0;
|
|
24166
24181
|
}
|
|
24167
24182
|
function attachThoughtTrailers(sessions, trailers) {
|
|
24168
24183
|
if (trailers.size === 0) return [...sessions];
|
|
24169
24184
|
const byThought = /* @__PURE__ */ new Map();
|
|
24170
|
-
for (const [commit,
|
|
24171
|
-
const
|
|
24172
|
-
|
|
24173
|
-
|
|
24185
|
+
for (const [commit, thoughts] of trailers) {
|
|
24186
|
+
for (const thought of thoughts) {
|
|
24187
|
+
const list = byThought.get(thought);
|
|
24188
|
+
if (list === void 0) byThought.set(thought, [commit]);
|
|
24189
|
+
else if (!list.some((hash2) => sameCommit(hash2, commit))) list.push(commit);
|
|
24190
|
+
}
|
|
24174
24191
|
}
|
|
24175
24192
|
return sessions.map((session) => {
|
|
24176
24193
|
let changed = false;
|
|
@@ -24178,8 +24195,8 @@ function attachThoughtTrailers(sessions, trailers) {
|
|
|
24178
24195
|
if (node2.kind !== "message") return node2;
|
|
24179
24196
|
const current = node2.commits ?? [];
|
|
24180
24197
|
const kept = current.filter((hash2) => {
|
|
24181
|
-
const
|
|
24182
|
-
return
|
|
24198
|
+
const thoughts = thoughtsOf(hash2, trailers);
|
|
24199
|
+
return thoughts === void 0 || thoughts.includes(node2.id);
|
|
24183
24200
|
});
|
|
24184
24201
|
const claimed = (byThought.get(node2.id) ?? []).filter(
|
|
24185
24202
|
(commit) => !kept.some((hash2) => sameCommit(hash2, commit))
|
|
@@ -25090,7 +25107,9 @@ async function show(opts) {
|
|
|
25090
25107
|
` ${candidate.sessionId} \xB7 ${candidate.label}${candidate.id === candidate.label ? "" : ` (${candidate.id})`} ${candidate.title}`
|
|
25091
25108
|
);
|
|
25092
25109
|
}
|
|
25093
|
-
throw new CliError(
|
|
25110
|
+
throw new CliError(
|
|
25111
|
+
COMMIT_REF.test(opts.ref) ? `commit ${opts.ref} comes from several thoughts; open one by its message hash` : `"${opts.ref}" is ambiguous; use a longer prefix`
|
|
25112
|
+
);
|
|
25094
25113
|
}
|
|
25095
25114
|
const { session, messages, message } = result.hit;
|
|
25096
25115
|
const sessionId = session.sessionId;
|
|
@@ -25559,7 +25578,7 @@ async function onSessionEnd(ctx) {
|
|
|
25559
25578
|
// package.json
|
|
25560
25579
|
var package_default = {
|
|
25561
25580
|
name: "@cogit/cli",
|
|
25562
|
-
version: "0.
|
|
25581
|
+
version: "0.31.0",
|
|
25563
25582
|
description: "Git remembers what changed. cogit remembers why.",
|
|
25564
25583
|
license: "MIT",
|
|
25565
25584
|
keywords: [
|