imdone-cli 0.76.0 → 0.77.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.
Files changed (27) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/.claude/plugins/imdone/README.md +1 -1
  3. package/dist/.claude/plugins/imdone/scripts/ai_adjustment_note_hook.mjs +89 -5
  4. package/dist/.claude/plugins/imdone/scripts/record_ai_adjustment_note.mjs +49 -1
  5. package/dist/.claude/skills/hypothesis-driven-development/SKILL.md +22 -11
  6. package/dist/.claude/skills/hypothesis-driven-development/references/interaction-contract.md +11 -10
  7. package/dist/.claude/skills/hypothesis-driven-development/references/prove-the-outcome.md +5 -5
  8. package/dist/.claude/skills/hypothesis-driven-development/references/session-setup.md +21 -10
  9. package/dist/.claude/skills/hypothesis-driven-development/scripts/evaluate_progress_notes_contract.mjs +6 -3
  10. package/dist/.codex/plugins/imdone/README.md +1 -1
  11. package/dist/.codex/plugins/imdone/scripts/ai_adjustment_note_hook.mjs +89 -5
  12. package/dist/.codex/plugins/imdone/scripts/record_ai_adjustment_note.mjs +49 -1
  13. package/dist/.codex/skills/hypothesis-driven-development/SKILL.md +22 -11
  14. package/dist/.codex/skills/hypothesis-driven-development/references/interaction-contract.md +11 -10
  15. package/dist/.codex/skills/hypothesis-driven-development/references/prove-the-outcome.md +5 -5
  16. package/dist/.codex/skills/hypothesis-driven-development/references/session-setup.md +21 -10
  17. package/dist/.codex/skills/hypothesis-driven-development/scripts/evaluate_progress_notes_contract.mjs +6 -3
  18. package/dist/CHANGELOG.md +4 -0
  19. package/dist/index.cjs +5 -4
  20. package/dist/index.cjs.map +2 -2
  21. package/dist/index.min.cjs +2 -2
  22. package/dist/index.min.cjs.map +2 -2
  23. package/dist/preinstall.cjs +1 -1
  24. package/dist/preinstall.cjs.map +1 -1
  25. package/dist/preinstall.min.cjs +1 -1
  26. package/dist/preinstall.min.cjs.map +1 -1
  27. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog 📝
2
2
 
3
+ ## 0.77.0
4
+
5
+ - Let bundled Codex and Claude `imdone` plugins record first-prompt context as local progress notes on the configured active story when a session starts inside the same software project, while later prompts use correction/context judgement and explicit issue/file targets still override the default
6
+
3
7
  ## 0.76.0
4
8
 
5
9
  - Tell users how to recover when config-dependent commands run outside an initialized imdone project or when the configured backlog checkout is missing, while keeping setup-free commands such as `docs`, `help`, `init`, `clone`, `license`, and `version` available before `.imdone-cli.yml` exists
@@ -2,4 +2,4 @@
2
2
 
3
3
  This plugin registers the imdone AI correction note hook with Claude-compatible plugin runtimes.
4
4
 
5
- The hook listens for developer prompts that disagree with AI output, ask for an adjustment, add missing context, or report an AI workflow problem such as hooks/plugins not loading. Matching prompts delegate to the bundled `ai-adjustment-note` writer so notes go to the active HDD story when one is configured, or report `needs-target` when the user must choose an issue or file.
5
+ The hook always captures the first prompt in a session when it can identify a new session inside an imdone-enabled software project. Later prompts use judgement: developer disagreement, requested adjustments, added context, or AI workflow problems delegate to the bundled `ai-adjustment-note` writer so notes go to the configured active story when one is configured, or report `needs-target` when the user must choose an issue or file.
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync } from 'node:fs';
2
+ import { createHash } from 'node:crypto';
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
3
5
  import { spawnSync } from 'node:child_process';
4
6
  import path from 'node:path';
5
7
  import { fileURLToPath } from 'node:url';
