@kairyou/agent-tools 0.11.0 → 0.13.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.
@@ -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.11.0",
3
+ "version": "0.13.0",
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
@@ -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 (standalone hooks file)
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/ (server + TUI plugins,
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
  }
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: at-daily-log
3
- description: "Summarize one day's Git activity into a concise daily work log. Uses the current repository, optional configured work projects, or paths named in conversation; configuration is never required."
4
- argument-hint: "[<date>]"
3
+ description: "Summarize each day's Git activity into a concise daily work log, for a single date or a range. Uses the current repository, optional configured work projects, or paths named in conversation; configuration is never required."
4
+ argument-hint: "[<date>|<range>]"
5
5
  ---
6
6
 
7
7
  # Daily Work Log
@@ -10,10 +10,13 @@ argument-hint: "[<date>]"
10
10
 
11
11
  ## Date and evidence scope
12
12
 
13
- Default to today. Accept plain-language dates or `YYYY-MM-DD`; query through the next
14
- day because Git's `--until` boundary is exclusive. State the resolved date.
13
+ Default to today. Accept plain-language dates or ranges (`2026-07-31`, yesterday, last
14
+ week, this month) and normalize to `[from, to]`; query through `<to + 1 day>` because
15
+ Git's `--until` boundary is exclusive. State the resolved date or range.
15
16
 
16
- Read optional `workProjects` from `~/.agent-tools/config.jsonc`.
17
+ Read optional `workProjects` from `~/.agent-tools/config.jsonc`. An entry is a path
18
+ or `{ "path", "prompt" }`, where the prompt is free text this skill follows for that
19
+ project, such as how to label items or which commits to skip.
17
20
 
18
21
  - No project named: current Git repository plus configured projects.
19
22
  - Projects named directly: only those projects.
@@ -36,35 +39,50 @@ by hash.
36
39
  Collect non-merge commits for the resolved author and day. Use subjects and changed
37
40
  paths to turn related commits into concrete completed work items. Fold formatting,
38
41
  version bumps, and follow-up fixes into the outcome they supported; do not inflate one
39
- change into several deliverables.
42
+ change into several deliverables. Never restate a
43
+ commit subject as the work item; describe the outcome it produced. A day left with
44
+ nothing has no reportable activity.
40
45
 
41
46
  Use file counts and added/deleted lines only when they meaningfully support the work
42
47
  item. Generated files, lockfile churn, renames, and bulk formatting often make those
43
48
  numbers misleading. Metrics are evidence, never hours, difficulty, impact, or a
44
49
  productivity score.
45
50
 
46
- Current uncommitted changes may be shown separately as **in progress**, but never as
47
- proof of work on a past date. User-provided non-code work must remain clearly identified
48
- as user context.
51
+ Uncommitted changes are not work items. At most, note them factually in a chat draft
52
+ for today (project and files, no invented progress); never write them to the file:
53
+ their dates are unverifiable, and a project left dirty for weeks would reappear as in
54
+ progress every day. User-provided non-code work must remain clearly identified as user
55
+ context.
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.
49
65
 
50
66
  ## Output
51
67
 
52
- Match the user's language and omit empty sections:
68
+ Match the user's language:
53
69
 
54
70
  ```markdown
55
71
  + 2026-07-31
56
- 完成 2 项:
57
72
  1. agent-tools: 完成多项目日报规则和 skill 落地.
58
73
  2. vscode-plugin: 修复 Webview 刷新后状态丢失问题, 补充回归验证.
74
+ ```
59
75
 
60
- 进行中 1 项:
61
- - iunit-web: 推进导入流程重构, 已完成数据解析.
76
+ Start each item with the label that best locates the work for a reader: the project
77
+ name when the day spans projects, a module or feature within a single one.
62
78
 
