create-pathfinder 1.4.0 → 1.5.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 (42) hide show
  1. package/AGENTS.md +4 -0
  2. package/CLAUDE.md +7 -1
  3. package/README.md +97 -9
  4. package/bin/create-pathfinder.mjs +32 -4
  5. package/context/features/example-feature-spec.md +5 -1
  6. package/context/project-overview.md +16 -3
  7. package/copy-list.json +0 -1
  8. package/package.json +2 -2
  9. package/skills/reflect/SKILL.md +46 -46
  10. package/skills/reverse-engineer/SKILL.md +2 -0
  11. package/skills/to-specs/SKILL.md +1 -1
  12. package/src/cli.mjs +765 -34
  13. package/src/clipboard.mjs +134 -0
  14. package/src/detect.mjs +183 -0
  15. package/src/editor.mjs +136 -0
  16. package/src/git.mjs +58 -0
  17. package/src/harnesses/adapter.mjs +288 -0
  18. package/src/harnesses/index.mjs +122 -0
  19. package/src/install.mjs +209 -1
  20. package/src/kickstart-prompt.mjs +81 -0
  21. package/src/prompt.mjs +307 -0
  22. package/templates/project-overview.template.md +16 -3
  23. package/prompts/01-kickstart-project.md +0 -1
  24. package/prompts/01-teach-current-feature.md +0 -9
  25. package/prompts/02-debate-me.md +0 -1
  26. package/prompts/02-quiz-current-feature.md +0 -7
  27. package/prompts/03-challenge-current-feature.md +0 -7
  28. package/prompts/03-prototype.md +0 -1
  29. package/prompts/04-teach-current-architecture.md +0 -7
  30. package/prompts/04-to-specs.md +0 -1
  31. package/prompts/05-learning-review.md +0 -5
  32. package/prompts/05-load-feature.md +0 -1
  33. package/prompts/06-start-feature.md +0 -1
  34. package/prompts/07-review-feature.md +0 -1
  35. package/prompts/08-complete-feature.md +0 -1
  36. package/prompts/09-learn-feature.md +0 -1
  37. package/prompts/10-learn-codebase.md +0 -1
  38. package/prompts/11-handoff.md +0 -1
  39. package/prompts/12-skillsmith.md +0 -1
  40. package/prompts/13-reverse-engineer.md +0 -18
  41. package/prompts/14-reflect.md +0 -13
  42. package/prompts/15-debug-issue.md +0 -15
