create-pathfinder 1.3.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 (62) hide show
  1. package/AGENTS.md +7 -0
  2. package/CLAUDE.md +71 -0
  3. package/LICENSE +21 -0
  4. package/README.md +60 -0
  5. package/bin/create-pathfinder.mjs +8 -0
  6. package/context/ai-interaction.md +76 -0
  7. package/context/coding-standards.md +72 -0
  8. package/context/current-feature.md +46 -0
  9. package/context/features/example-feature-spec.md +99 -0
  10. package/context/history.md +14 -0
  11. package/context/learning/learner-profile.md +35 -0
  12. package/context/learning/lessons/.gitkeep +0 -0
  13. package/context/learning/progress.md +28 -0
  14. package/context/project-overview.md +198 -0
  15. package/copy-list.json +11 -0
  16. package/package.json +51 -0
  17. package/prompts/01-kickstart-project.md +1 -0
  18. package/prompts/01-teach-current-feature.md +9 -0
  19. package/prompts/02-debate-me.md +1 -0
  20. package/prompts/02-quiz-current-feature.md +7 -0
  21. package/prompts/03-challenge-current-feature.md +7 -0
  22. package/prompts/03-prototype.md +1 -0
  23. package/prompts/04-teach-current-architecture.md +7 -0
  24. package/prompts/04-to-specs.md +1 -0
  25. package/prompts/05-learning-review.md +5 -0
  26. package/prompts/05-load-feature.md +1 -0
  27. package/prompts/06-start-feature.md +1 -0
  28. package/prompts/07-review-feature.md +1 -0
  29. package/prompts/08-complete-feature.md +1 -0
  30. package/prompts/09-learn-feature.md +1 -0
  31. package/prompts/10-learn-codebase.md +1 -0
  32. package/prompts/11-handoff.md +1 -0
  33. package/prompts/12-skillsmith.md +1 -0
  34. package/prompts/13-reverse-engineer.md +18 -0
  35. package/prompts/14-reflect.md +13 -0
  36. package/skills/challenge-me/SKILL.md +81 -0
  37. package/skills/complete-feature/SKILL.md +17 -0
  38. package/skills/debate-me/SKILL.md +82 -0
  39. package/skills/handoff/SKILL.md +12 -0
  40. package/skills/kickstart-pathfinder/SKILL.md +49 -0
  41. package/skills/learn-codebase/SKILL.md +33 -0
  42. package/skills/learn-feature/SKILL.md +48 -0
  43. package/skills/learning-review/SKILL.md +76 -0
  44. package/skills/load-feature/SKILL.md +17 -0
  45. package/skills/prototype/SKILL.md +46 -0
  46. package/skills/quiz-me/SKILL.md +78 -0
  47. package/skills/reflect/SKILL.md +481 -0
  48. package/skills/reverse-engineer/SKILL.md +252 -0
  49. package/skills/review-feature/SKILL.md +25 -0
  50. package/skills/skillsmith/SKILL.md +14 -0
  51. package/skills/start-feature/SKILL.md +25 -0
  52. package/skills/teach-architecture/SKILL.md +79 -0
  53. package/skills/teach-feature/SKILL.md +110 -0
  54. package/skills/to-specs/SKILL.md +45 -0
  55. package/src/cli.mjs +156 -0
  56. package/src/install.mjs +99 -0
  57. package/src/kit.mjs +94 -0
  58. package/templates/CHANGELOG.template.md +11 -0
  59. package/templates/feature-spec.template.md +99 -0
  60. package/templates/lesson.template.md +71 -0
  61. package/templates/progress-entry.template.md +23 -0
  62. package/templates/project-overview.template.md +198 -0
