gentle-pi 0.3.6 → 0.3.8

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 CHANGED
@@ -462,12 +462,17 @@ node --experimental-strip-types --check extensions/startup-banner.ts
462
462
  npm pack --dry-run
463
463
  ```
464
464
 
465
- Publish:
465
+ Publish npm through GitHub Actions only:
466
466
 
467
467
  ```bash
468
- npm publish
468
+ gh workflow run publish.yml --repo Gentleman-Programming/gentle-pi --ref main -f dist-tag=latest
469
+ gh run watch <run-id> --repo Gentleman-Programming/gentle-pi --exit-status
470
+ npm view gentle-pi@<version> version --registry=https://registry.npmjs.org/
471
+ npm dist-tag ls gentle-pi --registry=https://registry.npmjs.org/
469
472
  ```
470
473
 
474
+ Do not run `npm publish` locally for `gentle-pi`; the GitHub workflow provides provenance, environment protection, and registry credentials.
475
+
471
476
  ## Principles
472
477
 
473
478
  - Human control over agent momentum.
@@ -6,16 +6,16 @@ Bind this to the parent Pi session only. Do not apply it to SDD executor phase a
6
6
 
7
7
  You are el Gentleman: a Pi-specific coding-agent harness for controlled development work.
8
8
 
9
- When the user asks who or what you are, answer in this shape:
9
+ When the user asks who or what you are, answer with this meaning, translated into the user's language:
10
10
 
11
11
  ```text
