srm-report 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -3
- package/SKILL.md +4 -0
- package/assets/examples/project_21cse734p.py +7 -1
- package/bin/srm-report.js +90 -17
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# srm-report
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A skill for AI coding agents that builds a print-ready SRM Institute report (.docx and .pdf) from the template you attach. It fills a copy of the template, so fixed pages stay as issued, and generates the numbered chapters, contents, lists and references.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,7 +8,17 @@ A Claude Code skill that builds a print-ready SRM Institute report (.docx and .p
|
|
|
8
8
|
npx srm-report install
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
This
|
|
11
|
+
This installs for every agent it finds on your machine. Restart the agent, attach your template and ask for your report.
|
|
12
|
+
|
|
13
|
+
| Agent | Installed to | Command |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| Codex, Cursor, Gemini CLI, GitHub Copilot, Windsurf, OpenCode, Roo Code | `~/.agents/skills/srm-report` | `--agent codex` (or any of these names) |
|
|
16
|
+
| Claude Code | `~/.claude/skills/srm-report` | `--agent claude` |
|
|
17
|
+
| Antigravity | `~/.gemini/config/skills/srm-report` | `--agent antigravity` |
|
|
18
|
+
| Cline | `~/.cline/skills/srm-report` | `--agent cline` |
|
|
19
|
+
| Kiro | `~/.kiro/skills/srm-report` | `--agent kiro` |
|
|
20
|
+
|
|
21
|
+
Use `--agent all` for every folder. For any other agent, run `npx srm-report install --dir ./srm-report` and tell it to follow `srm-report/SKILL.md`.
|
|
12
22
|
|
|
13
23
|
To build the report you need Windows with Microsoft Word, and Python with `python-docx`, `PyMuPDF` and `Pillow`:
|
|
14
24
|
|
|
@@ -24,7 +34,7 @@ Everyone who installs this is asked to raise a pull request with what they impro
|
|
|
24
34
|
npx srm-report contribute # shows the diff, sends nothing
|
|
25
35
|
```
|
|
26
36
|
|
|
27
|
-
This is a strict guideline for AI agents using the skill: they must record improvements as they work and ask you at the end of every report. Nothing is sent until you have seen the changes and agreed. See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
37
|
+
This is a strict guideline for AI agents using the skill: they must record improvements as they work and ask you at the end of every report. Nothing is sent until you have seen the changes and agreed. This applies to every agent above. See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
28
38
|
|
|
29
39
|
## What's inside
|
|
30
40
|
|
package/SKILL.md
CHANGED
|
@@ -29,6 +29,8 @@ See `CONTRIBUTING.md`.
|
|
|
29
29
|
|
|
30
30
|
## How this skill works
|
|
31
31
|
|
|
32
|
+
This skill works with any agent that reads `SKILL.md`. Paths such as `scripts/rptlib.py` are relative to this file's folder, which differs between agents, so resolve them from where you loaded this file. Recipes find the folder themselves; if one can't, set `SRM_REPORT_SKILL`.
|
|
33
|
+
|
|
32
34
|
The user attaches the template they want. **The template is the spec.** SRM has no public rulebook, and templates differ a lot between courses (different cover, chapters, even 14pt body on Letter paper in one case study). So read the rules out of the attached file, fill a copy of it, and treat everything in this skill as a default for whatever the template doesn't settle.
|
|
33
35
|
|
|
34
36
|
When two sources disagree, this order decides:
|
|
@@ -82,6 +84,8 @@ python -X utf8 scripts/render_pages.py report.pdf pages 1,2,3,4 70 # chosen page
|
|
|
82
84
|
|
|
83
85
|
The Word pass fills the TOC and lists with real page numbers, exports the PDF and reports any table crossing a page. Work on a scratch copy, since the user often has the file open. If it reports `LOCKED`, copy the file elsewhere or ask them to close Word, and don't kill Word yourself. It can take a few minutes on a long report. If it stops with a COM error, run it once more; if the error repeats, the .docx is the problem. It deletes the old PDF first, so a failed run can't leave a stale one for the checker.
|
|
84
86
|
|
|
87
|
+
The Word pass needs Microsoft Word on Windows, in a session that can start it. If you run in a sandbox, in the cloud or on Linux or macOS and can't reach Word, build the .docx, say that the pass hasn't run, and give the user the exact command to run on their machine or ask for the sandbox to be relaxed. Don't substitute another converter and call the result final, because page numbers and table breaks would differ from what gets printed.
|
|
88
|
+
|
|
85
89
|
Put the template's numbers in `checks.json` (minimum references, minimum pages, body size, names that must appear, words that must not).
|
|
86
90
|
|
|
87
91
|
**Open the page images every time.** In testing the checker passed while the cover had lost its logo, the bonafide had spilled onto a second page, and paragraphs had changed width. Some of those are checked now, but not all of them can be. Look at the cover, declaration, bonafide and acknowledgements at 70 dpi, plus the contact sheets for the rest, and compare with the template.
|
|
@@ -15,7 +15,13 @@ raises KeyError naming the missing line: run inspect_template.py and adjust the
|
|
|
15
15
|
"""
|
|
16
16
|
import json, os, re, sys
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
# Agents keep skills in different folders. This file may also have been copied into a project.
|
|
19
|
+
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
20
|
+
PLACES = [os.environ.get("SRM_REPORT_SKILL"), os.path.join(HERE, "..", "..")] + [
|
|
21
|
+
os.path.expanduser(f"~/{p}/skills/srm-report") for p in (".agents", ".claude", ".gemini/config", ".cline", ".kiro")]
|
|
22
|
+
SKILL = next((p for p in PLACES if p and os.path.exists(os.path.join(p, "scripts", "rptlib.py"))), None)
|
|
23
|
+
if SKILL is None:
|
|
24
|
+
sys.exit("Can't find the srm-report skill. Set SRM_REPORT_SKILL to its folder.")
|
|
19
25
|
sys.path.insert(0, os.path.join(SKILL, "scripts"))
|
|
20
26
|
from docx.oxml import parse_xml
|
|
21
27
|
from docx.oxml.ns import qn
|
package/bin/srm-report.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Installs the srm-report skill for
|
|
2
|
+
// Installs the srm-report skill for AI coding agents, and sends a user's improvements back as a pull request.
|
|
3
3
|
const { execFileSync } = require("child_process");
|
|
4
|
+
const crypto = require("crypto");
|
|
4
5
|
const fs = require("fs");
|
|
5
6
|
const os = require("os");
|
|
6
7
|
const path = require("path");
|
|
@@ -9,7 +10,16 @@ const OWNER = "shauryaaojha";
|
|
|
9
10
|
const REPO = "srm-report";
|
|
10
11
|
const PKG_ROOT = path.join(__dirname, "..");
|
|
11
12
|
const SKILL_PATHS = ["SKILL.md", "references", "scripts", "assets"];
|
|
12
|
-
|
|
13
|
+
// Global skill folders from each agent's own docs. `home` is what marks the agent as present on this machine.
|
|
14
|
+
const TARGETS = [
|
|
15
|
+
{ id: "agents", dir: [".agents", "skills"], reads: "Codex, Cursor, Gemini CLI, GitHub Copilot, Windsurf, OpenCode, Roo Code" },
|
|
16
|
+
{ id: "claude", dir: [".claude", "skills"], reads: "Claude Code", home: ".claude" },
|
|
17
|
+
{ id: "antigravity", dir: [".gemini", "config", "skills"], reads: "Antigravity", home: ".gemini" },
|
|
18
|
+
{ id: "cline", dir: [".cline", "skills"], reads: "Cline", home: ".cline" },
|
|
19
|
+
{ id: "kiro", dir: [".kiro", "skills"], reads: "Kiro", home: ".kiro" },
|
|
20
|
+
];
|
|
21
|
+
const SHARED = ["codex", "cursor", "gemini", "copilot", "windsurf", "opencode", "roo"]; // all read ~/.agents/skills
|
|
22
|
+
const targetDir = (t) => path.join(os.homedir(), ...t.dir, REPO);
|
|
13
23
|
// Reports and templates must never travel. Only text that improves the skill goes in a pull request.
|
|
14
24
|
const BLOCKED_FILE = /\.(docx?|dotx|pdf|pptx?|xlsx?|png|jpe?g|gif|zip|tgz)$/i;
|
|
15
25
|
const SKIP = /(^|[\\/])(__pycache__|\.git)([\\/]|$)|\.pyc$/;
|
|
@@ -19,10 +29,19 @@ const PERSONAL = [
|
|
|
19
29
|
[/(?<![\d.])[6-9]\d{9}(?![\d.])/, "phone number"],
|
|
20
30
|
];
|
|
21
31
|
|
|
22
|
-
const HELP = `srm-report: SRM report skill for
|
|
32
|
+
const HELP = `srm-report: SRM report skill for AI coding agents
|
|
33
|
+
|
|
34
|
+
npx srm-report install
|
|
35
|
+
Install for every agent found on this machine. ~/.agents/skills is always included.
|
|
36
|
+
|
|
37
|
+
npx srm-report install --agent NAMES
|
|
38
|
+
Install for the named agents only, comma separated, or "all":
|
|
39
|
+
${[...SHARED, ...TARGETS.slice(1).map((t) => t.id)].join(", ")}
|
|
23
40
|
|
|
24
|
-
npx srm-report install
|
|
25
|
-
|
|
41
|
+
npx srm-report install --dir PATH
|
|
42
|
+
Install to one folder, for any other agent. Then tell it to follow PATH/SKILL.md.
|
|
43
|
+
|
|
44
|
+
Add --force to replace an existing copy.
|
|
26
45
|
|
|
27
46
|
npx srm-report contribute [--dir PATH]
|
|
28
47
|
Dry run. Compare your copy with the version you installed and print the diff. Sends nothing.
|
|
@@ -69,18 +88,72 @@ function copySkill(from, to) {
|
|
|
69
88
|
}
|
|
70
89
|
}
|
|
71
90
|
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
91
|
+
function chooseTargets(opts) {
|
|
92
|
+
if (opts.dir) return [{ path: path.resolve(opts.dir), reads: "the agent you point at it" }];
|
|
93
|
+
let picked;
|
|
94
|
+
if (opts.agent) {
|
|
95
|
+
const names = opts.agent.toLowerCase().split(",").map((s) => s.trim()).filter(Boolean);
|
|
96
|
+
picked = names.includes("all") ? TARGETS : TARGETS.filter((t) => names.includes(t.id) || (t.id === "agents" && names.some((n) => SHARED.includes(n))));
|
|
97
|
+
const known = ["all", ...SHARED, ...TARGETS.map((t) => t.id)];
|
|
98
|
+
const bad = names.filter((n) => !known.includes(n));
|
|
99
|
+
if (bad.length || !picked.length) die(`unknown agent "${bad.join(", ") || opts.agent}". Known: ${known.join(", ")}`);
|
|
100
|
+
} else {
|
|
101
|
+
picked = TARGETS.filter((t) => !t.home || fs.existsSync(path.join(os.homedir(), t.home)));
|
|
76
102
|
}
|
|
77
|
-
|
|
78
|
-
|
|
103
|
+
return picked.map((t) => ({ path: targetDir(t), reads: t.reads }));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function skillHash(dir) {
|
|
107
|
+
const h = crypto.createHash("sha256");
|
|
108
|
+
const files = SKILL_PATHS.flatMap((p) => {
|
|
109
|
+
const full = path.join(dir, p);
|
|
110
|
+
if (!fs.existsSync(full)) return [];
|
|
111
|
+
return fs.statSync(full).isDirectory() ? listFiles(full).map((f) => path.join(p, f)) : [p];
|
|
112
|
+
}).sort();
|
|
113
|
+
for (const f of files) h.update(f.replace(/\\/g, "/")).update(fs.readFileSync(path.join(dir, f)));
|
|
114
|
+
return h.digest("hex");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function install(opts) {
|
|
79
118
|
const { version } = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, "package.json"), "utf8"));
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
119
|
+
let done = 0;
|
|
120
|
+
for (const t of chooseTargets(opts)) {
|
|
121
|
+
if (fs.existsSync(path.join(t.path, "SKILL.md")) && !opts.force) {
|
|
122
|
+
console.log(`Skipped ${t.path}: already there, and it may hold your improvements. Send them with "npx srm-report contribute", or pass --force to replace it.`);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
for (const p of SKILL_PATHS) fs.rmSync(path.join(t.path, p), { recursive: true, force: true });
|
|
126
|
+
fs.mkdirSync(t.path, { recursive: true });
|
|
127
|
+
copySkill(PKG_ROOT, t.path);
|
|
128
|
+
fs.writeFileSync(path.join(t.path, "VERSION"), version + "\n");
|
|
129
|
+
fs.writeFileSync(path.join(t.path, "INSTALL_HASH"), skillHash(t.path) + "\n");
|
|
130
|
+
console.log(`Installed ${version} to ${t.path} (for ${t.reads})`);
|
|
131
|
+
done++;
|
|
132
|
+
}
|
|
133
|
+
if (done) {
|
|
134
|
+
console.log("Restart your agent so it finds the skill.");
|
|
135
|
+
console.log("If you improve it while making a report, send it back: npx srm-report contribute");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// With several copies installed, the one to send is the one that changed since install.
|
|
140
|
+
function findCopy(opts) {
|
|
141
|
+
if (opts.dir) {
|
|
142
|
+
const d = path.resolve(opts.dir);
|
|
143
|
+
if (!fs.existsSync(path.join(d, "SKILL.md"))) die(`no skill found at ${d}`);
|
|
144
|
+
return d;
|
|
145
|
+
}
|
|
146
|
+
const copies = TARGETS.map(targetDir).filter((d) => fs.existsSync(path.join(d, "SKILL.md")));
|
|
147
|
+
if (!copies.length) die('no installed skill found. Run "npx srm-report install", or pass --dir PATH.');
|
|
148
|
+
const stored = (d) => {
|
|
149
|
+
const f = path.join(d, "INSTALL_HASH");
|
|
150
|
+
return fs.existsSync(f) ? fs.readFileSync(f, "utf8").trim() : null;
|
|
151
|
+
};
|
|
152
|
+
const changed = copies.filter((d) => skillHash(d) !== stored(d));
|
|
153
|
+
if (changed.length > 1 && new Set(changed.map(skillHash)).size > 1) {
|
|
154
|
+
die(`more than one copy has changed. Pick one with --dir, after merging what you want to keep:\n ${changed.join("\n ")}`);
|
|
155
|
+
}
|
|
156
|
+
return changed[0] || copies[0];
|
|
84
157
|
}
|
|
85
158
|
|
|
86
159
|
function listFiles(dir, base = dir, out = []) {
|
|
@@ -103,8 +176,8 @@ function scanAdded(diff) {
|
|
|
103
176
|
}
|
|
104
177
|
|
|
105
178
|
function contribute(opts) {
|
|
106
|
-
const dir =
|
|
107
|
-
|
|
179
|
+
const dir = findCopy(opts);
|
|
180
|
+
console.log(`Using the copy at ${dir}`);
|
|
108
181
|
if (tryRun("gh", ["auth", "status"]) === null) die('the GitHub CLI is missing or signed out. Install gh and run "gh auth login".');
|
|
109
182
|
if (opts.yes && !opts.title) die('--yes needs --title "what improved"');
|
|
110
183
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "srm-report",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Claude Code
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Skill for AI coding agents (Claude Code, Codex, Cursor, Antigravity and more) that builds a print-ready SRM Institute report (.docx and .pdf) from the template you attach.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"srm-report": "bin/srm-report.js"
|
|
7
7
|
},
|
|
@@ -15,8 +15,13 @@
|
|
|
15
15
|
"CONTRIBUTING.md"
|
|
16
16
|
],
|
|
17
17
|
"keywords": [
|
|
18
|
+
"agent-skills",
|
|
18
19
|
"claude-code",
|
|
19
|
-
"
|
|
20
|
+
"codex",
|
|
21
|
+
"cursor",
|
|
22
|
+
"antigravity",
|
|
23
|
+
"gemini-cli",
|
|
24
|
+
"copilot",
|
|
20
25
|
"srm",
|
|
21
26
|
"srmist",
|
|
22
27
|
"report",
|