@@ -0,0 +1,81 @@
1
+ /**
2
+ * What to tell your agent first, in the words the chosen tool understands.
3
+ *
4
+ * This used to be one hardcoded string naming a file path, and that was right
5
+ * for exactly as long as Pathfinder configured nothing. Once a run can generate
6
+ * native adapters, the path form is no longer the best answer for someone who
7
+ * just watched twenty skills be installed into their harness — it is the answer
8
+ * for someone whose tool cannot discover them.
9
+ *
10
+ * A pure function of the selection, deliberately: no filesystem, no detection,
11
+ * no process. Everything harness-specific comes out of the registry's own
12
+ * `invocation`, so a third harness supplies its wording by existing rather than
13
+ * by adding a branch here.
14
+ */
15
+
16
+ /** The skill this prompt starts. The one name in this module that is not derived. */
17
+ export const KICKSTART_SKILL = "kickstart-pathfinder";
18
+
19
+ /**
20
+ * The harness-neutral prompt, unchanged from every version before this one.
21
+ *
22
+ * Two lines because it is read aloud to an agent, and because the second half —
23
+ * "do not install packages or write product code yet" — is the part that keeps
24
+ * a first session from turning into an unrequested scaffold.
25
+ */
26
+ const PATH_PROMPT =
27
+ `Use skills/${KICKSTART_SKILL}/SKILL.md. Help me initialize this project. ` +
28
+ "Do not install packages or write product code yet.";
29
+
30
+ /**
31
+ * The prompt to print, and to offer to copy.
32
+ *
33
+ * Exactly one harness gets that harness's native invocation, because it is the
34
+ * more reliable entry point once the adapter exists and it is shorter than the
35
+ * thing it replaces.
36
+ *
37
+ * Zero or several harnesses both fall back to the path form, for different
38
+ * reasons that happen to have the same answer. Zero means nothing was
39
+ * generated, so there is no native invocation to name. Several means one
40
+ * clipboard cannot serve two syntaxes — and picking a favorite would quietly
41
+ * decide which of the user's tools is the real one.
42
+ *
43
+ * @param {ReadonlyArray<{invocation?: (name: string) => string}>} harnesses
44
+ * The harnesses this run configured, in registry order.
45
+ * @returns {string}
46
+ */
47
+ export function kickstartPrompt(harnesses = []) {
48
+ const selected = Array.isArray(harnesses) ? harnesses.filter(Boolean) : [];
49
+ if (selected.length !== 1) return PATH_PROMPT;
50
+
51
+ const [harness] = selected;
52
+ if (typeof harness.invocation !== "function") return PATH_PROMPT;
53
+
54
+ const invocation = harness.invocation(KICKSTART_SKILL);
55
+
56
+ // A registry entry that returns nothing usable falls back rather than
57
+ // printing an empty "next step". Nothing in this repository does that today;
58
+ // the guard is here because the alternative failure is a blank instruction.
59
+ return typeof invocation === "string" && invocation.trim() !== "" ? invocation.trim() : PATH_PROMPT;
60
+ }
61
+
62
+ /**
63
+ * The prompt as the summary prints it: wrapped, indented, already a block.
64
+ *
65
+ * The path form is two indented lines under a heading and has been since 1.4.1,
66
+ * so it is reproduced exactly rather than re-wrapped by a general algorithm
67
+ * that might land the break one word over. A native invocation is a single
68
+ * short token and needs no wrapping at all.
69
+ *
70
+ * @returns {string[]} lines, indented, with no trailing blank
71
+ */
72
+ export function kickstartPromptLines(harnesses = []) {
73
+ const prompt = kickstartPrompt(harnesses);
74
+
75
+ if (prompt !== PATH_PROMPT) return [` ${prompt}`];
76
+
77
+ return [
78
+ ` Use skills/${KICKSTART_SKILL}/SKILL.md. Help me initialize this`,
79
+ " project. Do not install packages or write product code yet.",
80
+ ];
81
+ }
package/src/prompt.mjs ADDED
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Asking a question, and the rules about when one may be asked at all.
3
+ *
4
+ * Two constraints shape everything here.
5
+ *
6
+ * The first is the TTY guard. A prompt is only ever offered when *both* stdin
7
+ * and stdout are terminals, and that decision is made once, by the caller, and
8
+ * carried on this object. A prompter built with `interactive: false` does not
9
+ * ask a question quietly and default it — it throws, because a CLI that asks
10
+ * nothing must also print nothing, and a guard bug that degrades silently would
11
+ * show up as a script that hangs on someone's CI runner months later.
12
+ *
13
+ * The second is the decided interaction model: `node:readline` and printed
14
+ * lines. No setRawMode, no keypress handler, no cursor control, no redraw. That
15
+ * is what makes this work in a dumb terminal, over ssh, and inside an editor's
16
+ * integrated console, and it is a decision rather than a default.
17
+ *
18
+ * Answers are bounded. Unparseable input is re-prompted a fixed number of times
19
+ * and then gives up, so an input stream that will never produce a `y` cannot
20
+ * spin forever. Giving up returns null rather than the default: null means
21
+ * "nobody answered", and the caller decides what that is worth. For a question
22
+ * that authorizes an action, it is worth a refusal.
23
+ */
24
+
25
+ import { createInterface } from "node:readline";
26
+
27
+ const YES = new Set(["y", "yes"]);
28
+ const NO = new Set(["n", "no"]);
29
+
30
+ /**
31
+ * A question-asker bound to one pair of streams.
32
+ *
33
+ * @param {{input: NodeJS.ReadableStream, output: NodeJS.WritableStream,
34
+ * interactive?: boolean, retries?: number}} options
35
+ * @returns {{interactive: boolean,
36
+ * confirm: (question: string, options?: {defaultAnswer?: boolean}) => Promise<boolean|null>,
37
+ * chooseMany: (question: string, config?: object) => Promise<unknown[]|null>,
38
+ * chooseOne: (question: string, config?: object) => Promise<unknown|null>,
39
+ * text: (question: string) => Promise<string|null>,
40
+ * close: () => void}}
41
+ */
42
+ export function createPrompter({ input, output, interactive = false, retries = 3 }) {
43
+ let reader = null;
44
+
45
+ // Created on first use, not here. A run that never reaches a question — a
46
+ // repository that already exists, `--help`, a bad flag — must not attach a
47
+ // reader to stdin, because attaching one resumes the stream and a process
48
+ // holding an open stdin does not exit on its own.
49
+ function ensureReader() {
50
+ if (reader === null) reader = createReader({ input, output });
51
+ return reader;
52
+ }
53
+
54
+ return {
55
+ interactive,
56
+
57
+ async confirm(question, { defaultAnswer = true } = {}) {
58
+ if (!interactive) {
59
+ throw new Error(`refusing to ask "${question}": this prompter is not interactive`);
60
+ }
61
+
62
+ const suffix = defaultAnswer ? "[Y/n]" : "[y/N]";
63
+
64
+ for (let attempt = 0; attempt < retries; attempt += 1) {
65
+ const answer = await ensureReader().ask(`? ${question} ${suffix} `);
66
+
67
+ // End of input. Ctrl-D, or a stream that closed under us.
68
+ if (answer === null) return null;
69
+
70
+ const normalized = answer.trim().toLowerCase();
71
+ if (normalized === "") return defaultAnswer;
72
+ if (YES.has(normalized)) return true;
73
+ if (NO.has(normalized)) return false;
74
+
75
+ output.write(" Please answer y or n.\n");
76
+ }
77
+
78
+ return null;
79
+ },
80
+
81
+ /**
82
+ * Ask which of a numbered list apply. Several answers, or none.
83
+ *
84
+ * Numbers rather than checkboxes, for the same reason `confirm` is a line
85
+ * and not a keypress handler: no raw mode, no redraw, so it works in a dumb
86
+ * terminal, over ssh, and inside an editor console. `Enter` takes the
87
+ * default — which is what keeps the common case one keystroke — and `0` is
88
+ * an explicit "none of them", distinct from an empty line that means "the
89
+ * default", because a default of nothing and a choice of nothing should not
90
+ * have to be told apart by their side effects.
91
+ *
92
+ * Returns the chosen options' `value`s in list order, or null when nobody
93
+ * answered. Null is not an empty selection: the caller decides what an
94
+ * unanswered question is worth.
95
+ *
96
+ * @param {string} question
97
+ * @param {{options: {label: string, value: unknown}[], defaultSelection?: unknown[]}} config
98
+ * @returns {Promise<unknown[]|null>}
99
+ */
100
+ async chooseMany(question, { options: choices = [], defaultSelection = [] } = {}) {
101
+ if (!interactive) {
102
+ throw new Error(`refusing to ask "${question}": this prompter is not interactive`);
103
+ }
104
+ if (choices.length === 0) return [];
105
+
106
+ const defaults = choices.filter((choice) => defaultSelection.includes(choice.value));
107
+ const defaultNumbers = defaults.map((choice) => choices.indexOf(choice) + 1);
108
+
109
+ const header = [
110
+ `? ${question}`,
111
+ ...choices.map((choice, index) => ` ${index + 1}. ${choice.label}`),
112
+ defaultNumbers.length > 0
113
+ ? ` Numbers, comma-separated. Enter for the detected default [${defaultNumbers.join(",")}], or 0 for none.`
114
+ : " Numbers, comma-separated. Enter or 0 for none.",
115
+ "",
116
+ ].join("\n");
117
+
118
+ output.write(header);
119
+
120
+ for (let attempt = 0; attempt < retries; attempt += 1) {
121
+ const answer = await ensureReader().ask("> ");
122
+ if (answer === null) return null;
123
+
124
+ const normalized = answer.trim();
125
+ if (normalized === "") return defaults.map((choice) => choice.value);
126
+
127
+ const selected = parseSelection(normalized, choices.length);
128
+ if (selected !== null) return selected.map((index) => choices[index].value);
129
+
130
+ output.write(` Please answer with numbers from 1 to ${choices.length}, or 0 for none.\n`);
131
+ }
132
+
133
+ return null;
134
+ },
135
+
136
+ /**
137
+ * Ask which one of a numbered list. Exactly one, or none.
138
+ *
139
+ * The same lines-and-numbers shape as `chooseMany`, and separate from it on
140
+ * purpose: a question with one answer must not print "comma-separated" and
141
+ * must not have to decide what `1,2` meant. A caller that wants "none" as a
142
+ * possibility supplies it as an option, because a list of editors and a
143
+ * decision not to open one read better as three rows than as a rule.
144
+ *
145
+ * Returns the chosen option's `value`, or null when nobody answered. An
146
+ * option whose value is null and an unanswered question are indistinguishable
147
+ * here, which is correct for every question worth asking this way: both mean
148
+ * nothing should happen.
149
+ *
150
+ * @param {string} question
151
+ * @param {{options: {label: string, value: unknown}[], defaultValue?: unknown}} config
152
+ * @returns {Promise<unknown|null>}
153
+ */
154
+ async chooseOne(question, { options: choices = [], defaultValue } = {}) {
155
+ if (!interactive) {
156
+ throw new Error(`refusing to ask "${question}": this prompter is not interactive`);
157
+ }
158
+ if (choices.length === 0) return null;
159
+
160
+ const fallback = choices.find((choice) => choice.value === defaultValue) ?? choices[0];
161
+ const defaultNumber = choices.indexOf(fallback) + 1;
162
+
163
+ output.write(
164
+ [
165
+ `? ${question}`,
166
+ ...choices.map((choice, index) => ` ${index + 1}. ${choice.label}`),
167
+ ` A number, or Enter for [${defaultNumber}].`,
168
+ "",
169
+ ].join("\n"),
170
+ );
171
+
172
+ for (let attempt = 0; attempt < retries; attempt += 1) {
173
+ const answer = await ensureReader().ask("> ");
174
+ if (answer === null) return null;
175
+
176
+ const normalized = answer.trim();
177
+ if (normalized === "") return fallback.value;
178
+
179
+ if (/^\d+$/.test(normalized)) {
180
+ const number = Number(normalized);
181
+ if (number >= 1 && number <= choices.length) return choices[number - 1].value;
182
+ }
183
+
184
+ output.write(` Please answer with a number from 1 to ${choices.length}.\n`);
185
+ }
186
+
187
+ return null;
188
+ },
189
+
190
+ /**
191
+ * Ask for a line of free text. Whatever they typed, or null.
192
+ *
193
+ * Deliberately the thinnest of the three: no retries, no default, no
194
+ * validation. There is nothing here to re-prompt *about* — any line is a
195
+ * well-formed answer to "what is it called" — so judging one is the
196
+ * caller's job, and only the caller knows what makes a name unusable.
197
+ *
198
+ * An empty line comes back as `""` and is a real answer, distinct from the
199
+ * `null` of a stream that ended. A caller looping for several values reads
200
+ * the first as "done" and the second as "nobody is there", which happen to
201
+ * lead to the same place but for different reasons.
202
+ */
203
+ async text(question) {
204
+ if (!interactive) {
205
+ throw new Error(`refusing to ask "${question}": this prompter is not interactive`);
206
+ }
207
+
208
+ const answer = await ensureReader().ask(`? ${question} `);
209
+ return answer === null ? null : answer.trim();
210
+ },
211
+
212
+ close() {
213
+ if (reader !== null) {
214
+ reader.close();
215
+ reader = null;
216
+ }
217
+ },
218
+ };
219
+ }
220
+
221
+ /**
222
+ * Read a comma-separated list of numbers, or null if any part of it is not one.
223
+ *
224
+ * All-or-nothing on purpose. Taking the valid half of `1,banana` would act on a
225
+ * selection the user did not make, and this question decides what gets written
226
+ * into their project. Duplicates collapse and order follows the list, so `2,1`
227
+ * and `1,2,2` mean the same thing.
228
+ *
229
+ * `0` means none, and only on its own: `0,1` is a contradiction, not a subset.
230
+ */
231
+ function parseSelection(input, count) {
232
+ const parts = input.split(",").map((part) => part.trim());
233
+ if (parts.some((part) => !/^\d+$/.test(part))) return null;
234
+
235
+ const numbers = parts.map(Number);
236
+ if (numbers.includes(0)) return numbers.every((number) => number === 0) ? [] : null;
237
+ if (numbers.some((number) => number > count)) return null;
238
+
239
+ return [...new Set(numbers)].sort((a, b) => a - b).map((number) => number - 1);
240
+ }
241
+
242
+ /**
243
+ * A prompter that cannot be asked anything.
244
+ *
245
+ * What the CLI holds when there is no terminal on both ends, or when `--yes`
246
+ * has already answered everything. Handing back a real object rather than null
247
+ * keeps the call sites free of `prompter?.` — the guard is `interactive`, in
248
+ * one place, and it reads as the rule it is.
249
+ */
250
+ export function nonInteractivePrompter() {
251
+ return createPrompter({ input: null, output: null, interactive: false });
252
+ }
253
+
254
+ /**
255
+ * A line-at-a-time reader over one stream pair.
256
+ *
257
+ * Built on the `line` event rather than `readline.question`, which looks like
258
+ * the obvious choice and is not: a line that arrives while no question is
259
+ * outstanding is discarded, so the second `question` in a sequence — issued a
260
+ * microtask later, because the first was awaited — waits forever for input that
261
+ * was already delivered. Typing ahead, and any test that writes its answers up
262
+ * front, would hang. Holding the lines here instead means input is never lost
263
+ * between questions.
264
+ *
265
+ * `close` resolves every outstanding and future read as null. A prompt reached
266
+ * with a closed stdin must fall through to the caller's decision about an
267
+ * unanswered question, not hang.
268
+ */
269
+ function createReader({ input, output }) {
270
+ const readline = createInterface({ input, output });
271
+ const delivered = [];
272
+ const waiting = [];
273
+ let closed = false;
274
+
275
+ readline.on("line", (line) => {
276
+ const resolve = waiting.shift();
277
+ if (resolve) resolve(line);
278
+ else delivered.push(line);
279
+ });
280
+
281
+ readline.on("close", () => {
282
+ closed = true;
283
+ while (waiting.length > 0) waiting.shift()(null);
284
+ });
285
+
286
+ return {
287
+ /**
288
+ * Print `text` and resolve with the next line, or null at end of input.
289
+ *
290
+ * The prompt goes through readline rather than straight to the stream so
291
+ * that on a real terminal readline knows its width and can redraw the line
292
+ * correctly when the user backspaces over what they typed.
293
+ */
294
+ ask(text) {
295
+ readline.setPrompt(text);
296
+ readline.prompt();
297
+
298
+ if (delivered.length > 0) return Promise.resolve(delivered.shift());
299
+ if (closed) return Promise.resolve(null);
300
+ return new Promise((resolve) => waiting.push(resolve));
301
+ },
302
+
303
+ close() {
304
+ readline.close();
305
+ },
306
+ };
307
+ }
@@ -1,9 +1,10 @@
1
- # [Project Name] — Project Overview
1
+ # Project Overview
2
2
 
