@tyhld/conductor 0.12.54 → 0.12.55

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/relay.js CHANGED
@@ -1094,8 +1094,8 @@ export function fullCompletionGuide(site, commandId) {
1094
1094
  `---\n` +
1095
1095
  `この囲みの下に、いつもどおりの報告本文を書いてください(本文が空だと未完成と見なされます)。\n` +
1096
1096
  `その後の最終行に必ず ${marker} とだけ出力してください。\n\n` +
1097
- // ★案D(relay非依存の確実な完了通知): 完了したら必ず devlog MCP の conductor_complete を呼ぶ。
1098
- `そして完了したら必ず、devlog の MCP ツール conductor_complete を commandId="${commandId}" で1回だけ呼んでください` +
1097
+ // ★案D(relay非依存の確実な完了通知): 完了したら必ず ai-kana MCP の conductor_complete を呼ぶ。
1098
+ `そして完了したら必ず、ai-kana の MCP ツール conductor_complete を commandId="${commandId}" で1回だけ呼んでください` +
1099
1099
  `(relayに依存しない確実な完了通知です。report 引数に上記の報告本文を添えても構いません)。\n\n` +
1100
1100
  // ★成果引数の案内(devlog-tracker PR #100): conductor_complete に任意の成果を添えられる。
1101
1101
  // すべて任意・分からなければ省略可=渡さなくても従来どおり完了する。既存の完了検出
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tyhld/conductor",
3
- "version": "0.12.54",
3
+ "version": "0.12.55",
4
4
  "description": "采配くん管制の見守りアプリ(conductor-agent)。各PCで常駐し、中央(devlog-tracker)へ定期的に生存報告(heartbeat)を送る常駐CLI。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -613,9 +613,13 @@ else
613
613
 
614
614
  doing "Node ${NODE_MAJOR_WANTED} を入れます"
615
615
  nvm install "$NODE_MAJOR_WANTED" || die "Node ${NODE_MAJOR_WANTED} を入れられませんでした"
616
+ # ★nvm alias default が lts/* のように解決できない値だと、nvm use default が
617
+ # 終了コード3で黙って落ちる(実測 2026-09-28 FshimaNP)。入れたばかりの版を
618
+ # 名指しで使ったうえで、既定もその版に上書きする。
619
+ nvm use "$NODE_MAJOR_WANTED" >/dev/null \
620
+ || die "Node ${NODE_MAJOR_WANTED} に切り替えられませんでした。nvm use ${NODE_MAJOR_WANTED} を手動でお試しください。"
616
621
  nvm alias default "$NODE_MAJOR_WANTED" >/dev/null \
617
622
  || die 'Node の既定の版を決められませんでした'
618
- nvm use default >/dev/null || die 'Node に切り替えられませんでした'
619
623
  say " 用意できました: $(node --version)"
620
624
  record "Node: 入れた($(node --version))"
621
625
  fi
@@ -896,12 +900,84 @@ CONSOLE_URL=''
896
900
  CONSOLE_TOKEN=''
897
901
 
898
902
  if [[ -f "$ENV_FILE" ]]; then
899
- skip '接続情報はあります'
900
903
  # 権限が緩んでいたら締める(常駐側も起動時に直すが、ここでも揃えておく)。
901
904
  if [[ "$DRY_RUN" != "1" ]]; then
902
905
  chmod 600 "$ENV_FILE" 2>/dev/null || true
903
906
  fi
904
- record '接続情報: 済み'
907
+ # ★合言葉が管制に通るかを確かめる(実測 2026-09-28: 古い合言葉が 401 なのに
908
+ # 「接続情報はある」で飛ばされ、あとで「指示が届かない」だけが起きた)。
909
+ # 通らなければ入れ直しを聞く。確かめられないとき(ネット不調)は従来どおりスキップ。
910
+ _ENV_URL=''; _ENV_TOKEN=''
911
+ if [[ "$DRY_RUN" != "1" ]]; then
912
+ while IFS='=' read -r k v; do
913
+ case "$k" in
914
+ CONDUCTOR_URL) _ENV_URL="$v" ;;
915
+ CONDUCTOR_TOKEN) _ENV_TOKEN="$v" ;;
916
+ esac
917
+ done < "$ENV_FILE"
918
+ fi
919
+ if [[ -n "$_ENV_URL" && -n "$_ENV_TOKEN" ]]; then
920
+ _check="$(CONDUCTOR_URL="$_ENV_URL" CONDUCTOR_TOKEN="$_ENV_TOKEN" python3 <<'PYEOF' 2>/dev/null || echo unreachable
921
+ import os, urllib.error, urllib.request
922
+ PING_ID = '00000000-0000-4000-8000-000000000000'
923
+ class _NoRedirect(urllib.request.HTTPRedirectHandler):
924
+ def redirect_request(self, *a, **k):
925
+ return None
926
+ url = (os.environ.get('CONDUCTOR_URL') or '').rstrip('/')
927
+ token = os.environ.get('CONDUCTOR_TOKEN') or ''
928
+ req = urllib.request.Request(
929
+ f'{url}/api/conductor/hook-requests/{PING_ID}',
930
+ method='GET', headers={'Authorization': f'Bearer {token}'})
931
+ try:
932
+ with urllib.request.build_opener(_NoRedirect).open(req, timeout=10) as res:
933
+ code = getattr(res, 'status', 0) or 0
934
+ print('ok' if 200 <= code < 300 else 'bad-url')
935
+ except urllib.error.HTTPError as e:
936
+ if e.code == 404 or 200 <= e.code < 300:
937
+ print('ok')
938
+ elif e.code in (401, 403):
939
+ print('bad-token')
940
+ elif 300 <= e.code < 400:
941
+ print('bad-url')
942
+ else:
943
+ print('unreachable')
944
+ except Exception:
945
+ print('unreachable')
946
+ PYEOF
947
+ )"
948
+ unset _ENV_TOKEN
949
+ case "$_check" in
950
+ ok)
951
+ skip '接続情報はあります(管制への接続も確認しました)'
952
+ record '接続情報: 済み(接続確認OK)'
953
+ ;;
954
+ bad-token)
955
+ say ''
956
+ say ' ★いまの合言葉では管制に拒否されました(401)。'
957
+ say ' 合言葉が変わった可能性があります。入れ直しますか?'
958
+ reenter=''
959
+ ask_tty reenter ' 入れ直す (y/N)' ''
960
+ if [[ "$(lower "$reenter")" == "y" ]]; then
961
+ rm -f "$ENV_FILE"
962
+ say ' 古い接続情報を消しました。もう一度この1行を打ってください。'
963
+ say ' (続きから進みます)'
964
+ exit 1
965
+ else
966
+ note '★入れ直さずに進みます。指示が届かないときは rm ~/.conductor.env のあと、もう一度この1行を打ってください'
967
+ skip '接続情報はあります(★合言葉が古い可能性があります)'
968
+ record '接続情報: 済み(★合言葉が古い可能性あり)'
969
+ fi
970
+ ;;
971
+ *)
972
+ # ★ネット不調は止めない。従来どおりスキップ。
973
+ skip '接続情報はあります'
974
+ record '接続情報: 済み'
975
+ ;;
976
+ esac
977
+ else
978
+ skip '接続情報はあります'
979
+ record '接続情報: 済み'
980
+ fi
905
981
  note '★入れ直したいときは rm ~/.conductor.env のあと、もう一度この1行を打ってください'
