@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.
Files changed (68) hide show
  1. package/dist/cli.js +848 -164
  2. package/docs/README.md +10 -5
  3. package/docs/agents.md +320 -9
  4. package/docs/commands.md +5 -6
  5. package/docs/concepts.md +27 -9
  6. package/docs/cost.md +3 -2
  7. package/docs/doctor-codes.md +13 -4
  8. package/docs/export.md +2 -1
  9. package/docs/extending.md +11 -2
  10. package/docs/getting-started.md +89 -84
  11. package/docs/images/brain.jpg +0 -0
  12. package/docs/images/org.jpg +0 -0
  13. package/docs/images/prompt.jpg +0 -0
  14. package/docs/images/setup-org.jpg +0 -0
  15. package/docs/images/setup-plan.jpg +0 -0
  16. package/docs/images/staff.jpg +0 -0
  17. package/docs/manual-steps.md +36 -13
  18. package/docs/memory.md +9 -6
  19. package/docs/org-yaml.md +37 -9
  20. package/docs/portal.md +197 -31
  21. package/docs/prompts.md +50 -11
  22. package/docs/security.md +19 -7
  23. package/docs/session-workflow.md +8 -10
  24. package/docs/staff-yaml.md +3 -5
  25. package/docs/troubleshooting.md +17 -14
  26. package/docs/writing-a-charter.md +18 -17
  27. package/package.json +1 -1
  28. package/templates/brain/.github/workflows/%%STAFF%%-daily.yaml +1 -0
  29. package/templates/brain/.github/workflows/%%STAFF%%-mention.yaml +10 -4
  30. package/templates/brain/staff.yaml +0 -1
  31. package/templates/ops/.github/workflows/session.yaml +9 -26
  32. package/templates/ops/agents.mjs +121 -6
  33. package/templates/ops/compose.mjs +61 -4
  34. package/templates/ops/org/operating.md +0 -6
  35. package/templates/ops/prompts/_identity.md +8 -1
  36. package/templates/ops/prompts/mention.md +16 -2
  37. package/templates/portal/css/base.css +122 -8
  38. package/templates/portal/css/brain.css +8 -1
  39. package/templates/portal/css/diff.css +6 -2
  40. package/templates/portal/css/health.css +21 -2
  41. package/templates/portal/css/inbox.css +93 -5
  42. package/templates/portal/css/layout.css +26 -4
  43. package/templates/portal/css/markdown.css +23 -3
  44. package/templates/portal/css/setup.css +11 -6
  45. package/templates/portal/index.html +7 -1
  46. package/templates/portal/js/api.js +33 -0
  47. package/templates/portal/js/app.js +33 -7
  48. package/templates/portal/js/dialog.js +47 -4
  49. package/templates/portal/js/dom.js +25 -0
  50. package/templates/portal/js/icons.js +8 -1
  51. package/templates/portal/js/lightbox.js +273 -0
  52. package/templates/portal/js/md.js +23 -6
  53. package/templates/portal/js/mention.js +264 -0
  54. package/templates/portal/js/refresh.js +136 -6
  55. package/templates/portal/js/state.js +47 -5
  56. package/templates/portal/js/views/checklist.js +20 -7
  57. package/templates/portal/js/views/docs.js +94 -4
  58. package/templates/portal/js/views/files.js +58 -14
  59. package/templates/portal/js/views/health.js +163 -35
  60. package/templates/portal/js/views/inbox.js +882 -96
  61. package/templates/portal/js/views/memory.js +16 -1
  62. package/templates/portal/js/views/org.js +142 -62
  63. package/templates/portal/js/views/prompt.js +50 -63
  64. package/templates/portal/js/views/setup.js +37 -13
  65. package/templates/portal/js/views/staff.js +62 -2
  66. package/templates/portal/js/yaml.js +134 -0
  67. package/templates/brain/.github/workflows/%%STAFF%%-pr-mention.yaml +0 -50
  68. 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(/&lt;/g, "<")
41
+ .replace(/&gt;/g, ">")
42
+ .replace(/&quot;/g, '"')
43
+ .replace(/&amp;/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.