openplanr 3.5.0 → 3.6.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 +31 -0
- package/dist/cli/commands/backlog-commands.d.ts +3 -0
- package/dist/cli/commands/backlog-commands.d.ts.map +1 -0
- package/dist/cli/commands/backlog-commands.js +60 -0
- package/dist/cli/commands/backlog-commands.js.map +1 -0
- package/dist/cli/commands/company.d.ts.map +1 -1
- package/dist/cli/commands/company.js +17 -0
- package/dist/cli/commands/company.js.map +1 -1
- package/dist/cli/commands/planning-artifacts.d.ts.map +1 -1
- package/dist/cli/commands/planning-artifacts.js +13 -5
- package/dist/cli/commands/planning-artifacts.js.map +1 -1
- package/dist/dashboard/.vite/manifest.json +1 -1
- package/dist/dashboard/assets/{index-B-l_941W.js → index-C_OquZVk.js} +2 -2
- package/dist/dashboard/assets/{index-B-l_941W.js.map → index-C_OquZVk.js.map} +1 -1
- package/dist/dashboard/dashboard-manifest.json +8 -8
- package/dist/dashboard/index.html +1 -1
- package/dist/services/artifact-service.d.ts.map +1 -1
- package/dist/services/artifact-service.js +13 -6
- package/dist/services/artifact-service.js.map +1 -1
- package/dist/services/company-export-service.d.ts +41 -0
- package/dist/services/company-export-service.d.ts.map +1 -0
- package/dist/services/company-export-service.js +429 -0
- package/dist/services/company-export-service.js.map +1 -0
- package/dist/services/company-sync-service.d.ts +24 -1
- package/dist/services/company-sync-service.d.ts.map +1 -1
- package/dist/services/company-sync-service.js +36 -19
- package/dist/services/company-sync-service.js.map +1 -1
- package/docs/CLI.md +97 -1
- package/lib/backlog-item.d.mts +45 -1
- package/lib/backlog-item.mjs +302 -34
- package/lib/host-packages/adapter-registry.json +4 -4
- package/lib/host-packages/capability-map.json +1 -1
- package/lib/host-packages/claude/.claude-plugin/marketplace.json +2 -2
- package/lib/host-packages/claude/openplanr/.claude-plugin/icon.png +0 -0
- package/lib/host-packages/claude/openplanr/.claude-plugin/plugin.json +1 -1
- package/lib/host-packages/claude/openplanr/.openplanr-content.json +77 -77
- package/lib/host-packages/claude/openplanr/README.md +1 -1
- package/lib/host-packages/claude/openplanr/skills/ceo-review/references/note-checklist.md +131 -0
- package/lib/host-packages/claude/openplanr/skills/ceo-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/claude/openplanr/skills/chair-review/SKILL.md +2 -7
- package/lib/host-packages/claude/openplanr/skills/chair-review/references/note-checklist.md +131 -0
- package/lib/host-packages/claude/openplanr/skills/challenger-review/SKILL.md +3 -7
- package/lib/host-packages/claude/openplanr/skills/challenger-review/references/note-checklist.md +131 -0
- package/lib/host-packages/claude/openplanr/skills/cmo-review/references/note-checklist.md +131 -0
- package/lib/host-packages/claude/openplanr/skills/cmo-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/claude/openplanr/skills/coo-review/references/note-checklist.md +131 -0
- package/lib/host-packages/claude/openplanr/skills/coo-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/claude/openplanr/skills/cpo-review/references/note-checklist.md +131 -0
- package/lib/host-packages/claude/openplanr/skills/cpo-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/claude/openplanr/skills/cto-review/references/note-checklist.md +131 -0
- package/lib/host-packages/claude/openplanr/skills/cto-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/claude/openplanr/skills/investigate/SKILL.md +2 -3
- package/lib/host-packages/claude/openplanr/skills/investigate/references/backlog-filing.md +102 -0
- package/lib/host-packages/claude/openplanr/skills/investigate/references/findings.md +8 -16
- package/lib/host-packages/claude/openplanr/skills/operate/SKILL.md +73 -84
- package/lib/host-packages/claude/openplanr/skills/operate/references/note-checklist.md +131 -0
- package/lib/host-packages/claude/openplanr/skills/operate/references/return-contract.md +10 -0
- package/lib/host-packages/claude/openplanr/skills/plan/SKILL.md +16 -17
- package/lib/host-packages/claude/openplanr/skills/plan/references/planning-ids.md +53 -0
- package/lib/host-packages/claude/openplanr/skills/plan/references/return-contract.md +10 -0
- package/lib/host-packages/claude/openplanr/skills/plan-review/SKILL.md +6 -6
- package/lib/host-packages/claude/openplanr/skills/plan-review/references/return-contract.md +10 -0
- package/lib/host-packages/claude/openplanr/skills/release/SKILL.md +40 -139
- package/lib/host-packages/claude/openplanr/skills/release/references/cadence.md +10 -0
- package/lib/host-packages/claude/openplanr/skills/release/references/release-notes.md +1 -1
- package/lib/host-packages/claude/openplanr/skills/release/references/return-contract.md +10 -0
- package/lib/host-packages/claude/openplanr/skills/release/references/schemes.md +4 -2
- package/lib/host-packages/claude/openplanr/skills/ship/SKILL.md +37 -24
- package/lib/host-packages/claude/openplanr/skills/ship/references/backlog-filing.md +102 -0
- package/lib/host-packages/claude/openplanr/skills/ship/references/findings.md +8 -16
- package/lib/host-packages/claude/openplanr/skills/ship/references/return-contract.md +10 -0
- package/lib/host-packages/claude/openplanr/skills/spec/SKILL.md +9 -9
- package/lib/host-packages/claude/openplanr/skills/spec/references/return-contract.md +10 -0
- package/lib/host-packages/cursor/openplanr/.openplanr-content.json +74 -74
- package/lib/host-packages/cursor/openplanr/manifest.json +1 -1
- package/lib/host-packages/cursor/openplanr/rules/planr-ceo-review/references/note-checklist.md +131 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-ceo-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/cursor/openplanr/rules/planr-chair-review/references/note-checklist.md +131 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-chair-review.mdc +2 -7
- package/lib/host-packages/cursor/openplanr/rules/planr-challenger-review/references/note-checklist.md +131 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-challenger-review.mdc +3 -7
- package/lib/host-packages/cursor/openplanr/rules/planr-cmo-review/references/note-checklist.md +131 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-cmo-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/cursor/openplanr/rules/planr-coo-review/references/note-checklist.md +131 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-coo-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/cursor/openplanr/rules/planr-cpo-review/references/note-checklist.md +131 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-cpo-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/cursor/openplanr/rules/planr-cto-review/references/note-checklist.md +131 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-cto-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/cursor/openplanr/rules/planr-investigate/references/backlog-filing.md +102 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-investigate/references/findings.md +8 -16
- package/lib/host-packages/cursor/openplanr/rules/planr-investigate.mdc +2 -3
- package/lib/host-packages/cursor/openplanr/rules/planr-operate/references/note-checklist.md +131 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-operate/references/return-contract.md +10 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-operate.mdc +73 -84
- package/lib/host-packages/cursor/openplanr/rules/planr-plan/references/planning-ids.md +53 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-plan/references/return-contract.md +10 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-plan-review/references/return-contract.md +10 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-plan-review.mdc +6 -6
- package/lib/host-packages/cursor/openplanr/rules/planr-plan.mdc +16 -17
- package/lib/host-packages/cursor/openplanr/rules/planr-release/references/cadence.md +10 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-release/references/release-notes.md +1 -1
- package/lib/host-packages/cursor/openplanr/rules/planr-release/references/return-contract.md +10 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-release/references/schemes.md +4 -2
- package/lib/host-packages/cursor/openplanr/rules/planr-release.mdc +40 -139
- package/lib/host-packages/cursor/openplanr/rules/planr-ship/references/backlog-filing.md +102 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-ship/references/findings.md +8 -16
- package/lib/host-packages/cursor/openplanr/rules/planr-ship/references/return-contract.md +10 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-ship.mdc +37 -24
- package/lib/host-packages/cursor/openplanr/rules/planr-spec/references/return-contract.md +10 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-spec.mdc +9 -9
- package/lib/host-packages/openai/.claude-plugin/marketplace.json +2 -2
- package/lib/host-packages/openai/openplanr/.codex-plugin/plugin.json +1 -1
- package/lib/host-packages/openai/openplanr/.openplanr-content.json +89 -89
- package/lib/host-packages/openai/openplanr/skills/ceo-review/openplanr.skill.json +4 -4
- package/lib/host-packages/openai/openplanr/skills/ceo-review/references/note-checklist.md +131 -0
- package/lib/host-packages/openai/openplanr/skills/ceo-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/openai/openplanr/skills/chair-review/SKILL.md +2 -7
- package/lib/host-packages/openai/openplanr/skills/chair-review/openplanr.skill.json +4 -4
- package/lib/host-packages/openai/openplanr/skills/chair-review/references/note-checklist.md +131 -0
- package/lib/host-packages/openai/openplanr/skills/challenger-review/SKILL.md +3 -7
- package/lib/host-packages/openai/openplanr/skills/challenger-review/openplanr.skill.json +4 -4
- package/lib/host-packages/openai/openplanr/skills/challenger-review/references/note-checklist.md +131 -0
- package/lib/host-packages/openai/openplanr/skills/cmo-review/openplanr.skill.json +4 -4
- package/lib/host-packages/openai/openplanr/skills/cmo-review/references/note-checklist.md +131 -0
- package/lib/host-packages/openai/openplanr/skills/cmo-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/openai/openplanr/skills/coo-review/openplanr.skill.json +4 -4
- package/lib/host-packages/openai/openplanr/skills/coo-review/references/note-checklist.md +131 -0
- package/lib/host-packages/openai/openplanr/skills/coo-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/openai/openplanr/skills/cpo-review/openplanr.skill.json +4 -4
- package/lib/host-packages/openai/openplanr/skills/cpo-review/references/note-checklist.md +131 -0
- package/lib/host-packages/openai/openplanr/skills/cpo-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/openai/openplanr/skills/cto-review/openplanr.skill.json +4 -4
- package/lib/host-packages/openai/openplanr/skills/cto-review/references/note-checklist.md +131 -0
- package/lib/host-packages/openai/openplanr/skills/cto-review/references/operate-advisor-contract.md +2 -7
- package/lib/host-packages/openai/openplanr/skills/investigate/SKILL.md +2 -3
- package/lib/host-packages/openai/openplanr/skills/investigate/openplanr.skill.json +4 -4
- package/lib/host-packages/openai/openplanr/skills/investigate/references/backlog-filing.md +102 -0
- package/lib/host-packages/openai/openplanr/skills/investigate/references/findings.md +8 -16
- package/lib/host-packages/openai/openplanr/skills/operate/SKILL.md +73 -84
- package/lib/host-packages/openai/openplanr/skills/operate/openplanr.skill.json +15 -4
- package/lib/host-packages/openai/openplanr/skills/operate/references/note-checklist.md +131 -0
- package/lib/host-packages/openai/openplanr/skills/operate/references/return-contract.md +10 -0
- package/lib/host-packages/openai/openplanr/skills/plan/SKILL.md +16 -17
- package/lib/host-packages/openai/openplanr/skills/plan/openplanr.skill.json +15 -4
- package/lib/host-packages/openai/openplanr/skills/plan/references/planning-ids.md +53 -0
- package/lib/host-packages/openai/openplanr/skills/plan/references/return-contract.md +10 -0
- package/lib/host-packages/openai/openplanr/skills/plan-review/SKILL.md +6 -6
- package/lib/host-packages/openai/openplanr/skills/plan-review/openplanr.skill.json +12 -1
- package/lib/host-packages/openai/openplanr/skills/plan-review/references/return-contract.md +10 -0
- package/lib/host-packages/openai/openplanr/skills/release/SKILL.md +40 -139
- package/lib/host-packages/openai/openplanr/skills/release/openplanr.skill.json +4 -4
- package/lib/host-packages/openai/openplanr/skills/release/references/cadence.md +10 -0
- package/lib/host-packages/openai/openplanr/skills/release/references/release-notes.md +1 -1
- package/lib/host-packages/openai/openplanr/skills/release/references/return-contract.md +10 -0
- package/lib/host-packages/openai/openplanr/skills/release/references/schemes.md +4 -2
- package/lib/host-packages/openai/openplanr/skills/ship/SKILL.md +37 -24
- package/lib/host-packages/openai/openplanr/skills/ship/openplanr.skill.json +9 -9
- package/lib/host-packages/openai/openplanr/skills/ship/references/backlog-filing.md +102 -0
- package/lib/host-packages/openai/openplanr/skills/ship/references/findings.md +8 -16
- package/lib/host-packages/openai/openplanr/skills/ship/references/return-contract.md +10 -0
- package/lib/host-packages/openai/openplanr/skills/spec/SKILL.md +9 -9
- package/lib/host-packages/openai/openplanr/skills/spec/openplanr.skill.json +12 -1
- package/lib/host-packages/openai/openplanr/skills/spec/references/return-contract.md +10 -0
- package/package.json +1 -1
- package/lib/host-packages/claude/openplanr/.claude-plugin/icon.svg +0 -11
- package/lib/host-packages/claude/openplanr/runtime/operate/scripts/errors.mjs +0 -8
- package/lib/host-packages/claude/openplanr/runtime/operate/scripts/operate-review-contract.mjs +0 -428
- package/lib/host-packages/claude/openplanr/runtime/operate/scripts/operate-review-note.mjs +0 -686
- package/lib/host-packages/claude/openplanr/runtime/operate/scripts/validate-note.mjs +0 -39
- package/lib/host-packages/claude/openplanr/skills/ceo-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/claude/openplanr/skills/chair-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/claude/openplanr/skills/challenger-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/claude/openplanr/skills/cmo-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/claude/openplanr/skills/coo-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/claude/openplanr/skills/cpo-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/claude/openplanr/skills/cto-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/claude/openplanr/skills/investigate/scripts/backlog-item.mjs +0 -339
- package/lib/host-packages/claude/openplanr/skills/operate/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/claude/openplanr/skills/plan/scripts/planning-ids.mjs +0 -166
- package/lib/host-packages/claude/openplanr/skills/release/scripts/collect_changes.sh +0 -85
- package/lib/host-packages/claude/openplanr/skills/ship/scripts/backlog-item.mjs +0 -339
- package/lib/host-packages/claude/openplanr/skills/ship/scripts/discover-verification.mjs +0 -108
- package/lib/host-packages/cursor/openplanr/rules/planr-ceo-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/cursor/openplanr/rules/planr-chair-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/cursor/openplanr/rules/planr-challenger-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/cursor/openplanr/rules/planr-cmo-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/cursor/openplanr/rules/planr-coo-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/cursor/openplanr/rules/planr-cpo-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/cursor/openplanr/rules/planr-cto-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/cursor/openplanr/rules/planr-investigate/scripts/backlog-item.mjs +0 -339
- package/lib/host-packages/cursor/openplanr/rules/planr-operate/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/cursor/openplanr/rules/planr-plan/scripts/planning-ids.mjs +0 -166
- package/lib/host-packages/cursor/openplanr/rules/planr-release/scripts/collect_changes.sh +0 -85
- package/lib/host-packages/cursor/openplanr/rules/planr-ship/scripts/backlog-item.mjs +0 -339
- package/lib/host-packages/cursor/openplanr/rules/planr-ship/scripts/discover-verification.mjs +0 -108
- package/lib/host-packages/cursor/openplanr/runtime/operate/scripts/errors.mjs +0 -8
- package/lib/host-packages/cursor/openplanr/runtime/operate/scripts/operate-review-contract.mjs +0 -428
- package/lib/host-packages/cursor/openplanr/runtime/operate/scripts/operate-review-note.mjs +0 -686
- package/lib/host-packages/cursor/openplanr/runtime/operate/scripts/validate-note.mjs +0 -39
- package/lib/host-packages/openai/openplanr/runtime/operate/scripts/errors.mjs +0 -8
- package/lib/host-packages/openai/openplanr/runtime/operate/scripts/operate-review-contract.mjs +0 -428
- package/lib/host-packages/openai/openplanr/runtime/operate/scripts/operate-review-note.mjs +0 -686
- package/lib/host-packages/openai/openplanr/runtime/operate/scripts/validate-note.mjs +0 -39
- package/lib/host-packages/openai/openplanr/skills/ceo-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/openai/openplanr/skills/chair-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/openai/openplanr/skills/challenger-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/openai/openplanr/skills/cmo-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/openai/openplanr/skills/coo-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/openai/openplanr/skills/cpo-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/openai/openplanr/skills/cto-review/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/openai/openplanr/skills/investigate/scripts/backlog-item.mjs +0 -339
- package/lib/host-packages/openai/openplanr/skills/operate/scripts/validate-note.mjs +0 -12
- package/lib/host-packages/openai/openplanr/skills/plan/scripts/planning-ids.mjs +0 -166
- package/lib/host-packages/openai/openplanr/skills/release/scripts/collect_changes.sh +0 -85
- package/lib/host-packages/openai/openplanr/skills/ship/scripts/backlog-item.mjs +0 -339
- package/lib/host-packages/openai/openplanr/skills/ship/scripts/discover-verification.mjs +0 -108
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { existsSync, realpathSync } from 'node:fs';
|
|
4
|
-
import { readFile } from 'node:fs/promises';
|
|
5
|
-
import { resolve } from 'node:path';
|
|
6
|
-
import { fileURLToPath } from 'node:url';
|
|
7
|
-
|
|
8
|
-
import { inspectOperateReviewNote } from './operate-review-note.mjs';
|
|
9
|
-
|
|
10
|
-
function option(argv, name, fallback) {
|
|
11
|
-
const index = argv.indexOf(name);
|
|
12
|
-
return index >= 0 ? argv[index + 1] : fallback;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function runOperateReviewNoteValidator(argv = process.argv.slice(2)) {
|
|
16
|
-
const file = argv[0];
|
|
17
|
-
if (!file)
|
|
18
|
-
throw new Error(
|
|
19
|
-
'Usage: validate-note.mjs <markdown> --profile <profile> [--contract-version <version>]',
|
|
20
|
-
);
|
|
21
|
-
const result = inspectOperateReviewNote(await readFile(resolve(file), 'utf8'), {
|
|
22
|
-
profile: option(argv, '--profile', 'board-report'),
|
|
23
|
-
contractVersion: option(argv, '--contract-version', 'auto'),
|
|
24
|
-
});
|
|
25
|
-
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
26
|
-
if (!result.ok) process.exitCode = 1;
|
|
27
|
-
return result;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
process.argv[1] &&
|
|
32
|
-
existsSync(resolve(process.argv[1])) &&
|
|
33
|
-
realpathSync(resolve(process.argv[1])) === realpathSync(fileURLToPath(import.meta.url))
|
|
34
|
-
) {
|
|
35
|
-
runOperateReviewNoteValidator().catch((error) => {
|
|
36
|
-
process.stderr.write(`${error.code ?? 'E_OPERATE_NOTE'}: ${error.message}\n`);
|
|
37
|
-
process.exitCode = 1;
|
|
38
|
-
});
|
|
39
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Generated package-relative launcher; no global runtime or invocation downloads.
|
|
3
|
-
import { existsSync, realpathSync } from 'node:fs';
|
|
4
|
-
import { resolve } from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { runOperateReviewNoteValidator as run } from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
7
|
-
export * from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
8
|
-
if (process.argv[1] && existsSync(resolve(process.argv[1])) &&
|
|
9
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(resolve(process.argv[1]))) {
|
|
10
|
-
try { await run(process.argv.slice(2)); }
|
|
11
|
-
catch (error) { process.stderr.write(`${error.code ?? 'E_OPERATE_NOTE'}: ${error.message}\n`); process.exitCode = 1; }
|
|
12
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Generated package-relative launcher; no global runtime or invocation downloads.
|
|
3
|
-
import { existsSync, realpathSync } from 'node:fs';
|
|
4
|
-
import { resolve } from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { runOperateReviewNoteValidator as run } from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
7
|
-
export * from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
8
|
-
if (process.argv[1] && existsSync(resolve(process.argv[1])) &&
|
|
9
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(resolve(process.argv[1]))) {
|
|
10
|
-
try { await run(process.argv.slice(2)); }
|
|
11
|
-
catch (error) { process.stderr.write(`${error.code ?? 'E_OPERATE_NOTE'}: ${error.message}\n`); process.exitCode = 1; }
|
|
12
|
-
}
|
package/lib/host-packages/claude/openplanr/skills/challenger-review/scripts/validate-note.mjs
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Generated package-relative launcher; no global runtime or invocation downloads.
|
|
3
|
-
import { existsSync, realpathSync } from 'node:fs';
|
|
4
|
-
import { resolve } from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { runOperateReviewNoteValidator as run } from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
7
|
-
export * from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
8
|
-
if (process.argv[1] && existsSync(resolve(process.argv[1])) &&
|
|
9
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(resolve(process.argv[1]))) {
|
|
10
|
-
try { await run(process.argv.slice(2)); }
|
|
11
|
-
catch (error) { process.stderr.write(`${error.code ?? 'E_OPERATE_NOTE'}: ${error.message}\n`); process.exitCode = 1; }
|
|
12
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Generated package-relative launcher; no global runtime or invocation downloads.
|
|
3
|
-
import { existsSync, realpathSync } from 'node:fs';
|
|
4
|
-
import { resolve } from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { runOperateReviewNoteValidator as run } from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
7
|
-
export * from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
8
|
-
if (process.argv[1] && existsSync(resolve(process.argv[1])) &&
|
|
9
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(resolve(process.argv[1]))) {
|
|
10
|
-
try { await run(process.argv.slice(2)); }
|
|
11
|
-
catch (error) { process.stderr.write(`${error.code ?? 'E_OPERATE_NOTE'}: ${error.message}\n`); process.exitCode = 1; }
|
|
12
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Generated package-relative launcher; no global runtime or invocation downloads.
|
|
3
|
-
import { existsSync, realpathSync } from 'node:fs';
|
|
4
|
-
import { resolve } from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { runOperateReviewNoteValidator as run } from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
7
|
-
export * from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
8
|
-
if (process.argv[1] && existsSync(resolve(process.argv[1])) &&
|
|
9
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(resolve(process.argv[1]))) {
|
|
10
|
-
try { await run(process.argv.slice(2)); }
|
|
11
|
-
catch (error) { process.stderr.write(`${error.code ?? 'E_OPERATE_NOTE'}: ${error.message}\n`); process.exitCode = 1; }
|
|
12
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Generated package-relative launcher; no global runtime or invocation downloads.
|
|
3
|
-
import { existsSync, realpathSync } from 'node:fs';
|
|
4
|
-
import { resolve } from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { runOperateReviewNoteValidator as run } from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
7
|
-
export * from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
8
|
-
if (process.argv[1] && existsSync(resolve(process.argv[1])) &&
|
|
9
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(resolve(process.argv[1]))) {
|
|
10
|
-
try { await run(process.argv.slice(2)); }
|
|
11
|
-
catch (error) { process.stderr.write(`${error.code ?? 'E_OPERATE_NOTE'}: ${error.message}\n`); process.exitCode = 1; }
|
|
12
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Generated package-relative launcher; no global runtime or invocation downloads.
|
|
3
|
-
import { existsSync, realpathSync } from 'node:fs';
|
|
4
|
-
import { resolve } from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { runOperateReviewNoteValidator as run } from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
7
|
-
export * from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
8
|
-
if (process.argv[1] && existsSync(resolve(process.argv[1])) &&
|
|
9
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(resolve(process.argv[1]))) {
|
|
10
|
-
try { await run(process.argv.slice(2)); }
|
|
11
|
-
catch (error) { process.stderr.write(`${error.code ?? 'E_OPERATE_NOTE'}: ${error.message}\n`); process.exitCode = 1; }
|
|
12
|
-
}
|
|
@@ -1,339 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// @ts-check
|
|
3
|
-
// The one backlog item writer: the CLI imports it, and skills run it when no CLI is installed.
|
|
4
|
-
|
|
5
|
-
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
|
|
6
|
-
import { join, relative, resolve, sep } from 'node:path';
|
|
7
|
-
import { fileURLToPath } from 'node:url';
|
|
8
|
-
|
|
9
|
-
/** Holds one empty file per issued ID, so the ID of a removed artifact is never issued again. */
|
|
10
|
-
const ISSUED_IDS_DIR = '.issued-ids';
|
|
11
|
-
const CLOSED_STATUSES = new Set(['closed', 'done']);
|
|
12
|
-
|
|
13
|
-
/** @type {typeof import('./backlog-item.d.mts').CLI_INSTALL_COMMAND} */
|
|
14
|
-
export const CLI_INSTALL_COMMAND = 'npm install -g openplanr';
|
|
15
|
-
/** @type {typeof import('./backlog-item.d.mts').CLI_HINT} */
|
|
16
|
-
export const CLI_HINT = `Install the OpenPlanr CLI to manage these items: ${CLI_INSTALL_COMMAND}`;
|
|
17
|
-
|
|
18
|
-
/** @type {typeof import('./backlog-item.d.mts').reserveNextId} */
|
|
19
|
-
export async function reserveNextId(dir, prefix) {
|
|
20
|
-
const issuedDir = join(dir, ISSUED_IDS_DIR);
|
|
21
|
-
await mkdir(issuedDir, { recursive: true });
|
|
22
|
-
const pattern = new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&')}-(\\d{3,})`, 'u');
|
|
23
|
-
let next = 1;
|
|
24
|
-
for (const directory of [dir, issuedDir]) {
|
|
25
|
-
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
26
|
-
const match = entry.isDirectory() ? null : pattern.exec(entry.name);
|
|
27
|
-
if (match) next = Math.max(next, Number.parseInt(match[1], 10) + 1);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
for (; ; next++) {
|
|
31
|
-
const id = `${prefix}-${String(next).padStart(3, '0')}`;
|
|
32
|
-
try {
|
|
33
|
-
await writeFile(join(issuedDir, id), '', { flag: 'wx' });
|
|
34
|
-
return id;
|
|
35
|
-
} catch (error) {
|
|
36
|
-
if (/** @type {NodeJS.ErrnoException} */ (error).code !== 'EEXIST') throw error;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** @type {typeof import('./backlog-item.d.mts').slugify} */
|
|
42
|
-
export function slugify(text, maxLength = 80) {
|
|
43
|
-
let slug = text
|
|
44
|
-
.toLowerCase()
|
|
45
|
-
.trim()
|
|
46
|
-
.replace(/[^\w\s-]/g, '')
|
|
47
|
-
.replace(/[\s_]+/g, '-')
|
|
48
|
-
.replace(/-+/g, '-')
|
|
49
|
-
.replace(/^-+|-+$/g, '');
|
|
50
|
-
if (slug.length > maxLength) {
|
|
51
|
-
slug = slug.slice(0, maxLength);
|
|
52
|
-
// Trim at the last whole-word boundary to avoid cut-off words.
|
|
53
|
-
const lastDash = slug.lastIndexOf('-');
|
|
54
|
-
if (lastDash > 0) slug = slug.slice(0, lastDash);
|
|
55
|
-
}
|
|
56
|
-
return slug;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** @param {unknown} value */
|
|
60
|
-
function strings(value) {
|
|
61
|
-
if (Array.isArray(value)) return value.map(String).filter(Boolean);
|
|
62
|
-
if (typeof value === 'string')
|
|
63
|
-
return value
|
|
64
|
-
.split(',')
|
|
65
|
-
.map((item) => item.trim())
|
|
66
|
-
.filter(Boolean);
|
|
67
|
-
return [];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** @param {unknown} value */
|
|
71
|
-
function optionalText(value) {
|
|
72
|
-
return value === undefined || value === null || value === '' ? undefined : value;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const PRIORITIES = Object.freeze(['critical', 'high', 'medium', 'low']);
|
|
76
|
-
|
|
77
|
-
/** Bad backlog input; the code lets the CLI report it as an input error, not a crash. */
|
|
78
|
-
class BacklogInputError extends Error {
|
|
79
|
-
/** @param {string} message */
|
|
80
|
-
constructor(message) {
|
|
81
|
-
super(message);
|
|
82
|
-
this.name = 'BacklogInputError';
|
|
83
|
-
this.code = 'E_BACKLOG_INVALID';
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** @type {typeof import('./backlog-item.d.mts').backlogFields} */
|
|
88
|
-
export function backlogFields(input) {
|
|
89
|
-
const title = String(input.title ?? input.name ?? '').trim();
|
|
90
|
-
if (!title) throw new BacklogInputError('A backlog item needs a non-empty title.');
|
|
91
|
-
const priority = String(input.priority ?? 'medium');
|
|
92
|
-
if (!PRIORITIES.includes(priority))
|
|
93
|
-
throw new BacklogInputError(
|
|
94
|
-
`Backlog priority ${JSON.stringify(priority)} is not valid; use one of ${PRIORITIES.join(', ')}.`,
|
|
95
|
-
);
|
|
96
|
-
const criteria = optionalText(input.acceptanceCriteria);
|
|
97
|
-
const notes = optionalText(input.notes);
|
|
98
|
-
const epicId = optionalText(input.epicId);
|
|
99
|
-
return {
|
|
100
|
-
title,
|
|
101
|
-
priority,
|
|
102
|
-
tags: strings(input.tags),
|
|
103
|
-
description: String(input.description ?? title),
|
|
104
|
-
...(criteria === undefined
|
|
105
|
-
? {}
|
|
106
|
-
: { acceptanceCriteria: Array.isArray(criteria) ? criteria.map(String) : String(criteria) }),
|
|
107
|
-
...(notes === undefined ? {} : { notes: String(notes) }),
|
|
108
|
-
...(epicId === undefined ? {} : { epicId: String(epicId) }),
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/** @type {typeof import('./backlog-item.d.mts').renderBacklogItem} */
|
|
113
|
-
export function renderBacklogItem(id, fields, date) {
|
|
114
|
-
const quote = JSON.stringify;
|
|
115
|
-
const lines = [
|
|
116
|
-
'---',
|
|
117
|
-
`id: ${quote(id)}`,
|
|
118
|
-
`title: ${quote(fields.title)}`,
|
|
119
|
-
`priority: ${quote(fields.priority)}`,
|
|
120
|
-
`tags: [${fields.tags.map((tag) => quote(tag)).join(', ')}]`,
|
|
121
|
-
'status: "open"',
|
|
122
|
-
`created: ${quote(date)}`,
|
|
123
|
-
`updated: ${quote(date)}`,
|
|
124
|
-
...(fields.epicId ? [`epicId: ${quote(fields.epicId)}`] : []),
|
|
125
|
-
'---',
|
|
126
|
-
'',
|
|
127
|
-
`# ${id}: ${fields.title}`,
|
|
128
|
-
'',
|
|
129
|
-
];
|
|
130
|
-
/** @param {string} heading @param {string} body @param {boolean} [spaced] */
|
|
131
|
-
const section = (heading, body, spaced = false) =>
|
|
132
|
-
lines.push(`## ${heading}`, ...(spaced ? [''] : []), body, '');
|
|
133
|
-
if (fields.tags.length > 0)
|
|
134
|
-
section('Tags', fields.tags.map((tag) => `- ${tag}`).join('\n'), true);
|
|
135
|
-
// A description with its own sections stands alone instead of nesting under another heading.
|
|
136
|
-
if (/^## /u.test(fields.description)) lines.push(fields.description, '');
|
|
137
|
-
else if (fields.description !== fields.title) section('Description', fields.description);
|
|
138
|
-
if (fields.acceptanceCriteria !== undefined && fields.acceptanceCriteria.length > 0)
|
|
139
|
-
section(
|
|
140
|
-
'Acceptance Criteria',
|
|
141
|
-
Array.isArray(fields.acceptanceCriteria)
|
|
142
|
-
? fields.acceptanceCriteria.map((criterion) => `- ${criterion}`).join('\n')
|
|
143
|
-
: fields.acceptanceCriteria,
|
|
144
|
-
);
|
|
145
|
-
if (fields.notes) section('Notes', fields.notes);
|
|
146
|
-
lines.push('---', `_Close when done: \`openplanr backlog update ${id} --status closed\`_`, '');
|
|
147
|
-
return lines.join('\n');
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/** @type {typeof import('./backlog-item.d.mts').writeBacklogItem} */
|
|
151
|
-
export async function writeBacklogItem(backlogDir, prefix, fields, date) {
|
|
152
|
-
await mkdir(backlogDir, { recursive: true });
|
|
153
|
-
const id = await reserveNextId(backlogDir, prefix);
|
|
154
|
-
const filePath = join(backlogDir, `${id}-${slugify(fields.title)}.md`);
|
|
155
|
-
await writeFile(filePath, renderBacklogItem(id, fields, date));
|
|
156
|
-
return { id, filePath };
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/** @param {string} projectDir */
|
|
160
|
-
async function projectConfig(projectDir) {
|
|
161
|
-
const configPath = join(projectDir, '.planr', 'config.json');
|
|
162
|
-
let raw;
|
|
163
|
-
try {
|
|
164
|
-
raw = await readFile(configPath, 'utf8');
|
|
165
|
-
} catch (error) {
|
|
166
|
-
if (/** @type {NodeJS.ErrnoException} */ (error).code === 'ENOENT')
|
|
167
|
-
throw new Error(
|
|
168
|
-
`No OpenPlanr project at ${projectDir}: ${configPath} is missing. Run \`openplanr init\` or pass the project root.`,
|
|
169
|
-
);
|
|
170
|
-
throw error;
|
|
171
|
-
}
|
|
172
|
-
let config;
|
|
173
|
-
try {
|
|
174
|
-
config = JSON.parse(raw);
|
|
175
|
-
} catch (error) {
|
|
176
|
-
throw new Error(`${configPath} is not valid JSON: ${/** @type {Error} */ (error).message}`);
|
|
177
|
-
}
|
|
178
|
-
return { configPath, raw, config };
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/** @param {string} projectDir */
|
|
182
|
-
async function projectBacklog(projectDir) {
|
|
183
|
-
const { config } = await projectConfig(projectDir);
|
|
184
|
-
const agile = String(config.outputPaths?.agile ?? '.planr').replaceAll('\\', '/');
|
|
185
|
-
return {
|
|
186
|
-
backlogDir: resolve(projectDir, agile, 'backlog'),
|
|
187
|
-
prefix: String(config.idPrefix?.backlog ?? 'BL'),
|
|
188
|
-
templateOverrides:
|
|
189
|
-
typeof config.templateOverrides === 'string' ? config.templateOverrides : undefined,
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/** @param {string} projectDir @param {string} filePath */
|
|
194
|
-
const relativePath = (projectDir, filePath) => relative(projectDir, filePath).split(sep).join('/');
|
|
195
|
-
|
|
196
|
-
/** @type {typeof import('./backlog-item.d.mts').createBacklogItem} */
|
|
197
|
-
export async function createBacklogItem(
|
|
198
|
-
projectDir,
|
|
199
|
-
input,
|
|
200
|
-
date = new Date().toISOString().split('T')[0],
|
|
201
|
-
) {
|
|
202
|
-
const { backlogDir, prefix } = await projectBacklog(projectDir);
|
|
203
|
-
const fields = backlogFields(input);
|
|
204
|
-
const { id, filePath } = await writeBacklogItem(backlogDir, prefix, fields, date);
|
|
205
|
-
return { id, title: fields.title, artifactPath: relativePath(projectDir, filePath) };
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Top-level scalar frontmatter fields; double-quoted values are read as JSON strings.
|
|
210
|
-
* @param {string} text
|
|
211
|
-
* @param {string} filePath
|
|
212
|
-
*/
|
|
213
|
-
function frontmatter(text, filePath) {
|
|
214
|
-
const block = /^---\n([\s\S]*?)\n---/u.exec(text.replace(/\r\n/gu, '\n'))?.[1] ?? '';
|
|
215
|
-
/** @type {Record<string, string>} */
|
|
216
|
-
const fields = {};
|
|
217
|
-
for (const line of block.split('\n')) {
|
|
218
|
-
const match = /^(\w+):\s*(.*)$/u.exec(line);
|
|
219
|
-
if (!match) continue;
|
|
220
|
-
const value = match[2].trim();
|
|
221
|
-
if (!value.startsWith('"')) {
|
|
222
|
-
fields[match[1]] = value.replace(/^'(.*)'$/u, '$1');
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
try {
|
|
226
|
-
fields[match[1]] = String(JSON.parse(value));
|
|
227
|
-
} catch (error) {
|
|
228
|
-
throw new Error(
|
|
229
|
-
`${filePath}: frontmatter field "${match[1]}" is not a valid quoted string: ${/** @type {Error} */ (error).message}`,
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
return fields;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/** @type {typeof import('./backlog-item.d.mts').listOpenBacklog} */
|
|
237
|
-
export async function listOpenBacklog(projectDir) {
|
|
238
|
-
const { backlogDir } = await projectBacklog(projectDir);
|
|
239
|
-
let names;
|
|
240
|
-
try {
|
|
241
|
-
names = await readdir(backlogDir);
|
|
242
|
-
} catch (error) {
|
|
243
|
-
if (/** @type {NodeJS.ErrnoException} */ (error).code === 'ENOENT') return [];
|
|
244
|
-
throw error;
|
|
245
|
-
}
|
|
246
|
-
const items = [];
|
|
247
|
-
for (const name of names.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))) {
|
|
248
|
-
const match = /^([A-Z]+-\d{3,})-.+\.md$/u.exec(name);
|
|
249
|
-
if (!match) continue;
|
|
250
|
-
const filePath = join(backlogDir, name);
|
|
251
|
-
const fields = frontmatter(await readFile(filePath, 'utf8'), filePath);
|
|
252
|
-
const status = fields.status ?? 'open';
|
|
253
|
-
if (CLOSED_STATUSES.has(status)) continue;
|
|
254
|
-
items.push({
|
|
255
|
-
id: fields.id ?? match[1],
|
|
256
|
-
title: fields.title ?? name.slice(match[1].length + 1, -3).replace(/-/g, ' '),
|
|
257
|
-
status,
|
|
258
|
-
path: relativePath(projectDir, filePath),
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
return items;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/** @type {typeof import('./backlog-item.d.mts').takeCliHint} */
|
|
265
|
-
export async function takeCliHint(projectDir) {
|
|
266
|
-
const { configPath, raw, config } = await projectConfig(projectDir);
|
|
267
|
-
if (config.findings?.cliHintShown === true) return null;
|
|
268
|
-
const next = { ...config, findings: { ...config.findings, cliHintShown: true } };
|
|
269
|
-
const indent = /\n(\t| +)"/u.exec(raw)?.[1] ?? ' ';
|
|
270
|
-
await writeFile(configPath, `${JSON.stringify(next, null, indent)}\n`);
|
|
271
|
-
return CLI_HINT;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/** @param {string} source */
|
|
275
|
-
async function readInput(source) {
|
|
276
|
-
const text =
|
|
277
|
-
source === '-'
|
|
278
|
-
? await new Promise((done, fail) => {
|
|
279
|
-
let value = '';
|
|
280
|
-
process.stdin.setEncoding('utf8');
|
|
281
|
-
process.stdin.on('data', (chunk) => {
|
|
282
|
-
value += chunk;
|
|
283
|
-
});
|
|
284
|
-
process.stdin.on('end', () => done(value));
|
|
285
|
-
process.stdin.on('error', fail);
|
|
286
|
-
})
|
|
287
|
-
: await readFile(resolve(source), 'utf8');
|
|
288
|
-
const parsed = JSON.parse(String(text));
|
|
289
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
290
|
-
throw new Error('Backlog input must be one JSON object with at least a title.');
|
|
291
|
-
return parsed;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
const USAGE =
|
|
295
|
-
'Usage: backlog-item.mjs add --project <dir> --data <file|->\n backlog-item.mjs list --project <dir> --open\n';
|
|
296
|
-
|
|
297
|
-
/** @param {string[]} argv */
|
|
298
|
-
async function main(argv) {
|
|
299
|
-
const [command, ...rest] = argv;
|
|
300
|
-
/** @type {Record<string, string | boolean>} */
|
|
301
|
-
const options = {};
|
|
302
|
-
for (let index = 0; index < rest.length; index += 1) {
|
|
303
|
-
const flag = rest[index];
|
|
304
|
-
if (flag === '--open') options.open = true;
|
|
305
|
-
else if (flag === '--project' || flag === '--data')
|
|
306
|
-
options[flag.slice(2)] = rest[++index] ?? '';
|
|
307
|
-
else throw new Error(`Unknown argument: ${flag}\n${USAGE}`);
|
|
308
|
-
}
|
|
309
|
-
const projectDir = resolve(String(options.project ?? '.'));
|
|
310
|
-
if (command === 'add') {
|
|
311
|
-
if (typeof options.data !== 'string' || !options.data)
|
|
312
|
-
throw new Error(`add needs --data.\n${USAGE}`);
|
|
313
|
-
const { templateOverrides } = await projectBacklog(projectDir);
|
|
314
|
-
const result = await createBacklogItem(projectDir, await readInput(options.data));
|
|
315
|
-
const cliHint = await takeCliHint(projectDir);
|
|
316
|
-
process.stdout.write(
|
|
317
|
-
`${JSON.stringify({
|
|
318
|
-
ok: true,
|
|
319
|
-
action: 'backlog.created',
|
|
320
|
-
...result,
|
|
321
|
-
...(templateOverrides ? { templateOverrideIgnored: true } : {}),
|
|
322
|
-
...(cliHint ? { cliHint } : {}),
|
|
323
|
-
})}\n`,
|
|
324
|
-
);
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
if (command === 'list' && options.open) {
|
|
328
|
-
process.stdout.write(`${JSON.stringify({ items: await listOpenBacklog(projectDir) })}\n`);
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
throw new Error(USAGE);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
335
|
-
main(process.argv.slice(2)).catch((error) => {
|
|
336
|
-
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
337
|
-
process.exit(1);
|
|
338
|
-
});
|
|
339
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Generated package-relative launcher; no global runtime or invocation downloads.
|
|
3
|
-
import { existsSync, realpathSync } from 'node:fs';
|
|
4
|
-
import { resolve } from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { runOperateReviewNoteValidator as run } from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
7
|
-
export * from "../../../runtime/operate/scripts/validate-note.mjs";
|
|
8
|
-
if (process.argv[1] && existsSync(resolve(process.argv[1])) &&
|
|
9
|
-
realpathSync(fileURLToPath(import.meta.url)) === realpathSync(resolve(process.argv[1]))) {
|
|
10
|
-
try { await run(process.argv.slice(2)); }
|
|
11
|
-
catch (error) { process.stderr.write(`${error.code ?? 'E_OPERATE_NOTE'}: ${error.message}\n`); process.exitCode = 1; }
|
|
12
|
-
}
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { open, readdir, readFile, rename, stat, unlink, writeFile } from 'node:fs/promises';
|
|
4
|
-
import { dirname, join, resolve } from 'node:path';
|
|
5
|
-
|
|
6
|
-
const KINDS = Object.freeze(['SPEC', 'US', 'T']);
|
|
7
|
-
const EMPTY = Object.freeze({ SPEC: 1, US: 1, T: 1 });
|
|
8
|
-
const wait = (milliseconds) => new Promise((done) => setTimeout(done, milliseconds));
|
|
9
|
-
const format = (kind, value) => `${kind}-${String(value).padStart(3, '0')}`;
|
|
10
|
-
|
|
11
|
-
async function scanMaxima(root) {
|
|
12
|
-
const maxima = { SPEC: 0, US: 0, T: 0 };
|
|
13
|
-
const visit = async (directory) => {
|
|
14
|
-
let entries;
|
|
15
|
-
try {
|
|
16
|
-
entries = await readdir(directory, { withFileTypes: true });
|
|
17
|
-
} catch (error) {
|
|
18
|
-
if (error.code === 'ENOENT') return;
|
|
19
|
-
throw error;
|
|
20
|
-
}
|
|
21
|
-
for (const entry of entries) {
|
|
22
|
-
const match = /^(SPEC|US|T)-(\d{3,})(?:-|\.|$)/u.exec(entry.name);
|
|
23
|
-
if (match) maxima[match[1]] = Math.max(maxima[match[1]], Number(match[2]));
|
|
24
|
-
if (entry.isDirectory() && !entry.name.startsWith('.')) await visit(resolve(directory, entry.name));
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
await visit(root);
|
|
28
|
-
return maxima;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function readSequence(root) {
|
|
32
|
-
try {
|
|
33
|
-
const value = JSON.parse(await readFile(resolve(root, '.id-sequence.json'), 'utf8'));
|
|
34
|
-
if (
|
|
35
|
-
value.kind !== 'planning-id-sequence'
|
|
36
|
-
|| value.protocolVersion !== '1.7.0'
|
|
37
|
-
|| KINDS.some((kind) => !Number.isSafeInteger(value.next?.[kind]) || value.next[kind] < 1)
|
|
38
|
-
) throw new Error('Invalid planning ID sequence.');
|
|
39
|
-
return value;
|
|
40
|
-
} catch (error) {
|
|
41
|
-
if (error.code === 'ENOENT') return null;
|
|
42
|
-
throw error;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function currentSequence(root) {
|
|
47
|
-
const [stored, maxima] = await Promise.all([readSequence(root), scanMaxima(root)]);
|
|
48
|
-
return {
|
|
49
|
-
kind: 'planning-id-sequence',
|
|
50
|
-
schemaVersion: '1.0.0',
|
|
51
|
-
protocolVersion: '1.7.0',
|
|
52
|
-
next: Object.fromEntries(
|
|
53
|
-
KINDS.map((kind) => [kind, Math.max(stored?.next[kind] ?? EMPTY[kind], maxima[kind] + 1)]),
|
|
54
|
-
),
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function parseArgs(argv) {
|
|
59
|
-
const options = { root: '.planr/specs', preview: false, counts: { SPEC: 0, US: 0, T: 0 } };
|
|
60
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
61
|
-
const value = argv[index];
|
|
62
|
-
if (value === '--root') options.root = argv[++index];
|
|
63
|
-
else if (value === '--preview') options.preview = true;
|
|
64
|
-
else if (value === '--spec' || value === '--stories' || value === '--tasks') {
|
|
65
|
-
const kind = value === '--spec' ? 'SPEC' : value === '--stories' ? 'US' : 'T';
|
|
66
|
-
const count = Number(argv[++index]);
|
|
67
|
-
if (!Number.isSafeInteger(count) || count < 0) throw new Error(`${value} must be a non-negative integer.`);
|
|
68
|
-
options.counts[kind] = count;
|
|
69
|
-
} else throw new Error(`Unknown argument: ${value}`);
|
|
70
|
-
}
|
|
71
|
-
return options;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const shellQuote = (value) => (/^[\w./-]+$/u.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`);
|
|
75
|
-
|
|
76
|
-
/** Returns the nearest `.planr/config.json` at or above `directory` and the specs folder it configures. */
|
|
77
|
-
async function configuredRoot(directory) {
|
|
78
|
-
for (let current = directory; ; current = dirname(current)) {
|
|
79
|
-
const configPath = join(current, '.planr', 'config.json');
|
|
80
|
-
const raw = await readFile(configPath, 'utf8').catch((error) => {
|
|
81
|
-
if (error.code === 'ENOENT' || error.code === 'ENOTDIR') return null;
|
|
82
|
-
throw error;
|
|
83
|
-
});
|
|
84
|
-
if (raw !== null) {
|
|
85
|
-
let agile;
|
|
86
|
-
try {
|
|
87
|
-
agile = JSON.parse(raw).outputPaths?.agile ?? '.planr';
|
|
88
|
-
} catch (error) {
|
|
89
|
-
throw new Error(`Project config ${configPath} is not valid JSON: ${error.message}`);
|
|
90
|
-
}
|
|
91
|
-
if (typeof agile !== 'string') throw new Error(`Project config ${configPath} has a non-string outputPaths.agile.`);
|
|
92
|
-
return { configPath, expected: resolve(current, agile, 'specs') };
|
|
93
|
-
}
|
|
94
|
-
if (dirname(current) === current) return null;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** Accepts only an existing directory that the nearest project config names as its planning folder. */
|
|
99
|
-
async function assertPlanningRoot(planningRoot) {
|
|
100
|
-
const project = await configuredRoot(planningRoot);
|
|
101
|
-
if (!project) {
|
|
102
|
-
throw new Error(`Planning root ${planningRoot} is not inside an OpenPlanr project: no .planr/config.json in it or any parent directory.`);
|
|
103
|
-
}
|
|
104
|
-
const { configPath, expected } = project;
|
|
105
|
-
if (planningRoot !== expected) {
|
|
106
|
-
throw new Error(`Planning root ${planningRoot} is not the planning folder configured by ${configPath}, which expects ${expected}.`);
|
|
107
|
-
}
|
|
108
|
-
const status = await stat(planningRoot).catch((error) => {
|
|
109
|
-
if (error.code === 'ENOENT') return null;
|
|
110
|
-
throw error;
|
|
111
|
-
});
|
|
112
|
-
if (!status) {
|
|
113
|
-
throw new Error(
|
|
114
|
-
`Planning root ${planningRoot} does not exist; ${configPath} expects it. Create it with \`mkdir -p ${shellQuote(planningRoot)}\`, or fix outputPaths.agile in ${configPath}.`,
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
if (!status.isDirectory()) throw new Error(`Planning root ${planningRoot} is not a directory; ${configPath} expects a directory there.`);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
async function reserve({ root, preview, counts }) {
|
|
121
|
-
const planningRoot = resolve(root);
|
|
122
|
-
await assertPlanningRoot(planningRoot);
|
|
123
|
-
const run = async () => {
|
|
124
|
-
const sequence = await currentSequence(planningRoot);
|
|
125
|
-
const ids = Object.fromEntries(
|
|
126
|
-
KINDS.map((kind) => [kind, Array.from(
|
|
127
|
-
{ length: counts[kind] },
|
|
128
|
-
(_, index) => format(kind, sequence.next[kind] + index),
|
|
129
|
-
)]),
|
|
130
|
-
);
|
|
131
|
-
if (!preview) {
|
|
132
|
-
for (const kind of KINDS) sequence.next[kind] += counts[kind];
|
|
133
|
-
const temporary = resolve(planningRoot, `.id-sequence.${process.pid}.${Date.now()}.tmp`);
|
|
134
|
-
await writeFile(temporary, `${JSON.stringify(sequence, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
|
|
135
|
-
await rename(temporary, resolve(planningRoot, '.id-sequence.json'));
|
|
136
|
-
}
|
|
137
|
-
return { kind: 'planning-id-reservation', schemaVersion: '1.0.0', preview, ids };
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
if (preview) return run();
|
|
141
|
-
const lockPath = resolve(planningRoot, '.id-sequence.lock');
|
|
142
|
-
let handle;
|
|
143
|
-
for (let attempt = 0; attempt < 200; attempt += 1) {
|
|
144
|
-
try {
|
|
145
|
-
handle = await open(lockPath, 'wx', 0o600);
|
|
146
|
-
break;
|
|
147
|
-
} catch (error) {
|
|
148
|
-
if (error.code !== 'EEXIST') throw error;
|
|
149
|
-
await wait(25);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
if (!handle) throw new Error('Timed out waiting for the planning ID allocator.');
|
|
153
|
-
try {
|
|
154
|
-
return await run();
|
|
155
|
-
} finally {
|
|
156
|
-
await handle.close();
|
|
157
|
-
await unlink(lockPath).catch(() => undefined);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
try {
|
|
162
|
-
process.stdout.write(`${JSON.stringify(await reserve(parseArgs(process.argv.slice(2))), null, 2)}\n`);
|
|
163
|
-
} catch (error) {
|
|
164
|
-
process.stderr.write(`E_PLANNING_ID_RESERVATION: ${error.message}\n`);
|
|
165
|
-
process.exitCode = 1;
|
|
166
|
-
}
|