friction-log 0.0.0-stage → 0.6.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/CHANGELOG.md +54 -0
- package/LICENSE +21 -0
- package/README.md +98 -2
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +13 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/bilanz.d.ts +23 -0
- package/dist/commands/bilanz.js +106 -0
- package/dist/commands/bilanz.js.map +1 -0
- package/dist/commands/digest.d.ts +36 -0
- package/dist/commands/digest.js +288 -0
- package/dist/commands/digest.js.map +1 -0
- package/dist/commands/export.d.ts +36 -0
- package/dist/commands/export.js +118 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/file.d.ts +18 -0
- package/dist/commands/file.js +51 -0
- package/dist/commands/file.js.map +1 -0
- package/dist/commands/import.d.ts +31 -0
- package/dist/commands/import.js +283 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/init.d.ts +44 -0
- package/dist/commands/init.js +256 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +16 -0
- package/dist/commands/list.js +57 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/log.d.ts +18 -0
- package/dist/commands/log.js +52 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/rm.d.ts +9 -0
- package/dist/commands/rm.js +22 -0
- package/dist/commands/rm.js.map +1 -0
- package/dist/commands/scan.d.ts +25 -0
- package/dist/commands/scan.js +73 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/search.d.ts +15 -0
- package/dist/commands/search.js +37 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/sync-export.d.ts +46 -0
- package/dist/commands/sync-export.js +80 -0
- package/dist/commands/sync-export.js.map +1 -0
- package/dist/commands/update.d.ts +12 -0
- package/dist/commands/update.js +24 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.js +145 -0
- package/dist/config.js.map +1 -0
- package/dist/db.d.ts +81 -0
- package/dist/db.js +548 -0
- package/dist/db.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +503 -0
- package/dist/main.js.map +1 -0
- package/dist/node-guard.d.ts +14 -0
- package/dist/node-guard.js +36 -0
- package/dist/node-guard.js.map +1 -0
- package/dist/paths.d.ts +12 -0
- package/dist/paths.js +34 -0
- package/dist/paths.js.map +1 -0
- package/dist/scanners/claude-code.d.ts +6 -0
- package/dist/scanners/claude-code.js +202 -0
- package/dist/scanners/claude-code.js.map +1 -0
- package/dist/scanners/index.d.ts +4 -0
- package/dist/scanners/index.js +12 -0
- package/dist/scanners/index.js.map +1 -0
- package/dist/sinks/agent-tasks.d.ts +36 -0
- package/dist/sinks/agent-tasks.js +153 -0
- package/dist/sinks/agent-tasks.js.map +1 -0
- package/dist/sinks/github-issues.d.ts +20 -0
- package/dist/sinks/github-issues.js +98 -0
- package/dist/sinks/github-issues.js.map +1 -0
- package/dist/sinks/index.d.ts +11 -0
- package/dist/sinks/index.js +48 -0
- package/dist/sinks/index.js.map +1 -0
- package/dist/sinks/linear.d.ts +25 -0
- package/dist/sinks/linear.js +157 -0
- package/dist/sinks/linear.js.map +1 -0
- package/dist/sinks/markdown-file.d.ts +7 -0
- package/dist/sinks/markdown-file.js +54 -0
- package/dist/sinks/markdown-file.js.map +1 -0
- package/dist/sinks/stdout-json.d.ts +15 -0
- package/dist/sinks/stdout-json.js +49 -0
- package/dist/sinks/stdout-json.js.map +1 -0
- package/dist/templates/auth-expiry.yml +30 -0
- package/dist/templates/doc-gap.yml +30 -0
- package/dist/templates/output-overflow.yml +25 -0
- package/dist/templates/schema-drift.yml +26 -0
- package/dist/templates/tool-error.yml +34 -0
- package/dist/templates/tool-missing-capability.yml +26 -0
- package/dist/templates/workflow-friction.yml +26 -0
- package/dist/templates.d.ts +5 -0
- package/dist/templates.js +88 -0
- package/dist/templates.js.map +1 -0
- package/dist/types.d.ts +79 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/commands.md +74 -0
- package/docs/design.md +25 -0
- package/docs/sinks.md +79 -0
- package/docs/storage.md +20 -0
- package/docs/sync-export.md +23 -0
- package/package.json +64 -4
- package/templates/auth-expiry.yml +30 -0
- package/templates/doc-gap.yml +30 -0
- package/templates/output-overflow.yml +25 -0
- package/templates/schema-drift.yml +26 -0
- package/templates/tool-error.yml +34 -0
- package/templates/tool-missing-capability.yml +26 -0
- package/templates/workflow-friction.yml +26 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
6
|
+
import { defaultConfigPath } from "../paths.js";
|
|
7
|
+
export async function runInit(input = {}) {
|
|
8
|
+
const configPath = input.configPath ?? defaultConfigPath();
|
|
9
|
+
const detected = (input.detect ?? defaultDetect)();
|
|
10
|
+
const interactive = input.yes !== true;
|
|
11
|
+
const prompt = input.prompt ?? readlinePrompter();
|
|
12
|
+
const defaultSink = input.sink ??
|
|
13
|
+
(interactive
|
|
14
|
+
? (await prompt.select("Default sink", [
|
|
15
|
+
"markdown-file",
|
|
16
|
+
"stdout-json",
|
|
17
|
+
"github-issues",
|
|
18
|
+
"agent-tasks",
|
|
19
|
+
"linear",
|
|
20
|
+
], suggestSink(detected)))
|
|
21
|
+
: suggestSink(detected));
|
|
22
|
+
const configExistedBefore = existsSync(configPath);
|
|
23
|
+
const existing = configExistedBefore ? readFileSync(configPath, "utf8") : "";
|
|
24
|
+
const newConfig = mergeConfigYaml(existing, defaultSink, input.syncExport);
|
|
25
|
+
// Only write when the content actually changes; idempotency matters when
|
|
26
|
+
// init is run multiple times during onboarding.
|
|
27
|
+
let configWritten = false;
|
|
28
|
+
if (newConfig !== existing) {
|
|
29
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
30
|
+
writeFileSync(configPath, newConfig, "utf8");
|
|
31
|
+
configWritten = true;
|
|
32
|
+
}
|
|
33
|
+
let stopHookWrittenTo = null;
|
|
34
|
+
const wantHook = input.installStopHook ??
|
|
35
|
+
(detected.claudeCodeSettingsPath !== null &&
|
|
36
|
+
(interactive
|
|
37
|
+
? await prompt.confirm("Install friction-log scan as a Claude Code Stop-hook?", true)
|
|
38
|
+
: false));
|
|
39
|
+
if (wantHook && detected.claudeCodeSettingsPath) {
|
|
40
|
+
const path = detected.claudeCodeSettingsPath;
|
|
41
|
+
const before = existsSync(path) ? readFileSync(path, "utf8") : "{}";
|
|
42
|
+
const after = mergeStopHook(before);
|
|
43
|
+
if (after !== before) {
|
|
44
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
45
|
+
writeFileSync(path, after, "utf8");
|
|
46
|
+
}
|
|
47
|
+
stopHookWrittenTo = path;
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
configPath,
|
|
51
|
+
configWritten,
|
|
52
|
+
configExistedBefore,
|
|
53
|
+
defaultSink,
|
|
54
|
+
stopHookWrittenTo,
|
|
55
|
+
detected,
|
|
56
|
+
nextSteps: buildNextSteps(defaultSink, configPath, stopHookWrittenTo, input.syncExport),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function suggestSink(d) {
|
|
60
|
+
if (d.agentTasksTokenPresent)
|
|
61
|
+
return "agent-tasks";
|
|
62
|
+
if (d.linearKeyPresent)
|
|
63
|
+
return "linear";
|
|
64
|
+
if (d.ghAvailable)
|
|
65
|
+
return "github-issues";
|
|
66
|
+
return "markdown-file";
|
|
67
|
+
}
|
|
68
|
+
function defaultDetect() {
|
|
69
|
+
const claudeHome = process.env.CLAUDE_HOME ?? join(homedir(), ".claude");
|
|
70
|
+
const settingsPath = join(claudeHome, "settings.json");
|
|
71
|
+
const claudeCodeSettingsPath = existsSync(claudeHome) ? settingsPath : null;
|
|
72
|
+
const which = spawnSync("which", ["gh"], { encoding: "utf8" });
|
|
73
|
+
const ghAvailable = which.status === 0 && Boolean((which.stdout ?? "").trim());
|
|
74
|
+
return {
|
|
75
|
+
claudeCodeSettingsPath,
|
|
76
|
+
ghAvailable,
|
|
77
|
+
linearKeyPresent: Boolean(process.env.LINEAR_API_KEY),
|
|
78
|
+
agentTasksTokenPresent: Boolean(process.env.AGENT_TASKS_TOKEN),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export function mergeConfigYaml(existingYaml, defaultSink, syncExport) {
|
|
82
|
+
// Parse as best-effort; if existing config is corrupt we still want init
|
|
83
|
+
// to land a clean default scaffolding rather than wedging the user.
|
|
84
|
+
let parsed = {};
|
|
85
|
+
if (existingYaml.trim()) {
|
|
86
|
+
try {
|
|
87
|
+
const tmp = parseYaml(existingYaml);
|
|
88
|
+
if (tmp && typeof tmp === "object" && !Array.isArray(tmp)) {
|
|
89
|
+
parsed = tmp;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
parsed = {};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const sinks = parsed.sinks &&
|
|
97
|
+
typeof parsed.sinks === "object" &&
|
|
98
|
+
!Array.isArray(parsed.sinks)
|
|
99
|
+
? parsed.sinks
|
|
100
|
+
: {};
|
|
101
|
+
if (!sinks[defaultSink]) {
|
|
102
|
+
sinks[defaultSink] = scaffoldForSink(defaultSink);
|
|
103
|
+
}
|
|
104
|
+
parsed.sinks = sinks;
|
|
105
|
+
parsed.default_sink = defaultSink;
|
|
106
|
+
// Opt-in only, and this is the ONLY place that writes sync_export: when
|
|
107
|
+
// `syncExport` is omitted (the default `init` path), any existing
|
|
108
|
+
// sync_export block in `parsed` is passed through untouched below since we
|
|
109
|
+
// never delete or overwrite the key. This is the fix for the drift bug
|
|
110
|
+
// where init.ts wrote `default_sink` but loadConfig never read it back:
|
|
111
|
+
// config.ts's loadConfig reads this exact `sync_export` shape (see
|
|
112
|
+
// parseSyncExportBlock).
|
|
113
|
+
if (syncExport) {
|
|
114
|
+
parsed.sync_export = {
|
|
115
|
+
path: syncExport.path,
|
|
116
|
+
origin: syncExport.origin,
|
|
117
|
+
...(syncExport.peerPaths && syncExport.peerPaths.length
|
|
118
|
+
? { peer_paths: syncExport.peerPaths }
|
|
119
|
+
: {}),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return stringifyYaml(parsed);
|
|
123
|
+
}
|
|
124
|
+
function scaffoldForSink(sink) {
|
|
125
|
+
switch (sink) {
|
|
126
|
+
case "markdown-file":
|
|
127
|
+
return {};
|
|
128
|
+
case "stdout-json":
|
|
129
|
+
return {};
|
|
130
|
+
case "github-issues":
|
|
131
|
+
return { repo: "owner/repo", labels: ["friction"] };
|
|
132
|
+
case "agent-tasks":
|
|
133
|
+
return {
|
|
134
|
+
mode: "rest",
|
|
135
|
+
apiBase: "https://agent-tasks.example.test",
|
|
136
|
+
projectId: "<uuid>",
|
|
137
|
+
};
|
|
138
|
+
case "linear":
|
|
139
|
+
return { teamId: "<team-uuid>", state: "Backlog" };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const STOP_HOOK = {
|
|
143
|
+
type: "command",
|
|
144
|
+
command: "friction-log scan --silent --stdin-payload",
|
|
145
|
+
};
|
|
146
|
+
export function mergeStopHook(existingJson) {
|
|
147
|
+
let parsed = {};
|
|
148
|
+
if (existingJson.trim()) {
|
|
149
|
+
try {
|
|
150
|
+
const tmp = JSON.parse(existingJson);
|
|
151
|
+
if (tmp && typeof tmp === "object" && !Array.isArray(tmp)) {
|
|
152
|
+
parsed = tmp;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
parsed = {};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const hooks = parsed.hooks &&
|
|
160
|
+
typeof parsed.hooks === "object" &&
|
|
161
|
+
!Array.isArray(parsed.hooks)
|
|
162
|
+
? parsed.hooks
|
|
163
|
+
: {};
|
|
164
|
+
const stopEntries = Array.isArray(hooks.Stop)
|
|
165
|
+
? hooks.Stop
|
|
166
|
+
: [];
|
|
167
|
+
let group = stopEntries.find((e) => e && typeof e === "object" && (!("matcher" in e) || e.matcher === ""));
|
|
168
|
+
if (!group) {
|
|
169
|
+
group = { matcher: "", hooks: [] };
|
|
170
|
+
stopEntries.push(group);
|
|
171
|
+
}
|
|
172
|
+
const groupHooks = Array.isArray(group.hooks)
|
|
173
|
+
? group.hooks
|
|
174
|
+
: [];
|
|
175
|
+
if (!groupHooks.some((h) => h?.command === STOP_HOOK.command)) {
|
|
176
|
+
groupHooks.push({ ...STOP_HOOK });
|
|
177
|
+
}
|
|
178
|
+
group.hooks = groupHooks;
|
|
179
|
+
hooks.Stop = stopEntries;
|
|
180
|
+
parsed.hooks = hooks;
|
|
181
|
+
return JSON.stringify(parsed, null, 2) + "\n";
|
|
182
|
+
}
|
|
183
|
+
function buildNextSteps(sink, configPath, stopHook, syncExport) {
|
|
184
|
+
const out = [];
|
|
185
|
+
out.push(`Configuration written to ${configPath}.`);
|
|
186
|
+
out.push(`Default sink: ${sink}.`);
|
|
187
|
+
if (stopHook) {
|
|
188
|
+
out.push(`Stop-hook installed at ${stopHook}; new sessions auto-scan on close.`);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
out.push(`Stop-hook not installed. Run "friction-log scan" manually at end of session, or re-run "init" later.`);
|
|
192
|
+
}
|
|
193
|
+
if (syncExport) {
|
|
194
|
+
out.push(`sync-export configured: origin=${syncExport.origin}, writes to ${syncExport.path} after every mutation.`);
|
|
195
|
+
}
|
|
196
|
+
out.push("Quick smoke:");
|
|
197
|
+
out.push(` friction-log log --title "first friction" --tool "demo" --category tool-error`);
|
|
198
|
+
out.push(` friction-log list`);
|
|
199
|
+
out.push(` friction-log file 1 --sink ${sink}`);
|
|
200
|
+
return out;
|
|
201
|
+
}
|
|
202
|
+
/** Minimal readline-based prompter so init has no new runtime dep. */
|
|
203
|
+
function readlinePrompter() {
|
|
204
|
+
return {
|
|
205
|
+
async select(question, choices, def) {
|
|
206
|
+
const rl = await loadReadline();
|
|
207
|
+
try {
|
|
208
|
+
const list = choices
|
|
209
|
+
.map((c, i) => ` ${i + 1}) ${c}${c === def ? " (default)" : ""}`)
|
|
210
|
+
.join("\n");
|
|
211
|
+
const prompt = `${question}\n${list}\nChoice [${def ?? choices[0]}]: `;
|
|
212
|
+
const raw = (await rl.question(prompt)).trim();
|
|
213
|
+
if (!raw)
|
|
214
|
+
return def ?? choices[0];
|
|
215
|
+
const asIdx = Number(raw);
|
|
216
|
+
if (Number.isInteger(asIdx) && asIdx >= 1 && asIdx <= choices.length) {
|
|
217
|
+
return choices[asIdx - 1];
|
|
218
|
+
}
|
|
219
|
+
if (choices.includes(raw))
|
|
220
|
+
return raw;
|
|
221
|
+
process.stderr.write(`(invalid choice "${raw}"; using ${def ?? choices[0]})\n`);
|
|
222
|
+
return def ?? choices[0];
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
rl.close();
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
async confirm(question, def = true) {
|
|
229
|
+
const rl = await loadReadline();
|
|
230
|
+
try {
|
|
231
|
+
const hint = def ? "Y/n" : "y/N";
|
|
232
|
+
const raw = (await rl.question(`${question} [${hint}]: `))
|
|
233
|
+
.trim()
|
|
234
|
+
.toLowerCase();
|
|
235
|
+
if (!raw)
|
|
236
|
+
return def;
|
|
237
|
+
if (raw === "y" || raw === "yes")
|
|
238
|
+
return true;
|
|
239
|
+
if (raw === "n" || raw === "no")
|
|
240
|
+
return false;
|
|
241
|
+
return def;
|
|
242
|
+
}
|
|
243
|
+
finally {
|
|
244
|
+
rl.close();
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
async function loadReadline() {
|
|
250
|
+
const readline = await import("node:readline/promises");
|
|
251
|
+
return readline.createInterface({
|
|
252
|
+
input: process.stdin,
|
|
253
|
+
output: process.stdout,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Friction, FrictionSource, FrictionStatus } from "../types.js";
|
|
2
|
+
export interface ListCommandInput {
|
|
3
|
+
status?: FrictionStatus;
|
|
4
|
+
tool?: string;
|
|
5
|
+
category?: string;
|
|
6
|
+
source?: FrictionSource;
|
|
7
|
+
age?: string;
|
|
8
|
+
limit?: number;
|
|
9
|
+
dbPath?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ListCommandOutput {
|
|
12
|
+
frictions: Friction[];
|
|
13
|
+
}
|
|
14
|
+
export declare function parseAge(age: string | undefined, now?: Date): string | undefined;
|
|
15
|
+
export declare function runList(input: ListCommandInput): ListCommandOutput;
|
|
16
|
+
export declare function formatTable(frictions: Friction[]): string;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { FrictionDb } from "../db.js";
|
|
2
|
+
import { defaultDbPath } from "../paths.js";
|
|
3
|
+
const AGE_PATTERN = /^(\d+)([dwh])$/;
|
|
4
|
+
export function parseAge(age, now = new Date()) {
|
|
5
|
+
if (!age)
|
|
6
|
+
return undefined;
|
|
7
|
+
const match = AGE_PATTERN.exec(age);
|
|
8
|
+
if (!match) {
|
|
9
|
+
throw new Error(`friction-log: invalid --age "${age}". Use e.g. 14d, 4w, 12h`);
|
|
10
|
+
}
|
|
11
|
+
const n = Number(match[1]);
|
|
12
|
+
const unit = match[2];
|
|
13
|
+
const ms = unit === "h"
|
|
14
|
+
? n * 3600_000
|
|
15
|
+
: unit === "d"
|
|
16
|
+
? n * 86_400_000
|
|
17
|
+
: n * 7 * 86_400_000;
|
|
18
|
+
return new Date(now.getTime() - ms).toISOString();
|
|
19
|
+
}
|
|
20
|
+
export function runList(input) {
|
|
21
|
+
const db = new FrictionDb(input.dbPath ?? defaultDbPath());
|
|
22
|
+
try {
|
|
23
|
+
const filter = {
|
|
24
|
+
status: input.status,
|
|
25
|
+
tool: input.tool,
|
|
26
|
+
category: input.category,
|
|
27
|
+
source: input.source,
|
|
28
|
+
sinceIso: parseAge(input.age),
|
|
29
|
+
limit: input.limit,
|
|
30
|
+
};
|
|
31
|
+
return { frictions: db.listFrictions(filter) };
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
db.close();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function formatTable(frictions) {
|
|
38
|
+
if (frictions.length === 0)
|
|
39
|
+
return "(no frictions match)";
|
|
40
|
+
const header = ["id", "status", "tool", "category", "captured", "title"];
|
|
41
|
+
const rows = frictions.map((f) => [
|
|
42
|
+
String(f.id),
|
|
43
|
+
f.status,
|
|
44
|
+
f.toolSurface ?? "-",
|
|
45
|
+
f.category ?? "-",
|
|
46
|
+
f.capturedAt.slice(0, 16).replace("T", " "),
|
|
47
|
+
truncate(f.title, 60),
|
|
48
|
+
]);
|
|
49
|
+
const widths = header.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
50
|
+
const sep = widths.map((w) => "-".repeat(w)).join(" ");
|
|
51
|
+
const fmt = (row) => row.map((c, i) => c.padEnd(widths[i])).join(" ");
|
|
52
|
+
return [fmt(header), sep, ...rows.map(fmt)].join("\n");
|
|
53
|
+
}
|
|
54
|
+
function truncate(s, max) {
|
|
55
|
+
return s.length <= max ? s : s.slice(0, max - 1) + "…";
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Severity } from "../types.js";
|
|
2
|
+
export interface LogCommandInput {
|
|
3
|
+
title: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
tool?: string;
|
|
6
|
+
category?: string;
|
|
7
|
+
severity?: Severity;
|
|
8
|
+
sessionId?: string;
|
|
9
|
+
recurrenceOfId?: number;
|
|
10
|
+
dbPath?: string;
|
|
11
|
+
configPath?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface LogCommandOutput {
|
|
14
|
+
id: number;
|
|
15
|
+
capturedAt: string;
|
|
16
|
+
recurrenceOfId: number | null;
|
|
17
|
+
}
|
|
18
|
+
export declare function runLog(input: LogCommandInput): LogCommandOutput;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { FrictionDb } from "../db.js";
|
|
2
|
+
import { defaultDbPath } from "../paths.js";
|
|
3
|
+
import { maybeSyncExport } from "./sync-export.js";
|
|
4
|
+
export function runLog(input) {
|
|
5
|
+
const dbPath = input.dbPath ?? defaultDbPath();
|
|
6
|
+
const db = new FrictionDb(dbPath);
|
|
7
|
+
try {
|
|
8
|
+
if (input.recurrenceOfId !== undefined) {
|
|
9
|
+
const parent = db.getFriction(input.recurrenceOfId);
|
|
10
|
+
if (!parent) {
|
|
11
|
+
throw new Error(`friction-log: --recurrence-of ${input.recurrenceOfId} does not match any friction`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
// Normalise empty string to null so a `--session ''` (or a wrapper
|
|
15
|
+
// that always passes the flag with a possibly-empty value) does not
|
|
16
|
+
// sneak past the upsert and then trip the FK on the friction insert.
|
|
17
|
+
const sessionId = typeof input.sessionId === "string" && input.sessionId !== ""
|
|
18
|
+
? input.sessionId
|
|
19
|
+
: null;
|
|
20
|
+
// Sessions row must exist before the friction insert, otherwise the
|
|
21
|
+
// frictions.session_id FK violates and the log path exits 1 with a
|
|
22
|
+
// raw SQLite error. The common case is an agent attributing a
|
|
23
|
+
// mid-session friction to its live runtime session id (e.g.
|
|
24
|
+
// $CLAUDE_CODE_SESSION_ID) which was never imported. Upsert a
|
|
25
|
+
// placeholder so the call just works; a later `friction-log import`
|
|
26
|
+
// or `scan` overwrites it with full metadata via `upsertSession`.
|
|
27
|
+
if (sessionId !== null) {
|
|
28
|
+
db.ensureSession(sessionId);
|
|
29
|
+
}
|
|
30
|
+
const insert = {
|
|
31
|
+
title: input.title,
|
|
32
|
+
description: input.description ?? null,
|
|
33
|
+
toolSurface: input.tool ?? null,
|
|
34
|
+
category: input.category ?? null,
|
|
35
|
+
severity: input.severity ?? null,
|
|
36
|
+
sessionId,
|
|
37
|
+
recurrenceOfId: input.recurrenceOfId ?? null,
|
|
38
|
+
source: "manual",
|
|
39
|
+
};
|
|
40
|
+
const f = db.insertFriction(insert);
|
|
41
|
+
maybeSyncExport({ dbPath, configPath: input.configPath });
|
|
42
|
+
return {
|
|
43
|
+
id: f.id,
|
|
44
|
+
capturedAt: f.capturedAt,
|
|
45
|
+
recurrenceOfId: f.recurrenceOfId,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
db.close();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/commands/log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA4B,MAAM,UAAU,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAoBnD,MAAM,UAAU,MAAM,CAAC,KAAsB;IAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,aAAa,EAAE,CAAC;IAC/C,MAAM,EAAE,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,CAAC;QACH,IAAI,KAAK,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;YACvC,MAAM,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;YACpD,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CACb,iCAAiC,KAAK,CAAC,cAAc,8BAA8B,CACpF,CAAC;YACJ,CAAC;QACH,CAAC;QACD,mEAAmE;QACnE,oEAAoE;QACpE,qEAAqE;QACrE,MAAM,SAAS,GACb,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,IAAI,KAAK,CAAC,SAAS,KAAK,EAAE;YAC3D,CAAC,CAAC,KAAK,CAAC,SAAS;YACjB,CAAC,CAAC,IAAI,CAAC;QACX,oEAAoE;QACpE,mEAAmE;QACnE,8DAA8D;QAC9D,4DAA4D;QAC5D,8DAA8D;QAC9D,oEAAoE;QACpE,kEAAkE;QAClE,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,EAAE,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC9B,CAAC;QACD,MAAM,MAAM,GAAwB;YAClC,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI;YACtC,WAAW,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI;YAC/B,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI;YAChC,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI;YAChC,SAAS;YACT,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,IAAI;YAC5C,MAAM,EAAE,QAAQ;SACjB,CAAC;QACF,MAAM,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QACpC,eAAe,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;QAC1D,OAAO;YACL,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,UAAU,EAAE,CAAC,CAAC,UAAU;YACxB,cAAc,EAAE,CAAC,CAAC,cAAc;SACjC,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FrictionDb } from "../db.js";
|
|
2
|
+
import { defaultDbPath } from "../paths.js";
|
|
3
|
+
import { maybeSyncExport } from "./sync-export.js";
|
|
4
|
+
export function runRm(input) {
|
|
5
|
+
const dbPath = input.dbPath ?? defaultDbPath();
|
|
6
|
+
const db = new FrictionDb(dbPath);
|
|
7
|
+
try {
|
|
8
|
+
const friction = db.getFriction(input.frictionId);
|
|
9
|
+
if (!friction) {
|
|
10
|
+
throw new Error(`friction-log: friction id=${input.frictionId} not found`);
|
|
11
|
+
}
|
|
12
|
+
const removed = db.deleteFriction(input.frictionId);
|
|
13
|
+
if (removed) {
|
|
14
|
+
maybeSyncExport({ dbPath, configPath: input.configPath });
|
|
15
|
+
}
|
|
16
|
+
return { removed };
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
db.close();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=rm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rm.js","sourceRoot":"","sources":["../../src/commands/rm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAYnD,MAAM,UAAU,KAAK,CAAC,KAAqB;IACzC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,aAAa,EAAE,CAAC;IAC/C,MAAM,EAAE,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAClD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CACb,6BAA6B,KAAK,CAAC,UAAU,YAAY,CAC1D,CAAC;QACJ,CAAC;QACD,MAAM,OAAO,GAAG,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,OAAO,EAAE,CAAC;YACZ,eAAe,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,CAAC;IACrB,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Friction } from "../types.js";
|
|
2
|
+
export interface ScanCommandInput {
|
|
3
|
+
sessionId?: string;
|
|
4
|
+
transcriptPath?: string;
|
|
5
|
+
adapter?: string;
|
|
6
|
+
dbPath?: string;
|
|
7
|
+
configPath?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ScanCommandOutput {
|
|
10
|
+
sessionId: string;
|
|
11
|
+
candidatesFound: number;
|
|
12
|
+
inserted: number;
|
|
13
|
+
skippedDuplicates: number;
|
|
14
|
+
adapter: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function runScan(input: ScanCommandInput): Promise<ScanCommandOutput>;
|
|
17
|
+
export interface StopHookPayload {
|
|
18
|
+
session_id?: string;
|
|
19
|
+
sessionId?: string;
|
|
20
|
+
transcript_path?: string;
|
|
21
|
+
transcriptPath?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function payloadToScanInput(payload: StopHookPayload, adapter?: string): ScanCommandInput;
|
|
24
|
+
export declare function summarize(output: ScanCommandOutput, sessionId: string): string;
|
|
25
|
+
export { Friction };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { FrictionDb } from "../db.js";
|
|
2
|
+
import { defaultDbPath } from "../paths.js";
|
|
3
|
+
import { loadScanner } from "../scanners/index.js";
|
|
4
|
+
import { maybeSyncExport } from "./sync-export.js";
|
|
5
|
+
export async function runScan(input) {
|
|
6
|
+
const adapterName = input.adapter ?? "claude-code";
|
|
7
|
+
const scanner = loadScanner(adapterName);
|
|
8
|
+
const result = await scanner.scan({
|
|
9
|
+
sessionId: input.sessionId,
|
|
10
|
+
transcriptPath: input.transcriptPath,
|
|
11
|
+
});
|
|
12
|
+
const dbPath = input.dbPath ?? defaultDbPath();
|
|
13
|
+
const db = new FrictionDb(dbPath);
|
|
14
|
+
try {
|
|
15
|
+
db.upsertSession({
|
|
16
|
+
id: result.session.id,
|
|
17
|
+
startedAt: result.session.startedAt,
|
|
18
|
+
endedAt: result.session.endedAt,
|
|
19
|
+
projectPaths: result.session.projectPaths ?? null,
|
|
20
|
+
transcriptPath: result.session.transcriptPath ?? null,
|
|
21
|
+
adapter: result.session.adapter ?? adapterName,
|
|
22
|
+
});
|
|
23
|
+
let inserted = 0;
|
|
24
|
+
let skipped = 0;
|
|
25
|
+
for (const c of result.frictionCandidates) {
|
|
26
|
+
const existing = db.findFrictionByTriple(result.session.id, c.toolSurface ?? null, c.title);
|
|
27
|
+
if (existing) {
|
|
28
|
+
skipped++;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const insert = {
|
|
32
|
+
sessionId: result.session.id,
|
|
33
|
+
toolSurface: c.toolSurface ?? null,
|
|
34
|
+
title: c.title,
|
|
35
|
+
description: c.description ?? null,
|
|
36
|
+
severity: c.severity ?? null,
|
|
37
|
+
category: c.category ?? null,
|
|
38
|
+
source: "scan",
|
|
39
|
+
};
|
|
40
|
+
db.insertFriction(insert);
|
|
41
|
+
inserted++;
|
|
42
|
+
}
|
|
43
|
+
// Skip write-through entirely when nothing was actually inserted (all
|
|
44
|
+
// candidates were duplicates, or there were none): the export payload
|
|
45
|
+
// only carries frictions, so a session-only upsert with zero new
|
|
46
|
+
// frictions can never change its content anyway.
|
|
47
|
+
if (inserted > 0) {
|
|
48
|
+
maybeSyncExport({ dbPath, configPath: input.configPath });
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
sessionId: result.session.id,
|
|
52
|
+
candidatesFound: result.frictionCandidates.length,
|
|
53
|
+
inserted,
|
|
54
|
+
skippedDuplicates: skipped,
|
|
55
|
+
adapter: adapterName,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
db.close();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function payloadToScanInput(payload, adapter) {
|
|
63
|
+
return {
|
|
64
|
+
sessionId: payload.session_id ?? payload.sessionId,
|
|
65
|
+
transcriptPath: payload.transcript_path ?? payload.transcriptPath,
|
|
66
|
+
adapter,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export function summarize(output, sessionId) {
|
|
70
|
+
return (`scanned session=${sessionId} adapter=${output.adapter} ` +
|
|
71
|
+
`candidates=${output.candidatesFound} inserted=${output.inserted} skipped=${output.skippedDuplicates}`);
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Friction, FrictionSource, FrictionStatus } from "../types.js";
|
|
2
|
+
export interface SearchCommandInput {
|
|
3
|
+
query: string;
|
|
4
|
+
status?: FrictionStatus;
|
|
5
|
+
tool?: string;
|
|
6
|
+
category?: string;
|
|
7
|
+
source?: FrictionSource;
|
|
8
|
+
age?: string;
|
|
9
|
+
limit?: number;
|
|
10
|
+
dbPath?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface SearchCommandOutput {
|
|
13
|
+
frictions: Friction[];
|
|
14
|
+
}
|
|
15
|
+
export declare function runSearch(input: SearchCommandInput): SearchCommandOutput;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { FrictionDb } from "../db.js";
|
|
2
|
+
import { defaultDbPath } from "../paths.js";
|
|
3
|
+
import { parseAge } from "./list.js";
|
|
4
|
+
export function runSearch(input) {
|
|
5
|
+
if (!input.query || !input.query.trim()) {
|
|
6
|
+
throw new Error("friction-log: search query must not be empty");
|
|
7
|
+
}
|
|
8
|
+
const db = new FrictionDb(input.dbPath ?? defaultDbPath());
|
|
9
|
+
try {
|
|
10
|
+
const filter = {
|
|
11
|
+
status: input.status,
|
|
12
|
+
tool: input.tool,
|
|
13
|
+
category: input.category,
|
|
14
|
+
source: input.source,
|
|
15
|
+
sinceIso: parseAge(input.age),
|
|
16
|
+
limit: input.limit,
|
|
17
|
+
};
|
|
18
|
+
try {
|
|
19
|
+
return { frictions: db.searchFrictions(input.query, filter) };
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
23
|
+
// better-sqlite3 surfaces FTS5 parse failures as opaque SQLite errors
|
|
24
|
+
// ("unterminated string", "fts5: syntax error near ...", etc). The
|
|
25
|
+
// surrounding query is parameterized, so any error here is the user's
|
|
26
|
+
// MATCH expression. Turn it into a hint pointing at the FTS5 docs.
|
|
27
|
+
if (/fts5|MATCH|unterminated|syntax error|near "/i.test(msg)) {
|
|
28
|
+
throw new Error(`friction-log: invalid FTS5 query "${input.query}". See https://sqlite.org/fts5.html#full_text_query_syntax`);
|
|
29
|
+
}
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
db.close();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/commands/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA4B,MAAM,UAAU,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAiBrC,MAAM,UAAU,SAAS,CAAC,KAAyB;IACjD,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IACD,MAAM,EAAE,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,IAAI,aAAa,EAAE,CAAC,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,MAAM,GAAwB;YAClC,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC;YAC7B,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB,CAAC;QACF,IAAI,CAAC;YACH,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;QAChE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,sEAAsE;YACtE,mEAAmE;YACnE,sEAAsE;YACtE,mEAAmE;YACnE,IAAI,8CAA8C,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7D,MAAM,IAAI,KAAK,CACb,qCAAqC,KAAK,CAAC,KAAK,4DAA4D,CAC7G,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC"}
|