@ikunin/sprintpilot 1.0.5 → 2.0.5
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/README.md +48 -1
- package/_Sprintpilot/Sprintpilot.md +14 -1
- package/_Sprintpilot/manifest.yaml +1 -1
- package/_Sprintpilot/modules/autopilot/config.yaml +22 -0
- package/_Sprintpilot/modules/autopilot/profiles/_base.yaml +45 -0
- package/_Sprintpilot/modules/autopilot/profiles/large.yaml +22 -0
- package/_Sprintpilot/modules/autopilot/profiles/legacy.yaml +35 -0
- package/_Sprintpilot/modules/autopilot/profiles/medium.yaml +5 -0
- package/_Sprintpilot/modules/autopilot/profiles/nano.yaml +35 -0
- package/_Sprintpilot/modules/autopilot/profiles/small.yaml +5 -0
- package/_Sprintpilot/modules/git/config.yaml +8 -0
- package/_Sprintpilot/modules/ma/config.yaml +42 -0
- package/_Sprintpilot/scripts/agent-adapter.js +247 -0
- package/_Sprintpilot/scripts/cached-read.js +238 -0
- package/_Sprintpilot/scripts/check-prereqs.js +139 -0
- package/_Sprintpilot/scripts/dispatch-layer.js +192 -0
- package/_Sprintpilot/scripts/git-portable.js +219 -0
- package/_Sprintpilot/scripts/infer-dependencies.js +594 -0
- package/_Sprintpilot/scripts/inject-tasks-section.js +279 -0
- package/_Sprintpilot/scripts/list-remaining-stories.js +295 -0
- package/_Sprintpilot/scripts/log-timing.js +425 -0
- package/_Sprintpilot/scripts/mark-done-stories-tasks.js +254 -0
- package/_Sprintpilot/scripts/merge-shards.js +339 -0
- package/_Sprintpilot/scripts/preflight-merge.js +235 -0
- package/_Sprintpilot/scripts/resolve-dag.js +559 -0
- package/_Sprintpilot/scripts/resolve-profile.js +355 -0
- package/_Sprintpilot/scripts/state-shard.js +602 -0
- package/_Sprintpilot/scripts/submodule-lock.js +130 -0
- package/_Sprintpilot/scripts/summarize-timings.js +362 -0
- package/_Sprintpilot/scripts/sync-status.js +13 -0
- package/_Sprintpilot/scripts/with-retry.js +145 -0
- package/_Sprintpilot/skills/sprint-autopilot-on/workflow.md +572 -42
- package/bin/sprintpilot.js +4 -0
- package/lib/commands/install.js +157 -1
- package/package.json +1 -1
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// git-portable.js — cross-platform replacements for POSIX-shell git idioms
|
|
4
|
+
// that workflow.md previously inlined. Every subcommand emits a clean stdout
|
|
5
|
+
// value with no dependency on shell features (no `2>/dev/null`, no `||`, no
|
|
6
|
+
// `$(...)` substitution, no `grep`/`echo`/`true`).
|
|
7
|
+
//
|
|
8
|
+
// Usage:
|
|
9
|
+
// git-portable.js count-worktrees [--project-root <path>]
|
|
10
|
+
// git-portable.js config-get <key> [--default <value>] [--scope local|global|system]
|
|
11
|
+
// [--project-root <path>]
|
|
12
|
+
// git-portable.js common-dir [--project-root <path>]
|
|
13
|
+
// git-portable.js safe-add <path>... [--project-root <path>]
|
|
14
|
+
//
|
|
15
|
+
// Subcommands
|
|
16
|
+
//
|
|
17
|
+
// count-worktrees
|
|
18
|
+
// Replaces: `git worktree list --porcelain 2>/dev/null | grep -c '^worktree '`
|
|
19
|
+
// Stdout: an integer. Exits 0. Falls back to the integer 2 (matching
|
|
20
|
+
// workflow.md's "fail-open to 2 to force the full boot path" semantic)
|
|
21
|
+
// when git fails for any reason.
|
|
22
|
+
//
|
|
23
|
+
// config-get <key> [--default <value>]
|
|
24
|
+
// Replaces: `git config --get <key> 2>/dev/null || echo <value>`
|
|
25
|
+
// Stdout: the config value, or <value> on absence ("unset" if not given).
|
|
26
|
+
// --scope chooses the git config scope (default: local + global cascade,
|
|
27
|
+
// i.e. plain `git config --get`).
|
|
28
|
+
//
|
|
29
|
+
// common-dir
|
|
30
|
+
// Replaces: `GIT_COMMON=$(git -C <root> rev-parse --git-common-dir)`
|
|
31
|
+
// Stdout: the absolute path of the common git directory. Exit 1 on
|
|
32
|
+
// failure (the caller should error out — this is load-bearing for
|
|
33
|
+
// submodule init).
|
|
34
|
+
//
|
|
35
|
+
// safe-add <path>...
|
|
36
|
+
// Replaces: `git add <path1> <path2> ... 2>/dev/null || true`
|
|
37
|
+
// Filters paths to those that exist on disk, then runs `git add` on
|
|
38
|
+
// the survivors only. Emits a JSON summary on stdout:
|
|
39
|
+
// { "added": ["a", "b"], "skipped": ["c"] }
|
|
40
|
+
// Exit 0 always (best-effort by design). When zero paths exist,
|
|
41
|
+
// `added: []` and `git add` is not invoked.
|
|
42
|
+
|
|
43
|
+
const fs = require('node:fs');
|
|
44
|
+
const path = require('node:path');
|
|
45
|
+
const { spawnSync } = require('node:child_process');
|
|
46
|
+
|
|
47
|
+
const { parseArgs } = require('../lib/runtime/args');
|
|
48
|
+
const log = require('../lib/runtime/log');
|
|
49
|
+
|
|
50
|
+
const VALID_COMMANDS = ['count-worktrees', 'config-get', 'common-dir', 'safe-add'];
|
|
51
|
+
const VALID_SCOPES = ['local', 'global', 'system'];
|
|
52
|
+
|
|
53
|
+
function help() {
|
|
54
|
+
log.out(
|
|
55
|
+
[
|
|
56
|
+
'Usage:',
|
|
57
|
+
' git-portable.js count-worktrees [--project-root <path>]',
|
|
58
|
+
' git-portable.js config-get <key> [--default <value>] [--scope local|global|system] [--project-root <path>]',
|
|
59
|
+
' git-portable.js common-dir [--project-root <path>]',
|
|
60
|
+
' git-portable.js safe-add <path>... [--project-root <path>]',
|
|
61
|
+
'',
|
|
62
|
+
'Cross-platform replacements for POSIX-shell git idioms used in workflow.md.',
|
|
63
|
+
'No shell features (pipes, redirects, $(), || ) — safe under cmd.exe and PowerShell.',
|
|
64
|
+
].join('\n'),
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function git(projectRoot, args) {
|
|
69
|
+
return spawnSync('git', ['-C', projectRoot, ...args], {
|
|
70
|
+
encoding: 'utf8',
|
|
71
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
72
|
+
windowsHide: true,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ---------------------------------------------------------------
|
|
77
|
+
// count-worktrees
|
|
78
|
+
// ---------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
function countWorktrees(projectRoot) {
|
|
81
|
+
const res = git(projectRoot, ['worktree', 'list', '--porcelain']);
|
|
82
|
+
if (res.status !== 0) return 2; // fail-open per workflow contract
|
|
83
|
+
const out = String(res.stdout || '');
|
|
84
|
+
let count = 0;
|
|
85
|
+
for (const line of out.split(/\r?\n/)) {
|
|
86
|
+
if (line.startsWith('worktree ')) count++;
|
|
87
|
+
}
|
|
88
|
+
return count;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------
|
|
92
|
+
// config-get
|
|
93
|
+
// ---------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
function configGet(projectRoot, key, { defaultValue, scope }) {
|
|
96
|
+
const args = ['config'];
|
|
97
|
+
if (scope) args.push(`--${scope}`);
|
|
98
|
+
args.push('--get', key);
|
|
99
|
+
const res = git(projectRoot, args);
|
|
100
|
+
if (res.status === 0) {
|
|
101
|
+
return String(res.stdout || '').trim();
|
|
102
|
+
}
|
|
103
|
+
// git config --get exits 1 when the key is absent. Return default.
|
|
104
|
+
return defaultValue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---------------------------------------------------------------
|
|
108
|
+
// common-dir
|
|
109
|
+
// ---------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
function commonDir(projectRoot) {
|
|
112
|
+
const res = git(projectRoot, ['rev-parse', '--git-common-dir']);
|
|
113
|
+
if (res.status !== 0) {
|
|
114
|
+
return { ok: false, error: String(res.stderr || '').trim() };
|
|
115
|
+
}
|
|
116
|
+
const raw = String(res.stdout || '').trim();
|
|
117
|
+
if (!raw) return { ok: false, error: 'empty output' };
|
|
118
|
+
// git emits a relative path when run from inside the repo root; resolve
|
|
119
|
+
// against projectRoot for an absolute, parser-safe value.
|
|
120
|
+
const abs = path.isAbsolute(raw) ? raw : path.resolve(projectRoot, raw);
|
|
121
|
+
return { ok: true, value: abs };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ---------------------------------------------------------------
|
|
125
|
+
// safe-add
|
|
126
|
+
// ---------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
function safeAdd(projectRoot, paths) {
|
|
129
|
+
const added = [];
|
|
130
|
+
const skipped = [];
|
|
131
|
+
for (const p of paths) {
|
|
132
|
+
const abs = path.isAbsolute(p) ? p : path.join(projectRoot, p);
|
|
133
|
+
if (fs.existsSync(abs)) {
|
|
134
|
+
added.push(p);
|
|
135
|
+
} else {
|
|
136
|
+
skipped.push(p);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (added.length === 0) return { added, skipped };
|
|
140
|
+
const res = git(projectRoot, ['add', '--', ...added]);
|
|
141
|
+
if (res.status !== 0) {
|
|
142
|
+
// Surface partial-failure detail on stderr, but never throw.
|
|
143
|
+
log.warn(`git add failed (${res.status}): ${String(res.stderr || '').trim()}`);
|
|
144
|
+
}
|
|
145
|
+
return { added, skipped };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---------------------------------------------------------------
|
|
149
|
+
// CLI
|
|
150
|
+
// ---------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
function main() {
|
|
153
|
+
const { opts, positional } = parseArgs(process.argv.slice(2));
|
|
154
|
+
if (opts.help || positional.length === 0) {
|
|
155
|
+
help();
|
|
156
|
+
process.exit(opts.help ? 0 : 1);
|
|
157
|
+
}
|
|
158
|
+
const command = positional[0];
|
|
159
|
+
if (!VALID_COMMANDS.includes(command)) {
|
|
160
|
+
log.error(`unknown command '${command}'. Valid: ${VALID_COMMANDS.join(', ')}`);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
const projectRoot = opts['project-root'] || process.cwd();
|
|
164
|
+
|
|
165
|
+
if (command === 'count-worktrees') {
|
|
166
|
+
process.stdout.write(`${countWorktrees(projectRoot)}\n`);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (command === 'config-get') {
|
|
171
|
+
const key = positional[1];
|
|
172
|
+
if (!key) {
|
|
173
|
+
log.error('config-get requires <key>');
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
const defaultValue = opts.default !== undefined ? String(opts.default) : 'unset';
|
|
177
|
+
const scope = opts.scope ? String(opts.scope) : null;
|
|
178
|
+
if (scope && !VALID_SCOPES.includes(scope)) {
|
|
179
|
+
log.error(`invalid --scope '${scope}'. Valid: ${VALID_SCOPES.join(', ')}`);
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
process.stdout.write(`${configGet(projectRoot, key, { defaultValue, scope })}\n`);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (command === 'common-dir') {
|
|
187
|
+
const r = commonDir(projectRoot);
|
|
188
|
+
if (!r.ok) {
|
|
189
|
+
log.error(`failed to resolve git common dir: ${r.error}`);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
process.stdout.write(`${r.value}\n`);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (command === 'safe-add') {
|
|
197
|
+
const paths = positional.slice(1);
|
|
198
|
+
if (paths.length === 0) {
|
|
199
|
+
log.error('safe-add requires at least one <path>');
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
const summary = safeAdd(projectRoot, paths);
|
|
203
|
+
process.stdout.write(`${JSON.stringify(summary)}\n`);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
module.exports = {
|
|
209
|
+
VALID_COMMANDS,
|
|
210
|
+
VALID_SCOPES,
|
|
211
|
+
countWorktrees,
|
|
212
|
+
configGet,
|
|
213
|
+
commonDir,
|
|
214
|
+
safeAdd,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
if (require.main === module) {
|
|
218
|
+
main();
|
|
219
|
+
}
|