906
982
  elif [[ "$DRY_RUN" == "1" ]]; then
907
983
  plan '管制のアドレスと合言葉をお聞きして、接続情報を作ります'
@@ -1040,6 +1116,35 @@ else
1040
1116
  record '設置: 実行した'
1041
1117
  fi
1042
1118
 
1119
+ # ═════════════════════════════════════════════════════════════════════════════
1120
+ # ⑬½ 画面なし方式(headless)を既定にする
1121
+ #
1122
+ # ★新しいPCは画面なし方式で設置する(えふさん確定)。既存のPCは切り替えない
1123
+ # (プログラムの既定は tmux のまま)。ここは install.sh(新規設置の入口)だけが
1124
+ # 通るので、既存PCには影響しない。
1125
+ # ★systemd の drop-in で環境変数を渡す。ユニット本体は変えない。
1126
+ # ═════════════════════════════════════════════════════════════════════════════
1127
+
1128
+ UNIT_DIR_INST="$HOME/.config/systemd/user"
1129
+ HEADLESS_DROPIN_DIR="$UNIT_DIR_INST/conductor@${SITE}.service.d"
1130
+ HEADLESS_DROPIN="$HEADLESS_DROPIN_DIR/headless.conf"
1131
+
1132
+ if [[ "$DRY_RUN" == "1" ]]; then
1133
+ plan '画面なし方式(headless)を設定します'
1134
+ else
1135
+ mkdir -p "$HEADLESS_DROPIN_DIR"
1136
+ cat > "$HEADLESS_DROPIN" <<'DROPIN'
1137
+ # install.sh が設定: 画面なし方式(headless)で動かす。
1138
+ # ★tmux に切り替えたいときはこのファイルを消して systemctl --user daemon-reload。
1139
+ [Service]
1140
+ Environment=CONDUCTOR_RUNNER_MODE=headless
1141
+ DROPIN
1142
+ systemctl --user daemon-reload 2>/dev/null || true
1143
+ systemctl --user restart "conductor@${SITE}.service" 2>/dev/null || true
1144
+ say " 画面なし方式を設定しました(drop-in: ${HEADLESS_DROPIN})"
1145
+ record '方式: 画面なし(headless)'
1146
+ fi
1147
+
1043
1148
  # ═════════════════════════════════════════════════════════════════════════════
