Token导航 LogoToken导航

Agent Skill · 待分类

portfolio-dashboard(投资组合仪表板)

portfolio-dashboard 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

portfolio-dashboard 是待分类类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为权限未标注;许可证信息还不明确,第三方扫描已通过,按安装量在待分类类的 11,209 个 Skill 里排第 7,383。

总安装 367周安装 15GitHub Stars 111下载 118核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:portfolio-dashboard(投资组合仪表板)
来源仓库:https://github.com/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction
仓库路径:skills/portfolio-dashboard
安装命令:
npx skills add https://github.com/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction --skill portfolio-dashboard
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction --skill portfolio-dashboard

这个 Skill 是做什么的

这个 Skill 的分类还没最终确定,用法以来源仓库的说明为准。

用于处理 GitHub 仓库、Issue、Pull Request 等代码协作信息。适合整理仓库状态、代码变更和协作事项相关数据。通过 npx skills add 命令从指定 GitHub 仓库安装使用。建议确认权限范围和维护状态,避免触发文件读写操作。

portfolio-dashboard 属于待分类类 Skill,可作为该场景下的辅助能力补充。

来源仓库内路径:skills/portfolio-dashboard

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/datadrivenconstruction/ddc_skills_for_ai_agent…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    权限没有明确标注,第一次使用时只给一个无关紧要的任务,观察它到底读了哪些文件和网络。

常见问题

6 条

portfolio-dashboard 怎么安装?

在终端执行 npx skills add https://github.com/datadrivenconstruction/ddc_skills_for_ai_agents_in_const…。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

portfolio-dashboard 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

portfolio-dashboard 需要什么权限?

权限级别没有明确标注。第一次使用时建议只给无关紧要的任务,观察它实际读写了哪些内容。

portfolio-dashboard 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

portfolio-dashboard 用的人多吗?

公开数据是累计安装 367 次,近一周安装 15 次,下载 118 次,GitHub 111 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

portfolio-dashboard 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理待分类类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

待分类类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
infsh-cli

infsh CLI · Codex、Claude

4,374,576382权限未标注未明确未收录
ai-video-generation

AI 视频生成 · Codex、Claude

4,313,376382调用外部服务未明确有提醒
lark-openapi-explorer

云雀 OpenAPI 浏览器 · Codex、Claude

2,064,1209,011需要联网未明确有提醒
lark-workflow-standup-report

Lark 工作流程站立报告 · Codex、Claude

2,041,6328,978需要联网未明确通过
use-my-browser

使用我的浏览器 · Codex、Claude

1,848,49140操作浏览器未明确未通过
portfolio-dashboard

投资组合仪表板 · 本页

367111权限未标注未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Portfolio Dashboard

Overview

Aggregate and analyze data across multiple construction projects for portfolio-level visibility. Track KPIs, identify trends, compare project performance, and support strategic resource allocation decisions.

Portfolio Analytics Framework

┌─────────────────────────────────────────────────────────────────┐
│                  PORTFOLIO DASHBOARD                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  PROJECT A    PROJECT B    PROJECT C    PROJECT D               │
│     ↓             ↓            ↓            ↓                   │
│  ┌─────────────────────────────────────────────┐                │
│  │           DATA AGGREGATION                   │                │
│  │  Cost | Schedule | Safety | Quality | Risk   │                │
│  └─────────────────────────────────────────────┘                │
│                         ↓                                        │
│  ┌─────────────────────────────────────────────┐                │
│  │          PORTFOLIO KPIs                      │                │
│  │  📊 Total Value    📈 On-Schedule %          │                │
│  │  💰 On-Budget %    🛡️ Safety Rate            │                │
│  │  ⚠️ Risk Score     📋 Resource Util          │                │
│  └─────────────────────────────────────────────┘                │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Technical Implementation

from dataclasses import dataclass, field
from typing import List, Dict, Optional, Tuple
from datetime import datetime, timedelta
from enum import Enum
import statistics

