AI Agent:tools:ProgressTracker.py:ソースコード

import uuid
from typing import List, Optional, Dict
from datetime import datetime
import json
import os





import tools.program_called_command_list
WORK_SPACE_DIR = tools.program_called_command_list.WORK_SPACE_DIR

from tools.file_list import _create_folder
from tools import file_list
from tools.program_called_command_list import wait_safety

CURRENT_DIR= os.path.join(WORK_SPACE_DIR,"data/ProgressTracker")


# ===============================
# ProgressNode クラス
# ===============================
# 個々のタスクやサブタスクを表現するノード。
# 階層構造を持ち、進捗状態・スケジュール・並列性・IDを管理する。
class ProgressNode:
    def __init__(
        self,
        name: str,                         # タスク名(例:「プロンプト設計」)
        description: str = "",             # タスクの説明(任意)
        status: str = "未着手",            # ステータス(未着手 / 進行中 / 完了 / 保留 / 中断)
        start_time: Optional[datetime] = None,  # タスクの開始時刻(15分単位で管理可能)
        end_time: Optional[datetime] = None,    # タスクの終了時刻
        can_run_parallel: bool = False     # 並列実行可能かどうか(Trueなら他タスクと同時進行可能)
    ):
        self.id = str(uuid.uuid4())  # 各ノードに一意なIDを自動生成(UUID形式)
        self.name = name
        self.description = description
        self.status = status
        self.start_time = start_time
        self.end_time = end_time
        self.can_run_parallel = can_run_parallel
        self.note = ""  # 追加のメモ書き用フィールド
        self.children: List["ProgressNode"] = []  # 子ノード(サブタスク)を格納するリスト

        self.work_parent_ids: List[str] = []  # タスクの親ノードのIDを格納するリスト(ルートノードの場合は空)親ノードのタスクが終わっていないと開始できない
        self.menbers : List[str] = []  # このタスクに割り当てられたメンバーのリスト

    # 子ノード(サブタスク)を追加
    def add_child(self, child: "ProgressNode"):
        self.children.append(child)

    # ステータスを更新(例:「進行中」→「完了」)
    def update_status(self, status: str, note: str = ""):
        if None is self.status:
            self.start_time = datetime.now()
        if status == "完了":
            self.end_time =  datetime.now()
        self.status = status
        self.note = note
    # このノードが開始可能かどうかを判定
    def is_ready_to_start(self) -> bool:
        if not self.children:
            return True  # 子がなければすぐに開始可能
        if self.can_run_parallel:
            return True  # 並列実行可能なら子の完了を待たずに開始可能
        return all(child.status == "完了" for child in self.children)  # 子がすべて完了していればOK

    # ノード情報を辞書形式で出力(JSON化や保存用)
    def to_dict(self) -> Dict:
        return {
            "id": self.id,
            "name": self.name,
            "description": self.description,
            "status": self.status,
            "start_time": self.start_time.isoformat() if self.start_time else None,
            "end_time": self.end_time.isoformat() if self.end_time else None,
            "can_run_parallel": self.can_run_parallel,
            "note": self.note,
            "children": [child.to_dict() for child in self.children],
            "work_parent_ids": self.work_parent_ids,
            "menbers": self.menbers,
           
        }

    # Mermaidガントチャート用の行を再帰的に生成
    def get_mermaid_lines(self) -> List[str]:
        lines = []
        if self.start_time and self.end_time:
            # Mermaid形式: タスク名 :ステータス, 開始時刻, 終了時刻
            line = f'{self.name} :{self.status}, {self.start_time.isoformat()}, {self.end_time.isoformat()}'
            lines.append(line)
        for child in self.children:
            lines.extend(child.get_mermaid_lines())
        return lines

    # IDを指定してノードを再帰的に検索
    def find_by_id(self, target_id: str) -> Optional["ProgressNode"]:
        if self.id == target_id:
            return self
        for child in self.children:
            result = child.find_by_id(target_id)
            if result:
                return result
        return None

    # 親ノードIDの管理メソッド
    def get_parent_ids(self) -> List[str]:
        return self.work_parent_ids
    
    def add_parent_id(self, parent_id: str):
        if parent_id not in self.work_parent_ids:
            self.work_parent_ids.append(parent_id)
    
    def remove_parent_id(self, parent_id: str):
        if parent_id in self.work_parent_ids:
            self.work_parent_ids.remove(parent_id)

    # メンバー割り当ての管理メソッド
    def assign_member(self, member_name: str):
        if member_name not in self.menbers:
            self.menbers.append(member_name)
    
    def unassign_member(self, member_name: str):
        if member_name in self.menbers:
            self.menbers.remove(member_name)
    
    def get_members(self) -> List[str]:
        return self.menbers
    
