simt-dev 0.2.0__py3-none-any.whl

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 (138) hide show
  1. python/simt_dev/__init__.py +32 -0
  2. python/simt_dev/cli.py +225 -0
  3. simt_dev/__init__.py +32 -0
  4. simt_dev/cli.py +225 -0
  5. simt_dev/vendored/CHANGELOG.md +166 -0
  6. simt_dev/vendored/bin/simt-dev.mjs +28 -0
  7. simt_dev/vendored/docs/decisions/ADR-001-push-requires-approval.md +42 -0
  8. simt_dev/vendored/docs/decisions/ADR-002-refactor-blast-radius.md +45 -0
  9. simt_dev/vendored/docs/decisions/ADR-003-test-breadth.md +46 -0
  10. simt_dev/vendored/docs/decisions/ADR-004-hook-owns-format.md +45 -0
  11. simt_dev/vendored/docs/decisions/ADR-005-file-length.md +42 -0
  12. simt_dev/vendored/docs/decisions/ADR-006-coverage-bar.md +59 -0
  13. simt_dev/vendored/docs/decisions/ADR-007-section-skeleton.md +54 -0
  14. simt_dev/vendored/docs/decisions/ADR-008-verdict-vocabulary.md +59 -0
  15. simt_dev/vendored/docs/decisions/ADR-009-skill-layout.md +48 -0
  16. simt_dev/vendored/docs/decisions/ADR-010-runner-invocation.md +56 -0
  17. simt_dev/vendored/docs/decisions/ADR-011-windows-support.md +52 -0
  18. simt_dev/vendored/docs/decisions/ADR-012-drop-windows-support.md +59 -0
  19. simt_dev/vendored/docs/decisions/ADR-013-genericity-by-facts-and-local-profiles.md +114 -0
  20. simt_dev/vendored/docs/decisions/ADR-014-workspace-is-configuration.md +211 -0
  21. simt_dev/vendored/docs/decisions/ADR-015-a-source-is-content-not-a-location.md +117 -0
  22. simt_dev/vendored/docs/decisions/ADR-016-a-divergent-rule-is-retired-not-merged.md +180 -0
  23. simt_dev/vendored/docs/decisions/ADR-017-a-declared-gate-must-be-runnable.md +89 -0
  24. simt_dev/vendored/docs/decisions/ADR-018-an-unset-hooks-path-is-not-an-absent-hook.md +183 -0
  25. simt_dev/vendored/docs/decisions/ADR-019-a-declared-command-is-the-runner.md +110 -0
  26. simt_dev/vendored/docs/decisions/ADR-020-a-rule-is-written-per-surface-and-pointed-not-copied.md +100 -0
  27. simt_dev/vendored/docs/decisions/ADR-021-one-surface-per-repo.md +94 -0
  28. simt_dev/vendored/docs/decisions/ADR-022-a-key-simt-dev-reads-is-prefixed.md +98 -0
  29. simt_dev/vendored/docs/decisions/ADR-023-the-gate-ships-as-a-file-the-repo-owns.md +106 -0
  30. simt_dev/vendored/docs/decisions/ADR-024-simt-dev-overwrites-its-own-gate-file.md +111 -0
  31. simt_dev/vendored/docs/decisions/ADR-025-a-baseline-is-a-region-in-a-file-the-repo-owns.md +153 -0
  32. simt_dev/vendored/docs/decisions/ADR-026-a-config-without-a-region-is-a-file-the-repo-owns.md +117 -0
  33. simt_dev/vendored/docs/decisions/ADR-027-the-one-gitattributes-line-that-is-neither-dangerous-nor-empty.md +130 -0
  34. simt_dev/vendored/docs/decisions/ADR-028-a-base-file-is-a-floor-so-the-disagreement-it-cannot-repair-becomes-a-check.md +131 -0
  35. simt_dev/vendored/docs/decisions/ADR-029-the-pins-a-repository-declares-are-its-own.md +120 -0
  36. simt_dev/vendored/docs/decisions/ADR-030-what-a-plan-does-not-ship-is-a-decision-with-a-record.md +114 -0
  37. simt_dev/vendored/docs/decisions/ADR-031-the-suite-stages-the-environment-it-needs.md +129 -0
  38. simt_dev/vendored/docs/decisions/ADR-032-provenance-is-a-content-digest-not-a-commit.md +141 -0
  39. simt_dev/vendored/docs/decisions/ADR-033-three-plan-rows-closed-by-decision-not-by-authoring.md +82 -0
  40. simt_dev/vendored/docs/decisions/ADR-034-a-staged-tool-refuses-a-partially-staged-file.md +91 -0
  41. simt_dev/vendored/docs/decisions/ADR-035-one-write-decision-and-one-json-document.md +119 -0
  42. simt_dev/vendored/docs/decisions/ADR-036-a-writer-that-writes-says-so-and-the-instructions-naming-it-move-with-it.md +103 -0
  43. simt_dev/vendored/docs/decisions/ADR-037-unchanged-means-writing-would-not-change-a-byte.md +100 -0
  44. simt_dev/vendored/docs/decisions/ADR-038-the-gate-filter-belongs-to-every-manifest-writer.md +95 -0
  45. simt_dev/vendored/docs/decisions/ADR-039-a-ci-step-runs-a-form-that-can-fail.md +101 -0
  46. simt_dev/vendored/docs/decisions/ADR-040-the-last-writer-that-wrote-by-default-was-not-the-one-named.md +158 -0
  47. simt_dev/vendored/docs/decisions/ADR-041-the-suite-installs-the-package-in-order-to-test-it.md +92 -0
  48. simt_dev/vendored/docs/decisions/ADR-042-a-declared-glob-must-match-a-path-built-from-it.md +94 -0
  49. simt_dev/vendored/docs/decisions/ADR-043-a-keyword-is-a-phrase-and-two-letters-is-a-word.md +90 -0
  50. simt_dev/vendored/docs/decisions/ADR-044-compatibility-is-not-evidence.md +116 -0
  51. simt_dev/vendored/docs/decisions/ADR-045-a-dashboard-that-counts-a-defect.md +119 -0
  52. simt_dev/vendored/docs/decisions/ADR-046-one-rule-one-copy.md +140 -0
  53. simt_dev/vendored/docs/decisions/ADR-047-a-profile-selects-on-what-a-repository-runs-on.md +141 -0
  54. simt_dev/vendored/docs/decisions/ADR-048-a-baseline-carries-only-keys-a-reader-honours.md +150 -0
  55. simt_dev/vendored/docs/decisions/ADR-049-a-ruling-that-a-later-record-moved-says-so-where-it-is-printed.md +153 -0
  56. simt_dev/vendored/docs/decisions/ADR-050-a-byte-count-is-not-a-region-and-updated-is-not-a-change-of-rules.md +102 -0
  57. simt_dev/vendored/docs/decisions/ADR-051-a-pointer-is-the-readers-only-route-so-it-is-not-a-convenience.md +134 -0
  58. simt_dev/vendored/docs/decisions/ADR-052-a-dependency-is-counted-against-the-budget-and-there-is-one-budget.md +101 -0
  59. simt_dev/vendored/docs/decisions/ADR-053-the-node-floor-is-22-20-0.md +49 -0
  60. simt_dev/vendored/docs/decisions/ADR-054-the-digest-covers-every-byte-that-leaves-the-package.md +100 -0
  61. simt_dev/vendored/docs/decisions/ADR-055-a-status-line-travels-so-it-may-not-name-a-location.md +98 -0
  62. simt_dev/vendored/docs/decisions/ADR-056-three-names-one-of-which-is-cheap-to-move.md +160 -0
  63. simt_dev/vendored/docs/decisions/ADR-057-an-identity-is-per-developer-and-a-repository-does-not-own-it.md +115 -0
  64. simt_dev/vendored/docs/decisions/ADR-058-one-tool-three-channels-and-the-order-is-forced.md +148 -0
  65. simt_dev/vendored/docs/decisions/README.md +530 -0
  66. simt_dev/vendored/graph/skills.graph.json +1622 -0
  67. simt_dev/vendored/package.json +43 -0
  68. simt_dev/vendored/rules/core.md +76 -0
  69. simt_dev/vendored/rules/mdc/intake-questions.mdc +50 -0
  70. simt_dev/vendored/rules/mdc/preview-postflight.mdc +61 -0
  71. simt_dev/vendored/rules/mdc/slice-commit.mdc +52 -0
  72. simt_dev/vendored/rules/mdc/test-gate.mdc +54 -0
  73. simt_dev/vendored/rules/stacks/android.md +63 -0
  74. simt_dev/vendored/rules/stacks/cloudflare-workers.md +65 -0
  75. simt_dev/vendored/rules/stacks/connectiq.md +62 -0
  76. simt_dev/vendored/rules/stacks/container.md +31 -0
  77. simt_dev/vendored/rules/stacks/jvm.md +54 -0
  78. simt_dev/vendored/rules/stacks/node.md +45 -0
  79. simt_dev/vendored/rules/stacks/python.md +48 -0
  80. simt_dev/vendored/rules/stacks/react-native.md +58 -0
  81. simt_dev/vendored/rules/stacks/react-web.md +61 -0
  82. simt_dev/vendored/rules/stacks/typescript.md +21 -0
  83. simt_dev/vendored/rules/stacks/uv.md +32 -0
  84. simt_dev/vendored/src/argv.mjs +116 -0
  85. simt_dev/vendored/src/ci/dependabot.mjs +268 -0
  86. simt_dev/vendored/src/ci/workflow.mjs +360 -0
  87. simt_dev/vendored/src/commands/ci.mjs +285 -0
  88. simt_dev/vendored/src/commands/compare.mjs +195 -0
  89. simt_dev/vendored/src/commands/graph.mjs +73 -0
  90. simt_dev/vendored/src/commands/help.mjs +108 -0
  91. simt_dev/vendored/src/commands/hooks.mjs +471 -0
  92. simt_dev/vendored/src/commands/index.mjs +241 -0
  93. simt_dev/vendored/src/commands/init.mjs +632 -0
  94. simt_dev/vendored/src/commands/mcp.mjs +63 -0
  95. simt_dev/vendored/src/commands/rules.mjs +374 -0
  96. simt_dev/vendored/src/commands/skills.mjs +647 -0
  97. simt_dev/vendored/src/commands/skillsinstall.mjs +515 -0
  98. simt_dev/vendored/src/commands/sync.mjs +331 -0
  99. simt_dev/vendored/src/commands/upgrade.mjs +202 -0
  100. simt_dev/vendored/src/compare/discover.mjs +212 -0
  101. simt_dev/vendored/src/compare/hooksstate.mjs +159 -0
  102. simt_dev/vendored/src/compare/row.mjs +299 -0
  103. simt_dev/vendored/src/decisions/index.mjs +536 -0
  104. simt_dev/vendored/src/graph/load.mjs +541 -0
  105. simt_dev/vendored/src/hooks/capability.mjs +129 -0
  106. simt_dev/vendored/src/hooks/dispatcher.mjs +268 -0
  107. simt_dev/vendored/src/hooks/gates.mjs +144 -0
  108. simt_dev/vendored/src/hooks/resolve.mjs +194 -0
  109. simt_dev/vendored/src/hooks/shim.mjs +140 -0
  110. simt_dev/vendored/src/hooks/where.mjs +118 -0
  111. simt_dev/vendored/src/identity.mjs +218 -0
  112. simt_dev/vendored/src/locate.mjs +75 -0
  113. simt_dev/vendored/src/mcp/server.mjs +167 -0
  114. simt_dev/vendored/src/mcp/tools.mjs +410 -0
  115. simt_dev/vendored/src/name.mjs +95 -0
  116. simt_dev/vendored/src/output/index.mjs +96 -0
  117. simt_dev/vendored/src/projection/baselines.mjs +209 -0
  118. simt_dev/vendored/src/projection/conflicts.mjs +205 -0
  119. simt_dev/vendored/src/projection/manifest.mjs +478 -0
  120. simt_dev/vendored/src/projection/markers.mjs +349 -0
  121. simt_dev/vendored/src/projection/mdc.mjs +327 -0
  122. simt_dev/vendored/src/projection/project.mjs +504 -0
  123. simt_dev/vendored/src/projection/rules.mjs +640 -0
  124. simt_dev/vendored/src/projection/surfaces.mjs +398 -0
  125. simt_dev/vendored/src/projection/targets.mjs +221 -0
  126. simt_dev/vendored/src/resolve/fingerprint.mjs +553 -0
  127. simt_dev/vendored/src/resolve/near.mjs +61 -0
  128. simt_dev/vendored/src/resolve/resolve.mjs +457 -0
  129. simt_dev/vendored/src/resolve/source.mjs +105 -0
  130. simt_dev/vendored/src/toolchain.mjs +202 -0
  131. simt_dev/vendored/src/tsconfig.mjs +314 -0
  132. simt_dev/vendored/src/upgrade/plan.mjs +203 -0
  133. simt_dev/vendored/src/workspace.mjs +289 -0
  134. simt_dev-0.2.0.dist-info/METADATA +150 -0
  135. simt_dev-0.2.0.dist-info/RECORD +138 -0
  136. simt_dev-0.2.0.dist-info/WHEEL +4 -0
  137. simt_dev-0.2.0.dist-info/entry_points.txt +2 -0
  138. simt_dev-0.2.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,241 @@
1
+ // Command registry and dispatch.
2
+ //
3
+ // Every command named in docs/plans/0002-simt-dev-cli.md section 5 is registered
4
+ // here from day one. A command that is declared but not yet implemented exits
5
+ // UNSUPPORTED with a pointer to its implementation step; it never silently
6
+ // succeeds. An agent that calls `simt-dev compare` on an unreleased build must get a
7
+ // failure, not an empty result it will read as "no drift".
8
+
9
+ import { EXIT, emit, fail, isJson } from '../output/index.mjs';
10
+ import { help } from './help.mjs';
11
+ import { lintCommand } from './graph.mjs';
12
+ import {
13
+ indexCommand as skillsIndex,
14
+ findCommand as skillsFind,
15
+ briefCommand as skillsBrief,
16
+ showCommand as skillsShow,
17
+ whyCommand as skillsWhy,
18
+ } from './skills.mjs';
19
+ import { syncCommand, checkCommand } from './sync.mjs';
20
+ import { initCommand } from './init.mjs';
21
+ import { ciCommand } from './ci.mjs';
22
+ import { hooksCommand } from './hooks.mjs';
23
+ import { compareCommand } from './compare.mjs';
24
+ import { upgradeCommand } from './upgrade.mjs';
25
+ import { mcpCommand } from './mcp.mjs';
26
+ import { installCommand } from './skillsinstall.mjs';
27
+ import { listCommand as rulesList, showCommand as rulesShow, whyCommand as rulesWhy } from './rules.mjs';
28
+ import { parse } from '../argv.mjs';
29
+ import { fingerprint } from '../resolve/fingerprint.mjs';
30
+ import { resolveWorkspace } from '../workspace.mjs';
31
+
32
+ /** command path -> implementation sequence step (plan 0002 section 11). */
33
+ // Every command named in plan 0002 section 5 is now implemented, so this map is empty.
34
+ // It stays, rather than being deleted, because the next command should be added here
35
+ // first: a declared-but-unimplemented command must exit UNSUPPORTED with a pointer to
36
+ // its step, never silently succeed with empty output an agent will read as a result.
37
+ const STEPS = new Map([]);
38
+
39
+ /** groups whose subcommands may be omitted, and the default for each. */
40
+ const GROUPS = new Map([
41
+ ['skills', 'index'],
42
+ ['graph', 'lint'],
43
+ ['rules', 'list'],
44
+ ['hooks', 'status'],
45
+ ]);
46
+
47
+ export async function dispatch(argv) {
48
+ const { flags, positional } = parseOrExit(argv);
49
+
50
+ // Help is shown only when explicitly asked for. An unknown command must fail with
51
+ // a usage code, not print 40 lines of prose and exit 0 — an agent that mistyped
52
+ // needs to be told so, not handed a wall of text it may misread as output.
53
+ if (flags.help || flags.version || positional[0] === 'help') {
54
+ return help(argv);
55
+ }
56
+
57
+ const [first, second] = positional;
58
+ const path = commandPath(first, second);
59
+ // Arguments after the command path. A group consumed two tokens (`graph lint`),
60
+ // a bare command one, so the offset depends on the shape that resolved.
61
+ const offset = GROUPS.has(first) && second !== undefined ? 2 : 1;
62
+
63
+ if (path === null) {
64
+ return fail(`unknown command \`${[first, second].filter(Boolean).join(' ')}\``, EXIT.USAGE);
65
+ }
66
+
67
+ const step = STEPS.get(path);
68
+ if (step !== undefined) {
69
+ // Two things a reader must be able to act on, and neither may name a path inside
70
+ // simt-dev: they run this tool, not its source tree. The step number is the
71
+ // stable handle; the file that documents it is an implementation detail.
72
+ return fail(
73
+ `\`simt-dev ${path}\` is declared but not implemented`,
74
+ EXIT.UNSUPPORTED,
75
+ `implementation step ${step} is not built yet`,
76
+ );
77
+ }
78
+ const args = positional.slice(offset);
79
+ const IMPLEMENTED = {
80
+ doctor: runDoctor,
81
+ 'graph lint': lintCommand,
82
+ 'skills index': skillsIndex,
83
+ 'skills find': skillsFind,
84
+ 'skills brief': skillsBrief,
85
+ 'skills show': skillsShow,
86
+ 'skills why': skillsWhy,
87
+ 'skills install': installCommand,
88
+ 'rules list': rulesList,
89
+ 'rules show': rulesShow,
90
+ 'rules why': rulesWhy,
91
+ sync: syncCommand,
92
+ check: checkCommand,
93
+ init: initCommand,
94
+ ci: ciCommand,
95
+ 'hooks install': (args, flags) => hooksCommand('install', args, flags),
96
+ 'hooks status': (args, flags) => hooksCommand('status', args, flags),
97
+ 'hooks run': (args, flags) => hooksCommand('run', args, flags),
98
+ compare: compareCommand,
99
+ upgrade: upgradeCommand,
100
+ mcp: mcpCommand,
101
+ };
102
+ const run = IMPLEMENTED[path];
103
+ if (run) return run(args, flags);
104
+ return fail(`unknown command \`${path}\``, EXIT.USAGE);
105
+ }
106
+
107
+ /**
108
+ * Normalise argv into a registry key, or null when unrecognised.
109
+ * A group with an omitted subcommand resolves to that group's default, so
110
+ * `simt-dev skills` is `simt-dev skills index`.
111
+ */
112
+ function commandPath(first, second) {
113
+ if (first === undefined) return null;
114
+ if (!GROUPS.has(first)) return first;
115
+
116
+ const group = GROUPS.get(first);
117
+ if (second === undefined) return `${first} ${group}`;
118
+ // Reject a second token that is not a subcommand of this group, rather than
119
+ // silently routing `simt-dev skills nonsense` to the default subcommand.
120
+ const key = `${first} ${second}`;
121
+ if (!STEPS.has(key) && !KNOWN_SUBS.has(key)) {
122
+ return null;
123
+ }
124
+ return key;
125
+ }
126
+
127
+ /**
128
+ * Subcommands implemented now, so they must not be rejected as unknown.
129
+ * Implemented paths live here; declared-but-unimplemented ones live in STEPS.
130
+ */
131
+ const KNOWN_SUBS = new Set([
132
+ 'doctor',
133
+ 'graph lint',
134
+ 'skills index',
135
+ 'skills find',
136
+ 'skills brief',
137
+ 'skills show',
138
+ 'skills why',
139
+ 'skills install',
140
+ 'rules list',
141
+ 'rules show',
142
+ 'rules why',
143
+ 'hooks install',
144
+ 'hooks status',
145
+ 'hooks run',
146
+ ]);
147
+
148
+ function parseOrExit(argv) {
149
+ const parsed = parse(argv);
150
+ if (!parsed.ok) return { flags: {}, positional: [] };
151
+ return parsed.value;
152
+ }
153
+
154
+ /**
155
+ * Step 3 of the implementation sequence: classify a repo from marker files alone.
156
+ * Deliberately shipped early — it needs no registry, no rules and no projection,
157
+ * and it makes the tool useful before any content exists.
158
+ */
159
+ async function runDoctor(positional, flags) {
160
+ const root = flags.root ?? positional[0] ?? process.cwd();
161
+ const result = await fingerprint(root);
162
+ // Which source answered the workspace question, and where it looked. A tool
163
+ // that silently fell back to the wrong directory would make every downstream
164
+ // answer confidently wrong, so the answer is reported rather than inferred
165
+ // (ADR-014).
166
+ const ws = await resolveWorkspace(flags);
167
+
168
+ // Under `--json` the document is the whole report, so it has to carry both
169
+ // failures the text path reports AND the workspace answer the text path prints.
170
+ // This branch used to return before either check, with `ok` hardcoded true: a
171
+ // caller asking whether their config is readable was told yes, and given exit
172
+ // 0, while the same command in a terminal exited 3 and named the file. A
173
+ // scripted consumer has only this document to go on, so `ok` is derived from
174
+ // the exit code rather than written as a constant (S10).
175
+ const failure = result.error
176
+ ? { error: `${root}: ${result.error}`, remedy: null }
177
+ : ws.error
178
+ ? {
179
+ error: `workspace config ${ws.detail}: ${ws.error}`,
180
+ remedy: 'fix the JSON, or remove the file',
181
+ }
182
+ : null;
183
+ const code = failure ? EXIT.CONFIG : EXIT.OK;
184
+
185
+ if (isJson(flags)) {
186
+ emit(
187
+ `${JSON.stringify(
188
+ {
189
+ ok: code === EXIT.OK,
190
+ command: 'doctor',
191
+ root,
192
+ ...result,
193
+ // The workspace answer is a fact about which directory the tool would
194
+ // act on next (ADR-014). It is printed in the text report; leaving it
195
+ // out of the JSON meant a machine-readable caller could not see it at
196
+ // all, which is the one-question-two-answers defect again.
197
+ workspace: {
198
+ root: ws.root ?? null,
199
+ source: ws.source,
200
+ detail: ws.detail,
201
+ error: ws.error ?? null,
202
+ },
203
+ ...(failure ?? {}),
204
+ },
205
+ null,
206
+ 2,
207
+ )}\n`,
208
+ );
209
+ return code;
210
+ }
211
+ if (result.error) {
212
+ return fail(`${root}: ${result.error}`, EXIT.CONFIG);
213
+ }
214
+ emit(
215
+ [
216
+ `root ${root}`,
217
+ `stack ${result.stack.length > 0 ? result.stack.join(', ') : '(undetected)'}`,
218
+ `evidence ${result.evidence.length > 0 ? result.evidence.join(' ') : '(none)'}`,
219
+ // Facts are what a profile selects on when it wants to survive the
220
+ // framework being replaced, so they are reported with the path that proved
221
+ // each one rather than as a bare list (ADR-013).
222
+ `facts ${
223
+ result.facts.length > 0
224
+ ? result.facts.map((f) => `${f}(${result.factEvidence[f] ?? '?'})`).join(' ')
225
+ : '(none)'
226
+ }`,
227
+ `manifest ${result.manifest ? (result.manifest.error ?? result.manifest.id ?? 'present') : '(absent)'}`,
228
+ `workspace ${ws.root ?? '(none)'} (${ws.source}: ${ws.detail})`,
229
+ ].join('\n'),
230
+ { maxBytes: flags.maxBytes ?? 0 },
231
+ );
232
+ // An unconfigured workspace is a fact, not a failure: a single repo with no
233
+ // workspace around it is a normal, supported case. A config file that EXISTS and
234
+ // does not parse is a real mistake, and that does fail - with the parse error,
235
+ // because naming the file without saying what is wrong with it sends the reader
236
+ // to the file and gives them nothing to look for.
237
+ if (ws.error) {
238
+ return fail(`workspace config ${ws.detail}: ${ws.error}`, EXIT.CONFIG, 'fix the JSON, or remove the file');
239
+ }
240
+ return EXIT.OK;
241
+ }