@nanocollective/roster 0.1.0-alpha.2 → 0.1.0-alpha.4
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/dist/cli.js +848 -164
- package/docs/README.md +10 -5
- package/docs/agents.md +320 -9
- package/docs/commands.md +5 -6
- package/docs/concepts.md +27 -9
- package/docs/cost.md +3 -2
- package/docs/doctor-codes.md +13 -4
- package/docs/export.md +2 -1
- package/docs/extending.md +11 -2
- package/docs/getting-started.md +89 -84
- package/docs/images/brain.jpg +0 -0
- package/docs/images/org.jpg +0 -0
- package/docs/images/prompt.jpg +0 -0
- package/docs/images/setup-org.jpg +0 -0
- package/docs/images/setup-plan.jpg +0 -0
- package/docs/images/staff.jpg +0 -0
- package/docs/manual-steps.md +36 -13
- package/docs/memory.md +9 -6
- package/docs/org-yaml.md +37 -9
- package/docs/portal.md +197 -31
- package/docs/prompts.md +50 -11
- package/docs/security.md +19 -7
- package/docs/session-workflow.md +8 -10
- package/docs/staff-yaml.md +3 -5
- package/docs/troubleshooting.md +17 -14
- package/docs/writing-a-charter.md +18 -17
- package/package.json +1 -1
- package/templates/brain/.github/workflows/%%STAFF%%-daily.yaml +1 -0
- package/templates/brain/.github/workflows/%%STAFF%%-mention.yaml +10 -4
- package/templates/brain/staff.yaml +0 -1
- package/templates/ops/.github/workflows/session.yaml +9 -26
- package/templates/ops/agents.mjs +121 -6
- package/templates/ops/compose.mjs +61 -4
- package/templates/ops/org/operating.md +0 -6
- package/templates/ops/prompts/_identity.md +8 -1
- package/templates/ops/prompts/mention.md +16 -2
- package/templates/portal/css/base.css +122 -8
- package/templates/portal/css/brain.css +8 -1
- package/templates/portal/css/diff.css +6 -2
- package/templates/portal/css/health.css +21 -2
- package/templates/portal/css/inbox.css +93 -5
- package/templates/portal/css/layout.css +26 -4
- package/templates/portal/css/markdown.css +23 -3
- package/templates/portal/css/setup.css +11 -6
- package/templates/portal/index.html +7 -1
- package/templates/portal/js/api.js +33 -0
- package/templates/portal/js/app.js +33 -7
- package/templates/portal/js/dialog.js +47 -4
- package/templates/portal/js/dom.js +25 -0
- package/templates/portal/js/icons.js +8 -1
- package/templates/portal/js/lightbox.js +273 -0
- package/templates/portal/js/md.js +23 -6
- package/templates/portal/js/mention.js +264 -0
- package/templates/portal/js/refresh.js +136 -6
- package/templates/portal/js/state.js +47 -5
- package/templates/portal/js/views/checklist.js +20 -7
- package/templates/portal/js/views/docs.js +94 -4
- package/templates/portal/js/views/files.js +58 -14
- package/templates/portal/js/views/health.js +163 -35
- package/templates/portal/js/views/inbox.js +882 -96
- package/templates/portal/js/views/memory.js +16 -1
- package/templates/portal/js/views/org.js +142 -62
- package/templates/portal/js/views/prompt.js +50 -63
- package/templates/portal/js/views/setup.js +37 -13
- package/templates/portal/js/views/staff.js +62 -2
- package/templates/portal/js/yaml.js +134 -0
- package/templates/brain/.github/workflows/%%STAFF%%-pr-mention.yaml +0 -50
- package/templates/ops/prompts/pr-mention.md +0 -57
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/* Colour for YAML.
|
|
2
|
+
*
|
|
3
|
+
* `org.yaml` and `staff.yaml` are the two files here a person actually reads closely, and they
|
|
4
|
+
* were served as one flat grey wall: the comments, the keys and the values all the same weight,
|
|
5
|
+
* so finding `mention_timeout_minutes` meant reading every line. Structure is the thing YAML has
|
|
6
|
+
* and prose does not, and showing it costs about eighty lines.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately not a parser. It is a per-line tokeniser that knows comments, keys, quoted
|
|
9
|
+
* strings, numbers and the three keywords — enough to read by, and it cannot fail on a file it
|
|
10
|
+
* does not understand, because anything it does not recognise is left as plain text.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { el, esc } from "./dom.js";
|
|
14
|
+
|
|
15
|
+
/** A `<pre>` holding the highlighted source. */
|
|
16
|
+
export function yamlPre(text, className = "code yaml") {
|
|
17
|
+
const pre = el("pre", { className });
|
|
18
|
+
pre.innerHTML = yamlHTML(text);
|
|
19
|
+
return pre;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Escaped, highlighted HTML. Takes raw text: it does its own escaping. */
|
|
23
|
+
export function yamlHTML(text) {
|
|
24
|
+
return String(text ?? "")
|
|
25
|
+
.split("\n")
|
|
26
|
+
.map(colourLine)
|
|
27
|
+
.join("\n");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The same, for text that is already HTML-escaped.
|
|
32
|
+
*
|
|
33
|
+
* The markdown renderer escapes a fenced block before it knows what language it is, and
|
|
34
|
+
* un-escaping four entities to escape them again is exact and cheaper than restructuring
|
|
35
|
+
* the renderer around one case.
|
|
36
|
+
*/
|
|
37
|
+
export function yamlHTMLFromEscaped(escaped) {
|
|
38
|
+
return yamlHTML(
|
|
39
|
+
String(escaped ?? "")
|
|
40
|
+
.replace(/</g, "<")
|
|
41
|
+
.replace(/>/g, ">")
|
|
42
|
+
.replace(/"/g, '"')
|
|
43
|
+
.replace(/&/g, "&"),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const span = (cls, text) => '<span class="' + cls + '">' + esc(text) + "</span>";
|
|
48
|
+
|
|
49
|
+
function colourLine(raw) {
|
|
50
|
+
const [code, comment] = splitComment(raw);
|
|
51
|
+
return (code ? colourCode(code) : "") + (comment ? span("yc", comment) : "");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Split a trailing comment off, without cutting a `#` that is inside a string.
|
|
56
|
+
*
|
|
57
|
+
* `marker: will # the provenance tag` is a comment; `name: "a # b"` is not. YAML also only
|
|
58
|
+
* starts a comment at the beginning of a line or after whitespace, which is what keeps a
|
|
59
|
+
* `#hashtag` in a value intact.
|
|
60
|
+
*/
|
|
61
|
+
function splitComment(raw) {
|
|
62
|
+
let quote = null;
|
|
63
|
+
for (let i = 0; i < raw.length; i++) {
|
|
64
|
+
const c = raw[i];
|
|
65
|
+
if (quote) {
|
|
66
|
+
if (c === quote && raw[i - 1] !== "\\") quote = null;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (c === '"' || c === "'") {
|
|
70
|
+
quote = c;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (c === "#" && (i === 0 || /\s/.test(raw[i - 1]))) return [raw.slice(0, i), raw.slice(i)];
|
|
74
|
+
}
|
|
75
|
+
return [raw, ""];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const DOC = /^\s*(---|\.\.\.)\s*$/;
|
|
79
|
+
/** Indent, then any run of list dashes: ` - - name: x` is two levels of list. */
|
|
80
|
+
const LEAD = /^(\s*)((?:-\s+)*)/;
|
|
81
|
+
/** A key is everything up to the first colon that is followed by a space or the end of line. */
|
|
82
|
+
const KEY = /^([^\s"'#][^:]*?)(:)(\s|$)/;
|
|
83
|
+
|
|
84
|
+
function colourCode(code) {
|
|
85
|
+
if (DOC.test(code)) return span("yp", code);
|
|
86
|
+
|
|
87
|
+
const lead = LEAD.exec(code);
|
|
88
|
+
const indent = lead[1];
|
|
89
|
+
const dashes = lead[2];
|
|
90
|
+
let rest = code.slice(indent.length + dashes.length);
|
|
91
|
+
let out = indent + (dashes ? span("yd", dashes) : "");
|
|
92
|
+
|
|
93
|
+
/* A line that opens a flow collection has no key of its own: `- { handle: cto }` is a list
|
|
94
|
+
item whose keys are all inside the braces, and taking "{ handle" for a key marked the
|
|
95
|
+
brace as part of the name. */
|
|
96
|
+
const key = /^[{[]/.test(rest) ? null : KEY.exec(rest);
|
|
97
|
+
if (key) {
|
|
98
|
+
out += span("yk", key[1]) + span("yp", key[2]) + key[3];
|
|
99
|
+
rest = rest.slice(key[1].length + 1 + key[3].length);
|
|
100
|
+
}
|
|
101
|
+
return out + colourValue(rest);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Strings, inline-map keys, the three keywords, numbers, and the punctuation that holds a flow
|
|
105
|
+
collection together. Ordered: a quoted string wins over everything inside it. */
|
|
106
|
+
const TOKEN = new RegExp(
|
|
107
|
+
[
|
|
108
|
+
/("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/, // 1 quoted string
|
|
109
|
+
/([A-Za-z_][\w.-]*)(\s*:)(?=\s|$)/, // 2,3 a key inside an inline map
|
|
110
|
+
/(\b(?:true|false|null|yes|no|on|off)\b|~)/, // 4 keywords
|
|
111
|
+
/(-?\d+(?:\.\d+)?\b)/, // 5 numbers
|
|
112
|
+
/([{}[\],])/, // 6 flow punctuation
|
|
113
|
+
]
|
|
114
|
+
.map((r) => r.source)
|
|
115
|
+
.join("|"),
|
|
116
|
+
"g",
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
function colourValue(value) {
|
|
120
|
+
if (!value) return "";
|
|
121
|
+
let out = "";
|
|
122
|
+
let at = 0;
|
|
123
|
+
TOKEN.lastIndex = 0;
|
|
124
|
+
for (let m = TOKEN.exec(value); m; m = TOKEN.exec(value)) {
|
|
125
|
+
out += esc(value.slice(at, m.index));
|
|
126
|
+
if (m[1]) out += span("ys", m[1]);
|
|
127
|
+
else if (m[2]) out += span("yk", m[2]) + span("yp", m[3]);
|
|
128
|
+
else if (m[4]) out += span("yb", m[4]);
|
|
129
|
+
else if (m[5]) out += span("yn", m[5]);
|
|
130
|
+
else out += span("yp", m[6]);
|
|
131
|
+
at = m.index + m[0].length;
|
|
132
|
+
}
|
|
133
|
+
return out + esc(value.slice(at));
|
|
134
|
+
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
name: %%STAFF_UPPER%% on a product PR
|
|
2
|
-
|
|
3
|
-
# Generated by roster. The body lives in %%OPS_REPO%%/.github/workflows/session.yaml.
|
|
4
|
-
#
|
|
5
|
-
# Amends an open PR on the product repo in response to a review comment, so the human can ask for
|
|
6
|
-
# changes where they are already reading the diff instead of opening a ticket and cross-referencing.
|
|
7
|
-
#
|
|
8
|
-
# Not triggered directly. A forwarder in the PUBLIC product repo catches the mention and dispatches
|
|
9
|
-
# here. The split exists because that repo is public: its Actions logs are world readable, so the
|
|
10
|
-
# run that prints a charter and a chain of reasoning has to happen in a repo only we can see.
|
|
11
|
-
#
|
|
12
|
-
# NOTE: %%TOKENS%% are filled by `roster hire`. GitHub's own ${{ }} expressions are left alone.
|
|
13
|
-
|
|
14
|
-
on:
|
|
15
|
-
repository_dispatch:
|
|
16
|
-
types: [pr-mention]
|
|
17
|
-
|
|
18
|
-
# Manual handle, for testing without leaving a comment on a public PR, and for re-running a
|
|
19
|
-
# request the dispatcher already consumed.
|
|
20
|
-
workflow_dispatch:
|
|
21
|
-
inputs:
|
|
22
|
-
pr:
|
|
23
|
-
description: "PR number on the product repo"
|
|
24
|
-
required: true
|
|
25
|
-
comment_id:
|
|
26
|
-
description: "id of the comment that triggered this"
|
|
27
|
-
required: true
|
|
28
|
-
|
|
29
|
-
# Per PR. Several review comments in a row are the normal case; different PRs never contend.
|
|
30
|
-
concurrency:
|
|
31
|
-
group: %%STAFF%%-pr-${{ github.event.inputs.pr || github.event.client_payload.pr }}
|
|
32
|
-
cancel-in-progress: false
|
|
33
|
-
|
|
34
|
-
jobs:
|
|
35
|
-
amend:
|
|
36
|
-
uses: %%OPS_REPO%%/.github/workflows/session.yaml@main
|
|
37
|
-
with:
|
|
38
|
-
staff: %%STAFF%%
|
|
39
|
-
kind: pr-mention
|
|
40
|
-
ops_repo: %%OPS_REPO%%
|
|
41
|
-
model: %%MODEL%%
|
|
42
|
-
timeout_minutes: %%PR_MENTION_TIMEOUT%%
|
|
43
|
-
pr_number: ${{ github.event.inputs.pr || github.event.client_payload.pr }}
|
|
44
|
-
comment_id: ${{ github.event.inputs.comment_id || github.event.client_payload.comment_id }}
|
|
45
|
-
secrets:
|
|
46
|
-
APP_ID: ${{ secrets.%%SECRET_PREFIX%%_APP_ID }}
|
|
47
|
-
APP_PRIVATE_KEY: ${{ secrets.%%SECRET_PREFIX%%_APP_PRIVATE_KEY }}
|
|
48
|
-
PUBLIC_APP_ID: ${{ secrets.%%PUBLIC_SECRET_PREFIX%%_APP_ID }}
|
|
49
|
-
PUBLIC_APP_PRIVATE_KEY: ${{ secrets.%%PUBLIC_SECRET_PREFIX%%_APP_PRIVATE_KEY }}
|
|
50
|
-
AGENT_TOKEN: ${{ secrets.%%AGENT_SECRET%% }}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
You are **{{staff.name}}** at {{org.name}}. {{human.name}} has asked for a change to an open pull
|
|
2
|
-
request, in a comment on the PR itself. They are not watching a terminal; **your reply on that PR is
|
|
3
|
-
the only thing they will see.**
|
|
4
|
-
|
|
5
|
-
**This is not a session.** No boot ritual, no handoff, no rewriting #{{staff.status_issue}}. Make the
|
|
6
|
-
change asked for, push it, reply, stop.
|
|
7
|
-
|
|
8
|
-
{{#if staff.public_bot}}
|
|
9
|
-
## You are anonymous over there
|
|
10
|
-
|
|
11
|
-
The PR is on a **public** repo. You post as `{{staff.public_bot}}`, an ordinary automation account.
|
|
12
|
-
**Never sign as {{staff.name}}, never mention a charter, a tracker, a scheduled run, or that there is
|
|
13
|
-
a role behind the account.** Write like a maintainer's bot: short, factual, no personality.
|
|
14
|
-
|
|
15
|
-
Every `gh` call against that repo must carry the public identity: `GH_TOKEN=$PUBLIC_TOKEN gh ...`.
|
|
16
|
-
The bare `GH_TOKEN` is your private-tracker identity and using it there would sign your name to a
|
|
17
|
-
public repo.
|
|
18
|
-
{{/if}}
|
|
19
|
-
|
|
20
|
-
{{> prompts/_paths.md}}
|
|
21
|
-
|
|
22
|
-
## The request
|
|
23
|
-
|
|
24
|
-
**PR #{{event.pr_number}}, comment `{{event.comment_id}}`.** Read it before touching anything: an
|
|
25
|
-
inline review comment carries `path`, `line` and `diff_hunk`, which say exactly which code is meant.
|
|
26
|
-
Do not guess from the prose.
|
|
27
|
-
|
|
28
|
-
## Do the work
|
|
29
|
-
|
|
30
|
-
1. **You are already on the PR branch** - the runner checked it out. Do not start a new one.
|
|
31
|
-
2. **If the PR is from a fork you cannot push to**, do not try. Reply with the exact diff to apply,
|
|
32
|
-
and say why you could not push it.
|
|
33
|
-
3. **Make only the change asked for.** A review comment is a narrow request. Fixing something else
|
|
34
|
-
makes the diff unreviewable and costs a second review. If you spot something real and separate,
|
|
35
|
-
mention it in the reply and leave the code alone.
|
|
36
|
-
4. **Run the full gate before pushing.** If it goes red on the requested change, push nothing and
|
|
37
|
-
reply with what broke.
|
|
38
|
-
5. **Pull with rebase before pushing** - there may be several comments, and another run may be on
|
|
39
|
-
the same branch. If the push is still rejected, pull and retry rather than forcing: a force push
|
|
40
|
-
on a branch being reviewed throws away their place in the diff.
|
|
41
|
-
6. **You cannot verify anything UI-shaped** - there is no browser and no device here. Say so plainly
|
|
42
|
-
rather than implying a green gate covered it.
|
|
43
|
-
|
|
44
|
-
**Do not merge, and do not push to the default branch.**
|
|
45
|
-
|
|
46
|
-
{{> org/guardrails.md}}
|
|
47
|
-
|
|
48
|
-
## Reply where they asked
|
|
49
|
-
|
|
50
|
-
Answer in the same place the request came from. **Do not @-mention them** - they are subscribed to
|
|
51
|
-
their own PR.
|
|
52
|
-
|
|
53
|
-
{{> org/voice.md}}
|
|
54
|
-
|
|
55
|
-
**For this reply specifically:** what changed, the SHA, gate status, what it did not cover.
|
|
56
|
-
**Nothing else.** No preamble, no restating the request, no headers, no sign-off, no narration of
|
|
57
|
-
the steps you took. If nothing changed, one line saying so and why.
|