@@ -11,6 +13,8 @@ const DISAGREEMENT_PATTERN = /\b(no|nope|not quite|wrong|incorrect|that'?s not|t
11
13
  const CONTEXT_PATTERN = /\b(context|more context|additional context|to clarify|clarification|for context|the important part|the point is|the intent is|this is meant to|meant to)\b/i;
12
14
  const PROBLEM_SIGNAL_PATTERN = /\b(doesn'?t look like|does not look like|didn'?t|did not|doesn'?t|does not|isn'?t|is not|wasn'?t|was not|won'?t|will not|can'?t|cannot|couldn'?t|could not|failed|fails|failing|failure|not working|isn'?t working|broken|not installed|not loaded|not enabled|not showing|not listed|didn'?t record|did not record|didn'?t capture|did not capture|didn'?t run|did not run|didn'?t trigger|did not trigger|why didn'?t|why did not|should have recorded|should have captured|expected it to)\b/i;
13
15
  const AI_WORKFLOW_PATTERN = /\b(ai|agent|codex|claude|hook|plugin|prompt|output|response|capture|record|note|hdd|imdone|runtime|marketplace|install|installed|enabled|loaded)\b/i;
16
+ const FIRST_PROMPT_KEYS = ['isFirstPrompt', 'firstPrompt', 'is_first_prompt', 'first_prompt'];
17
+ const SESSION_ID_KEYS = ['session_id', 'sessionId', 'conversation_id', 'conversationId', 'thread_id', 'threadId', 'chat_id', 'chatId'];
14
18
 
15
19
  function readStdin() {
16
20
  return new Promise(resolve => {
@@ -61,7 +65,85 @@ function extractPrompt(payload) {
61
65
  .trim();
62
66
  }
63
67
 
64
- function classifyCapture(prompt) {
68
+ function collectValueByKeys(value, keys) {
69
+ if (!value || typeof value !== 'object') return null;
70
+
71
+ for (const key of keys) {
72
+ if (Object.prototype.hasOwnProperty.call(value, key)) return value[key];
73
+ }
74
+
75
+ if (Array.isArray(value)) {
76
+ for (const item of value) {
77
+ const found = collectValueByKeys(item, keys);
78
+ if (found !== null && found !== undefined) return found;
79
+ }
80
+ return null;
81
+ }
82
+
83
+ for (const item of Object.values(value)) {
84
+ const found = collectValueByKeys(item, keys);
85
+ if (found !== null && found !== undefined) return found;
86
+ }
87
+ return null;
88
+ }
89
+
90
+ function explicitFirstPrompt(payload) {
91
+ const value = collectValueByKeys(payload, FIRST_PROMPT_KEYS);
92
+ if (typeof value === 'boolean') return value;
93
+ if (typeof value === 'string' && /^(true|false)$/i.test(value.trim())) return value.trim().toLowerCase() === 'true';
94
+ return null;
95
+ }
96
+
97
+ function extractSessionId(payload) {
98
+ const value = collectValueByKeys(payload, SESSION_ID_KEYS);
99
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
100
+ }
101
+
102
+ function sessionStatePath(repoRoot) {
103
+ const projectHash = createHash('sha256').update(repoRoot).digest('hex').slice(0, 16);
104
+ return path.join(tmpdir(), 'imdone-ai-adjustment-note', `${projectHash}.json`);
105
+ }
106
+
107
+ function readSeenSessions(repoRoot) {
108
+ try {
109
+ const parsed = JSON.parse(readFileSync(sessionStatePath(repoRoot), 'utf8'));
110
+ return parsed && typeof parsed === 'object' && parsed.sessions && typeof parsed.sessions === 'object'
111
+ ? parsed.sessions
112
+ : {};
113
+ } catch {
114
+ return {};
115
+ }
116
+ }
117
+
118
+ function hasSeenSession(repoRoot, sessionId) {
119
+ return Boolean(readSeenSessions(repoRoot)[sessionId]);
120
+ }
121
+
122
+ function markSessionSeen(repoRoot, sessionId) {
123
+ if (!sessionId) return;
124
+ const stateFile = sessionStatePath(repoRoot);
125
+ const sessions = readSeenSessions(repoRoot);
126
+ sessions[sessionId] = new Date().toISOString();
127
+ mkdirSync(path.dirname(stateFile), { recursive: true });
128
+ writeFileSync(stateFile, JSON.stringify({ sessions }, null, 2), 'utf8');
129
+ }
130
+
131
+ function isFirstPrompt(payload, repoRoot) {
132
+ const explicit = explicitFirstPrompt(payload);
133
+ if (explicit !== null) return explicit;
134
+ const sessionId = extractSessionId(payload);
135
+ return Boolean(sessionId && !hasSeenSession(repoRoot, sessionId));
136
+ }
137
+
138
+ function classifyCapture(prompt, { firstPrompt = false } = {}) {
139
+ if (firstPrompt) {
140
+ return {
141
+ title: 'AI first prompt captured',
142
+ change: 'Developer started a prompt in an imdone-enabled software project.',
143
+ reason: 'Capturing the first prompt preserves plate-spun session context before follow-up judgement applies.'
144
+ };
145
+ }
146
+
65
147
  if (DISAGREEMENT_PATTERN.test(prompt) || CONTEXT_PATTERN.test(prompt) || (PROBLEM_SIGNAL_PATTERN.test(prompt) && AI_WORKFLOW_PATTERN.test(prompt))) {
66
148
  return {
67
149
  title: 'AI correction captured',
@@ -133,12 +215,14 @@ function runWriter({ writer, repoRoot, request, capture }) {
133
215
  async function main() {
134
216
  const payload = parsePayload(await readStdin());
135
217
  const prompt = extractPrompt(payload);
136
- const capture = prompt ? classifyCapture(prompt) : null;
137
- if (!capture) return;
138
-
139
218
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
140
219
  const startDir = firstString(payload.cwd, payload.projectRoot, payload.workspace, process.env.PWD, process.cwd());
141
220
  const repoRoot = findRepoRoot(startDir);
221
+ const sessionId = extractSessionId(payload);
222
+ const capture = prompt ? classifyCapture(prompt, { firstPrompt: isFirstPrompt(payload, repoRoot) }) : null;
223
+ if (sessionId) markSessionSeen(repoRoot, sessionId);
224
+ if (!capture) return;
225
+
142
226
  const writer = findWriter(repoRoot, scriptDir);
143
227
  if (!writer) {
144
228
  process.stderr.write('imdone ai-adjustment-note writer was not found; skipping note capture.\n');
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import path from 'node:path';
3
+ import { appendFile, mkdir } from 'node:fs/promises';
3
4
  import { execFileSync, spawnSync } from 'node:child_process';
4
5
 
5
6
  function parseArgs(argv) {
@@ -67,6 +68,44 @@ function runImdone(args, cwd) {
67
68
  });
68
69
  }
69
70
 
71
+ function getGitAuthor(cwd) {
72
+ const readConfig = (key) => {
73
+ try {
74
+ return execFileSync('git', ['-C', cwd, 'config', '--get', key], { encoding: 'utf8' }).trim();
75
+ } catch {
76
+ return '';
77
+ }
78
+ };
79
+ const name = readConfig('user.name');
80
+ const email = readConfig('user.email');
81
+ if (name && email) return `${name} <${email}>`;
82
+ if (name) return name;
83
+ if (email) return `<${email}>`;
84
+ return 'Unknown user';
85
+ }
86
+
87
+ function formatIsoWithOffset(date) {
88
+ const pad = (value, size = 2) => String(value).padStart(size, '0');
89
+ const offsetMinutes = -date.getTimezoneOffset();
90
+ const sign = offsetMinutes >= 0 ? '+' : '-';
91
+ const absoluteOffset = Math.abs(offsetMinutes);
92
+ return [
93
+ `${pad(date.getFullYear(), 4)}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`,
94
+ 'T',
95
+ `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`,
96
+ `.${pad(date.getMilliseconds(), 3)}`,
97
+ `${sign}${pad(Math.floor(absoluteOffset / 60))}:${pad(absoluteOffset % 60)}`
98
+ ].join('');
99
+ }
100
+
101
+ async function appendFileNote({ filePath, note, cwd }) {
102
+ const resolvedPath = path.resolve(cwd, filePath);
103
+ await mkdir(path.dirname(resolvedPath), { recursive: true });
104
+ const entry = `\n## ${formatIsoWithOffset(new Date())} | ${getGitAuthor(cwd)}\n\n${note}\n`;
105
+ await appendFile(resolvedPath, entry, 'utf8');
106
+ return resolvedPath;
107
+ }
108
+
70
109
  function getActiveStory(cwd) {
71
110
  const result = runImdone(['agent-config', 'get-active-story'], cwd);
72
111
  if (result.status !== 0) return null;
@@ -109,6 +148,15 @@ async function main() {
109
148
  return;
110
149
  }
111
150
 
151
+ if (options.file) {
152
+ const filePath = await appendFileNote({ filePath: options.file, note, cwd });
153
+ process.stdout.write(JSON.stringify({
154
+ status: 'recorded',
155
+ target: { type: 'file', filePath }
156
+ }, null, 2) + '\n');
157
+ return;
158
+ }
159
+
112
160
  const issueKey = getActiveStory(cwd);
113
161
 
114
162
  if (issueKey) {
@@ -127,7 +175,7 @@ async function main() {
127
175
 
128
176
  process.stdout.write(JSON.stringify({
129
177
  status: 'needs-target',
130
- message: 'HDD is not active. Ask the user for an imdone issue key before recording.'
178
+ message: 'No active story is configured. Ask the user for an imdone issue key before recording.'
131
179
  }, null, 2) + '\n');
132
180
  }
133
181
 
@@ -5,6 +5,17 @@ description: Guide a story through hypothesis-driven development from user-confi
5
5
 
6
6
  # Hypothesis-Driven Development
7
7
 
8
+ ## Tool Availability Boundary
9
+
10
+ Use imdone only after deterministic command checks:
11
+ - run `imdone --version`; if it exits 0, the `imdone` command is installed
12
+ - run `imdone agent-config get-config`; if it exits 0 with `status: "ok"`, the current workspace is an imdone project for HDD session state and push reminder settings
13
+ - try `imdone note <issueKey> "<note>"` for timestamped progress notes; use the direct Markdown fallback only if `imdone note` is unavailable or exits non-zero for the current workspace
14
+ - use `imdone-template` when a local issue needs the HDD template
15
+ - use `imdone pull`, `imdone push`, and `imdone status` for provider sync when the user chooses to sync
16
+
17
+ Do not depend on imdone. If the deterministic checks fail, continue the HDD workflow with plain Markdown files in the user's workspace. Store story state in the issue/story file and attachments when they exist, append progress notes directly to `attachments/progress-notes.md` with an ISO timestamp and author when possible only if `imdone note` is unavailable or fails, skip imdone push reminders, and describe sync/publish steps as external evidence gates instead of blocking local HDD work.
18
+
8
19
  ## Operating Model
9
20
 
10
21
  Guide a story through three outcome-focused phases:
@@ -23,7 +34,7 @@ Purpose:
23
34
  - require top-to-bottom implementation: build and execute the red step first, evaluate each markdown task as done before checking it, and do not start the next task while the preceding executable task is unchecked
24
35
  - once the plan is accepted, keep implementing until the whole plan is done; do not stop between planned tasks just to ask whether to continue, summarize status, or re-confirm the already-approved direction
25
36
  - capture implementation feedback in story artifacts so humans and AI can regain context after interruptions
26
- - make `attachments/progress-notes.md` the default place to record pivots and corrections through `imdone note`, using `attachments/plan.md` only for the execution plan and task checklist
37
+ - make `attachments/progress-notes.md` the default place to record pivots and corrections, trying `imdone note` first and appending directly to the Markdown file only if `imdone note` is unavailable or fails, using `attachments/plan.md` only for the execution plan and task checklist
27
38
  - treat `attachments/demo.md` as a single evolving proof artifact: define it early, then revise it during implementation when the real behavior, commands, or visible outcomes change
28
39
 
29
40
  Non-goals:
@@ -37,8 +48,8 @@ Before doing workflow work:
37
48
 
38
49
  1. Read `references/session-setup.md`.
39
50
  2. Resolve the story and load only the current story plus its relevant attachments.
40
- 3. Read HDD skill state through `imdone agent-config get-config`. If `push.status` is `missing`, prompt the user for push reminder preferences using the short setup in `references/configuration.md`, then persist the answer with `imdone agent-config set-push-config` before continuing. After push config exists, call `imdone agent-config begin-session` so interval timing starts from the current HDD session. For prompt-reminder config, push timing state, and `active_story` reads and writes, always use `imdone agent-config` instead of local helper scripts or direct YAML edits.
41
- 4. If the story is not already using the HDD template, use the `imdone-template` skill first.
51
+ 3. Run the deterministic imdone checks in `references/session-setup.md`. If `imdone agent-config get-config` exits 0 with `status: "ok"`, read HDD skill state from that result. If `push.status` is `missing`, prompt the user for push reminder preferences using the short setup in `references/configuration.md`, then persist the answer with `imdone agent-config set-push-config` before continuing. After push config exists, call `imdone agent-config begin-session` so interval timing starts from the current HDD session. For prompt-reminder config, push timing state, and `active_story` reads and writes in an imdone project, always use `imdone agent-config` instead of local helper scripts or direct YAML edits. If the deterministic checks fail, skip push reminder setup and keep session state in the current Markdown artifacts.
52
+ 4. If the story is not already using the HDD template, use the `imdone-template` skill first when imdone is available. If imdone is unavailable, repair the Markdown structure directly using the current HDD headings and attachments described in `references/session-setup.md`.
42
53
  5. Make sure the issue file links to every issue attachment that exists so artifacts are easy to find from the story. If the template already includes a link, leave it where it is. Duplicate links are acceptable.
43
54
  6. Read `references/interaction-contract.md` and follow it throughout the session.
44
55
 
@@ -97,7 +108,7 @@ Order:
97
108
  Write locations:
98
109
  - Design: `attachments/design.md` and at least one Mermaid diagram in `attachments/diagram.md`
99
110
  - Plan: `attachments/plan.md` using explicit red/green/refactor sequencing
100
- - Progress notes from feedback, pivots, and discovered constraints: record new notes with `imdone note <issueKey> "<note>"` into `attachments/progress-notes.md`; read legacy `Progress notes:` in `attachments/plan.md` only as backward-compatible context
111
+ - Progress notes from feedback, pivots, and discovered constraints: try `imdone note <issueKey> "<note>"` into `attachments/progress-notes.md` first; append directly to `attachments/progress-notes.md` with an ISO timestamp and author only if `imdone note` is unavailable or exits non-zero for the current workspace. Read legacy `Progress notes:` in `attachments/plan.md` only as backward-compatible context.
101
112
 
102
113
  Load `references/prove-the-outcome.md` only when working this phase.
103
114
  Use it for implementation hygiene guidance, including DRY cleanup in touched flows and code-as-documentation defaults.
@@ -148,23 +159,23 @@ Load only the reference file needed for the current decision:
148
159
  - Safe to re-run after interruption.
149
160
  - Prefer updating current story artifacts over free-floating advice.
150
161
  - Keep the issue file current as an attachment index. When an issue attachment exists or is created, make sure the issue file links to it. Leave existing template links in place, and accept duplicates when adding missing links is simpler than normalizing them.
151
- - Use `imdone agent-config` as the only boundary for HDD config reads and writes, including push reminder settings, push timing state, `active_story`, and saved-key path resolution. Do not parse or rewrite agent config YAML inline, and do not call local skill helper scripts for configuration state.
152
- - Before any push reminder, check `imdone agent-config get-config` and only ask whether to run `imdone push` when `push.imdoneStatus.hasPendingChanges` is true.
162
+ - In imdone projects, use `imdone agent-config` as the only boundary for HDD config reads and writes, including push reminder settings, push timing state, `active_story`, and saved-key path resolution. Do not parse or rewrite agent config YAML inline, and do not call local skill helper scripts for configuration state. Outside imdone projects, skip agent-config behavior and keep state in the current Markdown artifacts.
163
+ - Before any push reminder in an imdone project, check `imdone agent-config get-config` and only ask whether to run `imdone push` when `push.imdoneStatus.hasPendingChanges` is true. If imdone is unavailable, do not ask for `imdone push`; record any publish/sync need as an external evidence gate.
153
164
  - Do not mark Hypothesis complete until `## Problem Framing` exists and the user has provided or confirmed it during the HDD session.
154
165
  - After Hypothesis is complete, ask once whether to continue through the remaining HDD workflow without routine per-artifact interruption. Respect the chosen continuation scope until a blocker, missing decision, external evidence gate, major pivot, push prompt, or user correction requires stopping.
155
166
  - Every HDD user-facing question must include numbered answer choices so the user can reply with a number alone. Use a numbered free-form option when the user may need to correct the framing or provide custom details.
156
- - In `interval_prompt` mode, check push timing through `imdone agent-config get-config` before starting any substantial read, edit, verification, or implementation cycle, not only at phase boundaries. Treat `push.promptDue` as already gated by elapsed interval and pending `imdone status` changes.
157
- - When using this skill, end each user-facing message with a visible indicator showing the active story key, a short story summary, and the HDD skill version from `imdone --version`. Example: `🧭 HDD v0.58.2 | SCRUM-260Share changelog section on prompt upgrade`
167
+ - In `interval_prompt` mode inside an imdone project, check push timing through `imdone agent-config get-config` before starting any substantial read, edit, verification, or implementation cycle, not only at phase boundaries. Treat `push.promptDue` as already gated by elapsed interval and pending `imdone status` changes.
168
+ - When using this skill in an imdone project, end each user-facing message with a visible indicator showing the active story key, a short story summary, and the HDD skill version from `imdone --version`. When imdone is unavailable, use the visible indicator without an imdone version, for example: `🧭 HDD | StoryShort summary`
158
169
  - When progress notes are recorded, explicitly say so in the user-facing message after the story footer, using a visible indicator such as: `📝 Progress note recorded in attachments/progress-notes.md`
159
170
  - Rely on the tool's built-in edit diff. In chat, list changed file paths and summarize the change instead of replaying diffs.
160
171
  - Default recording pattern for pivots and corrections:
161
172
  - update the affected phase steps in `attachments/plan.md`
162
- - record a short progress note with `imdone note <issueKey> "<note>"` for what changed, why it changed, and what must be remembered
163
- - let `imdone note` add the author and full ISO timestamp in `attachments/progress-notes.md`
173
+ - record a short progress note with `imdone note <issueKey> "<note>"` first; append directly to `attachments/progress-notes.md` only if `imdone note` is unavailable or fails, for what changed, why it changed, and what must be remembered
174
+ - let `imdone note` add the author and full ISO timestamp when available; otherwise include the best available author and a full ISO timestamp in the Markdown note
164
175
  - add a new attachment only when the plan is no longer enough to recover the reasoning or boundary cleanly
165
176
  - No supported skill alias field was found in the current local skill format, so keep the canonical skill name `hypothesis-driven-development` unless alias support is added to the skill loader.
166
177
  - If the next honest step is outside the tool boundary, record the unblock note or evidence gate instead of pretending the story can advance locally.
167
- - In `phase_prompt` push mode, prompt for `imdone push` after the first real write to `attachments/plan.md` and again after any progress note is recorded in `attachments/progress-notes.md`, not only at the end of a phase.
178
+ - In `phase_prompt` push mode inside an imdone project, prompt for `imdone push` after the first real write to `attachments/plan.md` and again after any progress note is recorded in `attachments/progress-notes.md`, not only at the end of a phase. If imdone is unavailable, skip push prompts.
168
179
  - If a follow-on step involves creating issues with `imdone add` or the `imdone-add` skill, treat creation as a serialized repo operation. Do not run multiple `imdone add` commands in parallel in the same repo unless a later implementation explicitly proves that concurrent create flows are safe for git state, attachment upload, and local issue refresh.
169
180
  - During Phase 2, do not draft Design, Plan, or Implement artifacts from only the active story. First load the relevant expert reference, then search current backlog stories and the configured archive for similar hypotheses, acceptance criteria, plans, or outcomes. Summarize only the useful prior context; do not bulk-load unrelated archived work.
170
181
  - Before checking Implement complete, run the full project test suite from the plan's final confirmation phase after all focused checks are green. If the full suite fails, fix the failure or record a concrete blocker and leave Implement unchecked.
@@ -23,13 +23,13 @@ Use this throughout the workflow.
23
23
  - Preserve clean dependency direction, but do not invent abstractions early. When a new use case creates a real axis of change, the dependency-rule refactor is part of implementation completion, not optional follow-up work.
24
24
  - Red/green/refactor and top-to-bottom task order are mandatory during implementation. Build and execute red first, evaluate each task before checking it, check it before starting the next task, and stop if the preceding executable task is still unchecked.
25
25
  - Treat `attachments/plan.md` as a live artifact. When implementation changes the path, update the plan immediately.
26
- - When the user provides implementation feedback, decisions, corrections, or newly discovered constraints, update affected plan steps in `attachments/plan.md` when the execution path changes, and record the chronological note with `imdone note <issueKey> "<note>"` instead of leaving that context only in chat.
26
+ - When the user provides implementation feedback, decisions, corrections, or newly discovered constraints, update affected plan steps in `attachments/plan.md` when the execution path changes, and record the chronological note by trying `imdone note <issueKey> "<note>"` first. Append the note directly to `attachments/progress-notes.md` with an ISO timestamp and author only if `imdone note` is unavailable or exits non-zero for the current workspace.
27
27
  - Create additional attachments when warranted for clarity and resumability, for example focused test notes, rollout notes, API notes, review context, or diagrams.
28
28
  - Use this default recording pattern:
29
29
  - update `attachments/plan.md` only when the execution path, tasks, or checklist changes
30
- - call `imdone note <issueKey> "<note>"` for what changed in execution, status, or next step
30
+ - try `imdone note <issueKey> "<note>"` first; append directly to `attachments/progress-notes.md` only if `imdone note` is unavailable or exits non-zero for the current workspace
31
31
  - include why the path changed, what feedback or constraint caused it, and what another human or AI should not have to rediscover
32
- - let `imdone note` write the full ISO timestamp and author into `attachments/progress-notes.md`
32
+ - let `imdone note` write the full ISO timestamp and author into `attachments/progress-notes.md` when available; otherwise write the timestamp and best available author directly
33
33
  - Prefer the plan over a new attachment unless the context would be hard to recover from the plan alone.
34
34
  - Periodically check shared-context fitness, especially after pivots, blocker discovery, PR creation, before checking Implement complete, and before closing the story.
35
35
  - Keep the user in the loop when the plan changes: state what changed, why it changed, and the new next step before or while editing the plan.
@@ -97,17 +97,18 @@ If the user chooses option 1 or 2:
97
97
  ## Sync Behavior
98
98
 
99
99
  - detect the provider from the story metadata comment before using provider-specific wording; use `jira:` or `github:` markers as the source of truth
100
- - Use `imdone pull` when the local story needs the latest provider state before continuing.
100
+ - Use `imdone pull` when imdone is available and the local story needs the latest provider state before continuing.
101
+ - If imdone is unavailable, skip provider sync commands and record the needed provider refresh, publication, or review as an external evidence gate.
101
102
  - If a sync conflict occurs during `imdone push`, resolve the file conflict, then run `imdone merge` before continuing.
102
- - Follow `references/configuration.md` for push defaults and prompt behavior.
103
- - Before asking whether to run `imdone push`, check `imdone agent-config get-config` and only prompt when `push.imdoneStatus.hasPendingChanges` is true. If `imdone status` reports `Nothing to push`, skip the reminder.
103
+ - Follow `references/configuration.md` for push defaults and prompt behavior only in imdone projects.
104
+ - Before asking whether to run `imdone push`, check `imdone agent-config get-config` and only prompt when `push.imdoneStatus.hasPendingChanges` is true. If `imdone status` reports `Nothing to push`, skip the reminder. If imdone is unavailable, do not ask for `imdone push`.
104
105
  - If push behavior is configured as `interval_prompt`, call `imdone agent-config get-config` before substantial work cycles and when deciding whether to prompt, and use `push.promptDue`, `push.elapsedMinutes`, and `push.nextPromptAt` instead of rough mental timing.
105
106
  - In `interval_prompt` mode, call `imdone agent-config record-push-prompt` every time the workflow actually asks whether to run `imdone push`, including prompts triggered by elapsed time, major checkpoints, or explicit user requests.
106
107
  - After `imdone push` succeeds, do not call an HDD-only sync timestamp setter. The `imdone push` command owns successful push sync timestamp recording; refresh state with `imdone agent-config get-config` when the workflow needs the updated timing.
107
- - If `attachments/plan.md` is created from placeholder content into a real plan, treat that as a push-worthy checkpoint in `phase_prompt` mode.
108
- - If a progress note is recorded in `attachments/progress-notes.md`, treat that as a push-worthy checkpoint in `phase_prompt` mode because resumability context changed materially.
108
+ - If `attachments/plan.md` is created from placeholder content into a real plan in an imdone project, treat that as a push-worthy checkpoint in `phase_prompt` mode.
109
+ - If a progress note is recorded in `attachments/progress-notes.md` in an imdone project, treat that as a push-worthy checkpoint in `phase_prompt` mode because resumability context changed materially.
109
110
 
110
- After each phase:
111
+ After each phase in an imdone project:
111
112
  - if `push.mode` is `phase_prompt`, ask:
112
113
  ```text
113
114
  Phase complete. Push changes now? (imdone push)
@@ -134,4 +135,4 @@ After each phase:
134
135
  ```
135
136
  - if `push.mode` is `interval_prompt` and `push.promptDue` is false, do not ask to push yet; if `imdone status` is clean, say nothing about pushing
136
137
 
137
- If the user chooses yes, run `imdone push`, then call `imdone agent-config get-config` if the workflow needs refreshed timing. If no, leave the interval overdue state intact and remind the user to push before ending the session.
138
+ If the user chooses yes, run `imdone push`, then call `imdone agent-config get-config` if the workflow needs refreshed timing. If no, leave the interval overdue state intact and remind the user to push before ending the session. If imdone is unavailable, skip this push prompt behavior entirely.
@@ -122,10 +122,10 @@ Before drafting or revising Design, Plan, or Implement:
122
122
 
123
123
  - Preserve completed phases.
124
124
  - Update remaining phases to match what has been learned.
125
- - Add a short progress note for pivots, new constraints, or eliminated risks with `imdone note <issueKey> "<note>"`.
126
- - During implementation, capture user feedback and newly learned constraints in `attachments/progress-notes.md` through `imdone note` instead of leaving them only in conversation history.
125
+ - Add a short progress note for pivots, new constraints, or eliminated risks by trying `imdone note <issueKey> "<note>"` first. Append directly to `attachments/progress-notes.md` only if `imdone note` is unavailable or exits non-zero for the current workspace.
126
+ - During implementation, capture user feedback and newly learned constraints in `attachments/progress-notes.md` through `imdone note` when that command succeeds, or direct Markdown append only when `imdone note` is unavailable or fails, instead of leaving them only in conversation history.
127
127
  - Use progress notes to capture both "what changed" and "why it changed" plus what must be remembered.
128
- - Let `imdone note` add the full ISO timestamp and author, so resumability includes ordering within the day.
128
+ - Let `imdone note` add the full ISO timestamp and author when available; otherwise write the timestamp and best available author directly, so resumability includes ordering within the day.
129
129
  - Tell the user which phase changed and the new execution path.
130
130
  - Keep completed checklist items checked and leave future work unchecked.
131
131
  - If the pivot is significant, ask one alignment or comprehension question before rewriting multiple phases.
@@ -150,11 +150,11 @@ Before drafting or revising Design, Plan, or Implement:
150
150
  - Use code as documentation: prefer readable module boundaries, good function names, explicit data flow, and behavior-focused tests. Add comments only when the code cannot reasonably explain itself.
151
151
  - Keep `attachments/demo.md` current during implementation. If the built path, visible outcome, fallback behavior, or operator steps change, revise the existing demo plan instead of creating a second demo artifact.
152
152
  - If the root cause changes, update `attachments/plan.md` before continuing.
153
- - If the user changes direction, clarifies scope, or gives implementation feedback, update affected plan steps in `attachments/plan.md` immediately and record the changed path and reason with `imdone note`.
153
+ - If the user changes direction, clarifies scope, or gives implementation feedback, update affected plan steps in `attachments/plan.md` immediately and record the changed path and reason with `imdone note` first, falling back to a direct progress-note append only if `imdone note` is unavailable or fails.
154
154
  - Add a new attachment when the implementation feedback would be hard to recover from the plan alone, such as a focused note for testing, rollout, API contract, review context, or a clarifying diagram.
155
155
  - Prefer this pattern:
156
156
  - update the affected phase tasks in `attachments/plan.md`
157
- - run `imdone note <issueKey> "<old next step, new next step, status change, reason for the pivot, user feedback, discovered constraint, or warning for future resumption>"`
157
+ - run `imdone note <issueKey> "<old next step, new next step, status change, reason for the pivot, user feedback, discovered constraint, or warning for future resumption>"` first, or append that note directly to `attachments/progress-notes.md` only if `imdone note` is unavailable or fails
158
158
  - Before checking `Implement` complete, make sure `attachments/demo.md` still matches the real behavior well enough for another human or AI to show the slice without reconstructing the flow from memory.
159
159
  - Before checking `Implement` complete, run the full project test suite from the plan's final confirmation phase after focused checks and artifact-specific verification are green. If the full suite fails, treat that as implementation feedback: fix it, update the plan if the path changes, and do not check Implement complete until the full suite passes or a concrete blocker is recorded.
160
160
  - If the next unchecked item requires a real-world meeting, deployment, customer action, or other external evidence, stop local implementation at that evidence gate. Record the blocker, the required outside action, and where the resulting evidence must be captured before more boxes are checked.
@@ -14,7 +14,7 @@ Use the story attachments as the source of truth:
14
14
  - `attachments/dod.md`
15
15
  - `attachments/hdd-skill-feedback.md` if present
16
16
  - `attachments/hdd-skill-feedback.json` if present
17
- - `backlog/.imdone/agent-config.yml` if present through `imdone agent-config`; do not read or edit it directly
17
+ - `backlog/.imdone/agent-config.yml` if imdone is available, read through `imdone agent-config`; do not read or edit it directly
18
18
 
19
19
  If an attachment is empty, placeholder-only, or still stock-template content, use `.imdone/templates/stock/hypothesis-driven-development.md` and the referenced partial in `.imdone/templates/stock/partials/` as fallback.
20
20
 
@@ -23,15 +23,24 @@ If an attachment file is missing:
23
23
  - do not pre-seed later-phase attachments unless the workflow has actually reached that phase
24
24
  - keep placeholder-only future-phase files out of the way unless the story already links to them and the user wants them created now
25
25
 
26
- ## Check Push Reminder Setup
26
+ ## Deterministic Tool Availability And Push Reminder Setup
27
27
 
28
- Before resolving the story, call `imdone agent-config get-config`.
28
+ Before resolving the story, determine imdone availability with these deterministic checks:
29
29
 
30
- - if `push.status` is `configured`, use the stored reminder settings for this session
31
- - if `push.status` is `missing`, ask the user to choose their push reminder behavior using `references/configuration.md`, then persist it with `imdone agent-config set-push-config` before continuing
32
- - when checking whether to remind about pushing, use the `imdone status` result returned by `get-config`; only prompt when pending changes exist
33
- - do not silently accept smart defaults when push config is missing; present them as the default option the user can choose
30
+ 1. Run `imdone --version`.
31
+ - Exit 0 means the `imdone` command is installed.
32
+ - Non-zero exit or command-not-found means imdone command features are unavailable.
33
+ 2. If `imdone --version` succeeds, run `imdone agent-config get-config`.
34
+ - Exit 0 with JSON `status: "ok"` means the current workspace is initialized enough for HDD session state and push reminder behavior.
35
+ - Non-zero exit or a non-ok status means do not use `imdone agent-config`, push reminders, active-story persistence, or provider sync prompts for this session.
36
+ 3. For progress notes, use a separate command-specific check: try `imdone note <issueKey> "<note>"` when a note must be recorded. Append directly to `attachments/progress-notes.md` only if `imdone note` is unavailable or exits non-zero for the current workspace.
37
+
38
+ - if imdone is available and `push.status` is `configured`, use the stored reminder settings for this session
39
+ - if imdone is available and `push.status` is `missing`, ask the user to choose their push reminder behavior using `references/configuration.md`, then persist it with `imdone agent-config set-push-config` before continuing
40
+ - when checking whether to remind about pushing in an imdone project, use the `imdone status` result returned by `get-config`; only prompt when pending changes exist
41
+ - do not silently accept smart defaults when push config is missing in an imdone project; present them as the default option the user can choose
34
42
  - after push config exists, call `imdone agent-config begin-session` so interval timing starts from this HDD session instead of stale wall-clock time from an older run
43
+ - if the deterministic checks fail, skip push reminder setup and use plain Markdown artifacts as the session state; record provider sync or publishing as an external evidence gate when needed
35
44
 
36
45
  ## Resolve The Story
37
46
 
@@ -40,13 +49,13 @@ If an issue key is provided, open:
40
49
 
41
50
  If no issue key is provided:
42
51
  - prefer the story already active in this conversation
43
- - otherwise call `imdone agent-config get-active-story` to check for one saved local HDD story
52
+ - otherwise, when imdone is available, call `imdone agent-config get-active-story` to check for one saved local HDD story
44
53
  - if the helper returns a valid story path, use that story
45
54
  - otherwise use the most recently modified `backlog/current-sprint/*/issue-*.md`
46
55
  - confirm the candidate with the user before editing
47
56
 
48
57
  After the story is confirmed, read the story and all relevant attachments before asking anything.
49
- After the story is confirmed, call `imdone agent-config set-active-story --key <issueKey>` so the next no-key HDD session can resume that story.
58
+ After the story is confirmed in an imdone project, call `imdone agent-config set-active-story --key <issueKey>` so the next no-key HDD session can resume that story. If imdone is unavailable, rely on the conversation and local Markdown paths for resumption.
50
59
 
51
60
  Detect the issue provider from the issue file metadata comment before using provider-specific language or assumptions:
52
61
  - read the closing HTML comment in the issue markdown
@@ -62,7 +71,7 @@ Use the issue file as the attachment index for issue attachments:
62
71
  - duplicate links are acceptable when adding a missing link is simpler than normalizing existing ones
63
72
  - keep links current when attachment names change or when new focused attachments are added for clarity
64
73
 
65
- If the story is not already using the HDD template, use the `imdone-template` skill before continuing:
74
+ If the story is not already using the HDD template, use the `imdone-template` skill before continuing when imdone is available:
66
75
  - detect this by the absence of `#HDD-template` in the issue content or by obviously missing HDD template structure
67
76
  - apply template `hypothesis_driven_development` when it exists locally; otherwise apply `stock_hypothesis_driven_development`
68
77
  - re-read the story and attachments after templating, then continue the HDD workflow
@@ -70,6 +79,8 @@ If the story is not already using the HDD template, use the `imdone-template` sk
70
79
  - make sure `## Problem Framing`, `## Acceptance Criteria`, `## Vertical Slice`, `## Related Work`, `## Hypothesis`, and `## Validate Assumptions` live above `## Define the Outcome`; the checklist should only track whether those sections and attachment artifacts are complete
71
80
  - make sure `Measure Results` and `Retrospect` are backed by `## Results` and `## Retrospect` sections in `attachments/success-metrics.md`; the checklist should only track completion
72
81
 
82
+ If imdone is unavailable, repair the Markdown structure directly using the same expected headings and attachment filenames instead of blocking on template tooling.
83
+
73
84
  If the story already has `#HDD-template` but its sections, headings, or checklist order no longer match the current bundled HDD format:
74
85
  - treat it as an outdated HDD story that should be repaired with the user, not ignored
75
86
  - explain what is out of alignment before editing, for example missing checklist items, old heading names, or section order drift
@@ -19,8 +19,11 @@ function main() {
19
19
  .toLowerCase();
20
20
  const errors = [];
21
21
 
22
- if (!combined.includes("imdone note")) {
23
- errors.push("HDD progress-note guidance must tell agents to record new notes with `imdone note`.");
22
+ if (!combined.includes("imdone note") || !combined.includes("try `imdone note")) {
23
+ errors.push("HDD progress-note guidance must tell agents to try `imdone note` first.");
24
+ }
25
+ if (!combined.includes("append") || !combined.includes("only if `imdone note` is unavailable")) {
26
+ errors.push("HDD progress-note guidance must allow direct Markdown appends only when `imdone note` is unavailable or fails.");
24
27
  }
25
28
  if (!combined.includes("attachments/progress-notes.md")) {
26
29
  errors.push("HDD progress lookup must name `attachments/progress-notes.md` as the canonical progress-note artifact.");
@@ -34,7 +37,7 @@ function main() {
34
37
  return 1;
35
38
  }
36
39
 
37
- console.log("OK: HDD progress-note guidance uses imdone note with progress-notes.md and legacy plan fallback.");
40
+ console.log("OK: HDD progress-note guidance tries imdone note first, uses direct Markdown fallback only when needed, and keeps legacy plan fallback.");
38
41
  return 0;
39
42
  }
40
43
 
@@ -2,4 +2,4 @@
2
2
 
3
3
  This plugin registers the imdone AI correction note hook with Codex-compatible plugin runtimes.
4
4
 
5
- The hook listens for developer prompts that disagree with AI output, ask for an adjustment, add missing context, or report an AI workflow problem such as hooks/plugins not loading. Matching prompts delegate to the bundled `ai-adjustment-note` writer so notes go to the active HDD story when one is configured, or report `needs-target` when the user must choose an issue or file.
5
+ The hook always captures the first prompt in a session when it can identify a new session inside an imdone-enabled software project. Later prompts use judgement: developer disagreement, requested adjustments, added context, or AI workflow problems delegate to the bundled `ai-adjustment-note` writer so notes go to the configured active story when one is configured, or report `needs-target` when the user must choose an issue or file.
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync } from 'node:fs';
2
+ import { createHash } from 'node:crypto';
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
3
5
  import { spawnSync } from 'node:child_process';
4
6
  import path from 'node:path';
5
7
  import { fileURLToPath } from 'node:url';
@@ -11,6 +13,8 @@ const DISAGREEMENT_PATTERN = /\b(no|nope|not quite|wrong|incorrect|that'?s not|t
11
13
  const CONTEXT_PATTERN = /\b(context|more context|additional context|to clarify|clarification|for context|the important part|the point is|the intent is|this is meant to|meant to)\b/i;
12
14
  const PROBLEM_SIGNAL_PATTERN = /\b(doesn'?t look like|does not look like|didn'?t|did not|doesn'?t|does not|isn'?t|is not|wasn'?t|was not|won'?t|will not|can'?t|cannot|couldn'?t|could not|failed|fails|failing|failure|not working|isn'?t working|broken|not installed|not loaded|not enabled|not showing|not listed|didn'?t record|did not record|didn'?t capture|did not capture|didn'?t run|did not run|didn'?t trigger|did not trigger|why didn'?t|why did not|should have recorded|should have captured|expected it to)\b/i;
13
15
  const AI_WORKFLOW_PATTERN = /\b(ai|agent|codex|claude|hook|plugin|prompt|output|response|capture|record|note|hdd|imdone|runtime|marketplace|install|installed|enabled|loaded)\b/i;
16
+ const FIRST_PROMPT_KEYS = ['isFirstPrompt', 'firstPrompt', 'is_first_prompt', 'first_prompt'];
17
+ const SESSION_ID_KEYS = ['session_id', 'sessionId', 'conversation_id', 'conversationId', 'thread_id', 'threadId', 'chat_id', 'chatId'];
14
18
 
15
19
  function readStdin() {
16
20
  return new Promise(resolve => {
@@ -61,7 +65,85 @@ function extractPrompt(payload) {
61
65
  .trim();
62
66
  }
63
67
 
64
- function classifyCapture(prompt) {
68
+ function collectValueByKeys(value, keys) {
69
+ if (!value || typeof value !== 'object') return null;
70
+
71
+ for (const key of keys) {
72
+ if (Object.prototype.hasOwnProperty.call(value, key)) return value[key];
73
+ }
74
+
75
+ if (Array.isArray(value)) {
76
+ for (const item of value) {
77
+ const found = collectValueByKeys(item, keys);
78
+ if (found !== null && found !== undefined) return found;
79
+ }
80
+ return null;
81
+ }
82
+
83
+ for (const item of Object.values(value)) {
84
+ const found = collectValueByKeys(item, keys);
85
+ if (found !== null && found !== undefined) return found;
86
+ }
87
+ return null;
88
+ }
89
+
90
+ function explicitFirstPrompt(payload) {
91
+ const value = collectValueByKeys(payload, FIRST_PROMPT_KEYS);
92
+ if (typeof value === 'boolean') return value;
93
+ if (typeof value === 'string' && /^(true|false)$/i.test(value.trim())) return value.trim().toLowerCase() === 'true';
94
+ return null;
95
+ }
96
+
97
+ function extractSessionId(payload) {
98
+ const value = collectValueByKeys(payload, SESSION_ID_KEYS);
99
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
100
+ }
101
+
102
+ function sessionStatePath(repoRoot) {
103
+ const projectHash = createHash('sha256').update(repoRoot).digest('hex').slice(0, 16);
104
+ return path.join(tmpdir(), 'imdone-ai-adjustment-note', `${projectHash}.json`);
105
+ }
106
+
107
+ function readSeenSessions(repoRoot) {
108
+ try {
109
+ const parsed = JSON.parse(readFileSync(sessionStatePath(repoRoot), 'utf8'));
110
+ return parsed && typeof parsed === 'object' && parsed.sessions && typeof parsed.sessions === 'object'
111
+ ? parsed.sessions
112
+ : {};
113
+ } catch {
114
+ return {};
115
+ }
116
+ }
117
+
118
+ function hasSeenSession(repoRoot, sessionId) {
119
+ return Boolean(readSeenSessions(repoRoot)[sessionId]);
120
+ }
121
+
122
+ function markSessionSeen(repoRoot, sessionId) {
123
+ if (!sessionId) return;
124
+ const stateFile = sessionStatePath(repoRoot);
125
+ const sessions = readSeenSessions(repoRoot);
126
+ sessions[sessionId] = new Date().toISOString();
127
+ mkdirSync(path.dirname(stateFile), { recursive: true });
128
+ writeFileSync(stateFile, JSON.stringify({ sessions }, null, 2), 'utf8');
129
+ }
130
+
131
+ function isFirstPrompt(payload, repoRoot) {
132
+ const explicit = explicitFirstPrompt(payload);
133
+ if (explicit !== null) return explicit;
134
+ const sessionId = extractSessionId(payload);
135
+ return Boolean(sessionId && !hasSeenSession(repoRoot, sessionId));
136
+ }
137
+
138
+ function classifyCapture(prompt, { firstPrompt = false } = {}) {
139
+ if (firstPrompt) {
140
+ return {
141
+ title: 'AI first prompt captured',
142
+ change: 'Developer started a prompt in an imdone-enabled software project.',
143
+ reason: 'Capturing the first prompt preserves plate-spun session context before follow-up judgement applies.'
144
+ };
145
+ }
146
+
65
147
  if (DISAGREEMENT_PATTERN.test(prompt) || CONTEXT_PATTERN.test(prompt) || (PROBLEM_SIGNAL_PATTERN.test(prompt) && AI_WORKFLOW_PATTERN.test(prompt))) {
66
148
  return {
67
149
  title: 'AI correction captured',
@@ -133,12 +215,14 @@ function runWriter({ writer, repoRoot, request, capture }) {
133
215
  async function main() {
134
216
  const payload = parsePayload(await readStdin());
135
217
  const prompt = extractPrompt(payload);
136
- const capture = prompt ? classifyCapture(prompt) : null;
137
- if (!capture) return;
138
-
139
218
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
140
219
  const startDir = firstString(payload.cwd, payload.projectRoot, payload.workspace, process.env.PWD, process.cwd());
141
220
  const repoRoot = findRepoRoot(startDir);
221
+ const sessionId = extractSessionId(payload);
222
+ const capture = prompt ? classifyCapture(prompt, { firstPrompt: isFirstPrompt(payload, repoRoot) }) : null;
223
+ if (sessionId) markSessionSeen(repoRoot, sessionId);
224
+ if (!capture) return;
225
+
142
226
  const writer = findWriter(repoRoot, scriptDir);
143
227
  if (!writer) {
144
228
  process.stderr.write('imdone ai-adjustment-note writer was not found; skipping note capture.\n');