1044
1149
  # ⑭ 締め(★自己点検の○×は、すぐ上に設置係が出しています)
1045
1150
  # ═════════════════════════════════════════════════════════════════════════════
@@ -1073,16 +1178,11 @@ ${BAR}
1073
1178
  ✗ が残っているときは、その行に書いてある直し方をお試しください。
1074
1179
 
1075
1180
  ${BAR}
1076
- 次にやること — 職人(Claude Code)を動かす
1181
+ これで準備は終わりです
1077
1182
  ${BAR}
1078
1183
 
1079
- 下の2行を、そのままコピーして貼ってください。
1080
-
1081
- source ~/.bashrc
1082
- kc ${SITE}
1083
-
1084
- (1行目は、いま用意した合言葉をこの画面に読み込みます。
1085
- 2行目で職人が開きます。★次からは「kc ${SITE}」の1行だけで開けます)
1184
+ 管制から便を出すと自動で動きます。
1185
+ 職人の画面を手で開く必要はありません。
1086
1186
 
1087
1187
  ${BAR}
1088
1188
  いちばん確実な「動いています」の確かめ方
@@ -1091,7 +1191,13 @@ ${BAR}
1091
1191
  管制の画面(采配くん)をブラウザで開いてください。
1092
1192
  そこに「${SITE}」が出ていれば、つながって動いています。
1093
1193
 
1094
- 出てこないときは、職人の画面を開いたまま1〜2分待って、開き直してください。
1194
+ 出てこないときは1〜2分待って、開き直してください。
1095
1195
  それでも出ないときは、この画面をそのままサポートへお送りください。
1096
1196
 
1197
+ ${BAR}
1198
+ 職人の画面を手で開きたいとき(tmux 方式・補助)
1199
+ ${BAR}
1200
+
1201
+ source ~/.bashrc && kc ${SITE}
1202
+
1097
1203
  GUIDE
