@clubs.place/cli 0.0.0-stage → 0.1.18
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/.env.example +88 -0
- package/LICENSE +21 -0
- package/PROVENANCE.json +77 -0
- package/README.md +70 -2
- package/bin/clubs-nippo +7 -0
- package/bin/clubs-nippo-web.mjs +338 -0
- package/bin/clubs-nippo.mjs +527 -0
- package/docs/events-schema.md +76 -0
- package/examples/hermes-events.sample.jsonl +8 -0
- package/package.json +44 -4
- package/src/aggregate.mjs +105 -0
- package/src/autorun.mjs +25 -0
- package/src/autorun.test.mjs +43 -0
- package/src/cards.mjs +406 -0
- package/src/collect.mjs +111 -0
- package/src/config.mjs +173 -0
- package/src/generators/claude.mjs +31 -0
- package/src/generators/cli.mjs +81 -0
- package/src/generators/codex.mjs +28 -0
- package/src/generators/copilot.mjs +21 -0
- package/src/generators/gemini.mjs +20 -0
- package/src/generators/openai-compat.mjs +79 -0
- package/src/generators/openrouter.mjs +65 -0
- package/src/history.mjs +66 -0
- package/src/ingest.mjs +78 -0
- package/src/lemma.docHash.test.mjs +37 -0
- package/src/lemma.mjs +303 -0
- package/src/mask.mjs +45 -0
- package/src/mcp.mjs +269 -0
- package/src/narrative.mjs +191 -0
- package/src/prompt.mjs +184 -0
- package/src/prompt.test.mjs +146 -0
- package/src/remote-config.mjs +72 -0
- package/src/render.mjs +467 -0
- package/src/send.mjs +36 -0
- package/src/settings.mjs +54 -0
- package/src/sources/claude-code.mjs +174 -0
- package/src/sources/codex-cli.mjs +182 -0
- package/src/sources/copilot-cli.mjs +125 -0
- package/src/sources/cowork.mjs +193 -0
- package/src/sources/events-file.mjs +120 -0
- package/src/sources/gemini-cli.mjs +128 -0
- package/src/sources/hermes.mjs +427 -0
- package/src/store.mjs +227 -0
- package/src/tasks.mjs +229 -0
- package/src/tool-name.mjs +28 -0
- package/templates/report-v3.reference.html +162 -0
- package/web/assets/logo-clubs-white.png +0 -0
- package/web/assets/logo-clubs-white.svg +7 -0
- package/web/assets/logo-clubs-white.svg.tmp +30 -0
- package/web/assets/logo-clubs.svg +30 -0
- package/web/assets/qr.js +411 -0
- package/web/assets/sidebar.js +154 -0
- package/web/assets/twemoji.js +34 -0
- package/web/cards/achievements.html +230 -0
- package/web/cards/catalog.json +217 -0
- package/web/email/hero-v3.reference.html +123 -0
- package/web/home/index.html +269 -0
- package/web/preview.json +301 -0
- package/web/qr-selftest.html +46 -0
- package/web/reports/edit.html +255 -0
- package/web/reports/edit.reference.html +257 -0
- package/web/reports/index.html +196 -0
- package/web/settings/index.html +219 -0
- package/web/share/cards-reference.html +228 -0
- package/web/share/passport.html +214 -0
- package/web/share/teianbako.html +177 -0
- package/web/signup/index.html +326 -0
- package/web/tasks/index.html +229 -0
- package/web/verify/index.html +248 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="ja">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>日報を編集して記録 — CLUBS Console</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Noto+Sans+JP:wght@400;500;700;800&family=IBM+Plex+Mono:wght@500&display=swap" rel="stylesheet" />
|
|
10
|
+
<style>
|
|
11
|
+
:root{
|
|
12
|
+
--ink:#161A26; --ink-2:#3A4155; --muted:#6B7280;
|
|
13
|
+
--line:#E7E9F2; --bg:#F6F7FB; --card:#FFFFFF;
|
|
14
|
+
--sidebar:#161A26; --sidebar-ink:#AEB4C6; --sidebar-active:#232838;
|
|
15
|
+
--indigo:#4F6BF6; --violet:#8B5CF6;
|
|
16
|
+
--green:#0A8F66; --green-2:#17A45F; --green-bg:#E7F5EF; --green-line:#BFE6D6;
|
|
17
|
+
--amber-ink:#9A6A00; --amber-bg:#FFF6E5; --amber-line:#F5E4BC;
|
|
18
|
+
--gray-bg:#F3F4F6; --gray-line:#E5E7EB;
|
|
19
|
+
--radius:14px;
|
|
20
|
+
}
|
|
21
|
+
*{box-sizing:border-box}
|
|
22
|
+
body{margin:0; background:var(--bg); color:var(--ink);
|
|
23
|
+
font-family:"Inter","Noto Sans JP",system-ui,-apple-system,sans-serif; line-height:1.6; -webkit-font-smoothing:antialiased}
|
|
24
|
+
.mono{font-family:"IBM Plex Mono",ui-monospace,monospace}
|
|
25
|
+
svg{width:18px; height:18px}
|
|
26
|
+
.app{display:flex; min-height:100vh}
|
|
27
|
+
|
|
28
|
+
.side{width:236px; background:var(--sidebar); color:var(--sidebar-ink); flex:none; padding:18px 14px; display:flex; flex-direction:column; gap:4px}
|
|
29
|
+
.brand{display:flex; align-items:center; gap:9px; color:#fff; font-weight:700; padding:8px 10px 16px; letter-spacing:.02em}
|
|
30
|
+
.brand .dot{width:22px; height:22px; border-radius:7px; background:linear-gradient(135deg,var(--indigo),var(--violet))}
|
|
31
|
+
.nav{display:flex; align-items:center; gap:11px; padding:9px 11px; border-radius:10px; font-size:14px; color:var(--sidebar-ink); text-decoration:none; cursor:pointer}
|
|
32
|
+
.nav:hover{background:#1D2231}
|
|
33
|
+
.nav.active{background:var(--sidebar-active); color:#fff}
|
|
34
|
+
.nav svg{width:17px; height:17px; opacity:.9}
|
|
35
|
+
.side .spacer{flex:1}
|
|
36
|
+
.side .me{display:flex; align-items:center; gap:9px; padding:9px 11px; font-size:13px; border-top:1px solid #232838; margin-top:6px}
|
|
37
|
+
.side .me .av{width:26px; height:26px; border-radius:50%; background:linear-gradient(135deg,#66B9D8,#3E93B5); flex:none}
|
|
38
|
+
|
|
39
|
+
.main{flex:1; min-width:0; display:flex; flex-direction:column}
|
|
40
|
+
.topbar{height:60px; background:#fff; border-bottom:1px solid var(--line); display:flex; align-items:center; gap:12px; padding:0 26px}
|
|
41
|
+
.topbar .crumb{font-size:13px; color:var(--muted)} .topbar .crumb b{color:var(--ink)}
|
|
42
|
+
.topbar .spacer{flex:1}
|
|
43
|
+
.pill{font-size:12px; font-weight:700; padding:4px 11px; border-radius:999px; display:inline-flex; align-items:center; gap:6px}
|
|
44
|
+
.pill svg{width:13px;height:13px}
|
|
45
|
+
.pill.verified{color:var(--green); background:var(--green-bg); border:1px solid var(--green-line)}
|
|
46
|
+
.pill.unrec{color:var(--amber-ink); background:var(--amber-bg); border:1px solid var(--amber-line)}
|
|
47
|
+
|
|
48
|
+
.content{padding:26px; max-width:820px; width:100%; margin:0 auto}
|
|
49
|
+
.recorded{display:none; background:var(--green-bg); border:1px solid var(--green-line); border-radius:var(--radius); padding:16px 18px; margin-bottom:20px; align-items:center; gap:12px}
|
|
50
|
+
.recorded.show{display:flex}
|
|
51
|
+
.recorded .rc{width:38px;height:38px;border-radius:50%;background:#fff;border:1px solid var(--green-line);display:flex;align-items:center;justify-content:center;color:var(--green);flex:none}
|
|
52
|
+
.recorded b{color:var(--green)} .recorded a{color:var(--indigo);text-decoration:none;font-weight:600}
|
|
53
|
+
.recorded .rtext{font-size:13.5px;color:var(--ink-2)}
|
|
54
|
+
|
|
55
|
+
.h1{font-size:22px; font-weight:800; margin:0 0 3px}
|
|
56
|
+
.h1 .agent{color:var(--indigo)}
|
|
57
|
+
.daterow{font-size:13px; color:var(--muted); margin-bottom:20px}
|
|
58
|
+
|
|
59
|
+
.section-label{font-size:12px; font-weight:700; color:var(--muted); margin:0 0 10px; display:flex; align-items:center; gap:8px}
|
|
60
|
+
.section-label .lock{display:inline-flex; align-items:center; gap:5px; color:#8A8F9E; background:var(--gray-bg); border:1px solid var(--gray-line); border-radius:999px; padding:2px 9px; font-size:11px}
|
|
61
|
+
.section-label .lock svg{width:12px;height:12px}
|
|
62
|
+
.section-label .edhint{font-size:11.5px; color:var(--indigo); font-weight:700}
|
|
63
|
+
.metrics{display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:26px}
|
|
64
|
+
@media(max-width:640px){.metrics{grid-template-columns:repeat(2,1fr)}}
|
|
65
|
+
.metric{background:var(--card); border:1px solid var(--line); border-radius:var(--radius); padding:14px 16px}
|
|
66
|
+
.metric .mk{font-size:11px; color:var(--muted); font-weight:600}
|
|
67
|
+
.metric .mv{font-size:22px; font-weight:800; margin-top:3px}
|
|
68
|
+
.metric .mv small{font-size:13px; color:var(--muted); font-weight:600}
|
|
69
|
+
|
|
70
|
+
.block{background:var(--card); border:1px solid var(--line); border-radius:var(--radius); padding:16px 18px; margin-bottom:12px}
|
|
71
|
+
.block .bhead{display:flex; align-items:center; gap:10px; margin-bottom:8px}
|
|
72
|
+
.block .tool{font-size:11px; color:var(--indigo); font-weight:700; background:#EEF1FE; border:1px solid #DCE3FD; padding:2px 8px; border-radius:6px}
|
|
73
|
+
.block .metricline{font-size:12px; color:var(--muted); margin-top:8px; display:flex; align-items:center; gap:6px}
|
|
74
|
+
.block .metricline svg{width:12px;height:12px}
|
|
75
|
+
.fieldlabel{font-size:11px; color:var(--muted); font-weight:600; margin:0 0 4px}
|
|
76
|
+
input.title, textarea.body{width:100%; font-family:inherit; color:var(--ink); background:#FCFCFE; border:1px solid var(--line); border-radius:10px; padding:9px 11px; resize:vertical}
|
|
77
|
+
input.title{font-size:15px; font-weight:800; margin-bottom:10px}
|
|
78
|
+
textarea.body{font-size:14px; line-height:1.7; min-height:72px}
|
|
79
|
+
input.title:focus, textarea.body:focus{outline:none; border-color:var(--indigo); background:#fff; box-shadow:0 0 0 3px rgba(79,107,246,.12)}
|
|
80
|
+
input[readonly], textarea[readonly]{background:#F3F4F7; color:var(--ink-2)}
|
|
81
|
+
|
|
82
|
+
.subhead{font-size:15px; font-weight:800; margin:26px 0 10px}
|
|
83
|
+
|
|
84
|
+
.recbar{position:sticky; bottom:0; background:rgba(255,255,255,.93); backdrop-filter:blur(6px); border-top:1px solid var(--line); margin-top:24px; padding:16px 0}
|
|
85
|
+
.recbar .inner{max-width:820px; margin:0 auto; padding:0 26px; display:flex; align-items:center; gap:14px}
|
|
86
|
+
.recbar .note{font-size:12px; color:var(--muted); flex:1}
|
|
87
|
+
.btn{font:inherit; font-size:14px; font-weight:700; border-radius:10px; padding:10px 18px; cursor:pointer; border:1px solid; display:inline-flex; align-items:center; gap:8px}
|
|
88
|
+
.btn.secondary{background:#fff; color:var(--ink); border-color:var(--line)}
|
|
89
|
+
.btn.secondary:hover{background:var(--gray-bg)}
|
|
90
|
+
.btn.primary{color:#fff; border-color:transparent; background:linear-gradient(135deg,var(--indigo),var(--violet))}
|
|
91
|
+
.btn.primary:hover{filter:brightness(1.05)}
|
|
92
|
+
.btn:disabled{opacity:.55; cursor:default}
|
|
93
|
+
</style>
|
|
94
|
+
</head>
|
|
95
|
+
<body>
|
|
96
|
+
<div class="app">
|
|
97
|
+
<aside class="side">
|
|
98
|
+
<div class="brand"><span class="dot"></span>CLUBS</div>
|
|
99
|
+
<a class="nav"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12 12 2.25 21.75 12M4.5 9.75v10.5h15V9.75"/></svg>ホーム</a>
|
|
100
|
+
<a class="nav active"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M8.25 6.75h7.5M8.25 12h7.5m-7.5 5.25h4.5M4.5 3.75h15v16.5h-15z"/></svg>日報</a>
|
|
101
|
+
<a class="nav"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M15 9h3.75M15 12h3.75M15 15h3.75M4.5 19.5h15a2.25 2.25 0 0 0 2.25-2.25V6.75A2.25 2.25 0 0 0 19.5 4.5h-15A2.25 2.25 0 0 0 2.25 6.75v10.5A2.25 2.25 0 0 0 4.5 19.5m6-10.125a1.875 1.875 0 1 1-3.75 0 1.875 1.875 0 0 1 3.75 0M10.5 15a3 3 0 0 0-6 0"/></svg>パスポート</a>
|
|
102
|
+
<a class="nav"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6 6 0 0 1 0 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a7 7 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a7 7 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6 6 0 0 1 0-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124q.11-.066.22-.128c.332-.183.582-.495.644-.869z"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0"/></svg>設定</a>
|
|
103
|
+
<div class="spacer"></div>
|
|
104
|
+
<div class="me"><span class="av"></span>mayumi</div>
|
|
105
|
+
</aside>
|
|
106
|
+
|
|
107
|
+
<div class="main">
|
|
108
|
+
<div class="topbar">
|
|
109
|
+
<span class="crumb">日報 / <b id="crumbDate"></b> / 編集</span>
|
|
110
|
+
<span class="spacer"></span>
|
|
111
|
+
<span class="pill verified"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4"><path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/></svg>自動照合済み</span>
|
|
112
|
+
<span class="pill unrec" id="recPill">未記録(実績)</span>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div class="content">
|
|
116
|
+
<div class="recorded" id="recorded">
|
|
117
|
+
<div class="rc"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/></svg></div>
|
|
118
|
+
<div>
|
|
119
|
+
<div><b>記録しました。</b></div>
|
|
120
|
+
<div class="rtext">この版が実績としてパスポートに公開され、新たに証明されました。 <a id="verifyLink" href="#">検証ページを見る →</a></div>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<h1 class="h1"><span class="agent" id="agentName"></span> の日報</h1>
|
|
125
|
+
<div class="daterow" id="dateRow"></div>
|
|
126
|
+
|
|
127
|
+
<p class="section-label">きょうの集計 <span class="lock"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75M6 10.5h12a1.5 1.5 0 0 1 1.5 1.5v6A1.5 1.5 0 0 1 18 19.5H6A1.5 1.5 0 0 1 4.5 18v-6A1.5 1.5 0 0 1 6 10.5"/></svg>ログから自動集計・編集不可</span></p>
|
|
128
|
+
<div class="metrics" id="metrics"></div>
|
|
129
|
+
|
|
130
|
+
<p class="section-label">本日の業務 <span class="edhint">✎ 文章は編集できます(作業記録なので、整えてから記録できます)</span></p>
|
|
131
|
+
<div id="blocks"></div>
|
|
132
|
+
|
|
133
|
+
<div class="subhead">進捗と申し送り</div>
|
|
134
|
+
<div class="block"><div class="fieldlabel">明日にまわすもの</div><textarea class="body" id="handoff"></textarea></div>
|
|
135
|
+
|
|
136
|
+
<div class="subhead" id="selfNoteHead"></div>
|
|
137
|
+
<div class="block"><textarea class="body" id="selfNote"></textarea></div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<div class="recbar">
|
|
141
|
+
<div class="inner">
|
|
142
|
+
<span class="note">集計の数字は変更できません。<b>記録する</b>と、編集した本文が実績としてパスポートに公開され、その版が新たに証明されます。</span>
|
|
143
|
+
<button class="btn secondary" id="passportBtn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12s3.75-7.5 9.75-7.5S21.75 12 21.75 12s-3.75 7.5-9.75 7.5S2.25 12 2.25 12"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0"/></svg>パスポートを見る</button>
|
|
144
|
+
<button class="btn primary" id="recBtn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/></svg>記録する</button>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<script>
|
|
151
|
+
/* ■ 差し替え:記録エンドポイント(編集版を登録+パスポート公開)
|
|
152
|
+
async function recordReport(id, edited){
|
|
153
|
+
const r = await fetch(`/reports/${id}/record`, {method:"POST",
|
|
154
|
+
headers:{'Content-Type':'application/json'}, body: JSON.stringify(edited)});
|
|
155
|
+
return await r.json(); // { status:"recorded", docHash, verifyUrl }
|
|
156
|
+
}
|
|
157
|
+
※ 数字(aggregates)は送らない。サーバが下書きのロック値を使う(盛れない)。
|
|
158
|
+
※ 記録は「編集で内容が変わったときに新しい証明」を発行。自動✓(生の日報)は別途登録済み。 */
|
|
159
|
+
async function recordReport(id, edited){
|
|
160
|
+
await new Promise(r=>setTimeout(r,500));
|
|
161
|
+
return { status:"recorded",
|
|
162
|
+
docHash:"0x7c40aa19d8...e2b1",
|
|
163
|
+
verifyUrl:"/verify?doc=0x7c40aa19d8...e2b1" };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function getDraft(id){ return MOCK_DRAFT; }
|
|
167
|
+
let DRAFT;
|
|
168
|
+
|
|
169
|
+
const MOCK_DRAFT = {
|
|
170
|
+
id:"2026-07-13", agentName:"mayumi's Claude", date:"2026-07-13",
|
|
171
|
+
aggregates:{ activeHours:2.3, totalCalls:131, successRate:98, failedCalls:3 },
|
|
172
|
+
workBlocks:[
|
|
173
|
+
{ title:"x402 エコシステムの週次ウォッチ", tool:"WebSearch / ウェブ取得 / Write",
|
|
174
|
+
text:"毎週月曜の定常タスクとして、x402 まわりの動きと Trust402 のアップセル兆候を観測しました。ウェブ検索とページ取得で材料を集め、週次レポートの下書きを作成してオーナーに提示するところまで進めています。",
|
|
175
|
+
metric:"実行 15回 · ウェブ検索 7 / ページ取得 3 / 下書き作成 1" },
|
|
176
|
+
{ title:"Lemma 公式 X の今週の投稿案", tool:"ウェブ取得 / Read / Write",
|
|
177
|
+
text:"Lemma 公式 X の今週の投稿案を2本、下書きしました。ブリーフ・ブログ・前回投稿案を参照し、AI攻撃実験LPへの誘導と Lemma Critical の2イベントを均等に押す構成にしています。",
|
|
178
|
+
metric:"実行 6回 · 投稿案 2本 · 出力ファイル 1" },
|
|
179
|
+
{ title:"営業パイプラインのライト確認", tool:"カレンダー / スプレッドシート / Write",
|
|
180
|
+
text:"営業活動状況をライトに確認してレポート化しました。カレンダーから営業関連 MTG を抽出し、スプレッドシートと過去レポートに突き合わせ、予定登録とパイプライン更新まで行っています。",
|
|
181
|
+
metric:"実行 47回 · 編集 15 / 予定登録 2・更新 2 / 確認質問 1" },
|
|
182
|
+
{ title:"FRAME00 × Lemma の事業プランニング", tool:"Google ドライブ / Read / Write / Edit",
|
|
183
|
+
text:"事業プラン v2 と関連ドキュメントをドライブ横断で洗い出し、読み込んだうえで更新しました。マーケティング方針などの論点を反映しながら書き換え、「今週のプランニングリスト」にまとめています。",
|
|
184
|
+
metric:"実行 49回 · 編集 15 / 新規作成 7 / ドライブ検索 7" },
|
|
185
|
+
{ title:"CLUBS デザインシステムの置き場所調査", tool:"Read / Bash(GitHub 調査)",
|
|
186
|
+
text:"デザインシステム v1 の Markdown を受け取り、GitHub に上げる置き場所を確認しました。リポジトリの公開状態・構成・トークン定義・モックの有無を調べています。",
|
|
187
|
+
metric:"実行 5回 · リポジトリ調査 4 / ファイル読み取り 1" },
|
|
188
|
+
{ title:"デザインシステムのリポジトリ反映", tool:"Bash(git)/ Read / Edit",
|
|
189
|
+
text:"docs/ にドキュメントを追加し、README にも追記して main に直接プッシュしました。反映を確認し、その後 CTO 指摘を受けてプルリクエスト状態を再確認しています。",
|
|
190
|
+
metric:"実行 9回 · main へ push 1 / README 編集 1 / 検証コマンド 7" }
|
|
191
|
+
],
|
|
192
|
+
handoff:"3点あります。(1) メタウォーター会長の CLUBS for PUBLIC 継続懸念を踏まえたプレスの構成づくり。(2) DNP 向けサービス紹介資料のスライド詳細(方向性は合意)。(3) CTO からの証明API 進捗共有は「そのまま放置」の指示のため未着手。要否をご判断ください。",
|
|
193
|
+
selfNote:"月曜の定例4本に加えて、午後は CLUBS デザインシステムの反映まで進められました。証明APIの共有だけ手つかずですので、扱いをご指示いただけると助かります。"
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const $=(id)=>document.getElementById(id);
|
|
197
|
+
const svgIco=(d)=>`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">${d}</svg>`;
|
|
198
|
+
const LOCK='<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75M6 10.5h12a1.5 1.5 0 0 1 1.5 1.5v6A1.5 1.5 0 0 1 18 19.5H6A1.5 1.5 0 0 1 4.5 18v-6A1.5 1.5 0 0 1 6 10.5"/>';
|
|
199
|
+
const CHECK='<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/>';
|
|
200
|
+
|
|
201
|
+
function fillLocked(){
|
|
202
|
+
$("crumbDate").textContent = DRAFT.date;
|
|
203
|
+
$("agentName").textContent = DRAFT.agentName;
|
|
204
|
+
$("dateRow").textContent = `${DRAFT.date}(きのう分)・下書きは ${DRAFT.agentName} が作成しました`;
|
|
205
|
+
$("selfNoteHead").textContent = `${DRAFT.agentName} のひとこと`;
|
|
206
|
+
const a=DRAFT.aggregates;
|
|
207
|
+
$("metrics").innerHTML = [
|
|
208
|
+
["実働推定", `${a.activeHours}<small> 時間</small>`],
|
|
209
|
+
["実行回数", `${a.totalCalls}<small> 回</small>`],
|
|
210
|
+
["成功率", `${a.successRate}<small> %</small>`],
|
|
211
|
+
["失敗", `${a.failedCalls}<small> 件</small>`]
|
|
212
|
+
].map(([k,v])=>`<div class="metric"><div class="mk">${k}</div><div class="mv">${v}</div></div>`).join("");
|
|
213
|
+
}
|
|
214
|
+
function buildEditable(){
|
|
215
|
+
$("blocks").innerHTML = DRAFT.workBlocks.map((b,i)=>`
|
|
216
|
+
<div class="block">
|
|
217
|
+
<div class="bhead"><span class="tool">${b.tool}</span></div>
|
|
218
|
+
<div class="fieldlabel">案件名</div>
|
|
219
|
+
<input class="title" data-i="${i}" value="${b.title.replace(/"/g,'"')}" />
|
|
220
|
+
<div class="fieldlabel">内容</div>
|
|
221
|
+
<textarea class="body" data-i="${i}">${b.text}</textarea>
|
|
222
|
+
<div class="metricline">${svgIco(LOCK)}${b.metric}(自動集計・編集不可)</div>
|
|
223
|
+
</div>`).join("");
|
|
224
|
+
$("handoff").value = DRAFT.handoff;
|
|
225
|
+
$("selfNote").value = DRAFT.selfNote;
|
|
226
|
+
}
|
|
227
|
+
function collectEdited(){
|
|
228
|
+
const blocks = DRAFT.workBlocks.map((b,i)=>({
|
|
229
|
+
title: document.querySelector(`input.title[data-i="${i}"]`).value,
|
|
230
|
+
text: document.querySelector(`textarea.body[data-i="${i}"]`).value
|
|
231
|
+
}));
|
|
232
|
+
return { workBlocks:blocks, handoff:$("handoff").value, selfNote:$("selfNote").value };
|
|
233
|
+
}
|
|
234
|
+
function setRecorded(res){
|
|
235
|
+
$("recPill").textContent = "記録済み(実績)";
|
|
236
|
+
$("recPill").className = "pill verified";
|
|
237
|
+
$("recPill").innerHTML = svgIco(CHECK)+"記録済み(実績)";
|
|
238
|
+
$("verifyLink").href = res.verifyUrl || "#";
|
|
239
|
+
$("recorded").classList.add("show");
|
|
240
|
+
document.querySelectorAll("input.title,textarea.body").forEach(el=>el.setAttribute("readonly","true"));
|
|
241
|
+
const b=$("recBtn"); b.disabled=true; b.innerHTML=svgIco(CHECK)+"記録済み";
|
|
242
|
+
window.scrollTo({top:0,behavior:"smooth"});
|
|
243
|
+
}
|
|
244
|
+
$("recBtn").onclick = async ()=>{
|
|
245
|
+
const b=$("recBtn"); b.disabled=true; b.textContent="記録中…";
|
|
246
|
+
try{
|
|
247
|
+
const res = await recordReport(DRAFT.id, collectEdited());
|
|
248
|
+
if(res && res.status==="recorded") setRecorded(res);
|
|
249
|
+
else { b.disabled=false; b.innerHTML=svgIco(CHECK)+"記録する"; }
|
|
250
|
+
}catch(e){ b.disabled=false; b.innerHTML=svgIco(CHECK)+"記録する"; }
|
|
251
|
+
};
|
|
252
|
+
$("passportBtn").onclick = ()=>{ location.href="/hermes"; };
|
|
253
|
+
|
|
254
|
+
(async ()=>{ DRAFT = await getDraft("2026-07-13"); fillLocked(); buildEditable(); })();
|
|
255
|
+
</script>
|
|
256
|
+
</body>
|
|
257
|
+
</html>
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="ja">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>日報 — CLUBS Console</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Noto+Sans+JP:wght@400;500;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet" />
|
|
10
|
+
<style>
|
|
11
|
+
:root{
|
|
12
|
+
--ink:#161A26; --ink-2:#3A4155; --muted:#6B7280;
|
|
13
|
+
--line:#E7E9F2; --bg:#F6F7FB; --card:#FFFFFF;
|
|
14
|
+
--sidebar:#161A26; --sidebar-ink:#AEB4C6; --sidebar-active:#232838;
|
|
15
|
+
--indigo:#4F6BF6; --violet:#8B5CF6;
|
|
16
|
+
--green:#0A8F66; --green-2:#17A45F; --green-bg:#E7F5EF; --green-line:#BFE6D6;
|
|
17
|
+
--amber-ink:#9A6A00; --amber-bg:#FFF6E5; --amber-line:#F5E4BC;
|
|
18
|
+
--gray-bg:#F3F4F6; --gray-line:#E5E7EB;
|
|
19
|
+
--radius:14px;
|
|
20
|
+
}
|
|
21
|
+
*{box-sizing:border-box}
|
|
22
|
+
body{margin:0; background:var(--bg); color:var(--ink);
|
|
23
|
+
font-family:"Inter","Noto Sans JP",system-ui,-apple-system,"Segoe UI",sans-serif;
|
|
24
|
+
line-height:1.6; -webkit-font-smoothing:antialiased}
|
|
25
|
+
svg{width:18px; height:18px}
|
|
26
|
+
.app{display:flex; min-height:100vh}
|
|
27
|
+
|
|
28
|
+
/* sidebar */
|
|
29
|
+
|
|
30
|
+
.main{flex:1; min-width:0; display:flex; flex-direction:column}
|
|
31
|
+
.topbar{height:60px; background:#fff; border-bottom:1px solid var(--line); display:flex; align-items:center; gap:12px; padding:0 26px}
|
|
32
|
+
.topbar .crumb{font-size:13px; color:var(--muted)} .topbar .crumb b{color:var(--ink)}
|
|
33
|
+
.content{padding:26px; max-width:900px; width:100%; margin:0 auto}
|
|
34
|
+
|
|
35
|
+
.head{display:flex; align-items:flex-end; justify-content:space-between; gap:12px; margin-bottom:18px}
|
|
36
|
+
.h1{font-size:22px; font-weight:700; margin:0}
|
|
37
|
+
.head .sub{font-size:13px; color:var(--muted); margin-top:3px}
|
|
38
|
+
.agentsel{font-size:13px; color:var(--ink-2); background:#fff; border:1px solid var(--line);
|
|
39
|
+
border-radius:10px; padding:8px 12px; display:inline-flex; align-items:center; gap:8px}
|
|
40
|
+
.agentsel .av{width:18px; height:18px; border-radius:50%; background:linear-gradient(135deg,#8B5CF6,#4F6BF6)}
|
|
41
|
+
|
|
42
|
+
/* 今月ストリップ */
|
|
43
|
+
.month{display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:22px}
|
|
44
|
+
@media(max-width:640px){.month{grid-template-columns:repeat(2,1fr)}}
|
|
45
|
+
.mcard{background:var(--card); border:1px solid var(--line); border-radius:var(--radius); padding:14px 16px}
|
|
46
|
+
.mcard .k{font-size:11px; color:var(--muted); font-weight:600}
|
|
47
|
+
.mcard .v{font-size:21px; font-weight:700; margin-top:2px}
|
|
48
|
+
.mcard .v small{font-size:12px; color:var(--muted); font-weight:600}
|
|
49
|
+
.mcard .v .flame{color:#E8730C}
|
|
50
|
+
|
|
51
|
+
.listlabel{font-size:12px; font-weight:600; color:var(--muted); margin:0 0 10px}
|
|
52
|
+
|
|
53
|
+
/* 行 */
|
|
54
|
+
.row{display:flex; align-items:center; gap:16px; background:var(--card); border:1px solid var(--line);
|
|
55
|
+
border-radius:var(--radius); padding:15px 18px; margin-bottom:10px; cursor:pointer; text-decoration:none; color:inherit}
|
|
56
|
+
.row:hover{border-color:#CFD6EE; box-shadow:0 2px 10px rgba(20,26,38,.05)}
|
|
57
|
+
.row .date{width:96px; flex:none}
|
|
58
|
+
.row .date .d{font-size:15px; font-weight:700}
|
|
59
|
+
.row .date .w{font-size:11px; color:var(--muted)}
|
|
60
|
+
.row .mid{flex:1; min-width:0}
|
|
61
|
+
.row .agent{font-size:13px; color:var(--ink-2); font-weight:600}
|
|
62
|
+
.row .metrics{font-size:12.5px; color:var(--muted); margin-top:2px}
|
|
63
|
+
.row .metrics b{color:var(--ink-2); font-weight:600}
|
|
64
|
+
.badge{display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:600;
|
|
65
|
+
padding:5px 11px; border-radius:999px; border:1px solid; white-space:nowrap}
|
|
66
|
+
.badge svg{width:13px; height:13px}
|
|
67
|
+
.badge.recorded{color:var(--green); background:var(--green-bg); border-color:var(--green-line)}
|
|
68
|
+
.badge.anchored{color:var(--green-2); background:var(--green-bg); border-color:var(--green-line)}
|
|
69
|
+
.badge.pending{color:var(--muted); background:var(--gray-bg); border-color:var(--gray-line)}
|
|
70
|
+
.badge.draft{color:var(--amber-ink); background:var(--amber-bg); border-color:var(--amber-line)}
|
|
71
|
+
.row .chev{color:#B7BECF; flex:none}
|
|
72
|
+
|
|
73
|
+
.monthly{margin-top:20px; display:flex; align-items:center; gap:12px; background:#fff;
|
|
74
|
+
border:1px dashed #D7DDF3; border-radius:var(--radius); padding:14px 18px; font-size:13px; color:var(--ink-2)}
|
|
75
|
+
.monthly svg{width:18px; height:18px; color:var(--indigo); flex:none}
|
|
76
|
+
.monthly b{color:var(--ink)}
|
|
77
|
+
.monthly .tag{margin-left:auto; font-size:11px; font-weight:600; color:var(--indigo);
|
|
78
|
+
background:#EEF1FE; border:1px solid #DCE3FD; padding:3px 10px; border-radius:999px}
|
|
79
|
+
</style>
|
|
80
|
+
</head>
|
|
81
|
+
<body>
|
|
82
|
+
<div class="app">
|
|
83
|
+
<aside></aside>
|
|
84
|
+
|
|
85
|
+
<div class="main">
|
|
86
|
+
<div class="topbar"><span class="crumb"><b>日報</b></span></div>
|
|
87
|
+
<div class="content">
|
|
88
|
+
<div class="head">
|
|
89
|
+
<div>
|
|
90
|
+
<h1 class="h1">日報</h1>
|
|
91
|
+
<div class="sub">あなたのAIが日々の作業を報告します。数字はログから自動集計・編集できません。</div>
|
|
92
|
+
</div>
|
|
93
|
+
<span class="agentsel"><span class="av"></span><span id="agentName">—</span> ▾</span>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="month" id="month"></div>
|
|
97
|
+
|
|
98
|
+
<p class="listlabel">これまでの日報</p>
|
|
99
|
+
<div id="list"></div>
|
|
100
|
+
|
|
101
|
+
<div class="monthly">
|
|
102
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.69a4.5 4.5 0 0 1 0 6.36l-3 3a4.5 4.5 0 0 1-6.36-6.36l1.5-1.5M10.81 15.31a4.5 4.5 0 0 1 0-6.36l3-3a4.5 4.5 0 0 1 6.36 6.36l-1.5 1.5"/></svg>
|
|
103
|
+
<span><b>月次レポート</b>:その月の日報をまとめ、オンチェーンに記録して社外に出せる証明書にできます。</span>
|
|
104
|
+
<span class="tag">有償プラン</span>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<script>
|
|
111
|
+
/* ============================================================
|
|
112
|
+
■ 差し替え箇所 : 一覧・累計の供給元
|
|
113
|
+
実装では GET /reports?agent=... から読む。返り値は
|
|
114
|
+
{ reports:[...], month:{...} } の形。静的配信では preview.json(実データのスナップショット)に落ちる。
|
|
115
|
+
============================================================ */
|
|
116
|
+
/* API があればライブ。無ければ preview.json(実データのスナップショット)。
|
|
117
|
+
モックは持たない。共有URLに作り話の数字を出さないため。 */
|
|
118
|
+
async function getReports(){
|
|
119
|
+
try{
|
|
120
|
+
const r = await fetch("/api/reports");
|
|
121
|
+
if (r.ok) return { ...(await r.json()), live: true };
|
|
122
|
+
}catch{}
|
|
123
|
+
try{
|
|
124
|
+
const r = await fetch("../preview.json");
|
|
125
|
+
if (r.ok) {
|
|
126
|
+
const s = await r.json();
|
|
127
|
+
return { reports: s.reports, month: s.month, live: false, snapshotAt: s.snapshotAt };
|
|
128
|
+
}
|
|
129
|
+
}catch{}
|
|
130
|
+
return { reports: [], month: { streak:0, approved:0, activeHours:0, cards:0 }, live: true };
|
|
131
|
+
}
|
|
132
|
+
/* 静的配信ではライブのデータが無い。作り話を出さず、スナップショットであることを必ず明示する。 */
|
|
133
|
+
function snapshotBanner(at){
|
|
134
|
+
const el = document.createElement("div");
|
|
135
|
+
el.style.cssText = "position:sticky;top:0;z-index:99;background:#F7EFDD;color:#8A6520;font-size:12px;font-weight:700;padding:8px 14px;text-align:center;border-bottom:1px solid #E6D6BF";
|
|
136
|
+
el.textContent = `スナップショット(${at} 時点)。サーバに繋がっていないため、承認や保存はできません。`;
|
|
137
|
+
document.body.prepend(el);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
/* status: "draft"(下書き未承認) | "recorded"(承認・記録済み・オフチェーン)
|
|
142
|
+
| "anchored"(オンチェーン) | "pending"(登録待ち) */
|
|
143
|
+
|
|
144
|
+
const svgIco=(d)=>`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">${d}</svg>`;
|
|
145
|
+
const ICO={
|
|
146
|
+
check:'<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/>',
|
|
147
|
+
link:'<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.69a4.5 4.5 0 0 1 0 6.36l-3 3a4.5 4.5 0 0 1-6.36-6.36l1.5-1.5M10.81 15.31a4.5 4.5 0 0 1 0-6.36l3-3a4.5 4.5 0 0 1 6.36 6.36l-1.5 1.5"/>',
|
|
148
|
+
clock:'<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6l4 2M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>',
|
|
149
|
+
pencil:'<path stroke-linecap="round" stroke-linejoin="round" d="m16.86 4.49 2.65 2.65M3 21l.53-3.9a2 2 0 0 1 .56-1.13L15.3 4.7a1.5 1.5 0 0 1 2.12 0l1.88 1.88a1.5 1.5 0 0 1 0 2.12L8.03 19.9a2 2 0 0 1-1.13.56z"/>',
|
|
150
|
+
chev:'<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5"/>'
|
|
151
|
+
};
|
|
152
|
+
const BADGE={
|
|
153
|
+
draft:{cls:"draft", icon:ICO.pencil, label:"下書き・未承認"},
|
|
154
|
+
recorded:{cls:"recorded", icon:ICO.check, label:"承認・記録済み"},
|
|
155
|
+
anchored:{cls:"anchored", icon:ICO.link, label:"オンチェーン記録済み"},
|
|
156
|
+
pending:{cls:"pending", icon:ICO.clock, label:"照合予定"}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
function render({ reports, month }){
|
|
160
|
+
document.getElementById("month").innerHTML = [
|
|
161
|
+
["連続日数", `<span class="flame">🔥</span>${month.streak}<small> 日</small>`],
|
|
162
|
+
["承認済み", `${month.approved}<small> 本</small>`],
|
|
163
|
+
["実働(今月)", `${month.activeHours}<small> 時間</small>`],
|
|
164
|
+
["達成カード", `${month.cards}<small> 枚</small>`]
|
|
165
|
+
].map(([k,v])=>`<div class="mcard"><div class="k">${k}</div><div class="v">${v}</div></div>`).join("");
|
|
166
|
+
|
|
167
|
+
document.getElementById("list").innerHTML = reports.map(r=>{
|
|
168
|
+
const b=BADGE[r.status];
|
|
169
|
+
return `<a class="row" href="${r.href}">
|
|
170
|
+
<div class="date"><div class="d">${r.date.slice(5).replace("-","/")}</div><div class="w">${r.w}曜</div></div>
|
|
171
|
+
<div class="mid">
|
|
172
|
+
<div class="agent">${r.agent}</div>
|
|
173
|
+
<div class="metrics">実働 <b>${r.activeHours}h</b> ・ 実行 <b>${r.totalCalls}</b> ・ 成功率 <b>${r.successRate}%</b></div>
|
|
174
|
+
</div>
|
|
175
|
+
<span class="badge ${b.cls}">${svgIco(b.icon)}${b.label}</span>
|
|
176
|
+
<span class="chev">${svgIco(ICO.chev)}</span>
|
|
177
|
+
</a>`;
|
|
178
|
+
}).join("");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
(async ()=>{
|
|
182
|
+
const data = await getReports();
|
|
183
|
+
if (data.live === false) snapshotBanner(data.snapshotAt);
|
|
184
|
+
// エージェント名は一覧の実データから取る。固定値を出すと、誰の日報か嘘になる。
|
|
185
|
+
const agent = data.reports[0]?.agent;
|
|
186
|
+
const chip = document.getElementById("agentName");
|
|
187
|
+
if (chip) chip.textContent = agent ?? "—";
|
|
188
|
+
render(data);
|
|
189
|
+
})();
|
|
190
|
+
</script>
|
|
191
|
+
<script type="module">
|
|
192
|
+
import { mountSidebar } from "../assets/sidebar.js";
|
|
193
|
+
mountSidebar("reports");
|
|
194
|
+
</script>
|
|
195
|
+
</body>
|
|
196
|
+
</html>
|