incitaciones 0.10.1 → 0.10.3
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.
|
@@ -49,17 +49,37 @@ Uses `$JOURNAL_PATH` (defaults to `~/dev/status`) for the daily log journal, and
|
|
|
49
49
|
grep -i "<slug>" "$JOURNAL"/inbox.md
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
7. **Check
|
|
53
|
-
|
|
52
|
+
7. **Check for accumulated whisper knowledge.**
|
|
53
|
+
|
|
54
|
+
**If `turu` is available** (`turu key --json` succeeds), delegate every
|
|
55
|
+
mechanical step to it — never hand-roll repo-key or path derivation:
|
|
56
|
+
```bash
|
|
57
|
+
turu key --json # canonical repo key, branch slug, worktree slot
|
|
58
|
+
turu recall repo # repo-scope knowledge (env facts)
|
|
59
|
+
turu recall branch # branch-scope knowledge (notes)
|
|
60
|
+
turu recall worktree # worktree-slot knowledge (only when step 8 uses one)
|
|
61
|
+
```
|
|
62
|
+
- `turu recall` output already routes to the right store (global, repo,
|
|
63
|
+
or repo-local `.whisper/`), so never `ls`/`find` `~/.whisper/` to guess
|
|
64
|
+
paths — canonical keys don't match raw remote URLs.
|
|
65
|
+
- `turu recall` exits non-zero with `nothing to recall in this scope`
|
|
66
|
+
when a scope is empty — treat that as "no knowledge here", not a
|
|
67
|
+
failure; move on without improvising alternate lookup paths.
|
|
68
|
+
|
|
69
|
+
**Fallback (turu absent):** if `~/.whisper/` exists, load context with:
|
|
54
70
|
```bash
|
|
55
71
|
repo_url=$(git remote get-url origin 2>/dev/null | sed 's|https://||;s|git@||;s|\.git$||')
|
|
56
72
|
branch_slug=$(git rev-parse --abbrev-ref HEAD | sed 's|/|--|g')
|
|
57
73
|
whisper_dir=~/.whisper/repos/"${repo_url}"/branches/"${branch_slug}"
|
|
58
74
|
```
|
|
59
|
-
- Read `context.md` if it exists (extract beads-epic, status)
|
|
60
|
-
- Read `plan.md` if it has real content
|
|
61
75
|
- Read `notes.md` if it has real content
|
|
62
76
|
- Read `env.md` at repo level for infra facts
|
|
77
|
+
- If the repo has a committed repo-local `.whisper/` directory, prefer
|
|
78
|
+
that over the global path. Its layout mirrors the global one relative
|
|
79
|
+
to the repo root:
|
|
80
|
+
`.whisper/repos/<org-path>/<repo>/branches/<branch-slug>/notes.md`
|
|
81
|
+
(e.g. `.whisper/repos/ak/PhormaSci/talleres/branches/main/notes.md`).
|
|
82
|
+
|
|
63
83
|
- If beads is available, fetch open issues:
|
|
64
84
|
```bash
|
|
65
85
|
bd list --label branch:<branch> 2>/dev/null
|
|
@@ -93,9 +113,9 @@ Uses `$JOURNAL_PATH` (defaults to `~/dev/status`) for the daily log journal, and
|
|
|
93
113
|
**Related inbox items** *(if any)*
|
|
94
114
|
- items that mention this project
|
|
95
115
|
|
|
96
|
-
**Whisper context** *(if
|
|
97
|
-
-
|
|
98
|
-
- Notes:
|
|
116
|
+
**Whisper context** *(if turu recall or the fallback found content)*
|
|
117
|
+
- Env: repo-scope infra facts (`turu recall repo` / `env.md`)
|
|
118
|
+
- Notes: branch-scope findings (`turu recall branch` / `notes.md`)
|
|
99
119
|
- Beads issues: <n> open, <n> in_progress
|
|
100
120
|
|
|
101
121
|
**⚠ Active in flight** *(omit if none)*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "incitaciones",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "Reusable prompts and skills for CLI LLM tools, with native pi package support.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"content/manifest.json",
|
|
28
28
|
"pi-package/",
|
|
29
29
|
"scripts/cli.mjs",
|
|
30
|
+
"scripts/lib/",
|
|
30
31
|
"scripts/generate-pi-resources.mjs",
|
|
31
32
|
"LICENSE",
|
|
32
33
|
"README.md"
|
|
@@ -63,17 +63,37 @@ Uses `$JOURNAL_PATH` (defaults to `~/dev/status`) for the daily log journal, and
|
|
|
63
63
|
grep -i "<slug>" "$JOURNAL"/inbox.md
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
7. **Check
|
|
67
|
-
|
|
66
|
+
7. **Check for accumulated whisper knowledge.**
|
|
67
|
+
|
|
68
|
+
**If `turu` is available** (`turu key --json` succeeds), delegate every
|
|
69
|
+
mechanical step to it — never hand-roll repo-key or path derivation:
|
|
70
|
+
```bash
|
|
71
|
+
turu key --json # canonical repo key, branch slug, worktree slot
|
|
72
|
+
turu recall repo # repo-scope knowledge (env facts)
|
|
73
|
+
turu recall branch # branch-scope knowledge (notes)
|
|
74
|
+
turu recall worktree # worktree-slot knowledge (only when step 8 uses one)
|
|
75
|
+
```
|
|
76
|
+
- `turu recall` output already routes to the right store (global, repo,
|
|
77
|
+
or repo-local `.whisper/`), so never `ls`/`find` `~/.whisper/` to guess
|
|
78
|
+
paths — canonical keys don't match raw remote URLs.
|
|
79
|
+
- `turu recall` exits non-zero with `nothing to recall in this scope`
|
|
80
|
+
when a scope is empty — treat that as "no knowledge here", not a
|
|
81
|
+
failure; move on without improvising alternate lookup paths.
|
|
82
|
+
|
|
83
|
+
**Fallback (turu absent):** if `~/.whisper/` exists, load context with:
|
|
68
84
|
```bash
|
|
69
85
|
repo_url=$(git remote get-url origin 2>/dev/null | sed 's|https://||;s|git@||;s|\.git$||')
|
|
70
86
|
branch_slug=$(git rev-parse --abbrev-ref HEAD | sed 's|/|--|g')
|
|
71
87
|
whisper_dir=~/.whisper/repos/"${repo_url}"/branches/"${branch_slug}"
|
|
72
88
|
```
|
|
73
|
-
- Read `context.md` if it exists (extract beads-epic, status)
|
|
74
|
-
- Read `plan.md` if it has real content
|
|
75
89
|
- Read `notes.md` if it has real content
|
|
76
90
|
- Read `env.md` at repo level for infra facts
|
|
91
|
+
- If the repo has a committed repo-local `.whisper/` directory, prefer
|
|
92
|
+
that over the global path. Its layout mirrors the global one relative
|
|
93
|
+
to the repo root:
|
|
94
|
+
`.whisper/repos/<org-path>/<repo>/branches/<branch-slug>/notes.md`
|
|
95
|
+
(e.g. `.whisper/repos/ak/PhormaSci/talleres/branches/main/notes.md`).
|
|
96
|
+
|
|
77
97
|
- If beads is available, fetch open issues:
|
|
78
98
|
```bash
|
|
79
99
|
bd list --label branch:<branch> 2>/dev/null
|
|
@@ -107,9 +127,9 @@ Uses `$JOURNAL_PATH` (defaults to `~/dev/status`) for the daily log journal, and
|
|
|
107
127
|
**Related inbox items** *(if any)*
|
|
108
128
|
- items that mention this project
|
|
109
129
|
|
|
110
|
-
**Whisper context** *(if
|
|
111
|
-
-
|
|
112
|
-
- Notes:
|
|
130
|
+
**Whisper context** *(if turu recall or the fallback found content)*
|
|
131
|
+
- Env: repo-scope infra facts (`turu recall repo` / `env.md`)
|
|
132
|
+
- Notes: branch-scope findings (`turu recall branch` / `notes.md`)
|
|
113
133
|
- Beads issues: <n> open, <n> in_progress
|
|
114
134
|
|
|
115
135
|
**⚠ Active in flight** *(omit if none)*
|
|
@@ -49,17 +49,37 @@ Uses `$JOURNAL_PATH` (defaults to `~/dev/status`) for the daily log journal, and
|
|
|
49
49
|
grep -i "<slug>" "$JOURNAL"/inbox.md
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
7. **Check
|
|
53
|
-
|
|
52
|
+
7. **Check for accumulated whisper knowledge.**
|
|
53
|
+
|
|
54
|
+
**If `turu` is available** (`turu key --json` succeeds), delegate every
|
|
55
|
+
mechanical step to it — never hand-roll repo-key or path derivation:
|
|
56
|
+
```bash
|
|
57
|
+
turu key --json # canonical repo key, branch slug, worktree slot
|
|
58
|
+
turu recall repo # repo-scope knowledge (env facts)
|
|
59
|
+
turu recall branch # branch-scope knowledge (notes)
|
|
60
|
+
turu recall worktree # worktree-slot knowledge (only when step 8 uses one)
|
|
61
|
+
```
|
|
62
|
+
- `turu recall` output already routes to the right store (global, repo,
|
|
63
|
+
or repo-local `.whisper/`), so never `ls`/`find` `~/.whisper/` to guess
|
|
64
|
+
paths — canonical keys don't match raw remote URLs.
|
|
65
|
+
- `turu recall` exits non-zero with `nothing to recall in this scope`
|
|
66
|
+
when a scope is empty — treat that as "no knowledge here", not a
|
|
67
|
+
failure; move on without improvising alternate lookup paths.
|
|
68
|
+
|
|
69
|
+
**Fallback (turu absent):** if `~/.whisper/` exists, load context with:
|
|
54
70
|
```bash
|
|
55
71
|
repo_url=$(git remote get-url origin 2>/dev/null | sed 's|https://||;s|git@||;s|\.git$||')
|
|
56
72
|
branch_slug=$(git rev-parse --abbrev-ref HEAD | sed 's|/|--|g')
|
|
57
73
|
whisper_dir=~/.whisper/repos/"${repo_url}"/branches/"${branch_slug}"
|
|
58
74
|
```
|
|
59
|
-
- Read `context.md` if it exists (extract beads-epic, status)
|
|
60
|
-
- Read `plan.md` if it has real content
|
|
61
75
|
- Read `notes.md` if it has real content
|
|
62
76
|
- Read `env.md` at repo level for infra facts
|
|
77
|
+
- If the repo has a committed repo-local `.whisper/` directory, prefer
|
|
78
|
+
that over the global path. Its layout mirrors the global one relative
|
|
79
|
+
to the repo root:
|
|
80
|
+
`.whisper/repos/<org-path>/<repo>/branches/<branch-slug>/notes.md`
|
|
81
|
+
(e.g. `.whisper/repos/ak/PhormaSci/talleres/branches/main/notes.md`).
|
|
82
|
+
|
|
63
83
|
- If beads is available, fetch open issues:
|
|
64
84
|
```bash
|
|
65
85
|
bd list --label branch:<branch> 2>/dev/null
|
|
@@ -93,9 +113,9 @@ Uses `$JOURNAL_PATH` (defaults to `~/dev/status`) for the daily log journal, and
|
|
|
93
113
|
**Related inbox items** *(if any)*
|
|
94
114
|
- items that mention this project
|
|
95
115
|
|
|
96
|
-
**Whisper context** *(if
|
|
97
|
-
-
|
|
98
|
-
- Notes:
|
|
116
|
+
**Whisper context** *(if turu recall or the fallback found content)*
|
|
117
|
+
- Env: repo-scope infra facts (`turu recall repo` / `env.md`)
|
|
118
|
+
- Notes: branch-scope findings (`turu recall branch` / `notes.md`)
|
|
99
119
|
- Beads issues: <n> open, <n> in_progress
|
|
100
120
|
|
|
101
121
|
**⚠ Active in flight** *(omit if none)*
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Purpose: Shared compiler for router members and compat pointers.
|
|
2
|
+
// Responsibilities:
|
|
3
|
+
// - Resolve a manifest `pointer_for` value ("<router>/<member>") to its member file
|
|
4
|
+
// - Compile a skill directory into one self-contained markdown document
|
|
5
|
+
// (SKILL.md + all nested references/, recursively, with path headers)
|
|
6
|
+
// - Compile a pointer body: provenance banner + full member content
|
|
7
|
+
// - Validate that every pointer_for target exists
|
|
8
|
+
// Rationale: compiled pointers are distribution artifacts (site, pi-package,
|
|
9
|
+
// skills/ catalog, flat installs) and must never be hand-written prose. This
|
|
10
|
+
// module is the single implementation shared by build.sh, generate-pi-resources.mjs
|
|
11
|
+
// and generate-skills-dir.mjs so the content cannot drift between surfaces.
|
|
12
|
+
import fs from "node:fs";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
|
|
15
|
+
export function readText(filePath) {
|
|
16
|
+
return fs.readFileSync(filePath, "utf8");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Strip a leading frontmatter block from a markdown document.
|
|
20
|
+
export function stripFrontmatter(raw) {
|
|
21
|
+
if (!raw.startsWith("---\n")) return raw;
|
|
22
|
+
const end = raw.indexOf("\n---\n", 3);
|
|
23
|
+
if (end === -1) return raw;
|
|
24
|
+
return raw.slice(end + 5).replace(/^\n+/, "");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Resolve "<router>/<member>" to the member's entry file:
|
|
28
|
+
// prefers references/<member>/SKILL.md, falls back to references/<member>.md.
|
|
29
|
+
export function resolveMemberPath(repoRoot, pointerFor) {
|
|
30
|
+
const [router, member] = pointerFor.split("/");
|
|
31
|
+
const base = path.join(repoRoot, "content", "distilled", router, "references");
|
|
32
|
+
const multi = path.join(base, member, "SKILL.md");
|
|
33
|
+
if (fs.existsSync(multi)) return { file: multi, multiFile: true, router, member };
|
|
34
|
+
const flat = path.join(base, `${member}.md`);
|
|
35
|
+
if (fs.existsSync(flat)) return { file: flat, multiFile: false, router, member };
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Recursively collect markdown files under references/, sorted for determinism.
|
|
40
|
+
function collectReferences(refDir) {
|
|
41
|
+
const files = [];
|
|
42
|
+
if (!fs.existsSync(refDir)) return files;
|
|
43
|
+
const entries = fs.readdirSync(refDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
const full = path.join(refDir, entry.name);
|
|
46
|
+
if (entry.isDirectory()) {
|
|
47
|
+
files.push(...collectReferences(full));
|
|
48
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
49
|
+
files.push(full);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return files;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Compile a skill directory (containing SKILL.md) into one self-contained
|
|
56
|
+
// document: core instructions followed by every reference file, each headed
|
|
57
|
+
// by its path relative to the skill directory.
|
|
58
|
+
export function compileSkillDoc(repoRoot, skillRelDir) {
|
|
59
|
+
const skillDir = path.join(repoRoot, skillRelDir);
|
|
60
|
+
const skillFile = path.join(skillDir, "SKILL.md");
|
|
61
|
+
if (!fs.existsSync(skillFile)) {
|
|
62
|
+
throw new Error(`No SKILL.md in ${skillRelDir}`);
|
|
63
|
+
}
|
|
64
|
+
const parts = [];
|
|
65
|
+
parts.push(`#### Core Instructions (${path.join(skillRelDir, "SKILL.md")})\n\n`);
|
|
66
|
+
parts.push(readText(skillFile).trimEnd());
|
|
67
|
+
for (const ref of collectReferences(path.join(skillDir, "references"))) {
|
|
68
|
+
const rel = path.relative(skillDir, ref);
|
|
69
|
+
parts.push(`\n\n---\n\n#### Reference: ${rel}\n\n`);
|
|
70
|
+
parts.push(readText(ref).trimEnd());
|
|
71
|
+
}
|
|
72
|
+
return parts.join("");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Compile the body of a pointer: provenance banner + full member content.
|
|
76
|
+
// The pointer is a permanent compiled alias — never a prose stub, never deleted.
|
|
77
|
+
export function compilePointerBody(repoRoot, pointerFor) {
|
|
78
|
+
const resolved = resolveMemberPath(repoRoot, pointerFor);
|
|
79
|
+
if (!resolved) {
|
|
80
|
+
throw new Error(`pointer_for target not found: ${pointerFor}`);
|
|
81
|
+
}
|
|
82
|
+
const memberRel = path.relative(repoRoot, resolved.file);
|
|
83
|
+
const banner = [
|
|
84
|
+
`> **Moved:** this entry is now the **${resolved.member}** mode of the **${resolved.router}** skill.`,
|
|
85
|
+
`> The full method is compiled below from \`${memberRel}\`.`,
|
|
86
|
+
`> Invoke via \`/skill:${resolved.member}\`, invoke \`/skill:${resolved.router}\` for the mode table,`,
|
|
87
|
+
`> or use this URL directly in a chat interface.`,
|
|
88
|
+
"",
|
|
89
|
+
"",
|
|
90
|
+
].join("\n");
|
|
91
|
+
|
|
92
|
+
let content;
|
|
93
|
+
if (resolved.multiFile) {
|
|
94
|
+
content = compileSkillDoc(repoRoot, path.dirname(memberRel));
|
|
95
|
+
} else {
|
|
96
|
+
content = readText(resolved.file).trimEnd();
|
|
97
|
+
}
|
|
98
|
+
return banner + content;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// List member names of a multi-file router skill: subdirectories of references/
|
|
102
|
+
// that contain a SKILL.md (multi-file members), plus flat references/*.md.
|
|
103
|
+
export function listMembers(repoRoot, skillRelDir) {
|
|
104
|
+
const refDir = path.join(repoRoot, skillRelDir, "references");
|
|
105
|
+
const multi = [];
|
|
106
|
+
const flat = [];
|
|
107
|
+
if (!fs.existsSync(refDir)) return { multi, flat };
|
|
108
|
+
for (const entry of fs.readdirSync(refDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
109
|
+
if (entry.isDirectory() && fs.existsSync(path.join(refDir, entry.name, "SKILL.md"))) {
|
|
110
|
+
multi.push(entry.name);
|
|
111
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
112
|
+
flat.push(entry.name.replace(/\.md$/, ""));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { multi, flat };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Validate every pointer_for in the manifest resolves to an existing member.
|
|
119
|
+
// Returns an array of error strings (empty = valid).
|
|
120
|
+
export function validatePointers(repoRoot, manifest) {
|
|
121
|
+
const errors = [];
|
|
122
|
+
for (const prompt of manifest.prompts) {
|
|
123
|
+
if (!prompt.pointer_for) continue;
|
|
124
|
+
if (!prompt.disable_model_invocation) {
|
|
125
|
+
errors.push(`${prompt.name}: pointer_for set but disable_model_invocation is not true`);
|
|
126
|
+
}
|
|
127
|
+
if (!resolveMemberPath(repoRoot, prompt.pointer_for)) {
|
|
128
|
+
errors.push(`${prompt.name}: pointer_for target not found: ${prompt.pointer_for}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return errors;
|
|
132
|
+
}
|