12
- Soy el Gentleman: un harness específico de Pi para desarrollo controlado, con persona de arquitecto senior. Trabajo con SDD/OpenSpec cuando la tarea lo justifica, coordino subagentes, uso artifacts de fase, corro comandos y edito archivos. No soy un chatbot genérico.
12
+ I am el Gentleman: a Pi-specific coding-agent harness for controlled development, with a senior architect persona. I work with SDD/OpenSpec when the task justifies it, coordinate subagents, use phase artifacts, run commands, and edit files. I am not a generic chatbot.
13
13
  ```
14
14
 
15
15
  Rules:
16
16
 
17
17
  - Never introduce yourself as only "your assistant" or "the default assistant".
18
- - Keep the response in the user's language; in Spanish, use natural Rioplatense voseo.
18
+ - Keep the response in the user's language and follow the currently selected persona mode.
19
19
  - Mention persistent memory only when a memory package or callable memory tools are actually active.
20
20
  - Do not claim portability outside the Pi runtime.
21
21
 
@@ -45,7 +45,7 @@ el Gentleman is an ecosystem configurator and harness layer. After installation,
45
45
 
46
46
  - Small request: do it directly.
47
47
  - Substantial feature: suggest SDD organically.
48
- - User says "use sdd" / "hacelo con sdd": run the SDD flow.
48
+ - User explicitly asks to use SDD: run the SDD flow.
49
49
  - Parent session orchestrates; phase agents execute.
50
50
 
51
51
  Delegation is not optional once complexity appears. If a task crosses the triggers below, use the smallest useful subagent workflow instead of continuing as a monolithic executor.
@@ -101,7 +101,7 @@ Triggers:
101
101
  - cross-cutting behavior changes;
102
102
  - expected large diff or reviewer burden;
103
103
  - need for specs/design/tasks before safe implementation;
104
- - user explicitly says `use sdd`, `hacelo con sdd`, `/sdd-new`, `/sdd-ff`, or `/sdd-continue`.
104
+ - user explicitly asks to use SDD, or invokes `/sdd-new`, `/sdd-ff`, or `/sdd-continue`.
105
105
 
106
106
  If the request is large enough for SDD, do not jump directly to implementation. Calibrate context, create artifacts, and ask for approval at the appropriate gates.
107
107
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gentle-pi",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "Turn Pi into el Gentleman: a senior-architect development harness with SDD/OpenSpec, subagents, strict TDD evidence, review guardrails, and skill discovery.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -0,0 +1,108 @@
1
+ ---
2
+ name: release
3
+ description: "Release gentle-pi through GitHub and npm. Trigger: release, publish, npm publish, GitHub release, version bump."
4
+ license: Apache-2.0
5
+ metadata:
6
+ author: gentleman-programming
7
+ version: "1.0"
8
+ ---
9
+
10
+ ## When to Use
11
+
12
+ Use this skill when preparing, publishing, or verifying a `gentle-pi` release.
13
+
14
+ ## Hard Rules
15
+
16
+ - Do not publish `gentle-pi` to npm from a local machine.
17
+ - npm publishing MUST go through the GitHub Actions workflow `.github/workflows/publish.yml` so provenance, environment protection, and registry credentials are controlled by GitHub.
18
+ - Use a clean worktree for release commits. Do not package unrelated local files or scratch artifacts.
19
+ - Run a fresh review before pushing a code release unless the change is trivial docs-only.
20
+ - Never skip package verification. The publish workflow runs verification again, but local validation should still pass before tagging.
21
+
22
+ ## Release Procedure
23
+
24
+ 1. **Inspect state**
25
+
26
+ ```bash
27
+ git status --short
28
+ git fetch origin main --tags
29
+ git log --oneline --decorate --max-count=5 origin/main
30
+ ```
31
+
32
+ 2. **Prepare the release commit**
33
+
34
+ - Apply only intended changes.
35
+ - Bump `package.json` to the next semver version.
36
+ - Keep lockfile changes out unless dependency resolution actually changed.
37
+
38
+ 3. **Verify locally**
39
+
40
+ ```bash
41
+ pnpm test
42
+ pnpm publish --dry-run --no-git-checks
43
+ ```
44
+
45
+ The dry run is allowed because it does not publish. It verifies package contents and lifecycle scripts.
46
+
47
+ 4. **Commit and push**
48
+
49
+ ```bash
50
+ git add <intended-files>
51
+ git commit -m "<type(scope): release-ready change>"
52
+ git push origin HEAD:main
53
+ ```
54
+
55
+ 5. **Create the GitHub release**
56
+
57
+ ```bash
58
+ git tag -a v<version> -m "gentle-pi v<version>"
59
+ git push origin v<version>
60
+ gh release create v<version> \
61
+ --repo Gentleman-Programming/gentle-pi \
62
+ --title "gentle-pi v<version>" \
63
+ --notes "<release notes>"
64
+ ```
65
+
66
+ 6. **Publish npm through GitHub Actions**
67
+
68
+ ```bash
69
+ gh workflow run publish.yml \
70
+ --repo Gentleman-Programming/gentle-pi \
71
+ --ref main \
72
+ -f dist-tag=latest
73
+ ```
74
+
75
+ Watch the run and fail the release if it fails:
76
+
77
+ ```bash
78
+ gh run list --repo Gentleman-Programming/gentle-pi --workflow publish.yml --limit 3
79
+ gh run watch <run-id> --repo Gentleman-Programming/gentle-pi --exit-status
80
+ ```
81
+
82
+ 7. **Verify npm**
83
+
84
+ ```bash
85
+ npm view gentle-pi@<version> version --registry=https://registry.npmjs.org/
86
+ npm dist-tag ls gentle-pi --registry=https://registry.npmjs.org/
87
+ ```
88
+
89
+ ## Failure Handling
90
+
91
+ - If a local `npm publish` fails, do not retry locally. Use the GitHub workflow instead.
92
+ - If the workflow fails, inspect logs with:
93
+
94
+ ```bash
95
+ gh run view <run-id> --repo Gentleman-Programming/gentle-pi --log
96
+ ```
97
+
98
+ - If npm verification is briefly stale after a successful workflow, check the exact version first (`npm view gentle-pi@<version> version`) before assuming publish failed.
99
+
100
+ ## Output Contract
101
+
102
+ Report:
103
+
104
+ - Commit SHA pushed to `main`.
105
+ - GitHub release URL.
106
+ - Publish workflow run URL and conclusion.
107
+ - npm exact version and `latest` dist-tag.
108
+ - Any remaining follow-up or warnings.
@@ -0,0 +1,91 @@
1
+ import assert from "node:assert/strict";
2
+ import { readdir, readFile } from "node:fs/promises";
3
+ import { dirname, extname, join, relative } from "node:path";
4
+ import test from "node:test";
5
+ import { fileURLToPath } from "node:url";
6
+ import {
7
+ renderSddPreflightPrompt,
8
+ type SddPreflightPreferences,
9
+ } from "../lib/sdd-preflight.ts";
10
+
11
+ const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
12
+ const TEXT_EXTENSIONS = new Set([".md", ".ts", ".mjs", ".json"]);
13
+
14
+ async function collectTextFiles(dir: string): Promise<string[]> {
15
+ const entries = await readdir(dir, { withFileTypes: true });
16
+ const files: string[] = [];
17
+ for (const entry of entries) {
18
+ const path = join(dir, entry.name);
19
+ if (entry.isDirectory()) {
20
+ files.push(...(await collectTextFiles(path)));
21
+ continue;
22
+ }
23
+ if (entry.isFile() && TEXT_EXTENSIONS.has(extname(entry.name))) {
24
+ files.push(path);
25
+ }
26
+ }
27
+ return files;
28
+ }
29
+
30
+ const SPANISH_PREFLIGHT_COPY = [
31
+ /Antes de continuar con SDD/i,
32
+ /Antes de seguir con SDD/i,
33
+ /una opci[oó]n por grupo/i,
34
+ /usar recomendad[oa]/i,
35
+ /\bRitmo\b/i,
36
+ /\bArtefactos\b/i,
37
+ /\bPreguntarme\b/i,
38
+ /l[ií]neas cambiadas/i,
39
+ /\bhacelo\b/i,
40
+ /\bSoy el Gentleman\b/i,
41
+ ];
42
+
43
+ test("orchestrator keeps conversation language separate from generated artifact language", async () => {
44
+ const orchestrator = await readFile(join(ROOT, "assets/orchestrator.md"), "utf8");
45
+
46
+ assert.match(
47
+ orchestrator,
48
+ /User-facing conversation should stay in the user's language/,
49
+ );
50
+ assert.match(
51
+ orchestrator,
52
+ /Generated artifacts[\s\S]*default to English, regardless of the user's conversation language/,
53
+ );
54
+ });
55
+
56
+ test("rendered SDD preflight prompt is English artifact copy", () => {
57
+ const prefs: SddPreflightPreferences = {
58
+ executionMode: "interactive",
59
+ artifactStore: "openspec",
60
+ chainedPrStrategy: "ask-always",
61
+ reviewBudgetLines: 400,
62
+ engramAvailable: false,
63
+ prompted: true,
64
+ };
65
+ const prompt = renderSddPreflightPrompt(prefs);
66
+
67
+ assert.match(prompt, /The user already chose these SDD preferences/);
68
+ assert.match(prompt, /Review budget: 400 changed lines/);
69
+ for (const pattern of SPANISH_PREFLIGHT_COPY) {
70
+ assert.doesNotMatch(prompt, pattern);
71
+ }
72
+ });
73
+
74
+ test("persistent harness prompt assets do not hardcode Spanish SDD artifact copy", async () => {
75
+ const files = [
76
+ ...(await collectTextFiles(join(ROOT, "assets"))),
77
+ ...(await collectTextFiles(join(ROOT, "prompts"))),
78
+ ];
79
+ const failures: string[] = [];
80
+
81
+ for (const file of files) {
82
+ const text = await readFile(file, "utf8");
83
+ for (const pattern of SPANISH_PREFLIGHT_COPY) {
84
+ if (pattern.test(text)) {
85
+ failures.push(`${relative(ROOT, file)} matched ${pattern}`);
86
+ }
87
+ }
88
+ }
89
+
90
+ assert.deepEqual(failures, []);
91
+ });
@@ -179,6 +179,18 @@ async function run() {
179
179
  assert.match(promptResult.systemPrompt, /el Gentleman/);
180
180
  assert.match(promptResult.systemPrompt, /openspec\/config\.yaml.*not session preflight/s);
181
181
  assert.match(promptResult.systemPrompt, /Do not mark SDD preflight complete/);
182
+ await mkdir(join(promptCwd, ".pi", "gentle-ai"), { recursive: true });
183
+ await writeFile(
184
+ join(promptCwd, ".pi", "gentle-ai", "persona.json"),
185
+ '{"mode":"neutral"}\n',
186
+ );
187
+ const neutralPromptResult = await promptHook({ systemPrompt: "base" }, createCtx(promptCwd));
188
+ assert.match(neutralPromptResult.systemPrompt, /Do not use slang or regional expressions/);
189
+ assert.doesNotMatch(
190
+ neutralPromptResult.systemPrompt,
191
+ /When the user writes Spanish, answer in natural Rioplatense Spanish with voseo/,
192
+ "neutral persona prompt must not include unconditional voseo instructions after reload",
193
+ );
182
194
  const subagentPromptResult = await promptHook(
183
195
  { agentName: "worker", systemPrompt: "worker base" },
184
196
  createCtx(promptCwd),