class ProjectStatus(Enum):
    PLANNING = "planning"
    ACTIVE = "active"
    ON_HOLD = "on_hold"
    COMPLETE = "complete"
    CANCELLED = "cancelled"

class HealthStatus(Enum):
    GREEN = "green"       # On track
    YELLOW = "yellow"     # At risk
    RED = "red"           # Critical
    GREY = "grey"         # Not started/on hold

@dataclass
class ProjectMetrics:
    project_id: str
    project_name: str
    status: ProjectStatus
    contract_value: float
    percent_complete: float

    # Schedule
    planned_start: datetime
    planned_end: datetime
    actual_start: Optional[datetime]
    forecast_end: datetime
    schedule_variance_days: int = 0

    # Cost
    budget: float
    actual_cost: float
    forecast_cost: float
    cost_variance: float = 0.0
    cpi: float = 1.0
    spi: float = 1.0

    # Safety
    recordable_incidents: int = 0
    total_hours: float = 0
    trir: float = 0.0

    # Quality
    defects_open: int = 0
    rework_cost: float = 0.0

    # Risk
    risk_score: float = 0.0
    critical_risks: int = 0

    @property
    def health(self) -> HealthStatus:
        """Determine overall project health."""
        if self.status in [ProjectStatus.ON_HOLD, ProjectStatus.CANCELLED]:
            return HealthStatus.GREY

        # Critical if significantly over budget/schedule
        if self.cpi < 0.85 or self.spi < 0.85 or self.critical_risks > 3:
            return HealthStatus.RED

        # At risk if moderately off track
        if self.cpi < 0.95 or self.spi < 0.95 or self.critical_risks > 0:
            return HealthStatus.YELLOW

        return HealthStatus.GREEN

@dataclass
class PortfolioSummary:
    report_date: datetime
    total_projects: int
    active_projects: int
    total_contract_value: float
    total_budget: float
    total_actual_cost: float
    total_forecast_cost: float

    # Performance
    avg_cpi: float
    avg_spi: float
    on_budget_pct: float
    on_schedule_pct: float

    # Safety
    portfolio_trir: float
    total_incidents: int

    # Health distribution
    green_count: int
    yellow_count: int
    red_count: int

    # Trends
    cost_trend: str
    schedule_trend: str

@dataclass
class ProjectComparison:
    metric: str
    projects: Dict[str, float]
    avg: float
    best: Tuple[str, float]
    worst: Tuple[str, float]

