@dougharness/cli 1.0.1

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 (144) hide show
  1. package/LICENSE +21 -0
  2. package/dist/ablate.d.ts +96 -0
  3. package/dist/ablate.js +440 -0
  4. package/dist/ablate.js.map +1 -0
  5. package/dist/apply.d.ts +2 -0
  6. package/dist/apply.js +19 -0
  7. package/dist/apply.js.map +1 -0
  8. package/dist/bin.d.ts +2 -0
  9. package/dist/bin.js +80 -0
  10. package/dist/bin.js.map +1 -0
  11. package/dist/board-page.d.ts +15 -0
  12. package/dist/board-page.js +27 -0
  13. package/dist/board-page.js.map +1 -0
  14. package/dist/board-serve-process.d.ts +24 -0
  15. package/dist/board-serve-process.js +227 -0
  16. package/dist/board-serve-process.js.map +1 -0
  17. package/dist/board-serve.d.ts +9 -0
  18. package/dist/board-serve.js +302 -0
  19. package/dist/board-serve.js.map +1 -0
  20. package/dist/board.d.ts +6 -0
  21. package/dist/board.js +515 -0
  22. package/dist/board.js.map +1 -0
  23. package/dist/detect/fs.d.ts +6 -0
  24. package/dist/detect/fs.js +50 -0
  25. package/dist/detect/fs.js.map +1 -0
  26. package/dist/detect/index.d.ts +3 -0
  27. package/dist/detect/index.js +13 -0
  28. package/dist/detect/index.js.map +1 -0
  29. package/dist/detect/node.d.ts +3 -0
  30. package/dist/detect/node.js +179 -0
  31. package/dist/detect/node.js.map +1 -0
  32. package/dist/detect/python.d.ts +2 -0
  33. package/dist/detect/python.js +388 -0
  34. package/dist/detect/python.js.map +1 -0
  35. package/dist/detect/repo.d.ts +2 -0
  36. package/dist/detect/repo.js +102 -0
  37. package/dist/detect/repo.js.map +1 -0
  38. package/dist/detect/types.d.ts +98 -0
  39. package/dist/detect/types.js +2 -0
  40. package/dist/detect/types.js.map +1 -0
  41. package/dist/diff.d.ts +12 -0
  42. package/dist/diff.js +105 -0
  43. package/dist/diff.js.map +1 -0
  44. package/dist/generate/agents.d.ts +19 -0
  45. package/dist/generate/agents.js +184 -0
  46. package/dist/generate/agents.js.map +1 -0
  47. package/dist/generate/claude-md.d.ts +9 -0
  48. package/dist/generate/claude-md.js +111 -0
  49. package/dist/generate/claude-md.js.map +1 -0
  50. package/dist/generate/config.d.ts +51 -0
  51. package/dist/generate/config.js +155 -0
  52. package/dist/generate/config.js.map +1 -0
  53. package/dist/generate/proposal.d.ts +11 -0
  54. package/dist/generate/proposal.js +175 -0
  55. package/dist/generate/proposal.js.map +1 -0
  56. package/dist/generate/settings.d.ts +35 -0
  57. package/dist/generate/settings.js +213 -0
  58. package/dist/generate/settings.js.map +1 -0
  59. package/dist/generate/skills.d.ts +8 -0
  60. package/dist/generate/skills.js +264 -0
  61. package/dist/generate/skills.js.map +1 -0
  62. package/dist/init.d.ts +10 -0
  63. package/dist/init.js +69 -0
  64. package/dist/init.js.map +1 -0
  65. package/dist/trace.d.ts +9 -0
  66. package/dist/trace.js +106 -0
  67. package/dist/trace.js.map +1 -0
  68. package/node_modules/@dougharness/flow/.claude-plugin/plugin.json +10 -0
  69. package/node_modules/@dougharness/flow/LICENSE +21 -0
  70. package/node_modules/@dougharness/flow/agents/adversary-claude.md +33 -0
  71. package/node_modules/@dougharness/flow/agents/adversary.md +29 -0
  72. package/node_modules/@dougharness/flow/agents/implementer.md +23 -0
  73. package/node_modules/@dougharness/flow/agents/lead.md +35 -0
  74. package/node_modules/@dougharness/flow/agents/planner.md +70 -0
  75. package/node_modules/@dougharness/flow/agents/researcher.md +31 -0
  76. package/node_modules/@dougharness/flow/agents/reviewer.md +26 -0
  77. package/node_modules/@dougharness/flow/agents/verifier.md +24 -0
  78. package/node_modules/@dougharness/flow/lib/board.mjs +840 -0
  79. package/node_modules/@dougharness/flow/lib/code-index.mjs +629 -0
  80. package/node_modules/@dougharness/flow/lib/cost.mjs +392 -0
  81. package/node_modules/@dougharness/flow/lib/decisions.mjs +314 -0
  82. package/node_modules/@dougharness/flow/lib/embeddings.mjs +539 -0
  83. package/node_modules/@dougharness/flow/lib/land.mjs +219 -0
  84. package/node_modules/@dougharness/flow/lib/learn.mjs +742 -0
  85. package/node_modules/@dougharness/flow/lib/memory-import.mjs +145 -0
  86. package/node_modules/@dougharness/flow/lib/memory.mjs +1641 -0
  87. package/node_modules/@dougharness/flow/lib/models.mjs +148 -0
  88. package/node_modules/@dougharness/flow/lib/plan.mjs +773 -0
  89. package/node_modules/@dougharness/flow/lib/rehearse.mjs +825 -0
  90. package/node_modules/@dougharness/flow/lib/worktrees.mjs +215 -0
  91. package/node_modules/@dougharness/flow/package.json +19 -0
  92. package/node_modules/@dougharness/flow/scripts/board.mjs +260 -0
  93. package/node_modules/@dougharness/flow/scripts/cost.mjs +66 -0
  94. package/node_modules/@dougharness/flow/scripts/learn.mjs +161 -0
  95. package/node_modules/@dougharness/flow/scripts/memory.mjs +1274 -0
  96. package/node_modules/@dougharness/flow/scripts/plan.mjs +547 -0
  97. package/node_modules/@dougharness/flow/scripts/rehearse.mjs +129 -0
  98. package/node_modules/@dougharness/flow/skills/core-next/SKILL.md +92 -0
  99. package/node_modules/@dougharness/flow/skills/doug-approve/SKILL.md +15 -0
  100. package/node_modules/@dougharness/flow/skills/doug-decide/SKILL.md +26 -0
  101. package/node_modules/@dougharness/flow/skills/doug-implement/SKILL.md +22 -0
  102. package/node_modules/@dougharness/flow/skills/doug-learn/SKILL.md +20 -0
  103. package/node_modules/@dougharness/flow/skills/doug-next/SKILL.md +87 -0
  104. package/node_modules/@dougharness/flow/skills/doug-plan/SKILL.md +22 -0
  105. package/node_modules/@dougharness/flow/skills/doug-swarm/SKILL.md +55 -0
  106. package/node_modules/@dougharness/flow/skills/harness-fix/SKILL.md +85 -0
  107. package/node_modules/@dougharness/flow/skills/research/SKILL.md +41 -0
  108. package/node_modules/@dougharness/flow/skills/run-report/SKILL.md +51 -0
  109. package/node_modules/@dougharness/flow/skills/swarm-launch/SKILL.md +27 -0
  110. package/node_modules/@dougharness/flow/workflows/doug-implement.js +2687 -0
  111. package/node_modules/@dougharness/gates/.claude-plugin/plugin.json +10 -0
  112. package/node_modules/@dougharness/gates/LICENSE +21 -0
  113. package/node_modules/@dougharness/gates/hooks/hooks.json +184 -0
  114. package/node_modules/@dougharness/gates/lib/agent-context.mjs +111 -0
  115. package/node_modules/@dougharness/gates/lib/anchor.mjs +110 -0
  116. package/node_modules/@dougharness/gates/lib/baseline.mjs +160 -0
  117. package/node_modules/@dougharness/gates/lib/bash-rules.mjs +1522 -0
  118. package/node_modules/@dougharness/gates/lib/block-ledger.mjs +127 -0
  119. package/node_modules/@dougharness/gates/lib/checkpoint.mjs +97 -0
  120. package/node_modules/@dougharness/gates/lib/config.mjs +168 -0
  121. package/node_modules/@dougharness/gates/lib/evidence.mjs +59 -0
  122. package/node_modules/@dougharness/gates/lib/glob.mjs +86 -0
  123. package/node_modules/@dougharness/gates/lib/io.mjs +91 -0
  124. package/node_modules/@dougharness/gates/lib/paths.mjs +101 -0
  125. package/node_modules/@dougharness/gates/lib/proposals.mjs +201 -0
  126. package/node_modules/@dougharness/gates/lib/run.mjs +147 -0
  127. package/node_modules/@dougharness/gates/lib/scope.mjs +117 -0
  128. package/node_modules/@dougharness/gates/lib/secret-rules.mjs +268 -0
  129. package/node_modules/@dougharness/gates/lib/state.mjs +114 -0
  130. package/node_modules/@dougharness/gates/lib/trace.mjs +196 -0
  131. package/node_modules/@dougharness/gates/package.json +17 -0
  132. package/node_modules/@dougharness/gates/scripts/edit-loop.mjs +39 -0
  133. package/node_modules/@dougharness/gates/scripts/format-on-edit.mjs +45 -0
  134. package/node_modules/@dougharness/gates/scripts/guard-bash.mjs +32 -0
  135. package/node_modules/@dougharness/gates/scripts/protect-paths.mjs +49 -0
  136. package/node_modules/@dougharness/gates/scripts/reanchor.mjs +81 -0
  137. package/node_modules/@dougharness/gates/scripts/research-cap.mjs +295 -0
  138. package/node_modules/@dougharness/gates/scripts/secret-scan.mjs +26 -0
  139. package/node_modules/@dougharness/gates/scripts/session-start-baseline.mjs +57 -0
  140. package/node_modules/@dougharness/gates/scripts/statusline.mjs +133 -0
  141. package/node_modules/@dougharness/gates/scripts/stop-gate.mjs +1422 -0
  142. package/node_modules/@dougharness/gates/scripts/trace.mjs +75 -0
  143. package/package.json +33 -0
  144. package/templates/board-app.js +533 -0
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env node
2
+ // CLI for deterministic proposals from the outcomes/lessons store and the run trace (card learn-signals), on
3
+ // top of lib/learn.mjs. Log-then-propose, never auto-apply: `propose` only ever writes under
4
+ // .doug/.state/learn/, never a tracked file; `apply` is the one command that ever touches the checkout, and
5
+ // only for a proposal file named on the command line.
6
+ // learn.mjs signals [dir] [--json]
7
+ // prints the signal summary (outcomes, lessons, trace), one
8
+ // block per source, counts only
9
+ // learn.mjs propose [dir] [--json]
10
+ // collects signals, renders each candidate change as a unified
11
+ // diff, writes them under .doug/.state/learn/<timestamp>/, and
12
+ // prints one line per proposal ("no proposals" and exit 0 when
13
+ // there are none)
14
+ // learn.mjs apply <proposal-file> [dir] [--json]
15
+ // applies ONE proposal's diff with `git apply`; exit 1 with the
16
+ // reason when refused (a protected target, or a diff that no
17
+ // longer applies cleanly). Never called by `propose`.
18
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
19
+ import { dirname, join, resolve } from "node:path";
20
+ import { fileURLToPath } from "node:url";
21
+
22
+ const here = dirname(fileURLToPath(import.meta.url));
23
+
24
+ const USAGE = "usage: learn.mjs <signals|propose|apply> ...\n" + " learn.mjs signals [dir] [--json]\n" + " learn.mjs propose [dir] [--json]\n" + " learn.mjs apply <proposal-file> [dir] [--json]\n";
25
+
26
+ function fail(msg, code = 1) {
27
+ process.stderr.write(`${msg}\n`);
28
+ process.exit(code);
29
+ }
30
+
31
+ const argv = process.argv.slice(2);
32
+ const opts = {};
33
+ const positional = [];
34
+ for (const a of argv) {
35
+ if (a.startsWith("--")) {
36
+ const name = a.slice(2);
37
+ if (name !== "json") fail(`unknown flag --${name}\n\n${USAGE}`, 2);
38
+ opts.json = true;
39
+ continue;
40
+ }
41
+ positional.push(a);
42
+ }
43
+ const [cmd, ...rest] = positional;
44
+
45
+ function dirFrom(args, n) {
46
+ return resolve(process.env.CLAUDE_PROJECT_DIR || args[n] || process.cwd());
47
+ }
48
+
49
+ function readClaudeMd(dir) {
50
+ const file = join(dir, "CLAUDE.md");
51
+ return existsSync(file) ? readFileSync(file, "utf8") : "";
52
+ }
53
+
54
+ function readConfig(dir) {
55
+ const file = join(dir, ".doug/config.json");
56
+ if (!existsSync(file)) return {};
57
+ try {
58
+ return JSON.parse(readFileSync(file, "utf8"));
59
+ } catch {
60
+ return {};
61
+ }
62
+ }
63
+
64
+ // Mirrors template.test.mjs's parseFrontmatter: a single "---\n...\n---" block, one "key: value" per line.
65
+ // Adequate for name/description, the only two fields `tighten` looks at.
66
+ function parseFrontmatter(text) {
67
+ const m = /^---\n([\s\S]*?)\n---/.exec(text);
68
+ if (!m) return null;
69
+ const out = {};
70
+ for (const line of m[1].split("\n")) {
71
+ const kv = /^([A-Za-z_-]+):\s*(.*)$/.exec(line);
72
+ if (kv) out[kv[1]] = kv[2].trim();
73
+ }
74
+ return out;
75
+ }
76
+
77
+ // Every doug-flow skill this plugin ships (../skills, relative to this script — the plugin's own skills, not
78
+ // the project under analysis): { name, description, file } for `tighten` to weigh against trace invocation
79
+ // counts. `file` (review MINOR 7) is the real absolute path this skill's SKILL.md was read from, so
80
+ // proposeChanges can point a tighten proposal's `target` at the file it actually looked at instead of a
81
+ // hardcoded guess.
82
+ function readOwnSkills() {
83
+ const skillsDir = join(here, "..", "skills");
84
+ if (!existsSync(skillsDir)) return [];
85
+ const out = [];
86
+ for (const name of readdirSync(skillsDir)) {
87
+ const file = join(skillsDir, name, "SKILL.md");
88
+ if (!existsSync(file)) continue;
89
+ const fm = parseFrontmatter(readFileSync(file, "utf8"));
90
+ if (fm) out.push({ name: fm.name || name, description: fm.description || "", file });
91
+ }
92
+ return out;
93
+ }
94
+
95
+ function signalsText(s) {
96
+ return [
97
+ `outcomes: ${s.outcomes.rows.length} rows, ${s.outcomes.gateFailures} gate failures, blocks real=${s.outcomes.blocksByClass.real} marginal=${s.outcomes.blocksByClass.marginal} false=${s.outcomes.blocksByClass.false}, fixPasses sum=${s.outcomes.fixPasses.sum} max=${s.outcomes.fixPasses.max}, usd sum=${s.outcomes.usd.sum.toFixed(2)} n=${s.outcomes.usd.n}, cards=${Object.keys(s.outcomes.byCard).length}`,
98
+ `lessons: ${s.lessons.total} total, ${s.lessons.live} live, ${s.lessons.helpful} helpful, ${s.lessons.harmful} harmful, ${s.lessons.stale} stale, ${s.lessons.repeated.length} repeated`,
99
+ `trace: ${s.trace.files.length} files, ${s.trace.denials.length} denials, ${s.trace.unmatched.length} unmatched (reported only, never a proposal signal), ${s.trace.skills.length} skills invoked, ${s.trace.instructionsLoaded.length} instruction files loaded`,
100
+ ].join("\n");
101
+ }
102
+
103
+ try {
104
+ switch (cmd) {
105
+ case "signals": {
106
+ const dir = dirFrom(rest, 0);
107
+ const { openMemory } = await import("../lib/memory.mjs");
108
+ const { collectSignals } = await import("../lib/learn.mjs");
109
+ const m = openMemory(dir);
110
+ let signals;
111
+ try {
112
+ signals = collectSignals(m, { dir });
113
+ } finally {
114
+ m.close();
115
+ }
116
+ process.stdout.write(opts.json ? `${JSON.stringify(signals, null, 2)}\n` : `${signalsText(signals)}\n`);
117
+ break;
118
+ }
119
+ case "propose": {
120
+ const dir = dirFrom(rest, 0);
121
+ const { openMemory } = await import("../lib/memory.mjs");
122
+ const { collectSignals, proposeChanges, writeProposals } = await import("../lib/learn.mjs");
123
+ const m = openMemory(dir);
124
+ let signals;
125
+ try {
126
+ signals = collectSignals(m, { dir });
127
+ } finally {
128
+ m.close();
129
+ }
130
+ const proposals = proposeChanges(signals, { dir, claudeMd: readClaudeMd(dir), config: readConfig(dir), skills: readOwnSkills() });
131
+ const written = proposals.length ? writeProposals(proposals, { dir }) : null;
132
+ if (opts.json) {
133
+ process.stdout.write(`${JSON.stringify({ proposals, dir: written ? written.dir : null }, null, 2)}\n`);
134
+ } else if (!proposals.length) {
135
+ process.stdout.write("no proposals\n");
136
+ } else {
137
+ for (const p of proposals) process.stdout.write(`${p.id} ${p.kind} ${p.target}: ${p.reason}\n`);
138
+ process.stdout.write(`written to ${written.dir}\n`);
139
+ }
140
+ break;
141
+ }
142
+ case "apply": {
143
+ if (!rest[0]) fail(USAGE, 2);
144
+ const file = resolve(rest[0]);
145
+ const dir = dirFrom(rest, 1);
146
+ const { applyProposal } = await import("../lib/learn.mjs");
147
+ const result = applyProposal(file, { dir });
148
+ if (!result.ok) {
149
+ if (opts.json) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
150
+ process.stderr.write(`${result.reason}\n`);
151
+ process.exit(1);
152
+ }
153
+ process.stdout.write(opts.json ? `${JSON.stringify(result, null, 2)}\n` : `applied ${result.target}\n`);
154
+ break;
155
+ }
156
+ default:
157
+ fail(USAGE, 2);
158
+ }
159
+ } catch (err) {
160
+ fail(err.message);
161
+ }