@@ -108,6 +108,31 @@ MIN_CONDUCTOR_VERSION="0.12.3"
108
108
  fail() { echo; echo "[setup] エラー: $1" >&2; exit 1; }
109
109
  warn() { echo "[setup] warn: $1" >&2; }
110
110
 
111
+ # 入力元。curl | bash では /dev/tty から読む。テスト等で端末が無い場合は標準入力に落ちる。
112
+ # install.sh の CONDUCTOR_INSTALL_TTY と同じ差し替え口を使う。
113
+ _SETUP_TTY="${CONDUCTOR_INSTALL_TTY:-/dev/tty}"
114
+ # ★/dev/tty は「端末が無いプロセス」でもファイルとしては在る(-r が真を返す)。
115
+ # 実際に開けるかで判定する。開けなければ標準入力に落ちる。
116
+ _SETUP_TTY_OK=0
117
+ if exec 8<"$_SETUP_TTY" 2>/dev/null; then
118
+ exec 8<&-
119
+ _SETUP_TTY_OK=1
120
+ fi
121
+
122
+ # setup_read — /dev/tty があれば使い、無ければ標準入力から読む。
123
+ # 使い方: setup_read [-s] <変数名> <プロンプト>
124
+ setup_read() {
125
+ local silent='' var prompt
126
+ if [[ "${1:-}" == "-s" ]]; then silent='-s'; shift; fi
127
+ var="$1"; prompt="$2"
128
+ # shellcheck disable=SC2229 # $silent は動的な旗、$var は動的な変数名。意図どおり。
129
+ if [[ "$_SETUP_TTY_OK" == "1" ]]; then
130
+ IFS= read -r $silent -p "$prompt" "$var" <"$_SETUP_TTY" || printf -v "$var" ''
131
+ else
132
+ IFS= read -r $silent -p "$prompt" "$var" || printf -v "$var" ''
133
+ fi
134
+ }
135
+
111
136
  # version_lt <a> <b> — a が b より古いか。
112
137
  # ★`sort -V` に頼らない(Mac の sort には無い版がある=ADR-019 で Mac も対象)。
113
138
  # 数字3つ(major.minor.patch)だけを見る。読めない字は 0 とみなす(安全側=古い扱い)。
@@ -512,12 +537,12 @@ else
512
537
  echo "[setup] ★この2つは弊社からお渡しした値です。手元の案内をご覧ください。"
513
538
  echo
514
539
 
515
- read -r -p " ① 管制のアドレス(CONDUCTOR_URL・例: https://…): " input_url
540
+ setup_read input_url " ① 管制のアドレス(CONDUCTOR_URL・例: https://…): "
516
541
  [[ -z "$input_url" ]] && fail "管制のアドレスが空です。"
517
542
 
518
543
  # ★合言葉は画面に出さない(-s)。画面共有・録画に残さないため。
519
544
  # 何も出ないと固まったと思われるので、受け取ったことを必ず1行返す。
520
- read -r -s -p " ② 合言葉(CONDUCTOR_TOKEN・入力しても画面には出ません): " input_secret
545
+ setup_read -s input_secret " ② 合言葉(CONDUCTOR_TOKEN・入力しても画面には出ません): "
521
546
  echo
522
547
  [[ -z "$input_secret" ]] && fail "合言葉が空です。"
523
548
  echo "[setup] 受け取りました(${#input_secret} 文字)。画面には表示していません。"
@@ -821,7 +846,8 @@ fi
821
846
  if [[ -f "$SETTINGS_SRC" ]]; then
822
847
  if [[ -f "$SETTINGS_DST" ]]; then
823
848
  echo "[setup] $SETTINGS_DST は既に存在します。上書きしますか?"
824
- read -r -p " 上書き (y/N): " overwrite
849
+ setup_read overwrite " 上書き (y/N): "
850
+ # shellcheck disable=SC2154 # overwrite は setup_read で間接代入される
825
851
  if [[ "$(lower "$overwrite")" != "y" ]]; then
826
852
  echo "[setup] 現場の settings.json をスキップしました。"
827
853
  else
@@ -843,9 +869,10 @@ if [[ -f "$SETTINGS_SRC" ]]; then
843
869
  # Supabase 設定
844
870
  if [[ -f "$SETTINGS_DST" ]] && grep -q '<SUPABASE_PROJECT_ID>' "$SETTINGS_DST"; then
845
871
  echo
846
- read -r -p "[setup] Supabase を使いますか? (y/N): " use_supabase
872
+ setup_read use_supabase "[setup] Supabase を使いますか? (y/N): "
873
+ # shellcheck disable=SC2154 # use_supabase は setup_read で間接代入される
847
874
  if [[ "$(lower "$use_supabase")" == "y" ]]; then
848
- read -r -p " Supabase Project Reference ID: " supabase_id
875
+ setup_read supabase_id " Supabase Project Reference ID: "
849
876
  if [[ -n "$supabase_id" ]]; then
850
877
  sed_replace "s|<SUPABASE_PROJECT_ID>|${supabase_id}|g" "$SETTINGS_DST"
851
878
  echo "[setup] <SUPABASE_PROJECT_ID> → $supabase_id に置換"
@@ -885,7 +912,8 @@ if [[ "$PROFILE" == "proto" ]]; then
885
912
  write_env_example=1
886
913
  if [[ -f "$ENV_EXAMPLE_DST" ]]; then
887
914
  echo "[setup] $ENV_EXAMPLE_DST は既に存在します。上書きしますか?"
888
- read -r -p " 上書き (y/N): " overwrite_env
915
+ setup_read overwrite_env " 上書き (y/N): "
916
+ # shellcheck disable=SC2154 # overwrite_env は setup_read で間接代入される
889
917
  [[ "$(lower "$overwrite_env")" == "y" ]] || write_env_example=0
890
918
  fi
891
919
  if [[ "$write_env_example" == "1" ]]; then
@@ -156,7 +156,7 @@ description: 采配くんの便(プロンプト末尾に CONDUCTOR_JOB:<id>
156
156
  ```
157
157
  この囲みの下に、いつもどおりの報告本文を書く(本文が空だと未完成と見なされる)。
158
158
  ③ **その後の最終行に、配達役の1行にある `DONE:<現場>` とだけ出力する**(完了の合図)。
159
- ④ **devlog の MCP ツール `conductor_complete` を、配達役の1行にある commandId で1回だけ呼ぶ**(relay に依存しない確実な完了通知)。report 引数に上の報告本文を添えてもよい。
159
+ ④ **ai-kana の MCP ツール `conductor_complete` を、配達役の1行にある commandId で1回だけ呼ぶ**(relay に依存しない確実な完了通知)。report 引数に上の報告本文を添えてもよい。
160
160
  - **nextWork(次の独立作業候補)は必ず書く。** 無ければ「なし」と、なぜ無いかの理由を書く。
161
161
  - 可能なら次も一緒に渡す(すべて任意・分かる範囲でよく、渡さなくても完了する):`branch=`作った作業ブランチ名(`git rev-parse --abbrev-ref HEAD`)、`baseBranch=`分岐元(通常 main)、`headSha=`最後のコミット(短縮可)、`pushed=`push したか(true/false)、`filesChanged=`変更したファイル数、`prTitle=`お客さま(非エンジニア)がそのまま読む題名(rebase/squash/lint 等の専門用語を避け、やさしい日本語で)、`prNumber=`既に PR がある場合のみ、`outcome=`pushed(変更して push した)/no_change(変更不要だった)/blocked(衝突・要判断・許可待ち等で完了できなかった)のどれか。
162
162
  - **blocked のときも必ず conductor_complete を呼び、report に理由を書く。**