# ===============================
# ProgressTracker クラス
# ===============================
# プロジェクト全体を管理するクラス。
# ルートノードを持ち、ID検索・ステータス更新・可視化出力などを提供。
class ProgressTracker:
    def __init__(self, title: str, purpose_of_creation: str = ""):
        self.title = title  # プロジェクト名(ガントチャートのタイトルなどに使用)
        self.purpose_of_creation = purpose_of_creation
        self.root: Optional[ProgressNode] = None  # ルートノード(プロジェクト全体の起点)

    # プロジェクトのルートノードを設定
    def set_root(self, node: ProgressNode):
        self.root = node

    def get_root(self) -> Optional[ProgressNode]:
        return self.root
    # プロジェクト全体をJSON形式で出力(AIや外部連携用)
    def to_json(self) -> Dict:
        return {
            "title": self.title,
            "root": self.root.to_dict() if self.root else {}
        }

    # Mermaid記法でガントチャートを出力(人間向け可視化)
    def to_mermaid(self) -> str:
        if not self.root:
            return "gantt\n    title 未設定\n"
        lines = [
            "gantt",
            f"    title {self.title}",
            "    dateFormat  YYYY-MM-DDTHH:mm:ss",  # ISO形式で15分単位の時間管理を可能に
            "    axisFormat  %H:%M"  # 時刻表示を「時:分」に設定
        ]
        lines.extend(["    " + line for line in self.root.get_mermaid_lines()])
        return "\n".join(lines)

    # ツリー構造をコンソールに表示(デバッグや構造確認用)
    def print_tree(self):
        def _print(node: ProgressNode, indent: int = 0):
            print("  " * indent + f"- {node.name} [{node.status}] (ID: {node.id})")
            for child in node.children:
                _print(child, indent + 1)
        if self.root:
            _print(self.root)

    # IDを指定してノードを検索(ルート以下を再帰的に探索)
    def find_node_by_id(self, node_id: str) -> Optional[ProgressNode]:
        if not self.root:
            return None
        return self.root.find_by_id(node_id)

    # IDを指定してステータスを更新(成功すればTrue、失敗すればFalse)
    def update_status_by_id(self, node_id: str, new_status: str, note: str="") -> bool:
        node = self.find_node_by_id(node_id)
        if node:
            node.update_status(new_status, note)
            return True
        return False
    def updat_note_by_id(self, node_id: str, note: str) -> bool:
        node = self.find_node_by_id(node_id)
        if node:
            node.note = note
            return True
        return False
    # 親ノードを指定してタスクを追加(成功すればTrue、失敗すればFalse)
    def add_task(self, parent_id: str, task_node: ProgressNode) -> bool:
        parent_node = self.find_node_by_id(parent_id)
        if parent_node:
            parent_node.add_child(task_node)
            return True
        return False
    
    # ノードをIDで削除(成功すればTrue、失敗すればFalse)
    def remove_task(self, node_id: str) -> bool:
        # ノードを削除するためのヘルパー関数
        def _remove(node: ProgressNode, target_id: str) -> bool:
            for i, child in enumerate(node.children):
                if child.id == target_id:
                    del node.children[i]
                    return True
                if _remove(child, target_id):
                    return True
            return False

        if not self.root:
            return False
        if self.root.id == node_id:
            self.root = None  # ルートノード自体を削除
            return True
        return _remove(self.root, node_id)
    
    # 進捗データをJSONファイルに保存
    def save_to_file(self, file_dir: str):
        file_path = os.path.join(file_dir,self.title + "_progress.json")
        with open(file_path, "w", encoding="utf-8") as f:
            json.dump(self.to_json(), f, indent=4, ensure_ascii=False)

    # JSONファイルから進捗データを読み込み
    def load_from_file(self, file_dir: str, title: str = ""):
        if title:
            self.title = title
        file_path = os.path.join(file_dir,self.title + "_progress.json")
        
        def _dict_to_node(data: Dict) -> ProgressNode:
            node = ProgressNode(
                name=data["name"],
                description=data.get("description", ""),
                status=data.get("status", "未着手"),
                start_time=datetime.fromisoformat(data["start_time"]) if data.get("start_time") else None,
                end_time=datetime.fromisoformat(data["end_time"]) if data.get("end_time") else None,
                can_run_parallel=data.get("can_run_parallel", False)
            )
            node.id = data["id"]
            node.note = data.get("note", "")
            node.work_parent_ids = data.get("work_parent_ids", [])
            node.menbers = data.get("menbers", [])
            for child_data in data.get("children", []):
                child_node = _dict_to_node(child_data)
                node.add_child(child_node)
            return node

        with open(file_path, "r", encoding="utf-8") as f:
            data = json.load(f)
            self.title = data.get("title", "未設定")
            if "root" in data and data["root"]:
                self.root = _dict_to_node(data["root"])
            else:
                self.root = None
    # 親ノードIDの管理メソッド
    def get_parent_ids_of_task(self, node_id: str) -> Optional[List[str]]:
        node = self.find_node_by_id(node_id)
        if node:
            return node.get_parent_ids()
        return None
    
    def add_parent_id_to_task(self, node_id: str, parent_id: str) -> bool:
        node = self.find_node_by_id(node_id)
        if node:
            node.add_parent_id(parent_id)
            return True
        return False
    def remove_parent_id_from_task(self, node_id: str, parent_id: str) -> bool:
        node = self.find_node_by_id(node_id)
        if node:
            node.remove_parent_id(parent_id)
            return True
        return False
    
    # メンバー割り当ての管理メソッド
    def assign_member_to_task(self, node_id: str, member_name: str) -> bool:
        node = self.find_node_by_id(node_id)
        if node:
            node.assign_member(member_name)
            return True
        return False
    
    def unassign_member_from_task(self, node_id: str, member_name: str) -> bool:
        node = self.find_node_by_id(node_id)
        if node:
            node.unassign_member(member_name)
            return True
        return False
    def get_members_of_task(self, node_id: str) -> Optional[List[str]]:
        node = self.find_node_by_id(node_id)
        if node:
            return node.get_members()
        return None
    
    