class PortfolioDashboard:
    """Multi-project portfolio analytics."""

    # Health thresholds
    THRESHOLDS = {
        "cpi_warning": 0.95,
        "cpi_critical": 0.85,
        "spi_warning": 0.95,
        "spi_critical": 0.85,
        "trir_warning": 2.0,
        "risk_score_warning": 7.0
    }

    def __init__(self, portfolio_name: str):
        self.portfolio_name = portfolio_name
        self.projects: Dict[str, ProjectMetrics] = {}
        self.snapshots: List[Dict] = []  # Historical data

    def add_project(self, metrics: ProjectMetrics):
        """Add or update project in portfolio."""
        self.projects[metrics.project_id] = metrics

    def import_projects(self, projects_data: List[Dict]) -> int:
        """Import multiple projects from data."""
        count = 0
        for p in projects_data:
            metrics = ProjectMetrics(
                project_id=p['id'],
                project_name=p['name'],
                status=ProjectStatus(p.get('status', 'active')),
                contract_value=p['contract_value'],
                percent_complete=p.get('percent_complete', 0),
                planned_start=p['planned_start'],
                planned_end=p['planned_end'],
                actual_start=p.get('actual_start'),
                forecast_end=p.get('forecast_end', p['planned_end']),
                budget=p['budget'],
                actual_cost=p.get('actual_cost', 0),
                forecast_cost=p.get('forecast_cost', p['budget']),
                cpi=p.get('cpi', 1.0),
                spi=p.get('spi', 1.0),
                recordable_incidents=p.get('incidents', 0),
                total_hours=p.get('total_hours', 0),
                risk_score=p.get('risk_score', 0),
                critical_risks=p.get('critical_risks', 0)
            )

            # Calculate derived metrics
            metrics.cost_variance = metrics.budget - metrics.actual_cost
            metrics.schedule_variance_days = (metrics.planned_end - metrics.forecast_end).days

            if metrics.total_hours > 0:
                metrics.trir = (metrics.recordable_incidents * 200000) / metrics.total_hours

            self.add_project(metrics)
            count += 1

        return count

    def get_active_projects(self) -> List[ProjectMetrics]:
        """Get list of active projects."""
        return [p for p in self.projects.values()
                if p.status == ProjectStatus.ACTIVE]

    def calculate_portfolio_summary(self) -> PortfolioSummary:
        """Calculate portfolio-level summary metrics."""
        active = self.get_active_projects()
        all_projects = list(self.projects.values())

        if not all_projects:
            return None

        # Totals
        total_contract = sum(p.contract_value for p in all_projects)
        total_budget = sum(p.budget for p in all_projects)
        total_actual = sum(p.actual_cost for p in all_projects)
        total_forecast = sum(p.forecast_cost for p in all_projects)

        # Performance averages (weighted by budget)
        if total_budget > 0:
            avg_cpi = sum(p.cpi * p.budget for p in active) / sum(p.budget for p in active) if active else 1.0
            avg_spi = sum(p.spi * p.budget for p in active) / sum(p.budget for p in active) if active else 1.0
        else:
            avg_cpi = avg_spi = 1.0

        # On budget/schedule percentages
        on_budget = len([p for p in active if p.cpi >= 0.95])
        on_schedule = len([p for p in active if p.spi >= 0.95])

        on_budget_pct = (on_budget / len(active) * 100) if active else 100
        on_schedule_pct = (on_schedule / len(active) * 100) if active else 100

        # Safety metrics
        total_incidents = sum(p.recordable_incidents for p in all_projects)
        total_hours = sum(p.total_hours for p in all_projects)
        portfolio_trir = (total_incidents * 200000 / total_hours) if total_hours > 0 else 0

        # Health distribution
        green = len([p for p in active if p.health == HealthStatus.GREEN])
        yellow = len([p for p in active if p.health == HealthStatus.YELLOW])
        red = len([p for p in active if p.health == HealthStatus.RED])

        # Trends (compare to previous snapshot if available)
        cost_trend = "stable"
        schedule_trend = "stable"

        if self.snapshots:
            prev = self.snapshots[-1]
            if avg_cpi > prev.get('avg_cpi', 1.0):
                cost_trend = "improving"
            elif avg_cpi < prev.get('avg_cpi', 1.0):
                cost_trend = "declining"

            if avg_spi > prev.get('avg_spi', 1.0):
                schedule_trend = "improving"
            elif avg_spi < prev.get('avg_spi', 1.0):
                schedule_trend = "declining"

        return PortfolioSummary(
            report_date=datetime.now(),
            total_projects=len(all_projects),
            active_projects=len(active),
            total_contract_value=total_contract,
            total_budget=total_budget,
            total_actual_cost=total_actual,
            total_forecast_cost=total_forecast,
            avg_cpi=avg_cpi,
            avg_spi=avg_spi,
            on_budget_pct=on_budget_pct,
            on_schedule_pct=on_schedule_pct,
            portfolio_trir=portfolio_trir,
            total_incidents=total_incidents,
            green_count=green,
            yellow_count=yellow,
            red_count=red,
            cost_trend=cost_trend,
            schedule_trend=schedule_trend
        )

    def compare_projects(self, metric: str) -> ProjectComparison:
        """Compare projects by specific metric."""
        active = self.get_active_projects()

        if not active:
            return None

        metric_map = {
            "cpi": lambda p: p.cpi,
            "spi": lambda p: p.spi,
            "percent_complete": lambda p: p.percent_complete,
            "cost_variance": lambda p: p.cost_variance,
            "trir": lambda p: p.trir,
            "risk_score": lambda p: p.risk_score
        }

        if metric not in metric_map:
            raise ValueError(f"Unknown metric: {metric}")

        getter = metric_map[metric]
        values = {p.project_name: getter(p) for p in active}

        avg = statistics.mean(values.values())

        # Best/worst depends on metric (higher CPI good, lower TRIR good)
        if metric in ["trir", "risk_score"]:
            best = min(values.items(), key=lambda x: x[1])
            worst = max(values.items(), key=lambda x: x[1])
        else:
            best = max(values.items(), key=lambda x: x[1])
            worst = min(values.items(), key=lambda x: x[1])

        return ProjectComparison(
            metric=metric,
            projects=values,
            avg=avg,
            best=best,
            worst=worst
        )

    def get_projects_at_risk(self) -> List[ProjectMetrics]:
        """Get projects that need attention."""
        return [p for p in self.get_active_projects()
                if p.health in [HealthStatus.YELLOW, HealthStatus.RED]]

    def get_top_risks(self, limit: int = 10) -> List[Dict]:
        """Get top risks across portfolio."""
        risks = []

        for p in self.get_active_projects():
            if p.risk_score > 0:
                risks.append({
                    "project": p.project_name,
                    "risk_score": p.risk_score,
                    "critical_risks": p.critical_risks,
                    "cpi": p.cpi,
                    "spi": p.spi
                })

        return sorted(risks, key=lambda x: -x['risk_score'])[:limit]

    def forecast_cash_needs(self, months: int = 6) -> List[Dict]:
        """Forecast cash needs across portfolio."""
        forecasts = []

        for month in range(1, months + 1):
            month_date = datetime.now() + timedelta(days=month * 30)

            month_spend = 0
            for p in self.get_active_projects():
                # Simple linear projection based on remaining work
                remaining = p.forecast_cost - p.actual_cost
                months_remaining = max(1, (p.forecast_end - datetime.now()).days / 30)
                monthly_burn = remaining / months_remaining
                month_spend += monthly_burn

            forecasts.append({
                "month": month_date.strftime("%Y-%m"),
                "projected_spend": month_spend
            })

        return forecasts

    def save_snapshot(self):
        """Save current state for trend analysis."""
        summary = self.calculate_portfolio_summary()
        if summary:
            self.snapshots.append({
                "date": datetime.now(),
                "avg_cpi": summary.avg_cpi,
                "avg_spi": summary.avg_spi,
                "on_budget_pct": summary.on_budget_pct,
                "on_schedule_pct": summary.on_schedule_pct,
                "total_forecast": summary.total_forecast_cost
            })

    def generate_report(self) -> str:
        """Generate portfolio dashboard report."""
        summary = self.calculate_portfolio_summary()

        if not summary:
            return "No projects in portfolio"

        lines = [
            "# Portfolio Dashboard",
            "",
            f"**Portfolio:** {self.portfolio_name}",
            f"**Report Date:** {summary.report_date.strftime('%Y-%m-%d')}",
            "",
            "## Executive Summary",
            "",
            f"| Metric | Value |",
            f"|--------|-------|",
            f"| Total Projects | {summary.total_projects} ({summary.active_projects} active) |",
            f"| Total Contract Value | ${summary.total_contract_value:,.0f} |",
            f"| Total Budget | ${summary.total_budget:,.0f} |",
            f"| Actual Cost to Date | ${summary.total_actual_cost:,.0f} |",
            f"| Forecast at Completion | ${summary.total_forecast_cost:,.0f} |",
            "",
            "## Performance Indicators",
            "",
            f"| KPI | Value | Trend |",
            f"|-----|-------|-------|",
            f"| Avg CPI | {summary.avg_cpi:.2f} | {summary.cost_trend} |",
            f"| Avg SPI | {summary.avg_spi:.2f} | {summary.schedule_trend} |",
            f"| On Budget | {summary.on_budget_pct:.0f}% | |",
            f"| On Schedule | {summary.on_schedule_pct:.0f}% | |",
            f"| Portfolio TRIR | {summary.portfolio_trir:.2f} | |",
            "",
            "## Health Distribution",
            "",
            f"🟢 Green: {summary.green_count} | 🟡 Yellow: {summary.yellow_count} | 🔴 Red: {summary.red_count}",
            ""
        ]

        # Projects at risk
        at_risk = self.get_projects_at_risk()
        if at_risk:
            lines.extend([
                "## Projects Requiring Attention",
                "",
                "| Project | Health | CPI | SPI | Critical Risks |",
                "|---------|--------|-----|-----|----------------|"
            ])
            for p in sorted(at_risk, key=lambda x: x.cpi):
                health_icon = "🟡" if p.health == HealthStatus.YELLOW else "🔴"
                lines.append(
                    f"| {p.project_name} | {health_icon} | {p.cpi:.2f} | {p.spi:.2f} | {p.critical_risks} |"
                )
            lines.append("")

        # Project comparison
        lines.extend([
            "## Project Comparison - CPI",
            "",
            "| Project | CPI |",
            "|---------|-----|"
        ])

        cpi_compare = self.compare_projects("cpi")
        if cpi_compare:
            for name, value in sorted(cpi_compare.projects.items(), key=lambda x: -x[1]):
                lines.append(f"| {name} | {value:.2f} |")

        return "\n".join(lines)

