projectstore-codex 0.0.1 → 0.28.1
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/.codex-plugin/plugin.json +48 -0
- package/README.md +15 -7
- package/bin/projectstore-codex.mjs +88 -0
- package/hooks/hooks.json +59 -0
- package/node_modules/projectstore/.claude-plugin/marketplace.json +40 -0
- package/node_modules/projectstore/.claude-plugin/plugin.json +23 -0
- package/node_modules/projectstore/.mcp.json +14 -0
- package/node_modules/projectstore/AGENTS.md +26 -0
- package/node_modules/projectstore/LICENSE +21 -0
- package/node_modules/projectstore/README.md +284 -0
- package/node_modules/projectstore/agents/archaeologist.md +76 -0
- package/node_modules/projectstore/agents/clerk.md +93 -0
- package/node_modules/projectstore/agents/critic.md +94 -0
- package/node_modules/projectstore/agents/librarian.md +81 -0
- package/node_modules/projectstore/agents/planner.md +80 -0
- package/node_modules/projectstore/agents/reviewer.md +98 -0
- package/node_modules/projectstore/bin/projectstore.mjs +7 -0
- package/node_modules/projectstore/commands/adr.md +57 -0
- package/node_modules/projectstore/commands/agents.md +180 -0
- package/node_modules/projectstore/commands/bind.md +128 -0
- package/node_modules/projectstore/commands/codemap.md +50 -0
- package/node_modules/projectstore/commands/concept.md +17 -0
- package/node_modules/projectstore/commands/doctor.md +166 -0
- package/node_modules/projectstore/commands/epic.md +40 -0
- package/node_modules/projectstore/commands/graph.md +56 -0
- package/node_modules/projectstore/commands/kanban.md +40 -0
- package/node_modules/projectstore/commands/meeting.md +17 -0
- package/node_modules/projectstore/commands/reconcile.md +73 -0
- package/node_modules/projectstore/commands/research.md +17 -0
- package/node_modules/projectstore/commands/review.md +89 -0
- package/node_modules/projectstore/commands/runbook.md +17 -0
- package/node_modules/projectstore/commands/scaffold.md +23 -0
- package/node_modules/projectstore/commands/search.md +22 -0
- package/node_modules/projectstore/commands/spec.md +91 -0
- package/node_modules/projectstore/commands/status.md +27 -0
- package/node_modules/projectstore/commands/statusline.md +46 -0
- package/node_modules/projectstore/commands/story.md +113 -0
- package/node_modules/projectstore/docs/extending.md +172 -0
- package/node_modules/projectstore/docs/getting-started.md +133 -0
- package/node_modules/projectstore/docs/harnesses.md +163 -0
- package/node_modules/projectstore/docs/how-it-works.md +263 -0
- package/node_modules/projectstore/docs/images/loop-light.svg +94 -0
- package/node_modules/projectstore/docs/images/loop.svg +93 -0
- package/node_modules/projectstore/docs/images/statusline-hud.png +0 -0
- package/node_modules/projectstore/docs/images/team-light.svg +79 -0
- package/node_modules/projectstore/docs/images/team.svg +79 -0
- package/node_modules/projectstore/harnesses/claude-code.json +483 -0
- package/node_modules/projectstore/harnesses/codex.json +332 -0
- package/node_modules/projectstore/hooks/hooks.json +59 -0
- package/node_modules/projectstore/hooks/pre-compact.mjs +121 -0
- package/node_modules/projectstore/hooks/session-rules.mjs +63 -0
- package/node_modules/projectstore/hooks/session-start.mjs +301 -0
- package/node_modules/projectstore/hooks/session-stop.mjs +84 -0
- package/node_modules/projectstore/package.json +70 -0
- package/node_modules/projectstore/scaffold/checklists.json +88 -0
- package/node_modules/projectstore/scaffold/headings.json +171 -0
- package/node_modules/projectstore/scaffold/layouts/engineering.json +85 -0
- package/node_modules/projectstore/scripts/binding.mjs +165 -0
- package/node_modules/projectstore/scripts/build-adapters.mjs +264 -0
- package/node_modules/projectstore/scripts/cli.mjs +595 -0
- package/node_modules/projectstore/scripts/codemap.mjs +99 -0
- package/node_modules/projectstore/scripts/diff-refs.mjs +127 -0
- package/node_modules/projectstore/scripts/doctor.mjs +2127 -0
- package/node_modules/projectstore/scripts/draft.mjs +261 -0
- package/node_modules/projectstore/scripts/graph.mjs +219 -0
- package/node_modules/projectstore/scripts/harness.mjs +608 -0
- package/node_modules/projectstore/scripts/install-harness.mjs +1387 -0
- package/node_modules/projectstore/scripts/kanban.mjs +174 -0
- package/node_modules/projectstore/scripts/lib.mjs +3085 -0
- package/node_modules/projectstore/scripts/mcp.mjs +391 -0
- package/node_modules/projectstore/scripts/portable-registration.mjs +198 -0
- package/node_modules/projectstore/scripts/provenance.mjs +375 -0
- package/node_modules/projectstore/scripts/query.mjs +490 -0
- package/node_modules/projectstore/scripts/reconcile.mjs +422 -0
- package/node_modules/projectstore/scripts/statusline-launcher.mjs +141 -0
- package/node_modules/projectstore/scripts/statusline.mjs +253 -0
- package/node_modules/projectstore/scripts/story-section.mjs +209 -0
- package/node_modules/projectstore/scripts/surfaces.mjs +421 -0
- package/node_modules/projectstore/scripts/tokens.mjs +449 -0
- package/node_modules/projectstore/scripts/touch-session.mjs +336 -0
- package/node_modules/projectstore/scripts/version-guard.mjs +255 -0
- package/node_modules/projectstore/scripts/worktree.mjs +109 -0
- package/node_modules/projectstore/skills/projectstore-decision-detector/SKILL.md +40 -0
- package/node_modules/projectstore/skills/projectstore-peer-reviewer/SKILL.md +38 -0
- package/node_modules/projectstore/skills/projectstore-story-completion/SKILL.md +50 -0
- package/node_modules/projectstore/skills/projectstore-vault-communication/SKILL.md +96 -0
- package/node_modules/projectstore/templates/claude-md-block.md.tmpl +26 -0
- package/node_modules/projectstore/templates/de/adr.md.tmpl +67 -0
- package/node_modules/projectstore/templates/de/concept.md.tmpl +43 -0
- package/node_modules/projectstore/templates/de/epic.md.tmpl +59 -0
- package/node_modules/projectstore/templates/de/folder-readme.md.tmpl +14 -0
- package/node_modules/projectstore/templates/de/kanban.md.tmpl +36 -0
- package/node_modules/projectstore/templates/de/meeting.md.tmpl +38 -0
- package/node_modules/projectstore/templates/de/research.md.tmpl +47 -0
- package/node_modules/projectstore/templates/de/runbook.md.tmpl +53 -0
- package/node_modules/projectstore/templates/de/spec.md.tmpl +64 -0
- package/node_modules/projectstore/templates/de/story.md.tmpl +76 -0
- package/node_modules/projectstore/templates/de/strings.json +6 -0
- package/node_modules/projectstore/templates/en/adr.md.tmpl +67 -0
- package/node_modules/projectstore/templates/en/concept.md.tmpl +43 -0
- package/node_modules/projectstore/templates/en/epic.md.tmpl +59 -0
- package/node_modules/projectstore/templates/en/folder-readme.md.tmpl +14 -0
- package/node_modules/projectstore/templates/en/kanban.md.tmpl +36 -0
- package/node_modules/projectstore/templates/en/meeting.md.tmpl +38 -0
- package/node_modules/projectstore/templates/en/research.md.tmpl +47 -0
- package/node_modules/projectstore/templates/en/runbook.md.tmpl +53 -0
- package/node_modules/projectstore/templates/en/spec.md.tmpl +64 -0
- package/node_modules/projectstore/templates/en/story.md.tmpl +76 -0
- package/node_modules/projectstore/templates/en/strings.json +6 -0
- package/node_modules/projectstore/templates/es/adr.md.tmpl +67 -0
- package/node_modules/projectstore/templates/es/concept.md.tmpl +43 -0
- package/node_modules/projectstore/templates/es/epic.md.tmpl +59 -0
- package/node_modules/projectstore/templates/es/folder-readme.md.tmpl +14 -0
- package/node_modules/projectstore/templates/es/kanban.md.tmpl +36 -0
- package/node_modules/projectstore/templates/es/meeting.md.tmpl +38 -0
- package/node_modules/projectstore/templates/es/research.md.tmpl +47 -0
- package/node_modules/projectstore/templates/es/runbook.md.tmpl +53 -0
- package/node_modules/projectstore/templates/es/spec.md.tmpl +64 -0
- package/node_modules/projectstore/templates/es/story.md.tmpl +76 -0
- package/node_modules/projectstore/templates/es/strings.json +6 -0
- package/node_modules/projectstore/templates/fr/adr.md.tmpl +67 -0
- package/node_modules/projectstore/templates/fr/concept.md.tmpl +43 -0
- package/node_modules/projectstore/templates/fr/epic.md.tmpl +59 -0
- package/node_modules/projectstore/templates/fr/folder-readme.md.tmpl +14 -0
- package/node_modules/projectstore/templates/fr/kanban.md.tmpl +36 -0
- package/node_modules/projectstore/templates/fr/meeting.md.tmpl +38 -0
- package/node_modules/projectstore/templates/fr/research.md.tmpl +47 -0
- package/node_modules/projectstore/templates/fr/runbook.md.tmpl +53 -0
- package/node_modules/projectstore/templates/fr/spec.md.tmpl +64 -0
- package/node_modules/projectstore/templates/fr/story.md.tmpl +76 -0
- package/node_modules/projectstore/templates/fr/strings.json +6 -0
- package/node_modules/projectstore/templates/ru/adr.md.tmpl +67 -0
- package/node_modules/projectstore/templates/ru/concept.md.tmpl +43 -0
- package/node_modules/projectstore/templates/ru/epic.md.tmpl +59 -0
- package/node_modules/projectstore/templates/ru/folder-readme.md.tmpl +14 -0
- package/node_modules/projectstore/templates/ru/kanban.md.tmpl +36 -0
- package/node_modules/projectstore/templates/ru/meeting.md.tmpl +38 -0
- package/node_modules/projectstore/templates/ru/research.md.tmpl +47 -0
- package/node_modules/projectstore/templates/ru/runbook.md.tmpl +53 -0
- package/node_modules/projectstore/templates/ru/spec.md.tmpl +64 -0
- package/node_modules/projectstore/templates/ru/story.md.tmpl +76 -0
- package/node_modules/projectstore/templates/ru/strings.json +6 -0
- package/node_modules/projectstore/templates/zh/adr.md.tmpl +67 -0
- package/node_modules/projectstore/templates/zh/concept.md.tmpl +43 -0
- package/node_modules/projectstore/templates/zh/epic.md.tmpl +59 -0
- package/node_modules/projectstore/templates/zh/folder-readme.md.tmpl +14 -0
- package/node_modules/projectstore/templates/zh/kanban.md.tmpl +36 -0
- package/node_modules/projectstore/templates/zh/meeting.md.tmpl +38 -0
- package/node_modules/projectstore/templates/zh/research.md.tmpl +47 -0
- package/node_modules/projectstore/templates/zh/runbook.md.tmpl +53 -0
- package/node_modules/projectstore/templates/zh/spec.md.tmpl +64 -0
- package/node_modules/projectstore/templates/zh/story.md.tmpl +76 -0
- package/node_modules/projectstore/templates/zh/strings.json +6 -0
- package/package.json +36 -14
- package/plugin.json +53 -0
- package/skills/projectstore-adr/SKILL.md +76 -0
- package/skills/projectstore-agents/SKILL.md +50 -0
- package/skills/projectstore-archaeologist/SKILL.md +109 -0
- package/skills/projectstore-bind/SKILL.md +44 -0
- package/skills/projectstore-clerk/SKILL.md +126 -0
- package/skills/projectstore-codemap/SKILL.md +69 -0
- package/skills/projectstore-concept/SKILL.md +36 -0
- package/skills/projectstore-critic/SKILL.md +127 -0
- package/skills/projectstore-decision-detector/SKILL.md +59 -0
- package/skills/projectstore-doctor/SKILL.md +33 -0
- package/skills/projectstore-epic/SKILL.md +59 -0
- package/skills/projectstore-graph/SKILL.md +75 -0
- package/skills/projectstore-kanban/SKILL.md +60 -0
- package/skills/projectstore-librarian/SKILL.md +114 -0
- package/skills/projectstore-meeting/SKILL.md +36 -0
- package/skills/projectstore-peer-reviewer/SKILL.md +57 -0
- package/skills/projectstore-planner/SKILL.md +113 -0
- package/skills/projectstore-reconcile/SKILL.md +92 -0
- package/skills/projectstore-research/SKILL.md +36 -0
- package/skills/projectstore-review/SKILL.md +108 -0
- package/skills/projectstore-reviewer/SKILL.md +131 -0
- package/skills/projectstore-runbook/SKILL.md +36 -0
- package/skills/projectstore-scaffold/SKILL.md +42 -0
- package/skills/projectstore-search/SKILL.md +41 -0
- package/skills/projectstore-spec/SKILL.md +110 -0
- package/skills/projectstore-status/SKILL.md +47 -0
- package/skills/projectstore-statusline/SKILL.md +29 -0
- package/skills/projectstore-story/SKILL.md +132 -0
- package/skills/projectstore-story-completion/SKILL.md +69 -0
- package/skills/projectstore-vault-communication/SKILL.md +115 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// projectstore — codemap.mjs
|
|
3
|
+
// Derives code-map.md — the epic↔code overview — from epic/story frontmatter
|
|
4
|
+
// `code_refs` (ADR-004). Same one-way pattern as kanban.mjs: frontmatter is
|
|
5
|
+
// the source of truth, this file is a regenerated view.
|
|
6
|
+
//
|
|
7
|
+
// Output: JSON { path, content, stats } — compute only. The applier is
|
|
8
|
+
// reconcile.mjs --write (atomic replace, recompute-at-write), not the
|
|
9
|
+
// harness: no Write-tool step remains in the derived-view flows.
|
|
10
|
+
|
|
11
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { readConfig, loadLayout, folderByKind, parseFrontmatter, nowIso, isMain } from "./lib.mjs";
|
|
14
|
+
|
|
15
|
+
function die(msg) {
|
|
16
|
+
process.stderr.write(`projectstore/codemap: ${msg}\n`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function parseRefs(raw) {
|
|
21
|
+
if (!raw || raw === "[]") return [];
|
|
22
|
+
try {
|
|
23
|
+
const v = JSON.parse(raw);
|
|
24
|
+
return Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
|
|
25
|
+
} catch {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function main() {
|
|
31
|
+
const cfg = readConfig();
|
|
32
|
+
if (!cfg) die("No projectstore config. Run /projectstore:bind first.");
|
|
33
|
+
const layout = loadLayout(cfg.layout);
|
|
34
|
+
const folder = folderByKind(layout, "epic");
|
|
35
|
+
if (!folder) die("Layout has no epic folder — nothing to map.");
|
|
36
|
+
|
|
37
|
+
const root = join(cfg.vault_path, folder.path);
|
|
38
|
+
const epics = [];
|
|
39
|
+
const storyRows = [];
|
|
40
|
+
if (existsSync(root)) {
|
|
41
|
+
for (const id of readdirSync(root).sort()) {
|
|
42
|
+
const epicMd = join(root, id, "epic.md");
|
|
43
|
+
if (!existsSync(epicMd)) continue;
|
|
44
|
+
const fm = parseFrontmatter(readFileSync(epicMd, "utf8")).data;
|
|
45
|
+
epics.push({ id, title: fm.title || id, status: fm.status || "planned", refs: parseRefs(fm.code_refs) });
|
|
46
|
+
const storiesDir = join(root, id, "stories");
|
|
47
|
+
if (!existsSync(storiesDir)) continue;
|
|
48
|
+
for (const f of readdirSync(storiesDir).sort()) {
|
|
49
|
+
if (!f.endsWith(".md")) continue;
|
|
50
|
+
const sfm = parseFrontmatter(readFileSync(join(storiesDir, f), "utf8")).data;
|
|
51
|
+
const refs = parseRefs(sfm.code_refs);
|
|
52
|
+
if (refs.length) {
|
|
53
|
+
storyRows.push({ epic: id, story: f.replace(/\.md$/, ""), title: sfm.title || f, refs });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const lines = [
|
|
60
|
+
"---",
|
|
61
|
+
"",
|
|
62
|
+
"projectstore: derived",
|
|
63
|
+
// Full ISO, not date-only: freshness comparison on an active day is
|
|
64
|
+
// ill-defined otherwise (spec contract 6). Normalizers strip the line.
|
|
65
|
+
`generated_at: ${nowIso()}`,
|
|
66
|
+
"",
|
|
67
|
+
"---",
|
|
68
|
+
"",
|
|
69
|
+
"# Code map",
|
|
70
|
+
"",
|
|
71
|
+
"Epic ↔ code mapping, derived from frontmatter `code_refs` (source of truth).",
|
|
72
|
+
"Regenerate via `/projectstore:codemap`; edit refs via `/projectstore:codemap set`.",
|
|
73
|
+
"",
|
|
74
|
+
"| Epic | Title | Status | code_refs |",
|
|
75
|
+
"|------|-------|--------|-----------|",
|
|
76
|
+
];
|
|
77
|
+
for (const e of epics) {
|
|
78
|
+
const refs = e.refs.length ? e.refs.map((r) => `\`${r}\``).join(", ") : "—";
|
|
79
|
+
lines.push(`| [[${folder.path}/${e.id}/epic\\|${e.id}]] | ${e.title} | ${e.status} | ${refs} |`);
|
|
80
|
+
}
|
|
81
|
+
if (storyRows.length) {
|
|
82
|
+
lines.push("", "## Story-level refs (files each story touched)", "",
|
|
83
|
+
"| Epic | Story | code_refs |", "|------|-------|-----------|");
|
|
84
|
+
for (const s of storyRows) {
|
|
85
|
+
lines.push(`| ${s.epic} | ${s.title} | ${s.refs.map((r) => `\`${r}\``).join(", ")} |`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
lines.push("");
|
|
89
|
+
|
|
90
|
+
process.stdout.write(JSON.stringify({
|
|
91
|
+
path: join(cfg.vault_path, "code-map.md"),
|
|
92
|
+
content: lines.join("\n"),
|
|
93
|
+
stats: { epics: epics.length, epics_with_refs: epics.filter((e) => e.refs.length).length, story_rows: storyRows.length },
|
|
94
|
+
}, null, 2) + "\n");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (isMain(import.meta.url)) {
|
|
98
|
+
main();
|
|
99
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// projectstore — diff-refs.mjs (PS-SPEC story-009)
|
|
3
|
+
// Computes the files a story touched, for the reviewer's proposed code_refs.
|
|
4
|
+
// Our own extension: neither Backlog.md nor projectstore derived code mapping
|
|
5
|
+
// from git before this. Pure compute — the write path stays
|
|
6
|
+
// /projectstore:codemap set, approval-gated.
|
|
7
|
+
//
|
|
8
|
+
// node diff-refs.mjs --since <iso-timestamp> [--range <git-range>]
|
|
9
|
+
//
|
|
10
|
+
// Range derivation is STORY-SCOPED: `--since` is the story's started_at (set
|
|
11
|
+
// by /projectstore:story plan). Branch-wide ranges (merge-base..HEAD) are
|
|
12
|
+
// deliberately not the default — shared feature branches over-attribute and
|
|
13
|
+
// direct-to-main work yields an empty diff (research note, adoption item 3).
|
|
14
|
+
// Output: { since, range, files, uncommitted, fallback } — fallback=true
|
|
15
|
+
// means the caller must ask the user for an explicit range instead of
|
|
16
|
+
// guessing.
|
|
17
|
+
//
|
|
18
|
+
// Git runs in the PROJECT (the directory the harness hands us), never the vault — the vault
|
|
19
|
+
// is typically not a git repository.
|
|
20
|
+
|
|
21
|
+
import { spawnSync } from "node:child_process";
|
|
22
|
+
import { resolve } from "node:path";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
import { projectRoot, SOURCE_IGNORE } from "./lib.mjs";
|
|
25
|
+
|
|
26
|
+
// One definition, in lib.mjs, shared with the entry-rule hook and doctor —
|
|
27
|
+
// three consumers of "is this a source file" that must not drift apart.
|
|
28
|
+
const IGNORE = SOURCE_IGNORE;
|
|
29
|
+
|
|
30
|
+
// The default 15 s is a batch budget — diff-refs runs off the hot path. A caller
|
|
31
|
+
// on a user-facing path must pass its own: SessionStart's gather budget is 200 ms,
|
|
32
|
+
// so a stalled git there would hold session start for fifteen seconds.
|
|
33
|
+
export function gitIn(cwd, args, { timeout = 15000 } = {}) {
|
|
34
|
+
const r = spawnSync("git", args, {
|
|
35
|
+
cwd,
|
|
36
|
+
encoding: "utf8",
|
|
37
|
+
timeout,
|
|
38
|
+
});
|
|
39
|
+
return r.status === 0 ? r.stdout : null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const git = (args) => gitIn(projectRoot(), args);
|
|
43
|
+
|
|
44
|
+
// Doctor's only route to git. Kept here rather than in lib.mjs, whose header
|
|
45
|
+
// pins it as dependency-free with no subprocess anywhere.
|
|
46
|
+
export function uncommittedProjectFiles(cwd = projectRoot(), ignore = IGNORE) {
|
|
47
|
+
// A non-repository, a shallow clone with no HEAD, or a detached worktree with
|
|
48
|
+
// no commits all make these fail — null means "cannot tell", which is not the
|
|
49
|
+
// same as "nothing uncommitted" and must not be reported as a finding.
|
|
50
|
+
const tracked = gitIn(cwd, ["diff", "--name-only", "HEAD"]);
|
|
51
|
+
const untracked = gitIn(cwd, ["ls-files", "--others", "--exclude-standard"]);
|
|
52
|
+
if (tracked === null && untracked === null) return null;
|
|
53
|
+
return [...new Set([...(tracked || "").split("\n"), ...(untracked || "").split("\n")])]
|
|
54
|
+
.map((f) => f.trim())
|
|
55
|
+
.filter((f) => f && !ignore.some((rx) => rx.test(f)))
|
|
56
|
+
.sort();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Newest commit timestamp, ms, or null when git cannot answer.
|
|
60
|
+
export function lastCommitMs(cwd = projectRoot()) {
|
|
61
|
+
const out = gitIn(cwd, ["log", "-1", "--format=%cI"]);
|
|
62
|
+
if (!out) return null;
|
|
63
|
+
const t = Date.parse(out.trim());
|
|
64
|
+
return Number.isFinite(t) ? t : null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function filterFiles(list) {
|
|
68
|
+
return [...new Set(list)]
|
|
69
|
+
.map((f) => f.trim())
|
|
70
|
+
.filter((f) => f && !IGNORE.some((rx) => rx.test(f)))
|
|
71
|
+
.sort();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function main() {
|
|
75
|
+
const args = process.argv.slice(2);
|
|
76
|
+
const since = args.includes("--since") ? args[args.indexOf("--since") + 1] : null;
|
|
77
|
+
const range = args.includes("--range") ? args[args.indexOf("--range") + 1] : null;
|
|
78
|
+
|
|
79
|
+
let committed = [];
|
|
80
|
+
let usedRange = null;
|
|
81
|
+
if (range) {
|
|
82
|
+
const out = git(["diff", "--name-only", range]);
|
|
83
|
+
if (out === null) {
|
|
84
|
+
process.stdout.write(JSON.stringify({ error: `git diff ${range} failed`, fallback: true }) + "\n");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
committed = out.split("\n");
|
|
88
|
+
usedRange = range;
|
|
89
|
+
} else if (since) {
|
|
90
|
+
// committer-date window; work committed before the story went in-progress
|
|
91
|
+
// is invisible by design (story-scoped attribution).
|
|
92
|
+
//
|
|
93
|
+
// A bare YYYY-MM-DD gets midnight appended, and it is not cosmetic. Git's
|
|
94
|
+
// approxidate reads a date with no time as THAT DATE AT THE CURRENT TIME OF
|
|
95
|
+
// DAY, so `--since=2026-09-08` run at 17:46 silently excludes everything
|
|
96
|
+
// committed earlier the same day. `started_at` is written as a bare date,
|
|
97
|
+
// so a story started and finished in one day proposed `files: []` and the
|
|
98
|
+
// caller had no way to tell that from "nothing was committed" (measured
|
|
99
|
+
// 2026-09-08: --since=2026-09-08 returned nothing while
|
|
100
|
+
// --since="2026-09-08 00:00" returned the day's commit).
|
|
101
|
+
const window = /^\d{4}-\d{2}-\d{2}$/.test(since) ? `${since} 00:00:00` : since;
|
|
102
|
+
const out = git(["log", `--since=${window}`, "--name-only", "--pretty=format:"]);
|
|
103
|
+
committed = out ? out.split("\n") : [];
|
|
104
|
+
usedRange = `--since=${window}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ls-files --others expands untracked DIRECTORIES to individual files
|
|
108
|
+
// (porcelain would collapse them to "tests/") and needs no column slicing.
|
|
109
|
+
const uncommitted = filterFiles([
|
|
110
|
+
...(git(["diff", "--name-only", "HEAD"]) || "").split("\n"),
|
|
111
|
+
...(git(["ls-files", "--others", "--exclude-standard"]) || "").split("\n"),
|
|
112
|
+
]);
|
|
113
|
+
const files = filterFiles(committed);
|
|
114
|
+
|
|
115
|
+
const fallback = !range && !since || (files.length === 0 && uncommitted.length === 0);
|
|
116
|
+
process.stdout.write(JSON.stringify({
|
|
117
|
+
since: since || null,
|
|
118
|
+
range: usedRange,
|
|
119
|
+
files,
|
|
120
|
+
uncommitted,
|
|
121
|
+
fallback,
|
|
122
|
+
}, null, 2) + "\n");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
126
|
+
main();
|
|
127
|
+
}
|