#==============================
# グローバル変数
#==============================    
from langchain_core.tools import tool
from pydantic import BaseModel, Field

g_progress_tracker = None

class ProgressTrackerInput(BaseModel):
    title: str = Field(..., description="進捗管理のタイトル")
    purpose_of_creation: str = Field("", description="プロジェクト作成の目的")
@tool(args_schema = ProgressTrackerInput)
def create_progress_tracker(title: str, purpose_of_creation: str) -> None:
    """
    プロジェクトの進捗管理トラッカーを作成します。
    進捗管理トラッカーが各進捗管理ノードを管理します。
    ノード編集の前に実行してください。
    Args:
        :param title: 進捗管理のタイトル
        :type title: str
        :param purpose_of_creation: プロジェクト作成の目的
        :type purpose_of_creation: str
    """
    global g_progress_tracker
    wait_safety()
    g_progress_tracker = ProgressTracker(title, purpose_of_creation)

    # ファイルリストのパスの設定
    file_path = os.path.join(CURRENT_DIR, "progress_file_list.json")
    file_list.set_data_file_path(file_path)
    if not os.path.exists(CURRENT_DIR):
        _create_folder(CURRENT_DIR)
    if not os.path.exists(file_path):
        file_list.list_files_recursive(CURRENT_DIR)
        
    file_list._set_file_description(file_path, purpose_of_creation, "進捗管理データファイル")
    file_list._save_file_descriptions()

class SetProgressRootInput(BaseModel):
    name: str = Field(..., description="ルートノードの名前")
    description: str = Field("", description="ルートノードの説明")
    status: str = Field("未着手", description="ルートノードのステータス")
    start_time: Optional[datetime] = Field(None, description="ルートノードの開始時刻")

@tool(args_schema = SetProgressRootInput)    
def set_progress_root(name: str, description: str = "", status: str = "未着手",
                       start_time: Optional[datetime] = None) -> str:
    """
    プロジェクトの進捗管理のルートノードを作成し、設定します。
    Docstring for set_progress_root
    Args:
        :param name: ルードノードの名前
        :type name: str
        :param description: ルートノードの説明
        :type description: str
        :param status: ルートノードのステータス
        :type status: str
        :param start_time: ルートノードの開始時間
        :type start_time: Optional[datetime]
    Return: 作成されたルートノードのID
        :rtype: str
    """
    # ファイルリストのパスの設定
    #
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    root_node = ProgressNode(name=name, description=description, status=status, start_time=start_time)
    g_progress_tracker.set_root(root_node)
    return root_node.id