3
3
  > Describe the product, audience, intended feeling, and reason it should exist.
4
4
 
5
5
  ## Status
6
6
 
7
+ - Project: `[Project Name]`
7
8
  - Stage: `[idea / prototype / MVP / production / maintenance]`
8
9
  - Repo type: `[new / existing / application / library / service / monorepo / other]`
9
10
  - Primary goal: `[success definition]`
@@ -11,11 +12,23 @@
11
12
 
12
13
  ## Decision States
13
14
 
15
+ These four words describe a decision:
16
+
14
17
  - `TBD` — human decision required
15
18
  - `None` — intentionally excluded
16
19
  - `N/A` — not applicable
17
20
  - `Deferred` — intentionally postponed
18
21
 
22
+ ## Record Status
23
+
24
+ A `Status` column describes the record, not the decision:
25
+
26
+ - `proposed` — written down, not yet approved by the human
27
+ - `accepted` — approved by the human
28
+ - `superseded` — replaced by a later decision, kept for history
29
+
30
+ The two answer different questions. `TBD` says nobody has decided yet. `proposed` says something was recorded for the human to approve. A recorded proposal is not an approved decision.
31
+
19
32
  ## Product Vision
20
33
 
21
34
  - Problem:
@@ -55,9 +68,9 @@ starting state -> action/process -> useful result -> reason to return or continu
55
68
 
