@temet/cli 0.3.2 → 0.3.3
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/audit.js +13 -1
- package/dist/doctor.d.ts +5 -0
- package/dist/doctor.js +79 -0
- package/dist/index.js +37 -1
- package/dist/lib/diagnostic-runner.js +13 -2
- package/package.json +1 -1
package/dist/audit.js
CHANGED
|
@@ -459,7 +459,19 @@ export async function runAuditCommand(opts) {
|
|
|
459
459
|
}
|
|
460
460
|
resolvedPath = resolution.path;
|
|
461
461
|
}
|
|
462
|
-
|
|
462
|
+
let sessionFiles = findSessionFiles(resolvedPath);
|
|
463
|
+
if (sessionFiles.length === 0 && !opts.path && !opts.quiet && !opts.json) {
|
|
464
|
+
const { findClaudeProjectCandidates } = await import("./lib/path-resolver.js");
|
|
465
|
+
const candidates = findClaudeProjectCandidates();
|
|
466
|
+
if (candidates.length > 0) {
|
|
467
|
+
const selected = await chooseSessionCandidate(candidates);
|
|
468
|
+
if (selected) {
|
|
469
|
+
resolvedPath = selected.sessionDir;
|
|
470
|
+
sessionFiles = findSessionFiles(resolvedPath);
|
|
471
|
+
process.stderr.write(`${dim(`[temet] using ${selected.label}`, process.stderr)}\n`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
463
475
|
if (sessionFiles.length === 0) {
|
|
464
476
|
if (!opts.quiet) {
|
|
465
477
|
console.error(`[temet] no .jsonl session files found in ${resolvedPath}`);
|
package/dist/doctor.d.ts
ADDED
package/dist/doctor.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { basename } from "node:path";
|
|
3
|
+
import { findClaudeProjectCandidates, resolveSessionPath, } from "./lib/path-resolver.js";
|
|
4
|
+
import { getSettingsPath, isHookInstalled, readSettings } from "./lib/hook-installer.js";
|
|
5
|
+
import { getMenubarAppPath, isMenubarInstalled } from "./lib/menubar-installer.js";
|
|
6
|
+
import { getMenubarConfigPath, getMenubarStatePath, } from "./lib/menubar-state.js";
|
|
7
|
+
import { resolveAuth } from "./lib/narrator-lite.js";
|
|
8
|
+
import { findSessionFiles } from "./lib/session-audit.js";
|
|
9
|
+
function buildCheck(id, label, status, detail) {
|
|
10
|
+
return { id, label, status, detail };
|
|
11
|
+
}
|
|
12
|
+
function iconFor(status) {
|
|
13
|
+
return status === "ok" ? "OK" : "WARN";
|
|
14
|
+
}
|
|
15
|
+
export async function runDoctorCommand(opts) {
|
|
16
|
+
const resolution = resolveSessionPath(opts.path || undefined, process.env);
|
|
17
|
+
const resolvedPath = resolution.path;
|
|
18
|
+
const sessionFiles = resolvedPath ? findSessionFiles(resolvedPath) : [];
|
|
19
|
+
const hookInstalled = isHookInstalled(readSettings(getSettingsPath()));
|
|
20
|
+
const narrationAvailable = resolveAuth() !== null;
|
|
21
|
+
const menubarInstalled = process.platform === "darwin" && isMenubarInstalled();
|
|
22
|
+
const menubarConfigExists = existsSync(getMenubarConfigPath());
|
|
23
|
+
const menubarStateExists = existsSync(getMenubarStatePath());
|
|
24
|
+
const candidates = findClaudeProjectCandidates();
|
|
25
|
+
const checks = [
|
|
26
|
+
buildCheck("sessions-root", "Claude Code projects", candidates.length > 0 ? "ok" : "warn", candidates.length > 0
|
|
27
|
+
? `${candidates.length} project(s) with session history found`
|
|
28
|
+
: "No Claude Code project with session history was found"),
|
|
29
|
+
buildCheck("resolved-path", "Resolved session source", resolvedPath ? "ok" : "warn", resolvedPath
|
|
30
|
+
? `${resolution.source}: ${basename(resolvedPath)}`
|
|
31
|
+
: "No session path could be resolved automatically"),
|
|
32
|
+
buildCheck("session-files", "Session files", sessionFiles.length > 0 ? "ok" : "warn", resolvedPath
|
|
33
|
+
? `${sessionFiles.length} .jsonl file(s) in ${resolvedPath}`
|
|
34
|
+
: "No resolved project to scan"),
|
|
35
|
+
buildCheck("hook", "SessionEnd hook", hookInstalled ? "ok" : "warn", hookInstalled
|
|
36
|
+
? `Installed in ${getSettingsPath()}`
|
|
37
|
+
: "Not installed. Run temet install-hook"),
|
|
38
|
+
buildCheck("narration", "Narration access", narrationAvailable ? "ok" : "warn", narrationAvailable
|
|
39
|
+
? "Token detected for LLM narration"
|
|
40
|
+
: "No ANTHROPIC_API_KEY or CLAUDE_AUTH_TOKEN detected"),
|
|
41
|
+
buildCheck("menubar-app", "Temet app", process.platform !== "darwin" ? "warn" : menubarInstalled ? "ok" : "warn", process.platform !== "darwin"
|
|
42
|
+
? "Temet app is only available on macOS"
|
|
43
|
+
: menubarInstalled
|
|
44
|
+
? `Installed at ${getMenubarAppPath()}`
|
|
45
|
+
: "Not installed. Run temet install-menubar"),
|
|
46
|
+
buildCheck("menubar-config", "Temet app config", process.platform !== "darwin"
|
|
47
|
+
? "warn"
|
|
48
|
+
: menubarConfigExists
|
|
49
|
+
? "ok"
|
|
50
|
+
: "warn", process.platform !== "darwin"
|
|
51
|
+
? "Not applicable on this platform"
|
|
52
|
+
: menubarConfigExists
|
|
53
|
+
? `Config present at ${getMenubarConfigPath()}`
|
|
54
|
+
: "No menubar config found yet"),
|
|
55
|
+
buildCheck("menubar-state", "Temet app state", menubarStateExists ? "ok" : "warn", menubarStateExists
|
|
56
|
+
? `Sidecar present at ${getMenubarStatePath()}`
|
|
57
|
+
: "No menubar state written yet. Run temet audit --track"),
|
|
58
|
+
];
|
|
59
|
+
const report = {
|
|
60
|
+
version: 1,
|
|
61
|
+
checks,
|
|
62
|
+
summary: {
|
|
63
|
+
ok: checks.filter((check) => check.status === "ok").length,
|
|
64
|
+
warn: checks.filter((check) => check.status === "warn").length,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
if (opts.json) {
|
|
68
|
+
console.log(JSON.stringify(report, null, "\t"));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
console.log("Temet doctor");
|
|
72
|
+
console.log("");
|
|
73
|
+
for (const check of checks) {
|
|
74
|
+
console.log(`${iconFor(check.status)} ${check.label}`);
|
|
75
|
+
console.log(` ${check.detail}`);
|
|
76
|
+
}
|
|
77
|
+
console.log("");
|
|
78
|
+
console.log(`Summary: ${report.summary.ok} OK, ${report.summary.warn} warning${report.summary.warn === 1 ? "" : "s"}`);
|
|
79
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execFile } from "node:child_process";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
3
4
|
import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
5
|
import { homedir } from "node:os";
|
|
5
6
|
import { dirname, resolve } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
6
8
|
import { promisify } from "node:util";
|
|
7
9
|
import { buildAuditCliOptions, parseFlagBag, readOptionalString, } from "./lib/cli-args.js";
|
|
8
10
|
const execFileAsync = promisify(execFile);
|
|
@@ -11,10 +13,22 @@ const DEFAULT_SERVER_NAME = "temet";
|
|
|
11
13
|
const DEFAULT_PROTOCOL_APP_NAME = "Temet Handler";
|
|
12
14
|
const LINUX_DESKTOP_ENTRY = "temet-handler.desktop";
|
|
13
15
|
const LINUX_HANDLER_SCRIPT = "temet-protocol-handler";
|
|
14
|
-
const
|
|
16
|
+
const PACKAGE_JSON_PATH = fileURLToPath(new URL("../package.json", import.meta.url));
|
|
17
|
+
function readCliVersion() {
|
|
18
|
+
try {
|
|
19
|
+
const packageJson = JSON.parse(readFileSync(PACKAGE_JSON_PATH, "utf8"));
|
|
20
|
+
return packageJson.version || "0.0.0";
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return "0.0.0";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const CLI_VERSION = readCliVersion();
|
|
27
|
+
const HELP = `Temet CLI ${CLI_VERSION} — discover the skills you already demonstrate in AI work
|
|
15
28
|
|
|
16
29
|
Commands:
|
|
17
30
|
\ttemet audit [--path <session-dir>] Analyze local sessions, surface skills and workflows
|
|
31
|
+
\ttemet doctor [--path <session-dir>] Check local Temet setup, sessions, hook, and app state
|
|
18
32
|
\ttemet traces [--path <session-dir>] Show the evidence behind the strongest signals
|
|
19
33
|
\ttemet plan [--path <session-dir>] Turn the current audit into an operating plan
|
|
20
34
|
\ttemet install-hook Auto-audit after every Claude Code session
|
|
@@ -41,6 +55,7 @@ Advanced:
|
|
|
41
55
|
|
|
42
56
|
Examples:
|
|
43
57
|
\ttemet audit Auto-detect sessions from cwd
|
|
58
|
+
\ttemet doctor Check whether Temet is fully wired on this machine
|
|
44
59
|
\ttemet audit Open the text report by default
|
|
45
60
|
\ttemet traces Inspect the proof behind the current reading
|
|
46
61
|
\ttemet plan Get the current operating plan
|
|
@@ -55,6 +70,10 @@ function printHelp(exitCode = 0) {
|
|
|
55
70
|
console.log(HELP);
|
|
56
71
|
process.exit(exitCode);
|
|
57
72
|
}
|
|
73
|
+
function printVersion() {
|
|
74
|
+
console.log(CLI_VERSION);
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
58
77
|
function isObject(value) {
|
|
59
78
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
60
79
|
}
|
|
@@ -147,6 +166,9 @@ function parseConnectUrlOptions(flags, positionals) {
|
|
|
147
166
|
};
|
|
148
167
|
}
|
|
149
168
|
function parseArgs(argv) {
|
|
169
|
+
if (argv.includes("-v") || argv.includes("--version")) {
|
|
170
|
+
printVersion();
|
|
171
|
+
}
|
|
150
172
|
if (argv.length === 0 || argv.includes("-h") || argv.includes("--help")) {
|
|
151
173
|
printHelp(0);
|
|
152
174
|
}
|
|
@@ -159,6 +181,15 @@ function parseArgs(argv) {
|
|
|
159
181
|
options: buildAuditCliOptions(flags, process.env),
|
|
160
182
|
};
|
|
161
183
|
}
|
|
184
|
+
if (command === "doctor") {
|
|
185
|
+
return {
|
|
186
|
+
command,
|
|
187
|
+
options: {
|
|
188
|
+
path: readOptionalString(flags, "path") ?? undefined,
|
|
189
|
+
json: Boolean(flags.get("json")),
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
162
193
|
if (command === "traces" || command === "plan") {
|
|
163
194
|
return {
|
|
164
195
|
command,
|
|
@@ -511,6 +542,11 @@ async function run() {
|
|
|
511
542
|
await runAuditCommand(parsed.options);
|
|
512
543
|
return;
|
|
513
544
|
}
|
|
545
|
+
if (parsed.command === "doctor") {
|
|
546
|
+
const { runDoctorCommand } = await import("./doctor.js");
|
|
547
|
+
await runDoctorCommand(parsed.options);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
514
550
|
if (parsed.command === "traces") {
|
|
515
551
|
const { runTracesCommand } = await import("./traces.js");
|
|
516
552
|
await runTracesCommand(parsed.options);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
2
|
import { createInterface } from "node:readline";
|
|
3
|
-
import { resolveSessionPath } from "./path-resolver.js";
|
|
3
|
+
import { findClaudeProjectCandidates, resolveSessionPath, } from "./path-resolver.js";
|
|
4
4
|
import { trackAuditSnapshot } from "./audit-tracking.js";
|
|
5
5
|
import { findSessionFiles, runAudit, } from "./session-audit.js";
|
|
6
6
|
async function chooseSessionCandidate(candidates) {
|
|
@@ -55,7 +55,18 @@ export async function resolveDiagnostics(opts) {
|
|
|
55
55
|
process.stderr.write(`[temet] using ${label}\n`);
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
|
|
58
|
+
let sessionFiles = findSessionFiles(resolvedPath);
|
|
59
|
+
if (sessionFiles.length === 0 && !opts.path && !opts.quiet && !opts.json) {
|
|
60
|
+
const candidates = findClaudeProjectCandidates();
|
|
61
|
+
if (candidates.length > 0) {
|
|
62
|
+
const selected = await chooseSessionCandidate(candidates);
|
|
63
|
+
if (selected) {
|
|
64
|
+
resolvedPath = selected.sessionDir;
|
|
65
|
+
sessionFiles = findSessionFiles(resolvedPath);
|
|
66
|
+
process.stderr.write(`[temet] using ${selected.label}\n`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
59
70
|
if (sessionFiles.length === 0) {
|
|
60
71
|
throw new Error(`No .jsonl session files found in ${resolvedPath}.`);
|
|
61
72
|
}
|