class AddProgressTaskInput(BaseModel):
    parent_id: str = Field(..., description="親タスクノードのID")
    name: str = Field(..., description="新たに追加するタスクノードの名前")
    description: str = Field("", description=" 新たに追加するタスクの内容")
    can_run_parallel: bool = Field(False, description="並列実行可能かどうか")
@tool(args_schema = AddProgressTaskInput)
def add_progress_task(parent_id: str, name: str, description: str = "", can_run_parallel: bool = False) -> str:
    """
    プロジェクトの進捗管理の親ノードを指定してタスクを追加(追加されたタスクのIDを返す)
    Args:
        :param parent_id: 親となるタスクノードのID
        :type parent_id: str
        :param name: 新たに追加するタスクノードの名前
        :type name: str
        :param description: 新たに追加するタスクの内容, defaults to ""
        :type description: str, optional
        :param can_run_parallel: 並列事項可能かどうか, defaults to False
        :type can_run_parallel: bool, optional
    Return: 
        追加されたタスクのID
        
        :rtype: str
    """
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    print("add_progress_task:", parent_id, name, description, can_run_parallel)
    new_task = ProgressNode(name=name, description=description, can_run_parallel=can_run_parallel)
    success = g_progress_tracker.add_task(parent_id, new_task)
    if not success:
        raise ValueError(f"Parent node with ID {parent_id} not found.")
    return new_task.id

class UpdateProgressStatusInput(BaseModel):
    node_id: str = Field(..., description="更新するタスクノードのID")
    new_status: str = Field(..., description="新しいステータス")
    note: str = Field(..., description="新しいメモ書き")
@tool(args_schema = UpdateProgressStatusInput)
def update_progress_status(node_id: str, new_status: str, note: str) -> bool:
    """
    プロジェクトの進捗管理のタスクノードのIDを指定してステータスを更新(成功すればTrue、失敗すればFalse)
    ステータス:未着手、作業中、完了など
    Args:
        :param node_id: 更新するタスクノードのID
        :type node_id: str
        :param new_status: タスクノードの新しいステータス
        :type new_status: str
    Return:
         成功すればTrue、失敗すればFalse
        :rtype: bool
    """
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    return g_progress_tracker.update_status_by_id(node_id, new_status, note)

class UpdateProgressNoteInput(BaseModel):
    node_id: str = Field(..., description="タスクノードのID")
    note: str = Field(..., description="新しいメモ書き")
@tool(args_schema = UpdateProgressNoteInput)
def update_progress_note(node_id: str, note: str) -> bool:
    """
    プロジェクトの進捗管理のタスクノードのIDを指定してメモ書きを更新(成功すればTrue、失敗すればFalse)
    Args:
        :param node_id: タスクノードのID
        :type node_id: str
        :param note: メモ書き
        :type note: str
    Return:
         成功すればTrue、失敗すればFalse

        :rtype: bool
    """
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    return g_progress_tracker.updat_note_by_id(node_id, note)
    

class RemoveProgressTaskInput(BaseModel):
    node_id: str = Field(..., description="削除するノードのID")
@tool(args_schema = RemoveProgressTaskInput)
def remove_progress_task(node_id: str) -> bool:
    """
    プロジェクトの進捗管理のノードをIDで削除(成功すればTrue、失敗すればFalse)
    Args:
        :param node_id: Description
        :type node_id: str
    Return: 
        成功すればTrue、失敗すればFalse
        :rtype: bool
    """
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    return g_progress_tracker.remove_task(node_id)

@tool
def get_progress_mermaid() -> str:
    """
    プロジェクトの進捗管理のMermaid記法でガントチャートを出力(人間向け可視化)

    """
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    return g_progress_tracker.to_mermaid()

@tool
def get_progress_json() -> Dict:
    """
    プロジェクトの進捗管理全体をJSON形式で出力(AIや外部連携用)

    """
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    return g_progress_tracker.to_json()



def _save_progress_to_file() -> None:
    """
    プロジェクトの進捗管理の進捗データをJSONファイルに保存
  

    """
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    file_dir= CURRENT_DIR
    _create_folder(file_dir)
    g_progress_tracker.save_to_file(file_dir)

