@myclaw163/clawclaw-cli 0.6.55 → 0.6.57

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 (189) hide show
  1. package/README.md +440 -440
  2. package/bin/clawclaw-cli.mjs +3 -3
  3. package/package.json +48 -48
  4. package/personas//347/220/206/346/231/272/346/270/251/345/222/214.md +23 -23
  5. package/personas//350/200/201/350/260/213/346/267/261/347/256/227.md +22 -22
  6. package/personas//350/257/232/346/201/263/347/233/264/347/216/207.md +22 -22
  7. package/personas//350/275/273/346/235/276/346/264/273/346/263/274.md +22 -22
  8. package/personas//351/207/216/346/200/247/345/217/233/351/200/206.md +23 -23
  9. package/scripts/postinstall.mjs +20 -20
  10. package/scripts/sync-bundled-skill.mjs +245 -245
  11. package/scripts/sync-bundled-skill.test.mjs +152 -152
  12. package/skills/clawclaw/SKILL.md +244 -240
  13. package/skills/clawclaw/references/CHATTERBOX.md +142 -142
  14. package/skills/clawclaw/references/COMMANDS.md +132 -132
  15. package/skills/clawclaw/references/GAME-MECHANICS.md +186 -186
  16. package/skills/clawclaw/references/HUB.md +48 -48
  17. package/skills/clawclaw/references/KNOWLEDGE.md +43 -43
  18. package/skills/clawclaw/references/STRATEGIES.md +57 -57
  19. package/skills/clawclaw/references/STREAM.md +59 -58
  20. package/skills/clawclaw/references/TACTICS.md +65 -65
  21. package/src/assets/clawclaw-ascii-map.txt +40 -40
  22. package/src/cli.ts +110 -153
  23. package/src/commands/_schema.ts +109 -109
  24. package/src/commands/account.ts +209 -209
  25. package/src/commands/do.test.ts +37 -37
  26. package/src/commands/do.ts +95 -95
  27. package/src/commands/events.ts +22 -22
  28. package/src/commands/game-map.test.ts +28 -28
  29. package/src/commands/game-start-plan.test.ts +84 -142
  30. package/src/commands/game.ts +1027 -882
  31. package/src/commands/history-player.test.ts +102 -102
  32. package/src/commands/history.ts +573 -573
  33. package/src/commands/hub.test.ts +96 -96
  34. package/src/commands/hub.ts +234 -234
  35. package/src/commands/knowledge.test.ts +19 -19
  36. package/src/commands/knowledge.ts +168 -168
  37. package/src/commands/load.test.ts +51 -51
  38. package/src/commands/load.ts +13 -13
  39. package/src/commands/meeting-history.test.ts +106 -106
  40. package/src/commands/memory.ts +40 -40
  41. package/src/commands/peek.ts +45 -38
  42. package/src/commands/persona.ts +57 -57
  43. package/src/commands/skill.ts +128 -128
  44. package/src/commands/state.ts +46 -46
  45. package/src/commands/strategy.test.ts +135 -135
  46. package/src/commands/strategy.ts +180 -189
  47. package/src/commands/tts.ts +128 -128
  48. package/src/commands/upgrade.test.ts +82 -82
  49. package/src/commands/upgrade.ts +148 -148
  50. package/src/commands/watch.test.ts +969 -973
  51. package/src/commands/watch.ts +720 -709
  52. package/src/lib/auth.test.ts +59 -59
  53. package/src/lib/auth.ts +186 -186
  54. package/src/lib/command-meta.ts +37 -37
  55. package/src/lib/game-client.ts +391 -391
  56. package/src/lib/http-keepalive.ts +15 -15
  57. package/src/lib/http-transport.test.ts +42 -42
  58. package/src/lib/http-transport.ts +113 -113
  59. package/src/lib/hub-client.test.ts +56 -56
  60. package/src/lib/hub-client.ts +88 -88
  61. package/src/lib/hub-install.test.ts +98 -98
  62. package/src/lib/hub-install.ts +121 -121
  63. package/src/lib/hub-reminder.ts +75 -75
  64. package/src/lib/hub-unzip.test.ts +69 -69
  65. package/src/lib/hub-unzip.ts +62 -62
  66. package/src/lib/init-command.test.ts +75 -75
  67. package/src/lib/init-command.ts +120 -120
  68. package/src/lib/knowledge-store.test.ts +180 -180
  69. package/src/lib/knowledge-store.ts +374 -374
  70. package/src/lib/load-context.test.ts +52 -52
  71. package/src/lib/load-context.ts +52 -52
  72. package/src/lib/match-state.test.ts +134 -134
  73. package/src/lib/match-state.ts +94 -94
  74. package/src/lib/netease-tts.ts +83 -83
  75. package/src/lib/normalize.ts +42 -42
  76. package/src/lib/persona.test.ts +41 -41
  77. package/src/lib/persona.ts +72 -72
  78. package/src/lib/server-registry.ts +152 -152
  79. package/src/lib/skill-version.test.ts +48 -48
  80. package/src/lib/skill-version.ts +19 -19
  81. package/src/lib/strategy-export.test.ts +232 -232
  82. package/src/lib/strategy-export.ts +242 -242
  83. package/src/lib/tts-keys.ts +7 -7
  84. package/src/lib/tts-speech.test.ts +63 -63
  85. package/src/lib/tts-speech.ts +76 -76
  86. package/src/lib/workspace-argv.test.ts +49 -49
  87. package/src/lib/workspace-argv.ts +44 -44
  88. package/src/perception/player-history-store.test.ts +87 -87
  89. package/src/perception/player-history-store.ts +194 -194
  90. package/src/pipeline/event-store.ts +124 -124
  91. package/src/pipeline/pipeline.ts +35 -35
  92. package/src/runtime/auto-upgrade.test.ts +66 -66
  93. package/src/runtime/auto-upgrade.ts +31 -31
  94. package/src/runtime/event-daemon.test.ts +107 -28
  95. package/src/runtime/event-daemon.ts +409 -371
  96. package/src/runtime/owner-control.ts +150 -0
  97. package/src/runtime/raw-ws-log.test.ts +33 -33
  98. package/src/runtime/raw-ws-log.ts +32 -32
  99. package/src/runtime/runtime-logger.ts +107 -99
  100. package/src/runtime/ws-client.test.ts +104 -47
  101. package/src/runtime/ws-client.ts +272 -272
  102. package/src/sdk/action.ts +166 -166
  103. package/src/sdk/index.ts +110 -110
  104. package/src/sdk/types.ts +146 -146
  105. package/src/strategies/avoid-lone.ts +11 -11
  106. package/src/strategies/avoid-players.knowledge.md +20 -20
  107. package/src/strategies/avoid-players.ts +15 -15
  108. package/src/strategies/corpse-patrol.ts +22 -22
  109. package/src/strategies/crab-sabotage.ts +21 -21
  110. package/src/strategies/custom-module.test.ts +269 -269
  111. package/src/strategies/find-player.ts +16 -16
  112. package/src/strategies/game-utils.test.ts +164 -164
  113. package/src/strategies/game-utils.ts +737 -721
  114. package/src/strategies/goals/avoid-lone-top.ts +168 -168
  115. package/src/strategies/goals/avoid-players-top.test.ts +83 -83
  116. package/src/strategies/goals/avoid-players-top.ts +121 -121
  117. package/src/strategies/goals/conversation-goal.ts +51 -51
  118. package/src/strategies/goals/corpse-patrol-top.ts +91 -91
  119. package/src/strategies/goals/crab-octopus-reflexes.ts +93 -93
  120. package/src/strategies/goals/crab-sabotage-top.ts +197 -197
  121. package/src/strategies/goals/emergency-hunt-goal.ts +28 -28
  122. package/src/strategies/goals/find-player-top.ts +93 -93
  123. package/src/strategies/goals/flee-players-goal.ts +53 -53
  124. package/src/strategies/goals/goal-manager.ts +41 -41
  125. package/src/strategies/goals/goal-root-strategy.ts +49 -49
  126. package/src/strategies/goals/goal.ts +28 -28
  127. package/src/strategies/goals/keep-away-goal.ts +206 -206
  128. package/src/strategies/goals/kill-frenzy-top.ts +80 -80
  129. package/src/strategies/goals/kill-lone-top.ts +160 -160
  130. package/src/strategies/goals/kill-target-goal.ts +59 -59
  131. package/src/strategies/goals/kill-target-top.ts +109 -109
  132. package/src/strategies/goals/leaf-goal.ts +25 -25
  133. package/src/strategies/goals/linger-corpse-goal.ts +79 -79
  134. package/src/strategies/goals/lone-kill-core.ts +82 -82
  135. package/src/strategies/goals/lone-kill-goal.ts +24 -24
  136. package/src/strategies/goals/lone-kill-task-top.test.ts +85 -85
  137. package/src/strategies/goals/lone-kill-task-top.ts +86 -86
  138. package/src/strategies/goals/move-room-goal.ts +60 -60
  139. package/src/strategies/goals/normal-shrimp-top.test.ts +80 -80
  140. package/src/strategies/goals/normal-shrimp-top.ts +242 -242
  141. package/src/strategies/goals/paradise-fish-top.test.ts +126 -126
  142. package/src/strategies/goals/paradise-fish-top.ts +219 -219
  143. package/src/strategies/goals/patrol-top.ts +57 -57
  144. package/src/strategies/goals/report-patrol-top.ts +80 -80
  145. package/src/strategies/goals/safe-task-goal.ts +102 -102
  146. package/src/strategies/goals/social-task-top.ts +161 -161
  147. package/src/strategies/goals/task-kill-report-top.ts +163 -163
  148. package/src/strategies/goals/task-only-top.ts +57 -57
  149. package/src/strategies/goals/task-or-patrol-goal.ts +41 -41
  150. package/src/strategies/goals/task-report-top.ts +57 -57
  151. package/src/strategies/goals/wander-task-goal.ts +33 -33
  152. package/src/strategies/goals/warrior-shrimp-top.test.ts +86 -86
  153. package/src/strategies/goals/warrior-shrimp-top.ts +248 -248
  154. package/src/strategies/greeting.ts +53 -53
  155. package/src/strategies/kill-frenzy.ts +12 -12
  156. package/src/strategies/kill-lone.knowledge.md +20 -20
  157. package/src/strategies/kill-lone.ts +13 -13
  158. package/src/strategies/kill-target.ts +18 -18
  159. package/src/strategies/loader.test.ts +678 -678
  160. package/src/strategies/loader.ts +172 -172
  161. package/src/strategies/lone-kill-task.ts +21 -21
  162. package/src/strategies/meeting-gate.test.ts +59 -59
  163. package/src/strategies/meeting-gate.ts +23 -23
  164. package/src/strategies/move-room.ts +15 -15
  165. package/src/strategies/new-events-backfill.ts +98 -98
  166. package/src/strategies/paradise-fish.knowledge.md +20 -20
  167. package/src/strategies/paradise-fish.ts +25 -25
  168. package/src/strategies/pathfind/distance-field.ts +150 -150
  169. package/src/strategies/pathfind/escape-planner.test.ts +197 -197
  170. package/src/strategies/pathfind/escape-planner.ts +348 -348
  171. package/src/strategies/pathfind/walkable-grid.ts +117 -117
  172. package/src/strategies/patrol.ts +11 -11
  173. package/src/strategies/player-targets.ts +13 -13
  174. package/src/strategies/report-patrol.ts +11 -11
  175. package/src/strategies/shrimp-memory.knowledge.md +20 -20
  176. package/src/strategies/shrimp-memory.ts +25 -25
  177. package/src/strategies/social-task.test.ts +28 -28
  178. package/src/strategies/social-task.ts +49 -49
  179. package/src/strategies/spawn.ts +82 -71
  180. package/src/strategies/speech-module.ts +123 -123
  181. package/src/strategies/strategy-loop.ts +763 -757
  182. package/src/strategies/task-kill-report.ts +17 -17
  183. package/src/strategies/task-only.ts +11 -11
  184. package/src/strategies/task-report.ts +22 -22
  185. package/src/strategies/types.ts +96 -96
  186. package/src/strategies/warrior-memory.knowledge.md +20 -20
  187. package/src/strategies/warrior-memory.ts +16 -16
  188. package/src/runtime/daemon.ts +0 -100
  189. package/src/runtime/opening-mover.ts +0 -303
