@haposoft/cafekit 0.16.0-rc.6 → 0.16.0-rc.7

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 (86) hide show
  1. package/README.md +21 -58
  2. package/bin/__tests__/change-firewall.semantic-firewall.test.js +4 -5
  3. package/bin/__tests__/codex-hooks.test.js +137 -0
  4. package/bin/__tests__/codex-native.test.js +130 -74
  5. package/bin/__tests__/develop-contract.test.js +155 -76
  6. package/bin/__tests__/installer-safety.test.js +19 -154
  7. package/bin/__tests__/package-inventory.test.js +10 -20
  8. package/bin/__tests__/release-preflight.semantic-firewall.test.js +2 -2
  9. package/bin/__tests__/validator-grounding.test.js +11 -11
  10. package/bin/install.js +2 -7
  11. package/bin/lib/context.js +0 -45
  12. package/bin/lib/copy-utils.js +1 -1
  13. package/bin/lib/i18n.js +0 -3
  14. package/bin/lib/manifest.js +1 -1
  15. package/bin/phases/claude-runtime.js +1 -3
  16. package/bin/phases/codex-runtime.js +4 -2
  17. package/bin/phases/copy-payload.js +3 -15
  18. package/bin/phases/post-install.js +5 -17
  19. package/bin/phases/root-config.js +2 -3
  20. package/bin/phases/select-platform.js +2 -5
  21. package/bin/phases/summary.js +0 -1
  22. package/package.json +2 -5
  23. package/src/claude/CLAUDE.md +19 -6
  24. package/src/claude/agents/project-manager.md +41 -9
  25. package/src/claude/agents/spec-maker.md +76 -155
  26. package/src/claude/gitignore +1 -1
  27. package/src/claude/hooks/__tests__/spec-gate.test.js +162 -0
  28. package/src/claude/hooks/lib/context.cjs +1 -1
  29. package/src/claude/hooks/lib/detect.cjs +1 -1
  30. package/src/claude/hooks/spec-gate.cjs +44 -9
  31. package/src/claude/hooks/spec-state.cjs +22 -11
  32. package/src/claude/hooks/task-scaffold-guard.cjs +1 -1
  33. package/src/claude/migration-manifest.json +17 -3
  34. package/src/claude/rules/manage-docs.md +43 -24
  35. package/src/claude/rules/state-sync.md +57 -62
  36. package/src/claude/rules/workflow.md +30 -13
  37. package/src/claude/scripts/change-firewall.cjs +2 -2
  38. package/src/claude/scripts/generate-skill-catalog.cjs +0 -1
  39. package/src/claude/scripts/spec-receipt.cjs +141 -1
  40. package/src/claude/scripts/spec-resolver.cjs +288 -0
  41. package/src/claude/skills/develop/SKILL.md +89 -125
  42. package/src/claude/skills/develop/references/parallel-waves.md +75 -162
  43. package/src/claude/skills/develop/references/quality-gate.md +61 -121
  44. package/src/claude/skills/develop/references/subagent-patterns.md +31 -38
  45. package/src/claude/skills/question/SKILL.md +1 -1
  46. package/src/claude/skills/specs/SKILL.md +142 -204
  47. package/src/claude/skills/specs/references/review.md +109 -243
  48. package/src/claude/skills/specs/references/templates.md +173 -0
  49. package/src/claude/skills/sync/SKILL.md +66 -90
  50. package/src/claude/skills/sync/references/sync-protocols.md +62 -83
  51. package/src/codex/AGENTS.md +20 -7
  52. package/src/codex/hooks/lib/spec-receipt.cjs +18 -0
  53. package/src/codex/hooks/lib/spec-utils.cjs +25 -0
  54. package/src/codex/hooks/spec-gate.cjs +46 -16
  55. package/src/codex/hooks/spec-state.cjs +22 -12
  56. package/src/codex/rules/state-sync.md +57 -61
  57. package/src/common/AGENTS.md +8 -1
  58. package/bin/__tests__/opencode-spec-gate.test.js +0 -799
  59. package/bin/lib/opencode-install.js +0 -899
  60. package/bin/phases/opencode-runtime.js +0 -38
  61. package/src/claude/skills/specs/references/archive-workflow.md +0 -74
  62. package/src/claude/skills/specs/references/ask-user-question-gates.md +0 -70
  63. package/src/claude/skills/specs/references/codebase-analysis.md +0 -102
  64. package/src/claude/skills/specs/references/cross-spec-dependency.md +0 -76
  65. package/src/claude/skills/specs/references/research-strategy.md +0 -107
  66. package/src/claude/skills/specs/references/scope-inquiry.md +0 -107
  67. package/src/claude/skills/specs/references/translation-mirror.md +0 -83
  68. package/src/claude/skills/specs/rules/design-discovery-full.md +0 -45
  69. package/src/claude/skills/specs/rules/design-discovery-light.md +0 -28
  70. package/src/claude/skills/specs/rules/design-principles.md +0 -138
  71. package/src/claude/skills/specs/rules/design-review.md +0 -55
  72. package/src/claude/skills/specs/rules/ears-format.md +0 -42
  73. package/src/claude/skills/specs/rules/phase-decision-matrix.md +0 -56
  74. package/src/claude/skills/specs/rules/task-scoring-rubric.md +0 -52
  75. package/src/claude/skills/specs/rules/tasks-generation.md +0 -189
  76. package/src/opencode/AGENTS.md +0 -41
  77. package/src/opencode/plugins/docs-sync.ts +0 -175
  78. package/src/opencode/plugins/inspect-block.ts +0 -164
  79. package/src/opencode/plugins/privacy-block.ts +0 -182
  80. package/src/opencode/plugins/rules.ts +0 -155
  81. package/src/opencode/plugins/session.ts +0 -179
  82. package/src/opencode/plugins/spec-gate.ts +0 -457
  83. package/src/opencode/plugins/spec-state.ts +0 -311
  84. package/src/opencode/plugins/state.ts +0 -253
  85. package/src/opencode/plugins/task-scaffold-guard.ts +0 -161
  86. package/src/opencode/plugins/usage.ts +0 -178
