@sous-io/sous 0.1.0

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 (82) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +154 -0
  3. package/bin/run.js +17 -0
  4. package/bin/xcv +5 -0
  5. package/package.json +81 -0
  6. package/shared-prompts/_partials/resume-task.md +51 -0
  7. package/shared-prompts/_partials/sub-agent-delegation.md +32 -0
  8. package/shared-prompts/_partials/update-task-file.md +52 -0
  9. package/shared-prompts/memories/automated-browser-tasks/INDEX.tpl.md +52 -0
  10. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/SKILL.tpl.md +102 -0
  11. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/auth-failure-handling.mjs +81 -0
  12. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/chained-workflow.mjs +126 -0
  13. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/examples/simple-fetch.mjs +92 -0
  14. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/architecture.md +61 -0
  15. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/auth-and-sessions.md +65 -0
  16. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/ctx-api.md +96 -0
  17. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/installation.md +104 -0
  18. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/references/script-conventions.md +243 -0
  19. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/chrome-state.mjs +148 -0
  20. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/debug.mjs +383 -0
  21. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/debug.spec.mjs +267 -0
  22. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/eslint.config.mjs +56 -0
  23. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/harness.mjs +169 -0
  24. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/keyring.mjs +59 -0
  25. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/logger.mjs +25 -0
  26. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/params.mjs +140 -0
  27. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/run.mjs +140 -0
  28. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/settings.tpl.mjs +1 -0
  29. package/shared-prompts/skills/automated-browser-tasks/about-automated-browser-tasks/scripts/utils.mjs +185 -0
  30. package/shared-prompts/skills/automated-browser-tasks/create-automated-browser-task/SKILL.tpl.md +52 -0
  31. package/shared-prompts/skills/automated-browser-tasks/running-automated-browser-tasks/SKILL.tpl.md +59 -0
  32. package/shared-prompts/skills/automated-browser-tasks/update-automated-browser-task/SKILL.tpl.md +47 -0
  33. package/shared-prompts/skills/control-flow/approve/SKILL.tpl.md +26 -0
  34. package/shared-prompts/skills/control-flow/opine/SKILL.tpl.md +58 -0
  35. package/shared-prompts/skills/control-flow/repeat/SKILL.tpl.md +27 -0
  36. package/shared-prompts/skills/control-flow/research/SKILL.tpl.md +34 -0
  37. package/shared-prompts/skills/sous-skills/about-agent-skills/SKILL.tpl.md +177 -0
  38. package/shared-prompts/skills/sous-skills/about-agent-skills/examples/about-something.md +45 -0
  39. package/shared-prompts/skills/sous-skills/about-agent-skills/examples/do-something.md +33 -0
  40. package/shared-prompts/skills/sous-skills/about-agent-skills/references/advanced-patterns.md +87 -0
  41. package/shared-prompts/skills/sous-skills/about-agent-skills/references/commands.md +46 -0
  42. package/shared-prompts/skills/sous-skills/about-agent-skills/references/frontmatter.md +25 -0
  43. package/shared-prompts/skills/sous-skills/about-agent-skills/references/substitutions.md +50 -0
  44. package/shared-prompts/skills/sous-skills/about-liquid-templates/SKILL.tpl.md +268 -0
  45. package/shared-prompts/skills/sous-skills/about-liquid-templates/references/liquid-filters.md +82 -0
  46. package/shared-prompts/skills/sous-skills/about-sous/SKILL.tpl.md +51 -0
  47. package/shared-prompts/skills/sous-skills/create-skill/SKILL.tpl.md +114 -0
  48. package/shared-prompts/skills/task-files/about-task-files/SKILL.tpl.md +122 -0
  49. package/shared-prompts/skills/task-files/continue-task-in-new-branch/SKILL.tpl.md +80 -0
  50. package/shared-prompts/skills/task-files/go/SKILL.tpl.md +14 -0
  51. package/shared-prompts/skills/task-files/resume-task/SKILL.tpl.md +13 -0
  52. package/shared-prompts/skills/task-files/start-task/SKILL.tpl.md +93 -0
  53. package/shared-prompts/skills/task-files/update/SKILL.tpl.md +14 -0
  54. package/shared-prompts/skills/task-files/update-task-file/SKILL.tpl.md +13 -0
  55. package/src/base-command.ts +163 -0
  56. package/src/commands/build.ts +196 -0
  57. package/src/commands/clear.ts +71 -0
  58. package/src/commands/compile.ts +95 -0
  59. package/src/commands/launch.ts +111 -0
  60. package/src/commands/prune.ts +48 -0
  61. package/src/lib/build-service.ts +258 -0
  62. package/src/lib/config-discovery.ts +199 -0
  63. package/src/lib/env-local.ts +195 -0
  64. package/src/lib/include-resolver.ts +146 -0
  65. package/src/lib/markdown-compiler.ts +580 -0
  66. package/src/lib/pid-service.ts +88 -0
  67. package/src/lib/settings.ts +695 -0
  68. package/src/lib/state.ts +135 -0
  69. package/src/lib/watch-service.ts +115 -0
  70. package/src/templating/filters/bullet-list.ts +9 -0
  71. package/src/templating/filters/index.ts +8 -0
  72. package/src/templating/init-liquid-engine.ts +82 -0
  73. package/src/templating/lib/glob-files.ts +74 -0
  74. package/src/templating/lib/import-export.ts +32 -0
  75. package/src/templating/lib/tag-args.ts +19 -0
  76. package/src/templating/tags/exportScalarVarsJs.ts +43 -0
  77. package/src/templating/tags/getFiles.ts +89 -0
  78. package/src/templating/tags/index.ts +14 -0
  79. package/src/templating/tags/listFiles.ts +54 -0
  80. package/src/templating/tags/showVars.ts +22 -0
  81. package/src/utils/formatting.ts +338 -0
  82. package/src/utils/prompts.ts +19 -0
