@chenpengfei/daily-brief 0.1.0
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/CHANGELOG.md +26 -0
- package/LICENSE +21 -0
- package/README.md +28 -0
- package/config/sources.example.yaml +20 -0
- package/dist/src/adapters/fixture.js +70 -0
- package/dist/src/adapters/github-trending.js +183 -0
- package/dist/src/adapters/index.js +5 -0
- package/dist/src/adapters/rss.js +156 -0
- package/dist/src/adapters/types.js +1 -0
- package/dist/src/adapters/x.js +115 -0
- package/dist/src/agent/daily-brief-agent.js +350 -0
- package/dist/src/agent/index.js +10 -0
- package/dist/src/agent/model-runtime-config.js +221 -0
- package/dist/src/agent/model-stage-runtime.js +63 -0
- package/dist/src/agent/signal-narrative.js +247 -0
- package/dist/src/agent/signal-selection-ranking.js +276 -0
- package/dist/src/agent/source-grounding-audit.js +148 -0
- package/dist/src/agent/source-grounding-repair.js +159 -0
- package/dist/src/agent/source-item-understanding.js +206 -0
- package/dist/src/agent/stage-contracts.js +205 -0
- package/dist/src/agent/stage-runner.js +66 -0
- package/dist/src/brief/daily-brief.js +234 -0
- package/dist/src/brief/index.js +1 -0
- package/dist/src/cli.js +531 -0
- package/dist/src/collection/collect.js +67 -0
- package/dist/src/collection/index.js +1 -0
- package/dist/src/config/credential-store.js +169 -0
- package/dist/src/config/date-key.js +25 -0
- package/dist/src/config/index.js +5 -0
- package/dist/src/config/model-config.js +123 -0
- package/dist/src/config/paths.js +20 -0
- package/dist/src/config/source-registry.js +48 -0
- package/dist/src/discord/delivery.js +84 -0
- package/dist/src/discord/index.js +1 -0
- package/dist/src/domain/index.js +2 -0
- package/dist/src/domain/source-item.js +21 -0
- package/dist/src/domain/source.js +93 -0
- package/dist/src/storage/agent-run-artifact.js +44 -0
- package/dist/src/storage/brief-archive.js +17 -0
- package/dist/src/storage/index.js +3 -0
- package/dist/src/storage/source-item-store.js +63 -0
- package/dist/src/workflow/index.js +1 -0
- package/dist/src/workflow/status.js +95 -0
- package/docs/operations.md +74 -0
- package/docs/release-workflow.md +220 -0
- package/docs/user-manual.md +146 -0
- package/package.json +65 -0
- package/templates/daily-brief.md +9 -0
- package/templates/discord-notification.md +7 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { formatDateKey, resolveDailyBriefPaths } from "../config/index.js";
|
|
4
|
+
export function sourceItemStorePath(date, root = resolveDailyBriefPaths().sourceItemRoot, dateKey) {
|
|
5
|
+
const datePart = dateKey ?? formatDateKey(date);
|
|
6
|
+
const [year, month] = datePart.split("-");
|
|
7
|
+
if (!year || !month) {
|
|
8
|
+
throw new Error(`Invalid Source Item Store date: ${datePart}`);
|
|
9
|
+
}
|
|
10
|
+
return join(root, year, month, `${datePart}.jsonl`);
|
|
11
|
+
}
|
|
12
|
+
export async function appendSourceItems(items, date, root = resolveDailyBriefPaths().sourceItemRoot, dateKey) {
|
|
13
|
+
const path = sourceItemStorePath(date, root, dateKey);
|
|
14
|
+
const existing = await readSourceItems(date, root, dateKey);
|
|
15
|
+
const seenIds = new Set(existing.map((item) => item.id));
|
|
16
|
+
const seenHashes = new Set(existing.map((item) => item.contentHash));
|
|
17
|
+
const written = [];
|
|
18
|
+
const skipped = [];
|
|
19
|
+
for (const item of items) {
|
|
20
|
+
if (seenIds.has(item.id) || seenHashes.has(item.contentHash)) {
|
|
21
|
+
skipped.push(item);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
seenIds.add(item.id);
|
|
25
|
+
seenHashes.add(item.contentHash);
|
|
26
|
+
written.push(item);
|
|
27
|
+
}
|
|
28
|
+
if (written.length > 0) {
|
|
29
|
+
await mkdir(dirname(path), { recursive: true });
|
|
30
|
+
const next = [...existing, ...written].map((item) => JSON.stringify(item)).join("\n");
|
|
31
|
+
await writeFile(path, `${next}\n`, "utf8");
|
|
32
|
+
}
|
|
33
|
+
return { path, written, skipped };
|
|
34
|
+
}
|
|
35
|
+
export async function readSourceItems(date, root = resolveDailyBriefPaths().sourceItemRoot, dateKey) {
|
|
36
|
+
const path = sourceItemStorePath(date, root, dateKey);
|
|
37
|
+
let contents;
|
|
38
|
+
try {
|
|
39
|
+
contents = await readFile(path, "utf8");
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
return contents
|
|
48
|
+
.split("\n")
|
|
49
|
+
.filter((line) => line.trim().length > 0)
|
|
50
|
+
.map((line, index) => parseSourceItemLine(line, path, index + 1));
|
|
51
|
+
}
|
|
52
|
+
function isNodeError(error) {
|
|
53
|
+
return error instanceof Error && "code" in error;
|
|
54
|
+
}
|
|
55
|
+
function parseSourceItemLine(line, path, lineNumber) {
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(line);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
const cause = error instanceof Error ? `: ${error.message}` : "";
|
|
61
|
+
throw new Error(`${path} line ${lineNumber} contains malformed Source Item JSON${cause}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./status.js";
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { loadSourceRegistry, resolveDailyBriefPaths } from "../config/index.js";
|
|
4
|
+
export function evaluateWorkflowStatus(input) {
|
|
5
|
+
if (input.coreFailure) {
|
|
6
|
+
return {
|
|
7
|
+
health: "core-failure",
|
|
8
|
+
message: input.coreFailure.message,
|
|
9
|
+
materialPartialFailures: [],
|
|
10
|
+
coreFailure: input.coreFailure
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
if (!input.briefGenerated) {
|
|
14
|
+
return {
|
|
15
|
+
health: "core-failure",
|
|
16
|
+
message: "Daily Brief generation did not produce an archiveable Brief.",
|
|
17
|
+
materialPartialFailures: [],
|
|
18
|
+
coreFailure: {
|
|
19
|
+
kind: "brief-generation-unavailable",
|
|
20
|
+
message: "Daily Brief generation did not produce an archiveable Brief."
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const materialPartialFailures = input.collectionResults
|
|
25
|
+
.filter((result) => result.status === "failed")
|
|
26
|
+
.map((result) => `${result.sourceId}: ${result.reason ?? "Unknown failure"}`)
|
|
27
|
+
.filter(isMaterialPartialFailure);
|
|
28
|
+
if (materialPartialFailures.length > 0) {
|
|
29
|
+
return {
|
|
30
|
+
health: "partial-failure",
|
|
31
|
+
message: "Daily Brief generated with material Source Coverage gaps.",
|
|
32
|
+
materialPartialFailures
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
health: "success",
|
|
37
|
+
message: "Daily Brief workflow completed successfully.",
|
|
38
|
+
materialPartialFailures: []
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function createCoreWorkflowFailureNotification(failure) {
|
|
42
|
+
return [
|
|
43
|
+
"Daily Brief failed",
|
|
44
|
+
"",
|
|
45
|
+
`Core Workflow Failure: ${failure.kind}`,
|
|
46
|
+
failure.message,
|
|
47
|
+
"",
|
|
48
|
+
"No Daily Brief was generated, because sending a false or unsupported Brief would break trust."
|
|
49
|
+
].join("\n");
|
|
50
|
+
}
|
|
51
|
+
export async function getOperationalStatus(options = {}) {
|
|
52
|
+
const date = options.date ?? new Date();
|
|
53
|
+
try {
|
|
54
|
+
await loadSourceRegistry(options.sourceRegistryPath);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
return evaluateWorkflowStatus({
|
|
58
|
+
collectionResults: [],
|
|
59
|
+
briefGenerated: false,
|
|
60
|
+
coreFailure: {
|
|
61
|
+
kind: "unreadable-source-registry",
|
|
62
|
+
message: error instanceof Error ? error.message : String(error)
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const archivePath = briefArchivePath(date, options.archiveRoot ?? resolveDailyBriefPaths().briefArchiveRoot, options.dateKey);
|
|
67
|
+
try {
|
|
68
|
+
await readFile(archivePath, "utf8");
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return {
|
|
72
|
+
health: "partial-failure",
|
|
73
|
+
message: `No Daily Brief archived for ${options.dateKey ?? date.toISOString().slice(0, 10)} yet.`,
|
|
74
|
+
materialPartialFailures: []
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
health: "success",
|
|
79
|
+
message: `Daily Brief archive exists for ${options.dateKey ?? date.toISOString().slice(0, 10)}.`,
|
|
80
|
+
materialPartialFailures: []
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function isMaterialPartialFailure(message) {
|
|
84
|
+
const normalized = message.toLowerCase();
|
|
85
|
+
const nonMaterialPatterns = ["missing transcript", "transcript missing", "rate limit", "parse failure"];
|
|
86
|
+
return !nonMaterialPatterns.some((pattern) => normalized.includes(pattern));
|
|
87
|
+
}
|
|
88
|
+
function briefArchivePath(date, root, dateKey) {
|
|
89
|
+
const datePart = dateKey ?? date.toISOString().slice(0, 10);
|
|
90
|
+
const [year, month] = datePart.split("-");
|
|
91
|
+
if (!year || !month) {
|
|
92
|
+
throw new Error(`Invalid archive date: ${datePart}`);
|
|
93
|
+
}
|
|
94
|
+
return join(root, year, month, `${datePart}.md`);
|
|
95
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Daily Brief Operations
|
|
2
|
+
|
|
3
|
+
For end-user installation, setup, upgrade, and troubleshooting, see `docs/user-manual.md`. For maintainer release gates and publication steps, see `docs/release-workflow.md`.
|
|
4
|
+
|
|
5
|
+
Installed usage uses the `daily-brief` binary:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
daily-brief setup
|
|
9
|
+
daily-brief run-once --date 2026-05-28
|
|
10
|
+
daily-brief status
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Development usage from a repository checkout uses `npm run cli --`:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm run cli -- collect
|
|
17
|
+
npm run cli -- generate
|
|
18
|
+
npm run cli -- deliver
|
|
19
|
+
npm run cli -- status
|
|
20
|
+
npm run cli -- run-once
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Expected local cadence:
|
|
24
|
+
|
|
25
|
+
- 06:00 local time: run `collect`.
|
|
26
|
+
- 07:00 local time: run `run-once` or `generate` followed by `deliver`.
|
|
27
|
+
|
|
28
|
+
Scheduler integration is intentionally deployment-neutral. A local cron, launchd job, systemd timer, GitHub Actions workflow, or another scheduler can call the commands above; the repository does not bind the MVP to a specific host.
|
|
29
|
+
|
|
30
|
+
`run-once` executes collection, brief generation/archive, and Discord delivery in order. Source Item writes are deduplicated by Source Item id and content hash within the daily JSONL store, and Daily Brief generation merges repeated mentions into one multi-citation Signal, so rerunning the workflow for the same Collection Window does not duplicate equivalent Signals.
|
|
31
|
+
|
|
32
|
+
## Manual run
|
|
33
|
+
|
|
34
|
+
For a one-off Manual Run against the configured Sources:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm run cli -- sources list
|
|
38
|
+
npm run cli -- run-once
|
|
39
|
+
npm run cli -- status
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`sources list` confirms which Sources are enabled, including the default `github-trending-daily` Source. `run-once` performs collection, brief generation, archive writing, and Discord Delivery once. `status` reports operational health after the run.
|
|
43
|
+
|
|
44
|
+
Discord Delivery uses `DISCORD_WEBHOOK_URL` from the shell environment or local `.env`; `.env` is loaded automatically by the Operational CLI and does not need to be sourced manually.
|
|
45
|
+
|
|
46
|
+
## Runtime configuration
|
|
47
|
+
|
|
48
|
+
When the Operational CLI starts, it loads local `.env` values from the repository root if the file exists. Existing shell environment variables take precedence over `.env` values. The `.env` file is ignored by Git; use `.env.example` as the non-secret template.
|
|
49
|
+
|
|
50
|
+
Installed operational paths can be adjusted through environment variables:
|
|
51
|
+
|
|
52
|
+
- `DAILY_BRIEF_HOME`: user config directory. Defaults to `~/.daily-brief`.
|
|
53
|
+
- `DAILY_BRIEF_DATA_HOME`: generated data directory. Defaults to `~/.daily-brief/data`.
|
|
54
|
+
- `DAILY_BRIEF_DISCORD_TEMPLATE_PATH`: optional Discord notification template override. Defaults to the packaged Discord notification template.
|
|
55
|
+
- `DISCORD_WEBHOOK_URL`: Discord webhook URL. If unset, Discord Delivery is skipped with an explicit reason.
|
|
56
|
+
|
|
57
|
+
Model/provider and delivery configuration should normally be managed with:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
daily-brief model configure
|
|
61
|
+
daily-brief model status
|
|
62
|
+
daily-brief delivery configure --enabled true --webhook-url <url>
|
|
63
|
+
daily-brief delivery status
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Secrets live in `~/.daily-brief/auth.json` or environment variables, never in `sources.yaml` or committed project files. Installed CLI configuration does not accept the faux provider; faux is reserved for tests through an explicit test-only runtime gate.
|
|
67
|
+
|
|
68
|
+
`run-once` does not archive a normal Daily Brief when every enabled Source fails or when no Source Items exist for the requested date. It reports a Core Workflow Failure instead, because a false low-signal brief would hide collection failure.
|
|
69
|
+
|
|
70
|
+
## GitHub Trending collection
|
|
71
|
+
|
|
72
|
+
`github-trending-daily` monitors the site-wide daily GitHub Trending page. The Fetch Adapter treats each listed repository as a Trending Repository Observation and keeps parsing intentionally narrow: repository link, short description, stars/forks when visible, and stars today when visible. It does not fetch README files or repository detail APIs.
|
|
73
|
+
|
|
74
|
+
The adapter relies on focused HTML parsing plus regression tests for observed GitHub page changes. A GitHub Trending parsing failure is handled as a Source-level collection failure; it should not silently fall back to stale trend-list data.
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Formal Release Workflow
|
|
2
|
+
|
|
3
|
+
Formal Releases publish both a GitHub Release and the npm package `@chenpengfei/daily-brief`. The installed command remains `daily-brief`.
|
|
4
|
+
|
|
5
|
+
The workflow has three human-triggered Release Gates:
|
|
6
|
+
|
|
7
|
+
1. Agent Release Preparation Gate
|
|
8
|
+
2. Agent Release Review Gate
|
|
9
|
+
3. Human Release Gate
|
|
10
|
+
|
|
11
|
+
Publication commands are never run before the Human Release Gate.
|
|
12
|
+
|
|
13
|
+
## Release Invariants
|
|
14
|
+
|
|
15
|
+
- The Release Version uses SemVer: npm/package uses `X.Y.Z`, while GitHub tag and release title use `vX.Y.Z`.
|
|
16
|
+
- Release Pull Requests normally prepare release materials only: package metadata, version, Changelog, User Manual, release workflow documentation, and evidence.
|
|
17
|
+
- Release Pull Requests should not include ordinary product code fixes.
|
|
18
|
+
- Product-code Release Blockers normally leave the release line and are handled through a separate issue or fix PR.
|
|
19
|
+
- A minimal Release-Blocking Fix may remain in the Release Pull Request only when it is required to prove installability or publishability for the same release, is explicitly recorded in the Release Checklist Issue and Release Pull Request, and is called out for focused Agent Release Review.
|
|
20
|
+
- Published tags and npm versions are immutable. Post-Release Incidents use documentation and patch releases instead of moved tags or silent rollback.
|
|
21
|
+
|
|
22
|
+
## Gate 1: Agent Release Preparation
|
|
23
|
+
|
|
24
|
+
Trigger this gate by asking an Agent to prepare release `vX.Y.Z`.
|
|
25
|
+
|
|
26
|
+
The Agent must:
|
|
27
|
+
|
|
28
|
+
- Create or update the Release Checklist Issue.
|
|
29
|
+
- Create a `release/vX.Y.Z` branch and Release Pull Request.
|
|
30
|
+
- Update `package.json` and `package-lock.json` to the Release Version and package metadata.
|
|
31
|
+
- Update `CHANGELOG.md` with user-visible changes, installation or upgrade notes, and known limitations.
|
|
32
|
+
- Update `docs/user-manual.md` for any user-facing behavior changes.
|
|
33
|
+
- Update `docs/release-workflow.md` if the release process changes.
|
|
34
|
+
- Run `npm run release:check`.
|
|
35
|
+
- Run `npm run release:publish:dry-run`.
|
|
36
|
+
- Run the isolated Release Install Smoke Test.
|
|
37
|
+
- Record command evidence and any Release Blockers in the Release Checklist Issue and Release Pull Request.
|
|
38
|
+
|
|
39
|
+
The Release Check Command runs:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm test
|
|
43
|
+
npm run typecheck
|
|
44
|
+
npm run build
|
|
45
|
+
npm pack --dry-run
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The npm publish dry-run verifies scoped package publication metadata without publishing:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm run release:publish:dry-run
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The Release Install Smoke Test must use temporary npm and Daily Brief paths:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm pack
|
|
58
|
+
npm install --prefix /tmp/daily-brief-smoke -g ./chenpengfei-daily-brief-X.Y.Z.tgz --no-audit --no-fund
|
|
59
|
+
DAILY_BRIEF_HOME=/tmp/daily-brief-home \
|
|
60
|
+
DAILY_BRIEF_DATA_HOME=/tmp/daily-brief-data \
|
|
61
|
+
/tmp/daily-brief-smoke/bin/daily-brief --help
|
|
62
|
+
DAILY_BRIEF_HOME=/tmp/daily-brief-home \
|
|
63
|
+
DAILY_BRIEF_DATA_HOME=/tmp/daily-brief-data \
|
|
64
|
+
/tmp/daily-brief-smoke/bin/daily-brief setup
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Use a unique temporary directory for real runs. Do not install into the maintainer's real global npm prefix or real `~/.daily-brief` during the smoke test.
|
|
68
|
+
|
|
69
|
+
If a product-code blocker appears, stop the release preparation and classify it:
|
|
70
|
+
|
|
71
|
+
- If the blocker is not required to prove this release's installability or publishability, record the blocker, create or link the fix issue, and restart this gate after the fix is merged.
|
|
72
|
+
- If the blocker is a minimal Release-Blocking Fix, keep it in the Release Pull Request only after recording the reason, affected files, extra tests, smoke evidence, and review focus in the Release Checklist Issue and Release Pull Request.
|
|
73
|
+
|
|
74
|
+
Release-Blocking Fixes are exceptions, not a way to bundle unrelated behavior change into a release. The Agent Release Review Gate must review the exception explicitly.
|
|
75
|
+
|
|
76
|
+
## Gate 2: Agent Release Review
|
|
77
|
+
|
|
78
|
+
Trigger this gate in an independent Agent context after the Release Pull Request is ready.
|
|
79
|
+
|
|
80
|
+
The reviewing Agent must use a review stance and inspect:
|
|
81
|
+
|
|
82
|
+
- Release Checklist Issue evidence.
|
|
83
|
+
- Release Pull Request diff.
|
|
84
|
+
- `package.json` and `package-lock.json`.
|
|
85
|
+
- `CHANGELOG.md`.
|
|
86
|
+
- `docs/user-manual.md`.
|
|
87
|
+
- `docs/release-workflow.md`.
|
|
88
|
+
- Release CI Workflow result.
|
|
89
|
+
- Local evidence for `npm run release:check`.
|
|
90
|
+
- Local evidence for the Release Install Smoke Test.
|
|
91
|
+
- Any Release-Blocking Fix exception, including affected files, scope justification, targeted tests, smoke evidence, and whether a separate fix PR would be safer before release.
|
|
92
|
+
|
|
93
|
+
The reviewing Agent should report blocking findings first, with file and line references when possible. It may recommend approval or request fixes, but it must not publish the release or approve its own preparation work.
|
|
94
|
+
|
|
95
|
+
## Gate 3: Human Release
|
|
96
|
+
|
|
97
|
+
Only the maintainer can pass the Human Release Gate.
|
|
98
|
+
|
|
99
|
+
Before publishing, verify:
|
|
100
|
+
|
|
101
|
+
- Release Pull Request is merged.
|
|
102
|
+
- Local `main` matches `origin/main`.
|
|
103
|
+
- Release CI Workflow is green on the merged state.
|
|
104
|
+
- Agent Release Review Gate recommends approval or all findings are resolved.
|
|
105
|
+
- npm credentials and GitHub credentials are available to the maintainer.
|
|
106
|
+
- npm package state has been checked with `npm view @chenpengfei/daily-brief version`; first release should be unpublished, and later releases should show a version lower than the target Release Version.
|
|
107
|
+
- release notes have been prepared from the matching `CHANGELOG.md` entry.
|
|
108
|
+
|
|
109
|
+
Run the Human Release helper in preflight mode first:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm run release:human -- --version X.Y.Z
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
After the maintainer confirms the preflight output, publish with explicit confirmation:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm run release:human -- --version X.Y.Z --publish --yes --issue <release-checklist-issue-number>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The GitHub Release notes should be derived from the `CHANGELOG.md` entry and include:
|
|
122
|
+
|
|
123
|
+
- User-visible changes.
|
|
124
|
+
- Installation or upgrade notes.
|
|
125
|
+
- Known limitations.
|
|
126
|
+
- Installation command: `npm install -g @chenpengfei/daily-brief`.
|
|
127
|
+
|
|
128
|
+
After publication, verify public installation:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
npm view @chenpengfei/daily-brief version
|
|
132
|
+
npm install --prefix /tmp/daily-brief-public-smoke -g @chenpengfei/daily-brief@X.Y.Z --no-audit --no-fund
|
|
133
|
+
/tmp/daily-brief-public-smoke/bin/daily-brief --help
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The Human Release helper performs this public installation verification after `npm publish` and GitHub Release creation.
|
|
137
|
+
|
|
138
|
+
Record the npm version, GitHub Release URL, tag, and public install verification in the Release Checklist Issue.
|
|
139
|
+
|
|
140
|
+
## Post-Release Incidents
|
|
141
|
+
|
|
142
|
+
If npm publish succeeds but GitHub Release creation fails, continue by fixing GitHub Release creation for the same tag and version.
|
|
143
|
+
|
|
144
|
+
If GitHub Release is public but install verification fails, record the incident in the Release Checklist Issue, update release notes when relevant, create a fix issue, and publish a patch Release Version after the fix. Do not move the tag or unpublish the npm package unless the release exposed a severe secret or credential issue.
|
|
145
|
+
|
|
146
|
+
## Release Checklist Issue Template
|
|
147
|
+
|
|
148
|
+
```md
|
|
149
|
+
# Release vX.Y.Z
|
|
150
|
+
|
|
151
|
+
## Scope
|
|
152
|
+
|
|
153
|
+
- Release Version: X.Y.Z
|
|
154
|
+
- GitHub tag: vX.Y.Z
|
|
155
|
+
- npm package: @chenpengfei/daily-brief
|
|
156
|
+
- Release Pull Request:
|
|
157
|
+
|
|
158
|
+
## Gate 1: Agent Release Preparation
|
|
159
|
+
|
|
160
|
+
- [ ] Release branch created: release/vX.Y.Z
|
|
161
|
+
- [ ] Release Pull Request opened
|
|
162
|
+
- [ ] package metadata updated
|
|
163
|
+
- [ ] CHANGELOG.md updated
|
|
164
|
+
- [ ] docs/user-manual.md updated
|
|
165
|
+
- [ ] docs/release-workflow.md updated if needed
|
|
166
|
+
- [ ] npm run release:check completed
|
|
167
|
+
- [ ] npm run release:publish:dry-run completed
|
|
168
|
+
- [ ] Release Install Smoke Test completed with temporary npm prefix and Daily Brief homes
|
|
169
|
+
- [ ] Release Blockers recorded or confirmed absent
|
|
170
|
+
- [ ] Release-Blocking Fix exceptions recorded or confirmed absent
|
|
171
|
+
|
|
172
|
+
### Preparation Evidence
|
|
173
|
+
|
|
174
|
+
- release:check:
|
|
175
|
+
- publish dry-run:
|
|
176
|
+
- npm pack output:
|
|
177
|
+
- npm package state before publish:
|
|
178
|
+
- install smoke test:
|
|
179
|
+
- Release-Blocking Fix exception:
|
|
180
|
+
- git status:
|
|
181
|
+
- notes:
|
|
182
|
+
|
|
183
|
+
## Gate 2: Agent Release Review
|
|
184
|
+
|
|
185
|
+
- [ ] Independent Agent review requested
|
|
186
|
+
- [ ] Release Pull Request reviewed
|
|
187
|
+
- [ ] Release Checklist evidence reviewed
|
|
188
|
+
- [ ] Release CI Workflow result reviewed
|
|
189
|
+
- [ ] Release-Blocking Fix exceptions reviewed or confirmed absent
|
|
190
|
+
- [ ] Review recommendation recorded
|
|
191
|
+
|
|
192
|
+
### Review Findings
|
|
193
|
+
|
|
194
|
+
- Blocking:
|
|
195
|
+
- Non-blocking:
|
|
196
|
+
- Recommendation:
|
|
197
|
+
|
|
198
|
+
## Gate 3: Human Release
|
|
199
|
+
|
|
200
|
+
- [ ] Release Pull Request merged
|
|
201
|
+
- [ ] main matches origin/main
|
|
202
|
+
- [ ] npm package state checked
|
|
203
|
+
- [ ] GitHub Release notes file prepared
|
|
204
|
+
- [ ] tag pushed: vX.Y.Z
|
|
205
|
+
- [ ] npm publish completed
|
|
206
|
+
- [ ] GitHub Release created
|
|
207
|
+
- [ ] public install verification completed
|
|
208
|
+
|
|
209
|
+
### Publication Evidence
|
|
210
|
+
|
|
211
|
+
- npm version:
|
|
212
|
+
- npm package state before publish:
|
|
213
|
+
- GitHub Release URL:
|
|
214
|
+
- tag:
|
|
215
|
+
- public install:
|
|
216
|
+
|
|
217
|
+
## Post-Release Incidents
|
|
218
|
+
|
|
219
|
+
- None known.
|
|
220
|
+
```
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Daily Brief User Manual
|
|
2
|
+
|
|
3
|
+
Daily Brief is an installed command-line tool for generating a recurring Agent architecture and AI Coding brief from Sources you explicitly configure.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Daily Brief is distributed as the npm package `@chenpengfei/daily-brief` and installs the `daily-brief` command.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @chenpengfei/daily-brief
|
|
11
|
+
daily-brief --help
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Daily Brief requires Node.js 22 or newer.
|
|
15
|
+
|
|
16
|
+
## First Setup
|
|
17
|
+
|
|
18
|
+
Run setup after installing the package:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
daily-brief setup
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Setup creates user configuration and generated-data directories, initializes the Source Registry from the packaged example, prepares credential storage, and reports readiness. Setup does not collect Sources, call an LLM, generate a Daily Brief, or send a delivery notification.
|
|
25
|
+
|
|
26
|
+
By default, configuration files live under `~/.daily-brief/`, and generated data lives under `~/.daily-brief/data/`.
|
|
27
|
+
|
|
28
|
+
## Configure Sources
|
|
29
|
+
|
|
30
|
+
Sources are manually controlled. The agent processes configured Sources, but it does not autonomously add or remove them.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
daily-brief sources list
|
|
34
|
+
daily-brief sources edit
|
|
35
|
+
daily-brief sources validate
|
|
36
|
+
daily-brief sources enable <source-id>
|
|
37
|
+
daily-brief sources disable <source-id>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Use `sources validate` after editing the Source Registry.
|
|
41
|
+
|
|
42
|
+
## Configure Model Access
|
|
43
|
+
|
|
44
|
+
Agent Stages require an LLM Provider Configuration before real Daily Brief generation.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
daily-brief model configure
|
|
48
|
+
daily-brief model status
|
|
49
|
+
daily-brief model login
|
|
50
|
+
daily-brief model logout
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Credentials are stored in the Model Credential Store or environment variables. Do not put secrets in `sources.yaml` or committed project files.
|
|
54
|
+
|
|
55
|
+
## Configure Delivery
|
|
56
|
+
|
|
57
|
+
Discord Delivery is optional. Configure it when you want generated Daily Brief notifications pushed to Discord.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
daily-brief delivery configure --enabled true --webhook-url <url>
|
|
61
|
+
daily-brief delivery status
|
|
62
|
+
daily-brief delivery test
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
If Discord Delivery is disabled or no webhook is configured, generation can still run and will report skipped delivery explicitly.
|
|
66
|
+
|
|
67
|
+
## Run Daily Brief
|
|
68
|
+
|
|
69
|
+
For a full manual run:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
daily-brief run-once
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
For separated operational steps:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
daily-brief collect
|
|
79
|
+
daily-brief generate
|
|
80
|
+
daily-brief deliver
|
|
81
|
+
daily-brief status
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The expected local cadence is collection at 06:00 and generation or delivery at 07:00 local time. Daily Brief does not include a built-in scheduler; use an external scheduler to invoke the CLI.
|
|
85
|
+
|
|
86
|
+
## Inspect Status
|
|
87
|
+
|
|
88
|
+
Use status after setup, after manual runs, or when diagnosing failures:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
daily-brief status
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Status output is the operational surface for collection, analysis, archive, and delivery health.
|
|
95
|
+
|
|
96
|
+
## Paths and Environment
|
|
97
|
+
|
|
98
|
+
The installed CLI uses user-home paths by default:
|
|
99
|
+
|
|
100
|
+
- `DAILY_BRIEF_HOME`: configuration directory, default `~/.daily-brief`.
|
|
101
|
+
- `DAILY_BRIEF_DATA_HOME`: generated data directory, default `~/.daily-brief/data`.
|
|
102
|
+
- `DAILY_BRIEF_DISCORD_TEMPLATE_PATH`: optional Discord notification template override.
|
|
103
|
+
- `DISCORD_WEBHOOK_URL`: optional Discord webhook URL.
|
|
104
|
+
|
|
105
|
+
Repository checkouts may use a local `.env`; installed usage should normally rely on user configuration and credential commands.
|
|
106
|
+
|
|
107
|
+
## Upgrade
|
|
108
|
+
|
|
109
|
+
Upgrade the installed package through npm:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm install -g @chenpengfei/daily-brief@latest
|
|
113
|
+
daily-brief status
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Run `daily-brief setup` again when release notes or status output indicate that configuration needs to be refreshed. Setup preserves existing files unless an overwrite or force behavior is explicitly selected.
|
|
117
|
+
|
|
118
|
+
## Troubleshooting
|
|
119
|
+
|
|
120
|
+
If setup has not run, run:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
daily-brief setup
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
If Sources are not collected, run:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
daily-brief sources validate
|
|
130
|
+
daily-brief sources list
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
If model access fails, run:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
daily-brief model status
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
If Discord delivery fails or is skipped, run:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
daily-brief delivery status
|
|
143
|
+
daily-brief delivery test
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
If a run cannot honestly produce a Daily Brief, the CLI reports a Core Workflow Failure rather than archiving a false normal Daily Brief.
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chenpengfei/daily-brief",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Agent-driven daily brief CLI for manually curated Agent architecture and AI Coding sources.",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/chenpengfei/daily-brief#readme",
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/chenpengfei/daily-brief/issues"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/chenpengfei/daily-brief.git"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"agent",
|
|
21
|
+
"daily-brief",
|
|
22
|
+
"cli",
|
|
23
|
+
"ai-coding",
|
|
24
|
+
"briefing"
|
|
25
|
+
],
|
|
26
|
+
"files": [
|
|
27
|
+
"dist/src",
|
|
28
|
+
"config/sources.example.yaml",
|
|
29
|
+
"templates",
|
|
30
|
+
"docs/operations.md",
|
|
31
|
+
"docs/user-manual.md",
|
|
32
|
+
"docs/release-workflow.md",
|
|
33
|
+
"README.md",
|
|
34
|
+
"CHANGELOG.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"bin": {
|
|
38
|
+
"daily-brief": "dist/src/cli.js"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc -p tsconfig.json",
|
|
42
|
+
"cli": "tsx src/cli.ts",
|
|
43
|
+
"prepack": "npm run build",
|
|
44
|
+
"release:check": "npm test && npm run typecheck && npm run build && npm pack --dry-run",
|
|
45
|
+
"release:human": "node scripts/human-release.mjs",
|
|
46
|
+
"release:publish:dry-run": "npm publish --dry-run --access public",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@earendil-works/pi-agent-core": "^0.76.0",
|
|
52
|
+
"@earendil-works/pi-ai": "^0.76.0",
|
|
53
|
+
"fast-xml-parser": "^5.2.2",
|
|
54
|
+
"yaml": "^2.8.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^22.15.24",
|
|
58
|
+
"tsx": "^4.19.4",
|
|
59
|
+
"typescript": "^5.8.3",
|
|
60
|
+
"vitest": "^3.1.4"
|
|
61
|
+
},
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=22"
|
|
64
|
+
}
|
|
65
|
+
}
|