@tool
def save_progress_to_file() -> None:
    """
    プロジェクトの進捗管理の進捗データをJSONファイルに保存

    """
    wait_safety()    
    _save_progress_to_file()

def _load_progress_from_file(title: str = "") -> None:
    """
    JSONファイルからプロジェクトの進捗管理の進捗データを読み込み

    """   
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    file_dir=CURRENT_DIR
    _create_folder(file_dir)
    
    g_progress_tracker.load_from_file(file_dir, title=g_progress_tracker.title)

class LoadProgressFromFileInput(BaseModel):
    title: str = Field("", description="進捗管理のタイトル")
@tool(args_schema = LoadProgressFromFileInput)
def load_progress_from_file(title: str = "") -> None:
    """
    JSONファイルからプロジェクトの進捗管理の進捗データを読み込み
    ノード編集の前に実行してください。
    Args:
        :param title: 進捗管理のタイトル
        :type title: str
    """   
    wait_safety()    
    _load_progress_from_file(title=title)



class SetProgressNodeWorkParentsInput(BaseModel):
    node_id: str = Field(..., description="ノードのID")
    parent_ids: List[str] = Field(..., description="親ノードのIDリスト")
@tool(args_schema = SetProgressNodeWorkParentsInput)
def set_progress_node_work_parents(node_id: str, parent_ids: List[str]) -> bool:
    """
    プロジェクトの進捗管理のノードに親ノードIDリストを設定します。
    Args:
        :param node_id: ノードのID
        :type node_id: str
        :param parent_ids: 親ノードのIDリスト
        :type parent_ids: List[str]
    Return:
       成功すればTrue、失敗すればFalse
       :rtype: bool
    """
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    node = g_progress_tracker.find_node_by_id(node_id)
    if node:
        node.work_parent_ids = parent_ids
        return True
    return False

class GetProgressNodeWorkParentsInput(BaseModel):
    node_id: str = Field(..., description="ノードのID")
@tool(args_schema = GetProgressNodeWorkParentsInput)
def get_progress_node_work_parents(node_id: str) -> Optional[List[str]]:
    """
    プロジェクトの進捗管理のノードの親ノードIDリストを取得します。
    Args:
        :param node_id: ノードのID
        :type node_id: str
    Return:
        親ノードのIDリスト、ノードが見つからなければNone
        :rtype: Optional[List[str]]
    """
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    node = g_progress_tracker.find_node_by_id(node_id)
    if node:
        return node.work_parent_ids
    return None

class AddProgressNodeWorkParentInput(BaseModel):
    node_id: str = Field(..., description="ノードのID")
    parent_id: str = Field(..., description="親ノードのID")
@tool(args_schema = AddProgressNodeWorkParentInput)
def add_progress_node_work_parent(node_id: str, parent_id: str) -> bool:
    """
    プロジェクトの進捗管理のノードに親ノードIDを追加します。
    Args:
        :param node_id: ノードのID
        :type node_id: str
        :param parent_id: 親ノードのID
        :type parent_id: str
    Return:
         成功すればTrue、失敗すればFalse
        :rtype: bool
    """
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    node = g_progress_tracker.find_node_by_id(node_id)
    if node:
        node.add_parent_id(parent_id)
        return True
    return False
class RemoveProgressNodeWorkParentInput(BaseModel):
    node_id: str = Field(..., description="ノードのID")
    parent_id: str = Field(..., description="親ノードのID")
@tool(args_schema = RemoveProgressNodeWorkParentInput)
def remove_progress_node_work_parent(node_id: str, parent_id: str) -> bool:
    """
    プロジェクトの進捗管理のノードから親ノードIDを削除します。
    Args;
        :param node_id: ノードのID
        :type node_id: str
        :param parent_id: 親ノードのID
        :type parent_id: str
    Return:
        成功すればTrue、失敗すればFalse
        :rtype: bool
    """
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    node = g_progress_tracker.find_node_by_id(node_id)
    if node:
        node.remove_parent_id(parent_id)
        return True
    return False


class UnassignProgressNodeMemberInput(BaseModel):
    node_id: str = Field(..., description="ノードのID")
    member_name: str = Field(..., description="メンバー名")
