phronesis 1.0.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 (142) hide show
  1. package/README.md +66 -0
  2. package/package.json +34 -0
  3. package/src/act.js +61 -0
  4. package/src/active-context.js +56 -0
  5. package/src/adapter.js +386 -0
  6. package/src/cli.js +1893 -0
  7. package/src/codex-hooks-schema.js +92 -0
  8. package/src/compile.js +513 -0
  9. package/src/doctor.js +303 -0
  10. package/src/due.js +271 -0
  11. package/src/eval.js +467 -0
  12. package/src/event.js +212 -0
  13. package/src/export.js +136 -0
  14. package/src/guard.js +89 -0
  15. package/src/hooks.js +230 -0
  16. package/src/ingest.js +965 -0
  17. package/src/init.js +150 -0
  18. package/src/layout.js +394 -0
  19. package/src/links.js +83 -0
  20. package/src/lint.js +58 -0
  21. package/src/profile.js +116 -0
  22. package/src/prompts.js +26 -0
  23. package/src/registry.js +190 -0
  24. package/src/scaffold.js +134 -0
  25. package/src/search.js +139 -0
  26. package/src/skill-discovery.js +117 -0
  27. package/src/skills-bump.js +885 -0
  28. package/src/skills.js +355 -0
  29. package/src/subscription.js +5 -0
  30. package/src/validate.js +152 -0
  31. package/templates/claude/settings.json +86 -0
  32. package/templates/codex/INDEX.md +68 -0
  33. package/templates/codex/seed/ai-practice/compile-dont-just-capture.md +42 -0
  34. package/templates/codex/seed/ai-practice/design-for-the-distribution.md +42 -0
  35. package/templates/codex/seed/ai-practice/from-spec-author-to-outcome-curator.md +43 -0
  36. package/templates/codex/seed/ai-practice/recall-builds-trust-when-calibrated.md +70 -0
  37. package/templates/codex/seed/ai-practice/the-eval-is-the-spec.md +42 -0
  38. package/templates/codex/seed/ai-practice/the-gold-dust-leaves-no-trace.md +72 -0
  39. package/templates/codex/seed/ai-practice/the-model-is-not-the-moat.md +40 -0
  40. package/templates/codex/seed/ai-practice/when-generation-is-cheap-judgment-is-the-work.md +39 -0
  41. package/templates/codex/seed/judgment/contextual-stewardship-is-the-senior-skill.md +50 -0
  42. package/templates/codex/seed/judgment/decision-quality-is-distinct-from-outcome-quality.md +39 -0
  43. package/templates/codex/seed/judgment/embrace-reality-ego-and-blind-spots.md +38 -0
  44. package/templates/codex/seed/judgment/groups-seek-consensus-individuals-seek-truth.md +44 -0
  45. package/templates/codex/seed/judgment/heresies-are-where-the-edge-lives.md +42 -0
  46. package/templates/codex/seed/judgment/judgment-compounds-through-honest-reflection.md +38 -0
  47. package/templates/codex/seed/judgment/phronesis-is-judgment-in-the-particular.md +67 -0
  48. package/templates/codex/seed/judgment/show-me-the-apparatus.md +40 -0
  49. package/templates/codex/seed/judgment/small-judgment-gaps-compound-under-leverage.md +37 -0
  50. package/templates/codex/seed/judgment/taste-is-compressed-judgment.md +38 -0
  51. package/templates/codex/seed/judgment/trust-taste-only-where-feedback-is-valid.md +42 -0
  52. package/templates/codex/seed/judgment/weigh-the-reasoning-not-the-recommendation.md +43 -0
  53. package/templates/codex/seed/leadership/a-managers-output-is-the-teams-output.md +35 -0
  54. package/templates/codex/seed/leadership/most-team-problems-are-structural.md +40 -0
  55. package/templates/codex/seed/leadership/next-play.md +36 -0
  56. package/templates/codex/seed/leadership/psychological-safety.md +39 -0
  57. package/templates/codex/seed/leadership/right-size-your-teams.md +38 -0
  58. package/templates/codex/seed/leadership/spend-time-on-high-leverage-activities.md +36 -0
  59. package/templates/codex/seed/leadership/talent-density-is-the-master-variable.md +39 -0
  60. package/templates/codex/seed/leadership/under-pressure-calm-is-contagious.md +36 -0
  61. package/templates/codex/seed/leadership/your-best-alternative-is-your-leverage.md +37 -0
  62. package/templates/codex/seed/pm/a-problem-well-stated-is-half-solved.md +39 -0
  63. package/templates/codex/seed/pm/a-real-strategy-makes-planning-trivial.md +40 -0
  64. package/templates/codex/seed/pm/barrels-and-ammunition.md +39 -0
  65. package/templates/codex/seed/pm/develop-taste-in-your-beliefs.md +38 -0
  66. package/templates/codex/seed/pm/hire-for-progress.md +44 -0
  67. package/templates/codex/seed/pm/jobs-are-stable-solutions-churn.md +39 -0
  68. package/templates/codex/seed/pm/level-by-the-ambiguity-someone-can-own.md +39 -0
  69. package/templates/codex/seed/pm/listen-before-you-prescribe.md +38 -0
  70. package/templates/codex/seed/pm/live-in-the-future-and-build-whats-missing.md +38 -0
  71. package/templates/codex/seed/pm/match-effort-to-leverage.md +37 -0
  72. package/templates/codex/seed/pm/not-my-fault-but-my-problem.md +40 -0
  73. package/templates/codex/seed/pm/pick-the-job-then-refuse-everything-outside-it.md +37 -0
  74. package/templates/codex/seed/pm/retention-sets-the-ceiling.md +38 -0
  75. package/templates/codex/seed/pm/scale-with-leaders-not-process.md +38 -0
  76. package/templates/codex/seed/pm/solution-discovery-over-problem-validation.md +38 -0
  77. package/templates/codex/seed/pm/tenfold-better-on-a-painful-problem.md +40 -0
  78. package/templates/codex-surface/README.md +73 -0
  79. package/templates/codex-surface/hooks/_resolve.sh +36 -0
  80. package/templates/codex-surface/hooks/recall-guard.sh +30 -0
  81. package/templates/codex-surface/hooks/session-start.sh +18 -0
  82. package/templates/codex-surface/hooks/session-sweep-precompact.sh +20 -0
  83. package/templates/codex-surface/hooks/session-sweep-subagent.sh +23 -0
  84. package/templates/codex-surface/hooks/session-sweep.sh +19 -0
  85. package/templates/codex-surface/hooks.json +69 -0
  86. package/templates/domains/pm/AGENTS.md +111 -0
  87. package/templates/domains/pm/MEMORY.md +7 -0
  88. package/templates/domains/pm/USER.md +8 -0
  89. package/templates/phronesis-hooks/compile-active-context.sh +38 -0
  90. package/templates/phronesis-hooks/recall-guard.sh +103 -0
  91. package/templates/phronesis-hooks/session-start.sh +90 -0
  92. package/templates/phronesis-hooks/session-sweep.sh +140 -0
  93. package/templates/phronesis-hooks/skill-lifecycle.sh +37 -0
  94. package/templates/skills/compile/SKILL.md +279 -0
  95. package/templates/skills/compile/evals/rubric.md +75 -0
  96. package/templates/skills/decision-log/SKILL.md +156 -0
  97. package/templates/skills/decision-log/evals/rubric.md +97 -0
  98. package/templates/skills/extract-gold-dust/SKILL.md +311 -0
  99. package/templates/skills/extract-gold-dust/evals/rubric.md +52 -0
  100. package/templates/skills/ingest/SKILL.md +204 -0
  101. package/templates/skills/ingest/evals/rubric.md +117 -0
  102. package/templates/skills/lint/SKILL.md +246 -0
  103. package/templates/skills/lint/evals/rubric.md +139 -0
  104. package/templates/skills/meeting-prep/SKILL.md +195 -0
  105. package/templates/skills/meeting-prep/evals/rubric.md +93 -0
  106. package/templates/skills/onboard/SKILL.md +258 -0
  107. package/templates/skills/onboard/evals/rubric.md +60 -0
  108. package/templates/skills/prd-draft/SKILL.md +193 -0
  109. package/templates/skills/prd-draft/evals/rubric.md +38 -0
  110. package/templates/skills/prep-extraction/SKILL.md +217 -0
  111. package/templates/skills/prep-extraction/evals/rubric.md +45 -0
  112. package/templates/skills/recall/SKILL.md +155 -0
  113. package/templates/skills/recall/evals/rubric.md +37 -0
  114. package/templates/skills/research/SKILL.md +247 -0
  115. package/templates/skills/research/evals/rubric.md +139 -0
  116. package/templates/skills/stakeholder-update/SKILL.md +163 -0
  117. package/templates/skills/stakeholder-update/evals/rubric.md +111 -0
  118. package/vendor/core/package.json +25 -0
  119. package/vendor/core/src/action-registry.js +459 -0
  120. package/vendor/core/src/action-writers.js +201 -0
  121. package/vendor/core/src/actions.js +2140 -0
  122. package/vendor/core/src/active-context.js +167 -0
  123. package/vendor/core/src/artifacts.js +301 -0
  124. package/vendor/core/src/content-digest.js +21 -0
  125. package/vendor/core/src/contract.js +245 -0
  126. package/vendor/core/src/event-log.js +338 -0
  127. package/vendor/core/src/guard.js +398 -0
  128. package/vendor/core/src/hook-executor.js +527 -0
  129. package/vendor/core/src/index.js +180 -0
  130. package/vendor/core/src/ingest.js +286 -0
  131. package/vendor/core/src/install-root.js +42 -0
  132. package/vendor/core/src/layout.js +13 -0
  133. package/vendor/core/src/link-index.js +198 -0
  134. package/vendor/core/src/lint.js +368 -0
  135. package/vendor/core/src/schedule.js +535 -0
  136. package/vendor/core/src/search.js +534 -0
  137. package/vendor/core/src/session-hooks.js +129 -0
  138. package/vendor/core/src/skill-lifecycle.js +479 -0
  139. package/vendor/core/src/skill-registry.js +524 -0
  140. package/vendor/core/src/subscription.js +84 -0
  141. package/vendor/core/src/validate-object.js +62 -0
  142. package/vendor/core/src/workspace-scan.js +125 -0
