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.
- package/AGENTS.md +4 -0
- package/CLAUDE.md +7 -1
- package/README.md +97 -9
- package/bin/create-pathfinder.mjs +32 -4
- package/context/features/example-feature-spec.md +5 -1
- package/context/project-overview.md +16 -3
- package/copy-list.json +0 -1
- package/package.json +2 -2
- package/skills/reflect/SKILL.md +46 -46
- package/skills/reverse-engineer/SKILL.md +2 -0
- package/skills/to-specs/SKILL.md +1 -1
- package/src/cli.mjs +765 -34
- package/src/clipboard.mjs +134 -0
- package/src/detect.mjs +183 -0
- package/src/editor.mjs +136 -0
- package/src/git.mjs +58 -0
- package/src/harnesses/adapter.mjs +288 -0
- package/src/harnesses/index.mjs +122 -0
- package/src/install.mjs +209 -1
- package/src/kickstart-prompt.mjs +81 -0
- package/src/prompt.mjs +307 -0
- package/templates/project-overview.template.md +16 -3
- package/prompts/01-kickstart-project.md +0 -1
- package/prompts/01-teach-current-feature.md +0 -9
- package/prompts/02-debate-me.md +0 -1
- package/prompts/02-quiz-current-feature.md +0 -7
- package/prompts/03-challenge-current-feature.md +0 -7
- package/prompts/03-prototype.md +0 -1
- package/prompts/04-teach-current-architecture.md +0 -7
- package/prompts/04-to-specs.md +0 -1
- package/prompts/05-learning-review.md +0 -5
- package/prompts/05-load-feature.md +0 -1
- package/prompts/06-start-feature.md +0 -1
- package/prompts/07-review-feature.md +0 -1
- package/prompts/08-complete-feature.md +0 -1
- package/prompts/09-learn-feature.md +0 -1
- package/prompts/10-learn-codebase.md +0 -1
- package/prompts/11-handoff.md +0 -1
- package/prompts/12-skillsmith.md +0 -1
- package/prompts/13-reverse-engineer.md +0 -18
- package/prompts/14-reflect.md +0 -13
- 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
|
-
#
|
|
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
|
-
|
|
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 |
|
|
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.
|
package/prompts/02-debate-me.md
DELETED
|
@@ -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.
|
package/prompts/03-prototype.md
DELETED
|
@@ -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.
|
package/prompts/04-to-specs.md
DELETED
|
@@ -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.
|
package/prompts/11-handoff.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Use `skills/handoff/SKILL.md` to create a compact factual handoff for the next agent or session.
|
package/prompts/12-skillsmith.md
DELETED
|
@@ -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.
|
package/prompts/14-reflect.md
DELETED
|
@@ -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.
|