@dzhechkov/keysarium 1.7.15 → 1.7.19

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 (83) hide show
  1. package/.dz-manifest.json +214 -54
  2. package/README.md +29 -6
  3. package/README.ru.md +17 -0
  4. package/package.json +3 -3
  5. package/sbom.json +455 -55
  6. package/src/cli.js +32 -2
  7. package/src/commands/doctor.js +25 -1
  8. package/src/commands/init.js +56 -12
  9. package/src/commands/update.js +30 -3
  10. package/src/utils.js +7 -0
  11. package/templates/.agents/skills/casarium/SKILL.md +29 -0
  12. package/templates/.claude/agents/code-goal-planner.md +447 -0
  13. package/templates/.claude/commands/casarium.md +3 -1
  14. package/templates/.claude/commands/cjm-prototype.md +2 -2
  15. package/templates/.claude/commands/research.md +4 -1
  16. package/templates/.claude/rules/checkpoint-protocol.md +2 -1
  17. package/templates/.claude/rules/research-quality.md +11 -6
  18. package/templates/.claude/shards/phase-2-research.shard.md +12 -8
  19. package/templates/.claude/shards/phase-25-cjm.shard.md +5 -3
  20. package/templates/.claude/skills/feature-adr/SKILL.md +17 -15
  21. package/templates/.claude/skills/feature-adr/modules/03-adr.md +1 -1
  22. package/templates/.claude/skills/feature-adr/modules/03.5-ideation-swarm.md +1 -1
  23. package/templates/.claude/skills/feature-adr/modules/06-implementation-plan.md +2 -2
  24. package/templates/.claude/skills/feature-adr/modules/08-qe.md +1 -1
  25. package/templates/.claude/skills/feature-adr/modules/09-fleet-qe.md +12 -12
  26. package/templates/.claude/skills/feature-adr/references/agentic-qe/LICENSE.agentic-qe +21 -0
  27. package/templates/.claude/skills/feature-adr/references/agentic-qe/README.md +20 -16
  28. package/templates/.claude/skills/feature-adr/references/agentic-qe/UPSTREAM-MANIFEST.json +246 -0
  29. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-brutal-honesty-review/README.md +218 -0
  30. package/templates/.claude/skills/feature-adr/references/agentic-qe/{brutal-honesty-review.md → qe-ref-brutal-honesty-review/REFERENCE.md} +14 -1
  31. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-brutal-honesty-review/resources/assessment-rubrics.md +295 -0
  32. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-brutal-honesty-review/resources/review-template.md +102 -0
  33. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-brutal-honesty-review/schemas/output.json +291 -0
  34. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-brutal-honesty-review/scripts/assess-code.sh +179 -0
  35. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-brutal-honesty-review/scripts/assess-tests.sh +223 -0
  36. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-brutal-honesty-review/scripts/validate-config.json +34 -0
  37. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-enterprise-integration-testing/REFERENCE.md +758 -0
  38. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-enterprise-integration-testing/evals/enterprise-integration-testing.yaml +158 -0
  39. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-enterprise-integration-testing/schemas/output.json +74 -0
  40. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-enterprise-integration-testing/scripts/validate-config.json +25 -0
  41. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qcsd-ideation-swarm/REFERENCE.md +226 -0
  42. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qcsd-ideation-swarm/evals/qcsd-ideation-swarm.yaml +138 -0
  43. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qcsd-ideation-swarm/schemas/output.json +568 -0
  44. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qcsd-ideation-swarm/scripts/validate-config.json +25 -0
  45. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qcsd-ideation-swarm/steps/01-flag-detection.md +57 -0
  46. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qcsd-ideation-swarm/steps/02-core-agents.md +29 -0
  47. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qcsd-ideation-swarm/steps/03-batch1-results.md +15 -0
  48. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qcsd-ideation-swarm/steps/04-conditional-agents.md +23 -0
  49. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qcsd-ideation-swarm/steps/05-decision-synthesis.md +29 -0
  50. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qcsd-ideation-swarm/steps/06-report-generation.md +18 -0
  51. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qcsd-ideation-swarm/steps/07-learning-persistence.md +29 -0
  52. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qcsd-ideation-swarm/steps/08-final-output.md +18 -0
  53. package/templates/.claude/skills/feature-adr/references/agentic-qe/{qe-coverage-analysis.md → qe-ref-qe-coverage-analysis/REFERENCE.md} +42 -3
  54. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qe-coverage-analysis/evals/qe-coverage-analysis.yaml +494 -0
  55. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qe-coverage-analysis/schemas/output.json +286 -0
  56. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qe-coverage-analysis/scripts/validate-config.json +42 -0
  57. package/templates/.claude/skills/feature-adr/references/agentic-qe/{qe-requirements-validation.md → qe-ref-qe-requirements-validation/REFERENCE.md} +13 -3
  58. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qe-requirements-validation/evals/qe-requirements-validation.yaml +598 -0
  59. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qe-requirements-validation/schemas/output.json +587 -0
  60. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-qe-requirements-validation/scripts/validate-config.json +37 -0
  61. package/templates/.claude/skills/feature-adr/references/agentic-qe/{regression-testing.md → qe-ref-regression-testing/REFERENCE.md} +30 -49
  62. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-regression-testing/evals/regression-testing.yaml +504 -0
  63. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-regression-testing/schemas/output.json +311 -0
  64. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-regression-testing/scripts/validate-config.json +50 -0
  65. package/templates/.claude/skills/feature-adr/references/agentic-qe/{risk-based-testing.md → qe-ref-risk-based-testing/REFERENCE.md} +23 -52
  66. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-risk-based-testing/evals/risk-based-testing.yaml +141 -0
  67. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-risk-based-testing/schemas/output.json +480 -0
  68. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-risk-based-testing/scripts/validate-config.json +25 -0
  69. package/templates/.claude/skills/feature-adr/references/agentic-qe/{shift-left-testing.md → qe-ref-shift-left-testing/REFERENCE.md} +33 -88
  70. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-shift-left-testing/evals/shift-left-testing.yaml +145 -0
  71. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-shift-left-testing/schemas/output.json +459 -0
  72. package/templates/.claude/skills/feature-adr/references/agentic-qe/qe-ref-shift-left-testing/scripts/validate-config.json +25 -0
  73. package/templates/CLAUDE.md +3 -1
  74. package/templates/docs/feature-adr/04-admin-guide.md +1 -1
  75. package/templates/docs/improvement-directions.md +3 -3
  76. package/templates/docs/keysarium/02-admin-guide.md +2 -2
  77. package/templates/docs/keysarium/03-user-guide.md +3 -3
  78. package/templates/docs/keysarium/05-architecture.md +9 -8
  79. package/templates/docs/keysarium/06-user-flows.md +4 -4
  80. package/templates/lib/platform-templates/copilot.md +1 -1
  81. package/templates/lib/platform-templates/opencode.md +1 -1
  82. package/templates/.claude/skills/feature-adr/references/agentic-qe/enterprise-integration-testing.md +0 -111
  83. package/templates/.claude/skills/feature-adr/references/agentic-qe/qcsd-ideation-swarm.md +0 -126