@@ -0,0 +1,201 @@
1
+ // Action-writer helpers — the mechanical half of the T2.2 action spine (core-0008).
2
+ // Everything here is shared by every deterministic action type so core-0009/0010 are
3
+ // "fill handlers + schemas + gate rows": slug derivation, frontmatter serialization,
4
+ // the D12 dated-append-block shape, and the stage→validate→rename single-dest writer
5
+ // (rules.md:167 — no output artifact is ever half-written). No business logic: which
6
+ // fields an action writes is the handler's job (actions.js); how bytes land is ours.
7
+
8
+ import { join, dirname } from 'node:path';
9
+ import { readFile, writeFile } from 'node:fs/promises';
10
+ import yaml from 'js-yaml';
11
+ import fse from 'fs-extra';
12
+ import { checkObjectContent } from './validate-object.js';
13
+ import { matchFrontmatterBlock } from './workspace-scan.js';
14
+
15
+ // Hand-rolled zero-dep slugify → kebab ASCII matching the contract's slug grammar
16
+ // (contract.js RE_SLUG). Deliberately NOT the npm `slugify` package — that stays an
17
+ // apps/cli init-only dep; @phronesis/core holds its deps to js-yaml + fs-extra.
18
+ // Diacritics fold to their base letter (NFKD strip); anything else non-alphanumeric
19
+ // collapses to a single hyphen. Returns '' when nothing survives — callers fail closed.
20
+ export function slugify(input) {
21
+ if (typeof input !== 'string') return '';
22
+ return input
23
+ .normalize('NFKD')
24
+ .replace(/[\u0300-\u036f]/g, '') // combining marks left by NFKD
25
+ .toLowerCase()
26
+ .replace(/[^a-z0-9]+/g, '-')
27
+ .replace(/^-+|-+$/g, '');
28
+ }
29
+
30
+ // Serialize parsed frontmatter back to a `---` block. JSON_SCHEMA mirrors how the
31
+ // validator *reads* (validate.js / link-index.js): no implicit type coercion, so a
32
+ // `2026-05-15` date stays the plain string it was given. Insertion order preserved.
33
+ export function serializeFrontmatter(fm) {
34
+ const body = yaml.dump(fm, { schema: yaml.JSON_SCHEMA, lineWidth: -1, noRefs: true });
35
+ return `---\n${body}---\n`;
36
+ }
37
+
38
+ // --- D12 — the named dated-append-block format ---------------------------------------
39
+ //
40
+ // ONE shared shape for every append action (`enrich_person` here; project-state and
41
+ // changelog appends in core-0009), so the writers never improvise differently and later
42
+ // lint/retrieval can rely on it. V1 shape, frozen in core-0008: a stable `## Log`
43
+ // section; each entry `### YYYY-MM-DD\n\n{body}\n`, newest appended at the section end.
44
+ // Producer-local for V1 — it graduates to a spec note only if T5.2/T5.3 parse it.
45
+ //
46
+ // KNOWN LIMITS (line-based): an entry body whose own line starts `## ` reads as a new
47
+ // H2 and ends the Log section for every later append; and a payload-supplied backdate
48
+ // lands at the section end regardless, so "newest last" holds only for stamp-dated
49
+ // entries. Both acceptable while the shape is producer-local; revisit if it graduates.
50
+
51
+ const LOG_HEADING = '## Log';
52
+
53
+ // Split an object's source into its frontmatter block and its body, BYTE-precisely —
54
+ // the read-modify-write actions (record_outcome, update_project_state's headline path,
55
+ // core-0010's tombstone) rewrite the frontmatter and must carry the body over verbatim,
56
+ // including any leading blank line. splitFrontmatter (workspace-scan) returns only the
57
+ // YAML source, so it cannot say where the body starts; this one can. Both ride the ONE
58
+ // shared block regex (matchFrontmatterBlock) — the validator and the writers must never
59
+ // disagree about where a frontmatter block ends. Returns null when the content has no
60
+ // frontmatter block.
61
+ export function splitObjectSource(content) {
62
+ const m = matchFrontmatterBlock(content);
63
+ if (!m) return null;
64
+ return { yamlSource: m[1], body: content.slice(m[0].length) };
65
+ }
66
+
67
+ export function formatLogEntry(dateStamp, body) {
68
+ return `### ${dateStamp}\n\n${String(body).trim()}\n`;
69
+ }
70
+
71
+ // Append one dated entry to the content's `## Log` section, creating the section at the
72
+ // end of the document if absent. Everything outside the inserted lines is preserved
73
+ // verbatim — prior bytes are never reflowed.
74
+ export function appendLogBlock(content, dateStamp, body) {
75
+ const entry = formatLogEntry(dateStamp, body);
76
+ const lines = content.split('\n');
77
+ const headingAt = lines.findIndex((l) => l.trimEnd() === LOG_HEADING);
78
+
79
+ if (headingAt === -1) {
80
+ const sep = content.endsWith('\n\n') ? '' : content.endsWith('\n') ? '\n' : '\n\n';
81
+ return `${content}${sep}${LOG_HEADING}\n\n${entry}`;
82
+ }
83
+
84
+ // Section end = just before the next H2, else end of document.
85
+ let insertAt = lines.length;
86
+ for (let i = headingAt + 1; i < lines.length; i += 1) {
87
+ if (/^##\s/.test(lines[i]) && !/^###/.test(lines[i])) {
88
+ insertAt = i;
89
+ break;
90
+ }
91
+ }
92
+ // Trim trailing blank lines inside the section so the entry lands flush, one blank
93
+ // line after whatever the section already holds.
94
+ let end = insertAt;
95
+ while (end > headingAt + 1 && lines[end - 1].trim() === '') end -= 1;
96
+ const inserted = [...lines.slice(0, end), '', ...entry.split('\n')];
97
+ // formatLogEntry ends with '\n' → its split leaves a trailing ''; that supplies the
98
+ // blank line before a following H2 (or the file's final newline at EOF).
99
+ return [...inserted, ...lines.slice(insertAt)].join('\n');
100
+ }
101
+
102
+ // --- The stage→validate→rename writer (rules.md:167; multi-dest since core-0010) -------
103
+ //
104
+ // Write every artifact to `<dest>.partial-<pid>-<random>` (never `.md`, so workspace
105
+ // scans ignore it), validate ALL staged bytes against the full ontology contract —
106
+ // frontmatter AND link existence, exactly what `phronesis validate` enforces — and only
107
+ // then commit the renames, in array order. Any failure before the first rename removes
108
+ // every temp and renames nothing.
109
+ //
110
+ // F2 (core-0010) — all-or-nothing INTENT, idempotent-convergence guarantee: true 2-phase
111
+ // commit is not achievable on a POSIX fs without a journal, so a failure BETWEEN renames
112
+ // leaves the earlier writes committed. Callers therefore order `writes` so the
113
+ // recoverable direction commits first (promote_to_codex: codex object before the source
114
+ // tombstone) and make a re-run converge: `skipIfIdentical` treats an existing
115
+ // destination with byte-identical content as already-committed (a no-op), so the re-run
116
+ // completes the remaining writes instead of failing on collision.
117
+ //
118
+ // Validation sees the POST-transaction state: every write's relPath counts as existing
119
+ // for sibling link checks (the tombstone's `superseded-by` link resolves against the
120
+ // in-flight codex object). Callers must order writes so link dependencies commit first.
121
+ //
122
+ // `overwrite: false` also makes the final rename itself refuse a destination that
123
+ // appeared after the pre-flight check — the no-overwrite policy holds across a race.
124
+ export async function stageValidateRenameAll({ installRoot, writes }) {
125
+ // The path a write is VALIDATED as (its canonical typed-object shape) can differ from
126
+ // where it is WRITTEN: a compile draft is validated as its canonical compiled/ path —
127
+ // so contextFor derives the right layer/type-dir context and checkObjectContent
128
+ // accepts it — but written atomically to .phronesis/drafts/ (core-0017 H3). A
129
+ // `.phronesis/drafts/…` write path is not a typed-object path, so validating it as
130
+ // itself would (correctly) fail "not a typed-object path"; validateRelPath is the fix.
131
+ // It defaults to relPath, so the six deterministic writers are unchanged (write path
132
+ // == validate path) and every link/sibling check sees exactly the same input.
133
+ const validatePathOf = (w) => w.validateRelPath || w.relPath;
134
+ const pending = new Set(writes.map(validatePathOf));
135
+ const staged = []; // { write, tmp } — tmp null when the write is an identical no-op
136
+ try {
137
+ for (const w of writes) {
138
+ const dest = join(installRoot, w.relPath);
139
+ if (!w.overwrite && (await fse.pathExists(dest))) {
140
+ if (w.skipIfIdentical && (await readFile(dest, 'utf8')) === w.content) {
141
+ staged.push({ write: w, tmp: null });
142
+ continue;
143
+ }
144
+ throw new Error(
145
+ `${w.relPath} already exists with different content — fail closed (never overwrite; resolve the destination out-of-band)`,
146
+ );
147
+ }
148
+ // pid + random: pid alone collides for two concurrent runAction calls in ONE
149
+ // process (the T4.1 daemon and adapter surfaces are the declared next callers) —
150
+ // one caller could validate and rename the other's bytes.
151
+ const tmp = `${dest}.partial-${process.pid}-${Math.random().toString(36).slice(2, 10)}`;
152
+ staged.push({ write: w, tmp });
153
+ await fse.ensureDir(dirname(dest));
154
+ await writeFile(tmp, w.content);
155
+ const stagedBytes = await readFile(tmp, 'utf8');
156
+ const errors = await checkObjectContent({
157
+ installRoot,
158
+ relPath: validatePathOf(w),
159
+ content: stagedBytes,
160
+ pending,
161
+ });
162
+ if (errors.length) {
163
+ throw new Error(
164
+ `staged artifact fails the ontology contract (${validatePathOf(w)}): ${errors.join('; ')}`,
165
+ );
166
+ }
167
+ }
168
+ for (const s of staged) {
169
+ if (s.tmp === null) continue;
170
+ await fse.move(s.tmp, join(installRoot, s.write.relPath), {
171
+ overwrite: s.write.overwrite === true,
172
+ });
173
+ s.tmp = null; // consumed by the rename — nothing to clean up
174
+ }
175
+ } catch (err) {
176
+ // allSettled: a failing cleanup (EACCES on a temp) must never replace the real
177
+ // error the caller needs. KNOWN LIMIT: a SIGKILL between renames strands the
178
+ // not-yet-consumed temps — `.partial-*` is scan-ignored and gitignored, so they
179
+ // are litter, not corruption; nothing sweeps them in V1.
180
+ await Promise.allSettled(staged.filter((s) => s.tmp).map((s) => fse.remove(s.tmp)));
181
+ throw err;
182
+ }
183
+ }
184
+
185
+ // The single-destination writer every single-file action rides — one write, same
186
+ // staging, validation, and cleanup semantics (one implementation, not a sibling copy).
187
+ // `validateRelPath` (optional) is the canonical path the bytes are validated as when it
188
+ // differs from where they are written (the compile draft case, core-0017 H3); omit it
189
+ // and the write path is its own validate path (every other caller).
190
+ export async function stageValidateRename({
191
+ installRoot,
192
+ relPath,
193
+ content,
194
+ overwrite = false,
195
+ validateRelPath,
196
+ }) {
197
+ await stageValidateRenameAll({
198
+ installRoot,
199
+ writes: [{ relPath, content, overwrite, validateRelPath }],
200
+ });
201
+ }