@tool(args_schema = UnassignProgressNodeMemberInput)
def unassign_progress_node_member(node_id: str, member_name: str) -> bool:
    """
    プロジェクトの進捗管理のノードからメンバーの割り当てを解除します。
    Args:
        :param node_id: ノードのID
        :type node_id: str
        :param member_name: メンバー名
        :type member_name: str
    Return:
        成功すればTrue、失敗すればFalse
       :rtype: bool
    """
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    node = g_progress_tracker.find_node_by_id(node_id)
    if node:
        node.unassign_member(member_name)
        return True
    return False
    

class AssignProgressNodeMemberInput(BaseModel):
    node_id: str = Field(..., description="ノードのID")
    member_name: str = Field(..., description="メンバー名")
@tool(args_schema = AssignProgressNodeMemberInput)
def assign_progress_node_member(node_id: str, member_name: str) -> bool:
    """
    プロジェクトの進捗管理のノードにメンバーを割り当てます。
    Args:
        :param node_id: ノードのID
        :type node_id: str
        :param member_name: メンバー名
        :type member_name: str
    Return:
        成功すればTrue、失敗すればFalse
        :rtype: bool
    """
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    node = g_progress_tracker.find_node_by_id(node_id)
    if node:
        node.assign_member(member_name)
        return True
    return False

class GetProgressNodeMembersInput(BaseModel):
    node_id: str = Field(..., description="ノードのID")
@tool(args_schema = GetProgressNodeMembersInput)
def get_progress_node_members(node_id: str) -> Optional[List[str]]:
    """
    進捗管理のノードに割り当てられたメンバーリストを取得します。
    Args:
        :param node_id: ノードのID
        :type node_id: str
    Returns:
        メンバーリスト、ノードが見つからなければNone
        :rtype: Optional[List[str]]
    """
    global g_progress_tracker
    wait_safety()    
    if g_progress_tracker is None:
        raise ValueError("ProgressTracker is not initialized.")
    node = g_progress_tracker.find_node_by_id(node_id)
    if node:
        return node.get_members()
    return None

#==============================
# 進捗管理トラッカーのファイルリスト管理ツール
def _get_progress_file_descriptions() -> str:
    """
    プロジェクトの進捗管理トラッカーのデータの保存先フォルダ内のファイルリストを取得します。

    :return: ファイルパスのリスト
    :rtype: List[str]
    """
    return file_list._get_and_create_all_file_descriptions(CURRENT_DIR)


@tool
def get_progress_file_list() -> str:
    """
    プロジェクトの進捗管理トラッカーのデータの保存先フォルダ内のファイルリストを取得します。

    :return: ファイルパスのリスト
    :rtype: List[str]
    """
    wait_safety()    
    return _get_progress_file_descriptions()

class SetProgressFileDescriptionInput(BaseModel):
    file_path: str = Field(..., description="プロジェクトの進捗管理トラッカーのファイルのパス")
    description: str = Field(..., description="ファイルの説明")
    note: str = Field("", description="ファイルのメモ書き")
@tool(args_schema = SetProgressFileDescriptionInput)
def set_progress_file_description(file_path: str, description: str, note: str="") -> None:
    """
    プロジェクトの進捗管理トラッカーのデータの保存先フォルダ内のファイルリストにファイルの説明を追加します。
    この機能呼び出しの前にget_progress_file_listを実行してファイルリストを初期化してください。
    Args:
        :param file_path: ファイルのパス
        :type file_path: str
        :param description: ファイルの説明
        :type description: str
        :param note: ファイルのメモ書き
        :type note: str
    """
    wait_safety()    
    file_list._set_file_description(file_path, description, note)


def _save_progress_file_descriptions() -> None:
    file_list._save_file_descriptions()
@tool
def save_progress_file_descriptions() -> None:
    """
    プロジェクトの進捗管理トラッカーのデータの保存先フォルダ内の
    ファイルの説明を保存します。
    この機能呼び出しの前にget_progress_file_listを実行してファイルリストを初期化してください。
    """
    wait_safety()    
    file_list._save_file_descriptions()


def get_tools_list():
    return [
        create_progress_tracker,
        set_progress_root,
        add_progress_task,
        update_progress_status,
        update_progress_note,
        remove_progress_task,
        get_progress_mermaid,
        get_progress_json,
        save_progress_to_file,
        load_progress_from_file,
        set_progress_node_work_parents,
        get_progress_node_work_parents,
        add_progress_node_work_parent,
        remove_progress_node_work_parent,
        assign_progress_node_member,
        unassign_progress_node_member,
        get_progress_node_members,
        get_progress_file_list,
        set_progress_file_description,
        save_progress_file_descriptions
    ]