agentpostmortem 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env node
2
+
3
+ // scripts/init.ts
4
+ import fs from "fs/promises";
5
+ import path from "path";
6
+ import { fileURLToPath } from "url";
7
+ async function pathExists(targetPath) {
8
+ try {
9
+ await fs.access(targetPath);
10
+ return true;
11
+ } catch {
12
+ return false;
13
+ }
14
+ }
15
+ function parseArgs(argv) {
16
+ let target = process.cwd();
17
+ for (let index = 0; index < argv.length; index += 1) {
18
+ const arg = argv[index];
19
+ if (arg === "--target") {
20
+ const value = argv[index + 1];
21
+ if (!value) {
22
+ throw new Error("missing value for --target");
23
+ }
24
+ target = value;
25
+ index += 1;
26
+ continue;
27
+ }
28
+ throw new Error(`unknown argument: ${arg}`);
29
+ }
30
+ return { target: path.resolve(target) };
31
+ }
32
+ async function resolveTemplatesDir() {
33
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
34
+ const candidates = [
35
+ path.resolve(scriptDir, "../../src/templates"),
36
+ path.resolve(scriptDir, "../src/templates")
37
+ ];
38
+ for (const candidate of candidates) {
39
+ if (await pathExists(candidate)) {
40
+ return candidate;
41
+ }
42
+ }
43
+ throw new Error(`unable to locate templates directory from ${scriptDir}`);
44
+ }
45
+ async function copyIfMissing(sourcePath, destinationPath, relativePath) {
46
+ await fs.mkdir(path.dirname(destinationPath), { recursive: true });
47
+ if (await pathExists(destinationPath)) {
48
+ console.log(` [skip] ${relativePath} (already exists)`);
49
+ return { copied: 0, skipped: 1 };
50
+ }
51
+ await fs.copyFile(sourcePath, destinationPath);
52
+ console.log(` [copy] ${relativePath}`);
53
+ return { copied: 1, skipped: 0 };
54
+ }
55
+ async function copyCommandTemplates(templatesDir, targetRoot) {
56
+ const commandsTemplateDir = path.join(templatesDir, "commands");
57
+ const commandEntries = await fs.readdir(commandsTemplateDir, { withFileTypes: true });
58
+ let copied = 0;
59
+ let skipped = 0;
60
+ const commandFiles = commandEntries.filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort((left, right) => left.localeCompare(right));
61
+ for (const commandFile of commandFiles) {
62
+ const sourcePath = path.join(commandsTemplateDir, commandFile);
63
+ const relativePath = path.posix.join(".opencode", "commands", commandFile);
64
+ const destinationPath = path.join(targetRoot, ".opencode", "commands", commandFile);
65
+ const result = await copyIfMissing(sourcePath, destinationPath, relativePath);
66
+ copied += result.copied;
67
+ skipped += result.skipped;
68
+ }
69
+ return { copied, skipped };
70
+ }
71
+ async function copySkillTemplates(templatesDir, targetRoot) {
72
+ const skillsTemplateDir = path.join(templatesDir, "skills");
73
+ const skillEntries = await fs.readdir(skillsTemplateDir, { withFileTypes: true });
74
+ let copied = 0;
75
+ let skipped = 0;
76
+ const skillDirectories = skillEntries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort((left, right) => left.localeCompare(right));
77
+ for (const skillName of skillDirectories) {
78
+ const sourcePath = path.join(skillsTemplateDir, skillName, "SKILL.md");
79
+ if (!await pathExists(sourcePath)) {
80
+ throw new Error(`missing skill template: ${sourcePath}`);
81
+ }
82
+ const relativePath = path.posix.join(".opencode", "skills", skillName, "SKILL.md");
83
+ const destinationPath = path.join(targetRoot, ".opencode", "skills", skillName, "SKILL.md");
84
+ const result = await copyIfMissing(sourcePath, destinationPath, relativePath);
85
+ copied += result.copied;
86
+ skipped += result.skipped;
87
+ }
88
+ return { copied, skipped };
89
+ }
90
+ async function ensurePostmortemConfig(targetRoot) {
91
+ const relativePath = path.posix.join(".opencode", "postmortem.json");
92
+ const destinationPath = path.join(targetRoot, relativePath);
93
+ await fs.mkdir(path.dirname(destinationPath), { recursive: true });
94
+ if (await pathExists(destinationPath)) {
95
+ console.log(` [skip] ${relativePath} (already exists)`);
96
+ return { copied: 0, skipped: 1 };
97
+ }
98
+ await fs.writeFile(destinationPath, "{}", "utf8");
99
+ console.log(` [copy] ${relativePath}`);
100
+ return { copied: 1, skipped: 0 };
101
+ }
102
+ async function main() {
103
+ const { target } = parseArgs(process.argv.slice(2));
104
+ const templatesDir = await resolveTemplatesDir();
105
+ console.log(`Initializing templates in ${target}`);
106
+ const commandResult = await copyCommandTemplates(templatesDir, target);
107
+ const skillResult = await copySkillTemplates(templatesDir, target);
108
+ const configResult = await ensurePostmortemConfig(target);
109
+ const copied = commandResult.copied + skillResult.copied + configResult.copied;
110
+ const skipped = commandResult.skipped + skillResult.skipped + configResult.skipped;
111
+ console.log("");
112
+ console.log(`Done. Copied ${copied} file(s), skipped ${skipped} file(s).`);
113
+ console.log("Next step: Add 'agentpostmortem' to your opencode.json plugin array.");
114
+ }
115
+ main().catch((error) => {
116
+ const message = error instanceof Error ? error.message : String(error);
117
+ console.error(`init failed: ${message}`);
118
+ process.exitCode = 1;
119
+ });
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/package.json",
3
+ "name": "agentpostmortem",
4
+ "version": "0.1.0",
5
+ "private": false,
6
+ "type": "module",
7
+ "license": "MIT",
8
+ "description": "Postmortem workflow plugin for OpenCode: failure memory, prevention rules, guardrailed retry",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/Tpypan/AgentPostmortem.git"
12
+ },
13
+ "keywords": [
14
+ "opencode",
15
+ "plugin",
16
+ "postmortem",
17
+ "ai",
18
+ "debugging"
19
+ ],
20
+ "main": "dist/index.js",
21
+ "types": "dist/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "import": "./dist/index.js",
25
+ "types": "./dist/index.d.ts"
26
+ }
27
+ },
28
+ "bin": {
29
+ "postmortem-init": "dist/scripts/init.js"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "src/templates",
34
+ "INSTALL.md",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsup",
40
+ "build:check": "bun run build && bun --eval \"import p from './dist/index.js'; process.exit(typeof p === 'function' ? 0 : 1)\"",
41
+ "test": "bun test",
42
+ "lint": "tsc --noEmit",
43
+ "typecheck": "tsc --noEmit",
44
+ "prepublishOnly": "bun run build"
45
+ },
46
+ "dependencies": {
47
+ "@opencode-ai/plugin": "^1.2.15",
48
+ "zod": "4.1.8"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^24.3.0",
52
+ "tsup": "^8.0.0",
53
+ "typescript": "^5.9.2"
54
+ },
55
+ "directories": {
56
+ "example": "examples",
57
+ "test": "test"
58
+ },
59
+ "author": "",
60
+ "bugs": {
61
+ "url": "https://github.com/Tpypan/AgentPostmortem/issues"
62
+ },
63
+ "homepage": "https://github.com/Tpypan/AgentPostmortem#readme",
64
+ "engines": {
65
+ "node": ">=16"
66
+ },
67
+ "publishConfig": {
68
+ "access": "public"
69
+ }
70
+ }
@@ -0,0 +1,12 @@
1
+ ---
2
+ description: Disable or re-enable postmortem lesson injection for the active session
3
+ subtask: true
4
+ ---
5
+
6
+ /disable-lessons
7
+
8
+ Call the `postmortem_disable_lessons` plugin tool with flags mapped from $ARGUMENTS. Examples:
9
+
10
+ tool: postmortem_disable_lessons
11
+ tool: postmortem_disable_lessons --enable
12
+ tool: postmortem_disable_lessons --json
@@ -0,0 +1,10 @@
1
+ ---
2
+ description: Manage stored postmortem failures (list/show/forget/delete/prune/purge)
3
+ subtask: true
4
+ ---
5
+
6
+ /failures
7
+
8
+ Call the `postmortem_failures` plugin tool with flags mapped from `$ARGUMENTS`. Example:
9
+
10
+ tool: postmortem_failures --action list --json
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: Forget a stored failure record by id
3
+ subtask: true
4
+ ---
5
+
6
+ /forget
7
+
8
+ Forget a stored failure record by id. Expects an id argument.
9
+
10
+ Example:
11
+ tool: postmortem_failures --action forget --id $1
@@ -0,0 +1,10 @@
1
+ ---
2
+ description: Render a safe summary of the last postmortem run
3
+ subtask: true
4
+ ---
5
+
6
+ /inspect
7
+
8
+ Call the postmortem_inspect plugin tool with flags mapped from $ARGUMENTS. Example:
9
+
10
+ tool: postmortem_inspect --json
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: Show or set postmortem storage root selection
3
+ subtask: true
4
+ ---
5
+
6
+ /postmortem-config
7
+
8
+ Call the `postmortem_config` plugin tool with flags mapped from `$ARGUMENTS`. Examples:
9
+
10
+ tool: postmortem_config --action show --json
11
+ tool: postmortem_config --action set --storage repo --json
@@ -0,0 +1,14 @@
1
+ Command: postmortem_eval
2
+
3
+ Description:
4
+ Local-only evaluation of repeat-failure metrics from stored failures.jsonl. Reads the project's postmortem store (no telemetry) and computes deterministic metrics.
5
+
6
+ Args:
7
+ --json : output JSON
8
+ --window <n> : lookahead window size (default 10)
9
+
10
+ Metrics:
11
+ totalRecords
12
+ uniqueSignatures
13
+ repeatRateWithinWindow
14
+ repeatCountsBySignature
@@ -0,0 +1,10 @@
1
+ ---
2
+ description: Preview or persist a durable postmortem failure record
3
+ subtask: true
4
+ ---
5
+
6
+ /record-failure
7
+
8
+ Call the `postmortem_record_failure` plugin tool with flags mapped from `$ARGUMENTS`. Example:
9
+
10
+ tool: postmortem_record_failure --yes --json
@@ -0,0 +1,12 @@
1
+ ---
2
+ description: Preview or emit a guardrailed retry prompt for the last user task
3
+ subtask: true
4
+ ---
5
+
6
+ /retry
7
+
8
+ Call the `postmortem_retry` plugin tool with flags mapped from $ARGUMENTS. Examples:
9
+
10
+ tool: postmortem_retry
11
+ tool: postmortem_retry --skip rule-a --skip rule-b --explain
12
+ tool: postmortem_retry --yes
@@ -0,0 +1,10 @@
1
+ ---
2
+ description: Manage stored postmortem guardrail rules (list/show/enable/disable/edit/rate/add_from_failure)
3
+ subtask: true
4
+ ---
5
+
6
+ /rules
7
+
8
+ Call the `postmortem_rules` plugin tool with flags mapped from `$ARGUMENTS`. Example:
9
+
10
+ tool: postmortem_rules --action list --json
@@ -0,0 +1,10 @@
1
+ ---
2
+ description: Deterministically analyze a stored failure and generate typed hypotheses + prevention rules
3
+ subtask: true
4
+ ---
5
+
6
+ /why-failed
7
+
8
+ Call the `postmortem_why_failed` plugin tool with flags mapped from `$ARGUMENTS`. Example:
9
+
10
+ tool: postmortem_why_failed --latest --json
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: disable-lessons
3
+ description: Disable or re-enable postmortem guardrail injection for this session.
4
+ ---
5
+
6
+ ## What I do
7
+
8
+ - Disable or re-enable postmortem guardrail lessons for the current session
9
+ - Can be toggled on or off as needed
10
+ - Safe for memory by default
11
+
12
+ ## How to run
13
+
14
+ Call the tool directly:
15
+
16
+ ```
17
+ tool: postmortem_disable_lessons
18
+ tool: postmortem_disable_lessons --enable
19
+ tool: postmortem_disable_lessons --disable
20
+ tool: postmortem_disable_lessons --json
21
+ ```
22
+
23
+ ## Argument mapping
24
+
25
+ - `disable` (bool): Disable lessons
26
+ - `enable` (bool): Enable lessons
27
+ - `json` (bool): Output as JSON
28
+
29
+ ## Safety
30
+
31
+ - All output is redacted by default for memory safety
32
+ - Never request or display secrets
33
+ - To delete all postmortem data, first run `tool: postmortem_config --action show --json` to get the `root` path, then delete it manually with `rm -rf "<root>"`.
34
+ - Repo-local storage is opt-in via `.opencode/postmortem.json`.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: failures
3
+ description: List, show, forget, delete, prune, or purge stored postmortem failures.
4
+ ---
5
+
6
+ ## What I do
7
+
8
+ - List all stored postmortem failures
9
+ - Show, forget, delete, prune, or purge failures by ID or session
10
+ - Supports dry run and filtering
11
+
12
+ ## How to run
13
+
14
+ Call the tool directly:
15
+
16
+ ```
17
+ tool: postmortem_failures --action list --json
18
+ tool: postmortem_failures --action delete --id <failureId> --yes --json
19
+ ```
20
+
21
+ ## Argument mapping
22
+
23
+ - `action` (string): Action to perform (list, show, forget, delete, prune, purge)
24
+ - `id` (string): Failure ID
25
+ - `sessionId` (string): Session ID
26
+ - `json` (bool): Output as JSON
27
+ - `yes` (bool): Confirm destructive actions
28
+ - `dryRun` (bool): Preview changes
29
+ - `maxBytes` (number): Limit output size
30
+ - `olderThanDays` (number): Filter by age
31
+ - `keepLastN` (number): Keep last N failures
32
+
33
+ ## Safety
34
+
35
+ - All output is redacted by default for memory safety
36
+ - Never request or display secrets
37
+ - To delete all postmortem data, first run `tool: postmortem_config --action show --json` to get the `root` path, then delete it manually with `rm -rf "<root>"`.
38
+ - Repo-local storage is opt-in via `.opencode/postmortem.json`.
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: inspect
3
+ description: Render the last-run postmortem snapshot for this project. Safe by default.
4
+ ---
5
+
6
+ ## What I do
7
+
8
+ - Show the most recent postmortem snapshot for the current project
9
+ - Optionally include files, git status, or errors in the output
10
+ - Output is safe for memory by default (redacted)
11
+
12
+ ## How to run
13
+
14
+ Call the tool directly:
15
+
16
+ ```
17
+ tool: postmortem_inspect --json --files --git --errors
18
+ ```
19
+ ## Argument mapping
20
+
21
+ - `json` (bool): Output as JSON
22
+ - `files` (bool): Include file list
23
+ - `git` (bool): Include git status
24
+ - `errors` (bool): Include error details
25
+
26
+ ## Safety
27
+
28
+ - All output is redacted by default for memory safety
29
+ - Never request or display secrets
30
+ - To delete all postmortem data, first run `tool: postmortem_config --action show --json` to get the `root` path, then delete it manually with `rm -rf "<root>"`.
31
+ - Repo-local storage is opt-in via `.opencode/postmortem.json`.
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: postmortem-config
3
+ description: Show or set project-local postmortem storage config (user or repo root).
4
+ ---
5
+
6
+ ## What I do
7
+
8
+ - Show or set the postmortem storage configuration for this project
9
+ - Switch between user and repo storage, or set raw storage
10
+ - Can output as JSON for scripting
11
+
12
+ ## How to run
13
+
14
+ Call the tool directly:
15
+
16
+ ```
17
+ tool: postmortem_config --action show --json
18
+ tool: postmortem_config --action set --storage repo --json
19
+ ```
20
+
21
+ ## Argument mapping
22
+
23
+ - `action` (string): Action to perform (show, set)
24
+ - `storage` (string): Storage location (user, repo)
25
+ - `storeRaw` (bool): Store raw data
26
+ - `json` (bool): Output as JSON
27
+
28
+ ## Safety
29
+
30
+ - All output is redacted by default for memory safety
31
+ - To delete all postmortem data, first run `tool: postmortem_config --action show --json` to get the `root` path, then delete it manually with `rm -rf "<root>"`.
32
+ - Repo-local storage is opt-in via `.opencode/postmortem.json`.
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: postmortem-eval
3
+ description: Local-only evaluation of repeat-failure metrics from stored failures.jsonl.
4
+ ---
5
+
6
+ ## What I do
7
+
8
+ - Evaluate repeat-failure metrics from stored failures
9
+ - Operates locally, no remote calls
10
+ - Can limit the evaluation window
11
+
12
+ ## How to run
13
+
14
+ Call the tool directly:
15
+
16
+ ```
17
+ tool: postmortem_eval --json --window 30
18
+ ```
19
+ ## Argument mapping
20
+
21
+ - `json` (bool): Output as JSON
22
+ - `window` (number): Limit to last N days
23
+
24
+ ## Safety
25
+
26
+ - All output is redacted by default for memory safety
27
+ - Never request or display secrets
28
+ - To delete all postmortem data, first run `tool: postmortem_config --action show --json` to get the `root` path, then delete it manually with `rm -rf "<root>"`.
29
+ - Repo-local storage is opt-in via `.opencode/postmortem.json`.
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: record-failure
3
+ description: Preview or persist a durable failure record from the last-run snapshot.
4
+ ---
5
+
6
+ ## What I do
7
+
8
+ - Record a failure event based on the most recent postmortem snapshot
9
+ - Optionally add a reason or tags
10
+ - Can preview or persist the record
11
+
12
+ ## How to run
13
+
14
+ Call the tool directly:
15
+
16
+ ```
17
+ tool: postmortem_record_failure --yes --json --reason "..." --tags foo --tags bar
18
+ ```
19
+ ## Argument mapping
20
+
21
+ - `yes` (bool): Persist immediately
22
+ - `json` (bool): Output as JSON
23
+ - `reason` (string): Reason for failure
24
+ - `tags` (string[]): Tags for this failure
25
+
26
+ ## Safety
27
+
28
+ - All output is redacted by default for memory safety
29
+ - Never request or display secrets
30
+ - To delete all postmortem data, first run `tool: postmortem_config --action show --json` to get the `root` path, then delete it manually with `rm -rf "<root>"`.
31
+ - Repo-local storage is opt-in via `.opencode/postmortem.json`.
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: retry
3
+ description: Preview or emit a guardrailed retry prompt from the latest non-command user task.
4
+ ---
5
+
6
+ ## What I do
7
+
8
+ - Preview or emit a retry prompt for the last failed user task
9
+ - Can explain, skip rule IDs, or confirm retry
10
+ - Safe for memory by default
11
+
12
+ ## How to run
13
+
14
+ Call the tool directly:
15
+
16
+ ```
17
+ tool: postmortem_retry
18
+ tool: postmortem_retry --skip rule-a --skip rule-b --explain
19
+ tool: postmortem_retry --yes
20
+ ```
21
+
22
+ ## Argument mapping
23
+
24
+ - `yes` (bool): Confirm retry
25
+ - `explain` (bool): Explain the retry
26
+ - `skip` (string[]): Rule IDs to skip
27
+ - `json` (bool): Output as JSON
28
+
29
+ ## Safety
30
+
31
+ - All output is redacted by default for memory safety
32
+ - Never request or display secrets
33
+ - To delete all postmortem data, first run `tool: postmortem_config --action show --json` to get the `root` path, then delete it manually with `rm -rf "<root>"`.
34
+ - Repo-local storage is opt-in via `.opencode/postmortem.json`.
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: rules
3
+ description: List, show, enable, disable, edit, or rate postmortem rules and import suggestions from failure analysis.
4
+ ---
5
+
6
+ ## What I do
7
+
8
+ - List, show, enable, disable, edit, or rate postmortem rules
9
+ - Import suggestions from failure analysis
10
+ - Filter, update, or rate rules by ID or failure
11
+
12
+ ## How to run
13
+
14
+ Call the tool directly:
15
+
16
+ ```
17
+ tool: postmortem_rules --action list --json
18
+ tool: postmortem_rules --action disable --id <ruleId> --json
19
+ tool: postmortem_rules --action edit --id <ruleId> --text "..." --json
20
+ ```
21
+
22
+ ## Argument mapping
23
+
24
+ - `action` (string): Action to perform (list, show, enable, disable, edit, rate, import)
25
+ - `id` (string): Rule ID
26
+ - `failureId` (string): Filter by failure
27
+ - `json` (bool): Output as JSON
28
+ - `includeDisabled` (bool): Include disabled rules
29
+ - `text` (string): Rule text
30
+ - `severity` (string): Severity level
31
+ - `rating` (string): Rule rating
32
+ - `note` (string): Add a note
33
+
34
+ ## Safety
35
+
36
+ - All output is redacted by default for memory safety
37
+ - Never request or display secrets
38
+ - To delete all postmortem data, first run `tool: postmortem_config --action show --json` to get the `root` path, then delete it manually with `rm -rf "<root>"`.
39
+ - Repo-local storage is opt-in via `.opencode/postmortem.json`.
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: why-failed
3
+ description: Analyze a stored failure and persist hypotheses and prevention rules.
4
+ ---
5
+
6
+ ## What I do
7
+
8
+ - Analyze a stored failure deterministically
9
+ - Persist hypotheses and prevention rules for the failure
10
+ - Can target a specific failure or the latest
11
+
12
+ ## How to run
13
+
14
+ Call the tool directly:
15
+
16
+ ```
17
+ tool: postmortem_why_failed --latest --json
18
+ tool: postmortem_why_failed --id <failureId> --json
19
+ ```
20
+
21
+ ## Argument mapping
22
+
23
+ - `id` (string): Failure ID to analyze
24
+ - `latest` (bool): Analyze the latest failure
25
+ - `json` (bool): Output as JSON
26
+
27
+ ## Safety
28
+
29
+ - All output is redacted by default for memory safety
30
+ - Never request or display secrets
31
+ - To delete all postmortem data, first run `tool: postmortem_config --action show --json` to get the `root` path, then delete it manually with `rm -rf "<root>"`.
32
+ - Repo-local storage is opt-in via `.opencode/postmortem.json`.