@lazyingart/agintiflow 0.20.190 → 0.20.191
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/package.json +1 -1
- package/references/agintiflow-npm-publication-memory.md +11 -0
- package/references/lazyedit-publish-skill-installation-2026-06-03.md +88 -0
- package/references/paper-revision-skill-installation-2026-06-03.md +62 -0
- package/scripts/smoke-skillmesh.js +23 -0
- package/src/skill-library.js +11 -7
- package/src/skillmesh.js +56 -20
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.191",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -10,6 +10,17 @@ This note records the working publication route for AgInTiFlow so future release
|
|
|
10
10
|
|
|
11
11
|
Latest verified release:
|
|
12
12
|
|
|
13
|
+
- Version: `0.20.190`
|
|
14
|
+
- Commit: `f192f8e`
|
|
15
|
+
- GitHub Actions run: `https://github.com/lazyingart/AgInTiFlow/actions/runs/26736284533`
|
|
16
|
+
- Workflow result: success
|
|
17
|
+
- npm registry check: `npm view @lazyingart/agintiflow version dist-tags.latest --registry=https://registry.npmjs.org` returned `0.20.190`.
|
|
18
|
+
- Installed verification: `npm install -g @lazyingart/agintiflow@0.20.190` then `aginti --version` returned `0.20.190`.
|
|
19
|
+
- Webapp verification: `aginti webapp restart --port 3210` then `curl -fsS http://127.0.0.1:3210/health` returned `version":"0.20.190"` from the global npm package path.
|
|
20
|
+
- Feature verification: `aginti --provider mock --routing manual --model mock-agent --no-shell --allow-file-tools --scs off "hello"` returned a direct greeting and did not create `hello.py`.
|
|
21
|
+
|
|
22
|
+
Previous verified release:
|
|
23
|
+
|
|
13
24
|
- Version: `0.20.189`
|
|
14
25
|
- Commit: `0095284`
|
|
15
26
|
- GitHub Actions run: `https://github.com/lazyingart/AgInTiFlow/actions/runs/26734901522`
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# LazyEdit Publish Skill Installation
|
|
2
|
+
|
|
3
|
+
Date: 2026-06-03
|
|
4
|
+
|
|
5
|
+
## Installed Skill
|
|
6
|
+
|
|
7
|
+
Skill id:
|
|
8
|
+
|
|
9
|
+
- `lazyedit-publish-workflow`
|
|
10
|
+
|
|
11
|
+
Source skill:
|
|
12
|
+
|
|
13
|
+
- `/home/lachlan/.codex/skills/lazyedit-publish-workflow/SKILL.md`
|
|
14
|
+
|
|
15
|
+
AgInTiFlow project-local copy:
|
|
16
|
+
|
|
17
|
+
- `/home/lachlan/ProjectsLFS/Agent/AgInTiFlow/.aginti/skills/lazyedit-publish-workflow/SKILL.md`
|
|
18
|
+
|
|
19
|
+
Global AgInTiFlow custom copy:
|
|
20
|
+
|
|
21
|
+
- Skill Mesh storage under `/home/lachlan/.agintiflow/skillmesh/skills/lazyedit-publish-workflow/`
|
|
22
|
+
- Installed as a local-reviewed enabled Skill Mesh skill.
|
|
23
|
+
|
|
24
|
+
## Why Both Copies Exist
|
|
25
|
+
|
|
26
|
+
The project-local `.aginti/skills/` copy documents the skill in the AgInTiFlow repository and lets local development sessions load it as `source=project-local`.
|
|
27
|
+
|
|
28
|
+
The Skill Mesh copy makes the same skill available from other working directories such as LALACHAN, RARACHAN, LazyEdit, or Agent without requiring the user to start AgInTiFlow from the AgInTiFlow repo.
|
|
29
|
+
|
|
30
|
+
## Verification
|
|
31
|
+
|
|
32
|
+
Project-local loader:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
node -e "import { listSkills, selectSkillsForGoal } from './src/skill-library.js'; const skill=listSkills({projectRoot:process.cwd(), includeBody:true}).find(s=>s.id==='lazyedit-publish-workflow'); if(!skill) throw new Error('missing'); console.log(skill.source);"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Expected result:
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
project-local
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Global Skill Mesh install:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
node bin/aginti-cli.js skillmesh export lazyedit-publish-workflow --out /tmp/lazyedit-publish-workflow.skillpack.json
|
|
48
|
+
node bin/aginti-cli.js skillmesh import /tmp/lazyedit-publish-workflow.skillpack.json --enable
|
|
49
|
+
node bin/aginti-cli.js skillmesh status
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Expected result includes:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
enabled lazyedit-publish-workflow: LazyEdit Publish Workflow
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Cross-directory discovery:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
cd /home/lachlan/ProjectsLFS/Agent
|
|
62
|
+
aginti skills lazyedit
|
|
63
|
+
|
|
64
|
+
cd /home/lachlan/ProjectsLFS/LALACHAN
|
|
65
|
+
node /home/lachlan/ProjectsLFS/Agent/AgInTiFlow/bin/aginti-cli.js skills lazyedit
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Expected result:
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
lazyedit-publish-workflow ... source=skillmesh
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Selection check:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
node -e "import { selectSkillsForGoal } from '/home/lachlan/ProjectsLFS/Agent/AgInTiFlow/src/skill-library.js'; const selected=selectSkillsForGoal('publish latest LALACHAN generated video through LazyEdit to Shipinhao YouTube Instagram with corrected subtitles', {projectRoot:'/home/lachlan/ProjectsLFS/LALACHAN', includeBody:true}).map(s=>s.id); console.log(selected); if(!selected.includes('lazyedit-publish-workflow')) throw new Error('not selected');"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Expected result:
|
|
81
|
+
|
|
82
|
+
- `lazyedit-publish-workflow` appears in the selected skill list.
|
|
83
|
+
|
|
84
|
+
## Operational Notes
|
|
85
|
+
|
|
86
|
+
The skill is prompt guidance, not a privileged tool. LazyEdit publishing still runs through normal AgInTiFlow permission policy and shell/tmux safeguards.
|
|
87
|
+
|
|
88
|
+
The subtitle correction rule is preserved: generated video scripts/stories are reference context for correcting ASR recognition errors, not verbatim targets. The agent should compare ASR output against the script, infer intended wording, preserve timing/line structure, and avoid blindly copying script text when the audio differs.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Paper Revision Skill Installation
|
|
2
|
+
|
|
3
|
+
Date: 2026-06-03
|
|
4
|
+
|
|
5
|
+
Source repository:
|
|
6
|
+
|
|
7
|
+
- `/home/lachlan/ProjectsLFS/paper-revision-skill`
|
|
8
|
+
- `https://github.com/lachlanchen/paper-revision-skill`
|
|
9
|
+
|
|
10
|
+
Purpose:
|
|
11
|
+
|
|
12
|
+
- Add plan-gated academic manuscript revision as a reusable skill.
|
|
13
|
+
- Keep manuscript-specific revision policy in the skill, not in AgInTiFlow core.
|
|
14
|
+
- Let Codex, AgInTiFlow, Claude, Gemini, and Copilot consume the same workflow guidance with tool-specific entry files.
|
|
15
|
+
|
|
16
|
+
AgInTiFlow integration:
|
|
17
|
+
|
|
18
|
+
- The repository's root `SKILL.md` is Codex-style and uses `name:` plus `description:`.
|
|
19
|
+
- AgInTiFlow SkillMesh now accepts that dialect by normalizing `name:` to native `id:` and deriving `label:` when missing.
|
|
20
|
+
- Install the root `SKILL.md` as a reviewed local SkillMesh skill and enable it.
|
|
21
|
+
|
|
22
|
+
Suggested install command from the AgInTiFlow repository:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
node --input-type=module - <<'NODE'
|
|
26
|
+
import fs from "node:fs/promises";
|
|
27
|
+
import {
|
|
28
|
+
buildSkillPackFromMarkdown,
|
|
29
|
+
enableSkillMeshSkill,
|
|
30
|
+
installSkillPack,
|
|
31
|
+
} from "./src/skillmesh.js";
|
|
32
|
+
|
|
33
|
+
const content = await fs.readFile("/home/lachlan/ProjectsLFS/paper-revision-skill/SKILL.md", "utf8");
|
|
34
|
+
const pack = await buildSkillPackFromMarkdown(content, { valueScore: 95 });
|
|
35
|
+
await installSkillPack(pack, { enabled: true });
|
|
36
|
+
await enableSkillMeshSkill("paper-revision-skill", true);
|
|
37
|
+
console.log(JSON.stringify({ ok: true, installedSkills: pack.skills.map((skill) => skill.id), packHash: pack.packHash }, null, 2));
|
|
38
|
+
NODE
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Validation:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
node scripts/smoke-skillmesh.js
|
|
45
|
+
node --input-type=module - <<'NODE'
|
|
46
|
+
import { selectSkillsForGoal } from "./src/skill-library.js";
|
|
47
|
+
const selected = selectSkillsForGoal(
|
|
48
|
+
"Use the paper revision workflow to answer reviewer comments before editing LaTeX.",
|
|
49
|
+
{ limit: 8, includeBody: false }
|
|
50
|
+
);
|
|
51
|
+
console.log(JSON.stringify(selected.map((skill) => ({ id: skill.id, source: skill.source })), null, 2));
|
|
52
|
+
if (!selected.some((skill) => skill.id === "paper-revision-skill")) {
|
|
53
|
+
throw new Error("paper-revision-skill was not selected");
|
|
54
|
+
}
|
|
55
|
+
NODE
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Boundary:
|
|
59
|
+
|
|
60
|
+
- Do not move manuscript-revision details into AgInTiFlow core.
|
|
61
|
+
- Improve AgInTiFlow core only when the issue is general skill compatibility, skill discovery, SkillMesh validation, evidence tracking, PDF/build tooling, or tool invocation reliability.
|
|
62
|
+
- Keep the skill's plan-first, edit-second, PDF-verified revision workflow in the skill repository.
|
|
@@ -64,6 +64,28 @@ assert(!listSkills({ includeBody: false }).some((skill) => skill.id === "mesh-an
|
|
|
64
64
|
await setSkillMeshMode("share");
|
|
65
65
|
const config = await loadSkillMeshConfig();
|
|
66
66
|
assert(config.mode === "share", "share mode should persist");
|
|
67
|
+
|
|
68
|
+
const codexStyleMarkdown = [
|
|
69
|
+
"---",
|
|
70
|
+
"name: paper-revision-skill",
|
|
71
|
+
"description: Plan-gated academic manuscript revision workflow for LaTeX papers, reviewer responses, proof corrections, redlines, PDF-backed audits, and submission packaging.",
|
|
72
|
+
"---",
|
|
73
|
+
"",
|
|
74
|
+
"Plan first, edit second, verify from compiled PDFs, and keep revision evidence under references/.",
|
|
75
|
+
"",
|
|
76
|
+
].join("\n");
|
|
77
|
+
const codexPack = await buildSkillPackFromMarkdown(codexStyleMarkdown, { valueScore: 90 });
|
|
78
|
+
assert(codexPack.skills[0].id === "paper-revision-skill", "Codex-style name should become SkillMesh id");
|
|
79
|
+
assert(codexPack.skills[0].content.includes("id: paper-revision-skill"), "Codex-style skill should be normalized with id");
|
|
80
|
+
assert(codexPack.skills[0].content.includes("label: Paper Revision Skill"), "Codex-style skill should receive a label");
|
|
81
|
+
await installSkillPack(codexPack, { enabled: true });
|
|
82
|
+
assert(
|
|
83
|
+
selectSkillsForGoal("use the paper revision workflow to answer reviewer comments before editing latex", { limit: 8 }).some(
|
|
84
|
+
(skill) => skill.id === "paper-revision-skill"
|
|
85
|
+
),
|
|
86
|
+
"Codex-style imported skill should be selectable"
|
|
87
|
+
);
|
|
88
|
+
|
|
67
89
|
const serviceUnit = buildSkillMeshServiceUnit({
|
|
68
90
|
dataDir: path.join(tempRoot, "relay-service"),
|
|
69
91
|
runScript: path.join(tempRoot, "relay-service", "aginti-skill-relay.run.sh"),
|
|
@@ -120,6 +142,7 @@ console.log(
|
|
|
120
142
|
"metadata-sync",
|
|
121
143
|
"soft-unreachable-node",
|
|
122
144
|
"systemd-service-unit",
|
|
145
|
+
"codex-style-skill-import",
|
|
123
146
|
],
|
|
124
147
|
},
|
|
125
148
|
null,
|
package/src/skill-library.js
CHANGED
|
@@ -100,15 +100,19 @@ function normalizeExternalTools(value) {
|
|
|
100
100
|
export function loadSkillFile(filePath) {
|
|
101
101
|
const text = fs.readFileSync(filePath, "utf8");
|
|
102
102
|
const { meta, body } = parseFrontmatter(text, filePath);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
const id = String(meta.id || meta.name || "").trim();
|
|
104
|
+
if (!id) {
|
|
105
|
+
throw new Error(`${filePath}: id or name must be a non-empty string`);
|
|
106
|
+
}
|
|
107
|
+
const label = String(meta.label || titleCaseSkillId(id)).trim();
|
|
108
|
+
const description = String(meta.description || "").trim();
|
|
109
|
+
if (!description) {
|
|
110
|
+
throw new Error(`${filePath}: description must be a non-empty string`);
|
|
107
111
|
}
|
|
108
112
|
return {
|
|
109
|
-
id
|
|
110
|
-
label
|
|
111
|
-
description
|
|
113
|
+
id,
|
|
114
|
+
label,
|
|
115
|
+
description,
|
|
112
116
|
triggers: normalizeList(meta.triggers),
|
|
113
117
|
tools: normalizeList(meta.tools),
|
|
114
118
|
body,
|
package/src/skillmesh.js
CHANGED
|
@@ -321,6 +321,60 @@ function canonicalSkillContent(content = "") {
|
|
|
321
321
|
return String(content || "").replace(/\r\n/g, "\n").trim();
|
|
322
322
|
}
|
|
323
323
|
|
|
324
|
+
function titleCaseSkillId(id = "") {
|
|
325
|
+
return String(id || "")
|
|
326
|
+
.split(/[-_\s/]+/)
|
|
327
|
+
.filter(Boolean)
|
|
328
|
+
.map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`)
|
|
329
|
+
.join(" ");
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function parseSkillMarkdownContent(content = "", context = "SKILL.md") {
|
|
333
|
+
const text = String(content || "");
|
|
334
|
+
if (!text.trim()) throw new Error(`${context}: empty skill`);
|
|
335
|
+
if (Buffer.byteLength(text, "utf8") > MAX_SKILL_BYTES) throw new Error(`${context}: skill is too large`);
|
|
336
|
+
if (redactSensitiveText(text) !== text) throw new Error(`${context}: contains token-like secret text`);
|
|
337
|
+
const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
338
|
+
if (!match) throw new Error(`${context}: missing YAML frontmatter`);
|
|
339
|
+
const frontmatter = match[1].replace(/\r\n/g, "\n");
|
|
340
|
+
const fields = {};
|
|
341
|
+
const passthrough = [];
|
|
342
|
+
for (const line of frontmatter.split(/\n/)) {
|
|
343
|
+
const scalar = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
344
|
+
if (scalar) {
|
|
345
|
+
const key = scalar[1];
|
|
346
|
+
const value = scalar[2].trim().replace(/^['"]|['"]$/g, "");
|
|
347
|
+
if (value) fields[key] = value;
|
|
348
|
+
if (["id", "name", "label", "description"].includes(key)) continue;
|
|
349
|
+
}
|
|
350
|
+
passthrough.push(line);
|
|
351
|
+
}
|
|
352
|
+
const id = fields.id || fields.name || "";
|
|
353
|
+
if (!id) throw new Error(`${context}: id or name is required`);
|
|
354
|
+
if (!safeSkillId(id)) throw new Error(`${context}: invalid skill id ${id}`);
|
|
355
|
+
const description = fields.description || "";
|
|
356
|
+
if (!description) throw new Error(`${context}: description is required`);
|
|
357
|
+
const label = fields.label || titleCaseSkillId(id);
|
|
358
|
+
return {
|
|
359
|
+
fields: { ...fields, id, label, description },
|
|
360
|
+
body: text.slice(match[0].length).trim(),
|
|
361
|
+
passthrough: passthrough.join("\n").trim(),
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function normalizeSkillMarkdownForSkillMesh(content = "", context = "SKILL.md") {
|
|
366
|
+
const parsed = parseSkillMarkdownContent(content, context);
|
|
367
|
+
const frontmatter = [
|
|
368
|
+
"---",
|
|
369
|
+
`id: ${parsed.fields.id}`,
|
|
370
|
+
`label: ${parsed.fields.label}`,
|
|
371
|
+
`description: ${parsed.fields.description}`,
|
|
372
|
+
parsed.passthrough,
|
|
373
|
+
"---",
|
|
374
|
+
].filter((line) => line !== "");
|
|
375
|
+
return `${frontmatter.join("\n")}\n\n${parsed.body}`.trim();
|
|
376
|
+
}
|
|
377
|
+
|
|
324
378
|
function canonicalPackForSignature(pack) {
|
|
325
379
|
const copy = { ...pack };
|
|
326
380
|
delete copy.signature;
|
|
@@ -382,25 +436,7 @@ function verifyPackSignature(pack) {
|
|
|
382
436
|
}
|
|
383
437
|
|
|
384
438
|
function validateSkillMarkdownContent(content = "", context = "SKILL.md") {
|
|
385
|
-
|
|
386
|
-
if (!text.trim()) throw new Error(`${context}: empty skill`);
|
|
387
|
-
if (Buffer.byteLength(text, "utf8") > MAX_SKILL_BYTES) throw new Error(`${context}: skill is too large`);
|
|
388
|
-
if (redactSensitiveText(text) !== text) throw new Error(`${context}: contains token-like secret text`);
|
|
389
|
-
const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
390
|
-
if (!match) throw new Error(`${context}: missing YAML frontmatter`);
|
|
391
|
-
const frontmatter = match[1];
|
|
392
|
-
const fields = {};
|
|
393
|
-
for (const line of frontmatter.split(/\r?\n/)) {
|
|
394
|
-
const scalar = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
395
|
-
if (!scalar) continue;
|
|
396
|
-
const value = scalar[2].trim().replace(/^['"]|['"]$/g, "");
|
|
397
|
-
if (value) fields[scalar[1]] = value;
|
|
398
|
-
}
|
|
399
|
-
for (const field of ["id", "label", "description"]) {
|
|
400
|
-
if (!fields[field]) throw new Error(`${context}: ${field} is required`);
|
|
401
|
-
}
|
|
402
|
-
if (!safeSkillId(fields.id)) throw new Error(`${context}: invalid skill id ${fields.id}`);
|
|
403
|
-
return fields;
|
|
439
|
+
return parseSkillMarkdownContent(content, context).fields;
|
|
404
440
|
}
|
|
405
441
|
|
|
406
442
|
function builtInSkillIds() {
|
|
@@ -420,7 +456,7 @@ export async function buildSkillPack(skillId, { home = agintiflowHome(), author
|
|
|
420
456
|
export async function buildSkillPackFromMarkdown(content, { home = agintiflowHome(), author = "local-user", valueScore = 80 } = {}) {
|
|
421
457
|
const fields = validateSkillMarkdownContent(content, "SKILL.md");
|
|
422
458
|
const id = fields.id;
|
|
423
|
-
const normalized =
|
|
459
|
+
const normalized = normalizeSkillMarkdownForSkillMesh(content, "SKILL.md");
|
|
424
460
|
const entry = {
|
|
425
461
|
id,
|
|
426
462
|
path: `skills/${id}/SKILL.md`,
|