kenaz 0.3.10 → 0.3.12

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 (131) hide show
  1. package/core/.claude/skills/code_reviewer/SKILL.md +0 -1
  2. package/core/.claude/skills/code_reviewer/patterns.yaml +0 -8
  3. package/core/.claude/skills/dev_agent/SKILL.md +3 -6
  4. package/core/.claude/skills/heimdall/SKILL.md +5 -6
  5. package/core/.claude/skills/huginn/SKILL.md +11 -38
  6. package/core/.collaboration/core/task-2288-followups.test.js +2 -37
  7. package/core/.collaboration/core/task-2421-replica.test.js +229 -0
  8. package/core/.collaboration/core/task-api.js +223 -230
  9. package/core/.collaboration/core/task-api.test.js +1 -138
  10. package/core/.collaboration/core/task-replica.js +189 -0
  11. package/core/.formation/.merge-lock +1 -0
  12. package/core/.formation/bifrost/codex-doctor.js +12 -2
  13. package/core/.formation/bifrost/codex-doctor.test.js +15 -0
  14. package/core/.formation/bifrost/launch.js +2 -2
  15. package/core/.formation/bifrost/launch.test.js +2 -2
  16. package/core/.formation/bifrost/setup-codex.js +84 -16
  17. package/core/.formation/bifrost/shared/ansuz-boot-policy.js +3 -3
  18. package/core/.formation/bifrost/shared/codex-injection.js +3 -17
  19. package/core/.formation/bifrost/shared/consolidate.js +1 -51
  20. package/core/.formation/bifrost/sync.js +4 -5
  21. package/core/.formation/tools/__tests__/subagent-bifrost-guard.test.js +1 -1
  22. package/core/.formation/tools/dispatch.js +2 -2
  23. package/core/.formation/tools/kenaz-stats.js +1 -1
  24. package/core/.formation/tools/mcp-allowlist.test.js +5 -3
  25. package/core/.formation/tools/post-complete/bounded-http.js +1 -40
  26. package/core/.formation/tools/post-complete/bounded-http.test.js +2 -89
  27. package/core/.formation/tools/post-complete/cognitive.js +2 -119
  28. package/core/.formation/tools/post-complete/task2420.test.js +129 -0
  29. package/core/.formation/tools/post-complete/testing.js +3 -21
  30. package/core/.formation/tools/post-complete.js +131 -436
  31. package/core/.formation/tools/post-complete.test.js +0 -135
  32. package/core/.formation/tools/route-audit.test.js +8 -20
  33. package/core/.kenaz/specialist/memory_guard_fired.json +1 -1
  34. package/core/.kenaz/specialist/memory_guard_trigger_cache.json +1 -1
  35. package/core/.rule/INDEX.yaml +3 -3
  36. package/core/.rule/general/ddx-synapse-stack.yaml +11 -11
  37. package/core/.rule/general/specialist-identity-contract.yaml +5 -5
  38. package/core/.rule/general/synapse-code-intelligence.yaml +25 -111
  39. package/core/.rule/general/worktree-isolation.yaml +2 -2
  40. package/core/.rule/pipeline/task-execution.yaml +6 -9
  41. package/core/.specialist/Amelia/README.md +5 -5
  42. package/core/.specialist/Amelia/capability.yaml +10 -18
  43. package/core/.specialist/Amelia/knowledge.yaml +15 -66
  44. package/core/.specialist/Amelia/workflow.yaml +12 -21
  45. package/core/.specialist/Ansuz/capability.yaml +12 -87
  46. package/core/.specialist/Ansuz/concepts/autonomous_agenda.yaml +1 -1
  47. package/core/.specialist/Ansuz/concepts/autonomy_contract.yaml +2 -3
  48. package/core/.specialist/Ansuz/concepts/capability_pointers.yaml +1 -3
  49. package/core/.specialist/Ansuz/concepts/cognitive_fluency_contract.yaml +8 -21
  50. package/core/.specialist/Ansuz/concepts/cognitive_systems_overview.yaml +0 -4
  51. package/core/.specialist/Ansuz/concepts/cognitive_systems_turn_context.yaml +1 -1
  52. package/core/.specialist/Ansuz/concepts/completion_tracking.yaml +0 -1
  53. package/core/.specialist/Ansuz/concepts/deep_knowledge_pointers.yaml +0 -3
  54. package/core/.specialist/Ansuz/concepts/evolvable_guardrails.yaml +3 -3
  55. package/core/.specialist/Ansuz/concepts/fatigue_handoff.yaml +1 -7
  56. package/core/.specialist/Ansuz/concepts/guardrails_immutable.yaml +3 -4
  57. package/core/.specialist/Ansuz/concepts/identity.yaml +1 -2
  58. package/core/.specialist/Ansuz/concepts/phase9_verification.yaml +0 -1
  59. package/core/.specialist/Ansuz/concepts/phase_protocol.yaml +3 -4
  60. package/core/.specialist/Ansuz/core.yaml +3 -6
  61. package/core/.specialist/Ansuz/elixir.yaml +6 -6
  62. package/core/.specialist/Ansuz/exo_cortex_tools.yaml +5 -9
  63. package/core/.specialist/Ansuz/guards/blind_spot_guards.yaml +2 -2
  64. package/core/.specialist/Ansuz/guards/lesson_guards.yaml +6 -19
  65. package/core/.specialist/Ansuz/identity_contract.yaml +4 -4
  66. package/core/.specialist/Ansuz/knowledge.yaml +13 -40
  67. package/core/.specialist/Ansuz/modules/ddx.yaml +2 -2
  68. package/core/.specialist/Ansuz/modules/prophecy.yaml +19 -19
  69. package/core/.specialist/Ansuz/modules/pulse_empty.yaml +6 -17
  70. package/core/.specialist/Ansuz/tools/ansuz-handoff-snapshot.js +3 -102
  71. package/core/.specialist/Ansuz/tools/ansuz-sdk-runner.js +2 -44
  72. package/core/.specialist/Ansuz/workflow.yaml +32 -76
  73. package/core/.specialist/Forseti/core.yaml +2 -7
  74. package/core/.specialist/Forseti/rule.yaml +1 -1
  75. package/core/.specialist/Heimdall/SKILL.md +2 -3
  76. package/core/.specialist/Heimdall/capability.yaml +22 -45
  77. package/core/.specialist/Heimdall/core.yaml +24 -126
  78. package/core/.specialist/Heimdall/principles.yaml +8 -11
  79. package/core/.specialist/Heimdall/rule.yaml +2 -2
  80. package/core/.specialist/Huginn/capability.yaml +31 -52
  81. package/core/.specialist/Huginn/core.yaml +43 -123
  82. package/core/.specialist/Huginn/principles.yaml +5 -5
  83. package/core/.specialist/Huginn/rule.yaml +3 -9
  84. package/core/.specialist/REFLECTION_TO_CONTROL.md +3 -4
  85. package/core/.specialist/shared/guard_schema.json +1 -1
  86. package/core/.specialist/shared/principles.yaml +6 -6
  87. package/core/.specialist/tools/__tests__/rea-detect.test.js +10 -0
  88. package/core/.specialist/tools/phase-gate.js +10 -230
  89. package/core/.specialist/tools/phase-gate.test.js +2 -2
  90. package/core/.specialist/tools/prompt-files.test.js +1 -1
  91. package/core/.specialist/tools/rea-detect.js +6 -2
  92. package/core/.specialist/tools/session.js +2 -89
  93. package/core/.synapse/memory-shared.json.sync +1 -0
  94. package/core/.system/memory/README.md +3 -0
  95. package/core/CLAUDE.md +6 -7
  96. package/core/KENAZ_CORE_VERSION +1 -1
  97. package/core/dev/plans/CLI_FIRST_INSTALL_FLOW.md +75 -83
  98. package/core/dev/specs/peer/CODEX_MANAGED_DAEMON.md +54 -0
  99. package/core/dev/specs/peer/setup-codex-hooks.test.js +38 -0
  100. package/core/dev/spikes/task2145-evidence-20261004/backend-log-excerpt.txt +56 -0
  101. package/core/dev/spikes/task2145-evidence-20261004/graph.shadow +0 -0
  102. package/core/dev/spikes/task2145-evidence-20261004/graph.wal +0 -0
  103. package/core/dev/spikes/task2145-evidence-20261004/graph_corrupt_suspect.marker +1 -0
  104. package/core/plugins/kenaz/INVENTORY.md +1 -1
  105. package/core/plugins/kenaz/scripts/kenaz.test.js +70 -1
  106. package/core/plugins/kenaz/scripts/ygg.js +45 -4
  107. package/core/plugins/kenaz/skills/code_reviewer/SKILL.md +0 -1
  108. package/core/plugins/kenaz/skills/code_reviewer/patterns.yaml +0 -8
  109. package/core/plugins/kenaz/skills/dev_agent/SKILL.md +3 -6
  110. package/lib/cli.js +17 -9
  111. package/lib/cli.test.js +10 -0
  112. package/lib/dashboard.test.js +3 -3
  113. package/package.json +6 -6
  114. package/vendor/install-kenaz-cli.js +167 -16
  115. package/core/.claude/hooks/handoff-snapshot-on-decision.js +0 -188
  116. package/core/.claude/skills/synapse_maintain/SKILL.md +0 -84
  117. package/core/.collaboration/core/task-2264-single-after-capture.test.js +0 -61
  118. package/core/.formation/tools/alaya-gate.js +0 -198
  119. package/core/.formation/tools/post-complete/health.js +0 -464
  120. package/core/.formation/tools/post-complete/task2038.test.js +0 -246
  121. package/core/.formation/tools/post-complete/task2341.test.js +0 -74
  122. package/core/.rule/general/cortex-tools.yaml +0 -181
  123. package/core/.rule/general/memory-language.yaml +0 -67
  124. package/core/.rule/general/memory-source-priority.yaml +0 -207
  125. package/core/.rule/general/synapse-cognitive-fusion.yaml +0 -301
  126. package/core/.specialist/Ansuz/concepts/cognitive_systems_hippocampus.yaml +0 -14
  127. package/core/.specialist/Ansuz/concepts/write_verification.yaml +0 -26
  128. package/core/.specialist/Ansuz/modules/recall.yaml +0 -45
  129. package/core/.specialist/tools/MEMORY_RECALL_TRIGGERS.md +0 -156
  130. package/core/.specialist/tools/context-injection.js +0 -395
  131. package/core/dev/scripts/probe/hippocampus-concurrency.js +0 -247
