@kairyou/agent-tools 0.12.0 → 0.13.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/config.default.jsonc +12 -3
- package/dist/log/hook.mjs +1588 -0
- package/dist/log/opencode-plugin.mjs +113 -0
- package/docs/en/extras.md +85 -5
- package/docs/zh-CN/extras.md +84 -5
- package/integrations/log/hook.mjs +936 -0
- package/integrations/log/opencode-plugin.mjs +137 -0
- package/package.json +1 -1
- package/scripts/build.mjs +6 -0
- package/scripts/install.mjs +110 -7
- package/skills/workflow/at-daily-log/SKILL.md +13 -4
- package/skills/workflow/at-self-eval/SKILL.md +7 -2
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// OpenCode adapter for the log capability: translates plugin events into the
|
|
2
|
+
// hook payloads dist/log/hook.mjs consumes, so both agents share one recorder.
|
|
3
|
+
//
|
|
4
|
+
// Verified against a real OpenCode run: chat.message carries the user prompt,
|
|
5
|
+
// tool.execute.after carries absolute file paths in output.metadata.files, and
|
|
6
|
+
// the assistant's final text arrives as message.part.updated parts belonging
|
|
7
|
+
// to messages that message.updated announced as role=assistant.
|
|
8
|
+
|
|
9
|
+
import { spawn } from "node:child_process";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
|
|
13
|
+
const HOOK = path.join(path.dirname(fileURLToPath(import.meta.url)), "hook.mjs");
|
|
14
|
+
|
|
15
|
+
// Sends run strictly one after another: the hook does an unlocked
|
|
16
|
+
// read-modify-write on the day state, so parallel hook processes from the
|
|
17
|
+
// same adapter would drop each other's events.
|
|
18
|
+
let sendQueue = Promise.resolve();
|
|
19
|
+
|
|
20
|
+
function send(payload) {
|
|
21
|
+
sendQueue = sendQueue.then(
|
|
22
|
+
() =>
|
|
23
|
+
new Promise((resolve) => {
|
|
24
|
+
try {
|
|
25
|
+
// OpenCode is a Bun single binary, so process.execPath points at
|
|
26
|
+
// opencode.exe; the hook needs the node on PATH (required >= 22 anyway).
|
|
27
|
+
const child = spawn("node", [HOOK], {
|
|
28
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
29
|
+
windowsHide: true,
|
|
30
|
+
});
|
|
31
|
+
child.on("error", () => resolve());
|
|
32
|
+
child.on("exit", () => resolve());
|
|
33
|
+
child.stdin.write(JSON.stringify(payload));
|
|
34
|
+
child.stdin.end();
|
|
35
|
+
} catch {
|
|
36
|
+
// Logging must never break the session.
|
|
37
|
+
resolve();
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const AgentToolsLog = async ({ directory, project } = {}) => {
|
|
44
|
+
const cwd = directory || project?.worktree || process.cwd();
|
|
45
|
+
const assistantMessageIds = new Set();
|
|
46
|
+
const lastAssistantText = new Map();
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
"chat.message": async (_input, output) => {
|
|
50
|
+
const message = output?.message;
|
|
51
|
+
if (message?.role !== "user") return;
|
|
52
|
+
const text = (output?.parts || [])
|
|
53
|
+
.filter((part) => part?.type === "text" && typeof part.text === "string")
|
|
54
|
+
.map((part) => part.text)
|
|
55
|
+
.join("\n");
|
|
56
|
+
if (!text.trim()) return;
|
|
57
|
+
send({
|
|
58
|
+
hook_event_name: "UserPromptSubmit",
|
|
59
|
+
session_id: message.sessionID,
|
|
60
|
+
cwd,
|
|
61
|
+
prompt: text,
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
"tool.execute.before": async (input, output) => {
|
|
65
|
+
const tool = String(input?.tool || "");
|
|
66
|
+
if (tool === "bash") return;
|
|
67
|
+
const args = output?.args;
|
|
68
|
+
if (!args || typeof args !== "object") return;
|
|
69
|
+
// Forwarded so the hook can snapshot a baseline for the detailed diff;
|
|
70
|
+
// tools whose args carry no file path (such as apply_patch) are simply
|
|
71
|
+
// skipped there and their diff stays "unknown".
|
|
72
|
+
send({
|
|
73
|
+
hook_event_name: "PreToolUse",
|
|
74
|
+
session_id: input?.sessionID,
|
|
75
|
+
cwd,
|
|
76
|
+
tool_name: tool === "write" ? "Write" : "Edit",
|
|
77
|
+
tool_input: args,
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
"tool.execute.after": async (input, output) => {
|
|
81
|
+
const sessionId = input?.sessionID;
|
|
82
|
+
const tool = String(input?.tool || "");
|
|
83
|
+
if (tool === "bash") {
|
|
84
|
+
send({
|
|
85
|
+
hook_event_name: "PostToolUse",
|
|
86
|
+
session_id: sessionId,
|
|
87
|
+
cwd,
|
|
88
|
+
tool_name: "Bash",
|
|
89
|
+
tool_input: { command: String(input?.args?.command || "") },
|
|
90
|
+
});
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const files = output?.metadata?.files;
|
|
94
|
+
if (!Array.isArray(files)) return;
|
|
95
|
+
for (const file of files) {
|
|
96
|
+
if (!file?.filePath) continue;
|
|
97
|
+
send({
|
|
98
|
+
hook_event_name: "PostToolUse",
|
|
99
|
+
session_id: sessionId,
|
|
100
|
+
cwd,
|
|
101
|
+
tool_name: tool === "write" ? "Write" : "Edit",
|
|
102
|
+
tool_input: { file_path: file.filePath },
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
event: async ({ event }) => {
|
|
107
|
+
const type = event?.type;
|
|
108
|
+
const properties = event?.properties || {};
|
|
109
|
+
if (type === "message.updated") {
|
|
110
|
+
if (properties.info?.role === "assistant") assistantMessageIds.add(properties.info.id);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (type === "message.part.updated") {
|
|
114
|
+
const part = properties.part;
|
|
115
|
+
if (part?.type === "text" && assistantMessageIds.has(part.messageID)) {
|
|
116
|
+
lastAssistantText.set(part.sessionID, String(part.text || ""));
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (type === "session.idle") {
|
|
121
|
+
send({
|
|
122
|
+
hook_event_name: "Stop",
|
|
123
|
+
session_id: properties.sessionID,
|
|
124
|
+
cwd,
|
|
125
|
+
last_assistant_message: lastAssistantText.get(properties.sessionID) || "",
|
|
126
|
+
});
|
|
127
|
+
// Cleared per turn: a cancelled or text-less next turn must not
|
|
128
|
+
// inherit this turn's summary as its own result.
|
|
129
|
+
lastAssistantText.delete(properties.sessionID);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (type === "session.deleted") {
|
|
133
|
+
lastAssistantText.delete(properties.sessionID);
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kairyou/agent-tools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "Reusable Agent Skills, plus integrations (statusline, provider usage, vision) that install into Codex, Claude Code, and opencode.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
package/scripts/build.mjs
CHANGED
|
@@ -35,6 +35,12 @@ const TARGETS = {
|
|
|
35
35
|
cli: path.join(ROOT, "integrations", "vision", "lib", "cli.mjs"),
|
|
36
36
|
},
|
|
37
37
|
},
|
|
38
|
+
log: {
|
|
39
|
+
entryPoints: {
|
|
40
|
+
hook: path.join(ROOT, "integrations", "log", "hook.mjs"),
|
|
41
|
+
"opencode-plugin": path.join(ROOT, "integrations", "log", "opencode-plugin.mjs"),
|
|
42
|
+
},
|
|
43
|
+
},
|
|
38
44
|
};
|
|
39
45
|
|
|
40
46
|
// Repo-shipped usage routes (a fork can commit integrations/usage/routes/*.mjs
|
package/scripts/install.mjs
CHANGED
|
@@ -7,19 +7,20 @@
|
|
|
7
7
|
// statusline Claude Code statusLine script (claude only).
|
|
8
8
|
// usage Active API provider quota/balance (all agents).
|
|
9
9
|
// vision inspect_image MCP server + at-vision skill (all agents).
|
|
10
|
+
// log AI session work log via agent hooks (all agents).
|
|
10
11
|
//
|
|
11
12
|
// Standalone commands (dispatched before capability parsing):
|
|
12
13
|
// inspect-image <path|url> --question "..." Human diagnostic for vision.
|
|
13
14
|
// mcp-vision Run the vision MCP stdio server.
|
|
14
15
|
//
|
|
15
16
|
// Targets:
|
|
16
|
-
// claude -> ~/.claude/settings.json (statusLine key)
|
|
17
|
+
// claude -> ~/.claude/settings.json (statusLine key + log hooks)
|
|
17
18
|
// + ~/.claude/skills/at-usage
|
|
18
19
|
// ~/.claude.json (vision MCP) + ~/.claude/skills (at-vision)
|
|
19
|
-
// codex -> ~/.codex/hooks.json (
|
|
20
|
+
// codex -> ~/.codex/hooks.json (usage + log hooks)
|
|
20
21
|
// + ~/.agents/skills/at-usage
|
|
21
22
|
// ~/.codex/config.toml (vision MCP) + ~/.agents/skills (at-vision)
|
|
22
|
-
// opencode -> ~/.config/opencode/ (
|
|
23
|
+
// opencode -> ~/.config/opencode/ (usage + log plugins,
|
|
23
24
|
// vision MCP in opencode.json + skills/at-vision)
|
|
24
25
|
// Runtime scripts are copied into ~/.agent-tools so this installer can be
|
|
25
26
|
// run via npx from GitHub without requiring a persistent local clone.
|
|
@@ -69,6 +70,8 @@ const PACKAGE_VERSION = (() => {
|
|
|
69
70
|
// Everything copied into ~/.agent-tools is built output from dist/ (see
|
|
70
71
|
// scripts/build.mjs); integrations/ holds the sources.
|
|
71
72
|
const SOURCE = {
|
|
73
|
+
logHook: path.join(REPO_ROOT, "dist", "log", "hook.mjs"),
|
|
74
|
+
logOpencodePlugin: path.join(REPO_ROOT, "dist", "log", "opencode-plugin.mjs"),
|
|
72
75
|
codexUsageHook: path.join(REPO_ROOT, "dist", "usage", "codex-hook.mjs"),
|
|
73
76
|
usageScript: path.join(REPO_ROOT, "dist", "usage", "core.mjs"),
|
|
74
77
|
usageCli: path.join(REPO_ROOT, "dist", "usage", "cli.mjs"),
|
|
@@ -78,6 +81,8 @@ const SOURCE = {
|
|
|
78
81
|
opencodeUsageTui: path.join(REPO_ROOT, "dist", "usage", "opencode-tui.mjs"),
|
|
79
82
|
};
|
|
80
83
|
const RUNTIME = {
|
|
84
|
+
logHook: path.join(INSTALL_ROOT, "dist", "log", "hook.mjs"),
|
|
85
|
+
logOpencodePlugin: path.join(INSTALL_ROOT, "dist", "log", "opencode-plugin.mjs"),
|
|
81
86
|
codexUsageHook: path.join(INSTALL_ROOT, "dist", "usage", "codex-hook.mjs"),
|
|
82
87
|
usageScript: path.join(INSTALL_ROOT, "dist", "usage", "core.mjs"),
|
|
83
88
|
usageCli: path.join(INSTALL_ROOT, "dist", "usage", "cli.mjs"),
|
|
@@ -86,12 +91,12 @@ const RUNTIME = {
|
|
|
86
91
|
opencodeUsagePlugin: path.join(INSTALL_ROOT, "dist", "usage", "opencode-plugin.mjs"),
|
|
87
92
|
opencodeUsageTui: path.join(INSTALL_ROOT, "dist", "usage", "opencode-tui.mjs"),
|
|
88
93
|
};
|
|
89
|
-
const ALL_CAPS = ["statusline", "usage", "vision"];
|
|
94
|
+
const ALL_CAPS = ["statusline", "usage", "vision", "log"];
|
|
90
95
|
const ALL_AGENTS = ["claude", "codex", "opencode"];
|
|
91
96
|
const AGENT_CAPS = {
|
|
92
|
-
claude: ["statusline", "usage", "vision"],
|
|
93
|
-
codex: ["usage", "vision"],
|
|
94
|
-
opencode: ["usage", "vision"],
|
|
97
|
+
claude: ["statusline", "usage", "vision", "log"],
|
|
98
|
+
codex: ["usage", "vision", "log"],
|
|
99
|
+
opencode: ["usage", "vision", "log"],
|
|
95
100
|
};
|
|
96
101
|
const VISION_MCP_NAME = "agent-tools-vision";
|
|
97
102
|
const VISION_SKILL_NAME = "at-vision";
|
|
@@ -233,6 +238,13 @@ function installRuntimeAssets(opts) {
|
|
|
233
238
|
addFile(SOURCE.usageScript, RUNTIME.usageScript);
|
|
234
239
|
addFile(SOURCE.config, RUNTIME.config, { mergeJsonc: true });
|
|
235
240
|
}
|
|
241
|
+
if (wants(opts, "log")) {
|
|
242
|
+
addFile(SOURCE.logHook, RUNTIME.logHook);
|
|
243
|
+
if (opts.agents.includes("opencode")) {
|
|
244
|
+
addFile(SOURCE.logOpencodePlugin, RUNTIME.logOpencodePlugin);
|
|
245
|
+
}
|
|
246
|
+
addFile(SOURCE.config, RUNTIME.config, { mergeJsonc: true });
|
|
247
|
+
}
|
|
236
248
|
if (wants(opts, "usage")) {
|
|
237
249
|
if (opts.agents.includes("codex")) {
|
|
238
250
|
addFile(SOURCE.codexUsageHook, RUNTIME.codexUsageHook);
|
|
@@ -446,6 +458,74 @@ function applyProviderUsage(cfg, { remove }) {
|
|
|
446
458
|
if (Object.keys(cfg.hooks).length === 0) delete cfg.hooks;
|
|
447
459
|
}
|
|
448
460
|
|
|
461
|
+
// ---- Log capability: agent hooks running dist/log/hook.mjs, identified by
|
|
462
|
+
// command signature (no meta key, mirroring the codex usage hooks). ----
|
|
463
|
+
|
|
464
|
+
const LOG_EVENTS = [
|
|
465
|
+
["UserPromptSubmit", ""],
|
|
466
|
+
["PreToolUse", "Write|Edit|MultiEdit"],
|
|
467
|
+
["PostToolUse", "Write|Edit|MultiEdit|Bash"],
|
|
468
|
+
["Stop", ""],
|
|
469
|
+
];
|
|
470
|
+
|
|
471
|
+
// Exact match against the command this installer writes: anything else, even a
|
|
472
|
+
// user's own .../dist/log/hook.mjs, is not ours and must survive uninstall.
|
|
473
|
+
function isOurLogEntry(entry) {
|
|
474
|
+
const expected = nodeCmd(RUNTIME.logHook);
|
|
475
|
+
return (
|
|
476
|
+
entry &&
|
|
477
|
+
Array.isArray(entry.hooks) &&
|
|
478
|
+
entry.hooks.some((h) => h?.command === expected)
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Claude filters tool events via matchers; Codex hooks.json has no matcher
|
|
483
|
+
// concept, so there the hook filters by tool name itself.
|
|
484
|
+
function applyLogHooks(cfg, { remove, matchers }) {
|
|
485
|
+
cfg.hooks = cfg.hooks || {};
|
|
486
|
+
for (const [event, matcher] of LOG_EVENTS) {
|
|
487
|
+
const entries = (cfg.hooks[event] || []).filter((entry) => !isOurLogEntry(entry));
|
|
488
|
+
if (!remove) {
|
|
489
|
+
const entry = {
|
|
490
|
+
hooks: [{ type: "command", command: nodeCmd(RUNTIME.logHook), timeout: 30 }],
|
|
491
|
+
};
|
|
492
|
+
if (matchers && matcher) entry.matcher = matcher;
|
|
493
|
+
entries.push(entry);
|
|
494
|
+
}
|
|
495
|
+
if (entries.length > 0) cfg.hooks[event] = entries;
|
|
496
|
+
else delete cfg.hooks[event];
|
|
497
|
+
}
|
|
498
|
+
if (Object.keys(cfg.hooks).length === 0) delete cfg.hooks;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function runClaudeLog(opts) {
|
|
502
|
+
const settings = opts.settings || path.join(os.homedir(), ".claude", "settings.json");
|
|
503
|
+
console.log(`claude log: ${settings}`);
|
|
504
|
+
const cfg = readJson(settings);
|
|
505
|
+
applyLogHooks(cfg, { remove: opts.uninstall, matchers: true });
|
|
506
|
+
writeJson(settings, cfg, opts.dryRun);
|
|
507
|
+
console.log(opts.uninstall ? " - log" : " + log (session work log hooks)");
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function runCodexLog(opts) {
|
|
511
|
+
const file = opts.codexHooks || path.join(os.homedir(), ".codex", "hooks.json");
|
|
512
|
+
console.log(`codex log: ${file}`);
|
|
513
|
+
const cfg = readJson(file);
|
|
514
|
+
applyLogHooks(cfg, { remove: opts.uninstall, matchers: false });
|
|
515
|
+
if (opts.uninstall && Object.keys(cfg).length === 0 && fs.existsSync(file)) {
|
|
516
|
+
removeFile(file, opts.dryRun);
|
|
517
|
+
} else {
|
|
518
|
+
writeJson(file, cfg, opts.dryRun);
|
|
519
|
+
}
|
|
520
|
+
console.log(opts.uninstall ? " - log" : " + log (session work log hooks)");
|
|
521
|
+
if (!opts.uninstall && !opts.dryRun) {
|
|
522
|
+
console.log(
|
|
523
|
+
" NOTE: Codex will not run this hook until you trust it — run `/hooks` " +
|
|
524
|
+
"inside Codex and approve the agent-tools hooks."
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
449
529
|
// ---- Vision capability helpers. ----
|
|
450
530
|
|
|
451
531
|
// The installed tree mirrors the package tree, so the bundled runtime lands in
|
|
@@ -828,6 +908,7 @@ function runClaudeVision(opts) {
|
|
|
828
908
|
|
|
829
909
|
function runClaude(opts) {
|
|
830
910
|
if (wants(opts, "vision")) runClaudeVision(opts);
|
|
911
|
+
if (wants(opts, "log")) runClaudeLog(opts);
|
|
831
912
|
if (wants(opts, "usage")) {
|
|
832
913
|
const skillsDir = opts.claudeSkillsDir || path.join(os.homedir(), ".claude", "skills");
|
|
833
914
|
console.log(`claude usage: ${skillsDir}`);
|
|
@@ -891,6 +972,7 @@ function runCodexVision(opts) {
|
|
|
891
972
|
|
|
892
973
|
function runCodex(opts) {
|
|
893
974
|
if (wants(opts, "vision")) runCodexVision(opts);
|
|
975
|
+
if (wants(opts, "log")) runCodexLog(opts);
|
|
894
976
|
if (!wants(opts, "usage")) {
|
|
895
977
|
return;
|
|
896
978
|
}
|
|
@@ -933,6 +1015,26 @@ function runCodex(opts) {
|
|
|
933
1015
|
// usage after a session goes idle. A TUI plugin displays the shared snapshot. ----
|
|
934
1016
|
|
|
935
1017
|
const OPENCODE_STUB_NAME = "agent-tools-usage.js";
|
|
1018
|
+
const OPENCODE_LOG_STUB_NAME = "agent-tools-log.js";
|
|
1019
|
+
|
|
1020
|
+
function runOpencodeLog(opts) {
|
|
1021
|
+
const configDir = opencodeConfigDir(opts);
|
|
1022
|
+
const stub = path.join(configDir, "plugins", OPENCODE_LOG_STUB_NAME);
|
|
1023
|
+
console.log(`opencode log: ${stub}`);
|
|
1024
|
+
if (opts.uninstall) {
|
|
1025
|
+
if (fs.existsSync(stub)) removeFile(stub, opts.dryRun);
|
|
1026
|
+
else console.log(" no agent-tools log plugin found; nothing to remove.");
|
|
1027
|
+
console.log(" - log");
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
const target = pathToFileURL(RUNTIME.logOpencodePlugin).href;
|
|
1031
|
+
const contents =
|
|
1032
|
+
"// Generated by agent-tools installer; do not edit.\n" +
|
|
1033
|
+
`export { AgentToolsLog } from ${JSON.stringify(target)};\n`;
|
|
1034
|
+
writeText(stub, contents, opts.dryRun);
|
|
1035
|
+
console.log(" + log (session work log plugin)");
|
|
1036
|
+
if (!opts.dryRun) console.log(" NOTE: restart opencode to load the agent-tools log plugin.");
|
|
1037
|
+
}
|
|
936
1038
|
|
|
937
1039
|
function opencodeConfigDir(opts) {
|
|
938
1040
|
return (
|
|
@@ -993,6 +1095,7 @@ function runOpencodeVision(opts) {
|
|
|
993
1095
|
|
|
994
1096
|
function runOpencode(opts) {
|
|
995
1097
|
if (wants(opts, "vision")) runOpencodeVision(opts);
|
|
1098
|
+
if (wants(opts, "log")) runOpencodeLog(opts);
|
|
996
1099
|
if (!wants(opts, "usage")) {
|
|
997
1100
|
return;
|
|
998
1101
|
}
|
|
@@ -54,6 +54,15 @@ their dates are unverifiable, and a project left dirty for weeks would reappear
|
|
|
54
54
|
progress every day. User-provided non-code work must remain clearly identified as user
|
|
55
55
|
context.
|
|
56
56
|
|
|
57
|
+
`~/.agent-tools/config.jsonc` may also carry `log.output`, an automatically recorded
|
|
58
|
+
AI session log, separate from `dailyLog.output`. It is a markdown file with dated
|
|
59
|
+
entries, or a directory holding one `<date>.md` report per day; entries under
|
|
60
|
+
`log.projects` may route their sessions to their own `output`, so check those paths
|
|
61
|
+
too. Read the day's content as supplementary evidence, as it captures work that
|
|
62
|
+
produced no commits, such as troubleshooting or research sessions. Merge, do not
|
|
63
|
+
duplicate, work already backed by commits; when the key or the day's content is
|
|
64
|
+
absent, skip this entirely.
|
|
65
|
+
|
|
57
66
|
## Output
|
|
58
67
|
|
|
59
68
|
Match the user's language:
|
|
@@ -85,7 +94,7 @@ write request, writes after previewing the entry. Resolve the destination in ord
|
|
|
85
94
|
3. no destination: return the draft without writing.
|
|
86
95
|
|
|
87
96
|
Read the destination before editing. Wrap each date's generated content in that date's
|
|
88
|
-
own markers, `<!-- log:2026-07-31:start 5,a1b2c3d -->` / `<!-- log:2026-07-31:end -->`,
|
|
97
|
+
own markers, `<!-- daily-log:2026-07-31:start 5,a1b2c3d -->` / `<!-- daily-log:2026-07-31:end -->`,
|
|
89
98
|
where the start marker stores the day's commit count and newest commit hash across the
|
|
90
99
|
scanned projects; the date line and every line outside the markers belong to the user.
|
|
91
100
|
Refresh an existing block when either value changed or the user explicitly asks;
|
|
@@ -96,9 +105,9 @@ user's lines instead of editing them. Do not duplicate the date. Insert a new da
|
|
|
96
105
|
among the existing dated entries at its date-order position, inferring ascending or
|
|
97
106
|
descending from the dates already present (ascending when that is ambiguous); content
|
|
98
107
|
above or below the dated entries, such as notes or todo lists, stays where it is. On duplicate or
|
|
99
|
-
unpaired markers, stop and propose the edit instead of writing. When Git
|
|
100
|
-
activity, leave the file unchanged and say so; the
|
|
101
|
-
themselves. Recording a range applies these rules to each day's block independently.
|
|
108
|
+
unpaired markers, stop and propose the edit instead of writing. When neither Git nor
|
|
109
|
+
the session log shows activity for the day, leave the file unchanged and say so; the
|
|
110
|
+
user can add a manual entry themselves. Recording a range applies these rules to each day's block independently.
|
|
102
111
|
|
|
103
112
|
Scheduling is separate; set it up only when the user asks. Prefer the OS scheduler
|
|
104
113
|
(Task Scheduler, cron, launchd) over agent-internal timers, which stop with the agent:
|
|
@@ -57,8 +57,13 @@ Git jargon. Do not show commit counts or a generic source line.
|
|
|
57
57
|
## Optional logs
|
|
58
58
|
|
|
59
59
|
A pasted daily/weekly log or explicit file path provides business context. If neither is
|
|
60
|
-
supplied, optionally read `dailyLog.output`
|
|
61
|
-
|
|
60
|
+
supplied, optionally read `dailyLog.output` and `log.output` (an automatically recorded
|
|
61
|
+
AI session log; a dated markdown file, or a directory of per-day `<date>.md` reports,
|
|
62
|
+
of which read the dates inside the window; entries under `log.projects` may add their
|
|
63
|
+
own `output` paths, check those too) from `~/.agent-tools/config.jsonc`; the
|
|
64
|
+
latter also captures work that never produced commits,
|
|
65
|
+
such as troubleshooting or research sessions. Explicit conversation input always wins;
|
|
66
|
+
a missing key or file is non-fatal.
|
|
62
67
|
|
|
63
68
|
Commit-backed items may be summarized directly. Keep log-only work separate for user
|
|
64
69
|
confirmation; include it only after confirmation, using only dates stated in the log or
|