ccus-cli 0.2.26 → 0.2.27

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/README.md CHANGED
@@ -22,11 +22,11 @@
22
22
 
23
23
  个人和聚合面板均提供「周评分」:默认 `50 × √(周消息数 / 200) + 50 × √(周累计额度百分点 / 70)`,消息和额度均合计 Claude + Codex。两项达到基准得 100 分,分数不封顶。
24
24
 
25
- 个人和团队面板提供「出勤天数」选择,默认 5 天,可选 1~7 天,切换后立即更新评分。消息和额度达标线按 `出勤天数 / 5` 调整,原始统计值不变;例如 3 天对应 120 条消息、42 个额度百分点。团队顶部选择批量设置所有成员,每行可单独调整,该成员所展示的各周共用所选天数;刷新页面恢复 5 天。点击「导出周统计 CSV」下载按当前各成员出勤天数评分的 `weekly.csv`,保留现有列,多周逐周导出。命令行导出仍按默认 5 天评分。
25
+ 个人和团队面板提供「出勤天数」选择,默认 5 天,可选 0.5~7 天,步进 0.5 天,切换后立即更新评分。消息和额度达标线按 `出勤天数 / 5` 调整,原始统计值不变;例如 3 天对应 120 条消息、42 个额度百分点。团队顶部选择批量设置所有成员,每行可单独调整,该成员所展示的各周共用所选天数;刷新页面恢复 5 天。点击「导出周统计 CSV」下载按当前各成员出勤天数评分的 `weekly.csv`,保留现有列,多周逐周导出。命令行导出仍按默认 5 天评分。
26
26
 
27
27
  权重和标准 5 天基准集中在 `src/lib/weekly-score.ts` 的 `DEFAULT_SCORE_SETTINGS` 修改:`messageWeight` 为消息占比(额度占比为 100 减去消息占比),`messageBaseline` 为周消息基准,`quotaBaseline` 为周额度基准。修改后重新构建并重启面板服务。
28
28
 
29
- 评分按当前查询范围逐周计算,未满一周不折算;查看本周评分请使用 `ccus dashboard serve --range this-week`。聚合面板在「多人对比」中显示周评分,多周取逐周评分的平均值。额度缺失显示 `--`(消息占比为 100% 时可单独评分)。`weekly.csv` 末尾包含 `weeklyScore`、`attendanceDays` 两列:评分与看板共用公式,保留最多 1 位小数,不可用时留空;出勤天数为 1~7,看板导出使用各成员当前选择,命令行默认 5 天。bundle、`daily.csv` 和 `detail.csv` 不变。
29
+ 评分按当前查询范围逐周计算,未满一周不折算;查看本周评分请使用 `ccus dashboard serve --range this-week`。聚合面板在「多人对比」中显示周评分,多周取逐周评分的平均值。额度缺失显示 `--`(消息占比为 100% 时可单独评分)。`weekly.csv` 末尾包含 `weeklyScore`、`attendanceDays` 两列:评分与看板共用公式,保留最多 1 位小数,不可用时留空;出勤天数为 0.5~7、步进 0.5,看板导出使用各成员当前选择,命令行默认 5 天。bundle、`daily.csv` 和 `detail.csv` 不变。
30
30
 
31
31
  ## 安装
32
32
 