56
69
  ## Recommended and Approved Technology
57
70
 
58
- Recommendations may be proposed by `debate-me`; approved choices belong here.
71
+ Recommended and approved choices both live here. `debate-me` and `kickstart-pathfinder` may record a choice before the human approves it; that row is marked `proposed` in `Status` and stays that way until it is `accepted`. Leave `Status` empty while the choice is still `TBD`.
59
72
 
60
- | Layer | Approved choice | Reason | Status |
73
+ | Layer | Choice | Reason | Status |
61
74
  | --- | --- | --- | --- |
62
75
  | Platform/runtime | `TBD` | | |
63
76
  | Language(s) | `TBD` | | |
@@ -1 +0,0 @@
1
- Use `skills/kickstart-pathfinder/SKILL.md` to discover and initialize this new or existing project. Ask progressively, preserve existing repo facts, distinguish recommendations from approved choices, and do not install packages, generate feature specs, or write product code yet.
@@ -1,9 +0,0 @@
1
- Use `skills/teach-feature/SKILL.md`.
2
-
3
- Teach me the verified current feature from its spec, focused diff, implementation, and tests.
4
-
5
- Adapt the depth to `context/learning/learner-profile.md`.
6
-
7
- Create the lesson under `context/learning/lessons/`.
8
-
9
- Do not modify product code, install packages, commit, or merge.
@@ -1 +0,0 @@
1
- Use `skills/debate-me/SKILL.md` to pressure-test this idea or project. Recommend an MVP, technology/architecture, delivery workflow, and prototype checkpoint based on the findings. Present them for my acceptance, modification, comparison, or deferral; do not silently approve them for me.
@@ -1,7 +0,0 @@
1
- Use `skills/quiz-me/SKILL.md`.
2
-
3
- Quiz me on the most recent lesson for the current feature.
4
-
5
- Ask one question at a time. Use varied question formats. Diagnose understanding rather than rewarding memorization.
6
-
7
- Update `context/learning/progress.md` conservatively after the quiz.
@@ -1,7 +0,0 @@
1
- Use `skills/challenge-me/SKILL.md`.
2
-
3
- Create a small transfer challenge based on the current feature's lesson and my learning gaps.
4
-
5
- Change at least one meaningful constraint so I must apply the concept in a new context.
6
-
7
- Do not modify code until I explicitly approve implementation.
@@ -1 +0,0 @@
1
- Use `skills/prototype/SKILL.md` to validate the most important unresolved assumption with the cheapest useful prototype. Define review criteria, create or iterate the artifact, and do not treat it as production code.
@@ -1,7 +0,0 @@
1
- Use `skills/teach-architecture/SKILL.md`.
2
-
3
- Explain how the current feature fits into the wider application architecture.
4
-
5
- Clearly distinguish implemented, mocked, planned, and later-recommended architecture.
6
-
7
- Create a concise architecture lesson with Mermaid diagrams. Do not refactor code.
@@ -1 +0,0 @@
1
- Use `skills/to-specs/SKILL.md` to create a coherent MVP roadmap of context-sized, independently verifiable feature specs. Do not implement code or invent unresolved decisions.
@@ -1,5 +0,0 @@
1
- Use `skills/learning-review/SKILL.md` in interview mode.
2
-
3
- Review my recent lessons, quizzes, challenges, completed features, and progress.
4
-
5
- Identify what I can credibly explain in a senior frontend interview, where my evidence is weak, and the three highest-value concepts to reinforce through upcoming features.
@@ -1 +0,0 @@
1
- Use `skills/load-feature/SKILL.md` to prepare the requested or next feature, validate its dependencies and context size, and populate `context/current-feature.md`. Do not implement yet.
@@ -1 +0,0 @@
1
- Use `skills/start-feature/SKILL.md` to implement the active feature one stable delivery chunk at a time under the project-selected workflow.
@@ -1 +0,0 @@
1
- Use `skills/review-feature/SKILL.md` to review the actual implementation against its spec, regressions, and project quality priorities. Report findings before changing code.
@@ -1 +0,0 @@
1
- Use `skills/complete-feature/SKILL.md` to verify, record, and close the accepted feature under the project delivery policy, then route to feature learning when enabled.
@@ -1 +0,0 @@
1
- Use `skills/learn-feature/SKILL.md` to create a rich interactive lesson and varied quiz for the completed feature, scoped to its implementation and direct dependencies.
@@ -1 +0,0 @@
1
- Use `skills/learn-codebase/SKILL.md` to create a modular interactive learning portal for this repository at the current milestone.
@@ -1 +0,0 @@
1
- Use `skills/handoff/SKILL.md` to create a compact factual handoff for the next agent or session.
@@ -1 +0,0 @@
1
- Use `skills/skillsmith/SKILL.md` to teach me how to design or improve a small local skill for a repeated workflow problem.
@@ -1,18 +0,0 @@
1
- Use `skills/reverse-engineer/SKILL.md`.
2
-
3
- Reverse-engineer the reference I provide.
4
-
5
- Focus on the specific product, experience, system, component, animation, repository, or workflow I name.
6
-
7
- Clearly separate:
8
-
9
- * directly observed behavior
10
- * strong inferences
11
- * possible reconstruction choices
12
- * unknown details
13
-
14
- Explain the transferable patterns and produce a practical reconstruction blueprint.
15
-
16
- Do not copy protected assets or claim unsupported knowledge about private implementation details.
17
-
18
- Do not modify project files, create feature specs, install dependencies, or implement code unless I explicitly invoke the appropriate Pathfinder skill afterward.
@@ -1,13 +0,0 @@
1
- Use `skills/reflect/SKILL.md`.
2
-
3
- Reflect on the completed work I name.
4
-
5
- Reconstruct what actually happened from repository evidence, then classify each finding as PROJECT, WORKFLOW CANDIDATE, or NOISE.
6
-
7
- Only propose a Pathfinder change when the lesson would still hold in another language, framework, and business domain, and is not already covered by an existing skill or document.
8
-
9
- Prefer the smallest durable improvement. Concluding that Pathfinder should not change is a valid result.
10
-
11
- Then make one bounded pass over the reflection itself. Report `No Reflect improvement proposed.` unless evidence shows the process genuinely failed to do its job. Do not recurse further.
12
-
13
- Do not modify Pathfinder, `AGENTS.md`, skills, prompts, or project files as part of reflection. Propose the changes and wait for my decision.
@@ -1,15 +0,0 @@
1
- Use `skills/debug-issue/SKILL.md`.
2
-
3
- Debug the failure I describe.
4
-
5
- Establish expected behavior, actual behavior, and reproduction status before proposing any fix.
6
-
7
- Read only the code, logs, tests, and configuration relevant to the failure. Do not refactor unrelated code while the cause is still unknown.
8
-
9
- State a small ranked set of hypotheses, then test the cheapest one that meaningfully reduces uncertainty. Change one explanatory variable at a time.
10
-
11
- Do not call something the root cause because the symptom disappeared. If the evidence only supports a probable cause or a workaround, say so.
12
-
13
- Apply the smallest justified fix, then verify against the original failure and the nearby behavior it could have affected. Remove temporary instrumentation.
14
-
15
- Stop and report rather than thrashing when the evidence runs out, the reproduction is too unstable, or the fix would require an architectural, dependency, security, or destructive change I have not approved. Preserve what has already been ruled out.