Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "computer-usage-summary-skill",
"version": "0.2.2",
"version": "0.2.3",
"description": "Privacy-first, cross-platform local ActivityWatch summaries for Codex.",
"author": {
"name": "liuyewang",
Expand Down
8 changes: 8 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ This is a privacy-safe product and validation log. It records decisions and
feedback themes, never ActivityWatch events, window titles, URLs, account
names, local paths, or behavioral telemetry.

## 2026-07-27 - v0.2.3 Standardized Conversational Output

- Standardized conversational summaries around `Today at a glance`, `What you
did`, `App table`, and `Caveats`.
- Kept the four-section structure for unavailable ActivityWatch data and made
reduced output opt-in when the user explicitly requests it.
- Added a regression test for the default conversational output contract.

## 2026-07-27 - v0.2.2 Review Fixes

- Convert invalid IANA time-zone input into a clean CLI validation error.
Expand Down
35 changes: 35 additions & 0 deletions skills/computer-usage-summary/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,41 @@ When the app, local API, buckets, or requested-range events are unavailable:
7. When ActivityWatch is unavailable, return its structured reason. Markdown,
TSV, and CSV outputs provide `status`, `reason`, and `source` columns.

## Default Conversational Output

For conversational summaries, always use the following four sections, in this
order, unless the user explicitly asks for a simpler or shorter output:

1. `Today at a glance`
- State the local date and time zone.
- Report confirmed active time, away time, and evidence coverage.
- In fallback mode, state that unavailable values are `unavailable`; do not
estimate them.
2. `What you did`
- Give a chronological list of meaningful activity windows.
- Include the time window, activity, supporting app or sanitized title,
confidence, and evidence label (`confirmed ActivityWatch`, `Screen Time
reported`, `live snapshot`, or `metadata clue`).
3. `App table`
- Include app name, observed foreground sessions, active time, first seen,
last active, and source note when ActivityWatch data is available.
- If data is unavailable, keep this section and show the structured reason
with `unavailable` metrics rather than omitting the table.
4. `Caveats`
- State the relevant privacy and evidence limits, including that foreground
sessions are observed intervals rather than process-launch counts and that
tracking starts only when ActivityWatch starts.

Keep these sections even when one contains little or no data. A request such as
"简单一点", "简要输出", "只要结论", or "只给表格" is an explicit request
for a reduced format; then omit or compress only the sections the user asks to
remove. Do not infer a reduced format merely because the request is short.

For a requested export (`TSV`, `CSV`, or a saved report), follow the export
format the user requested instead of wrapping it in these conversational
headings. If the user asks for both a summary and an export, provide the four
conversational sections first and then the requested export.

## Privacy Rules

- Treat window titles as sensitive. Remove URLs, truncate long titles, and
Expand Down
13 changes: 13 additions & 0 deletions tests/test_activitywatch_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,19 @@ def test_skill_frontmatter_is_present(self):
self.assertTrue(skill.startswith("---\nname: computer-usage-summary\n"))
self.assertIn("description:", skill.split("---", 2)[1])

def test_skill_default_conversational_output_contract(self):
skill = (SKILL_ROOT / "SKILL.md").read_text(encoding="utf-8")
headings = [
"`Today at a glance`",
"`What you did`",
"`App table`",
"`Caveats`",
]
positions = [skill.index(heading) for heading in headings]
self.assertEqual(positions, sorted(positions))
self.assertIn("unless the user explicitly asks for a simpler or shorter output", skill)
self.assertIn("Do not infer a reduced format merely because the request is short.", skill)


if __name__ == "__main__":
unittest.main()
Loading