63
- 汇总: 3 个项目, 2 项完成, 1 项进行中.
64
- ```
79
+ Keep entries compact: the file accumulates for months, so every recurring line must
80
+ earn its place. Do not add per-day summary, total, or section-header lines; the
81
+ numbered items already show project and count.
65
82
 
66
83
  Prefer outcomes over raw Git metrics. If no verified activity exists, say so rather
67
- than fabricate an entry.
84
+ than fabricate an entry. For a range, output one entry per day with activity, oldest
85
+ first, and skip empty days.
68
86
 
69
87
  ## Draft or record
70
88
 
@@ -76,24 +94,33 @@ write request, writes after previewing the entry. Resolve the destination in ord
76
94
  3. no destination: return the draft without writing.
77
95
 
78
96
  Read the destination before editing. Wrap each date's generated content in that date's
79
- own markers, `<!-- log:2026-07-31:start -->` / `<!-- log:2026-07-31:end -->`; the date
80
- line and every line outside the markers belong to the user. For an existing date, show
97
+ own markers, `<!-- daily-log:2026-07-31:start 5,a1b2c3d -->` / `<!-- daily-log:2026-07-31:end -->`,
98
+ where the start marker stores the day's commit count and newest commit hash across the
99
+ scanned projects; the date line and every line outside the markers belong to the user.
100
+ Refresh an existing block when either value changed or the user explicitly asks;
101
+ otherwise leave it alone, because regenerated wording varies between runs. For an existing date, show
81
102
  the current block and the regenerated one before writing, then replace only that
82
103
  date's block. If the date exists without markers, append a marked block below the
83
- user's lines instead of editing them. Do not duplicate the date. On duplicate or
84
- unpaired markers, stop and propose the edit instead of writing. When Git shows no
85
- activity, leave the file unchanged and say so; the user can add a manual entry
86
- themselves.
104
+ user's lines instead of editing them. Do not duplicate the date. Insert a new date
105
+ among the existing dated entries at its date-order position, inferring ascending or
106
+ descending from the dates already present (ascending when that is ambiguous); content
107
+ above or below the dated entries, such as notes or todo lists, stays where it is. On duplicate or
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.
87
111
 
88
112
  Scheduling is separate; set it up only when the user asks. Prefer the OS scheduler
89
113
  (Task Scheduler, cron, launchd) over agent-internal timers, which stop with the agent:
90
114
  schedule a headless run of the CLI this skill is executing in, invoking the skill with
91
- a recording request (in Claude Code, `claude -p "/at-daily-log record the log"`; other
92
- CLIs have their own headless form). Keep projects and output in config so the command
93
- stays stable. Before registering, confirm the schedule (suggest workdays),
94
- make sure the output file is resolvable, and run the exact command once; register only
95
- after that test run records correctly, and show how to remove the task. Headless auth
115
+ the recording request the user asked for (in Claude Code,
116
+ `claude -p "/at-daily-log record the log"`; other CLIs have their own headless form).
117
+ Keep projects and output in config so the command stays stable. Before registering,
118
+ confirm the schedule, make sure the output file is resolvable, and run the exact
119
+ command once; register only after that test run records correctly, and show how to
120
+ remove the task. Headless auth
96
121
  differs from the interactive session, so a failed test run means stop instead of
97
122
  registering, and show the exact command, its error output, and the likely fix (log in
98
- for headless use, adjust the output path). An unattended run has nobody to confirm with, so it must follow the
99
- marker rules exactly and skip any file it cannot edit that way.
123
+ for headless use, adjust the output path). An unattended run has nobody to confirm
124
+ with, so it must follow the marker rules exactly and skip any file it cannot edit that
125
+ way: it fills missing days and refreshes days whose stored count or hash moved,
126
+ nothing else.
@@ -33,7 +33,9 @@ user can provide other author names if needed.
33
33
 
34
34
  ## Project evidence
35
35
 
36
- Read optional `workProjects` from `~/.agent-tools/config.jsonc`.
36
+ Read optional `workProjects` from `~/.agent-tools/config.jsonc`. An entry is a path
37
+ or `{ "path", "prompt" }`, where the prompt is free text this skill follows for that
38
+ project, such as how to label items or which commits to skip.
37
39
 
38
40
  - No project named: current Git repository plus configured projects.
39
41
  - Projects named directly: only those projects.
@@ -55,8 +57,13 @@ Git jargon. Do not show commit counts or a generic source line.
55
57
  ## Optional logs
56
58
 
57
59
  A pasted daily/weekly log or explicit file path provides business context. If neither is
58
- supplied, optionally read `dailyLog.output` from config. Explicit conversation input
59
- always wins; a missing log is non-fatal.
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.
60
67
 
61
68
  Commit-backed items may be summarized directly. Keep log-only work separate for user
62
69
  confirmation; include it only after confirmation, using only dates stated in the log or