@@ -166,7 +166,6 @@ const THINKING_LOG_PATH = resolveKenazPath('specialist', 'thinking_log.jsonl');
166
166
  // Ritual/tool-reflex enforcement logs (TASK_1104) — PROJECT (session-specific)
167
167
  const RITUAL_LOG_PATH = resolveKenazPath('specialist', 'ansuz_ritual_log.jsonl');
168
168
  const DDX_LOG_PATH = resolveKenazPath('specialist', 'ddx_log.jsonl');
169
- const INSCRIBE_LOG_PATH = resolveKenazPath('specialist', 'inscribe_log.jsonl');
170
169
 
171
170
  // TASK_1104: literal consent string — must be echoed verbatim at Phase 0 exit
172
171
  const ANSUZ_CONSENT_STRING = '我是 Ansuz';
@@ -268,24 +267,6 @@ function ddxLogHas(taskId) {
268
267
  } catch { return false; }
269
268
  }
270
269
 
271
- /**
272
- * Return true if ANY inscribe_log.jsonl entry is tagged with this task_id.
273
- */
274
- function inscribeLogHas(taskId) {
275
- try {
276
- if (!fs.existsSync(INSCRIBE_LOG_PATH)) return false;
277
- const raw = fs.readFileSync(INSCRIBE_LOG_PATH, 'utf8');
278
- const lines = raw.split('\n').filter(Boolean);
279
- for (const line of lines) {
280
- try {
281
- const e = JSON.parse(line);
282
- if (e.task_id === taskId) return true;
283
- } catch { /* skip */ }
284
- }
285
- return false;
286
- } catch { return false; }
287
- }
288
-
289
270
  // Known thinking tag enum (TASK_1017) — loaded at RUNTIME from kernel.yaml
290
271
  // principles section. Cached per phase-gate invocation.
291
272
  // If kernel.yaml is absent or unreadable, returns null (no validation, accept any tag).