Quick Start

from datetime import datetime, timedelta

# Initialize dashboard
dashboard = PortfolioDashboard("Regional Construction Portfolio")

# Import project data
projects = [
    {
        "id": "PRJ-001",
        "name": "Downtown Office Tower",
        "status": "active",
        "contract_value": 50000000,
        "budget": 48000000,
        "actual_cost": 25000000,
        "forecast_cost": 49000000,
        "percent_complete": 55,
        "planned_start": datetime(2024, 1, 1),
        "planned_end": datetime(2025, 6, 30),
        "forecast_end": datetime(2025, 7, 15),
        "cpi": 0.92,
        "spi": 0.95,
        "incidents": 2,
        "total_hours": 150000,
        "risk_score": 7.5,
        "critical_risks": 2
    },
    {
        "id": "PRJ-002",
        "name": "Hospital Expansion",
        "status": "active",
        "contract_value": 80000000,
        "budget": 75000000,
        "actual_cost": 30000000,
        "forecast_cost": 74000000,
        "percent_complete": 40,
        "planned_start": datetime(2024, 3, 1),
        "planned_end": datetime(2026, 2, 28),
        "forecast_end": datetime(2026, 2, 28),
        "cpi": 1.02,
        "spi": 1.00,
        "incidents": 0,
        "total_hours": 100000,
        "risk_score": 4.0,
        "critical_risks": 0
    }
]

dashboard.import_projects(projects)

# Get portfolio summary
summary = dashboard.calculate_portfolio_summary()
print(f"Portfolio Value: ${summary.total_contract_value:,.0f}")
print(f"Avg CPI: {summary.avg_cpi:.2f}")
print(f"On Budget: {summary.on_budget_pct:.0f}%")

# Find projects at risk
at_risk = dashboard.get_projects_at_risk()
print(f"Projects at risk: {len(at_risk)}")

# Compare projects
cpi_comparison = dashboard.compare_projects("cpi")
print(f"Best CPI: {cpi_comparison.best[0]} ({cpi_comparison.best[1]:.2f})")

# Generate report
print(dashboard.generate_report())

Requirements

pip install (no external dependencies)

平台分布

Codex

36.35%
按下载量换算43

Claude

30.02%
按下载量换算35

Cursor

18.57%
按下载量换算22

Gemini CLI

10.92%
按下载量换算13

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

权限未标注

当前来源未能明确判断权限范围,默认进入异常复核队列。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills