関連企業 Testlify

Appforest Timetracker - VS Code 拡張機能

Appforest Timesheets(Timesheets by Appforest)を補完するVS Code拡張機能。エディターでの作業を記録し、Gitブランチのコンテキストからチケットを特定してTimesheets by Appforestに工数を記録する。

私たちのJiraプラグイン、Timesheets by AppforestはTestlifyでの工数管理を一元化しましたが、それでも開発者は手を止めてツールを切り替え、工数を手入力する必要がありました。Appforest Timetrackerはそのギャップを埋めます。エディター内でコーディング活動を記録し、工数の記録をワンアクションに変えるVS Code拡張機能で、エンジニアはIDEを離れることなくTimesheetsに記録できます。コンセプトからVisual Studio Marketplace向けのパッケージングまで、私が一貫して担当しました。

作ったもの

  • 拡張機能のコア: 公式のVS Code Extension API上に構築しました。ドキュメントの変更・保存・選択のリスナーがアクティビティのタイムスタンプをリセットし、ウィンドウのフォーカス状態を1秒間隔でポーリングして照合することで、開発者を邪魔せずにアクティブ時間とアイドル時間を静かに判別します。
  • Gitを認識した工数記録: 拡張機能は現在のGitブランチを読み取り、そこからJiraのチケットIDを自動的に抽出します。これにより、記録した時間はチケットを手で探したり再入力したりすることなく、正しい課題に紐づきます。
  • ライフサイクル全体: VS CodeのSecretStorage APIによる安全なトークン保存、パッケージング、そしてVisual Studio Marketplaceへの手動公開まで、すべて担当しました。

なぜ意味があったか

Gitブランチのコンテキストからチケットの照合を自動化したことで、工数記録の手作業の工程がなくなり、報告ミスが減り、経営陣はスプリント計画とクライアント請求のためのよりきれいなデータを手にできました。

一人で、小さく、実物

私が唯一のコントリビューターで、2025年4月以降32コミットです。特定の日々の煩わしさを解消するために作った、個人規模の小さな社内ツールであり、大きなプロダクトではありません。

記録した時間は実際どこへ行くのか

この拡張機能はJiraのREST APIを直接呼び出しません。Timesheets by Appforest自身のバックエンド、つまりForgeアプリがマニフェストで宣言しているのと同じリモートサービスに投稿し、Jira側の処理はそちらが担います。この2つのプロジェクトは、それぞれ独立してJiraと話すのではなく、互いに通信するように作られています。Forgeアプリのリゾルバーに、VS Code由来の提案を記録後にクリアするための専用関数が含まれているのも、そのためです。

アクティビティとチケット検出の実際の仕組み

Gitブランチの検出は、gitライブラリを取り込むのではなくgit rev-parse --abbrev-ref HEADを直接シェルアウトして実行します。値をひとつ読み取るだけなのに依存関係のAPI全体を抱え込むよりも、これは妥当な判断です。ブランチ名は同期する前に/^[A-Z]+-\d+$/で検証されるため、チケットの形式(ABC-123のような)に一致しないフィーチャーブランチは、誤った課題に記録されたりエラーになったりせず、黙ってスキップされます。アクティビティ追跡はファイル切り替えの粒度ではなく、ドキュメントの変更・保存リスナーに加えてウィンドウのフォーカス状態を1秒間隔でポーリングするもので、10秒のアイドル閾値と、セッションが一時停止とみなされるまでの60秒の猶予期間を持ちます。進捗はVS CodeのglobalStateに毎秒保存され、再起動時に復元されます。復元時に最後に同期した件数が追跡中の件数を何らかの理由で上回っていた場合(書き込み途中で同期が中断された兆候)は、それ以上ずれが広がらないように値を範囲内に戻す自己修復チェックも備えています。

認証は設定ベースではなくコマンド駆動です。専用のコマンド(authenticateToken、deleteToken、updateToken、syncNow、resetTime)が入力プロンプトを通じてトークンのライフサイクルを扱います。そもそも設定UIが存在しないのはこのためで、これは機能の欠落ではなく意図的な選択です。

データモデルと規模

ここには、一人の開発者自身のエディターセッションを超えてスケールする必要のあるものは何もありません。ステータスバー表示(チケット時間と日次合計)、アイドル/アクティブの状態マシン、深夜0時の日次リセットジョブが1つずつあるだけです。この規模でより重いデータモデルを持つ根拠はなく、それを作ることはこのツールが抱えていない問題を解決することになります。

自作か既製品か

simple-gitやisomorphic-gitのようなライブラリではなくgitバイナリをシェルアウトして呼ぶ。値をひとつ(現在のブランチ)読み取るだけの用途に、Git操作全体のために作られた依存関係を持ち込む理由はありません。独自の暗号化ファイルによるトークンストアではなくVS Code自身のSecretStorage APIを使う。プラットフォームがまさにこのケース向けの安全な認証情報の保存をすでに解決しています。

Stack

VS Code Extension API、TypeScript、Git操作にはNode組み込みのchild_process、トークン保存にはVS CodeのSecretStorage API、Visual Studio Marketplaceで公開。