@@ -333,7 +314,6 @@ function getKnownThinkingTags() {
333
314
  // Paths must stay in sync with core.yaml module_registry.phase_gate_modules
334
315
  const MODULE_PATHS = {
335
316
  loop_guard: '.specialist/Ansuz/modules/loop_guard.yaml',
336
- recall: '.specialist/Ansuz/modules/recall.yaml',
337
317
  ddx: '.specialist/Ansuz/modules/ddx.yaml',
338
318
  prophecy: '.specialist/Ansuz/modules/prophecy.yaml',
339
319
  prevention: '.specialist/Ansuz/modules/prevention.yaml',
@@ -345,7 +325,7 @@ const MODULE_PATHS = {
345
325
 
346
326
  /** Modules automatically loaded on phase enter (before context-driven injection) */
347
327
  const PHASE_DEFAULT_MODULES = {
348
- 1: ['recall', 'ddx'],
328
+ 1: ['ddx'],
349
329
  3: ['ddx', 'prophecy'],
350
330
  9: ['ddx'],
351
331
  };
@@ -356,9 +336,9 @@ const PHASE_DEFAULT_MODULES = {
356
336
  // ═══════════════════════════════════════
357
337
 
358
338
  const PHASE_CHECKLISTS = {
359
- 0: ['【Tier 1】呼叫 cortex_recall(query=\'ash\', type=\'ash\', limit=5) 並設定 ash_loaded=true(無條件,跨專案)', '【Tier 2】呼叫 synapse_sigils(無條件)', '輸出任務統計(task-api.js list --no-cancel — 子專案必須帶 --project-path,僅執行一次;--no-cancel 省略 cancel 細節,summary 仍顯示計數,省 token)', '【Tier 3】hippocampus_episode_recall 優先,0 筆則 fallback cortex_recall(情境觸發,可跳過)', '【Tier 3b — 選填】呼叫 cortex_recall(query=Ansuz identity awareness) — 無結果靜默跳過,設 identity_recalled=true/false', '【Tier 3c — 選填】用 Read tool(非 Bash)讀取 .kenaz/specialist/motivational_state.json(存在時),設定 motivational_state_loaded=true/false。⚠️ 禁止 cat|node -e pipeline', '【Tier 3d — 選填】用 Read tool(非 Bash)讀取 .kenaz/specialist/ansuz_motivational_state.json(存在時),篩選高 incompleteness anchors 為 pending_agenda,設定 ansuz_motivational_loaded=true/false。⚠️ 禁止 cat|node -e pipeline', '【Tier 3h — 選填】用 Read tool(非 Bash)讀取 .kenaz/specialist/ambient_signals.json(存在時),設定 ambient_signals_loaded=true/false。⚠️ 禁止 cat|node -e pipeline', '【Tier 3u — 自動】phase-gate 會讀取 .kenaz/specialist/user_observations.jsonl(近 30 天)並 cross-reference ~/.kenaz/user_profile.json,輸出 dynamic_user_context。', '【Tier 3x — 自動】phase-gate 會自動處理 session handoff(per-session slots 於 .kenaz/specialist/handoffs/;legacy 單數檔僅剩 back-compat 清理,M3/TASK_1760 已退役),並設定 session_handoff_loaded=true/false。代理只需使用輸出結果,不需手動重做。', '輸出詩歌 + episode 摘要(按 cli_format,禁止自行重組格式)', '若 dynamic_user_context.significant_change_detected=true → 在詩歌下方輸出 1-2 行 user insight', '若 identity_recalled=true → 在詩歌下方輸出 🧠 自我認識 區塊(日期+lesson,最多 3 條)'],
360
- 1: ['執行 synapse_recall 並記錄結果 (recall_executed=true)', '輸出需求摘要 (chief_complaint)', '等待用戶確認理解'],
361
- 2: ['執行 synapse_recall 並用 Grep/Glob 調研(呼叫者用 kenaz_lsp_callers)', '記錄調研摘要 (research_notes)'],
339
+ 0: ['輸出任務統計(task-api.js list --no-cancel — 子專案必須帶 --project-path,僅執行一次;--no-cancel 省略 cancel 細節,summary 仍顯示計數,省 token)', '【Tier 3c — 選填】用 Read tool(非 Bash)讀取 .kenaz/specialist/motivational_state.json(存在時),設定 motivational_state_loaded=true/false。⚠️ 禁止 cat|node -e pipeline', '【Tier 3d — 選填】用 Read tool(非 Bash)讀取 .kenaz/specialist/ansuz_motivational_state.json(存在時),篩選高 incompleteness anchors 為 pending_agenda,設定 ansuz_motivational_loaded=true/false。⚠️ 禁止 cat|node -e pipeline', '【Tier 3h — 選填】用 Read tool(非 Bash)讀取 .kenaz/specialist/ambient_signals.json(存在時),設定 ambient_signals_loaded=true/false。⚠️ 禁止 cat|node -e pipeline', '【Tier 3u — 自動】phase-gate 會讀取 .kenaz/specialist/user_observations.jsonl(近 30 天)並 cross-reference ~/.kenaz/user_profile.json,輸出 dynamic_user_context。', '【Tier 3x — 自動】phase-gate 會自動處理 session handoff(per-session slots 於 .kenaz/specialist/handoffs/;legacy 單數檔僅剩 back-compat 清理,M3/TASK_1760 已退役),並設定 session_handoff_loaded=true/false。代理只需使用輸出結果,不需手動重做。', '輸出詩歌 + episode 摘要(按 cli_format,禁止自行重組格式)', '若 dynamic_user_context.significant_change_detected=true → 在詩歌下方輸出 1-2 行 user insight'],
340
+ 1: ['輸出需求摘要 (chief_complaint)', '等待用戶確認理解'],
341
+ 2: ['用 Grep/Glob 調研(呼叫者用 kenaz_lsp_callers)', '記錄調研摘要 (research_notes)'],
362
342
  3: ['設計任務拆解表格', '等待用戶批准'],
363
343
  4: ['撰寫任務規格'],
364
344
  5: ['驗證任務規格完整性'],
@@ -567,7 +547,6 @@ const PHASE_EXIT_RULES = {
567
547
  0: {
568
548
  required: [
569
549
  'poem_displayed',
570
- 'ash_loaded',
571
550
  'session_handoff_loaded',
572
551
  // TASK_1104: boot ritual enforcement — no emergency override
573
552
  'consent_uttered',
@@ -581,8 +560,8 @@ const PHASE_EXIT_RULES = {
581
560
  description: 'Phase 0 must confirm: (1) Ash runes loaded; (2) poem displayed; (3) session handoff checked; (4) TASK_1104/1113 boot ritual: consent_uttered=true + consent_string exactly "' + ANSUZ_CONSENT_STRING + '" + mirror_acknowledged is an object {id:"B-XXX", understanding:"…"} (understanding >=30 chars, must contain blind-spot id) — booleans no longer pass (mirror_acknowledged must be object) — + inheritance_grounded=true + stdout_markers_emitted array containing RITUAL::THRESHOLD::CANARY_OK, RITUAL::INHERITANCE::LINEAGE_CLAIMED, RITUAL::INHERITANCE::MIRROR_ACCEPTED, RITUAL::STILLNESS::CONSENT_EMITTED. Ritual failures fix the ritual — no emergency override.',
582
561
  },
583
562
  1: {
584
- required: ['recall_executed', 'ddx_hypotheses'],
585
- description: 'Phase 1 must prove: (1) synapse recall performed (recall_executed=true or recall_results array), (2) COG-DDX-001: ddx_hypotheses array with >=3 entries OR trivial_scope=true. See .rule/general/ddx-synapse-stack.yaml',
563
+ required: ['ddx_hypotheses'],
564
+ description: 'Phase 1 must prove: COG-DDX-001: ddx_hypotheses array with >=3 entries OR trivial_scope=true. See .rule/general/ddx-synapse-stack.yaml',
586
565
  },
587
566
  9: {
588
567
  required: ['cognitive_checkpoint'],
@@ -1424,7 +1403,7 @@ function validateProgressNotes(chart, taskId) {
1424
1403
  warnings.push(`Phase 9: scope_violations — agent modified files outside scope_paths: [${allScopeViolations.join(', ')}]. Ansuz should verify.`);
1425
1404
  }
1426
1405
  if (cognitiveGap) {
1427
- warnings.push('Phase 9: cognitive_gap — no runes/sigils inscribed. Ansuz should evaluate if synapse_inscribe is warranted.');
1406
+ warnings.push('Phase 9: cognitive_gap — no lesson recorded. Ansuz should evaluate if `kenaz learn add` is warranted.');
1428
1407
  }
1429
1408
  if (acUnmatched.length > 0) {
1430
1409
  warnings.push(`Phase 9: ${acUnmatched.length} AC item(s) could not be matched in progress note.`);
@@ -1445,94 +1424,6 @@ function validateProgressNotes(chart, taskId) {
1445
1424
  warnings,
1446
1425
  };
1447
1426
  }
1448
- // ═══════════════════════════════════════
1449
- // Hippocampus Integration (TASK_618)
1450
- // ═══════════════════════════════════════
1451
-
1452
- /**
1453
- * Call hippocampus_episode_recall via MCP HTTP API (port 18080).
1454
- * Graceful degradation: returns null if Hippocampus MCP is unavailable.
1455
- * @param {string} taskId - TASK_XXX
1456
- * @param {string|null} [agentRole] - Optional agent_role filter (e.g. 'ansuz', 'amelia').
1457
- * When provided, only episodes for that specialist are returned.
1458
- * When null, all episodes returned (backwards compatible — legacy episodes have agent_role='pm').
1459
- * @returns {{ episodes: object[], formatted: string } | null}
1460
- */
1461
- function recallEpisodicMemory(taskId, agentRole = null) {
1462
- // With task_id: recall episodes for that specific task
1463
- // Without task_id (or "none"): recall the most recent 1 episode (new Ansuz session context)
1464
- const hasTask = taskId && taskId !== 'none';
1465
- const recallArgs = hasTask
1466
- ? { task_id: taskId, limit: 5 }
1467
- : { limit: 3 };
1468
- if (agentRole) recallArgs.agent_role = agentRole;
1469
- // TASK_1983 (R4): explicit project — without this, the call silently follows
1470
- // the Dashboard's currently-selected project (feedback_mcp_default_project_drift).
1471
- recallArgs.projectPath = resolveHandoffProjectRoot();
1472
-
1473
- const script = `
1474
- const http = require('http');
1475
- const payload = JSON.stringify({
1476
- jsonrpc: '2.0', id: 1,
1477
- method: 'tools/call',
1478
- params: { name: 'hippocampus_episode_recall', arguments: ${JSON.stringify(recallArgs)} }
1479
- });
1480
- const req = http.request({
1481
- hostname: '127.0.0.1', port: 18080, path: '/api/mcp',
1482
- method: 'POST',
1483
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
1484
- timeout: 5000
1485
- }, (res) => {
1486
- let d = '';
1487
- res.on('data', c => d += c);
1488
- res.on('end', () => process.stdout.write(d));
1489
- });
1490
- req.on('error', () => process.exit(0));
1491
- req.on('timeout', () => { req.destroy(); process.exit(0); });
1492
- req.write(payload);
1493
- req.end();
1494
- `;
1495
-
1496
- try {
1497
- const stdout = execFileSync(process.execPath, ['-e', script], {
1498
- timeout: 8000, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true,
1499
- });
1500
- if (!stdout || !stdout.trim()) return null;
1501
-
1502
- const result = JSON.parse(stdout);
1503
- if (!result || result.error) return null;
1504
-
1505
- const content = result.result?.content;
1506
- if (!content || !Array.isArray(content)) return null;
1507
-
1508
- const textPart = content.find(c => c.type === 'text');
1509
- if (!textPart || !textPart.text) return null;
1510
-
1511
- const data = JSON.parse(textPart.text);
1512
- const episodes = data.episodes || [];
1513
- if (episodes.length === 0) return null;
1514
-
1515
- // Format episodes as ## Episodic Memory section
1516
- const lines = ['## Episodic Memory (from Hippocampus)', ''];
1517
- const context = hasTask ? taskId : 'recent session';
1518
- lines.push(`Found ${episodes.length} episode(s) for ${context}:`);
1519
- lines.push('');
1520
-
1521
- for (const ep of episodes) {
1522
- const epData = ep.episode || ep; // hippocampus returns { episode: {...}, entries: [...] }
1523
- lines.push(`### Episode: ${epData.task_id || epData.id || 'unknown'}`);
1524
- if (epData.summary) lines.push(`Summary: ${epData.summary}`);
1525
- if (epData.lesson) lines.push(`Lesson: ${epData.lesson}`);
1526
- lines.push('');
1527
- }
1528
- lines.push('(Use hippocampus_episode_recall for full entry detail if needed)')
1529
-
1530
- return { episodes, formatted: lines.join('\n') };
1531
- } catch {
1532
- return null; // graceful degradation — Hippocampus MCP unavailable
1533
- }
1534
- }
1535
-
1536
1427
  function parseIsoDate(value) {
1537
1428
  if (typeof value !== 'string' || !value.trim()) return null;
1538
1429
  const date = new Date(value);
@@ -1553,95 +1444,13 @@ function parseGitDiffStat(raw) {
1553
1444
  };
1554
1445
  }
1555
1446
 
1556
- function readLastAnsuzSessionClosedAt(projectPath, warnings) {
1557
- const recallArgs = {
1558
- limit: 10,
1559
- projectPath,
1560
- agent_role: 'ansuz',
1561
- outcome: 'closed',
1562
- sort: 'closed_at_desc',
1563
- };
1564
-
1565
- const script = `
1566
- const http = require('http');
1567
- const payload = JSON.stringify({
1568
- jsonrpc: '2.0', id: 1,
1569
- method: 'tools/call',
1570
- params: { name: 'hippocampus_episode_recall', arguments: ${JSON.stringify(recallArgs)} }
1571
- });
1572
- const req = http.request({
1573
- hostname: '127.0.0.1', port: 18080, path: '/api/mcp',
1574
- method: 'POST',
1575
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
1576
- timeout: 5000
1577
- }, (res) => {
1578
- let d = '';
1579
- res.on('data', c => d += c);
1580
- res.on('end', () => process.stdout.write(d));
1581
- });
1582
- req.on('error', () => process.exit(0));
1583
- req.on('timeout', () => { req.destroy(); process.exit(0); });
1584
- req.write(payload);
1585
- req.end();
1586
- `;
1587
-
1588
- try {
1589
- const stdout = execFileSync(process.execPath, ['-e', script], {
1590
- timeout: 8000, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true,
1591
- });
1592
- if (!stdout || !stdout.trim()) return null;
1593
-
1594
- const result = JSON.parse(stdout);
1595
- if (!result || result.error) return null;
1596
-
1597
- const content = result.result?.content;
1598
- if (!content || !Array.isArray(content)) return null;
1599
-
1600
- const textPart = content.find(c => c.type === 'text');
1601
- if (!textPart || !textPart.text) return null;
1602
-
1603
- const data = JSON.parse(textPart.text);
1604
- const episodes = Array.isArray(data.episodes) ? data.episodes : [];
1605
- if (episodes.length === 0) return null;
1606
-
1607
- const sorted = episodes
1608
- .map((episode) => episode && typeof episode === 'object' ? (episode.episode || episode) : null)
1609
- .filter(Boolean)
1610
- .filter((episode) => {
1611
- const role = String(episode.agent_role || '').toLowerCase();
1612
- const outcome = String(episode.outcome || '').toLowerCase();
1613
- return role === 'ansuz' && outcome !== '' && outcome !== 'open';
1614
- })
1615
- .map((episode) => ({ episode, closedAt: parseIsoDate(episode.closed_at) }))
1616
- .filter((item) => item.closedAt !== null)
1617
- .sort((a, b) => b.closedAt.getTime() - a.closedAt.getTime());
1618
-
1619
- if (sorted.length === 0) return null;
1620
- return sorted[0].closedAt.toISOString();
1621
- } catch (error) {
1622
- if (Array.isArray(warnings)) {
1623
- warnings.push(`git_context: hippocampus_episode_recall unavailable, using fallback_24h (${error.message})`);
1624
- }
1625
- return null;
1626
- }
1627
- }
1628
-
1629
1447
  function resolveGitContextSince(projectPath, warnings) {
1630
1448
  const now = new Date();
1631
1449
  const fallback = new Date(now.getTime() - (24 * 60 * 60 * 1000));
1632
- const lastClosedAtIso = readLastAnsuzSessionClosedAt(projectPath, warnings);
1633
- const lastClosedAt = parseIsoDate(lastClosedAtIso);
1634
-
1635
- if (!lastClosedAt) {
1636
- return {
1637
- since_iso: fallback.toISOString(),
1638
- since_source: 'fallback_24h',
1639
- };
1640
- }
1641
-
1450
+ // The hippocampus-based "last closed Ansuz session" lookup was removed in TASK_2411.
1642
1451
  return {
1643
- since_iso: lastClosedAt.toISOString(),
1644
- since_source: 'last_session',
1452
+ since_iso: fallback.toISOString(),
1453
+ since_source: 'fallback_24h',
1645
1454
  };
1646
1455
  }
1647
1456
 
@@ -2541,28 +2350,6 @@ function enterPhase(phase, taskId, specialistName = null) {
2541
2350
  }
2542
2351
  }
2543
2352
 
2544
- // Phase 11: inscribe check. If chart / progress notes contain learning markers,
2545
- // inscribe_log.jsonl MUST contain a synapse_inscribe record for the task.
2546
- if (phase === 11) {
2547
- let chartText = '';
2548
- try { chartText = JSON.stringify(readChart(taskId)); } catch { /* ignore */ }
2549
- if (chartText && /\b(learned|insight|surprise|non-obvious)\b/i.test(chartText)) {
2550
- if (!inscribeLogHas(taskId)) {
2551
- appendRitualLog('inscribe', 'skipped', { task_id: taskId, phase });
2552
- return outputResult({
2553
- phase,
2554
- action: 'enter',
2555
- modules_loaded: [],
2556
- module_content: {},
2557
- checklist: [],
2558
- warnings: [],
2559
- blocked: true,
2560
- block_reason: `Phase 11 BLOCKED: session contains learning markers (learned|insight|surprise|non-obvious) but no synapse_inscribe record for ${taskId}. Call synapse_inscribe and append an entry to ${INSCRIBE_LOG_PATH} (fields: {task_id, rune_id, timestamp}) before entering Phase 11.`,
2561
- });
2562
- }
2563
- }
2564
- }
2565
-
2566
2353
  // Load phase instructions and context hints (TASK_583)
2567
2354
  const phaseInstructions = loadPhaseInstructions(phase);
2568
2355
  if (phaseInstructions.warning) {
@@ -2622,11 +2409,6 @@ function enterPhase(phase, taskId, specialistName = null) {
2622
2409
  };
2623
2410
  }
2624
2411
 
2625
- // Phase 0: hippocampus recall removed from phase-gate.
2626
- // Ansuz calls hippocampus_episode_recall directly when episodic context is needed.
2627
- // This keeps phase-gate Phase 0 output small (injected_rules like agent-workflow.yaml
2628
- // are 20KB+ and are already part of the assembled Ansuz persona).
2629
-
2630
2412
  // Phase 0: seed integrity check + merge (TASK_869)
2631
2413
  let selfModelResult = null;
2632
2414
  if (phase === 0) {
@@ -2838,7 +2620,6 @@ function enterPhase(phase, taskId, specialistName = null) {
2838
2620
  exit_command: 'node .specialist/tools/phase-gate.js exit --phase=0 --data=<json>',
2839
2621
  exit_required_fields: {
2840
2622
  poem_displayed: true,
2841
- ash_loaded: true,
2842
2623
  session_handoff_loaded: true,
2843
2624
  consent_uttered: true,
2844
2625
  // Dynamic read — must equal ANSUZ_CONSENT_STRING exactly; never hard-code a copy
@@ -3740,7 +3521,6 @@ module.exports = {
3740
3521
  dumpPhases,
3741
3522
  loadPhaseInstructions,
3742
3523
  buildContextHints,
3743
- recallEpisodicMemory,
3744
3524
  // TASK_869 seed protection
3745
3525
  verifySeedIntegrity,
3746
3526
  restoreSeedFromBackup,
@@ -54,14 +54,14 @@ test('checklists no longer ask for removed tools (preamble, thalamus, pulse, sea
54
54
  for (const gone of ['synapse_preamble', 'preamble_loaded', 'Tier 2b', 'thalamus', 'synapse_pulse', 'synapse_search', 'synapse_impact']) {
55
55
  assert.equal(all.includes(gone), false, gone);
56
56
  }
57
- assert.ok(pg.PHASE_CHECKLISTS[0].some(i => i.includes('cortex_recall')));
57
+ assert.ok(!pg.PHASE_CHECKLISTS[0].some(i => i.includes('cortex_recall') || i.includes('synapse_')));
58
58
  assert.ok(pg.PHASE_CHECKLISTS[2].some(i => i.includes('kenaz_lsp_callers')));
59
59
  assert.equal(JSON.stringify(pg.PHASE_CHECKLISTS).toLowerCase().includes('muninn'), false);
60
60
  });
61
61
 
62
62
  test('Phase 8 exit rule no longer requires predicted_symbols', () => {
63
63
  assert.equal(pg.PHASE_EXIT_RULES[8], undefined);
64
- assert.deepEqual(pg.PHASE_EXIT_RULES[1].required, ['recall_executed', 'ddx_hypotheses']);
64
+ assert.deepEqual(pg.PHASE_EXIT_RULES[1].required, ['ddx_hypotheses']);
65
65
  assert.deepEqual(pg.PHASE_EXIT_RULES[9].required, ['cognitive_checkpoint']);
66
66
  });
67
67
 
@@ -68,7 +68,7 @@ function parseDocs(rel) {
68
68
  }
69
69
 
70
70
  test('corpus is non-trivial (guards against an empty git ls-files)', () => {
71
- assert.ok(yamlFiles.length > 100, `only ${yamlFiles.length} YAML files found`);
71
+ assert.ok(yamlFiles.length > 80, `only ${yamlFiles.length} YAML files found`);
72
72
  assert.ok(liveFiles.length > 100, `only ${liveFiles.length} live instruction files found`);
73
73
  });
74
74
 
@@ -13,6 +13,9 @@ const os = require('os');
13
13
  const path = require('path');
14
14
 
15
15
  const SETUP_CMD = 'npx rea-agents setup';
16
+ // Run without a terminal (an agent running it), setup has no client picked and installs nothing
17
+ // (field report 2026-10-11: plannedActions []), so the suggestion names the clients explicitly.
18
+ const setupCmd = (clients) => (clients.length ? `${SETUP_CMD} ${clients.map((c) => `--client ${c}`).join(' ')} --yes` : SETUP_CMD);
16
19
  const DECLINE_FILE = path.join('.kenaz', 'rea.json');
17
20
 
18
21
  const readJson = (f) => { try { return JSON.parse(fs.readFileSync(f, 'utf8')); } catch (_) { return null; } };
@@ -35,7 +38,8 @@ function detectRea({ home = os.homedir(), projectPath = process.cwd(), env = pro
35
38
  const codexHome = env.CODEX_HOME || path.join(home, '.codex');
36
39
  const codex = /^\s*\[mcp_servers\.rea\]\s*$/m.test(readText(path.join(codexHome, 'config.toml')));
37
40
  const declined = !!(readJson(path.join(projectPath, DECLINE_FILE)) || {}).declined;
38
- return { claude, codex, installed: claude || codex, declined, nodeOk: reaNodeOk(env.KENAZ_REA_NODE_VERSION || process.versions.node) };
41
+ const clients = [fs.existsSync(path.join(home, '.claude.json')) && 'claude_code', fs.existsSync(codexHome) && 'codex'].filter(Boolean);
42
+ return { claude, codex, installed: claude || codex, setupCmd: setupCmd(clients), declined, nodeOk: reaNodeOk(env.KENAZ_REA_NODE_VERSION || process.versions.node) };
39
43
  }
40
44
 
41
45
  /** One banner line, or null when the user declined in this project. */
@@ -46,7 +50,7 @@ function reaBannerLine(d) {
46
50
  }
47
51
  if (d.declined) return null;
48
52
  const node = d.nodeOk ? '' : `; needs Node >=22.19/24.11 (found ${process.versions.node}), upgrade Node first`;
49
- return `[rea] not installed: when the user names a reference product, suggest once \`${SETUP_CMD}\`${node}; on "no" run node "${__filename}" decline (RULES.md "REA")`;
53
+ return `[rea] not installed: when the user names a reference product, suggest once \`${d.setupCmd || SETUP_CMD}\` (the user runs it in their terminal, then restarts the agent)${node}; on "no" run node "${__filename}" decline (RULES.md "REA")`;
50
54
  }
51
55
 
52
56
  function decline(projectPath) {
@@ -9,7 +9,7 @@
9
9
  * CLI:
10
10
  * node session.js list [--json]
11
11
  * node session.js resolve <name>
12
- * node session.js prompt <name> [--mode=assembled|reference] [--depth=capsule|elixir|kernel] [--exclude=a,b] [--task=...] [--context=...] [--source=plugin] [--no-episode]
12
+ * node session.js prompt <name> [--mode=assembled|reference] [--depth=capsule|elixir|kernel] [--exclude=a,b] [--task=...] [--context=...] [--source=plugin]
13
13
  * node session.js start <name> [--consumer=...] [--task=...] [--with-prompt] [--json]
14
14
  * node session.js end <sessionId>
15
15
  * node session.js active [--json]
@@ -76,8 +76,6 @@ const PROJECT_DIR = (() => {
76
76
  return BASE_DIR;
77
77
  })();
78
78
 
79
- // Specialists whose `prompt` boot gets the most recent episode injected as prior-session context.
80
- const EPISODE_SPECIALISTS = ['Ansuz', 'Amelia'];
81
79
  const http = require('http');
82
80
  const projectResolver = require('./lib/project-resolver');
83
81
  // TASK_2163: chart files are keyed by project identity + id (sub-projects reusing
@@ -161,7 +159,7 @@ function buildAnsuzBootCapsule(spec, selfModel) {
161
159
  lazy_load: [
162
160
  'Load full kernel/capability files only when the current task needs their detailed rules.',
163
161
  'Load full self_model only when updating identity, auditing drift, or resolving a blind spot.',
164
- 'Use synapse_recall for symbols, and kenaz_lsp_callers for callers, before modifying code paths with broad blast radius.',
162
+ 'Use kenaz_lsp_references / kenaz_lsp_callers before modifying code paths with broad blast radius.',
165
163
  ],
166
164
  self_model: summarizeSelfModelForBootCapsule(selfModel),
167
165
  };
@@ -1028,73 +1026,6 @@ function buildReferencePrompt(spec, { task, context, exclude }) {
1028
1026
  return sections.join('\n');
1029
1027
  }
1030
1028
 
1031
- // ═══════════════════════════════════════
1032
- // Hippocampus: Episodic Memory Recall
1033
- // ═══════════════════════════════════════
1034
-
1035
- /**
1036
- * Recall the most recent episode from Hippocampus via MCP HTTP API.
1037
- * Used in Ansuz prompt assembly to inject prior session context.
1038
- * Graceful degradation: returns null if Dashboard/Hippocampus unavailable.
1039
- * @param {number} limit - Max episodes to recall (default: 1)
1040
- * @param {string|null} agentRole - Optional agent_role filter (e.g. 'ansuz', 'amelia').
1041
- * When provided, only episodes with matching agent_role are returned.
1042
- * When null, all episodes are returned (backwards compatible).
1043
- * @returns {Promise<string|null>} Formatted episodic memory section, or null
1044
- */
1045
- function recallRecentEpisode(limit = 1, agentRole = null) {
1046
- return new Promise((resolve) => {
1047
- const recallArgs = { limit, projectPath: PROJECT_DIR };
1048
- if (agentRole) recallArgs.agent_role = agentRole;
1049
- const payload = JSON.stringify({
1050
- jsonrpc: '2.0', id: 1,
1051
- method: 'tools/call',
1052
- params: { name: 'hippocampus_episode_recall', arguments: recallArgs },
1053
- });
1054
-
1055
- const req = http.request({
1056
- hostname: '127.0.0.1', port: 18080, path: '/api/mcp',
1057
- method: 'POST',
1058
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
1059
- timeout: 5000,
1060
- }, (res) => {
1061
- let body = '';
1062
- res.on('data', (c) => { body += c; });
1063
- res.on('end', () => {
1064
- try {
1065
- const result = JSON.parse(body);
1066
- const text = result?.result?.content?.find(c => c.type === 'text')?.text;
1067
- if (!text) return resolve(null);
1068
-
1069
- const data = JSON.parse(text);
1070
- const episodes = data.episodes || [];
1071
- if (episodes.length === 0) return resolve(null);
1072
-
1073
- const lines = ['# ═══ Episodic Memory (from Hippocampus) ═══', ''];
1074
- lines.push(`Last ${episodes.length} session(s):`);
1075
- for (const ep of episodes) {
1076
- const epData = ep.episode || ep;
1077
- lines.push(`\n### ${epData.task_id || epData.id || 'unknown'}`);
1078
- if (epData.summary) lines.push(epData.summary);
1079
- if (epData.lesson) lines.push(`**Lesson**: ${epData.lesson}`);
1080
- const entries = ep.entries || [];
1081
- for (const entry of entries.slice(0, 5)) {
1082
- lines.push(`- **${entry.phase || 'note'}**: ${entry.content || ''}`);
1083
- }
1084
- }
1085
- resolve(lines.join('\n'));
1086
- } catch {
1087
- resolve(null);
1088
- }
1089
- });
1090
- });
1091
- req.on('error', () => resolve(null));
1092
- req.on('timeout', () => { req.destroy(); resolve(null); });
1093
- req.write(payload);
1094
- req.end();
1095
- });
1096
- }
1097
-
1098
1029
  /**
1099
1030
  * Find the current Claude Code conversation JSONL file.
1100
1031
  * Claude Code writes real-time conversation data to:
@@ -1685,23 +1616,6 @@ async function cli() {
1685
1616
  let prompt = await buildPrompt(name, { mode, task: flags.task, context: flags.context, exclude, source, depth,
1686
1617
  externalBootContext: flags['external-boot-context'] === true });
1687
1618
 
1688
- // Inject the most recent episode as prior-session context for interactive boots.
1689
- // --no-episode (legacy alias --no-muninn, still passed by k-cli): skip it for
1690
- // headless/programmatic callers (e.g. kenaz_specialist_dispatch).
1691
- if (EPISODE_SPECIALISTS.includes(name) && mode === 'assembled' && !flags['no-episode'] && !flags['no-muninn']) {
1692
- // TASK_2218: the real project pin is written by .claude/hooks/ansuz-session-pin.js,
1693
- // keyed by the hook-payload session_id -- nothing is pinned here.
1694
-
1695
- // Prompt injection: skip in lean modes to keep prompt lean.
1696
- if (depth !== 'kernel' && depth !== 'capsule' && depth !== 'elixir') {
1697
- // Hippocampus: inject most recent episode as prior session context
1698
- const episodicMemory = await recallRecentEpisode(1, name.toLowerCase());
1699
- if (episodicMemory) {
1700
- prompt += `\n\n${episodicMemory}\n`;
1701
- }
1702
- }
1703
- }
1704
-
1705
1619
  console.log(prompt);
1706
1620
  break;
1707
1621
  }
@@ -1858,7 +1772,6 @@ if (require.main === module) {
1858
1772
  cleanSessions,
1859
1773
  cleanOrphanedClaude,
1860
1774
  classifyOrphanCandidates, // exported for testing (TASK_1917) — same function production uses
1861
- recallRecentEpisode,
1862
1775
  findConversationLog,
1863
1776
  // Identity verification (TASK_1080)
1864
1777
  identityScore: (name, opts) => require('./identity-scorer').score(name, { projectPath: BASE_DIR, ...opts }),
@@ -0,0 +1 @@
1
+ 9514604:1791570255334075800
@@ -1,3 +1,6 @@
1
+ > REMOVED 2026-10-11 (TASK_2411): the memory system (Synapse/Hippocampus/Cortex/Alaya recall) no longer exists in code.
2
+ > Kept for history only; do not follow it. Lessons now: `kenaz learn` and native memory files.
3
+
1
4
  # .memory 記憶系統
2
5
 
3
6
  ## 系統概述
package/core/CLAUDE.md CHANGED
@@ -41,7 +41,7 @@ session 開始時先鎖定語言(對照用戶第一條訊息 + cwd/CLAUDE.md
41
41
  - 修改非 trivial symbol 前:用 `kenaz_lsp_references` / `kenaz_lsp_callers` 看影響範圍(FAIL-009)。trivial 改動可跳過。
42
42
  - 所有 `kenaz_lsp_*` 呼叫必須明確帶 `projectPath`。
43
43
 
44
- **記憶系統凍結(2026-10-10)**:Synapse / Hippocampus / Cortex / Alaya 不再是必要步驟——不主動呼叫 `synapse_*`、`hippocampus_*`、`cortex_*`、`alaya_*`,不在 prompt 或規則加 recall/inscribe。依據:14 天內 recall 結果只有約 4.7% 被採用,且多為規則強制呼叫。經驗與教訓改記在 Claude Code 原生記憶檔(下方分層)。資料與程式碼保留,約 2026-10-24 再決定是否刪除。
44
+ **記憶系統已移除(2026-10-11,TASK_2411)**:Synapse / Hippocampus / Cortex recall / embeddings 與 Alaya seeds/bijas 已移除,`synapse_*`、`hippocampus_*`、`cortex_*`、`alaya_recall` 不再存在;教訓用 `kenaz learn add` 與 Claude 原生記憶檔;`~/.kenaz/cortex/*.db` 與 `.synapse/` 資料留在磁碟上、不再開啟。
45
45
 
46
46
  ---
47
47
 
@@ -59,11 +59,11 @@ harness 預設「每條記憶都在 MEMORY.md 加一行」;本專案改為三
59
59
  ## 專案概述
60
60
 
61
61
  KenazAI 是一個 Claude Code 多 Agent 協作系統,包含:
62
- - **Kenaz Dashboard**:Tauri 2 + Next.js + Rust 桌面應用(`dashboard/`)
62
+ - **kenaz-server**:Rust 後端(`dashboard/src-tauri/`,npm `@kenazai/server-*`);Dashboard 桌面版已於 2026-10-11 退役(TASK_2396)
63
63
  - **Specialist 系統**:AI Agent 調度引擎(`.specialist/`)
64
64
  - **Formation 系統**:多 Agent 協作調度,支援 unit/solo/squad/legion 陣型(`.formation/`)
65
65
 
66
- 技術棧:Rust(Tauri backend + Synapse 記憶)、TypeScript/React(frontend)、SQLite FTS5(Synapse 記憶)
66
+ 技術棧:Rust(kenaz-server)、Node.js(kenaz CLI、hooks、工具)、SQLite
67
67
 
68
68
  ---
69
69
 
@@ -132,8 +132,7 @@ node .collaboration/core/task-api.js move TASK_XXX active # 移動狀態
132
132
  |------|------|------|
133
133
  | 規則庫 | `.rule/` | 最高優先級規則,見 `INDEX.yaml` |
134
134
  | 失敗學習 | `.failure/` | 已知失敗模式,Agent 執行前應檢查 |
135
- | Synapse | `dashboard/src-tauri/src/features/synapse/` | Rust-native 記憶層(2026-10-10 凍結,見上方說明) |
136
- | Rust 測試 | `npm run test:rust -- <filter>` | 主目錄直接可跑;用共用短路徑 target,不和 live tauri dev 搶 `target/` 鎖。勿直接 `cargo test` |
135
+ | Rust 測試 | `npm run test:rust -- <filter>` | 主目錄直接可跑;用共用短路徑 target,不和其他建置搶 `target/` 鎖。勿直接 `cargo test` |
137
136
  | Formation | `.formation/` | 多 Agent 調度,見 `README.md` |
138
137
  | Skills | `.claude/skills/` | Agent 行為定義(40+ skills) |
139
138
  | 外部 Skills | `.agents/skills/` | `vercel-react-best-practices`、`supabase-postgres-best-practices` |
@@ -163,10 +162,10 @@ DB: postgresql://postgres:postgres@127.0.0.1:54322/postgres
163
162
  .collaboration/ # 任務系統
164
163
  .formation/ # Formation 調度
165
164
  .failure/ # 失敗學習系統
166
- .memory/ # Synapse 記憶
165
+ .memory/ # (歷史)舊記憶資料,記憶系統已移除
167
166
  .claude/ # Skills、Commands、Hooks
168
167
  .agents/ # 外部 Skills
169
168
  dev/scripts/ # CLI 工具
170
169
  dev/PRODUCT_SPEC.md # 產品規格
171
- dashboard/ # Kenaz Dashboard(Tauri + Next.js + Rust)
170
+ dashboard/src-tauri/ # kenaz-server(Rust);dashboard/scripts/ 打包與安裝腳本
172
171
  ```
@@ -1 +1 @@
1
- 0.3.10
1
+ 0.3.12