package/src/cli.mjs ADDED
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Command-line surface: parse arguments, refuse unsafe situations, report.
3
+ *
4
+ * No CLI framework. The flag set is four booleans and the whole parser is a
5
+ * loop; a dependency tree for that would be indefensible in a project whose
6
+ * identity is "not a framework."
7
+ */
8
+
9
+ import { findGitRoot, findKitRoot, COPY_LIST } from "./kit.mjs";
10
+ import { applyPlan, planInstall } from "./install.mjs";
11
+
12
+ const USAGE = `Usage: npx create-pathfinder [options]
13
+
14
+ Installs the Pathfinder workflow kit into the current Git repository.
15
+
16
+ Copies: ${COPY_LIST.join(", ")}
17
+
18
+ Options:
19
+ --dry-run Report what would be written; change nothing.
20
+ --force Overwrite files that already exist. Off by default.
21
+ -h, --help Show this message.
22
+ `;
23
+
24
+ export function run(argv, { cwd, out, err }) {
25
+ const options = parseArguments(argv);
26
+
27
+ if (options.error) {
28
+ err(`create-pathfinder: ${options.error}\n\n${USAGE}`);
29
+ return 2;
30
+ }
31
+
32
+ if (options.help) {
33
+ out(USAGE);
34
+ return 0;
35
+ }
36
+
37
+ // Refused rather than allowed with a warning: this tool writes several
38
+ // hundred files, and without version control the user has no way to inspect
39
+ // or undo what it did.
40
+ const gitRoot = findGitRoot(cwd);
41
+ if (gitRoot === null) {
42
+ err(
43
+ `create-pathfinder: ${cwd} is not inside a Git repository.\n\n` +
44
+ "The kit is installed into version control so you can review the\n" +
45
+ "files it adds and undo them if you change your mind. Run `git init`\n" +
46
+ "here first, or cd into an existing repository, then run this again.\n",
47
+ );
48
+ return 1;
49
+ }
50
+
51
+ const kitRoot = findKitRoot();
52
+ if (kitRoot === null) {
53
+ err(
54
+ "create-pathfinder: this package is missing the kit files it should copy.\n" +
55
+ "That is a packaging bug, not something you did. Please report it at\n" +
56
+ "https://github.com/rikilamadrid/pathfinder/issues\n",
57
+ );
58
+ return 1;
59
+ }
60
+
61
+ if (kitRoot === gitRoot) {
62
+ err(
63
+ "create-pathfinder: this is the Pathfinder kit repository itself.\n" +
64
+ "There is nothing to install here. Run it in the project you want the\n" +
65
+ "kit copied into.\n",
66
+ );
67
+ return 1;
68
+ }
69
+
70
+ const plan = planInstall(kitRoot, cwd, { force: options.force });
71
+ const result = applyPlan(plan, { dryRun: options.dryRun });
72
+
73
+ report({ result, plan, cwd, gitRoot, options, out, err });
74
+ return result.errors.length > 0 ? 1 : 0;
75
+ }
76
+
77
+ function parseArguments(argv) {
78
+ const options = { dryRun: false, force: false, help: false, error: null };
79
+
80
+ for (const argument of argv) {
81
+ switch (argument) {
82
+ case "--dry-run":
83
+ options.dryRun = true;
84
+ break;
85
+ case "--force":
86
+ options.force = true;
87
+ break;
88
+ case "-h":
89
+ case "--help":
90
+ options.help = true;
91
+ break;
92
+ default:
93
+ options.error = `unknown option \`${argument}\``;
94
+ return options;
95
+ }
96
+ }
97
+
98
+ return options;
99
+ }
100
+
101
+ /**
102
+ * Say what happened, in full.
103
+ *
104
+ * Skipped files are listed individually, not counted. The whole promise of the
105
+ * default mode is that it left your work alone, and a bare "42 skipped" does
106
+ * not let anyone check that claim.
107
+ */
108
+ function report({ result, plan, cwd, gitRoot, options, out, err }) {
109
+ const lines = [];
110
+ const verb = options.dryRun ? "Would install" : "Installed";
111
+
112
+ lines.push(`${verb} the Pathfinder kit into ${cwd}`);
113
+ if (gitRoot !== cwd) {
114
+ lines.push(`Note: the repository root is ${gitRoot}, not this directory.`);
115
+ }
116
+ lines.push("");
117
+
118
+ const written = options.dryRun ? plan.filter((i) => i.status === "write").length : result.written;
119
+ lines.push(` ${written} file${plural(written)} ${options.dryRun ? "to write" : "written"}`);
120
+
121
+ if (result.overwritten > 0) {
122
+ lines.push(` ${result.overwritten} file${plural(result.overwritten)} overwritten (--force)`);
123
+ }
124
+
125
+ const skipped = plan.filter((item) => item.status === "skip");
126
+ if (skipped.length > 0) {
127
+ lines.push(` ${skipped.length} file${plural(skipped.length)} left untouched because they already exist:`);
128
+ for (const item of skipped) lines.push(` ${item.relativePath}`);
129
+ lines.push("");
130
+ lines.push(" Nothing above was modified. Re-run with --force to replace them.");
131
+ }
132
+
133
+ if (written === 0 && skipped.length === plan.length) {
134
+ lines.push("");
135
+ lines.push("The kit is already installed here.");
136
+ } else {
137
+ lines.push("");
138
+ lines.push("Next step — give your agent this prompt:");
139
+ lines.push("");
140
+ lines.push(" Use skills/kickstart-pathfinder/SKILL.md. Help me initialize this");
141
+ lines.push(" project. Do not install packages or write product code yet.");
142
+ }
143
+
144
+ out(lines.join("\n") + "\n");
145
+
146
+ if (result.errors.length > 0) {
147
+ const failures = result.errors
148
+ .map((error) => ` ${error.relativePath}: ${error.message}`)
149
+ .join("\n");
150
+ err(`\ncreate-pathfinder: ${result.errors.length} file${plural(result.errors.length)} could not be written:\n${failures}\n`);
151
+ }
152
+ }
153
+
154
+ function plural(count) {
155
+ return count === 1 ? "" : "s";
156
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * The copier.
3
+ *
4
+ * Split in two on purpose. `planInstall` decides what would happen and touches
5
+ * nothing; `applyPlan` carries a plan out. That split is what makes --dry-run
6
+ * honest — it runs the identical planning code the real install runs, rather
7
+ * than a parallel description of it that can drift.
8
+ */
9
+
10
+ import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "node:fs";
11
+ import { dirname, join, relative, sep } from "node:path";
12
+
13
+ import { COPY_LIST, isExcluded } from "./kit.mjs";
14
+
15
+ /**
16
+ * Decide the fate of every file in the kit. Reads only; writes nothing.
17
+ *
18
+ * Existing files are detected one file at a time rather than one top-level
19
+ * entry at a time. A project that already has `context/` gets the files it is
20
+ * missing and keeps the ones it wrote, instead of the whole directory being
21
+ * declared present and skipped.
22
+ *
23
+ * @returns {{relativePath: string, source: string, destination: string,
24
+ * status: "write" | "skip" | "overwrite"}[]}
25
+ */
26
+ export function planInstall(kitRoot, targetRoot, { force = false } = {}) {
27
+ const plan = [];
28
+
29
+ for (const entry of COPY_LIST) {
30
+ for (const source of walkFiles(join(kitRoot, entry))) {
31
+ const relativePath = relative(kitRoot, source).split(sep).join("/");
32
+ const destination = join(targetRoot, relativePath);
33
+ const exists = existsSync(destination);
34
+
35
+ plan.push({
36
+ relativePath,
37
+ source,
38
+ destination,
39
+ status: !exists ? "write" : force ? "overwrite" : "skip",
40
+ });
41
+ }
42
+ }
43
+
44
+ return plan.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
45
+ }
46
+
47
+ /**
48
+ * Carry out a plan.
49
+ *
50
+ * Failures are collected rather than thrown, so one unwritable path reports
51
+ * itself alongside everything that did succeed instead of aborting the run
52
+ * halfway with no summary. Entries marked `skip` are never opened.
53
+ *
54
+ * @returns {{written: number, skipped: number, overwritten: number,
55
+ * errors: {relativePath: string, message: string}[]}}
56
+ */
57
+ export function applyPlan(plan, { dryRun = false } = {}) {
58
+ const result = { written: 0, skipped: 0, overwritten: 0, errors: [] };
59
+
60
+ for (const item of plan) {
61
+ if (item.status === "skip") {
62
+ result.skipped += 1;
63
+ continue;
64
+ }
65
+
66
+ if (!dryRun) {
67
+ try {
68
+ mkdirSync(dirname(item.destination), { recursive: true });
69
+ copyFileSync(item.source, item.destination);
70
+ } catch (error) {
71
+ result.errors.push({
72
+ relativePath: item.relativePath,
73
+ message: error.message,
74
+ });
75
+ continue;
76
+ }
77
+ }
78
+
79
+ if (item.status === "overwrite") result.overwritten += 1;
80
+ else result.written += 1;
81
+ }
82
+
83
+ return result;
84
+ }
85
+
86
+ /** Every file under `path`, recursively, minus junk. A file yields itself. */
87
+ function* walkFiles(path) {
88
+ const stats = statSync(path);
89
+
90
+ if (!stats.isDirectory()) {
91
+ yield path;
92
+ return;
93
+ }
94
+
95
+ for (const child of readdirSync(path).sort()) {
96
+ if (isExcluded(child)) continue;
97
+ yield* walkFiles(join(path, child));
98
+ }
99
+ }
package/src/kit.mjs ADDED
@@ -0,0 +1,94 @@
1
+ /**
2
+ * What the kit is, and where to find it.
3
+ *
4
+ * The copy list is the single fact this whole tool exists to act on. It is
5
+ * duplicated in exactly two other places — the README quickstart and
6
+ * NOT_A_FRAMEWORK.md — and nowhere else. Nothing here embeds a copy of the
7
+ * kit's content: the real directories are read at install time.
8
+ */
9
+
10
+ import { existsSync, readFileSync } from "node:fs";
11
+ import { dirname, join, resolve } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+
14
+ const HERE = dirname(fileURLToPath(import.meta.url));
15
+
16
+ /**
17
+ * Everything a destination project receives, and nothing else.
18
+ *
19
+ * Read from copy-list.json rather than declared here, so the list is data with
20
+ * one home instead of a value restated in every file that needs it. The
21
+ * validator reads the same file and checks the README and npm's `files`
22
+ * against it; nothing has to parse this source code to learn the list.
23
+ *
24
+ * Deliberately absent from it: CHANGELOG.md and README.md (the kit's own
25
+ * history and front page, not the project's), .github/, assets/, site/,
26
+ * packages/, and .features/. A destination project that wants a changelog
27
+ * starts from templates/CHANGELOG.template.md instead.
28
+ *
29
+ * Read with readFileSync rather than a JSON import, which still requires an
30
+ * import attribute on the Node 18 this package supports.
31
+ */
32
+ export const COPY_LIST = Object.freeze(
33
+ JSON.parse(readFileSync(join(HERE, "..", "copy-list.json"), "utf8")).entries,
34
+ );
35
+
36
+ /**
37
+ * Junk that is never part of the kit, matched by basename at any depth.
38
+ *
39
+ * These are git-ignored in the kit repository, so they exist in a maintainer's
40
+ * working tree but not in version control — and npm strips them from the
41
+ * published tarball on its own. Without this list the installer would copy
42
+ * `skills/.DS_Store` when run from a checkout and not when run from npm, which
43
+ * is the worst kind of difference: invisible, and only on someone else's
44
+ * machine.
45
+ */
46
+ const EXCLUDED = new Set([".DS_Store", "Thumbs.db", "__MACOSX", ".git"]);
47
+
48
+ export function isExcluded(basename) {
49
+ return EXCLUDED.has(basename) || basename.startsWith("._");
50
+ }
51
+
52
+ const PACKAGE_ROOT = resolve(HERE, "..");
53
+
54
+ /**
55
+ * Locate the kit directories this tool should copy from.
56
+ *
57
+ * Two layouts are possible and both are checked, nearest first:
58
+ *
59
+ * 1. Published package — the kit ships inside the tarball alongside this
60
+ * code. How it gets there is chunk 2's decision; this resolver only
61
+ * requires that it ends up at the package root.
62
+ * 2. Development — running from a checkout of the kit itself, where the
63
+ * package sits at packages/create-pathfinder/ and the kit is two levels up.
64
+ *
65
+ * Returns null when neither candidate holds a complete kit, which is a bug in
66
+ * the package rather than a user error, and is reported as such.
67
+ */
68
+ export function findKitRoot() {
69
+ const candidates = [PACKAGE_ROOT, resolve(PACKAGE_ROOT, "..", "..")];
70
+ return candidates.find(isKitRoot) ?? null;
71
+ }
72
+
73
+ function isKitRoot(directory) {
74
+ return COPY_LIST.every((entry) => existsSync(join(directory, entry)));
75
+ }
76
+
77
+ /**
78
+ * Find the Git repository containing `startDirectory`, or null.
79
+ *
80
+ * Walks the filesystem rather than shelling out to `git`, so the tool works
81
+ * without the git binary on PATH and cannot be confused by its exit codes.
82
+ * `.git` is tested with existsSync rather than as a directory because linked
83
+ * worktrees and submodules use a `.git` *file* pointing elsewhere.
84
+ */
85
+ export function findGitRoot(startDirectory) {
86
+ let current = resolve(startDirectory);
87
+
88
+ for (;;) {
89
+ if (existsSync(join(current, ".git"))) return current;
90
+ const parent = dirname(current);
91
+ if (parent === current) return null;
92
+ current = parent;
93
+ }
94
+ }
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ Use this file only when the destination project adopts a changelog.
4
+
5
+ ## [Unreleased]
6
+
7
+ ### Added
8
+
9
+ ### Changed
10
+
11
+ ### Fixed
@@ -0,0 +1,99 @@
1
+ # [Feature Name]
2
+
3
+ ## Status
4
+
5
+ Not Started
6
+
7
+ ## Overview
8
+
9
+ Describe the smallest coherent outcome, where it fits, and why it matters.
10
+
11
+ ## Problem
12
+
13
+ - What is missing, risky, confusing, inaccessible, unreliable, or inefficient?
14
+ - Who or what is affected?
15
+ - Why does this matter now?
16
+
17
+ ## Goal
18
+
19
+ State one user-visible or system-verifiable outcome.
20
+
21
+ ## Dependencies
22
+
23
+ - Earlier feature, system, decision, prototype, or `None`.
24
+
25
+ ## Requirements
26
+
27
+ - Requirement 1
28
+ - Requirement 2
29
+ - Requirement 3
30
+ - Include relevant failure, permission, loading, empty, retry, responsive, accessibility, operational, or compatibility states only when applicable.
31
+
32
+ ## Out of Scope
33
+
34
+ - Explicit exclusion
35
+ - Later feature, if known
36
+ - Unrelated refactors, dependencies, or polish
37
+
38
+ ## Experience or Operational Notes — When Applicable
39
+
40
+ - User interaction, system behavior, responsive behavior, accessibility, observability, performance, security, or operational expectations.
41
+
42
+ ## Technical Notes
43
+
44
+ Likely areas:
45
+
46
+ - `[specific path or bounded area]`
47
+
48
+ Implementation constraints:
49
+
50
+ - Follow project context and approved prototype direction.
51
+ - Do not silently resolve open architecture decisions.
52
+ - Keep prototype and production code boundaries explicit.
53
+
54
+ ## Context Boundary
55
+
56
+ Read:
57
+
58
+ - `[specific context and code]`
59
+
60
+ Avoid loading:
61
+
62
+ - `[unrelated systems, old specs, generated output]`
63
+
64
+ Split this feature further if its required context is not focused enough for reliable implementation and verification.
65
+
66
+ ## Delivery Chunks
67
+
68
+ 1. `[stable, verifiable increment]`
69
+ 2. `[stable, verifiable increment]`
70
+ 3. `[optional stable increment]`
71
+
72
+ Each chunk should leave the project stable.
73
+
74
+ ## Acceptance Criteria
75
+
76
+ - The defined outcome works end to end within scope.
77
+ - Relevant failure and edge behavior is handled.
78
+ - Applicable quality requirements are met.
79
+ - Required automated/manual checks pass.
80
+ - The work can be reviewed independently.
81
+
82
+ ## Verification
83
+
84
+ - Manual or operational checks:
85
+ - Automated checks:
86
+ - Edge cases:
87
+ - Quality checks:
88
+
89
+ ## Learning Targets
90
+
91
+ - Concepts worth explaining after completion:
92
+ - Diagram or demonstration opportunity:
93
+ - Quiz ideas:
94
+
95
+ ## Suggested Delivery Metadata
96
+
97
+ - Git action: `[follow project workflow]`
98
+ - Suggested branch, when applicable: `[project naming convention]`
99
+ - Suggested commit, when applicable: `[project convention]`
@@ -0,0 +1,71 @@
1
+ # Lesson — [Feature Name]
2
+
3
+ - Date:
4
+ - Branch:
5
+ - Feature spec:
6
+ - Commit or diff range:
7
+ - Difficulty:
8
+ - Estimated review time:
9
+
10
+ ## 1. Feature in One Minute
11
+
12
+ What changed, why it exists, and the user or system value it creates.
13
+
14
+ ## 2. Mental Model
15
+
16
+ Explain the feature through one clear model. Include a small Mermaid diagram when useful.
17
+
18
+ ## 3. Execution and Data Flow
19
+
20
+ Trace one representative interaction from input to visible result.
21
+
22
+ ## 4. Important Files
23
+
24
+ | File | Responsibility | Why it matters |
25
+ | --- | --- | --- |
26
+
27
+ ## 5. Key Decisions and Tradeoffs
28
+
29
+ For each meaningful decision:
30
+
31
+ - Decision
32
+ - Evidence in the repository
33
+ - Benefit
34
+ - Cost
35
+ - Credible alternative
36
+ - When the alternative would be better
37
+
38
+ ## 6. Concepts Worth Retaining
39
+
40
+ Limit this to the most transferable concepts.
41
+
42
+ ## 7. Quality Lens
43
+
44
+ ### Testing
45
+
46
+ ### Accessibility
47
+
48
+ ### Performance
49
+
50
+ ### Maintainability
51
+
52
+ ### Error and edge states
53
+
54
+ ## 8. Production-Scale Gap
55
+
56
+ What would need to change for higher traffic, larger datasets, multiple teams, stricter security, or real-time collaboration?
57
+
58
+ ## 9. Interview Preparation
59
+
60
+ - Likely question:
61
+ - Strong answer outline:
62
+ - Follow-up question:
63
+ - Vocabulary to use carefully:
64
+
65
+ ## 10. Check Your Understanding
66
+
67
+ Three short retrieval questions. Do not include answers here.
68
+
69
+ ## 11. Suggested Next Action
70
+
71
+ Choose one: quiz, challenge, architecture lesson, spaced review, or no further work.
@@ -0,0 +1,23 @@
1
+ ## Progress Update — [Date]
2
+
3
+ ### Lesson
4
+
5
+ - Feature:
6
+ - Lesson file:
7
+ - Evidence reviewed:
8
+
9
+ ### Concepts
10
+
11
+ | Concept | Previous | New | Evidence |
12
+ | --- | --- | --- | --- |
13
+
14
+ ### Misconceptions or Gaps
15
+
16
+ - Gap:
17
+ - Correction:
18
+ - Recommended reinforcement:
19
+
20
+ ### Next Review
21
+
22
+ - Topic:
23
+ - Suggested timing or trigger: