openplanr 1.2.5 → 1.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/dist/cli/commands/context.d.ts +6 -0
- package/dist/cli/commands/context.d.ts.map +1 -0
- package/dist/cli/commands/context.js +45 -0
- package/dist/cli/commands/context.js.map +1 -0
- package/dist/cli/commands/export.d.ts.map +1 -1
- package/dist/cli/commands/export.js +8 -0
- package/dist/cli/commands/export.js.map +1 -1
- package/dist/cli/commands/report-linter.d.ts +6 -0
- package/dist/cli/commands/report-linter.d.ts.map +1 -0
- package/dist/cli/commands/report-linter.js +61 -0
- package/dist/cli/commands/report-linter.js.map +1 -0
- package/dist/cli/commands/report.d.ts +6 -0
- package/dist/cli/commands/report.d.ts.map +1 -0
- package/dist/cli/commands/report.js +150 -0
- package/dist/cli/commands/report.js.map +1 -0
- package/dist/cli/commands/story.d.ts.map +1 -1
- package/dist/cli/commands/story.js +31 -0
- package/dist/cli/commands/story.js.map +1 -1
- package/dist/cli/commands/voice.d.ts +6 -0
- package/dist/cli/commands/voice.d.ts.map +1 -0
- package/dist/cli/commands/voice.js +64 -0
- package/dist/cli/commands/voice.js.map +1 -0
- package/dist/cli/index.js +8 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/models/schema.d.ts +63 -0
- package/dist/models/schema.d.ts.map +1 -1
- package/dist/models/schema.js +31 -0
- package/dist/models/schema.js.map +1 -1
- package/dist/models/types.d.ts +160 -0
- package/dist/models/types.d.ts.map +1 -1
- package/dist/services/context-pack-service.d.ts +12 -0
- package/dist/services/context-pack-service.d.ts.map +1 -0
- package/dist/services/context-pack-service.js +155 -0
- package/dist/services/context-pack-service.js.map +1 -0
- package/dist/services/distribution-service.d.ts +18 -0
- package/dist/services/distribution-service.d.ts.map +1 -0
- package/dist/services/distribution-service.js +104 -0
- package/dist/services/distribution-service.js.map +1 -0
- package/dist/services/evidence-service.d.ts +17 -0
- package/dist/services/evidence-service.d.ts.map +1 -0
- package/dist/services/evidence-service.js +85 -0
- package/dist/services/evidence-service.js.map +1 -0
- package/dist/services/github-service.d.ts +28 -1
- package/dist/services/github-service.d.ts.map +1 -1
- package/dist/services/github-service.js +95 -0
- package/dist/services/github-service.js.map +1 -1
- package/dist/services/report-linter-service.d.ts +10 -0
- package/dist/services/report-linter-service.d.ts.map +1 -0
- package/dist/services/report-linter-service.js +95 -0
- package/dist/services/report-linter-service.js.map +1 -0
- package/dist/services/report-service.d.ts +37 -0
- package/dist/services/report-service.d.ts.map +1 -0
- package/dist/services/report-service.js +173 -0
- package/dist/services/report-service.js.map +1 -0
- package/dist/services/standup-parser.d.ts +21 -0
- package/dist/services/standup-parser.d.ts.map +1 -0
- package/dist/services/standup-parser.js +104 -0
- package/dist/services/standup-parser.js.map +1 -0
- package/dist/services/story-standup-service.d.ts +7 -0
- package/dist/services/story-standup-service.d.ts.map +1 -0
- package/dist/services/story-standup-service.js +27 -0
- package/dist/services/story-standup-service.js.map +1 -0
- package/dist/services/template-service.d.ts.map +1 -1
- package/dist/services/template-service.js +1 -0
- package/dist/services/template-service.js.map +1 -1
- package/dist/services/voice-service.d.ts +12 -0
- package/dist/services/voice-service.d.ts.map +1 -0
- package/dist/services/voice-service.js +42 -0
- package/dist/services/voice-service.js.map +1 -0
- package/dist/templates/export/planning-report.html.hbs +10 -0
- package/dist/templates/export/planning-report.md.hbs +11 -0
- package/dist/templates/linter/linter-config.json.hbs +13 -0
- package/dist/templates/reports/executive.md.hbs +28 -0
- package/dist/templates/reports/release-notes.md.hbs +37 -0
- package/dist/templates/reports/retrospective.md.hbs +36 -0
- package/dist/templates/reports/sprint.md.hbs +91 -0
- package/dist/templates/reports/standup.md.hbs +27 -0
- package/dist/templates/reports/weekly.md.hbs +42 -0
- package/dist/templates/voice/standup.md.hbs +20 -0
- package/dist/utils/markdown.d.ts.map +1 -1
- package/dist/utils/markdown.js +11 -5
- package/dist/utils/markdown.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stakeholder report generation from context packs and Handlebars templates.
|
|
3
|
+
*/
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { ensureDir, writeFile } from '../utils/fs.js';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
import { buildStakeholderReportContext } from './context-pack-service.js';
|
|
8
|
+
import { renderTemplate } from './template-service.js';
|
|
9
|
+
const TEMPLATE_BY_TYPE = {
|
|
10
|
+
sprint: 'reports/sprint.md.hbs',
|
|
11
|
+
weekly: 'reports/weekly.md.hbs',
|
|
12
|
+
executive: 'reports/executive.md.hbs',
|
|
13
|
+
standup: 'reports/standup.md.hbs',
|
|
14
|
+
retro: 'reports/retrospective.md.hbs',
|
|
15
|
+
release: 'reports/release-notes.md.hbs',
|
|
16
|
+
};
|
|
17
|
+
export function resolveReportTemplate(reportType) {
|
|
18
|
+
return TEMPLATE_BY_TYPE[reportType];
|
|
19
|
+
}
|
|
20
|
+
export async function generateStakeholderReportMarkdown(context, config) {
|
|
21
|
+
const templatePath = resolveReportTemplate(context.reportType);
|
|
22
|
+
try {
|
|
23
|
+
return await renderTemplate(templatePath, { ...context, json: JSON.stringify(context, null, 2) }, config.templateOverrides);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
logger.debug('report template render failed', err);
|
|
27
|
+
throw new Error(`Failed to render report template "${templatePath}": ${err.message}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function wrapHtmlDocument(title, bodyMarkdownAsHtml, accent) {
|
|
31
|
+
const color = accent || '#4169E1';
|
|
32
|
+
return `<!DOCTYPE html>
|
|
33
|
+
<html lang="en">
|
|
34
|
+
<head>
|
|
35
|
+
<meta charset="utf-8"/>
|
|
36
|
+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
37
|
+
<title>${escapeHtml(title)}</title>
|
|
38
|
+
<style>
|
|
39
|
+
body { font-family: system-ui, sans-serif; max-width: 52rem; margin: 2rem auto; padding: 0 1rem; color: #111; }
|
|
40
|
+
h1 { border-bottom: 2px solid ${color}; padding-bottom: 0.25rem; }
|
|
41
|
+
h2 { margin-top: 1.75rem; color: #222; }
|
|
42
|
+
code, pre { background: #f4f4f4; padding: 0.2em 0.4em; border-radius: 4px; }
|
|
43
|
+
a { color: ${color}; }
|
|
44
|
+
.meta { color: #555; font-size: 0.9rem; }
|
|
45
|
+
</style>
|
|
46
|
+
</head>
|
|
47
|
+
<body>
|
|
48
|
+
<h1>${escapeHtml(title)}</h1>
|
|
49
|
+
<p class="meta">Generated by OpenPlanr</p>
|
|
50
|
+
<div class="content">
|
|
51
|
+
${bodyMarkdownAsHtml}
|
|
52
|
+
</div>
|
|
53
|
+
</body>
|
|
54
|
+
</html>`;
|
|
55
|
+
}
|
|
56
|
+
function escapeHtml(s) {
|
|
57
|
+
return s
|
|
58
|
+
.replace(/&/g, '&')
|
|
59
|
+
.replace(/</g, '<')
|
|
60
|
+
.replace(/>/g, '>')
|
|
61
|
+
.replace(/"/g, '"');
|
|
62
|
+
}
|
|
63
|
+
function isSafeHttpUrl(url) {
|
|
64
|
+
try {
|
|
65
|
+
const u = new URL(url);
|
|
66
|
+
return u.protocol === 'http:' || u.protocol === 'https:';
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Remove the first Markdown H1 line (and following blank lines) so HTML export does not
|
|
74
|
+
* duplicate the document title: wrapHtmlDocument() already injects a single <h1>.
|
|
75
|
+
*/
|
|
76
|
+
export function stripLeadingMarkdownH1(md) {
|
|
77
|
+
const lines = md.split(/\r?\n/);
|
|
78
|
+
if (lines.length === 0)
|
|
79
|
+
return md;
|
|
80
|
+
const first = lines[0]?.trim() ?? '';
|
|
81
|
+
if (!/^#\s+.+$/.test(first))
|
|
82
|
+
return md;
|
|
83
|
+
let i = 1;
|
|
84
|
+
while (i < lines.length && lines[i]?.trim() === '')
|
|
85
|
+
i += 1;
|
|
86
|
+
return lines.slice(i).join('\n');
|
|
87
|
+
}
|
|
88
|
+
/** Minimal markdown → HTML for reports (headings, lists, links, paragraphs). */
|
|
89
|
+
export function markdownToBasicHtml(md) {
|
|
90
|
+
const lines = md.split(/\r?\n/);
|
|
91
|
+
const out = [];
|
|
92
|
+
let inUl = false;
|
|
93
|
+
const flushUl = () => {
|
|
94
|
+
if (inUl) {
|
|
95
|
+
out.push('</ul>');
|
|
96
|
+
inUl = false;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
for (const line of lines) {
|
|
100
|
+
const h2 = line.match(/^## (.+)$/);
|
|
101
|
+
if (h2) {
|
|
102
|
+
flushUl();
|
|
103
|
+
out.push(`<h2>${escapeHtml(h2[1])}</h2>`);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const h1 = line.match(/^# (.+)$/);
|
|
107
|
+
if (h1) {
|
|
108
|
+
flushUl();
|
|
109
|
+
out.push(`<h1>${escapeHtml(h1[1])}</h1>`);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const li = line.match(/^\s*[-*]\s+(.+)$/);
|
|
113
|
+
if (li) {
|
|
114
|
+
if (!inUl) {
|
|
115
|
+
out.push('<ul>');
|
|
116
|
+
inUl = true;
|
|
117
|
+
}
|
|
118
|
+
out.push(`<li>${inlineMd(li[1])}</li>`);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (line.trim() === '') {
|
|
122
|
+
flushUl();
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
flushUl();
|
|
126
|
+
out.push(`<p>${inlineMd(line)}</p>`);
|
|
127
|
+
}
|
|
128
|
+
flushUl();
|
|
129
|
+
return out.join('\n');
|
|
130
|
+
}
|
|
131
|
+
function inlineMd(text) {
|
|
132
|
+
let s = escapeHtml(text);
|
|
133
|
+
s = s.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
134
|
+
s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m, label, urlEsc) => {
|
|
135
|
+
const url = urlEsc.replace(/&/g, '&');
|
|
136
|
+
if (isSafeHttpUrl(url)) {
|
|
137
|
+
return `<a href="${escapeHtml(url)}" rel="noopener noreferrer">${label}</a>`;
|
|
138
|
+
}
|
|
139
|
+
return `${label} (${urlEsc})`;
|
|
140
|
+
});
|
|
141
|
+
return s;
|
|
142
|
+
}
|
|
143
|
+
export async function formatStakeholderReportOutput(context, config, format) {
|
|
144
|
+
const markdown = await generateStakeholderReportMarkdown(context, config);
|
|
145
|
+
if (format === 'html') {
|
|
146
|
+
const inner = markdownToBasicHtml(stripLeadingMarkdownH1(markdown));
|
|
147
|
+
const title = `${context.projectName} — ${context.reportType} report`;
|
|
148
|
+
const html = wrapHtmlDocument(title, inner, context.branding?.accentColor);
|
|
149
|
+
return { markdown, html };
|
|
150
|
+
}
|
|
151
|
+
return { markdown };
|
|
152
|
+
}
|
|
153
|
+
export async function generateStakeholderReport(projectDir, config, opts) {
|
|
154
|
+
const context = await buildStakeholderReportContext(projectDir, config, opts);
|
|
155
|
+
const { markdown, html } = await formatStakeholderReportOutput(context, config, opts.format);
|
|
156
|
+
return { markdown, html, context };
|
|
157
|
+
}
|
|
158
|
+
export async function writeReportOutputs(args) {
|
|
159
|
+
const rel = args.outputDir ?? path.join(args.config.outputPaths.agile, 'reports');
|
|
160
|
+
const dir = path.join(args.projectDir, rel);
|
|
161
|
+
await ensureDir(dir);
|
|
162
|
+
const stamp = new Date().toISOString().split('T')[0];
|
|
163
|
+
const safe = args.baseName.replace(/[^a-z0-9-_]+/gi, '-').toLowerCase();
|
|
164
|
+
const mdPath = path.join(dir, `${stamp}-${safe}.md`);
|
|
165
|
+
await writeFile(mdPath, args.markdown);
|
|
166
|
+
let htmlPath;
|
|
167
|
+
if (args.html) {
|
|
168
|
+
htmlPath = path.join(dir, `${stamp}-${safe}.html`);
|
|
169
|
+
await writeFile(htmlPath, args.html);
|
|
170
|
+
}
|
|
171
|
+
return { mdPath, htmlPath };
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=report-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-service.js","sourceRoot":"","sources":["../../src/services/report-service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAO7B,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAA4B,6BAA6B,EAAE,MAAM,2BAA2B,CAAC;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,gBAAgB,GAA0C;IAC9D,MAAM,EAAE,uBAAuB;IAC/B,MAAM,EAAE,uBAAuB;IAC/B,SAAS,EAAE,0BAA0B;IACrC,OAAO,EAAE,wBAAwB;IACjC,KAAK,EAAE,8BAA8B;IACrC,OAAO,EAAE,8BAA8B;CACxC,CAAC;AAEF,MAAM,UAAU,qBAAqB,CAAC,UAAiC;IACrE,OAAO,gBAAgB,CAAC,UAAU,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iCAAiC,CACrD,OAAiC,EACjC,MAAuB;IAEvB,MAAM,YAAY,GAAG,qBAAqB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,IAAI,CAAC;QACH,OAAO,MAAM,cAAc,CACzB,YAAY,EACZ,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EACtD,MAAM,CAAC,iBAAiB,CACzB,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE,GAAG,CAAC,CAAC;QACnD,MAAM,IAAI,KAAK,CACb,qCAAqC,YAAY,MAAO,GAAa,CAAC,OAAO,EAAE,CAChF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa,EAAE,kBAA0B,EAAE,MAAe;IAClF,MAAM,KAAK,GAAG,MAAM,IAAI,SAAS,CAAC;IAClC,OAAO;;;;;WAKE,UAAU,CAAC,KAAK,CAAC;;;oCAGQ,KAAK;;;iBAGxB,KAAK;;;;;QAKd,UAAU,CAAC,KAAK,CAAC;;;EAGvB,kBAAkB;;;QAGZ,CAAC;AACT,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,EAAU;IAC/C,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAChC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACrC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACvC,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE;QAAE,CAAC,IAAI,CAAC,CAAC;IAC3D,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,mBAAmB,CAAC,EAAU;IAC5C,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAChC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,IAAI,IAAI,EAAE,CAAC;YACT,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAClB,IAAI,GAAG,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACnC,IAAI,EAAE,EAAE,CAAC;YACP,OAAO,EAAE,CAAC;YACV,GAAG,CAAC,IAAI,CAAC,OAAO,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YAC1C,SAAS;QACX,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAClC,IAAI,EAAE,EAAE,CAAC;YACP,OAAO,EAAE,CAAC;YACV,GAAG,CAAC,IAAI,CAAC,OAAO,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YAC1C,SAAS;QACX,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAC1C,IAAI,EAAE,EAAE,CAAC;YACP,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACjB,IAAI,GAAG,IAAI,CAAC;YACd,CAAC;YACD,GAAG,CAAC,IAAI,CAAC,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACxC,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACvB,OAAO,EAAE,CAAC;YACV,SAAS;QACX,CAAC;QACD,OAAO,EAAE,CAAC;QACV,GAAG,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,EAAE,CAAC;IACV,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC;IAC/C,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,0BAA0B,EAAE,CAAC,EAAE,EAAE,KAAa,EAAE,MAAc,EAAE,EAAE;QAC9E,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAC1C,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,YAAY,UAAU,CAAC,GAAG,CAAC,+BAA+B,KAAK,MAAM,CAAC;QAC/E,CAAC;QACD,OAAO,GAAG,KAAK,KAAK,MAAM,GAAG,CAAC;IAChC,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,OAAiC,EACjC,MAAuB,EACvB,MAA+B;IAE/B,MAAM,QAAQ,GAAG,MAAM,iCAAiC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1E,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,mBAAmB,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,GAAG,OAAO,CAAC,WAAW,MAAM,OAAO,CAAC,UAAU,SAAS,CAAC;QACtE,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QAC3E,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC5B,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,UAAkB,EAClB,MAAuB,EACvB,IAA+D;IAE/D,MAAM,OAAO,GAAG,MAAM,6BAA6B,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAC9E,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,MAAM,6BAA6B,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7F,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACrC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAOxC;IACC,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAClF,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IAC5C,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;IACrB,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IACxE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC;IACrD,MAAM,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,QAA4B,CAAC;IACjC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,IAAI,OAAO,CAAC,CAAC;QACnD,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heuristic parsing of a standup transcript into yesterday / today / blockers.
|
|
3
|
+
*/
|
|
4
|
+
export interface TranscriptSegment {
|
|
5
|
+
section: 'yesterday' | 'today' | 'blockers' | 'note';
|
|
6
|
+
text: string;
|
|
7
|
+
/** Placeholder for future audio/text sync (milliseconds from clip start) */
|
|
8
|
+
audioOffsetMs?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ParsedStandup {
|
|
11
|
+
yesterday: string[];
|
|
12
|
+
today: string[];
|
|
13
|
+
blockers: string[];
|
|
14
|
+
incomplete: boolean;
|
|
15
|
+
notes: string[];
|
|
16
|
+
/** Best-effort line segments (no audio yet — offsets unused until capture exists) */
|
|
17
|
+
segments: TranscriptSegment[];
|
|
18
|
+
}
|
|
19
|
+
export declare function parseStandupTranscript(raw: string): ParsedStandup;
|
|
20
|
+
export declare function formatStandupMarkdown(parsed: ParsedStandup): string;
|
|
21
|
+
//# sourceMappingURL=standup-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standup-parser.d.ts","sourceRoot":"","sources":["../../src/services/standup-parser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,qFAAqF;IACrF,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAMD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CA0EjE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAoBnE"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heuristic parsing of a standup transcript into yesterday / today / blockers.
|
|
3
|
+
*/
|
|
4
|
+
const YDAY = /^(yesterday|last day|previously|done)\s*[:-]?\s*(.+)$/i;
|
|
5
|
+
const TODAY = /^(today|now|this session|working on)\s*[:-]?\s*(.+)$/i;
|
|
6
|
+
const BLOCK = /^(blockers?|blocked|impediments?)\s*[:-]?\s*(.+)$/i;
|
|
7
|
+
export function parseStandupTranscript(raw) {
|
|
8
|
+
const yesterday = [];
|
|
9
|
+
const today = [];
|
|
10
|
+
const blockers = [];
|
|
11
|
+
const notes = [];
|
|
12
|
+
const segments = [];
|
|
13
|
+
const lines = raw
|
|
14
|
+
.split(/\r?\n/)
|
|
15
|
+
.map((l) => l.trim())
|
|
16
|
+
.filter(Boolean);
|
|
17
|
+
let mode = null;
|
|
18
|
+
for (const line of lines) {
|
|
19
|
+
let m = line.match(YDAY);
|
|
20
|
+
if (m) {
|
|
21
|
+
mode = 'y';
|
|
22
|
+
const t = m[2].trim();
|
|
23
|
+
yesterday.push(t);
|
|
24
|
+
segments.push({ section: 'yesterday', text: t });
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
m = line.match(TODAY);
|
|
28
|
+
if (m) {
|
|
29
|
+
mode = 't';
|
|
30
|
+
const t = m[2].trim();
|
|
31
|
+
today.push(t);
|
|
32
|
+
segments.push({ section: 'today', text: t });
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
m = line.match(BLOCK);
|
|
36
|
+
if (m) {
|
|
37
|
+
mode = 'b';
|
|
38
|
+
const t = m[2].trim();
|
|
39
|
+
blockers.push(t);
|
|
40
|
+
segments.push({ section: 'blockers', text: t });
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (/^[-*]\s+/.test(line)) {
|
|
44
|
+
const item = line.replace(/^[-*]\s+/, '').trim();
|
|
45
|
+
if (mode === 'y') {
|
|
46
|
+
yesterday.push(item);
|
|
47
|
+
segments.push({ section: 'yesterday', text: item });
|
|
48
|
+
}
|
|
49
|
+
else if (mode === 't') {
|
|
50
|
+
today.push(item);
|
|
51
|
+
segments.push({ section: 'today', text: item });
|
|
52
|
+
}
|
|
53
|
+
else if (mode === 'b') {
|
|
54
|
+
blockers.push(item);
|
|
55
|
+
segments.push({ section: 'blockers', text: item });
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
notes.push(item);
|
|
59
|
+
segments.push({ section: 'note', text: item });
|
|
60
|
+
}
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (mode === 'y') {
|
|
64
|
+
yesterday.push(line);
|
|
65
|
+
segments.push({ section: 'yesterday', text: line });
|
|
66
|
+
}
|
|
67
|
+
else if (mode === 't') {
|
|
68
|
+
today.push(line);
|
|
69
|
+
segments.push({ section: 'today', text: line });
|
|
70
|
+
}
|
|
71
|
+
else if (mode === 'b') {
|
|
72
|
+
blockers.push(line);
|
|
73
|
+
segments.push({ section: 'blockers', text: line });
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
notes.push(line);
|
|
77
|
+
segments.push({ section: 'note', text: line });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const incomplete = yesterday.length === 0 || today.length === 0;
|
|
81
|
+
return { yesterday, today, blockers, incomplete, notes, segments };
|
|
82
|
+
}
|
|
83
|
+
export function formatStandupMarkdown(parsed) {
|
|
84
|
+
const sec = (title, items) => {
|
|
85
|
+
const body = items.length > 0 ? items.map((i) => `- ${i}`).join('\n') : '_— none recorded —_';
|
|
86
|
+
return `## ${title}\n\n${body}`;
|
|
87
|
+
};
|
|
88
|
+
return [
|
|
89
|
+
'# Standup',
|
|
90
|
+
'',
|
|
91
|
+
sec('Yesterday', parsed.yesterday),
|
|
92
|
+
'',
|
|
93
|
+
sec('Today', parsed.today),
|
|
94
|
+
'',
|
|
95
|
+
sec('Blockers', parsed.blockers),
|
|
96
|
+
parsed.notes.length > 0 ? `\n## Notes\n\n${parsed.notes.map((n) => `- ${n}`).join('\n')}` : '',
|
|
97
|
+
parsed.incomplete
|
|
98
|
+
? '\n> **Note:** Some standup sections look incomplete. Fill in missing parts before sending.\n'
|
|
99
|
+
: '',
|
|
100
|
+
]
|
|
101
|
+
.filter(Boolean)
|
|
102
|
+
.join('\n');
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=standup-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standup-parser.js","sourceRoot":"","sources":["../../src/services/standup-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Append generated standup markdown to a user story file.
|
|
3
|
+
*/
|
|
4
|
+
import type { OpenPlanrConfig } from '../models/types.js';
|
|
5
|
+
export declare function injectStandupSection(raw: string, standupMarkdown: string, date: string): string;
|
|
6
|
+
export declare function appendStandupToStory(projectDir: string, config: OpenPlanrConfig, storyId: string, standupMarkdown: string): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=story-standup-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"story-standup-service.d.ts","sourceRoot":"","sources":["../../src/services/story-standup-service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAK1D,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAe/F;AAED,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAOf"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Append generated standup markdown to a user story file.
|
|
3
|
+
*/
|
|
4
|
+
import { readArtifactRaw, updateArtifact } from './artifact-service.js';
|
|
5
|
+
const STANDUP_SECTION = '## Standup notes';
|
|
6
|
+
export function injectStandupSection(raw, standupMarkdown, date) {
|
|
7
|
+
const block = `\n### ${date}\n\n${standupMarkdown.trim()}\n`;
|
|
8
|
+
if (raw.includes(STANDUP_SECTION)) {
|
|
9
|
+
const idx = raw.indexOf(STANDUP_SECTION) + STANDUP_SECTION.length;
|
|
10
|
+
return `${raw.slice(0, idx)}${block}${raw.slice(idx)}`;
|
|
11
|
+
}
|
|
12
|
+
const tasksMatch = /\n## Tasks\b/m.exec(raw);
|
|
13
|
+
if (tasksMatch?.index !== undefined) {
|
|
14
|
+
const i = tasksMatch.index;
|
|
15
|
+
return `${raw.slice(0, i)}\n\n${STANDUP_SECTION}\n${block}\n${raw.slice(i)}`;
|
|
16
|
+
}
|
|
17
|
+
return `${raw.trimEnd()}\n\n${STANDUP_SECTION}\n${block}\n`;
|
|
18
|
+
}
|
|
19
|
+
export async function appendStandupToStory(projectDir, config, storyId, standupMarkdown) {
|
|
20
|
+
const raw = await readArtifactRaw(projectDir, config, 'story', storyId);
|
|
21
|
+
if (!raw)
|
|
22
|
+
throw new Error(`Story ${storyId} not found.`);
|
|
23
|
+
const date = new Date().toISOString().split('T')[0];
|
|
24
|
+
const next = injectStandupSection(raw, standupMarkdown, date);
|
|
25
|
+
await updateArtifact(projectDir, config, 'story', storyId, next);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=story-standup-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"story-standup-service.js","sourceRoot":"","sources":["../../src/services/story-standup-service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAExE,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAE3C,MAAM,UAAU,oBAAoB,CAAC,GAAW,EAAE,eAAuB,EAAE,IAAY;IACrF,MAAM,KAAK,GAAG,SAAS,IAAI,OAAO,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC;IAE7D,IAAI,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe,CAAC,MAAM,CAAC;QAClE,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IACzD,CAAC;IAED,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,UAAU,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;QACpC,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC;QAC3B,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,eAAe,KAAK,KAAK,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/E,CAAC;IAED,OAAO,GAAG,GAAG,CAAC,OAAO,EAAE,OAAO,eAAe,KAAK,KAAK,IAAI,CAAC;AAC9D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,UAAkB,EAClB,MAAuB,EACvB,OAAe,EACf,eAAuB;IAEvB,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACxE,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,SAAS,OAAO,aAAa,CAAC,CAAC;IAEzD,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,oBAAoB,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,CAAC,CAAC;IAC9D,MAAM,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-service.d.ts","sourceRoot":"","sources":["../../src/services/template-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"template-service.d.ts","sourceRoot":"","sources":["../../src/services/template-service.ts"],"names":[],"mappings":"AA2BA,uFAAuF;AACvF,wBAAsB,cAAc,CAClC,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,sCAAsC;AACrE,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,CAYjB"}
|
|
@@ -13,6 +13,7 @@ Handlebars.registerHelper('checkboxList', (items) => {
|
|
|
13
13
|
});
|
|
14
14
|
Handlebars.registerHelper('join', (arr, sep) => Array.isArray(arr) ? arr.join(typeof sep === 'string' ? sep : ', ') : '');
|
|
15
15
|
Handlebars.registerHelper('eq', (a, b) => a === b);
|
|
16
|
+
Handlebars.registerHelper('length', (arr) => (Array.isArray(arr) ? arr.length : 0));
|
|
16
17
|
/** Compile and render a Handlebars template, checking the override directory first. */
|
|
17
18
|
export async function renderTemplate(templatePath, data, // Template data is inherently untyped
|
|
18
19
|
overrideDir) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-service.js","sourceRoot":"","sources":["../../src/services/template-service.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,aAAa,GAAG,IAAI,GAAG,EAAsC,CAAC;AAEpE,UAAU,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhF,UAAU,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,GAAW,EAAE,EAAE,CACrD,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CACjD,CAAC;AAEF,UAAU,CAAC,cAAc,CAAC,cAAc,EAAE,CAAC,KAAe,EAAE,EAAE;IAC5D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzD,CAAC,CAAC,CAAC;AAEH,UAAU,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,GAAa,EAAE,GAAY,EAAE,EAAE,CAChE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CACzE,CAAC;AAEF,UAAU,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAU,EAAE,CAAU,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AAErE,uFAAuF;AACvF,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,YAAoB,EACpB,IAA6B,EAAE,sCAAsC;AACrE,WAAoB;IAEpB,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IACtE,IAAI,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC3C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,CAAC,KAAK,CAAC,uBAAuB,QAAQ,EAAE,CAAC,CAAC;QAChD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrC,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACxC,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,KAAK,CAAC,0BAA0B,QAAQ,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,YAAoB,EAAE,WAAoB;IAC3E,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;QAC1D,IAAI,MAAM,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YACnC,OAAO,YAAY,CAAC;QACtB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,YAAY,CAAC,CAAC;AACpD,CAAC"}
|
|
1
|
+
{"version":3,"file":"template-service.js","sourceRoot":"","sources":["../../src/services/template-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voice-oriented standup workflow: consume transcript text from file or stdin.
|
|
3
|
+
* Live microphone capture can be added later without changing the parser.
|
|
4
|
+
*/
|
|
5
|
+
import type { VoiceStandupSession } from '../models/types.js';
|
|
6
|
+
export declare function loadTranscriptFromFile(path: string): Promise<VoiceStandupSession>;
|
|
7
|
+
export declare function transcriptToStandupMarkdown(transcript: string): string;
|
|
8
|
+
/** Read transcript from `--file` or stdin (must not be empty). */
|
|
9
|
+
export declare function readStandupTranscriptSource(opts: {
|
|
10
|
+
file?: string;
|
|
11
|
+
}): Promise<string>;
|
|
12
|
+
//# sourceMappingURL=voice-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voice-service.d.ts","sourceRoot":"","sources":["../../src/services/voice-service.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAG9D,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAWvF;AAED,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGtE;AAED,kEAAkE;AAClE,wBAAsB,2BAA2B,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAe1F"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voice-oriented standup workflow: consume transcript text from file or stdin.
|
|
3
|
+
* Live microphone capture can be added later without changing the parser.
|
|
4
|
+
*/
|
|
5
|
+
import { readFile } from 'node:fs/promises';
|
|
6
|
+
import { formatStandupMarkdown, parseStandupTranscript } from './standup-parser.js';
|
|
7
|
+
export async function loadTranscriptFromFile(path) {
|
|
8
|
+
try {
|
|
9
|
+
const transcript = await readFile(path, 'utf-8');
|
|
10
|
+
return { status: 'done', transcript: transcript.trim() };
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
return {
|
|
14
|
+
status: 'error',
|
|
15
|
+
transcript: '',
|
|
16
|
+
errorMessage: err.message,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function transcriptToStandupMarkdown(transcript) {
|
|
21
|
+
const parsed = parseStandupTranscript(transcript);
|
|
22
|
+
return formatStandupMarkdown(parsed);
|
|
23
|
+
}
|
|
24
|
+
/** Read transcript from `--file` or stdin (must not be empty). */
|
|
25
|
+
export async function readStandupTranscriptSource(opts) {
|
|
26
|
+
if (opts.file) {
|
|
27
|
+
const session = await loadTranscriptFromFile(opts.file);
|
|
28
|
+
if (session.status === 'error') {
|
|
29
|
+
throw new Error(session.errorMessage || 'Failed to read transcript file');
|
|
30
|
+
}
|
|
31
|
+
return session.transcript;
|
|
32
|
+
}
|
|
33
|
+
const chunks = [];
|
|
34
|
+
for await (const c of process.stdin)
|
|
35
|
+
chunks.push(c);
|
|
36
|
+
const text = Buffer.concat(chunks).toString('utf-8').trim();
|
|
37
|
+
if (!text) {
|
|
38
|
+
throw new Error('Provide --file <path> or pipe transcript text on stdin.');
|
|
39
|
+
}
|
|
40
|
+
return text;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=voice-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voice-service.js","sourceRoot":"","sources":["../../src/services/voice-service.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAEpF,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,IAAY;IACvD,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACjD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;IAC3D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,MAAM,EAAE,OAAO;YACf,UAAU,EAAE,EAAE;YACd,YAAY,EAAG,GAAa,CAAC,OAAO;SACrC,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,UAAkB;IAC5D,MAAM,MAAM,GAAG,sBAAsB,CAAC,UAAU,CAAC,CAAC;IAClD,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC;AAED,kEAAkE;AAClE,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,IAAuB;IACvE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxD,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,IAAI,gCAAgC,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,OAAO,CAAC,UAAU,CAAC;IAC5B,CAAC;IACD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,OAAO,CAAC,KAAK;QAAE,MAAM,CAAC,IAAI,CAAC,CAAW,CAAC,CAAC;IAC9D,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -222,6 +222,16 @@
|
|
|
222
222
|
{{/each}}
|
|
223
223
|
{{/if}}
|
|
224
224
|
|
|
225
|
+
{{#if evidence.length}}
|
|
226
|
+
<h2>Evidence index</h2>
|
|
227
|
+
<p class="subtitle">Flat references for traceability (stories and tasks in this export).</p>
|
|
228
|
+
<ul>
|
|
229
|
+
{{#each evidence}}
|
|
230
|
+
<li><span class="badge badge-pending">{{this.kind}}</span> {{this.label}}{{#if this.detail}} — <em>{{this.detail}}</em>{{/if}}</li>
|
|
231
|
+
{{/each}}
|
|
232
|
+
</ul>
|
|
233
|
+
{{/if}}
|
|
234
|
+
|
|
225
235
|
<footer>
|
|
226
236
|
Exported by <code>planr export</code> on {{date}}.
|
|
227
237
|
</footer>
|
|
@@ -134,6 +134,17 @@
|
|
|
134
134
|
{{{this.body}}}
|
|
135
135
|
|
|
136
136
|
{{/each}}
|
|
137
|
+
{{/if}}
|
|
138
|
+
|
|
139
|
+
{{#if evidence.length}}
|
|
140
|
+
## Evidence index
|
|
141
|
+
|
|
142
|
+
Flat references for traceability (stories and tasks in this export):
|
|
143
|
+
|
|
144
|
+
{{#each evidence}}
|
|
145
|
+
- **{{this.kind}}** — {{this.label}}{{#if this.detail}} — _{{this.detail}}_{{/if}}
|
|
146
|
+
{{/each}}
|
|
147
|
+
|
|
137
148
|
{{/if}}
|
|
138
149
|
---
|
|
139
150
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"rules": [
|
|
3
|
+
{ "id": "evidence-density", "enabled": true, "minEvidenceLinks": 1 },
|
|
4
|
+
{ "id": "weekly-structure", "enabled": true, "requireSections": ["Wins", "Risks", "Ask"] }
|
|
5
|
+
],
|
|
6
|
+
"vaguePhrases": [
|
|
7
|
+
{
|
|
8
|
+
"pattern": "\\\\b(fixed bugs|almost done|making progress)\\\\b",
|
|
9
|
+
"alternatives": ["Closed #12 and #15 (see PR 44)", "Completed 3 of 5 stories"],
|
|
10
|
+
"hint": "Use countable outcomes and links."
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Executive summary — {{projectName}}
|
|
2
|
+
|
|
3
|
+
> {{generatedAt}}
|
|
4
|
+
|
|
5
|
+
## Outcome
|
|
6
|
+
|
|
7
|
+
- **Active epics:** {{length artifacts.epics}}
|
|
8
|
+
- **Stories:** {{length artifacts.stories}} total in planning tree
|
|
9
|
+
|
|
10
|
+
## Momentum
|
|
11
|
+
|
|
12
|
+
{{#if github.pullRequests.length}}
|
|
13
|
+
- **Recent PR activity:** {{length github.pullRequests}} PRs touched in the last {{daysLookback}} days.
|
|
14
|
+
{{else}}
|
|
15
|
+
- **Recent PR activity:** _none in window or GitHub unavailable._
|
|
16
|
+
{{/if}}
|
|
17
|
+
|
|
18
|
+
## Risks & dependencies
|
|
19
|
+
|
|
20
|
+
- _Summarize top 2 risks with mitigation._
|
|
21
|
+
|
|
22
|
+
## Timeline confidence
|
|
23
|
+
|
|
24
|
+
- _On track / at risk — cite evidence IDs below._
|
|
25
|
+
|
|
26
|
+
## Evidence{{#each evidence}}
|
|
27
|
+
- {{this.label}}{{#if this.url}} — {{this.url}}{{/if}}
|
|
28
|
+
{{/each}}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Release notes — {{projectName}}
|
|
2
|
+
|
|
3
|
+
> {{generatedAt}} · draft from planning artifacts + GitHub
|
|
4
|
+
|
|
5
|
+
## Highlights
|
|
6
|
+
|
|
7
|
+
- _User-facing bullets — edit before publishing._
|
|
8
|
+
|
|
9
|
+
## Completed work (planning)
|
|
10
|
+
|
|
11
|
+
{{#each artifacts.stories}}
|
|
12
|
+
{{#if (eq this.status 'done')}}
|
|
13
|
+
- `{{this.id}}` — {{this.title}}
|
|
14
|
+
{{/if}}
|
|
15
|
+
{{/each}}
|
|
16
|
+
|
|
17
|
+
{{#unless artifacts.stories}}
|
|
18
|
+
_No story data._
|
|
19
|
+
{{/unless}}
|
|
20
|
+
|
|
21
|
+
## Merged / shipped (GitHub)
|
|
22
|
+
|
|
23
|
+
{{#if github.pullRequests.length}}
|
|
24
|
+
{{#each github.pullRequests}}
|
|
25
|
+
{{#if this.mergedAt}}
|
|
26
|
+
- [PR #{{this.number}}]({{this.url}}) — {{this.title}}
|
|
27
|
+
{{/if}}
|
|
28
|
+
{{/each}}
|
|
29
|
+
{{else}}
|
|
30
|
+
_No PR metadata — add manually._
|
|
31
|
+
{{/if}}
|
|
32
|
+
|
|
33
|
+
## Evidence
|
|
34
|
+
|
|
35
|
+
{{#each evidence}}
|
|
36
|
+
- **{{this.kind}}** — {{this.label}}{{#if this.url}} — {{this.url}}{{/if}}
|
|
37
|
+
{{/each}}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Sprint retrospective — {{projectName}}
|
|
2
|
+
|
|
3
|
+
> {{generatedAt}}
|
|
4
|
+
|
|
5
|
+
{{#if sprint}}
|
|
6
|
+
_Sprint: {{sprint.sprintId}} — {{sprint.name}} ({{sprint.status}})_
|
|
7
|
+
{{/if}}
|
|
8
|
+
|
|
9
|
+
## What went well
|
|
10
|
+
|
|
11
|
+
-
|
|
12
|
+
|
|
13
|
+
## What did not go well
|
|
14
|
+
|
|
15
|
+
-
|
|
16
|
+
|
|
17
|
+
## Action items
|
|
18
|
+
|
|
19
|
+
| Action | Owner | Due |
|
|
20
|
+
|--------|-------|-----|
|
|
21
|
+
| | | |
|
|
22
|
+
|
|
23
|
+
## Signals (automated)
|
|
24
|
+
|
|
25
|
+
{{#if github.commits.length}}
|
|
26
|
+
- Commits in window: {{length github.commits}}
|
|
27
|
+
{{/if}}
|
|
28
|
+
{{#if github.pullRequests.length}}
|
|
29
|
+
- PRs in window: {{length github.pullRequests}}
|
|
30
|
+
{{/if}}
|
|
31
|
+
|
|
32
|
+
## Evidence
|
|
33
|
+
|
|
34
|
+
{{#each evidence}}
|
|
35
|
+
- {{this.label}}{{#if this.url}} — {{this.url}}{{/if}}
|
|
36
|
+
{{/each}}
|