@profile-as-code/cli 0.3.6 → 0.3.7
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 +410 -14
- package/dist/index.js +8212 -3772
- package/package.json +3 -2
- package/skills/README.md +39 -5
- package/skills/devto/SKILL.md +82 -0
- package/skills/devto/longform.yaml +33 -0
- package/skills/devto/post.md +96 -0
- package/skills/ghost/SKILL.md +85 -0
- package/skills/ghost/longform.yaml +36 -0
- package/skills/ghost/post.md +98 -0
- package/skills/github-org/SKILL.md +1 -2
- package/skills/github-org/checks.yaml +1 -1
- package/skills/github-org/playbook.md +2 -3
- package/skills/github-readme/checks.yaml +1 -1
- package/skills/github-repo-meta/SKILL.md +82 -0
- package/skills/github-repo-meta/checks.yaml +50 -0
- package/skills/github-repo-meta/playbook.md +45 -0
- package/skills/hashnode/SKILL.md +79 -0
- package/skills/hashnode/longform.yaml +33 -0
- package/skills/hashnode/post.md +90 -0
- package/skills/hotmart/checks.yaml +1 -1
- package/skills/kofi/checks.yaml +1 -1
- package/skills/listmonk/SKILL.md +83 -0
- package/skills/listmonk/longform.yaml +33 -0
- package/skills/listmonk/post.md +93 -0
- package/skills/medium/SKILL.md +86 -0
- package/skills/medium/longform.yaml +33 -0
- package/skills/medium/post.md +96 -0
- package/skills/substack/SKILL.md +83 -0
- package/skills/substack/longform.yaml +36 -0
- package/skills/substack/post.md +91 -0
- package/skills/wordpress/SKILL.md +84 -0
- package/skills/wordpress/longform.yaml +33 -0
- package/skills/wordpress/post.md +99 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@profile-as-code/cli",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "pasco
|
|
3
|
+
"version": "0.3.7",
|
|
4
|
+
"description": "pasco \u2014 the profile-as-code CLI: init, validate, build, diff, edit (read / preview / write one contract field, comment-preserving), status + partial refine (task template), check (offline rules M6\u2013M8; catalog ships M3/M4), audit (offline findings projection), entity (the schema.org projection + paste-ready JSON-LD for surfaces the engine does not render), cite-check (deterministic citation matching over saved answer-engine answers, no network), export ai-policy (robots.txt + llms.txt + AI-POLICY.md: retrieval allowed, AI training blocked by default), export profile (the normalized, versioned profile object for programmatic consumers), preview (one self-contained HTML page of the rendered surfaces, written locally and never published), skills:export (copies the platform skills this package ships). Consumes @profile-as-code/core; never re-implements load/drift/geo/compile.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"funding": [
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"node": ">=22"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
+
"marked": "^18.1.1",
|
|
60
61
|
"yaml": "^2.9.1",
|
|
61
62
|
"zod": "^4.6.7"
|
|
62
63
|
},
|
package/skills/README.md
CHANGED
|
@@ -53,10 +53,44 @@ skills/<plataforma>/
|
|
|
53
53
|
marca (≠ voz personal), handle-matrix checks (`handle-claimed`/`handle-consistent`,
|
|
54
54
|
offline en `pasco check`), trademark/impersonación. Ver `skills/github-org/`.
|
|
55
55
|
|
|
56
|
-
## Post skills (MVP, #264)
|
|
56
|
+
## Post skills (MVP, #264/#266/#361)
|
|
57
57
|
|
|
58
58
|
The week-1 post experiment uses a **different shape** from the profile skills:
|
|
59
|
-
`skills/<platform>/post.md` (judgment) +
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
`skills/<platform>/post.md` (judgment) + a machine-readable rule file.
|
|
60
|
+
`pasco post` reads them; `pasco refine` keeps reading `SKILL.md`.
|
|
61
|
+
|
|
62
|
+
- **Short** (`posting.yaml`): `linkedin`, `x` — char limits, hashtags.
|
|
63
|
+
- **Long-form** (`longform.yaml`, #266): `substack`, `ghost`, `medium`, `devto`,
|
|
64
|
+
`hashnode`, `wordpress`, `listmonk` — required fields (`title`/`dek`), an
|
|
65
|
+
editorial word band, and `export: api | paste` (Substack/Ghost are paste
|
|
66
|
+
surfaces). The word band is house style, not a platform limit: cite a source
|
|
67
|
+
before encoding a hard cap.
|
|
68
|
+
|
|
69
|
+
Every long-form channel also carries its judgment layer (#361): the `post.md` the
|
|
70
|
+
short channels already have, plus a `SKILL.md` that they do not ship.
|
|
71
|
+
|
|
72
|
+
- `post.md` — what a piece on that channel is for, its shape, its voice, and its
|
|
73
|
+
GEO and i18n policy, plus what `export: api | paste` means for the writing.
|
|
74
|
+
- `SKILL.md` — the channel skill document: mission, when the skill runs, the
|
|
75
|
+
flow, the pointers to the contract, and the suggested model in its frontmatter.
|
|
76
|
+
It is a strategy reference, not a bundle input: `pasco post task` carries the
|
|
77
|
+
contract and `post.md`, never this file.
|
|
78
|
+
|
|
79
|
+
**Do not use `pasco refine <channel>` on a post channel.** Refine is the
|
|
80
|
+
profile-refinement command; with a `SKILL.md` present it now resolves the file (it
|
|
81
|
+
used to answer "no skill yet") and emits a **profile** task that saves to
|
|
82
|
+
`profiles/<channel>/variants/<id>.md` — not a post workflow. The path for these
|
|
83
|
+
channels is `pasco post` with `longform.yaml`. Teaching the engine to tell a
|
|
84
|
+
profile skill from a post skill is a separate engine matter, not something a
|
|
85
|
+
skill can fix. (`checks.yaml` is not the cause: `pasco check` reads that catalog
|
|
86
|
+
and reports "no catalog" without failing when it is missing.)
|
|
87
|
+
|
|
88
|
+
`longform.yaml` stays the contract: required/optional fields, the word band and
|
|
89
|
+
`export` are declared there, and the prose explains them instead of restating
|
|
90
|
+
their numbers. A channel's *dated* platform sources live in the contract's
|
|
91
|
+
`sources[]`; a claim the contract does not already carry needs its own source
|
|
92
|
+
(URL + date + tier) before it enters either file. The post shape is Markdown
|
|
93
|
+
beside the rule file — not the `research/` + `playbook.md` + `checks.yaml` trio,
|
|
94
|
+
which belongs to the profile skills above.
|
|
95
|
+
|
|
96
|
+
Neither shape is projected to harnesses yet — see `docs/post-mvp.md`.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devto-post
|
|
3
|
+
description: >
|
|
4
|
+
Long-form post skill for DEV (Forem). Says what a DEV article is for and how
|
|
5
|
+
the agent writes one; the machine contract is skills/devto/longform.yaml
|
|
6
|
+
(required/optional fields, editorial word band, export: api). The judgment
|
|
7
|
+
layer — shape, voice, GEO, i18n — lives in post.md. The agent writes N
|
|
8
|
+
variants, the operator picks one, and the choice is recorded with
|
|
9
|
+
`pasco post choose`.
|
|
10
|
+
model: juicio (frontier) — choosing the technical claim and its trade-off is
|
|
11
|
+
judgment; the mechanical parts (markdown, front matter) are not
|
|
12
|
+
applies_to: devto
|
|
13
|
+
projection: agent-agnostic (any harness that reads Markdown and can run the CLI)
|
|
14
|
+
version: 0.1.0
|
|
15
|
+
status: draft (#361)
|
|
16
|
+
sources: skills/devto/longform.yaml
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# DEV (Forem) long-form — skill
|
|
20
|
+
|
|
21
|
+
## Mission
|
|
22
|
+
|
|
23
|
+
Turn one canonical seed into a DEV article that a developer can act on: one
|
|
24
|
+
claim, the smallest example that proves it, and the caveat that keeps it honest.
|
|
25
|
+
The skill is the judgment layer (D5); the deterministic part is the contract plus
|
|
26
|
+
`pasco post check`.
|
|
27
|
+
|
|
28
|
+
## When it runs
|
|
29
|
+
|
|
30
|
+
`pasco post task <seed> --format longform --channel devto` emits a task bundle
|
|
31
|
+
that points at the contract (`longform.yaml`) and the judgment file (`post.md`);
|
|
32
|
+
it does **not** carry this `SKILL.md`. This file is the channel's strategy
|
|
33
|
+
reference for whoever opens it directly: read the contract for the fields and the
|
|
34
|
+
band, then `post.md` for how to write.
|
|
35
|
+
|
|
36
|
+
Do **not** run `pasco refine devto` for this channel. Refine is the profile
|
|
37
|
+
command: with a `SKILL.md` now present it resolves this file instead of answering
|
|
38
|
+
"no skill for devto yet", and emits a **profile** task ("write N variants of the
|
|
39
|
+
devto profile") that saves to `profiles/devto/variants/<id>.md` — not a post
|
|
40
|
+
workflow. Telling a profile skill from a post skill is an engine decision, not
|
|
41
|
+
something this skill can fix.
|
|
42
|
+
|
|
43
|
+
## Flow
|
|
44
|
+
|
|
45
|
+
1. Read `longform.yaml` + `post.md`. Do not re-derive the limits: the contract
|
|
46
|
+
owns them.
|
|
47
|
+
2. Write N variants as YAML front matter + markdown body, at
|
|
48
|
+
`posts/<seed>/devto/devto-<n>.md`.
|
|
49
|
+
3. The operator picks one; record it with `pasco post choose`.
|
|
50
|
+
4. `pasco post check` — fix what it reports instead of explaining it away.
|
|
51
|
+
5. `pasco post export <seed> --target postiz` for the API draft payload.
|
|
52
|
+
6. Publish manually, then verify on the live page that the front matter landed.
|
|
53
|
+
|
|
54
|
+
## Contract pointers (single source each)
|
|
55
|
+
|
|
56
|
+
| Question | Home |
|
|
57
|
+
|---|---|
|
|
58
|
+
| required / optional fields | `longform.yaml` `fields` |
|
|
59
|
+
| editorial word band | `longform.yaml` `limits.words` (not a platform cap) |
|
|
60
|
+
| api vs paste | `longform.yaml` `export` |
|
|
61
|
+
| link policy | `longform.yaml` `links.policy` (`identity-ssot-only`) |
|
|
62
|
+
| dated platform sources | `longform.yaml` `sources[]` |
|
|
63
|
+
| how to write the piece | `post.md` |
|
|
64
|
+
|
|
65
|
+
## GEO and i18n
|
|
66
|
+
|
|
67
|
+
Public, indexed article page: the citation unit is the code block plus the
|
|
68
|
+
sentence that states what it proves. Write `en` by default; a Spanish piece is a
|
|
69
|
+
separate seed for that audience, never a duplicate of the English one. The
|
|
70
|
+
crawler stance is the repo's global `ai.policy` (`platforms/registry.yaml`, v1,
|
|
71
|
+
2026-10-27) — no per-channel rule is invented here.
|
|
72
|
+
|
|
73
|
+
## Verification
|
|
74
|
+
|
|
75
|
+
- `pasco post check` exit 0 (no `missing-field`, `word-count` or
|
|
76
|
+
`url-not-in-ssot` errors).
|
|
77
|
+
- Facts byte-identical to the SSOT; every URL from the identity SSOT.
|
|
78
|
+
- On the live page: title, tags and canonical URL arrived; the dek shows up as
|
|
79
|
+
the subtitle the channel renders, or not at all — never as a repeat of the
|
|
80
|
+
body's first line.
|
|
81
|
+
- Report any contract gap as a finding — the engine is not extended from a
|
|
82
|
+
skill (#361).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# devto long-form rules — machine-readable (#266)
|
|
2
|
+
#
|
|
3
|
+
# Read by `pasco post task|check|export` when the seed is `format: longform`.
|
|
4
|
+
# The word range is an EDITORIAL default (the operator's house style), NOT a
|
|
5
|
+
# platform limit: cite a platform source before encoding a hard cap here.
|
|
6
|
+
|
|
7
|
+
platform: devto
|
|
8
|
+
format: longform
|
|
9
|
+
export: api
|
|
10
|
+
settings:
|
|
11
|
+
__type: devto
|
|
12
|
+
fields:
|
|
13
|
+
required:
|
|
14
|
+
- title
|
|
15
|
+
- dek
|
|
16
|
+
optional:
|
|
17
|
+
- tags
|
|
18
|
+
- canonical
|
|
19
|
+
- publication
|
|
20
|
+
limits:
|
|
21
|
+
words:
|
|
22
|
+
min: 600
|
|
23
|
+
max: 3000
|
|
24
|
+
links:
|
|
25
|
+
policy: identity-ssot-only
|
|
26
|
+
structure:
|
|
27
|
+
- title
|
|
28
|
+
- dek
|
|
29
|
+
- sections
|
|
30
|
+
sources:
|
|
31
|
+
- url: https://developers.forem.com/api
|
|
32
|
+
date: 2026-10-29
|
|
33
|
+
note: "The Forem/DEV articles API takes title, tags and canonical_url; the body is markdown."
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# DEV (Forem) long-form post — skill (MVP)
|
|
2
|
+
|
|
3
|
+
> Long-form post skill. The contract is `longform.yaml`: required/optional
|
|
4
|
+
> fields, the editorial word band and `export` live there and are **not**
|
|
5
|
+
> restated here — this file is the judgment layer the agent reads before
|
|
6
|
+
> writing. The contract's `sources[]` carries the dated URL for every platform
|
|
7
|
+
> claim; re-verify before making a hard one, and cite a source (date + tier)
|
|
8
|
+
> before calling anything a limit.
|
|
9
|
+
|
|
10
|
+
## What a DEV piece is for
|
|
11
|
+
|
|
12
|
+
A technical piece for developers who will judge it by whether the code and the
|
|
13
|
+
claim hold up. The reader arrives from a tag feed, from search, or from a
|
|
14
|
+
cross-post on another channel — so the piece must stand alone: one technique,
|
|
15
|
+
one result, or one decision with its trade-off. It is not a career update and
|
|
16
|
+
not a newsletter issue.
|
|
17
|
+
|
|
18
|
+
## Shape
|
|
19
|
+
|
|
20
|
+
1. **Title** — the outcome the reader gets, in concrete verbs. No numbers you
|
|
21
|
+
cannot source, no curiosity gap the body does not pay off.
|
|
22
|
+
2. **Dek** — one line of scope: what the piece assumes and what it delivers.
|
|
23
|
+
The contract requires it as a field, and the engine's task bundle is explicit
|
|
24
|
+
that title and dek are fields, never the first lines of the body — so the dek
|
|
25
|
+
is delivered in the front matter, not repeated as the body's opening line. No
|
|
26
|
+
platform mapping is declared; confirm on the first publish whether DEV renders
|
|
27
|
+
it as a subtitle [?].
|
|
28
|
+
3. **Sections** — one H2 per step or idea. Show the smallest example that
|
|
29
|
+
proves the point, then the caveat. The contract fixes the three-part
|
|
30
|
+
structure; how many sections that takes is editorial.
|
|
31
|
+
|
|
32
|
+
## Rules
|
|
33
|
+
|
|
34
|
+
- **Front matter is the field contract.** Supply the fields `longform.yaml`
|
|
35
|
+
declares. A field the channel does not declare is not exported (`pasco post
|
|
36
|
+
check` warns `unknown-field`) — never smuggle it into the body instead.
|
|
37
|
+
- **Length:** the band in `limits.words` is the operator's editorial default,
|
|
38
|
+
not a DEV limit (the contract says so in its own header). Treat it as the
|
|
39
|
+
budget for a piece a developer finishes in one sitting, not as a ceiling to
|
|
40
|
+
fill.
|
|
41
|
+
- **Links:** `links.policy` is `identity-ssot-only` — every URL comes from the
|
|
42
|
+
identity SSOT. DEV syndicates, so `canonical` must point at the canonical
|
|
43
|
+
home from the SSOT, never at a second syndication copy.
|
|
44
|
+
- **Facts:** every number, date, version and URL is cited with
|
|
45
|
+
`{{fact:<pointer>}}`; the engine renders it and fails on a dangling pointer.
|
|
46
|
+
- **Voice:** first person, technical, no hype. Match `brandVoice`; the piece
|
|
47
|
+
should sound like an engineer explaining a decision, not a growth account.
|
|
48
|
+
|
|
49
|
+
## Export: `api`
|
|
50
|
+
|
|
51
|
+
The contract declares `export: api`: the body is markdown that the Forem/DEV
|
|
52
|
+
articles API receives, with the title, tags and canonical URL carried as payload
|
|
53
|
+
fields [OFICIAL — the contract's source, `sources[]` in `longform.yaml`, dated
|
|
54
|
+
2026-10-29]. Consequences for the writing:
|
|
55
|
+
|
|
56
|
+
- Markdown only. No raw HTML, no embeds that only an editor understands.
|
|
57
|
+
- `canonical` is a payload field, not a body line — do not paste the URL into
|
|
58
|
+
the text as a substitute.
|
|
59
|
+
- The export produces a Postiz draft payload; publishing stays manual (no
|
|
60
|
+
scheduler, no OAuth — `docs/post-mvp.md`).
|
|
61
|
+
|
|
62
|
+
## GEO (how this channel is read and cited)
|
|
63
|
+
|
|
64
|
+
DEV article pages are public and indexed, and the quotable unit is the snippet
|
|
65
|
+
plus the sentence that says what it proves. Keep the version and the date inside
|
|
66
|
+
the code block or the sentence around it; a table of unsourced benchmarks is not
|
|
67
|
+
citable. The crawler stance is the repo's global `ai.policy`
|
|
68
|
+
(`platforms/registry.yaml`, v1, 2026-10-27) — do not invent a per-channel rule.
|
|
69
|
+
Consistency check: every version, date and number matches the SSOT byte for
|
|
70
|
+
byte, exactly as on the profile surfaces.
|
|
71
|
+
|
|
72
|
+
## i18n
|
|
73
|
+
|
|
74
|
+
DEV's audience and tag vocabulary are English-first: write `en` by default. A
|
|
75
|
+
Spanish piece is its own seed for the hispano dev community, not a translation
|
|
76
|
+
of the English one. Do not publish the same text twice in two languages: two
|
|
77
|
+
canonical URLs of one text split the citation.
|
|
78
|
+
|
|
79
|
+
## Anti-patterns
|
|
80
|
+
|
|
81
|
+
- "X is dead" / "N tools you must know" listicles with no artifact behind them.
|
|
82
|
+
- An intro that restates the title for three paragraphs.
|
|
83
|
+
- A code block that cannot run, or a version pin that is never stated.
|
|
84
|
+
- Cross-posting without `canonical` — two copies then compete for one query.
|
|
85
|
+
- A benchmark number with no method, date or source.
|
|
86
|
+
|
|
87
|
+
## Output contract
|
|
88
|
+
|
|
89
|
+
Save each variant as YAML front matter + a markdown body at
|
|
90
|
+
`posts/<seed>/devto/devto-<n>.md`, then record the choice and check it:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
pasco post choose <seed> --channel devto --variant devto-1
|
|
94
|
+
pasco post check <seed> # missing-field · word-count · url-not-in-ssot · unknown-field
|
|
95
|
+
pasco post export <seed> --target postiz
|
|
96
|
+
```
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ghost-post
|
|
3
|
+
description: >
|
|
4
|
+
Long-form post skill for Ghost. Says what a Ghost essay/newsletter issue is for
|
|
5
|
+
and how the agent writes it; the machine contract is
|
|
6
|
+
skills/ghost/longform.yaml (required/optional fields, editorial word band,
|
|
7
|
+
export: paste). The judgment layer — shape, voice, GEO, i18n — lives in
|
|
8
|
+
post.md. The agent writes N variants, the operator picks one, and the choice is
|
|
9
|
+
recorded with `pasco post choose`; the deploy is a manual paste driven by the
|
|
10
|
+
generated checklist.
|
|
11
|
+
model: juicio (frontier) — the argument and the opening are judgment; the
|
|
12
|
+
mechanical parts (markdown, front matter, paste checklist) are not
|
|
13
|
+
applies_to: ghost
|
|
14
|
+
projection: agent-agnostic (any harness that reads Markdown and can run the CLI)
|
|
15
|
+
version: 0.1.0
|
|
16
|
+
status: draft (#361)
|
|
17
|
+
sources: skills/ghost/longform.yaml
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Ghost long-form — skill
|
|
21
|
+
|
|
22
|
+
## Mission
|
|
23
|
+
|
|
24
|
+
Turn one canonical seed into an essay worth a subscription: one argument, the
|
|
25
|
+
evidence that carries it, and an opening that earns the first scroll in an
|
|
26
|
+
inbox. Ghost is the surface the operator owns end to end (publication, domain,
|
|
27
|
+
archive), so the piece is written to be the citable copy — while every URL it
|
|
28
|
+
claims still comes from the identity SSOT.
|
|
29
|
+
|
|
30
|
+
## When it runs
|
|
31
|
+
|
|
32
|
+
`pasco post task <seed> --format longform --channel ghost` emits a task bundle
|
|
33
|
+
that points at the contract (`longform.yaml`) and the judgment file (`post.md`);
|
|
34
|
+
it does **not** carry this `SKILL.md`. This file is the channel's strategy
|
|
35
|
+
reference for whoever opens it directly: read the contract for the fields and the
|
|
36
|
+
band, then `post.md` for how to write.
|
|
37
|
+
|
|
38
|
+
Do **not** run `pasco refine ghost` for this channel. Refine is the profile
|
|
39
|
+
command: with a `SKILL.md` now present it resolves this file instead of answering
|
|
40
|
+
"no skill for ghost yet", and emits a **profile** task ("write N variants of the
|
|
41
|
+
ghost profile") that saves to `profiles/ghost/variants/<id>.md` — not a post
|
|
42
|
+
workflow. Telling a profile skill from a post skill is an engine decision, not
|
|
43
|
+
something this skill can fix.
|
|
44
|
+
|
|
45
|
+
## Flow
|
|
46
|
+
|
|
47
|
+
1. Read `longform.yaml` + `post.md`; the contract owns every limit.
|
|
48
|
+
2. Write N variants as YAML front matter + markdown body, at
|
|
49
|
+
`posts/<seed>/ghost/ghost-<n>.md`.
|
|
50
|
+
3. The operator picks one; record it with `pasco post choose`.
|
|
51
|
+
4. `pasco post check` — fix the findings.
|
|
52
|
+
5. `pasco post export <seed>` → `paste-checklist.md` (no body API in v1).
|
|
53
|
+
6. Paste the body and each field into the publication editor, tick the checklist,
|
|
54
|
+
and verify the live page before calling it published.
|
|
55
|
+
|
|
56
|
+
## Contract pointers (single source each)
|
|
57
|
+
|
|
58
|
+
| Question | Home |
|
|
59
|
+
|---|---|
|
|
60
|
+
| required / optional fields | `longform.yaml` `fields` |
|
|
61
|
+
| editorial word band | `longform.yaml` `limits.words` (not a platform cap) |
|
|
62
|
+
| api vs paste | `longform.yaml` `export: paste` (decision #266) |
|
|
63
|
+
| link policy | `longform.yaml` `links.policy` (`identity-ssot-only`) |
|
|
64
|
+
| dated platform sources | `longform.yaml` `sources[]` |
|
|
65
|
+
| how to write the piece | `post.md` |
|
|
66
|
+
|
|
67
|
+
## GEO and i18n
|
|
68
|
+
|
|
69
|
+
The publication's domain is the citable address; its archive is public, the email
|
|
70
|
+
copy is not. State the claim next to a heading so the section is extractable, and
|
|
71
|
+
keep every fact byte-identical to the profile. One publication, one language:
|
|
72
|
+
write `en` by default and treat a Spanish publication as a separate surface with
|
|
73
|
+
its own domain. The crawler stance is the repo's global `ai.policy`
|
|
74
|
+
(`platforms/registry.yaml`, v1, 2026-10-27).
|
|
75
|
+
|
|
76
|
+
## Verification
|
|
77
|
+
|
|
78
|
+
- `pasco post check` exit 0 (no `missing-field`, `word-count` or
|
|
79
|
+
`url-not-in-ssot` errors).
|
|
80
|
+
- The pasted body kept its heading levels, code fences and links; nothing was
|
|
81
|
+
flattened by the editor.
|
|
82
|
+
- Front matter fields arrived on the live page (title + dek where the theme
|
|
83
|
+
renders it); canonical, if set, matches the SSOT.
|
|
84
|
+
- Report any contract gap as a finding — the engine is not extended from a
|
|
85
|
+
skill (#361).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# ghost long-form rules — machine-readable (#266)
|
|
2
|
+
#
|
|
3
|
+
# Read by `pasco post task|check|export` when the seed is `format: longform`.
|
|
4
|
+
# The word range is an EDITORIAL default (the operator's house style), NOT a
|
|
5
|
+
# platform limit: cite a platform source before encoding a hard cap here.
|
|
6
|
+
# `export: paste` in v1 even though Ghost has an Admin API: the issue defers the
|
|
7
|
+
# API channel and keeps Ghost a paste surface like Substack (#266 decision).
|
|
8
|
+
|
|
9
|
+
platform: ghost
|
|
10
|
+
format: longform
|
|
11
|
+
export: paste
|
|
12
|
+
settings:
|
|
13
|
+
__type: ghost
|
|
14
|
+
fields:
|
|
15
|
+
required:
|
|
16
|
+
- title
|
|
17
|
+
- dek
|
|
18
|
+
optional:
|
|
19
|
+
- preview
|
|
20
|
+
- tags
|
|
21
|
+
- canonical
|
|
22
|
+
- list
|
|
23
|
+
limits:
|
|
24
|
+
words:
|
|
25
|
+
min: 600
|
|
26
|
+
max: 3000
|
|
27
|
+
links:
|
|
28
|
+
policy: identity-ssot-only
|
|
29
|
+
structure:
|
|
30
|
+
- title
|
|
31
|
+
- dek
|
|
32
|
+
- sections
|
|
33
|
+
sources:
|
|
34
|
+
- url: https://ghost.org/docs/admin-api/
|
|
35
|
+
date: 2026-10-29
|
|
36
|
+
note: "Ghost ships an Admin API; v1 keeps Ghost a paste surface (paste checklist) per #266, not an API channel."
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Ghost long-form post — skill (MVP)
|
|
2
|
+
|
|
3
|
+
> Long-form post skill. The contract is `longform.yaml`: required/optional
|
|
4
|
+
> fields, the editorial word band and `export` live there and are **not**
|
|
5
|
+
> restated here — this file is the judgment layer the agent reads before
|
|
6
|
+
> writing. The contract's `sources[]` carries the dated URL for every platform
|
|
7
|
+
> claim; re-verify before making a hard one, and cite a source (date + tier)
|
|
8
|
+
> before calling anything a limit.
|
|
9
|
+
|
|
10
|
+
## What a Ghost piece is for
|
|
11
|
+
|
|
12
|
+
An essay or newsletter issue that lives on a publication the operator controls,
|
|
13
|
+
with its own domain and archive. That makes it the surface where the canonical
|
|
14
|
+
URL of a piece can exist: other channels point their `canonical` here *when the
|
|
15
|
+
SSOT already carries that URL* — never a URL the agent typed. The reader is
|
|
16
|
+
either a subscriber reading an email or someone who arrived from search; write
|
|
17
|
+
for both without writing twice.
|
|
18
|
+
|
|
19
|
+
## Shape
|
|
20
|
+
|
|
21
|
+
1. **Title** — the one claim the piece makes, stated so it still reads well in
|
|
22
|
+
an inbox list and in a search result.
|
|
23
|
+
2. **Dek** — the line that earns the next scroll: context, stake, or the
|
|
24
|
+
question the piece answers. The contract requires it; how the theme renders
|
|
25
|
+
it (subtitle, excerpt, nothing) varies per publication theme [?] — verify on
|
|
26
|
+
the first publish and record what you saw.
|
|
27
|
+
3. **Sections** — an essay argues, so give each section one move: the situation,
|
|
28
|
+
the tension, the evidence, the conclusion. H2 headings are the anchors a
|
|
29
|
+
reader (and an extractor) navigates by.
|
|
30
|
+
|
|
31
|
+
## Rules
|
|
32
|
+
|
|
33
|
+
- **Front matter is the field contract.** The fields `longform.yaml` declares are
|
|
34
|
+
the ones the channel consumes; anything else is dropped (`unknown-field`
|
|
35
|
+
warning) — do not re-add it in the body.
|
|
36
|
+
- **Length:** the band in `limits.words` is the operator's editorial default
|
|
37
|
+
(the contract says so). Aim for the piece the argument needs; the band is a
|
|
38
|
+
guardrail, not a quota.
|
|
39
|
+
- **Links:** `links.policy` is `identity-ssot-only`. A Ghost publication is a
|
|
40
|
+
URL surface like any other: every URL in the piece comes from the identity
|
|
41
|
+
SSOT, and `canonical` is only set when the SSOT already carries the target.
|
|
42
|
+
- **Facts:** `{{fact:<pointer>}}` for every number, date and URL; a dangling
|
|
43
|
+
pointer fails the check.
|
|
44
|
+
- **Voice:** first person, essayistic, no hype. Match `brandVoice`; a newsletter
|
|
45
|
+
issue is the one surface where a longer voice can breathe — do not turn that
|
|
46
|
+
into padding.
|
|
47
|
+
|
|
48
|
+
## Export: `paste`
|
|
49
|
+
|
|
50
|
+
The contract declares `export: paste` even though Ghost ships an Admin API: v1
|
|
51
|
+
keeps Ghost a paste surface like Substack, by decision (#266), and the contract's
|
|
52
|
+
`sources[]` records the Admin API docs [OFICIAL, dated 2026-10-29]. Consequences
|
|
53
|
+
for the writing and the deploy:
|
|
54
|
+
|
|
55
|
+
- The body is **pasted into the publication editor**; formatting must survive a
|
|
56
|
+
paste. Keep markdown simple: headings, paragraphs, lists, code fences, block
|
|
57
|
+
quotes. No raw HTML, no footnotes the editor may flatten, no tables unless you
|
|
58
|
+
re-check them after pasting.
|
|
59
|
+
- `pasco post export` writes a paste checklist, not a publish: the deploy is
|
|
60
|
+
manual and the checklist is the record of what was pasted.
|
|
61
|
+
- Fields are pasted one by one — treat the checklist as the field contract's
|
|
62
|
+
human-readable form and tick each line.
|
|
63
|
+
|
|
64
|
+
## GEO (how this channel is read and cited)
|
|
65
|
+
|
|
66
|
+
The publication's own domain is the citable address: its archive pages are
|
|
67
|
+
public and indexable, while the email copy is not. That is why the canonical URL
|
|
68
|
+
matters here more than on a network — the citation unit is a section heading
|
|
69
|
+
plus the claim under it, and it resolves to a URL the operator owns. The crawler
|
|
70
|
+
stance is the repo's global `ai.policy` (`platforms/registry.yaml`, v1,
|
|
71
|
+
2026-10-27); no per-channel rule is invented here. Consistency check: the piece's
|
|
72
|
+
facts are byte-identical to the profile and to every other channel's projection.
|
|
73
|
+
|
|
74
|
+
## i18n
|
|
75
|
+
|
|
76
|
+
A Ghost publication is per language: one publication, one audience language.
|
|
77
|
+
Write `en` by default; a Spanish publication is a separate publication with its
|
|
78
|
+
own domain and archive, and the piece belongs to exactly one of them. Do not
|
|
79
|
+
publish a machine translation of the same text under the same canonical.
|
|
80
|
+
|
|
81
|
+
## Anti-patterns
|
|
82
|
+
|
|
83
|
+
- An essay whose first two paragraphs are a table of contents.
|
|
84
|
+
- A `canonical` invented for the occasion — it must come from the SSOT.
|
|
85
|
+
- Rich HTML that the editor flattens on paste, discovered after publishing.
|
|
86
|
+
- Email-only framing in a piece that is also the public archive page.
|
|
87
|
+
- A newsletter issue that is a link dump with no argument.
|
|
88
|
+
|
|
89
|
+
## Output contract
|
|
90
|
+
|
|
91
|
+
Save each variant as YAML front matter + a markdown body at
|
|
92
|
+
`posts/<seed>/ghost/ghost-<n>.md`, then record the choice and check it:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
pasco post choose <seed> --channel ghost --variant ghost-1
|
|
96
|
+
pasco post check <seed> # missing-field · word-count · url-not-in-ssot · unknown-field
|
|
97
|
+
pasco post export <seed> # paste: writes posts/<seed>/ghost/paste-checklist.md
|
|
98
|
+
```
|
|
@@ -77,8 +77,7 @@ se cita, se ignora.
|
|
|
77
77
|
2. `pasco check` — handle-claimed / handle-consistent OK (offline) + `url-live` de
|
|
78
78
|
cada handle claimed (network, verificación manual).
|
|
79
79
|
3. `pasco build github-org --lang en` → `profiles/github-org/README.md`.
|
|
80
|
-
4. Copiar el README al repo del org (deploySpec: git; el humano mergea
|
|
81
|
-
repo-governance).
|
|
80
|
+
4. Copiar el README al repo del org (deploySpec: git; el humano mergea).
|
|
82
81
|
5. Verificación post-deploy: cada badge/link del README resuelve; los handles
|
|
83
82
|
pending NO figuran como surfaces vivas.
|
|
84
83
|
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
platform: github-org
|
|
20
20
|
rule: fact-present
|
|
21
21
|
value: true
|
|
22
|
-
note: "
|
|
22
|
+
note: "El README que escribió `pasco build` se compara contra el render actual del SSOT en los dos sentidos — URL, fecha o número agregado u omitido, y texto sin token que lo explique (WARN, D12; `pasco audit` lo gradúa). `link-in-identity` es una premisa aparte."
|
|
23
23
|
|
|
24
24
|
- id: org-links-identity
|
|
25
25
|
platform: github-org
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
- Workspace brand (`pasco init --flavor brand`) con `content/` lleno y
|
|
6
6
|
`pasco validate` en verde.
|
|
7
|
-
- Org de GitHub creado (privado hasta pasar el checklist de public-readiness
|
|
8
|
-
repo-governance).
|
|
7
|
+
- Org de GitHub creado (privado hasta pasar el checklist de public-readiness).
|
|
9
8
|
|
|
10
9
|
## Pasos
|
|
11
10
|
|
|
@@ -17,7 +16,7 @@
|
|
|
17
16
|
3. **Build:** `pasco build github-org --lang en` →
|
|
18
17
|
`profiles/github-org/README.md`.
|
|
19
18
|
4. **Deploy:** copiá el README al repo `org/org` del GitHub org y commiteá en tu
|
|
20
|
-
branch + PR — el humano mergea
|
|
19
|
+
branch + PR — el humano mergea.
|
|
21
20
|
5. **Verificación post-deploy:** cada badge y link del README resuelve; los
|
|
22
21
|
handles `pending` NO figuran como surfaces vivas; el JSON-LD `Organization`
|
|
23
22
|
está embebido (GEO §17.1).
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
platform: github-readme
|
|
12
12
|
rule: fact-present
|
|
13
13
|
value: true
|
|
14
|
-
note: "
|
|
14
|
+
note: "El README que escribió `pasco build` se compara contra el render actual del SSOT en los dos sentidos: URL, fecha o número que la superficie agrega u omite, y texto que ningún token explica (WARN, D12; `pasco audit` lo gradúa). `link-in-identity` es una premisa aparte."
|
|
15
15
|
|
|
16
16
|
- id: gh-links-identity
|
|
17
17
|
platform: github-readme
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: github-repo-meta
|
|
3
|
+
description: >
|
|
4
|
+
Superficie de presencia `github-repo-meta` (D23, #262): la metadata del
|
|
5
|
+
artefacto de código — descripción del repo, topics, social preview, release
|
|
6
|
+
notes y la página npm derivada. Convierte un release en presencia visible
|
|
7
|
+
sobre el repo, sin inventar hechos: la descripción y las notas salen del
|
|
8
|
+
release plan (`pasco release plan`) y del SSOT, nunca de la memoria del agente.
|
|
9
|
+
model: mecánico (la copy es corta y acotada; el juicio de posicionamiento vive en
|
|
10
|
+
la skill de la README del perfil)
|
|
11
|
+
applies_to: github-repo-meta
|
|
12
|
+
projection: agent-agnostic (AionUi, OpenHands, Claude Code, Cursor, pi…)
|
|
13
|
+
version: 0.1.0
|
|
14
|
+
status: draft (D23, #262)
|
|
15
|
+
sources: "platforms/registry.yaml (kind: repo-meta) · docs/SPEC.md §22"
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# GitHub repo metadata — skill de superficie repo-meta (D23)
|
|
19
|
+
|
|
20
|
+
## Misión
|
|
21
|
+
|
|
22
|
+
Que un release exista como **presencia**: el repo se encuentra, se entiende y se
|
|
23
|
+
comparte. El motor no renderiza esta superficie (no hay renderer: es HTML/API de
|
|
24
|
+
GitHub); lo que el motor SÍ da es el **plan**: `pasco release plan <id>` nombra la
|
|
25
|
+
superficie, su estado derivado del ledger y la **espina factual** del release. Esta
|
|
26
|
+
skill es la capa de juicio que convierte esa espina en metadata publicable.
|
|
27
|
+
|
|
28
|
+
## Regla de oro
|
|
29
|
+
|
|
30
|
+
> La metadata del repo describe el **artefacto**, no a la persona. El perfil
|
|
31
|
+
> (github-readme) es la voz; `github-repo-meta` es la ficha: qué es esto, para
|
|
32
|
+
> quién, y qué cambió.
|
|
33
|
+
|
|
34
|
+
- La descripción NO repite el positioning personal: dice qué hace el repo.
|
|
35
|
+
- Los facts (versión, fecha, link) salen del release row y del SSOT; si un dato
|
|
36
|
+
no está, no se escribe.
|
|
37
|
+
- Un release = una pasada. No optimices metadata sin release: sin evento, la
|
|
38
|
+
superficie queda `never` en el plan (y eso es correcto, D23).
|
|
39
|
+
|
|
40
|
+
## Flujo
|
|
41
|
+
|
|
42
|
+
1. **Plan**: `pasco release plan <release-id> --json` → la fila
|
|
43
|
+
`github-repo-meta` (estado) + `baseText[kind=repo-meta]` (la espina factual).
|
|
44
|
+
Sin `releases[]` en el workspace, el comando falla con la razón: agregá el
|
|
45
|
+
release primero (schema v1.15).
|
|
46
|
+
2. **Composición** (juicio, esta skill):
|
|
47
|
+
- `description` ≤350 chars, la promesa del artefacto + una señal de para quién.
|
|
48
|
+
- `topics` ≤20, minúsculas, guiones; genéricos primero, específicos después.
|
|
49
|
+
- release notes: `baseText` como primer párrafo + `changedUnits` como bullets
|
|
50
|
+
verificables; nada de métricas sin fuente.
|
|
51
|
+
- social preview 1280×640 (el preview se sube a mano: no hay API).
|
|
52
|
+
3. **Deploy** (API de GitHub, D9):
|
|
53
|
+
```sh
|
|
54
|
+
gh repo edit --description "<description>" --add-topic <topic> …
|
|
55
|
+
gh release edit <tag> --notes-file <notas.md>
|
|
56
|
+
```
|
|
57
|
+
4. **Verificación** (playbook.md, sección "Verificación post-deploy"): la
|
|
58
|
+
lectura de vuelta con `gh repo view --json description,repositoryTopics` y la
|
|
59
|
+
inspección visual del social preview.
|
|
60
|
+
5. **Cierre del loop**: en v1 el ledger solo direcciona las superficies del enum
|
|
61
|
+
de perfiles; `github-repo-meta` no tiene record y el plan la lee `never`. Es
|
|
62
|
+
un límite declarado (D23): el plan nombra lo que falta tocar; extender la
|
|
63
|
+
clave de superficie del ledger es un cambio de contrato aparte.
|
|
64
|
+
|
|
65
|
+
## Límites y trampas
|
|
66
|
+
|
|
67
|
+
- **Descripción duplicada**: si el repo y la página npm dicen lo mismo, es señal
|
|
68
|
+
de que el release no cambió nada relevante; no reescribas por inercia.
|
|
69
|
+
- **Topics como hashtags**: no son keywords de marketing; son el índice por el
|
|
70
|
+
que un desconocido encuentra el artefacto.
|
|
71
|
+
- **Notas = changelog completo**: el release note es la parte que le importa a
|
|
72
|
+
quien evalúa adoptar; el detalle vive en CHANGELOG.
|
|
73
|
+
- **Social preview desactualizado**: es la única pieza sin API; verificala a
|
|
74
|
+
mano o no cuenta como publicada.
|
|
75
|
+
|
|
76
|
+
## Checks
|
|
77
|
+
|
|
78
|
+
`checks.yaml` declara los criterios con la misma forma que el resto de las skills
|
|
79
|
+
(`rule` del catálogo, `platform: github-repo-meta`). En v1 `pasco check` no
|
|
80
|
+
ejecuta una superficie fuera del enum de perfiles: el catálogo queda declarado
|
|
81
|
+
para cuando la superficie tenga renderer, y hoy la verificación es la del
|
|
82
|
+
playbook.
|