package/src/cli.js CHANGED
@@ -10,7 +10,7 @@ function showBanner() {
10
10
  console.log('');
11
11
  console.log(cyan('╔══════════════════════════════════════════════════════╗'));
12
12
  console.log(cyan('║') + bold(' \uD83D\uDD2C DZ-KEYSARIUM — AI Research Toolkit ') + cyan('║'));
13
- console.log(cyan('║') + ' Full 7-phase pipeline for Claude Code ' + cyan('║'));
13
+ console.log(cyan('║') + ' Full 7-phase research pipeline ' + cyan('║'));
14
14
  console.log(cyan('╚══════════════════════════════════════════════════════╝'));
15
15
  console.log('');
16
16
  }
@@ -40,6 +40,8 @@ function showHelp() {
40
40
  console.log(' ' + yellow('--minimal') + ' Install only .claude/ directory (skills, commands, rules)');
41
41
  console.log(' ' + yellow('--force') + ' Overwrite existing files without prompting');
42
42
  console.log(' ' + yellow('--with-docs') + ' Include docs/ directory');
43
+ console.log(' ' + yellow('--target') + ' Install for claude (default) or codex');
44
+ console.log(' ' + yellow('--project') + ' Project directory (default: current directory)');
43
45
  console.log(' ' + yellow('--dry-run') + ' Show what would be done without making changes');
44
46
  console.log(' ' + yellow('--help, -h') + ' Show this help message');
45
47
  console.log(' ' + yellow('--version, -v') + ' Show version number');
@@ -47,6 +49,7 @@ function showHelp() {
47
49
  console.log(bold('Examples:'));
48
50
  console.log(dim(' $ ') + 'npx @dzhechkov/keysarium init');
49
51
  console.log(dim(' $ ') + 'npx @dzhechkov/keysarium init --minimal --force');
52
+ console.log(dim(' $ ') + 'npx @dzhechkov/keysarium init --target codex --project ./my-case');
50
53
  console.log(dim(' $ ') + 'npx @dzhechkov/keysarium update --dry-run');
51
54
  console.log(dim(' $ ') + 'npx @dzhechkov/keysarium doctor');
52
55
  console.log('');
@@ -64,11 +67,14 @@ function parseArgs(argv) {
64
67
  withDocs: false,
65
68
  dryRun: false,
66
69
  targetDir: process.cwd(),
70
+ target: 'claude',
71
+ targetSpecified: false,
67
72
  };
68
73
 
69
74
  let command = null;
70
75
 
71
- for (const arg of args) {
76
+ for (let index = 0; index < args.length; index += 1) {
77
+ const arg = args[index];
72
78
  switch (arg) {
73
79
  case '--minimal':
74
80
  flags.minimal = true;
@@ -82,6 +88,25 @@ function parseArgs(argv) {
82
88
  case '--dry-run':
83
89
  flags.dryRun = true;
84
90
  break;
91
+ case '--target': {
92
+ const value = args[++index];
93
+ if (value !== 'claude' && value !== 'codex') {
94
+ error('--target must be claude or codex');
95
+ process.exit(1);
96
+ }
97
+ flags.target = value;
98
+ flags.targetSpecified = true;
99
+ break;
100
+ }
101
+ case '--project': {
102
+ const value = args[++index];
103
+ if (!value || value.startsWith('-')) {
104
+ error('--project requires a directory path');
105
+ process.exit(1);
106
+ }
107
+ flags.targetDir = path.resolve(value);
108
+ break;
109
+ }
85
110
  case '--help':
86
111
  case '-h':
87
112
  command = 'help';
@@ -132,6 +157,11 @@ function main() {
132
157
 
133
158
  // Route to command handlers
134
159
  const resolvedCommand = command || 'init';
160
+ if (flags.targetSpecified && resolvedCommand !== 'init') {
161
+ error('--target is supported only by init; update and doctor use the installation manifest');
162
+ process.exitCode = 1;
163
+ return;
164
+ }
135
165
 
136
166
  switch (resolvedCommand) {
137
167
  case 'init':
@@ -293,6 +293,28 @@ function checkSettingsValid(targetDir) {
293
293
  });
294
294
  }
295
295
 
296
+ function checkCodexEntrypoint(targetDir) {
297
+ return check('Codex entrypoint', () => {
298
+ const entry = path.join(targetDir, '.agents', 'skills', 'casarium', 'SKILL.md');
299
+ const command = path.join(targetDir, '.claude', 'commands', 'casarium.md');
300
+ const shards = path.join(targetDir, '.claude', 'shards');
301
+ const missing = [entry, command, shards].filter((item) => !fileExists(item));
302
+ if (missing.length > 0) {
303
+ return { passed: false, detail: `Missing ${missing.map((item) => path.relative(targetDir, item)).join(', ')}`,
304
+ fix: 'Run keysarium update to restore the installed Codex entrypoint.' };
305
+ }
306
+ const body = fs.readFileSync(entry, 'utf8');
307
+ const frontmatter = body.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/);
308
+ if (!frontmatter || !/^name:\s*casarium\s*$/m.test(frontmatter[1])
309
+ || !/^description:\s*\S.+$/m.test(frontmatter[1])
310
+ || !body.includes('.claude/commands/casarium.md')) {
311
+ return { passed: false, detail: 'SKILL.md lacks Codex discovery metadata or the canonical command reference',
312
+ fix: 'Run keysarium update to restore the Codex entrypoint.' };
313
+ }
314
+ return { passed: true, detail: 'Project skill and canonical pipeline references present' };
315
+ });
316
+ }
317
+
296
318
  /**
297
319
  * Check 8: researches/ directory exists.
298
320
  */
@@ -372,7 +394,9 @@ async function run(options) {
372
394
  checkRulesPresent(targetDir),
373
395
  checkShardsPresent(targetDir),
374
396
  checkAgentsPresent(targetDir),
375
- checkSettingsValid(targetDir),
397
+ manifest.components?.includes('codex_entry')
398
+ ? checkCodexEntrypoint(targetDir)
399
+ : checkSettingsValid(targetDir),
376
400
  checkResearchDir(targetDir),
377
401
  ];
378
402
 
@@ -8,6 +8,7 @@ const {
8
8
  copyDirRecursive, copyDirTracked, fileExists, readJSON,
9
9
  ensureDir, getRelativePaths,
10
10
  createManifest, writeManifest, getTemplatesDir,
11
+ hashFile,
11
12
  COMPONENTS, MANIFEST_FILE,
12
13
  } = require('../utils');
13
14
 
@@ -22,8 +23,10 @@ const {
22
23
  * --with-docs: add docs group
23
24
  */
24
25
  function resolveComponents(options) {
25
- const { minimal, withDocs } = options;
26
- const coreKeys = ['skills', 'commands', 'rules', 'shards', 'agents', 'hooks', 'claude_md', 'harvest'];
26
+ const { minimal, withDocs, target } = options;
27
+ const coreKeys = ['skills', 'commands', 'rules', 'shards', 'agents', 'claude_md', 'harvest'];
28
+ if (target === 'codex') coreKeys.push('codex_entry');
29
+ else coreKeys.push('hooks');
27
30
  const keys = [...coreKeys];
28
31
 
29
32
  if (!minimal) {
@@ -81,9 +84,10 @@ function installComponent(comp, templatesDir, targetDir, force) {
81
84
  * @param {boolean} options.withDocs — Include docs/ directory
82
85
  * @param {boolean} options.dryRun — Preview without writing anything
83
86
  * @param {string} options.targetDir — Destination project root
87
+ * @param {string} options.target — claude (default) or codex
84
88
  */
85
89
  async function run(options) {
86
- const { force, dryRun, targetDir } = options;
90
+ const { force, dryRun, targetDir, target = 'claude' } = options;
87
91
  const manifestPath = path.join(targetDir, MANIFEST_FILE);
88
92
 
89
93
  // ── a) Check for existing installation ──────────────────────────────────
@@ -93,9 +97,25 @@ async function run(options) {
93
97
  info(`Run ${cyan('npx @dzhechkov/keysarium update')} to update, or use ${yellow('--force')} to overwrite.`);
94
98
  process.exit(1);
95
99
  }
100
+ const existing = readJSON(manifestPath);
101
+ if (!existing || !Array.isArray(existing.components)) {
102
+ logError('Existing .keysarium.json is invalid; refusing to overwrite its ownership record.');
103
+ process.exit(1);
104
+ }
105
+ const existingTarget = existing.components.includes('codex_entry') ? 'codex' : 'claude';
106
+ if (existingTarget !== target) {
107
+ logError(`Cannot switch an installed ${existingTarget} toolkit to ${target} with --force. Remove it first, then init for the new target.`);
108
+ process.exit(1);
109
+ }
96
110
  warn('Existing installation found — overwriting (--force).');
97
111
  }
98
112
 
113
+ const codexEntryPath = path.join(targetDir, '.agents', 'skills', 'casarium', 'SKILL.md');
114
+ if (target === 'codex' && fileExists(codexEntryPath) && !force) {
115
+ logError('A project skill named casarium already exists. Use --force only if it may be replaced.');
116
+ process.exit(1);
117
+ }
118
+
99
119
  // ── b) Determine components ─────────────────────────────────────────────
100
120
  const componentKeys = resolveComponents(options);
101
121
  const templatesDir = getTemplatesDir();
@@ -103,6 +123,7 @@ async function run(options) {
103
123
  // ── c) Show plan ────────────────────────────────────────────────────────
104
124
  console.log('');
105
125
  info(bold('Installation plan:'));
126
+ info(`Target: ${target}; project: ${targetDir}`);
106
127
  console.log('');
107
128
 
108
129
  for (const key of componentKeys) {
@@ -145,6 +166,15 @@ async function run(options) {
145
166
  const version = pkg ? pkg.version : '0.0.0';
146
167
 
147
168
  const manifest = createManifest(version, componentKeys, installedFiles.sort());
169
+ // Only claim a baseline for the planner when the installed bytes equal the template.
170
+ // A pre-existing user file preserved by init must not be adopted as our version.
171
+ const plannerRel = path.join('.claude', 'agents', 'code-goal-planner.md');
172
+ const installedPlanner = path.join(targetDir, plannerRel);
173
+ const templatePlanner = path.join(templatesDir, plannerRel);
174
+ if (fileExists(installedPlanner) && fileExists(templatePlanner)
175
+ && hashFile(installedPlanner) === hashFile(templatePlanner)) {
176
+ manifest.templateHashes = { [plannerRel]: hashFile(templatePlanner) };
177
+ }
148
178
  writeManifest(targetDir, manifest);
149
179
  info('Created .keysarium.json manifest');
150
180
 
@@ -161,16 +191,30 @@ async function run(options) {
161
191
 
162
192
  console.log('');
163
193
  console.log(bold('Next steps:'));
164
- console.log(` 1. Open ${cyan('Claude Code')} in this directory`);
165
- console.log(` 2. Run ${cyan('/casarium [your case description]')}`);
166
- console.log(` 3. Follow the 7-phase pipeline`);
167
- console.log('');
168
- console.log(bold('Available commands:'));
169
- console.log(` ${cyan('/casarium')} ${dim('\u2014 Full pipeline')}`);
170
- console.log(` ${cyan('/new-research')} ${dim('\u2014 New research')}`);
171
- console.log(` ${cyan('/parallel-research')} ${dim('\u2014 Multiple cases in parallel')}`);
172
- console.log(` ${cyan('/harvest')} ${dim('\u2014 Extract knowledge')}`);
194
+ if (target === 'codex') {
195
+ console.log(` 1. Open ${cyan('Codex')} in ${targetDir}`);
196
+ console.log(` 2. Invoke ${cyan('$casarium [your case description]')}`);
197
+ console.log(` 3. Confirm each phase checkpoint and select a CJM variant`);
198
+ console.log('');
199
+ console.log(bold('Codex integration status:'));
200
+ console.log(' Bundled workflow skills: installed under .claude/skills/ as phase references');
201
+ console.log(' Full pipeline entrypoint: installed at .agents/skills/casarium/SKILL.md');
202
+ console.log(' Codex hooks and MCP integrations: not installed');
203
+ console.log(' Claude settings/hooks: not installed for this target');
204
+ } else {
205
+ console.log(` 1. Open ${cyan('Claude Code')} in this directory`);
206
+ console.log(` 2. Run ${cyan('/casarium [your case description]')}`);
207
+ console.log(` 3. Follow the 7-phase pipeline`);
208
+ }
173
209
  console.log('');
210
+ if (target === 'claude') {
211
+ console.log(bold('Available commands:'));
212
+ console.log(` ${cyan('/casarium')} ${dim('\u2014 Full pipeline')}`);
213
+ console.log(` ${cyan('/new-research')} ${dim('\u2014 New research')}`);
214
+ console.log(` ${cyan('/parallel-research')} ${dim('\u2014 Multiple cases in parallel')}`);
215
+ console.log(` ${cyan('/harvest')} ${dim('\u2014 Extract knowledge')}`);
216
+ console.log('');
217
+ }
174
218
 
175
219
  process.exit(0);
176
220
  }
@@ -7,6 +7,7 @@ const {
7
7
  info, success, warn, error: logError, step,
8
8
  copyDirRecursive, fileExists, readJSON,
9
9
  ensureDir, getRelativePaths, diffFiles,
10
+ hashFile,
10
11
  readManifest, writeManifest, getTemplatesDir,
11
12
  COMPONENTS, MANIFEST_FILE,
12
13
  } = require('../utils');
@@ -20,11 +21,11 @@ const {
20
21
  *
21
22
  * @param {object} options
22
23
  * @param {boolean} options.dryRun — Preview without writing anything
23
- * @param {boolean} options.force — Passed through (unused here)
24
+ * @param {boolean} options.force — Back up and replace a locally edited planner agent
24
25
  * @param {string} options.targetDir — Destination project root
25
26
  */
26
27
  async function run(options) {
27
- const { dryRun, targetDir } = options;
28
+ const { dryRun, force, targetDir } = options;
28
29
  const manifestPath = path.join(targetDir, MANIFEST_FILE);
29
30
 
30
31
  // ── a) Read existing manifest ───────────────────────────────────────────
@@ -60,7 +61,11 @@ async function run(options) {
60
61
  let totalAdded = 0;
61
62
  let totalModified = 0;
62
63
  let totalUnchanged = 0;
64
+ let preservedPlanner = false;
63
65
  const filesToCopy = []; // { src, dest, status, relPath }
66
+ const plannerRel = path.join('.claude', 'agents', 'code-goal-planner.md');
67
+ // The unmodified wrapper shipped before the full agent was introduced.
68
+ const legacyPlannerHash = 'fd6faca31bacfa3c70c0ff2a8ba152957d952b8590b4df0934ffd19af5078592';
64
69
 
65
70
  for (const key of installedKeys) {
66
71
  const comp = COMPONENTS[key];
@@ -107,11 +112,24 @@ async function run(options) {
107
112
  }
108
113
 
109
114
  for (const rel of diff.modified) {
115
+ const relPath = path.join(comp.src, rel);
116
+ let backup = false;
117
+ if (relPath === plannerRel) {
118
+ const currentHash = hashFile(path.join(destBase, rel));
119
+ const baseline = manifest.templateHashes?.[plannerRel];
120
+ const clean = currentHash === baseline || (!baseline && currentHash === legacyPlannerHash);
121
+ if (!clean && !force) {
122
+ preservedPlanner = true;
123
+ continue;
124
+ }
125
+ backup = !clean;
126
+ }
110
127
  filesToCopy.push({
111
128
  src: path.join(srcBase, rel),
112
129
  dest: path.join(destBase, rel),
113
130
  status: 'modified',
114
- relPath: path.join(comp.src, rel),
131
+ relPath,
132
+ backup,
115
133
  });
116
134
  totalModified++;
117
135
  }
@@ -125,6 +143,7 @@ async function run(options) {
125
143
  console.log(` ${green('+')} ${totalAdded} file(s) to add`);
126
144
  console.log(` ${yellow('~')} ${totalModified} file(s) to update`);
127
145
  console.log(` ${dim('=')} ${totalUnchanged} file(s) unchanged`);
146
+ if (preservedPlanner) warn(`Local edit preserved: ${plannerRel}. Use --force to back up and replace it.`);
128
147
  console.log('');
129
148
 
130
149
  if (totalAdded === 0 && totalModified === 0) {
@@ -137,6 +156,7 @@ async function run(options) {
137
156
  for (const f of filesToCopy) {
138
157
  const marker = f.status === 'added' ? green('+ ADD') : yellow('~ MOD');
139
158
  console.log(` ${marker} ${f.relPath}`);
159
+ if (f.backup) console.log(` backup: ${f.relPath}.bak`);
140
160
  }
141
161
  console.log('');
142
162
  warn('Dry run — no files were written.');
@@ -150,6 +170,7 @@ async function run(options) {
150
170
  step(i + 1, filesToCopy.length, `${label} ${f.relPath}`);
151
171
 
152
172
  ensureDir(path.dirname(f.dest));
173
+ if (f.backup) fs.copyFileSync(f.dest, `${f.dest}.bak`);
153
174
  fs.copyFileSync(f.src, f.dest);
154
175
  }
155
176
 
@@ -176,6 +197,12 @@ async function run(options) {
176
197
  manifest.version = newVersion;
177
198
  manifest.updatedAt = new Date().toISOString();
178
199
  manifest.files = allFiles.sort();
200
+ const installedPlanner = path.join(targetDir, plannerRel);
201
+ const templatePlanner = path.join(templatesDir, plannerRel);
202
+ if (fileExists(installedPlanner) && fileExists(templatePlanner)
203
+ && hashFile(installedPlanner) === hashFile(templatePlanner)) {
204
+ manifest.templateHashes = { ...manifest.templateHashes, [plannerRel]: hashFile(templatePlanner) };
205
+ }
179
206
 
180
207
  writeManifest(targetDir, manifest);
181
208
  info('Updated .keysarium.json manifest');
package/src/utils.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
+ const { createHash } = require('node:crypto');
5
6
 
6
7
  // ===========================================================================
7
8
  // Colors — ANSI escape codes (zero dependencies)
@@ -36,6 +37,10 @@ function step(n, total, msg) {
36
37
  console.log(cyan(`[${n}/${total}]`) + ' ' + msg);
37
38
  }
38
39
 
40
+ function hashFile(file) {
41
+ return createHash('sha256').update(fs.readFileSync(file)).digest('hex');
42
+ }
43
+
39
44
  // ===========================================================================
40
45
  // File operations — all synchronous, Node.js built-ins only
41
46
  // ===========================================================================
@@ -311,6 +316,7 @@ const COMPONENT_SPECS = {
311
316
  docs: { src: 'docs', name: 'Documentation', unit: 'guides', countMode: 'files', group: 'docs' },
312
317
  claude_md: { src: 'CLAUDE.md', name: 'Master instructions', group: 'core', isFile: true },
313
318
  harvest: { src: 'TOOLKIT_HARVEST.md', name: 'Knowledge harvest tracker', group: 'core', isFile: true },
319
+ codex_entry: { src: '.agents/skills/casarium', name: 'Codex pipeline entrypoint', unit: 'entrypoint', countMode: 'files', group: 'codex' },
314
320
  };
315
321
 
316
322
  /**
@@ -370,6 +376,7 @@ module.exports = {
370
376
  warn,
371
377
  error,
372
378
  step,
379
+ hashFile,
373
380
 
374
381
  // File operations
375
382
  copyDirRecursive,
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: casarium
3
+ description: Run the installed Keysarium seven-phase case research pipeline in Codex, with phase artifacts and user checkpoints. Use when the user invokes $casarium or asks to work through a Keysarium case.
4
+ ---
5
+
6
+ # Casarium in Codex
7
+
8
+ This project contains the complete Keysarium workflow. Start by reading
9
+ `.claude/commands/casarium.md`; it is the source of truth for phase order, outputs and
10
+ checkpoints. Treat its `$ARGUMENTS` as the user's request after `$casarium`. If the case is
11
+ missing, ask for it.
12
+
13
+ For each phase, read only its `.claude/shards/phase-*.shard.md` and the skill files it names
14
+ under `.claude/skills/`. Read `.claude/rules/checkpoint-protocol.md` and
15
+ `.claude/rules/research-quality.md` when reaching their gates. Create phase artifacts under
16
+ `researches/<case-name>/` as the phase runs. Show the checkpoint and wait for the user's
17
+ confirmation before advancing. Phase 2.5 requires an explicit CJM choice; preserve that
18
+ choice in later phases. A failed research gate cannot emit a success promise.
19
+
20
+ The `.claude/` files are workflow references in Codex. Claude slash commands, Agent tool
21
+ calls, hook settings and model names inside them are not Codex commands or installed Codex
22
+ integrations. Use available Codex tools for the work and state any unavailable capability;
23
+ do not claim that a named Claude agent or hook ran. Parallel work is optional when the host
24
+ supports it. The research mode requirements still apply to every worker.
25
+
26
+ If the case asks for AI Factory mapping, read
27
+ `.claude/shards/phase-ai-factory.shard.md` and its referenced skill before Phase 0. Otherwise
28
+ leave that branch inactive. The project-local `$casarium` skill is the Codex entrypoint;
29
+ `/casarium` remains the Claude Code entrypoint.