@@ -497,7 +497,7 @@ function weeklyCsvDownloadScript(rows) {
497
497
  const header = (0, export_1.buildAggregatedWeeklyCsv)([]);
498
498
  const data = rows.map(row => ({
499
499
  personKey: row.personKey,
500
- csv: [1, 2, 3, 4, 5, 6, 7].map(days => (0, export_1.buildAggregatedWeeklyCsv)([row], days).slice(header.length + 1)),
500
+ csv: weekly_score_1.ATTENDANCE_DAYS.map(days => (0, export_1.buildAggregatedWeeklyCsv)([row], days).slice(header.length + 1)),
501
501
  }));
502
502
  // 防止用户字段闭合内联脚本;CSV 转义和公式防护复用导出模块。
503
503
  const serialized = JSON.stringify({ header, rows: data }).replaceAll("<", "\\u003c");
@@ -510,9 +510,9 @@ function weeklyCsvDownloadScript(rows) {
510
510
  const attendance = new Map();
511
511
  document.querySelectorAll('[data-attendance-person]').forEach(control => {
512
512
  const days = Number(control.value);
513
- attendance.set(control.dataset.attendancePerson, Number.isInteger(days) && days >= 1 && days <= 7 ? days : 5);
513
+ attendance.set(control.dataset.attendancePerson, Number.isInteger(days * 2) && days >= 0.5 && days <= 7 ? days : 5);
514
514
  });
515
- const csv = [data.header, ...data.rows.map(row => row.csv[(attendance.get(row.personKey) ?? 5) - 1])].join('\\n');
515
+ const csv = [data.header, ...data.rows.map(row => row.csv[(attendance.get(row.personKey) ?? 5) * 2 - 1])].join('\\n');
516
516
  const url = URL.createObjectURL(new Blob(['\\uFEFF', csv], { type: 'text/csv;charset=utf-8' }));
517
517
  const link = document.createElement('a');
518
518
  link.href = url;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_SCORE_SETTINGS = void 0;
3
+ exports.ATTENDANCE_DAYS = exports.DEFAULT_SCORE_SETTINGS = void 0;
4
4
  exports.calculateWeeklyScore = calculateWeeklyScore;
5
5
  exports.renderAttendanceControl = renderAttendanceControl;
6
6
  exports.renderAttendanceScore = renderAttendanceScore;
@@ -11,9 +11,10 @@ exports.DEFAULT_SCORE_SETTINGS = {
11
11
  messageBaseline: 200,
12
12
  quotaBaseline: 70,
13
13
  };
14
+ exports.ATTENDANCE_DAYS = Array.from({ length: 14 }, (_, index) => (index + 1) / 2);
14
15
  function calculateWeeklyScore(messages, quota, settings, attendanceDays = 5) {
15
16
  const { messageWeight, messageBaseline, quotaBaseline } = settings;
16
- if (!Number.isInteger(attendanceDays) || attendanceDays < 1 || attendanceDays > 7
17
+ if (!Number.isInteger(attendanceDays * 2) || attendanceDays < 0.5 || attendanceDays > 7
17
18
  || !Number.isFinite(messageWeight) || messageWeight < 0 || messageWeight > 100
18
19
  || !Number.isFinite(messageBaseline) || messageBaseline <= 0
19
20
  || !Number.isFinite(quotaBaseline) || quotaBaseline <= 0
@@ -28,18 +29,18 @@ function calculateWeeklyScore(messages, quota, settings, attendanceDays = 5) {
28
29
  }
29
30
  function renderAttendanceControl(personKey) {
30
31
  const attributes = personKey === undefined ? 'id="attendance-days"' : `data-attendance-person="${escapeHtml(personKey)}" aria-label="出勤天数"`;
31
- const select = `<select ${attributes}>${[1, 2, 3, 4, 5, 6, 7].map(days => `<option value="${days}"${days === 5 ? " selected" : ""}>${days} 天</option>`).join("")}</select>`;
32
+ const select = `<select ${attributes}>${exports.ATTENDANCE_DAYS.map(days => `<option value="${days}"${days === 5 ? " selected" : ""}>${days} 天</option>`).join("")}</select>`;
32
33
  return personKey === undefined ? `<label>出勤天数 ${select}</label>` : select;
33
34
  }
34
35
  /** 多周逐周评分后取平均;浏览器切换预先按各出勤天数算好的结果。 */
35
36
  function renderAttendanceScore(rows) {
36
- const scores = [1, 2, 3, 4, 5, 6, 7].map(days => {
37
+ const scores = exports.ATTENDANCE_DAYS.map(days => {
37
38
  const values = rows.map(row => calculateWeeklyScore(row.userMessageCount, row.sevenDayCumulativeUsagePct, exports.DEFAULT_SCORE_SETTINGS, days));
38
39
  if (!values.length || values.some(value => value === null))
39
40
  return "--";
40
41
  return (values.reduce((sum, value) => sum + (value ?? 0), 0) / values.length).toFixed(1);
41
42
  });
42
- return `<span data-attendance-scores="${escapeHtml(JSON.stringify(scores))}">${scores[4]}</span>`;
43
+ return `<span data-attendance-scores="${escapeHtml(JSON.stringify(scores))}">${scores[9]}</span>`;
43
44
  }
44
45
  function attendanceScoreScript() {
45
46
  return `<script>
@@ -48,18 +49,18 @@ function attendanceScoreScript() {
48
49
  if (!select) return;
49
50
  select.addEventListener('change', () => {
50
51
  const days = Number(select.value);
51
- if (!Number.isInteger(days) || days < 1 || days > 7) return;
52
+ if (!Number.isInteger(days * 2) || days < 0.5 || days > 7) return;
52
53
  document.querySelectorAll('[data-attendance-scores]').forEach(element => {
53
- element.textContent = JSON.parse(element.dataset.attendanceScores)[days - 1];
54
+ element.textContent = JSON.parse(element.dataset.attendanceScores)[days * 2 - 1];
54
55
  });
55
56
  document.querySelectorAll('[data-attendance-person]').forEach(element => { element.value = select.value; });
56
57
  });
57
58
  document.querySelectorAll('[data-attendance-person]').forEach(control => {
58
59
  control.addEventListener('change', () => {
59
60
  const days = Number(control.value);
60
- if (!Number.isInteger(days) || days < 1 || days > 7) return;
61
+ if (!Number.isInteger(days * 2) || days < 0.5 || days > 7) return;
61
62
  const score = control.closest('tr').querySelector('[data-attendance-scores]');
62
- score.textContent = JSON.parse(score.dataset.attendanceScores)[days - 1];
63
+ score.textContent = JSON.parse(score.dataset.attendanceScores)[days * 2 - 1];
63
64
  });
64
65
  });
65
66
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccus-cli",
3
- "version": "0.2.26",
3
+ "version": "0.2.27",
4
4
  "description": "Claude Code statusline usage logger and dashboard CLI",
5
5
  "type": "commonjs",
6
6
  "bin": {