@@ -0,0 +1,22 @@
1
+ import type { Liquid } from "liquidjs";
2
+ import type { Context } from "liquidjs/dist/context/context.js";
3
+ import { sortObjectKeys } from "../../utils/formatting.js";
4
+
5
+ /** Dumps all variables currently in scope as a fenced JSON block. */
6
+ export function registerShowVarsTag(engine: Liquid): void {
7
+ engine.registerTag("showVars", {
8
+ render(ctx: Context) {
9
+ const seen = new WeakSet();
10
+ const scope = sortObjectKeys(ctx.getAll() as Record<string, unknown>);
11
+ const json = JSON.stringify(scope, (_key, value) => {
12
+ if (typeof value === "object" && value !== null) {
13
+ if (seen.has(value)) return "[Circular]";
14
+ seen.add(value);
15
+ }
16
+ return value;
17
+ }, 2);
18
+
19
+ return "# Sous Debug: Variable Dump\n```json\n" + json + "\n```";
20
+ },
21
+ });
22
+ }
@@ -0,0 +1,338 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { color } from "@oclif/color";
3
+
4
+ const DEFAULT_VAR_NAME_PADDING = 20;
5
+
6
+ const HEADER_LINES = [
7
+ " ▄█████ ▄████▄ ██ ██ ▄█████ ",
8
+ " ▀▀▀▄▄▄ ██ ██ ██ ██ ▀▀▀▄▄▄ ",
9
+ " █████▀ ▀████▀ ▀████▀ █████▀ ",
10
+ ];
11
+
12
+
13
+
14
+
15
+
16
+ // --- Core Output Functions -----------------------------------------------------------------------
17
+
18
+ /**
19
+ * Writes a string of text to the console, with a newline.
20
+ */
21
+ export function log(text: string): void {
22
+ console.log(text);
23
+ }
24
+
25
+ /**
26
+ * Writes a string of text to the console. Concurrent calls to this
27
+ * function will replace the text of the previous call, making this
28
+ * function useful for things like progress indicators.
29
+ */
30
+ export function logPersistent(text: string): void {
31
+ process.stdout.write(`\r${text}`);
32
+ }
33
+
34
+ // --- Whitespace Helpers --------------------------------------------------------------------------
35
+
36
+ /**
37
+ * Writes a blank line to the console.
38
+ * Note that we output a space character to ensure that the line is not
39
+ * completely empty, which some terminal emulators will ignore.
40
+ */
41
+ export function blankLine(): void {
42
+ log(" ");
43
+ }
44
+
45
+ /**
46
+ * Writes multiple blank lines to the console.
47
+ */
48
+ export function blankLines(blankLineCount = 2): void {
49
+ for (let i = 0; i < blankLineCount; i++) {
50
+ blankLine();
51
+ }
52
+ }
53
+
54
+ // --- String Builders -----------------------------------------------------------------------------
55
+
56
+ /**
57
+ * Applies a prefix symbol to a string of text. If `prefixSymbol` is
58
+ * an empty string, the text is returned verbatim.
59
+ */
60
+ function applyPrefixSymbol(text: string, prefixSymbol = "➔"): string {
61
+ const prefix = prefixSymbol ? `${prefixSymbol} ` : "";
62
+ return `${prefix}${text}`;
63
+ }
64
+
65
+ /**
66
+ * Formats a heading text string. If the heading does not end with a
67
+ * period, a colon is appended.
68
+ */
69
+ function formatHeadingText(heading: string): string {
70
+ const append = heading.endsWith(".") ? "" : ":";
71
+ return `${heading}${append}`;
72
+ }
73
+
74
+ /**
75
+ * Indents a block of text by a specified number of spaces.
76
+ */
77
+ export function indent(text: string, count = 2, char = " "): string {
78
+ return text
79
+ .split("\n")
80
+ .map(line => char.repeat(count) + line)
81
+ .join("\n");
82
+ }
83
+
84
+ // --- Header & Footer -----------------------------------------------------------------------------
85
+
86
+ /**
87
+ * Writes the CLI header to the console.
88
+ */
89
+ export function header(): void {
90
+ blankLines();
91
+ log(color.cyan(HEADER_LINES.join("\n")));
92
+ log(" Agent Configuration Manager ");
93
+ blankLine();
94
+ }
95
+
96
+ /**
97
+ * Writes the CLI footer to the console.
98
+ */
99
+ export function footer(): void {
100
+ blankLines(2);
101
+ }
102
+
103
+ // --- Headings & Subheadings ----------------------------------------------------------------------
104
+
105
+ /**
106
+ * Writes a heading to the console.
107
+ *
108
+ * @example
109
+ * heading("Compiling targets");
110
+ * // ▶ Compiling targets:
111
+ */
112
+ export function heading(text: string, prefixSymbol = "▶"): void {
113
+ const formatted = formatHeadingText(text);
114
+ const final = applyPrefixSymbol(formatted, prefixSymbol);
115
+ blankLine();
116
+ log(color.yellowBright(final));
117
+ }
118
+
119
+ /**
120
+ * Writes a subheading to the console.
121
+ *
122
+ * @example
123
+ * subheading("Target 1");
124
+ * // ➔ Target 1:
125
+ */
126
+ export function subheading(text: string, prefixSymbol = "➔"): void {
127
+ const formatted = formatHeadingText(text);
128
+ const final = applyPrefixSymbol(formatted, prefixSymbol);
129
+ blankLine();
130
+ log(color.whiteBright(final));
131
+ }
132
+
133
+ // --- Data Display Functions ----------------------------------------------------------------------
134
+
135
+ /**
136
+ * Dumps the contents of an object to the console.
137
+ */
138
+ export function dump<T>(obj: T, headingText?: string): void {
139
+ if (headingText) {
140
+ subheading(headingText);
141
+ }
142
+ const normalized =
143
+ typeof obj === "object" && obj !== null
144
+ ? (sortObjectKeys(obj as Record<string, any>) as T)
145
+ : obj;
146
+ console.dir(normalized);
147
+ }
148
+
149
+ /**
150
+ * Displays a count of something.
151
+ *
152
+ * @example
153
+ * showCount(5, "tokens");
154
+ * // Found [5] tokens
155
+ */
156
+ export function showCount(count: number, entity = "items", headingText = "", verb = "Found"): void {
157
+ if (headingText) {
158
+ subheading(headingText);
159
+ }
160
+
161
+ const prefix = verb ? `${verb} ` : "";
162
+ let strCount = count.toLocaleString();
163
+ strCount = color.whiteBright(strCount);
164
+ strCount = color.cyan("[") + strCount + color.cyan("]");
165
+
166
+ let line = `${prefix}${strCount} ${entity}`;
167
+ if (headingText) {
168
+ line = indent(line);
169
+ }
170
+
171
+ log(line);
172
+ }
173
+
174
+ /**
175
+ * Displays a variable name and its value to the console.
176
+ *
177
+ * @example
178
+ * showVar("Config", "./my-config.js");
179
+ * // Config : ./my-config.js
180
+ */
181
+ export function showVar(name: string, value: any, padding = DEFAULT_VAR_NAME_PADDING): void {
182
+ const str = `${color.cyan(name.padEnd(padding))}: ${value}`;
183
+ log(indent(str));
184
+ }
185
+
186
+ /**
187
+ * Displays a list of variables to the console, with aligned colons.
188
+ *
189
+ * @example
190
+ * showVars({ Config: "./my-config.js", Strict: "false" });
191
+ * // Config : ./my-config.js
192
+ * // Strict : false
193
+ */
194
+ export function showVars(vars: Record<string, any>): void {
195
+ const padding = findLongestKeyLength(vars) + 1;
196
+ for (const [name, value] of Object.entries(vars)) {
197
+ showVar(name, value, padding);
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Displays a heading labelled "Command Variables" followed by a variable list.
203
+ */
204
+ export function showCommandVars(vars: Record<string, any>): void {
205
+ subheading("Command Variables", "$");
206
+ showVars(vars);
207
+ }
208
+
209
+ /**
210
+ * Displays the status of an in-progress scan using an in-place log line.
211
+ */
212
+ export function scanStatus(matchedRecordCount: number, totalScanCount: number): void {
213
+ const displayMatched = `[${color.white(matchedRecordCount.toLocaleString())}]`;
214
+ const displayScanned = `[${color.white(totalScanCount.toLocaleString())}]`;
215
+ logPersistent(
216
+ color.gray(
217
+ `Scan in progress: ${displayMatched} records matched (so far) of ${displayScanned} records scanned ...`,
218
+ ),
219
+ );
220
+ }
221
+
222
+ /**
223
+ * Displays the status of an in-progress deletion using an in-place log line.
224
+ */
225
+ export function deleteStatus(recordsDeleted: number, totalRecordsToDelete: number): void {
226
+ const displayDeleted = `[${color.white(recordsDeleted.toLocaleString())}]`;
227
+ const displayTotal = `[${color.white(totalRecordsToDelete.toLocaleString())}]`;
228
+ logPersistent(
229
+ color.gray(
230
+ `Deletion in progress: ${displayDeleted} of ${displayTotal} records deleted ...`,
231
+ ),
232
+ );
233
+ }
234
+
235
+ // --- Special Notices -----------------------------------------------------------------------------
236
+
237
+ /**
238
+ * Writes an error message to the console in red.
239
+ */
240
+ export function displayError(text: string): void {
241
+ const lines = text.split("\n");
242
+ log("");
243
+ for (const line of lines) {
244
+ if (line.trim() !== "") {
245
+ log(indent(color.redBright(line.trim())));
246
+ }
247
+ }
248
+ log("");
249
+ log("");
250
+ }
251
+
252
+ /**
253
+ * Writes a pre-formatted, multi-line error message to the console in red,
254
+ * preserving the message's own indentation and blank lines.
255
+ *
256
+ * Use this instead of `displayError` when the message contains deliberate
257
+ * structure (a checked-paths list, a code sample, numbered steps). `displayError`
258
+ * trims every line and drops blanks, which flattens that structure.
259
+ *
260
+ * @example
261
+ * displayErrorBlock("No config found.\n\n Checked:\n /a/.sous/");
262
+ */
263
+ export function displayErrorBlock(text: string): void {
264
+ log("");
265
+ for (const line of text.split("\n")) {
266
+ log(line === "" ? " " : indent(color.redBright(line)));
267
+ }
268
+ log("");
269
+ log("");
270
+ }
271
+
272
+ /**
273
+ * Writes a notice indicating that the operation is running in dry-run mode.
274
+ *
275
+ * @example
276
+ * dryRunNotice("File will not be written.");
277
+ * // [Dry Run] File will not be written.
278
+ */
279
+ export function dryRunNotice(text: string): void {
280
+ log(" " + color.cyan("[Dry Run] " + text));
281
+ }
282
+
283
+ /**
284
+ * Writes a warning message to the console with a yellow banner.
285
+ */
286
+ export function warning(text: string): void {
287
+ blankLines();
288
+ log(color.bgYellowBright(color.black(" WARNING: ")));
289
+
290
+ const lines = text.split("\n");
291
+ for (const line of lines) {
292
+ if (line.trim() !== "") {
293
+ log(highlightUpperCaseWords(indent(line.trim())));
294
+ }
295
+ }
296
+ blankLine();
297
+ }
298
+
299
+ // --- Miscellaneous Helpers -----------------------------------------------------------------------
300
+
301
+ /**
302
+ * Highlights all UPPERCASE words in the given string.
303
+ */
304
+ function highlightUpperCaseWords(
305
+ str: string,
306
+ highlightFn: (word: string) => string = color.yellowBright,
307
+ ): string {
308
+ return str.replace(/\b[A-Z][A-Z_'"()\[\]{}<>|&*!@#%^\\-]+\b/g, match => highlightFn(match));
309
+ }
310
+
311
+ /**
312
+ * Sorts the keys of an object alphabetically.
313
+ */
314
+ export function sortObjectKeys<T extends Record<string, any>>(obj: T): T {
315
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
316
+ return obj;
317
+ }
318
+
319
+ try {
320
+ return Object.keys(obj)
321
+ .sort()
322
+ .reduce((acc, key) => {
323
+ // @ts-ignore
324
+ acc[key] = obj[key];
325
+ return acc;
326
+ }, {} as T);
327
+ } catch {
328
+ /* c8 ignore next */
329
+ return obj;
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Finds the length of the longest key in an object.
335
+ */
336
+ function findLongestKeyLength(vars: Record<string, any>): number {
337
+ return Math.max(...Object.keys(vars).map(key => key.length));
338
+ }
@@ -0,0 +1,19 @@
1
+ import { confirm as inquire } from "@inquirer/prompts";
2
+ import { color } from "@oclif/color";
3
+
4
+ import { blankLine, blankLines, log } from "./formatting.js";
5
+
6
+ /**
7
+ * Ask the user if they're sure they want to proceed.
8
+ * @param prompt - An optional, custom, prompt to display to the user.
9
+ */
10
+ export async function areYouSure(prompt = "Are you SURE you want to proceed?"): Promise<void> {
11
+ blankLine();
12
+ const confirm = await inquire({ message: prompt });
13
+ if (!confirm) {
14
+ blankLine();
15
+ log(color.redBright("Aborting."));
16
+ blankLines();
17
+ process.exit(0);
18
+ }
19
+ }