mancode 0.3.2 → 0.3.3

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.
package/dist/cli.js CHANGED
@@ -9,7 +9,7 @@ import { program } from "commander";
9
9
  // src/commands/init.ts
10
10
  import { promises as fs2 } from "fs";
11
11
  import path13 from "path";
12
- import process3 from "process";
12
+ import process4 from "process";
13
13
 
14
14
  // src/installers/claude-code.ts
15
15
  import {
@@ -510,215 +510,229 @@ function yamlString(s) {
510
510
  }
511
511
 
512
512
  // src/templates/inline.ts
513
- var SESSION_START_HOOK = `#!/bin/bash
514
- # .mancode/hooks/session-start.sh
515
- # mancode SessionStart hook - \u52A0\u8F7D\u9879\u76EE\u4E0A\u4E0B\u6587
516
- # \u7CFB\u7EDF\u4F9D\u8D56\uFF1Abash\u3001git\u3001\uFF08\u53EF\u9009\uFF09jq
517
- set -uo pipefail
518
-
519
- PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")"
520
- STATE_FILE="$PROJECT_ROOT/.mancode/state.json"
521
- PROFILE_FILE="$PROJECT_ROOT/.mancode/project-profile.json"
522
-
523
- HAS_JQ=0
524
- if [ "\${MANCODE_DISABLE_JQ:-0}" != "1" ]; then
525
- command -v jq >/dev/null 2>&1 && HAS_JQ=1
526
- fi
527
-
528
- json_get() {
529
- local key="$1"
530
- local file="$2"
531
- if [ "$HAS_JQ" = "1" ]; then
532
- jq -r ".$key // empty" "$file" 2>/dev/null || true
533
- else
534
- grep "\\"$key\\"" "$file" 2>/dev/null | sed -E 's/.*: *"([^"]*)".*/\\1/' || true
535
- fi
536
- }
537
-
538
- json_any_get() {
539
- local key="$1"
540
- local file="$2"
541
- if [ "$HAS_JQ" = "1" ]; then
542
- jq -r ".$key // empty" "$file" 2>/dev/null || true
543
- else
544
- grep "\\"$key\\"" "$file" 2>/dev/null | head -n 1 | sed -E 's/.*: *"?([^",}]*)"?[,]?.*/\\1/' || true
545
- fi
546
- }
547
-
548
- if [ ! -f "$STATE_FILE" ]; then
549
- echo "\u2139\uFE0F mancode \u672A\u521D\u59CB\u5316\u3002\u8FD0\u884C \\\`mancode init\\\` \u5F00\u59CB\u3002"
550
- exit 0
551
- fi
552
-
553
- # \u6E05\u6D17\u51FD\u6570\uFF1A\u53BB\u6362\u884C\u3001\u9650\u5236\u957F\u5EA6\uFF08\u9632\u6B62\u810F\u6570\u636E\u6C61\u67D3 prompt\uFF09
554
- sanitize() {
555
- printf '%s' "$1" | tr '\\n\\r' ' ' | head -c 200
556
- }
557
-
558
- MODE=$(json_get "currentMode" "$STATE_FILE")
559
- TECH_STACK=$(json_get "techStack" "$STATE_FILE")
560
- UI_LIBRARY=$(json_get "uiLibrary" "$STATE_FILE")
561
- TEAM_AUTO=$(json_any_get "teamModeAutoDetected" "$STATE_FILE")
562
- CONTRIBUTORS=$(json_any_get "contributors" "$STATE_FILE")
563
-
564
- echo "mancode_mode: \${MODE:-solo}"
565
- echo "project_type: $(sanitize "$TECH_STACK")"
566
- echo "ui_library: $(sanitize "$UI_LIBRARY")"
567
- echo ""
568
-
569
- echo "## mancode \xB7 \${MODE:-solo} mode"
570
- echo ""
571
- echo "\u4F60\u6B63\u5728\u4F7F\u7528 mancode \${MODE:-solo} \u6A21\u5F0F\u3002"
572
- echo ""
573
- echo "### \u6838\u5FC3\u539F\u5219"
574
- echo "1. **\u4F18\u5148\u590D\u7528\u9879\u76EE\u5DF2\u6709\u4EE3\u7801**"
575
- echo " - \u68C0\u67E5\u5DF2\u68C0\u6D4B\u5230\u7684\u6E90\u7801\u76EE\u5F55\u548C\u5DF2\u6709\u7C7B\u4F3C\u5B9E\u73B0"
576
- echo " - \u590D\u7528\u73B0\u6709\u7EC4\u4EF6\u3001\u51FD\u6570\u3001\u6837\u5F0F"
577
- echo ""
578
- if [ "$(json_get "uiAssets" "$PROFILE_FILE")" = "detected" ]; then
579
- echo "2. **\u5E94\u7528\u9879\u76EE\u5BA1\u7F8E token**\uFF08\u4EC5\u5728\u9879\u76EE profile \u786E\u8BA4\u6709 UI \u8D44\u4EA7\u4E14\u4EFB\u52A1\u6D89\u53CA UI \u65F6\uFF09"
580
- echo " - UI library: $(sanitize "$UI_LIBRARY")"
581
- echo " - \u4F7F\u7528\u9879\u76EE\u5DF2\u6709\u7684\u8BBE\u8BA1 token"
582
- else
583
- echo "2. **\u6309\u9879\u76EE\u80FD\u529B\u5DE5\u4F5C**"
584
- echo " - \u4E0D\u5047\u5B9A\u5B58\u5728 UI\u3001\u6D4F\u89C8\u5668\u6216\u7279\u5B9A\u6280\u672F\u6808"
585
- echo " - \u5148\u8BFB\u53D6 project-profile \u4E0E\u9879\u76EE\u73B0\u6709\u9A8C\u8BC1\u65B9\u5F0F"
586
- fi
587
- echo ""
588
- echo "3. **\u6700\u5C0F\u6539\u52A8**"
589
- echo " - \u53EA\u6539\u7528\u6237\u8981\u6C42\u7684\u90E8\u5206"
590
- echo " - \u4E0D\u91CD\u6784\u65E0\u5173\u4EE3\u7801"
591
-
592
- if [ "$TEAM_AUTO" = "true" ] && [ "\${MODE:-solo}" = "solo" ]; then
593
- echo ""
594
- echo "### \u56E2\u961F\u534F\u4F5C\u63D0\u9192"
595
- echo "\u68C0\u6D4B\u5230\u56E2\u961F\u9879\u76EE\uFF08contributors: \${CONTRIBUTORS:-2}\uFF09\u3002"
596
- echo '- \u6D89\u53CA\u591A\u4EBA\u534F\u4F5C\u3001\u4EA4\u63A5\u3001PR\u3001\u5171\u4EAB\u6A21\u5757\u65F6\uFF0C\u4F18\u5148\u4F7F\u7528 /manteam <task>\u3002'
597
- echo '- \u53EA\u505A\u4E2A\u4EBA\u5C0F\u6539\u52A8\u65F6\uFF0C\u53EF\u4EE5\u7EE7\u7EED solo\uFF1B\u9700\u8981\u9000\u51FA\u6D41\u7A0B\u7528 /mansolo\u3002'
598
- fi
513
+ var SESSION_START_HOOK = String.raw`#!/usr/bin/env node
514
+ // .mancode/hooks/session-start.mjs
515
+ // mancode SessionStart hook - cross-platform project context
516
+ import { readFileSync } from 'node:fs';
517
+ import path from 'node:path';
518
+ import { fileURLToPath } from 'node:url';
519
+
520
+ const projectRoot = path.resolve(
521
+ path.dirname(fileURLToPath(import.meta.url)),
522
+ '../..',
523
+ );
524
+ const state = readJson(path.join(projectRoot, '.mancode', 'state.json'));
525
+ const profile =
526
+ readJson(path.join(projectRoot, '.mancode', 'project-profile.json')) || {};
527
+
528
+ if (!state) {
529
+ console.log('ℹ️ mancode 未初始化。运行 mancode init 开始。');
530
+ process.exit(0);
531
+ }
532
+
533
+ const mode = text(state.currentMode) || 'solo';
534
+ const techStack = sanitize(state.techStack);
535
+ const uiLibrary = sanitize(state.uiLibrary);
536
+ const output = [
537
+ 'mancode_mode: ' + mode,
538
+ 'project_type: ' + techStack,
539
+ 'ui_library: ' + uiLibrary,
540
+ '',
541
+ '## mancode · ' + mode + ' mode',
542
+ '',
543
+ '你正在使用 mancode ' + mode + ' 模式。',
544
+ '',
545
+ '### 核心原则',
546
+ '1. **优先复用项目已有代码**',
547
+ ' - 检查已检测到的源码目录和已有类似实现',
548
+ ' - 复用现有组件、函数、样式',
549
+ '',
550
+ ];
551
+
552
+ if (profile.uiAssets === 'detected') {
553
+ output.push(
554
+ '2. **应用项目审美 token**(仅在项目 profile 确认有 UI 资产且任务涉及 UI 时)',
555
+ ' - UI library: ' + uiLibrary,
556
+ ' - 使用项目已有的设计 token',
557
+ );
558
+ } else {
559
+ output.push(
560
+ '2. **按项目能力工作**',
561
+ ' - 不假定存在 UI、浏览器或特定技术栈',
562
+ ' - 先读取 project-profile 与项目现有验证方式',
563
+ );
564
+ }
565
+
566
+ output.push(
567
+ '',
568
+ '3. **最小改动**',
569
+ ' - 只改用户要求的部分',
570
+ ' - 不重构无关代码',
571
+ );
572
+
573
+ if (state.teamModeAutoDetected === true && mode === 'solo') {
574
+ output.push(
575
+ '',
576
+ '### 团队协作提醒',
577
+ '检测到团队项目(contributors: ' +
578
+ (Number.isFinite(state.contributors) ? state.contributors : 2) +
579
+ ')。',
580
+ '- 涉及多人协作、交接、PR、共享模块时,优先使用 /manteam <task>。',
581
+ '- 只做个人小改动时,可以继续 solo;需要退出流程用 /mansolo。',
582
+ );
583
+ }
584
+
585
+ console.log(output.join('\n'));
586
+
587
+ function readJson(filePath) {
588
+ try {
589
+ return JSON.parse(readFileSync(filePath, 'utf8'));
590
+ } catch {
591
+ return null;
592
+ }
593
+ }
594
+
595
+ function text(value) {
596
+ return typeof value === 'string' ? value : '';
597
+ }
598
+
599
+ function sanitize(value) {
600
+ return String(value ?? '').replace(/[\r\n]/g, ' ').slice(0, 200);
601
+ }
599
602
  `;
600
- var USER_PROMPT_SUBMIT_HOOK = `#!/bin/bash
601
- # .mancode/hooks/user-prompt-submit.sh
602
- # mancode UserPromptSubmit hook - \u6CE8\u5165 6 \u95EE\u8FFD\u95EE + \u5BA1\u7F8E token
603
- set -uo pipefail
604
-
605
- PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")"
606
- STATE_FILE="$PROJECT_ROOT/.mancode/state.json"
607
- AESTHETICS_FILE="$PROJECT_ROOT/.mancode/aesthetics/style-tokens.json"
608
-
609
- HAS_JQ=0
610
- if [ "\${MANCODE_DISABLE_JQ:-0}" != "1" ]; then
611
- command -v jq >/dev/null 2>&1 && HAS_JQ=1
612
- fi
613
-
614
- json_get() {
615
- local key="$1"
616
- local file="$2"
617
- if [ "$HAS_JQ" = "1" ]; then
618
- jq -r ".$key // empty" "$file" 2>/dev/null || true
619
- else
620
- grep "\\"$key\\"" "$file" 2>/dev/null | sed -E 's/.*: *"([^"]*)".*/\\1/' || true
621
- fi
622
- }
623
-
624
- # \u6E05\u6D17\u6CE8\u5165\u5230 prompt \u7684\u52A8\u6001\u503C\uFF1A\u53BB\u6362\u884C\u3001\u9650\u5236\u957F\u5EA6\uFF0C\u907F\u514D\u810F token \u6C61\u67D3\u4E0A\u4E0B\u6587\u7ED3\u6784
625
- sanitize() {
626
- printf '%s' "$1" | tr '\\n\\r' ' ' | head -c 200
627
- }
628
-
629
- MODE=$(json_get "currentMode" "$STATE_FILE")
630
-
631
- # \u53EA\u5728 solo \u6A21\u5F0F\u4E0B\u8F93\u51FA 6 \u95EE
632
- if [ "$MODE" = "solo" ]; then
633
- echo "## \u52A8\u624B\u524D\uFF0C\u5148\u60F3\u516D\u4E2A\u95EE\u9898\uFF1A"
634
- echo ""
635
- echo "1. **\u4E3A\u4EC0\u4E48\u505A\uFF1F**"
636
- echo " - \u8FD9\u4E2A\u6539\u52A8\u89E3\u51B3\u4EC0\u4E48\u95EE\u9898\uFF1F"
637
- echo ""
638
- echo "2. **\u5DF2\u7ECF\u6709\u4EC0\u4E48\uFF1F**"
639
- echo " - \u9879\u76EE\u91CC\u6709\u6CA1\u6709\u7C7B\u4F3C\u7684\u5B9E\u73B0\u53EF\u4EE5\u590D\u7528\uFF1F"
640
- echo ""
641
- echo "3. **\u6700\u5C11\u6539\u591A\u5C11\uFF1F**"
642
- echo " - \u80FD\u7528\u4E00\u884C\u89E3\u51B3\u5417\uFF1F\u80FD\u590D\u7528\u73B0\u6709\u4EE3\u7801\u5417\uFF1F"
643
- echo ""
644
- echo "4. **\u80FD\u4E0D\u80FD\u4E0D\u62C6\u65B0\u7CFB\u7EDF\uFF1F**"
645
- echo " - \u4E0D\u65B0\u5EFA\u6587\u4EF6\u6216\u6A21\u5757\u80FD\u5B8C\u6210\u5417\uFF1F"
646
- echo ""
647
- echo "5. **\u975E\u5E73\u51E1\u903B\u8F91\u600E\u6837\u6700\u5C0F\u8FD0\u884C\u9A8C\u8BC1\uFF1F**"
648
- echo ""
649
- echo "6. **\u6709\u4EC0\u4E48\u6CA1\u628A\u63E1\u7684\uFF1F**"
650
- echo " - \u5148\u81EA\u884C\u67E5\u4EE3\u7801\u6216\u6587\u6863\uFF0C\u6700\u591A 2 \u6B21\u5DE5\u5177\u8C03\u7528\uFF1B\u4ECD\u4E0D\u786E\u5B9A\u518D\u95EE\u7528\u6237\u3002"
651
- echo ""
652
- fi
653
-
654
- # Claude Code \u901A\u8FC7 stdin \u4F20\u5165 JSON: {"prompt": "...", ...}
655
- # \u5148\u8BFB\u53D6\u5B8C\u6574\u8F93\u5165\uFF0C\u518D\u89E3\u6790
656
- INPUT=$(cat)
657
-
658
- if [ "$HAS_JQ" = "1" ]; then
659
- USER_PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""' 2>/dev/null)
660
- # jq \u5931\u8D25\u6216\u8FD4\u56DE\u7A7A\uFF0Cfallback \u5230\u539F\u59CB\u8F93\u5165
661
- if [ -z "$USER_PROMPT" ]; then
662
- USER_PROMPT="$INPUT"
663
- fi
664
- else
665
- # \u65E0 jq: \u4F7F\u7528 sed \u63D0\u53D6 JSON \u4E2D\u7684 prompt \u5B57\u6BB5
666
- USER_PROMPT=$(echo "$INPUT" | sed -n 's/.*"prompt"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p')
667
- # sed \u5931\u8D25\uFF0Cfallback \u5230\u539F\u59CB\u8F93\u5165
668
- if [ -z "$USER_PROMPT" ]; then
669
- USER_PROMPT="$INPUT"
670
- fi
671
- fi
672
-
673
- if [ "$MODE" = "solo" ] && echo "$USER_PROMPT" | grep -qiE "\u5148(\u522B|\u4E0D\u8981|\u770B|\u770B\u770B|\u8C03\u7814|\u5206\u6790|\u8BC4\u4F30)|\u7ED9.*\u65B9\u6848|\u7ED9.*\u8BA1\u5212|\u600E\u4E48.*\u505A|\u5982\u4F55.*\u505A|\u600E\u4E48.*\u5B9E\u73B0|\u5982\u4F55.*\u5B9E\u73B0|\u5E94\u8BE5\u600E\u4E48|\u600E\u4E48.*\u62C6|\u62C6\u5206|\u53EA\u7ED9.*\u8BA1\u5212|\u4E0D\u8981.*\u6539\u4EE3\u7801|\u522B.*\u6539\u4EE3\u7801|\u4E0D\u8981.*\u52A8\u4EE3\u7801|\u522B.*\u52A8\u4EE3\u7801|\u8BC4\u4F30.*\u98CE\u9669|\u98CE\u9669.*\u8BC4\u4F30|\u8BBE\u8BA1.*\u65B9\u6848|\u67B6\u6784|\u8FC1\u79FB|\u96C6\u6210|\\b(plan|planning|research|investigate|approach|proposal|architecture|risk|migration|integration)\\b|how (should|would|to)|do not (edit|modify|change)|don.t (edit|modify|change)|no code changes|without changing code"; then
674
- echo "## mancode \u81EA\u52A8\u8DEF\u7531"
675
- echo ""
676
- echo "\u8FD9\u4E2A\u8BF7\u6C42\u662F\u89C4\u5212/\u8C03\u7814\u7C7B\u4EFB\u52A1\u3002\u4E0D\u8981\u76F4\u63A5\u8FDB\u5165 solo \u5B9E\u65BD\u3002"
677
- echo "\u5FC5\u987B\u5148\u8C03\u7528 Skill tool\uFF0Cskill='man'\uFF0C\u628A\u7528\u6237\u539F\u59CB\u8BF7\u6C42\u4F5C\u4E3A task\uFF0C\u6267\u884C Scout \u8C03\u7814\u3001\u6F84\u6E05\u548C Plan Coach plan\u3002"
678
- echo "\u7528\u6237\u53EA\u8981\u8BA1\u5212\u65F6\uFF0C\u5728 Step 4 \u9009\u62E9\u201C\u53EA\u8981\u8BA1\u5212\u201D\uFF1B\u4E0D\u8981\u5207\u5230\u53E6\u4E00\u4E2A\u547D\u4EE4\u3002"
679
- echo ""
680
- fi
681
-
682
- # UI token \u4EC5\u5728 profile \u786E\u8BA4\u5B58\u5728 UI \u8D44\u4EA7\u4E14\u4EFB\u52A1\u660E\u786E\u6D89\u53CA UI \u65F6\u6CE8\u5165\u3002
683
- PROFILE_FILE="$PROJECT_ROOT/.mancode/project-profile.json"
684
- UI_ASSETS=""
685
- if [ -f "$PROFILE_FILE" ]; then
686
- UI_ASSETS=$(json_get "uiAssets" "$PROFILE_FILE")
687
- fi
688
- if [ "$UI_ASSETS" = "detected" ] && echo "$USER_PROMPT" | grep -qiE "\\b(button|component|page|style|ui|design|layout|css|color|font|theme|card|input|modal|dialog|header|footer|sidebar|dropdown|tooltip|toast|avatar|badge)\\b|\u754C\u9762|\u9875\u9762|\u6309\u94AE|\u6837\u5F0F|\u989C\u8272|\u5B57\u4F53|\u5E03\u5C40|\u7EC4\u4EF6|\u5F39\u7A97|\u5BFC\u822A|\u5361\u7247|\u8F93\u5165\u6846|\u4E3B\u9898|\u7F8E\u5316|\u4F18\u5316.*\u754C\u9762|\u8C03\u6574.*\u6837\u5F0F"; then
689
- if [ -f "$AESTHETICS_FILE" ]; then
690
- MATCH_LEVEL=$(json_get "matchLevel" "$AESTHETICS_FILE")
691
-
692
- if [ "$MATCH_LEVEL" = "high" ]; then
693
- if [ "$HAS_JQ" = "1" ]; then
694
- # \u63D0\u53D6\u6458\u8981 + cap\uFF08docs/07 \xA74.1\uFF1Acolors \u22648, fonts \u22644, \u603B < 800 tokens\uFF09
695
- UI=$(jq -r '.uiLibrary // empty' "$AESTHETICS_FILE" 2>/dev/null)
696
- DARK=$(jq -r '.darkMode // empty' "$AESTHETICS_FILE" 2>/dev/null)
697
- MATCH=$(jq -r '.matchLevel // empty' "$AESTHETICS_FILE" 2>/dev/null)
698
- COLORS=$(jq -r '.colors | to_entries | map(select(.key | test("^[A-Za-z0-9_-]{1,80}$"))) | .[0:8] | map("\\(.key)=\\(.value)") | join(", ")' "$AESTHETICS_FILE" 2>/dev/null)
699
- FONTS=$(jq -r '.fonts | to_entries | map(select(.key | test("^[A-Za-z0-9_-]{1,80}$"))) | .[0:4] | map("\\(.key)=\\(.value | first)") | join(", ")' "$AESTHETICS_FILE" 2>/dev/null)
700
- COMPONENTS=$(jq -r '(.components // []) | map(select(test("^[A-Z][A-Za-z0-9]{0,79}$"))) | .[0:8] | join(", ")' "$AESTHETICS_FILE" 2>/dev/null)
701
- CSS_VARS=$(jq -r '(.cssVariables // {}) | to_entries | map(select(.key | test("^[A-Za-z0-9_-]{1,80}$"))) | .[0:8] | map("--\\(.key)=\\(.value)") | join(", ")' "$AESTHETICS_FILE" 2>/dev/null)
702
-
703
- echo "## \u5BA1\u7F8E token \u6458\u8981"
704
- [ -n "$UI" ] && echo "UI: $(sanitize "$UI")"
705
- [ -n "$DARK" ] && echo "Dark: $(sanitize "$DARK")"
706
- [ -n "$MATCH" ] && echo "Match: $(sanitize "$MATCH")"
707
- [ -n "$COLORS" ] && echo "Colors (\u524D 8): $(sanitize "$COLORS")"
708
- [ -n "$FONTS" ] && echo "Fonts (\u524D 4): $(sanitize "$FONTS")"
709
- [ -n "$COMPONENTS" ] && echo "Components (\u524D 8): $(sanitize "$COMPONENTS")"
710
- [ -n "$CSS_VARS" ] && echo "CSS variables (\u524D 8): $(sanitize "$CSS_VARS")"
711
- echo "\u5B8C\u6574 token: .mancode/aesthetics/style-tokens.json"
712
- echo ""
713
- else
714
- # \u65E0 jq: \u53EA\u8F93\u51FA\u6307\u9488\uFF08cap \u65E0\u6CD5\u4E25\u683C\u6267\u884C\uFF09
715
- echo "## \u5BA1\u7F8E token"
716
- echo "\u8BFB\u53D6 .mancode/aesthetics/style-tokens.json"
717
- echo ""
718
- fi
719
- fi
720
- fi
721
- fi
603
+ var USER_PROMPT_SUBMIT_HOOK = String.raw`#!/usr/bin/env node
604
+ // .mancode/hooks/user-prompt-submit.mjs
605
+ // mancode UserPromptSubmit hook - cross-platform prompt and style context
606
+ import { readFileSync } from 'node:fs';
607
+ import path from 'node:path';
608
+ import { fileURLToPath } from 'node:url';
609
+
610
+ const projectRoot = path.resolve(
611
+ path.dirname(fileURLToPath(import.meta.url)),
612
+ '../..',
613
+ );
614
+ const mancodeDir = path.join(projectRoot, '.mancode');
615
+ const state = readJson(path.join(mancodeDir, 'state.json')) || {};
616
+ const profile = readJson(path.join(mancodeDir, 'project-profile.json')) || {};
617
+ const mode = typeof state.currentMode === 'string' ? state.currentMode : '';
618
+ const output = [];
619
+
620
+ if (mode === 'solo') {
621
+ output.push(
622
+ '## 动手前,先想六个问题:',
623
+ '',
624
+ '1. **为什么做?**',
625
+ ' - 这个改动解决什么问题?',
626
+ '',
627
+ '2. **已经有什么?**',
628
+ ' - 项目里有没有类似的实现可以复用?',
629
+ '',
630
+ '3. **最少改多少?**',
631
+ ' - 能用一行解决吗?能复用现有代码吗?',
632
+ '',
633
+ '4. **能不能不拆新系统?**',
634
+ ' - 不新建文件或模块能完成吗?',
635
+ '',
636
+ '5. **非平凡逻辑怎样最小运行验证?**',
637
+ '',
638
+ '6. **有什么没把握的?**',
639
+ ' - 先自行查代码或文档,最多 2 次工具调用;仍不确定再问用户。',
640
+ '',
641
+ );
642
+ }
643
+
644
+ const rawInput = readFileSync(0, 'utf8');
645
+ const userPrompt = readPrompt(rawInput);
646
+ const planningPattern = /先(?:别|不要|看|看看|调研|分析|评估)|给.*方案|给.*计划|怎么.*做|如何.*做|怎么.*实现|如何.*实现|应该怎么|怎么.*拆|拆分|只给.*计划|不要.*改代码|别.*改代码|不要.*动代码|别.*动代码|评估.*风险|风险.*评估|设计.*方案|架构|迁移|集成|\b(?:plan|planning|research|investigate|approach|proposal|architecture|risk|migration|integration)\b|how (?:should|would|to)|do not (?:edit|modify|change)|don.t (?:edit|modify|change)|no code changes|without changing code/iu;
647
+
648
+ if (mode === 'solo' && planningPattern.test(userPrompt)) {
649
+ output.push(
650
+ '## mancode 自动路由',
651
+ '',
652
+ '这个请求是规划/调研类任务。不要直接进入 solo 实施。',
653
+ "必须先调用 Skill tool,skill='man',把用户原始请求作为 task,执行 Scout 调研、澄清和 Plan Coach plan。",
654
+ '用户只要计划时,在 Step 4 选择“只要计划”;不要切到另一个命令。',
655
+ '',
656
+ );
657
+ }
658
+
659
+ const uiPattern = /\b(?:button|component|page|style|ui|design|layout|css|color|font|theme|card|input|modal|dialog|header|footer|sidebar|dropdown|tooltip|toast|avatar|badge)\b|界面|页面|按钮|样式|颜色|字体|布局|组件|弹窗|导航|卡片|输入框|主题|美化|优化.*界面|调整.*样式/iu;
660
+
661
+ if (profile.uiAssets === 'detected' && uiPattern.test(userPrompt)) {
662
+ appendAestheticSummary(
663
+ output,
664
+ readJson(path.join(mancodeDir, 'aesthetics', 'style-tokens.json')),
665
+ );
666
+ }
667
+
668
+ if (output.length > 0) console.log(output.join('\n'));
669
+
670
+ function appendAestheticSummary(lines, tokens) {
671
+ if (!tokens || tokens.matchLevel !== 'high') return;
672
+
673
+ const colors = safeEntries(tokens.colors, 8).map(
674
+ ([key, value]) => key + '=' + String(value),
675
+ );
676
+ const fonts = safeEntries(tokens.fonts, 4)
677
+ .filter(([, value]) => Array.isArray(value) && value.length > 0)
678
+ .map(([key, value]) => key + '=' + String(value[0]));
679
+ const components = Array.isArray(tokens.components)
680
+ ? tokens.components
681
+ .filter(
682
+ (value) =>
683
+ typeof value === 'string' && /^[A-Z][A-Za-z0-9]{0,79}$/.test(value),
684
+ )
685
+ .slice(0, 8)
686
+ : [];
687
+ const cssVariables = safeEntries(tokens.cssVariables, 8).map(
688
+ ([key, value]) => '--' + key + '=' + String(value),
689
+ );
690
+
691
+ lines.push('## 审美 token 摘要');
692
+ appendValue(lines, 'UI', tokens.uiLibrary);
693
+ appendValue(lines, 'Dark', tokens.darkMode);
694
+ appendValue(lines, 'Match', tokens.matchLevel);
695
+ appendValue(lines, 'Colors (前 8)', colors.join(', '));
696
+ appendValue(lines, 'Fonts (前 4)', fonts.join(', '));
697
+ appendValue(lines, 'Components (前 8)', components.join(', '));
698
+ appendValue(lines, 'CSS variables (前 8)', cssVariables.join(', '));
699
+ lines.push('完整 token: .mancode/aesthetics/style-tokens.json', '');
700
+ }
701
+
702
+ function safeEntries(value, limit) {
703
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return [];
704
+ return Object.entries(value)
705
+ .filter(([key]) => /^[A-Za-z0-9_-]{1,80}$/.test(key))
706
+ .slice(0, limit);
707
+ }
708
+
709
+ function appendValue(lines, label, value) {
710
+ const clean = sanitize(value);
711
+ if (clean) lines.push(label + ': ' + clean);
712
+ }
713
+
714
+ function readPrompt(input) {
715
+ try {
716
+ const parsed = JSON.parse(input);
717
+ return typeof parsed.prompt === 'string' && parsed.prompt
718
+ ? parsed.prompt
719
+ : input;
720
+ } catch {
721
+ return input;
722
+ }
723
+ }
724
+
725
+ function readJson(filePath) {
726
+ try {
727
+ return JSON.parse(readFileSync(filePath, 'utf8'));
728
+ } catch {
729
+ return null;
730
+ }
731
+ }
732
+
733
+ function sanitize(value) {
734
+ return String(value ?? '').replace(/[\r\n]/g, ' ').slice(0, 200);
735
+ }
722
736
  `;
723
737
  var SOLO_SKILL = `# mancode \xB7 solo mode
724
738
 
@@ -1247,7 +1261,7 @@ ${spec.body}
1247
1261
  }
1248
1262
 
1249
1263
  // src/installers/common.ts
1250
- import { chmod, mkdir as mkdir2, readFile as readFile2, stat, writeFile as writeFile2 } from "fs/promises";
1264
+ import { mkdir as mkdir2, readFile as readFile2, stat, writeFile as writeFile2 } from "fs/promises";
1251
1265
  import path2 from "path";
1252
1266
 
1253
1267
  // src/system/team-memory.ts
@@ -1437,12 +1451,10 @@ async function readTextIfExists(filePath) {
1437
1451
  }
1438
1452
  }
1439
1453
  async function installHooks(hooksDir) {
1440
- const sessionStartDst = path2.join(hooksDir, "session-start.sh");
1454
+ const sessionStartDst = path2.join(hooksDir, "session-start.mjs");
1441
1455
  await writeFile2(sessionStartDst, SESSION_START_HOOK, "utf-8");
1442
- await chmod(sessionStartDst, 493);
1443
- const userPromptDst = path2.join(hooksDir, "user-prompt-submit.sh");
1456
+ const userPromptDst = path2.join(hooksDir, "user-prompt-submit.mjs");
1444
1457
  await writeFile2(userPromptDst, USER_PROMPT_SUBMIT_HOOK, "utf-8");
1445
- await chmod(userPromptDst, 493);
1446
1458
  }
1447
1459
  async function pathExists(p) {
1448
1460
  try {
@@ -1460,12 +1472,18 @@ function isNodeError(err) {
1460
1472
  var CLAUDE_SKILL_MANAGED_MARKER = "<!-- Managed by mancode:claude-skill. Do not edit this marker. -->";
1461
1473
  var CLAUDE_AGENT_MANAGED_MARKER = "<!-- Managed by mancode:claude-agent. Do not edit this marker. -->";
1462
1474
  var MANCODE_HOOK_COMMANDS = [
1475
+ 'node ".mancode/hooks/session-start.mjs"',
1476
+ 'node ".mancode/hooks/user-prompt-submit.mjs"'
1477
+ ];
1478
+ var LEGACY_MANCODE_HOOK_COMMANDS = [
1463
1479
  "bash .mancode/hooks/session-start.sh",
1464
1480
  "bash .mancode/hooks/user-prompt-submit.sh"
1465
1481
  ];
1466
1482
  function isGeneratedMancodeHookCommand(command) {
1467
1483
  const normalized = command.trim();
1468
- return MANCODE_HOOK_COMMANDS.some((item) => item === normalized);
1484
+ return [...MANCODE_HOOK_COMMANDS, ...LEGACY_MANCODE_HOOK_COMMANDS].some(
1485
+ (item) => item === normalized
1486
+ );
1469
1487
  }
1470
1488
  var LEGACY_CLAUDE_SKILL_SETTINGS = {
1471
1489
  solo: ".claude/skills/mancode-solo.md",
@@ -1494,6 +1512,14 @@ async function installClaudeCode(projectRoot, options) {
1494
1512
  await installAgents(path3.join(claudeDir, "agents"), force);
1495
1513
  }
1496
1514
  await updateClaudeSettings(claudeDir, settings);
1515
+ await removeLegacyHookFiles(projectRoot);
1516
+ }
1517
+ async function removeLegacyHookFiles(projectRoot) {
1518
+ const hooksDir = path3.join(projectRoot, ".mancode", "hooks");
1519
+ await Promise.all([
1520
+ rm(path3.join(hooksDir, "session-start.sh"), { force: true }),
1521
+ rm(path3.join(hooksDir, "user-prompt-submit.sh"), { force: true })
1522
+ ]);
1497
1523
  }
1498
1524
  async function installSoloSkill(skillsDir, force) {
1499
1525
  await installProjectSkill(
@@ -2860,11 +2886,12 @@ function isPlatformName(platform) {
2860
2886
  }
2861
2887
 
2862
2888
  // src/system/detect-team.ts
2863
- import { exec } from "child_process";
2889
+ import { execFile } from "child_process";
2890
+ import { access as access2 } from "fs/promises";
2864
2891
  import path10 from "path";
2865
2892
  import process2 from "process";
2866
2893
  import { promisify } from "util";
2867
- var execAsync = promisify(exec);
2894
+ var execFileAsync = promisify(execFile);
2868
2895
  var NO_TEAM = {
2869
2896
  isTeam: false,
2870
2897
  contributors: 1,
@@ -2872,101 +2899,68 @@ var NO_TEAM = {
2872
2899
  hasRemote: false
2873
2900
  };
2874
2901
  var REMOTE_PATTERN = /github\.com|gitlab\.com|bitbucket\.org/;
2875
- var EXEC_OPTS = {
2876
- shell: "/bin/bash",
2877
- encoding: "utf-8",
2878
- env: {
2879
- ...process2.env,
2880
- GIT_PAGER: "cat",
2881
- PAGER: "cat",
2882
- GIT_TERMINAL_PROMPT: "0"
2883
- }
2902
+ var GIT_ENV = {
2903
+ ...process2.env,
2904
+ GIT_PAGER: "cat",
2905
+ PAGER: "cat",
2906
+ GIT_TERMINAL_PROMPT: "0"
2884
2907
  };
2885
2908
  async function detectTeamStatus(projectRoot) {
2886
- const gitDir = path10.join(projectRoot, ".git");
2887
2909
  try {
2888
- await execAsync("git rev-parse --is-inside-work-tree", {
2889
- cwd: projectRoot,
2890
- ...EXEC_OPTS
2891
- });
2892
- } catch {
2893
- return NO_TEAM;
2894
- }
2895
- try {
2896
- await execAsync(`test -d ${JSON.stringify(gitDir)}`, {
2897
- cwd: projectRoot,
2898
- ...EXEC_OPTS
2899
- });
2910
+ await access2(path10.join(projectRoot, ".git"));
2911
+ const inside = await runGit(projectRoot, [
2912
+ "rev-parse",
2913
+ "--is-inside-work-tree"
2914
+ ]);
2915
+ if (inside.trim() !== "true") return NO_TEAM;
2916
+ const [allEmails, recentEmails, remotes] = await Promise.all([
2917
+ runGit(projectRoot, ["log", "--all", "--format=%ae"]),
2918
+ runGit(projectRoot, ["log", "--since=30 days ago", "--format=%ae"]),
2919
+ runGit(projectRoot, ["remote", "-v"])
2920
+ ]);
2921
+ const contributors = countUniqueLines(allEmails);
2922
+ const recentActive = countUniqueLines(recentEmails);
2923
+ const hasRemote = REMOTE_PATTERN.test(remotes);
2924
+ return {
2925
+ isTeam: contributors > 1 && hasRemote && recentActive > 1,
2926
+ contributors: contributors || 1,
2927
+ recentActive: recentActive || 1,
2928
+ hasRemote
2929
+ };
2900
2930
  } catch {
2901
2931
  return NO_TEAM;
2902
2932
  }
2903
- const contributors = await countUniqueEmails(
2904
- "git log --all --format='%ae'",
2905
- projectRoot
2906
- );
2907
- const recentActive = await countUniqueEmails(
2908
- "git log --since='30 days ago' --format='%ae'",
2909
- projectRoot
2910
- );
2911
- const hasRemote = await checkRemote(projectRoot);
2912
- const safeContributors = Number.isFinite(contributors) ? contributors : 0;
2913
- const safeRecent = Number.isFinite(recentActive) ? recentActive : 0;
2914
- const isTeam = safeContributors > 1 && hasRemote && safeRecent > 1;
2915
- return {
2916
- isTeam,
2917
- contributors: safeContributors || 1,
2918
- recentActive: safeRecent || 1,
2919
- hasRemote
2920
- };
2921
2933
  }
2922
- async function countUniqueEmails(command, cwd) {
2923
- try {
2924
- const { stdout } = await execAsync(
2925
- `${command} 2>/dev/null | sort -u | grep -c . || true`,
2926
- { cwd, ...EXEC_OPTS }
2927
- );
2928
- const n = Number.parseInt(stdout.trim(), 10);
2929
- return Number.isFinite(n) ? n : 0;
2930
- } catch {
2931
- return 0;
2932
- }
2934
+ async function runGit(cwd, args) {
2935
+ const { stdout } = await execFileAsync("git", args, {
2936
+ cwd,
2937
+ encoding: "utf-8",
2938
+ env: GIT_ENV,
2939
+ windowsHide: true
2940
+ });
2941
+ return stdout;
2933
2942
  }
2934
- async function checkRemote(cwd) {
2935
- try {
2936
- const { stdout } = await execAsync("git remote -v 2>/dev/null || true", {
2937
- cwd,
2938
- ...EXEC_OPTS
2939
- });
2940
- return REMOTE_PATTERN.test(stdout);
2941
- } catch {
2942
- return false;
2943
- }
2943
+ function countUniqueLines(output) {
2944
+ return new Set(
2945
+ output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean)
2946
+ ).size;
2944
2947
  }
2945
2948
 
2946
2949
  // src/system/detect.ts
2947
- import { exec as exec2 } from "child_process";
2950
+ import { execFile as execFile2 } from "child_process";
2951
+ import process3 from "process";
2948
2952
  import { promisify as promisify2 } from "util";
2949
- var execAsync2 = promisify2(exec2);
2950
- async function detectSystemDeps() {
2951
- const ALLOWED_COMMANDS = /* @__PURE__ */ new Set(["bash", "git", "jq", "node"]);
2952
- const check = async (cmd) => {
2953
- if (!ALLOWED_COMMANDS.has(cmd)) {
2954
- throw new Error(`Unsupported dependency check: ${cmd}`);
2955
- }
2956
- try {
2957
- await execAsync2(`command -v ${cmd}`, { shell: "/bin/bash" });
2958
- return true;
2959
- } catch {
2960
- return false;
2961
- }
2962
- };
2963
- const [bash, git, jq, node] = await Promise.all([
2964
- check("bash"),
2965
- check("git"),
2966
- check("jq"),
2967
- check("node")
2968
- ]);
2969
- return { bash, git, jq, node };
2953
+ var execFileAsync2 = promisify2(execFile2);
2954
+ async function detectSystemDeps(env = process3.env) {
2955
+ try {
2956
+ await execFileAsync2("git", ["--version"], {
2957
+ env,
2958
+ windowsHide: true
2959
+ });
2960
+ return { git: true };
2961
+ } catch {
2962
+ return { git: false };
2963
+ }
2970
2964
  }
2971
2965
 
2972
2966
  // src/system/project-profile.ts
@@ -3662,7 +3656,7 @@ var EXIT_ALREADY_INITIALIZED = 1;
3662
3656
  var EXIT_NOT_A_PROJECT_DIR = 2;
3663
3657
  var EXIT_INIT_FAILED = 5;
3664
3658
  var DEFAULT_INIT_PLATFORM = "claude-code";
3665
- async function init(rootDir = process3.cwd(), options = {}) {
3659
+ async function init(rootDir = process4.cwd(), options = {}) {
3666
3660
  const mancodeDir = path13.join(rootDir, ".mancode");
3667
3661
  const stateFile = path13.join(mancodeDir, "state.json");
3668
3662
  const wasInitialized = await pathExists3(stateFile);
@@ -3711,20 +3705,12 @@ async function init(rootDir = process3.cwd(), options = {}) {
3711
3705
  try {
3712
3706
  console.log("\u2713 \u68C0\u6D4B\u7CFB\u7EDF\u4F9D\u8D56...");
3713
3707
  const deps = await detectSystemDeps();
3714
- if (!deps.bash || !deps.git) {
3715
- console.error("\u2717 Missing required dependencies:");
3716
- if (!deps.bash) console.error(" - bash (required)");
3717
- if (!deps.git) console.error(" - git (required)");
3718
- console.error("");
3719
- console.error(" Install them and try again.");
3720
- return EXIT_NOT_A_PROJECT_DIR;
3721
- }
3722
- if (!deps.jq) {
3708
+ if (!deps.git) {
3723
3709
  console.log(
3724
- "\u26A0\uFE0F jq not found (optional). Hooks will use grep/sed fallback (slightly slower)."
3710
+ "\u26A0\uFE0F Git not found (optional). Team auto-detection will use solo defaults."
3725
3711
  );
3726
3712
  } else {
3727
- console.log(" bash \u2713 git \u2713 jq \u2713");
3713
+ console.log(" git \u2713");
3728
3714
  }
3729
3715
  console.log("\u2713 \u68C0\u6D4B\u9879\u76EE\u7C7B\u578B...");
3730
3716
  const profile = await detectProjectProfile(rootDir);
@@ -4005,7 +3991,7 @@ async function pathExists3(p) {
4005
3991
  // src/commands/install.ts
4006
3992
  import { promises as fs4 } from "fs";
4007
3993
  import path15 from "path";
4008
- import process4 from "process";
3994
+ import process5 from "process";
4009
3995
 
4010
3996
  // src/installers/platform-status.ts
4011
3997
  import { promises as fs3 } from "fs";
@@ -4028,8 +4014,8 @@ async function checkPlatformReadiness(rootDir, platform) {
4028
4014
  ),
4029
4015
  claudeHooksRegistered(rootDir),
4030
4016
  pathsExist([
4031
- path14.join(rootDir, ".mancode", "hooks", "session-start.sh"),
4032
- path14.join(rootDir, ".mancode", "hooks", "user-prompt-submit.sh")
4017
+ path14.join(rootDir, ".mancode", "hooks", "session-start.mjs"),
4018
+ path14.join(rootDir, ".mancode", "hooks", "user-prompt-submit.mjs")
4033
4019
  ])
4034
4020
  ]);
4035
4021
  const present = hasSoloSkill && registered && hasHookFiles;
@@ -4258,9 +4244,9 @@ function hooksRegistered(settings) {
4258
4244
  if (!isRecord4(settings)) return false;
4259
4245
  const hooks = settings.hooks;
4260
4246
  if (!isRecord4(hooks)) return false;
4261
- return hasHookCommand(hooks.SessionStart, ".mancode/hooks/session-start.sh") && hasHookCommand(
4247
+ return hasHookCommand(hooks.SessionStart, ".mancode/hooks/session-start.mjs") && hasHookCommand(
4262
4248
  hooks.UserPromptSubmit,
4263
- ".mancode/hooks/user-prompt-submit.sh"
4249
+ ".mancode/hooks/user-prompt-submit.mjs"
4264
4250
  );
4265
4251
  }
4266
4252
  function hasHookCommand(value, needle) {
@@ -4290,7 +4276,7 @@ var EXIT_OK2 = 0;
4290
4276
  var EXIT_NOT_INITIALIZED = 1;
4291
4277
  var EXIT_UNSUPPORTED_PLATFORM = 2;
4292
4278
  var EXIT_INSTALL_FAILED = 3;
4293
- async function install(rootDir = process4.cwd(), platform = "claude-code", options = {}) {
4279
+ async function install(rootDir = process5.cwd(), platform = "claude-code", options = {}) {
4294
4280
  const stateFile = path15.join(rootDir, ".mancode", "state.json");
4295
4281
  if (!await pathExists5(stateFile)) {
4296
4282
  console.error("\u2717 mancode not initialized.");
@@ -4452,9 +4438,9 @@ function isRecord5(value) {
4452
4438
  // src/commands/list-platforms.ts
4453
4439
  import { promises as fs5 } from "fs";
4454
4440
  import path16 from "path";
4455
- import process5 from "process";
4441
+ import process6 from "process";
4456
4442
  var EXIT_OK3 = 0;
4457
- async function listPlatforms(rootDir = process5.cwd()) {
4443
+ async function listPlatforms(rootDir = process6.cwd()) {
4458
4444
  const installed = new Set(await readInstalledPlatforms(rootDir));
4459
4445
  const platforms = getPlatformInstallers();
4460
4446
  console.log("");
@@ -4492,16 +4478,16 @@ function describePlatform(platform) {
4492
4478
  }
4493
4479
 
4494
4480
  // src/commands/manps.ts
4495
- import { access as access3 } from "fs/promises";
4481
+ import { access as access4 } from "fs/promises";
4496
4482
  import path18 from "path";
4497
4483
  import { createInterface } from "readline/promises";
4498
4484
 
4499
4485
  // src/system/preseason.ts
4500
- import { execFile } from "child_process";
4486
+ import { execFile as execFile3 } from "child_process";
4501
4487
  import { createHash } from "crypto";
4502
4488
  import { existsSync, readFileSync } from "fs";
4503
4489
  import {
4504
- access as access2,
4490
+ access as access3,
4505
4491
  lstat,
4506
4492
  mkdir as mkdir7,
4507
4493
  readFile as readFile8,
@@ -5061,7 +5047,7 @@ function architectureScannerUnavailableIssue() {
5061
5047
  function runDepcruise(binary, projectRoot) {
5062
5048
  return new Promise((resolve) => {
5063
5049
  const needsShell = process.platform === "win32";
5064
- execFile(
5050
+ execFile3(
5065
5051
  binary,
5066
5052
  ["--output-type", "json", "."],
5067
5053
  {
@@ -5401,7 +5387,7 @@ function pathExistsSync(file) {
5401
5387
  }
5402
5388
  async function pathExists6(file) {
5403
5389
  try {
5404
- await access2(file);
5390
+ await access3(file);
5405
5391
  return true;
5406
5392
  } catch {
5407
5393
  return false;
@@ -5564,7 +5550,7 @@ async function readStdinLines() {
5564
5550
  }
5565
5551
  async function pathExists7(p) {
5566
5552
  try {
5567
- await access3(p);
5553
+ await access4(p);
5568
5554
  return true;
5569
5555
  } catch {
5570
5556
  return false;
@@ -5574,10 +5560,10 @@ async function pathExists7(p) {
5574
5560
  // src/commands/refresh-style.ts
5575
5561
  import { promises as fs6 } from "fs";
5576
5562
  import path19 from "path";
5577
- import process6 from "process";
5563
+ import process7 from "process";
5578
5564
  var EXIT_OK5 = 0;
5579
5565
  var EXIT_NOT_INITIALIZED3 = 1;
5580
- async function refreshStyle(rootDir = process6.cwd()) {
5566
+ async function refreshStyle(rootDir = process7.cwd()) {
5581
5567
  const stateFile = path19.join(rootDir, ".mancode", "state.json");
5582
5568
  if (!await pathExists8(stateFile)) {
5583
5569
  console.error("\u2717 mancode not initialized.");
@@ -5728,7 +5714,7 @@ async function pathExists8(p) {
5728
5714
  import { spawn } from "child_process";
5729
5715
  import { promises as fs7 } from "fs";
5730
5716
  import path22 from "path";
5731
- import process7 from "process";
5717
+ import process8 from "process";
5732
5718
 
5733
5719
  // src/system/workflow.ts
5734
5720
  import {
@@ -6287,7 +6273,7 @@ var HOOK_ESTIMATE_TIMEOUT_MS = 2e3;
6287
6273
  var EXIT_OK6 = 0;
6288
6274
  var EXIT_NOT_INITIALIZED4 = 1;
6289
6275
  var EXIT_CORRUPT_STATE = 2;
6290
- async function status(rootDir = process7.cwd(), options = {}) {
6276
+ async function status(rootDir = process8.cwd(), options = {}) {
6291
6277
  const stateFile = path22.join(rootDir, ".mancode", "state.json");
6292
6278
  if (!await pathExists9(stateFile)) {
6293
6279
  console.error("\u2717 mancode not initialized.");
@@ -6426,9 +6412,9 @@ function getEffectiveTeamStatus(state, config, detected) {
6426
6412
  }
6427
6413
  async function checkHooks(rootDir) {
6428
6414
  const [sessionStart, userPromptSubmit, registered] = await Promise.all([
6429
- pathExists9(path22.join(rootDir, ".mancode", "hooks", "session-start.sh")),
6415
+ pathExists9(path22.join(rootDir, ".mancode", "hooks", "session-start.mjs")),
6430
6416
  pathExists9(
6431
- path22.join(rootDir, ".mancode", "hooks", "user-prompt-submit.sh")
6417
+ path22.join(rootDir, ".mancode", "hooks", "user-prompt-submit.mjs")
6432
6418
  ),
6433
6419
  isRegistered(rootDir)
6434
6420
  ]);
@@ -6450,9 +6436,9 @@ function hooksRegistered2(settings) {
6450
6436
  if (!isRecord8(settings)) return false;
6451
6437
  const hooks = settings.hooks;
6452
6438
  if (!isRecord8(hooks)) return false;
6453
- return hasHookCommand2(hooks.SessionStart, ".mancode/hooks/session-start.sh") && hasHookCommand2(
6439
+ return hasHookCommand2(hooks.SessionStart, ".mancode/hooks/session-start.mjs") && hasHookCommand2(
6454
6440
  hooks.UserPromptSubmit,
6455
- ".mancode/hooks/user-prompt-submit.sh"
6441
+ ".mancode/hooks/user-prompt-submit.mjs"
6456
6442
  );
6457
6443
  }
6458
6444
  function hasHookCommand2(value, needle) {
@@ -6470,7 +6456,7 @@ async function estimateHookInjection(rootDir) {
6470
6456
  rootDir,
6471
6457
  ".mancode",
6472
6458
  "hooks",
6473
- "user-prompt-submit.sh"
6459
+ "user-prompt-submit.mjs"
6474
6460
  );
6475
6461
  if (!await pathExists9(hookPath)) {
6476
6462
  return { tokens: 0, cap: 800 };
@@ -6487,7 +6473,7 @@ async function estimateHookInjection(rootDir) {
6487
6473
  }
6488
6474
  function runHookEstimate(rootDir, hookPath) {
6489
6475
  return new Promise((resolve, reject) => {
6490
- const child = spawn("bash", [hookPath], {
6476
+ const child = spawn(process8.execPath, [hookPath], {
6491
6477
  cwd: rootDir,
6492
6478
  stdio: ["pipe", "pipe", "ignore"]
6493
6479
  });
@@ -6571,9 +6557,9 @@ function printText(r) {
6571
6557
  console.log("");
6572
6558
  if (r.platforms.includes("claude-code")) {
6573
6559
  console.log("Hooks:");
6574
- console.log(` ${r.hooks.sessionStart ? "\u2713" : "\u2717"} session-start.sh`);
6560
+ console.log(` ${r.hooks.sessionStart ? "\u2713" : "\u2717"} session-start.mjs`);
6575
6561
  console.log(
6576
- ` ${r.hooks.userPromptSubmit ? "\u2713" : "\u2717"} user-prompt-submit.sh`
6562
+ ` ${r.hooks.userPromptSubmit ? "\u2713" : "\u2717"} user-prompt-submit.mjs`
6577
6563
  );
6578
6564
  console.log(
6579
6565
  ` ${r.hooks.registered ? "\u2713" : "\u2717"} registered in .claude/settings.json`
@@ -6605,13 +6591,13 @@ async function pathExists9(p) {
6605
6591
  }
6606
6592
 
6607
6593
  // src/commands/uninstall.ts
6608
- import { access as access4, readFile as readFile11, rm as rm5, writeFile as writeFile12 } from "fs/promises";
6594
+ import { access as access5, readFile as readFile11, rm as rm5, writeFile as writeFile12 } from "fs/promises";
6609
6595
  import path23 from "path";
6610
- import process8 from "process";
6596
+ import process9 from "process";
6611
6597
  var EXIT_OK7 = 0;
6612
6598
  var EXIT_NOT_INITIALIZED5 = 1;
6613
6599
  var EXIT_UNSUPPORTED_PLATFORM2 = 2;
6614
- async function uninstall(rootDir = process8.cwd(), platform, options = {}) {
6600
+ async function uninstall(rootDir = process9.cwd(), platform, options = {}) {
6615
6601
  const stateFile = path23.join(rootDir, ".mancode", "state.json");
6616
6602
  if (!await pathExists10(stateFile)) {
6617
6603
  console.error("\u2717 mancode not initialized.");
@@ -6834,7 +6820,7 @@ function isRecord9(value) {
6834
6820
  }
6835
6821
  async function pathExists10(p) {
6836
6822
  try {
6837
- await access4(p);
6823
+ await access5(p);
6838
6824
  return true;
6839
6825
  } catch {
6840
6826
  return false;
@@ -6842,17 +6828,17 @@ async function pathExists10(p) {
6842
6828
  }
6843
6829
 
6844
6830
  // src/commands/version.ts
6845
- import process9 from "process";
6831
+ import process10 from "process";
6846
6832
  function version() {
6847
- const nodeVersion = process9.version;
6848
- const platform = `${process9.platform}/${process9.arch}`;
6833
+ const nodeVersion = process10.version;
6834
+ const platform = `${process10.platform}/${process10.arch}`;
6849
6835
  console.log(`mancode/${VERSION}`);
6850
6836
  console.log(`node/${nodeVersion.replace("v", "")}`);
6851
6837
  console.log(platform);
6852
6838
  }
6853
6839
 
6854
6840
  // src/commands/workflow.ts
6855
- import { access as access5 } from "fs/promises";
6841
+ import { access as access6 } from "fs/promises";
6856
6842
  import path24 from "path";
6857
6843
  var EXIT_OK8 = 0;
6858
6844
  var EXIT_NOT_INITIALIZED6 = 1;
@@ -7339,7 +7325,7 @@ function ago(iso) {
7339
7325
  }
7340
7326
  async function pathExists11(p) {
7341
7327
  try {
7342
- await access5(p);
7328
+ await access6(p);
7343
7329
  return true;
7344
7330
  } catch {
7345
7331
  return false;