@@ -1,17 +1,17 @@
1
- import type { StrategyEntry } from './types.js';
2
- import { GoalRootStrategy } from './goals/goal-root-strategy.js';
3
- import { TaskKillReportTop } from './goals/task-kill-report-top.js';
4
-
5
- export const strategy: StrategyEntry = {
6
- id: 'task-kill-report',
7
- description: '需传入嫌疑目标(座位号或名字,可多人)。看到目标且冷却好,50内就出刀,否则追上去;冷却中贴身跟随。目标不在视野时,遇到尸体就报告;没尸体就做普通任务。',
8
- create(args?: string[]) {
9
- if (!args || args.length === 0) {
10
- throw new Error('task-kill-report strategy requires suspected target seat numbers or player names as arguments.');
11
- }
12
- const targets = [...args];
13
- return new GoalRootStrategy('task-kill-report', () => new TaskKillReportTop(targets), {
14
- resetOnMeetingResume: false,
15
- });
16
- },
17
- };
1
+ import type { StrategyEntry } from './types.js';
2
+ import { GoalRootStrategy } from './goals/goal-root-strategy.js';
3
+ import { TaskKillReportTop } from './goals/task-kill-report-top.js';
4
+
5
+ export const strategy: StrategyEntry = {
6
+ id: 'task-kill-report',
7
+ description: '需传入嫌疑目标(座位号或名字,可多人)。看到目标且冷却好,50内就出刀,否则追上去;冷却中贴身跟随。目标不在视野时,遇到尸体就报告;没尸体就做普通任务。',
8
+ create(args?: string[]) {
9
+ if (!args || args.length === 0) {
10
+ throw new Error('task-kill-report strategy requires suspected target seat numbers or player names as arguments.');
11
+ }
12
+ const targets = [...args];
13
+ return new GoalRootStrategy('task-kill-report', () => new TaskKillReportTop(targets), {
14
+ resetOnMeetingResume: false,
15
+ });
16
+ },
17
+ };
@@ -1,11 +1,11 @@
1
- import type { StrategyEntry } from './types.js';
2
- import { GoalRootStrategy } from './goals/goal-root-strategy.js';
3
- import { TaskOnlyTop } from './goals/task-only-top.js';
4
-
5
- export const strategy: StrategyEntry = {
6
- id: 'task-only',
7
- description: '专心做任务。紧急任务出现时优先处理,其次按顺序完成普通任务,如果是蟹,则蟹的伪装任务也会做。没有尸体报告和战斗行为。',
8
- create() {
9
- return new GoalRootStrategy('task-only', () => new TaskOnlyTop(), { resetOnMeetingResume: false });
10
- },
11
- };
1
+ import type { StrategyEntry } from './types.js';
2
+ import { GoalRootStrategy } from './goals/goal-root-strategy.js';
3
+ import { TaskOnlyTop } from './goals/task-only-top.js';
4
+
5
+ export const strategy: StrategyEntry = {
6
+ id: 'task-only',
7
+ description: '专心做任务。紧急任务出现时优先处理,其次按顺序完成普通任务,如果是蟹,则蟹的伪装任务也会做。没有尸体报告和战斗行为。',
8
+ create() {
9
+ return new GoalRootStrategy('task-only', () => new TaskOnlyTop(), { resetOnMeetingResume: false });
10
+ },
11
+ };
@@ -1,22 +1,22 @@
1
- import type { StrategyEntry } from './types.js';
2
- import { GoalRootStrategy } from './goals/goal-root-strategy.js';
3
- import { TaskReportTop } from './goals/task-report-top.js';
4
- import { parseGreetingArgs } from './greeting.js';
5
-
6
- class TaskReportStrategy extends GoalRootStrategy {
7
- constructor(private readonly greetingPhrases: string[]) {
8
- super('task-report', () => new TaskReportTop(greetingPhrases), { resetOnMeetingResume: false });
9
- }
10
-
11
- speechWarmupTexts(): string[] {
12
- return this.greetingPhrases;
13
- }
14
- }
15
-
16
- export const strategy: StrategyEntry = {
17
- id: 'task-report',
18
- description: '遇尸体最优先:达到报告距离立刻报告,没达到距离就靠近准备报告。紧急任务出现时优先处理,其次按顺序完成普通任务。传入打招呼话术时,视野内出现人就随机发送一条,之后120秒内不再发言。(普通虾默认;进阶版见 shrimp-memory)参数:可选:1~3 条打招呼话术。',
19
- create(args?: string[]) {
20
- return new TaskReportStrategy(parseGreetingArgs(args, 'task-report'));
21
- },
22
- };
1
+ import type { StrategyEntry } from './types.js';
2
+ import { GoalRootStrategy } from './goals/goal-root-strategy.js';
3
+ import { TaskReportTop } from './goals/task-report-top.js';
4
+ import { parseGreetingArgs } from './greeting.js';
5
+
6
+ class TaskReportStrategy extends GoalRootStrategy {
7
+ constructor(private readonly greetingPhrases: string[]) {
8
+ super('task-report', () => new TaskReportTop(greetingPhrases), { resetOnMeetingResume: false });
9
+ }
10
+
11
+ speechWarmupTexts(): string[] {
12
+ return this.greetingPhrases;
13
+ }
14
+ }
15
+
16
+ export const strategy: StrategyEntry = {
17
+ id: 'task-report',
18
+ description: '遇尸体最优先:达到报告距离立刻报告,没达到距离就靠近准备报告。紧急任务出现时优先处理,其次按顺序完成普通任务。传入打招呼话术时,视野内出现人就随机发送一条,之后120秒内不再发言。(普通虾默认;进阶版见 shrimp-memory)参数:可选:1~3 条打招呼话术。',
19
+ create(args?: string[]) {
20
+ return new TaskReportStrategy(parseGreetingArgs(args, 'task-report'));
21
+ },
22
+ };
@@ -1,96 +1,96 @@
1
- import type { GameState, TaskInfo, EmergencyInfo, CorpseInfo } from '../sdk/types.js';
2
- import type { Action } from '../sdk/action.js';
3
- import type { KnowledgeView } from '../lib/knowledge-store.js';
4
-
5
- /**
6
- * Minimal interface for custom modules (built-in and strategy-registered).
7
- * Strategy authors implement this to inject custom analysis modules
8
- * (e.g. CoMovementTracker, VisibilityTracker, SpeechAnalyzer) via ctx.customModules.
9
- * The strategy-loop manages lifecycle through these hooks:
10
- * - processEvents: called every tick with new events, tick number, and current game state
11
- * - afterDecide: called after strategy.decide(), can filter/modify decisions (chained by registration order)
12
- * - onMeetingResume: called after each meeting (decay or full reset)
13
- * - onMapLoaded: called when fresh map data is available
14
- */
15
- export interface CustomModule {
16
- processEvents(events: any[], tick: number, state: GameState): void;
17
- afterDecide?(decisions: BehaviorDecision[], state: GameState, ctx: StrategyContext): BehaviorDecision[];
18
- onMeetingResume?(): void;
19
- onMapLoaded?(rawRooms: any[]): void;
20
- }
21
-
22
- export interface BehaviorDecision {
23
- action: Action;
24
- }
25
-
26
- export interface CorpseTarget {
27
- x: number;
28
- y: number;
29
- name?: string;
30
- room?: string;
31
- }
32
-
33
- export interface Strategy {
34
- readonly name: string;
35
- decide(state: GameState, ctx: StrategyContext): BehaviorDecision[];
36
- /** Called when wandering resumes after a meeting pause. */
37
- onMeetingResume?(): void;
38
- /** Called by strategy-loop after getRoleInfo() resolves, allowing late config update. */
39
- updateRole?(role: string): void;
40
- /** Return custom analysis modules. Called once during setup, before the main loop. */
41
- customModules?(): CustomModule[];
42
- /** Optional texts used by strategy-loop for async speech audio warmup. */
43
- speechWarmupTexts?(): string[];
44
- }
45
-
46
- export interface StrategyEntry {
47
- readonly id: string;
48
- readonly description: string;
49
- create(args?: string[]): Strategy;
50
- }
51
-
52
- export interface RoomTarget {
53
- name: string;
54
- x: number;
55
- y: number;
56
- }
57
-
58
- export interface StrategyContext {
59
- taskData: TaskInfo[];
60
- emergency: EmergencyInfo | null;
61
- taskLocalBlockedUntil: number;
62
- reportCorpseTarget: CorpseTarget | null;
63
- reportBlockedUntil: number;
64
- notifications: string[];
65
- lastProgressNotifyAt: number;
66
- teammates: Set<string>;
67
- alarmDone: boolean;
68
- rooms: RoomTarget[];
69
- playerNamesBySeat: Record<string, string>;
70
- /** Set by strategy-loop when a move target is unreachable too many times. Patrol strategies should advance past the current room. */
71
- forcePatrolAdvance: boolean;
72
- blockedMoveTarget: { x: number; y: number } | null;
73
- /** Own seat number, used by speech module for initiation arbitration. */
74
- mySeat: number;
75
- /** Speech-module notifications, flushed by strategy-loop as robot_speak_rule events. */
76
- speechNotifications: string[];
77
- /**
78
- * Strategy → agent real-time briefings, flushed by strategy-loop as `strategy_alert`
79
- * events (notable → surfaced live by `ccl watch`, unlike `notifications` which only
80
- * land in the log as `type:'auto'`). Push **sparingly**, only on a decisive autonomous
81
- * move the agent must account for when it next speaks/votes (e.g. the bot reported a
82
- * corpse — possibly its own kill — to deflect). NOT for per-tick progress.
83
- */
84
- agentAlerts: string[];
85
- /**
86
- * 跨 tick 的已知尸体记忆,由 strategy-loop 每 tick 用中心 CorpseMemory 写入:尸体从出现一直存在到
87
- * 开会才清空(开会时由 strategy-loop 清空)。是 `state.corpses`(仅当前视野)的严格超集,含已离开
88
- * 视野、之前看见过的尸体。策略应统一读它做尸体检测/接近/任务回避/死亡确认,而非只看 `state.corpses`。
89
- * 通过 hasKnownCorpse / nearestKnownCorpse / firstAvailableTask 的 avoidCorpses 参数消费。
90
- */
91
- knownCorpses?: CorpseInfo[];
92
- knowledge?: KnowledgeView;
93
- recentlyKilledTargets?: Map<string, number>;
94
- /** Strategy-registered modules. strategy-loop auto-calls processEvents, afterDecide, onMeetingResume and onMapLoaded. */
95
- customModules?: CustomModule[];
96
- }
1
+ import type { GameState, TaskInfo, EmergencyInfo, CorpseInfo } from '../sdk/types.js';
2
+ import type { Action } from '../sdk/action.js';
3
+ import type { KnowledgeView } from '../lib/knowledge-store.js';
4
+
5
+ /**
6
+ * Minimal interface for custom modules (built-in and strategy-registered).
7
+ * Strategy authors implement this to inject custom analysis modules
8
+ * (e.g. CoMovementTracker, VisibilityTracker, SpeechAnalyzer) via ctx.customModules.
9
+ * The strategy-loop manages lifecycle through these hooks:
10
+ * - processEvents: called every tick with new events, tick number, and current game state
11
+ * - afterDecide: called after strategy.decide(), can filter/modify decisions (chained by registration order)
12
+ * - onMeetingResume: called after each meeting (decay or full reset)
13
+ * - onMapLoaded: called when fresh map data is available
14
+ */
15
+ export interface CustomModule {
16
+ processEvents(events: any[], tick: number, state: GameState): void;
17
+ afterDecide?(decisions: BehaviorDecision[], state: GameState, ctx: StrategyContext): BehaviorDecision[];
18
+ onMeetingResume?(): void;
19
+ onMapLoaded?(rawRooms: any[]): void;
20
+ }
21
+
22
+ export interface BehaviorDecision {
23
+ action: Action;
24
+ }
25
+
26
+ export interface CorpseTarget {
27
+ x: number;
28
+ y: number;
29
+ name?: string;
30
+ room?: string;
31
+ }
32
+
33
+ export interface Strategy {
34
+ readonly name: string;
35
+ decide(state: GameState, ctx: StrategyContext): BehaviorDecision[];
36
+ /** Called when wandering resumes after a meeting pause. */
37
+ onMeetingResume?(): void;
38
+ /** Called by strategy-loop after getRoleInfo() resolves, allowing late config update. */
39
+ updateRole?(role: string): void;
40
+ /** Return custom analysis modules. Called once during setup, before the main loop. */
41
+ customModules?(): CustomModule[];
42
+ /** Optional texts used by strategy-loop for async speech audio warmup. */
43
+ speechWarmupTexts?(): string[];
44
+ }
45
+
46
+ export interface StrategyEntry {
47
+ readonly id: string;
48
+ readonly description: string;
49
+ create(args?: string[]): Strategy;
50
+ }
51
+
52
+ export interface RoomTarget {
53
+ name: string;
54
+ x: number;
55
+ y: number;
56
+ }
57
+
58
+ export interface StrategyContext {
59
+ taskData: TaskInfo[];
60
+ emergency: EmergencyInfo | null;
61
+ taskLocalBlockedUntil: number;
62
+ reportCorpseTarget: CorpseTarget | null;
63
+ reportBlockedUntil: number;
64
+ notifications: string[];
65
+ lastProgressNotifyAt: number;
66
+ teammates: Set<string>;
67
+ alarmDone: boolean;
68
+ rooms: RoomTarget[];
69
+ playerNamesBySeat: Record<string, string>;
70
+ /** Set by strategy-loop when a move target is unreachable too many times. Patrol strategies should advance past the current room. */
71
+ forcePatrolAdvance: boolean;
72
+ blockedMoveTarget: { x: number; y: number } | null;
73
+ /** Own seat number, used by speech module for initiation arbitration. */
74
+ mySeat: number;
75
+ /** Speech-module notifications, flushed by strategy-loop as robot_speak_rule events. */
76
+ speechNotifications: string[];
77
+ /**
78
+ * Strategy → agent real-time briefings, flushed by strategy-loop as `strategy_alert`
79
+ * events (notable → surfaced live by `ccl watch`, unlike `notifications` which only
80
+ * land in the log as `type:'auto'`). Push **sparingly**, only on a decisive autonomous
81
+ * move the agent must account for when it next speaks/votes (e.g. the bot reported a
82
+ * corpse — possibly its own kill — to deflect). NOT for per-tick progress.
83
+ */
84
+ agentAlerts: string[];
85
+ /**
86
+ * 跨 tick 的已知尸体记忆,由 strategy-loop 每 tick 用中心 CorpseMemory 写入:尸体从出现一直存在到
87
+ * 开会才清空(开会时由 strategy-loop 清空)。是 `state.corpses`(仅当前视野)的严格超集,含已离开
88
+ * 视野、之前看见过的尸体。策略应统一读它做尸体检测/接近/任务回避/死亡确认,而非只看 `state.corpses`。
89
+ * 通过 hasKnownCorpse / nearestKnownCorpse / firstAvailableTask 的 avoidCorpses 参数消费。
90
+ */
91
+ knownCorpses?: CorpseInfo[];
92
+ knowledge?: KnowledgeView;
93
+ recentlyKilledTargets?: Map<string, number>;
94
+ /** Strategy-registered modules. strategy-loop auto-calls processEvents, afterDecide, onMeetingResume and onMapLoaded. */
95
+ customModules?: CustomModule[];
96
+ }
@@ -1,20 +1,20 @@
1
- # warrior-memory 知识契约
2
-
3
- `warrior-memory` 只读取统一的玩家三档标记:
4
-
5
- | 标记 | 行为 |
6
- |------|------|
7
- | `suspect` | 可疑:始终回避观察,即使刀可用也不主动击杀 |
8
- | `hostile` | 敌对:刀可用时主动追杀;刀冷却或枪虾无次数时回避 |
9
- | `trusted` | 可信:绝不攻击,也不会因其在场而对陌生人后撤 |
10
-
11
- 无标记玩家按陌生人处理:平时不主动猎杀;无 trusted 同伴且单个陌生人贴近 270px 内时先拉开距离。尸体场景仍有更高优先级:刀可用时会攻击尸体旁最近的非 trusted 非队友,因此确认好人后应及时标为 trusted。
12
-
13
- ```bash
14
- ccl knowledge mark 5 suspect --confidence 0.7 --note "电力房尾随"
15
- ccl knowledge mark 5 hostile --confidence 0.9 --note "确认敌对"
16
- ccl knowledge mark 3 trusted --note "不在场证明成立"
17
- ccl knowledge del player 5
18
- ```
19
-
20
- 启动参数 `ccl strategy warrior-memory -- 3 7` 仍表示本次策略运行的明确猎杀目标。`role` 是纯身份事实,不直接控制策略。
1
+ # warrior-memory 知识契约
2
+
3
+ `warrior-memory` 只读取统一的玩家三档标记:
4
+
5
+ | 标记 | 行为 |
6
+ |------|------|
7
+ | `suspect` | 可疑:始终回避观察,即使刀可用也不主动击杀 |
8
+ | `hostile` | 敌对:刀可用时主动追杀;刀冷却或枪虾无次数时回避 |
9
+ | `trusted` | 可信:绝不攻击,也不会因其在场而对陌生人后撤 |
10
+
11
+ 无标记玩家按陌生人处理:平时不主动猎杀;无 trusted 同伴且单个陌生人贴近 270px 内时先拉开距离。尸体场景仍有更高优先级:刀可用时会攻击尸体旁最近的非 trusted 非队友,因此确认好人后应及时标为 trusted。
12
+
13
+ ```bash
14
+ ccl knowledge mark 5 suspect --confidence 0.7 --note "电力房尾随"
15
+ ccl knowledge mark 5 hostile --confidence 0.9 --note "确认敌对"
16
+ ccl knowledge mark 3 trusted --note "不在场证明成立"
17
+ ccl knowledge del player 5
18
+ ```
19
+
20
+ 启动参数 `ccl strategy warrior-memory -- 3 7` 仍表示本次策略运行的明确猎杀目标。`role` 是纯身份事实,不直接控制策略。
@@ -1,16 +1,16 @@
1
- import type { StrategyEntry } from './types.js';
2
- import { GoalRootStrategy } from './goals/goal-root-strategy.js';
3
- import { WarriorShrimpTop } from './goals/warrior-shrimp-top.js';
4
- import { parseTargetArgs } from './player-targets.js';
5
-
6
- export const strategy: StrategyEntry = {
7
- id: 'warrior-memory',
8
- description:
9
- '带刀虾·记忆进阶版(武士虾/枪虾通用,好人带刀;kill-lone 的带记忆升级)。统一读取 ccl knowledge 三档标记:suspect 只回避观察,hostile 在刀好时主动追杀、刀不好时回避,trusted 视为可信同伴且绝不攻击。发现尸体且旁边有非 trusted 对象、刀可用就先出刀,否则靠近报警;无可信同伴且单个陌生人贴近270内时先拉开到300再做任务;无危险时优先紧急任务,否则做任务/巡逻。枪虾会尊重剩余出刀次数。',
10
- create(args?: string[]) {
11
- const huntTargets = args ? parseTargetArgs(args) : [];
12
- return new GoalRootStrategy('warrior-memory', () => new WarriorShrimpTop(huntTargets), {
13
- resetOnMeetingResume: false,
14
- });
15
- },
16
- };
1
+ import type { StrategyEntry } from './types.js';
2
+ import { GoalRootStrategy } from './goals/goal-root-strategy.js';
3
+ import { WarriorShrimpTop } from './goals/warrior-shrimp-top.js';
4
+ import { parseTargetArgs } from './player-targets.js';
5
+
6
+ export const strategy: StrategyEntry = {
7
+ id: 'warrior-memory',
8
+ description:
9
+ '带刀虾·记忆进阶版(武士虾/枪虾通用,好人带刀;kill-lone 的带记忆升级)。统一读取 ccl knowledge 三档标记:suspect 只回避观察,hostile 在刀好时主动追杀、刀不好时回避,trusted 视为可信同伴且绝不攻击。发现尸体且旁边有非 trusted 对象、刀可用就先出刀,否则靠近报警;无可信同伴且单个陌生人贴近270内时先拉开到300再做任务;无危险时优先紧急任务,否则做任务/巡逻。枪虾会尊重剩余出刀次数。',
10
+ create(args?: string[]) {
11
+ const huntTargets = args ? parseTargetArgs(args) : [];
12
+ return new GoalRootStrategy('warrior-memory', () => new WarriorShrimpTop(huntTargets), {
13
+ resetOnMeetingResume: false,
14
+ });
15
+ },
16
+ };
@@ -1,100 +0,0 @@
1
- import { existsSync, mkdirSync, openSync, readFileSync } from 'fs';
2
- import { resolve, dirname, delimiter } from 'path';
3
- import { spawn, execSync } from 'child_process';
4
- import { join } from 'path';
5
- import { fileURLToPath } from 'url';
6
- import { getProfileStateDir, getProfileLogsDir } from '../lib/init-command.js';
7
- import { AuthStore } from '../lib/auth.js';
8
- import { RuntimeLogger } from './runtime-logger.js';
9
-
10
- const log = new RuntimeLogger();
11
-
12
- const __filename = fileURLToPath(import.meta.url);
13
- const __dirname = dirname(__filename);
14
-
15
- function isPidAlive(pid: number): boolean {
16
- try { process.kill(pid, 0); return true; } catch { return false; }
17
- }
18
-
19
- export function getRunningDaemonPid(stateDir: string): number | null {
20
- const runtimePath = join(stateDir, 'runtime.json');
21
- if (!existsSync(runtimePath)) return null;
22
- try {
23
- const info = JSON.parse(readFileSync(runtimePath, 'utf8'));
24
- if (typeof info?.pid === 'number' && isPidAlive(info.pid)) return info.pid;
25
- } catch {}
26
- return null;
27
- }
28
-
29
- export async function waitDaemonExit(stateDir: string, timeoutMs = 15000): Promise<void> {
30
- const runtimePath = join(stateDir, 'runtime.json');
31
- let pid: number | undefined;
32
- try {
33
- if (existsSync(runtimePath)) {
34
- const info = JSON.parse(readFileSync(runtimePath, 'utf8'));
35
- if (typeof info?.pid === 'number') pid = info.pid;
36
- }
37
- } catch {}
38
-
39
- const deadline = Date.now() + timeoutMs;
40
- while (Date.now() < deadline) {
41
- const fileGone = !existsSync(runtimePath);
42
- const procGone = pid === undefined ? true : !isPidAlive(pid);
43
- if (fileGone && procGone) return;
44
- await new Promise(r => setTimeout(r, 200));
45
- }
46
- throw new Error(
47
- `Daemon did not exit within ${timeoutMs}ms (pid=${pid ?? 'unknown'}).`,
48
- );
49
- }
50
-
51
- export function spawnDaemon(): { pid: number; logFile: string } {
52
- const store = new AuthStore();
53
- const active = store.getActive();
54
- if (!active) throw new Error('Not logged in.');
55
-
56
- const stateDir = getProfileStateDir(active);
57
- mkdirSync(stateDir, { recursive: true });
58
-
59
- const existingPid = getRunningDaemonPid(stateDir);
60
- if (existingPid) {
61
- throw new Error(`Daemon already running (pid=${existingPid}). Stop it first.`);
62
- }
63
-
64
- const logsDir = getProfileLogsDir(active);
65
- mkdirSync(logsDir, { recursive: true });
66
-
67
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
68
- const logFile = join(logsDir, `daemon-${timestamp}.log`);
69
- const logFd = openSync(logFile, 'a');
70
-
71
- const fwd = (p: string) => p.replace(/\\/g, '/');
72
- const binEntry = fwd(resolve(__dirname, '..', '..', 'bin', 'clawclaw-cli.mjs'));
73
- if (!existsSync(binEntry)) throw new Error('clawclaw-cli bin entry not found');
74
-
75
- let npmGlobalModules = '';
76
- try { npmGlobalModules = execSync('npm root -g', { encoding: 'utf8', windowsHide: true }).trim(); } catch {}
77
-
78
- const existingNodePath = process.env.NODE_PATH ?? '';
79
- const nodePath = [npmGlobalModules, existingNodePath].filter(Boolean).join(delimiter);
80
- const isWin = process.platform === 'win32';
81
-
82
- const child = spawn(process.execPath, [binEntry, '_daemon'], {
83
- detached: true,
84
- stdio: isWin ? ['ignore', 'ignore', 'ignore'] : ['ignore', logFd, logFd],
85
- cwd: process.cwd(),
86
- env: {
87
- ...process.env,
88
- NODE_PATH: nodePath,
89
- ...(isWin ? { CLAWCLAW_LOG_FILE: logFile } : {}),
90
- },
91
- windowsHide: true,
92
- });
93
-
94
- child.on('error', (err) => {
95
- log.error('DAEMON_SPAWN', `failed to start daemon`, err);
96
- });
97
-
98
- child.unref();
99
- return { pid: child.pid!, logFile };
100
- }