@@ -35,7 +35,6 @@ const FORBIDDEN_PAYLOAD = [
35
35
  const RUNTIMES = {
36
36
  claude: { root: '.claude', rules: '.claude/hooks/rules.cjs' },
37
37
  codex: { root: '.codex', rules: '.codex/hooks/rules.cjs' },
38
- opencode: { root: '.opencode', rules: '.opencode/plugins/rules.ts' },
39
38
  };
40
39
 
41
40
  function npmPack(args, cwd) {
@@ -303,29 +302,25 @@ function assertCombinedInstructionIsolation(root) {
303
302
  const core = managedBlock(agents, '<!-- CAFEKIT CORE START -->', '<!-- CAFEKIT CORE END -->');
304
303
  const claudeBlock = managedBlock(claude, '<!-- CAFEKIT CLAUDE START -->', '<!-- CAFEKIT CLAUDE END -->');
305
304
  const codexBlock = managedBlock(agents, '<!-- CAFEKIT CODEX START -->', '<!-- CAFEKIT CODEX END -->');
306
- const opencodeBlock = managedBlock(agents, '<!-- CAFEKIT OPENCODE START -->', '<!-- CAFEKIT OPENCODE END -->');
307
305
 
308
306
  for (const [content, marker] of [
309
307
  [agents, '<!-- CAFEKIT CORE START -->'],
310
308
  [agents, '<!-- CAFEKIT CODEX START -->'],
311
- [agents, '<!-- CAFEKIT OPENCODE START -->'],
312
309
  [claude, '<!-- CAFEKIT CLAUDE START -->']
313
310
  ]) assert.equal((content.match(new RegExp(marker, 'g')) || []).length, 1);
314
311
 
315
- assert.doesNotMatch(core, /Claude|Codex|OpenCode|\.claude|\.codex|\.opencode|\/hapo:|\$hapo-/i);
316
- assert.doesNotMatch(claudeBlock, /Codex|OpenCode|\.codex|\.opencode|\$hapo-/i);
312
+ assert.doesNotMatch(core, /Claude|Codex|\.claude|\.codex|\/hapo:|\$hapo-/i);
313
+ assert.doesNotMatch(claudeBlock, /Codex|\.codex|\$hapo-/i);
317
314
  assert.match(codexBlock, /native project instruction surface is root `AGENTS\.md`/);
318
- assert.match(opencodeBlock, /native project instruction surface is root `AGENTS\.md`/);
319
315
  assert.match(agents, /shared-root trade-off is intentional/);
320
316
  // H5 ownership/ignore contract
321
317
  assert.match(core, /runtime-neutral/i);
322
318
  assert.match(core, /fail-safe/i);
323
319
  assert.match(codexBlock, /owned by Codex/i);
324
- assert.match(codexBlock, /ignore this entire Codex block/i);
325
- assert.match(opencodeBlock, /owned by OpenCode/i);
326
- assert.match(opencodeBlock, /ignore this entire OpenCode block/i);
320
+ assert.match(codexBlock, /If you are Claude Code or any other runtime, ignore this entire Codex block/i);
321
+ assert.doesNotMatch(codexBlock, /If you are Codex CLI or any other runtime, ignore this entire Codex block/i);
327
322
  assert.doesNotMatch(core, /\$hapo-|hapo:/i);
328
- assert.doesNotMatch(claudeBlock, /<!-- CAFEKIT (CODEX|OPENCODE) /);
323
+ assert.doesNotMatch(claudeBlock, /<!-- CAFEKIT CODEX /);
329
324
  }
330
325
  function stableInstallSnapshot(root, platforms) {
331
326
  const files = ['AGENTS.md', 'CLAUDE.md', '.gitignore'];
@@ -359,12 +354,6 @@ function assertHookLanguage(root, platform, lang, sessionId) {
359
354
  assert.equal(runtimeJson.locale.responseLanguage, lang || null);
360
355
  const rulesPath = path.join(root, runtime.rules);
361
356
  assert.ok(fs.existsSync(rulesPath), `installed rules path missing: ${rulesPath}`);
362
- if (platform === 'opencode') {
363
- const plugin = fs.readFileSync(rulesPath, 'utf8');
364
- assert.match(plugin, /if \(respondLang\)/);
365
- assert.match(plugin, /Respond in \$\{respondLang\}/);
366
- return;
367
- }
368
357
  const result = spawnSync(process.execPath, [rulesPath], {
369
358
  cwd: root,
370
359
  input: JSON.stringify({ cwd: root, session_id: sessionId }),
@@ -857,9 +846,8 @@ test('packed tarball installer matrix proves locale, transforms, paths, and reru
857
846
  const cases = [
858
847
  { name: 'claude', platforms: ['claude'] },
859
848
  { name: 'codex', platforms: ['codex'] },
860
- { name: 'opencode', platforms: ['opencode'] },
861
- { name: 'combined', platforms: ['claude', 'codex', 'opencode'] },
862
- { name: 'combined-rerun', platforms: ['claude', 'codex', 'opencode'], rerun: true },
849
+ { name: 'combined', platforms: ['claude', 'codex'] },
850
+ { name: 'combined-rerun', platforms: ['claude', 'codex'], rerun: true },
863
851
  ];
864
852
  for (const matrixCase of cases) {
865
853
  for (const lang of [null, 'vi']) {
@@ -871,7 +859,9 @@ test('packed tarball installer matrix proves locale, transforms, paths, and reru
871
859
  assertInstalledScripts(project, platform);
872
860
  assertTransforms(project, platform);
873
861
  }
874
- if (matrixCase.platforms.length === 3) assertCombinedInstructionIsolation(project);
862
+ if (matrixCase.platforms.includes('claude') && matrixCase.platforms.includes('codex')) {
863
+ assertCombinedInstructionIsolation(project);
864
+ }
875
865
  if (matrixCase.rerun) {
876
866
  fs.appendFileSync(path.join(project, 'AGENTS.md'), '\n## User matrix note\nKeep this exact.\n');
877
867
  fs.appendFileSync(path.join(project, 'CLAUDE.md'), '\n## User Claude matrix note\nKeep this exact.\n');
@@ -173,14 +173,14 @@ function buildEvolutionSpecFixture(root, name) {
173
173
  // isolated root) -- reaches a real `released` generation 1 authority with no
174
174
  // hand-crafted digests. Returns the bound instance and key commits/digests.
175
175
  function publishOneReleasedGeneration(root) {
176
- writeJson(path.join(root, 'specs/cafekit-semantic-eval-firewall/reports/bootstrap-legacy-bridge-review.json'), {
176
+ writeJson(path.join(root, 'specs/archive/cafekit-semantic-eval-firewall/reports/bootstrap-legacy-bridge-review.json'), {
177
177
  schema_version: '1', verdict: 'PASS', blocking_count: 0, reviewed_criteria: ['R1.1'],
178
178
  semantic_digest: cf.sha256Tag('bridge'), bootWindowBaseCommit: git(root, ['rev-parse', 'HEAD']).trim(),
179
179
  written_at: new Date().toISOString(),
180
180
  });
181
181
  const baseCommit = git(root, ['rev-parse', 'HEAD']).trim();
182
182
  const reviewReceiptDigest = cf.sha256Tag('seed-review-receipt');
183
- writeJson(path.join(root, 'specs/cafekit-semantic-eval-firewall/spec.json'), {
183
+ writeJson(path.join(root, 'specs/archive/cafekit-semantic-eval-firewall/spec.json'), {
184
184
  validation: {
185
185
  semantic_review_history: {
186
186
  entries: [{ sequence: 0, review_epoch: 0, verdict: 'PASS', review_receipt_digest: reviewReceiptDigest, semantic_digest: cf.sha256Tag('feature-digest') }],
@@ -525,9 +525,8 @@ The transition records success, failure, and recovery states.
525
525
  test('Codex installer transform preserves all owned spec assets with idempotent runtime parity', () => {
526
526
  for (const relative of [
527
527
  'src/claude/skills/specs/SKILL.md',
528
- 'src/claude/skills/specs/rules/tasks-generation.md',
529
- 'src/claude/skills/specs/rules/design-principles.md',
530
528
  'src/claude/skills/specs/references/review.md',
529
+ 'src/claude/skills/specs/references/templates.md',
531
530
  'src/claude/skills/specs/templates/design.md',
532
531
  'src/claude/skills/specs/templates/task.md',
533
532
  'src/claude/scripts/spec-scaffold.cjs',
@@ -548,14 +547,15 @@ test('Codex installer transform preserves all owned spec assets with idempotent
548
547
 
549
548
  const skill = fs.readFileSync(path.join(ROOT, 'src/claude/skills/specs/SKILL.md'), 'utf8');
550
549
  const review = fs.readFileSync(path.join(ROOT, 'src/claude/skills/specs/references/review.md'), 'utf8');
551
- const designRules = fs.readFileSync(path.join(ROOT, 'src/claude/skills/specs/rules/design-principles.md'), 'utf8');
552
- const taskRules = fs.readFileSync(path.join(ROOT, 'src/claude/skills/specs/rules/tasks-generation.md'), 'utf8');
553
- assert.match(skill, /\| `Compact` \| `spec\.json`, `requirements\.md`, `design\.md` \| only on a research trigger \| only on a typed topology trigger \|/);
554
- assert.match(skill, /\| `Full` \| `spec\.json`, `requirements\.md`, `design\.md` \| only on a research trigger \| only on a typed topology trigger \|/);
555
- assert.doesNotMatch(review, /Standard[^\n]{0,100}(?:must not|forbidden|never)[^\n]{0,60}tasks?/i);
556
- assert.match(designRules, /## Conditional detail[\s\S]*Migration:[\s\S]*only when existing state or public contracts change/);
557
- assert.match(taskRules, /distinct ownership, a durable[\s\S]{0,180}parallel coordination/);
558
- assert.match(taskRules, /receipts\/<task-basename>\.md|execution closeout/);
550
+ const templates = fs.readFileSync(path.join(ROOT, 'src/claude/skills/specs/references/templates.md'), 'utf8');
551
+ assert.match(skill, /specs\/<feature>\/[\s\S]{0,160}plan\.md[\s\S]{0,160}task-01-<slug>\.md/);
552
+ assert.match(skill, /C1 — Scope[\s\S]*C2 — Findings[\s\S]*C3 — Done/);
553
+ assert.doesNotMatch(skill, /--(?:status|validate|archive)\b/);
554
+ assert.match(review, /path:line/);
555
+ assert.match(review, /Cap the presented[\s\S]{0,40}list at 15/);
556
+ assert.match(review, /Round three requires runtime evidence/);
557
+ assert.match(templates, /\| ID \| EARS criterion \| Proof \|/);
558
+ assert.match(templates, /## Canonical inline Receipt[\s\S]*Verification: PASS[\s\S]*Exit: 0[\s\S]*Base:[\s\S]*Head:/);
559
559
 
560
560
  const parityRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cafekit-codex-validator-parity-'));
561
561
  try {
@@ -3201,7 +3201,7 @@ test('D11/R1.14: the legacy-bridge precondition artifact is reachable and change
3201
3201
  // resolves its own production root the same way, via git from its own file
3202
3202
  // location) -- ROOT here is packages/spec, one level below that root.
3203
3203
  const MONOREPO_ROOT = path.resolve(ROOT, '..', '..');
3204
- const bridgePath = path.join(MONOREPO_ROOT, 'specs/cafekit-semantic-eval-firewall/reports/bootstrap-legacy-bridge-review.json');
3204
+ const bridgePath = path.join(MONOREPO_ROOT, 'specs/archive/cafekit-semantic-eval-firewall/reports/bootstrap-legacy-bridge-review.json');
3205
3205
  assert.ok(fs.existsSync(bridgePath), 'the legacy-bridge artifact must exist before any R0-01 change-firewall assertion runs');
3206
3206
  const parsed = CHANGE_FIREWALL.assertLegacyBridgeArtifact();
3207
3207
  assert.equal(parsed.schema_version, '1');
package/bin/install.js CHANGED
@@ -33,7 +33,6 @@ const {
33
33
  removeObsoleteAgents
34
34
  } = require('./phases/claude-runtime');
35
35
  const { mergeClaudeSettings } = require('./phases/claude-settings');
36
- const { installOpenCodeRuntime } = require('./phases/opencode-runtime');
37
36
  const { installCodexRuntime } = require('./phases/codex-runtime');
38
37
  const { writePlatformVersionMetadata } = require('./phases/write-metadata');
39
38
  const { checkVersions } = require('./lib/version-check');
@@ -70,10 +69,6 @@ function installPlatform(ctx, platformKey) {
70
69
  copyRulesDirectory(ctx, platformKey);
71
70
  }
72
71
 
73
- if (platformKey === 'opencode') {
74
- installOpenCodeRuntime(ctx, platformKey);
75
- }
76
-
77
72
  if (platformKey === 'codex') {
78
73
  installCodexRuntime(ctx, platformKey);
79
74
  }
@@ -95,11 +90,11 @@ function printHelp() {
95
90
  Usage: npx @haposoft/cafekit [options]
96
91
 
97
92
  Installs CafeKit skills, agents, rules and runtime into the current project
98
- for Claude Code, OpenCode, and/or Codex CLI. Re-runs are selective: managed files are updated,
93
+ for Claude Code and/or Codex CLI. Re-runs are selective: managed files are updated,
99
94
  your edits are preserved.
100
95
 
101
96
  Options:
102
- --platform <id[,id]> Select claude, opencode, or codex explicitly
97
+ --platform <id[,id]> Select claude or codex explicitly
103
98
  --dry-run Preview changes; write nothing
104
99
  --force-overwrite Overwrite user-modified managed files (backup kept)
105
100
  -u, --upgrade, -f, --force Alias of --force-overwrite
@@ -13,7 +13,6 @@
13
13
  const fs = require('fs');
14
14
  const path = require('path');
15
15
  const packageJson = require('../../package.json');
16
- const { getOpenCodeCopyOptions } = require('./opencode-install');
17
16
  const { isInteractive, createUI } = require('./ui');
18
17
  const { resolveLang, createTranslator } = require('./i18n');
19
18
  const { normalizeSourcePaths } = require('./copy-utils');
@@ -57,12 +56,10 @@ function loadClaudeMigrationManifest() {
57
56
  const DEPENDENCY_TEMPLATES = {
58
57
  commands: {
59
58
  claude: {},
60
- opencode: {},
61
59
  codex: {}
62
60
  },
63
61
  agents: {
64
62
  claude: {},
65
- opencode: {},
66
63
  codex: {}
67
64
  }
68
65
  };
@@ -93,29 +90,6 @@ const PLATFORMS = {
93
90
  commands: true
94
91
  }
95
92
  },
96
- opencode: {
97
- id: 'opencode',
98
- name: 'OpenCode',
99
- description: 'OpenCode terminal AI coding agent',
100
- folder: '.opencode',
101
- detectFiles: ['.opencode', 'opencode.json', 'opencode.jsonc'],
102
- commandsDir: '.opencode/commands',
103
- skillsDir: '.opencode/skills',
104
- agentsDir: '.opencode/agents',
105
- skillsRef: '.opencode/skills',
106
- commandPrefix: '/',
107
- sourceDir: 'claude',
108
- sourceSubdir: 'commands', // Dead config (manifest commands.core is []); kept for shape parity
109
- backupTargets: ['.opencode', 'AGENTS.md', 'opencode.json', 'opencode.jsonc'],
110
- capabilities: {
111
- skills: true,
112
- agents: true,
113
- references: true,
114
- scripts: true,
115
- rules: true,
116
- commands: false
117
- }
118
- },
119
93
  codex: {
120
94
  id: 'codex',
121
95
  name: 'Codex CLI',
@@ -192,26 +166,8 @@ function getRuntimeSupportTargetDir(platformKey, subdir) {
192
166
  return path.join(PLATFORMS[platformKey].folder, subdir);
193
167
  }
194
168
 
195
- // Warn opencode-only installs when a legacy .claude/ folder remains from
196
- // pre-0.9.0 installs (when OpenCode shared the .claude/ runtime). We do not
197
- // delete anything; user must remove it explicitly to opt in.
198
- function warnLegacyClaudeFolder(platforms) {
199
- if (!platforms.includes('opencode')) return;
200
- if (platforms.includes('claude')) return;
201
- if (!fs.existsSync('.claude')) return;
202
-
203
- console.log('');
204
- console.log('⚠ Legacy .claude/ folder detected.');
205
- console.log(' As of CafeKit 0.9.0, OpenCode installs are self-contained under .opencode/.');
206
- console.log(' The .claude/ folder from older installs is no longer used by OpenCode.');
207
- console.log(' After this install finishes you can remove it: rm -rf .claude');
208
- console.log('');
209
- }
210
169
 
211
170
  function getCopyOptions(platformKey, baseOptions = {}) {
212
- if (platformKey === 'opencode') {
213
- return getOpenCodeCopyOptions(baseOptions);
214
- }
215
171
  if (platformKey === 'codex') {
216
172
  // Lazy require avoids coupling the shared registry to a runtime adapter
217
173
  // during module initialization.
@@ -358,7 +314,6 @@ module.exports = {
358
314
  getPlatformKeys,
359
315
  hasPlatformCapability,
360
316
  getRuntimeSupportTargetDir,
361
- warnLegacyClaudeFolder,
362
317
  getCopyOptions,
363
318
  parseInstallerArgs,
364
319
  createResults,
@@ -13,7 +13,7 @@ function isTextAsset(filePath) {
13
13
 
14
14
  // Generated artifacts never ship as runtime payload: the coverage db, the
15
15
  // Python bytecode cache directory, and bytecode files. Skipping them here keeps
16
- // both copyRecursive and copyManagedTree (and the OpenCode plugin copy path
16
+ // both copyRecursive and copyManagedTree
17
17
  // built on copyRecursive) from ever copying build residue.
18
18
  function isGeneratedArtifact(name) {
19
19
  const base = String(name || '');
package/bin/lib/i18n.js CHANGED
@@ -107,7 +107,6 @@ const MESSAGES = {
107
107
  // Next steps
108
108
  nextStepsTitle: 'Next steps',
109
109
  nsClaude: 'Start coding: /hapo:specs <feature>',
110
- nsOpencode: 'Start coding: ask the agent to begin a new feature or brainstorm',
111
110
  nsCodex: 'Start with `$hapo-specs <feature>`; review and trust project hooks with `/hooks`',
112
111
  nsKeys: 'Some skills need API keys: {skills}\n Copy <skill>/.env.example → .env and fill in the values',
113
112
  nsForce: 'To force-refresh managed files: npx @haposoft/cafekit --force-overwrite',
@@ -228,7 +227,6 @@ const MESSAGES = {
228
227
 
229
228
  nextStepsTitle: '次のステップ',
230
229
  nsClaude: 'コーディング開始: /hapo:specs <機能名>',
231
- nsOpencode: 'コーディング開始: エージェントに新機能の開始またはブレストを依頼',
232
230
  nsCodex: '`$hapo-specs <機能名>` で開始し、`/hooks` でプロジェクトフックを確認・信頼',
233
231
  nsKeys: '一部のスキルに API キーが必要です: {skills}\n <skill>/.env.example を .env にコピーし、値を入力してください',
234
232
  nsForce: '管理ファイルを強制更新: npx @haposoft/cafekit --force-overwrite',
@@ -348,7 +346,6 @@ const MESSAGES = {
348
346
 
349
347
  nextStepsTitle: 'Bước tiếp theo',
350
348
  nsClaude: 'Bắt đầu code: /hapo:specs <mô-tả-tính-năng>',
351
- nsOpencode: 'Bắt đầu code: yêu cầu agent khởi động tính năng mới hoặc brainstorm',
352
349
  nsCodex: 'Bắt đầu bằng `$hapo-specs <tính-năng>`; kiểm tra và trust project hooks bằng `/hooks`',
353
350
  nsKeys: 'Một số skill cần API key: {skills}\n Sao chép <skill>/.env.example → .env rồi điền giá trị',
354
351
  nsForce: 'Làm mới file được quản lý: npx @haposoft/cafekit --force-overwrite',
@@ -169,7 +169,7 @@ function createTracker(platformFolder, version, options = {}) {
169
169
  * Classify a managed file against the recorded baseline + the incoming payload.
170
170
  *
171
171
  * @param {string} absPath destination path on disk
172
- * @param {string} platformFolder root of the platform install (.claude/.opencode)
172
+ * @param {string} platformFolder root of the platform install (e.g. .claude)
173
173
  * @param {object} manifest result of read(platformFolder)
174
174
  * @param {string} payloadHash sha256 of the content about to be written
175
175
  * @returns {{state: string, diskHash: string|null, recordedHash: string|null, changed: boolean}}
@@ -18,7 +18,6 @@ const {
18
18
  const { sha256 } = require('../lib/manifest');
19
19
  const { writeManagedFile, copyManagedTree } = require('../lib/managed-writer');
20
20
  const { report, treeAction } = require('./report');
21
- const { normalizeOpenCodeBody } = require('../lib/opencode-install');
22
21
  const {
23
22
  upsertManagedCoreBlock,
24
23
  managedRange,
@@ -120,7 +119,7 @@ function copyClaudeAgentsMdFile(ctx, platformKey) {
120
119
  ensureSharedAgentsMdCore(ctx);
121
120
  }
122
121
 
123
- /** Copy ROUTING.md (SKILLS_DIR substituted; OpenCode body normalized). */
122
+ /** Copy ROUTING.md (SKILLS_DIR substituted). */
124
123
  function copyRoutingFile(ctx, platformKey) {
125
124
  const platform = PLATFORMS[platformKey];
126
125
  const src = path.join(SRC, platform.sourceDir, 'ROUTING.md');
@@ -129,7 +128,6 @@ function copyRoutingFile(ctx, platformKey) {
129
128
 
130
129
  const transform = (content) => {
131
130
  let c = content.replace(/\{\{SKILLS_DIR\}\}/g, platform.skillsRef);
132
- if (platformKey === 'opencode') c = normalizeOpenCodeBody(c);
133
131
  return c;
134
132
  };
135
133
  const { action } = writeManagedFile({
@@ -7,7 +7,6 @@ const { writeManagedFile, copyManagedTree } = require('../lib/managed-writer');
7
7
  const { report, treeAction } = require('./report');
8
8
  const { copyRulesDirectory } = require('./claude-runtime');
9
9
  const {
10
- normalizeCodexBody,
11
10
  managedRange,
12
11
  upsertManagedCodexBlock
13
12
  } = require('../lib/codex-install');
@@ -138,7 +137,10 @@ function installManagedAgentsMd(ctx) {
138
137
  ctx.results.errors++;
139
138
  return;
140
139
  }
141
- const block = normalizeCodexBody(fs.readFileSync(runtimeSource, 'utf8'));
140
+ // This template is already Codex-native. Running it through the generic
141
+ // Claude-to-Codex converter corrupts intentional cross-runtime wording such
142
+ // as the fail-safe instruction for Claude Code to ignore this block.
143
+ const block = fs.readFileSync(runtimeSource, 'utf8');
142
144
  const next = upsertManagedCodexBlock(existing, block);
143
145
  const action = !exists ? 'created' : next === existing ? 'unchanged' : 'updated';
144
146
 
@@ -17,10 +17,6 @@ const {
17
17
  } = require('../lib/context');
18
18
  const { writeManagedFile, copyManagedTree } = require('../lib/managed-writer');
19
19
  const { report, treeAction } = require('./report');
20
- const {
21
- convertOpenCodeAgentContent,
22
- convertOpenCodeCommandContent
23
- } = require('../lib/opencode-install');
24
20
  const {
25
21
  codexAgentName,
26
22
  convertCodexAgentContent
@@ -29,8 +25,7 @@ const {
29
25
  const SRC = path.join(__dirname, '../../src');
30
26
 
31
27
  /**
32
- * Spec command files installed for a platform. Claude ships spec-* commands;
33
- * OpenCode generates its own command wrappers elsewhere.
28
+ * Spec command files installed for a platform. Claude ships spec-* commands.
34
29
  */
35
30
  function getPlatformSpecFiles(platformKey, ctx) {
36
31
  if (platformKey === 'claude') {
@@ -76,7 +71,6 @@ function copyPlatformFiles(ctx, platformKey) {
76
71
  const tracker = ctx.trackers[platformKey];
77
72
  const platformFolder = platform.folder;
78
73
 
79
- // For OpenCode, text bodies are path-normalized (.claude → .opencode, etc.).
80
74
  const bodyTransform = getCopyOptions(platformKey, {}).transform;
81
75
 
82
76
  const skillsSourceDir = path.join(SRC, 'claude/skills');
@@ -162,9 +156,7 @@ function copyPlatformFiles(ctx, platformKey) {
162
156
  'tester.md', 'code-reviewer.md', 'fullstack-developer.md', 'debugger.md'
163
157
  ];
164
158
  let agentTransform;
165
- if (platformKey === 'opencode') {
166
- agentTransform = (content, src) => convertOpenCodeAgentContent(content, path.basename(src));
167
- } else if (platformKey === 'codex') {
159
+ if (platformKey === 'codex') {
168
160
  agentTransform = (content, src) => convertCodexAgentContent(content, path.basename(src));
169
161
  }
170
162
 
@@ -222,11 +214,7 @@ function copyPlatformFiles(ctx, platformKey) {
222
214
  return;
223
215
  }
224
216
  const cmdTransform = (content, s) => {
225
- let c = content.replace(/\{\{SKILLS_DIR\}\}/g, platform.skillsRef);
226
- if (platformKey === 'opencode') {
227
- c = convertOpenCodeCommandContent(c, path.basename(s));
228
- }
229
- return c;
217
+ return content.replace(/\{\{SKILLS_DIR\}\}/g, platform.skillsRef);
230
218
  };
231
219
  const { action } = writeManagedFile({
232
220
  src, dest, platformFolder, ctx, tracker, transform: cmdTransform
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Phase: post-install runtime configuration.
3
3
  *
4
- * OpenCode model + addressing config. Interactive prompts use ctx.ui (clack);
4
+ * Addressing config. Interactive prompts use ctx.ui (clack);
5
5
  * in non-interactive/dry-run they are skipped via fallbacks so spawned/CI runs
6
6
  * never hang.
7
7
  *
@@ -15,14 +15,12 @@ const fs = require('fs');
15
15
  const path = require('path');
16
16
  const { PLATFORMS } = require('../lib/context');
17
17
  const { LANGUAGE_LABELS } = require('../lib/i18n');
18
- const { setupOpenCodeModel, transformManagedOpenCodeContent } = require('../lib/opencode-install');
19
18
  const { transformManagedCodexContent } = require('../lib/codex-install');
20
19
  const { transformManagedClaudeContent } = require('./claude-runtime');
21
20
  const { transformManagedCoreContent } = require('../lib/instruction-blocks');
22
21
  const ASSISTANT_NAMES = {
23
22
  claude: 'Claude Code',
24
- codex: 'Codex CLI',
25
- opencode: 'OpenCode'
23
+ codex: 'Codex CLI'
26
24
  };
27
25
  const LANGUAGE_LABEL_BY_CODE = {
28
26
  vi: 'Vietnamese',
@@ -50,10 +48,6 @@ function instructionTargets(ctx) {
50
48
  file: path.join(process.cwd(), 'AGENTS.md'),
51
49
  transform: transformManagedCodexContent
52
50
  },
53
- opencode: {
54
- file: path.join(process.cwd(), 'AGENTS.md'),
55
- transform: transformManagedOpenCodeContent
56
- }
57
51
  };
58
52
  return Object.keys(targets)
59
53
  .filter((key) => ctx.platforms.includes(key))
@@ -161,7 +155,7 @@ function patchRuntimeLocale(ctx) {
161
155
 
162
156
  /** Human assistant name for the active platform(s). */
163
157
  function assistantName(platformKey) {
164
- return ASSISTANT_NAMES[platformKey] || ASSISTANT_NAMES.opencode;
158
+ return ASSISTANT_NAMES[platformKey] || platformKey;
165
159
  }
166
160
 
167
161
  function configureAddressing(ctx, userAddress) {
@@ -188,7 +182,7 @@ ${name} always addresses the user as "${userAddress}" throughout the conversatio
188
182
  }
189
183
 
190
184
  async function setupAddressing(ctx) {
191
- if (!ctx.platforms.some((key) => key === 'claude' || key === 'codex' || key === 'opencode')) return;
185
+ if (!ctx.platforms.some((key) => key === 'claude' || key === 'codex')) return;
192
186
 
193
187
  let existingName = null;
194
188
  for (const target of instructionTargets(ctx)) {
@@ -237,16 +231,10 @@ async function setupAddressing(ctx) {
237
231
  /** Run the post-install configuration sequence. */
238
232
  async function runPostInstall(ctx) {
239
233
  if (ctx.dryRun) {
240
- ctx.ui.info('[dry-run] Skipping OpenCode model / addressing setup');
234
+ ctx.ui.info('[dry-run] Skipping addressing setup');
241
235
  return ctx;
242
236
  }
243
237
 
244
- // OpenCode model uses its own readline; only run it when it won't block:
245
- // interactive, or an env override is present (which it reads without prompting).
246
- const hasModelEnv = Boolean(process.env.OPENCODE_MODEL || process.env.OPENCODE_DEFAULT_MODEL);
247
- if (ctx.platforms.includes('opencode') && (ctx.interactive || hasModelEnv)) {
248
- await setupOpenCodeModel(ctx.platforms, ctx.results);
249
- }
250
238
 
251
239
  await setupAddressing(ctx);
252
240
 
@@ -4,11 +4,11 @@
4
4
  * Ensures the root .gitignore carries CafeKit-managed patterns:
5
5
  * - workflow dirs (plans, shared research)
6
6
  * - install safety artifacts (backup, lock)
7
- * - runtime folders (.claude/, .opencode/, .codex/, .agents/) — reinstall via
7
+ * - runtime folders (.claude/, .codex/, .agents/) — reinstall via
8
8
  * `npx @haposoft/cafekit`; do not commit the local payload
9
9
  *
10
10
  * Layered with the in-folder `.claude/.gitignore` /
11
- * `.opencode/.gitignore` templates (secrets, skill deps, session
11
+ * runtime `.gitignore` templates (secrets, skill deps, session
12
12
  * state) so force-adds and partial un-ignores stay safe.
13
13
  *
14
14
  * Honors dry-run.
@@ -109,7 +109,6 @@ function ensureGitignore(ctx) {
109
109
  '.cafekit.lock',
110
110
  // Local runtime payload — reinstall with npx; keep out of git
111
111
  '.claude/',
112
- '.opencode/',
113
112
  '.codex/',
114
113
  '.agents/'
115
114
  ];
@@ -11,8 +11,7 @@ const path = require('path');
11
11
  const {
12
12
  PLATFORMS,
13
13
  detectPlatforms,
14
- getPlatformKeys,
15
- warnLegacyClaudeFolder
14
+ getPlatformKeys
16
15
  } = require('../lib/context');
17
16
  const { SUPPORTED, LANGUAGE_LABELS, OTHER_LABEL } = require('../lib/i18n');
18
17
 
@@ -162,7 +161,6 @@ async function resolvePlatforms(ctx) {
162
161
  ctx.ui.info(ctx.t('installingFor', {
163
162
  names: platformNames(requested)
164
163
  }));
165
- warnLegacyClaudeFolder(requested);
166
164
  reportInstallMode(ctx);
167
165
  return ctx;
168
166
  }
@@ -216,7 +214,6 @@ async function resolvePlatforms(ctx) {
216
214
  ctx.platforms = platforms;
217
215
 
218
216
  ctx.ui.info(ctx.t('installingFor', { names: platformNames(platforms) }));
219
- warnLegacyClaudeFolder(platforms);
220
217
 
221
218
  reportInstallMode(ctx);
222
219
 
@@ -225,7 +222,7 @@ async function resolvePlatforms(ctx) {
225
222
 
226
223
  /**
227
224
  * Get the installed platform from cafekit.json files.
228
- * Returns the platform key (e.g. 'claude', 'opencode') or null.
225
+ * Returns the platform key (e.g. 'claude', 'codex') or null.
229
226
  */
230
227
  function getInstalledPlatforms() {
231
228
  const installed = [];
@@ -55,7 +55,6 @@ function printSummary(ctx) {
55
55
  for (const key of platforms) {
56
56
  const messageKey = {
57
57
  claude: 'nsClaude',
58
- opencode: 'nsOpencode',
59
58
  codex: 'nsCodex'
60
59
  }[key];
61
60
  if (messageKey) lines.push(t(messageKey));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@haposoft/cafekit",
3
- "version": "0.16.0-rc.6",
4
- "description": "Native spec-driven workflows for Claude Code, Codex CLI, and OpenCode. Bundles CafeKit skills, runtime hooks, agents, and installer scaffolding.",
3
+ "version": "0.16.0-rc.7",
4
+ "description": "Native spec-driven workflows for Claude Code and Codex CLI. Bundles CafeKit skills, runtime hooks, agents, and installer scaffolding.",
5
5
  "author": "Haposoft <nghialt@haposoft.com>",
6
6
  "license": "MIT",
7
7
  "private": false,
@@ -56,9 +56,6 @@
56
56
  "engines": {
57
57
  "node": ">=18.0.0"
58
58
  },
59
- "devDependencies": {
60
- "@opencode-ai/plugin": "1.15.11"
61
- },
62
59
  "dependencies": {
63
60
  "@clack/prompts": "0.11.0",
64
61
  "picocolors": "^1.1.1"
@@ -12,9 +12,22 @@
12
12
  - macOS/Linux: `.claude/skills/.venv/bin/python3 scripts/<script>.py`
13
13
  - Windows: `.claude\skills\.venv\Scripts\python.exe scripts\<script>.py`
14
14
  - Consult `.claude/rules/state-sync.md`, `.claude/rules/hook-protocols.md`, and `.claude/rules/skill-workflow-routing.md` when their topics apply.
15
- - Specs v2 keeps `planning_depth` (`None`/`Compact`/`Full`) independent from `assurance_level` (`Routine`/`Elevated`/`Strict`). Any risk has an Elevated automatic floor; Strict is opt-in only for an explicit user/project independent-audit requirement or a user-confirmed scope-specific audit decision, never a keyword or severity label. Run the artifact router before discovery: None has no durable spec; Compact/Full share the bounded `spec.json` + `requirements.md` + `design.md` core. Research requires material unresolved uncertainty, an external-current fact, or an explicit request; tasks require typed coordination topology. Full/Strict never create either automatically.
16
- - In v2.1, `spec.json` is machine authority and Markdown is a human projection. Task plans have exactly seven sections: `Outcome`, `Scope`, `Anchors and Ownership`, `Changes`, `Acceptance`, `Dependencies`, `Verification Plan`; the only ownership table is `ID | Type | Target | Role | Access | Action`.
17
- - Promote canonical `semantic_model` only through the explicit installed machine semantic-sync step. A semantic Markdown edit requires resynchronization and round-trip validation; never hand-author the machine shape.
18
- - `coordination.boundaries` typed as ownership/dependency/transition/proof/parallel is topology authority. Legacy trigger fields, priority markers, related-file lists, approval fields, and prose are inert compatibility inputs, never canonical authoring.
19
- - Canonical lifecycle is exactly `in_progress`, `paused`, `blocked`, or `done`. Technical readiness differs from closeout; authors cannot self-declare readiness, review authority, execution proof, or final `done`.
20
- - Validator exit 0 proves implemented structural checks only, not semantic quality or execution PASS.
15
+ - New Specs work uses the process-first flow. `/hapo:specs` opens C1, writes
16
+ `specs/<feature>/plan.md` with flat `task-NN-*.md` files beside it, then opens
17
+ C2 after adversarial review. It never starts implementation.
18
+ - Start implementation only through a new explicit `/hapo:develop` invocation.
19
+ Execute one unblocked task at a time; each task has exactly one `Status:`
20
+ field and the controller is its sole state-and-proof writer.
21
+ - Use `/hapo:sync` for surgical updates to observed file state. A done task
22
+ requires a canonical final inline `## Receipt` with the exact command,
23
+ `Exit: 0`, `Verification: PASS`, runtime-derived Base and Head values, and
24
+ non-empty fenced current output.
25
+ - At C3, show current receipts and unresolved limitations. The user decides
26
+ completion; no command, review, or host state may invent approval or proof.
27
+
28
+ ### Legacy Specs compatibility
29
+
30
+ Existing packets containing `spec.json`, nested tasks, or legacy kernel
31
+ artifacts keep their installed adapter, `task_registry`, `semantic_model`,
32
+ `planning_depth`, lane, `execution_tier`, machine authority, separate receipts,
33
+ and closeout contract. Do not migrate them during unrelated process-first work.