@ikunin/sprintpilot 2.2.18 → 2.2.19
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.
|
@@ -867,31 +867,123 @@ function decorateGitOp(action, state, profile, projectRoot) {
|
|
|
867
867
|
// options), inline the resulting `steps[]` onto the action.
|
|
868
868
|
function decorateRunScript(action, state, profile, projectRoot) {
|
|
869
869
|
if (!action || action.type !== 'run_script') return action;
|
|
870
|
-
if (action.op
|
|
871
|
-
|
|
870
|
+
if (action.op === 'land_story') {
|
|
871
|
+
try {
|
|
872
|
+
const root = projectRoot || process.cwd();
|
|
873
|
+
const scriptsDir = path.join(root, '_Sprintpilot', 'scripts');
|
|
874
|
+
const snapshotPath = path.join(
|
|
875
|
+
root,
|
|
876
|
+
'_bmad-output',
|
|
877
|
+
'implementation-artifacts',
|
|
878
|
+
'.land-snapshots',
|
|
879
|
+
`${state.story_key || 'sprint'}.json`,
|
|
880
|
+
);
|
|
881
|
+
const branch = gitPlan.branchName(profile, state.story_key, state.current_epic, state);
|
|
882
|
+
const platform = profile.platform_provider || 'auto';
|
|
883
|
+
const planned = land.planLand(state, profile, {
|
|
884
|
+
scriptsDir,
|
|
885
|
+
snapshotPath,
|
|
886
|
+
branch,
|
|
887
|
+
platform,
|
|
888
|
+
projectRoot: root,
|
|
889
|
+
});
|
|
890
|
+
return { ...action, branch: planned.branch, steps: planned.steps };
|
|
891
|
+
} catch (e) {
|
|
892
|
+
log.warn(`land-plan failed for op=${action.op}: ${e.message}`);
|
|
893
|
+
return action;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
if (action.op === 'install_dependencies') {
|
|
872
897
|
const root = projectRoot || process.cwd();
|
|
873
|
-
const
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
'
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
898
|
+
const steps = planDependencyInstall(root);
|
|
899
|
+
if (steps.length === 0) {
|
|
900
|
+
// No manifest detected — fall back to a no-op success rather than
|
|
901
|
+
// halting the autopilot on an unrecognized project shape. The LLM
|
|
902
|
+
// already had a recoverable blocker; the orchestrator's retry will
|
|
903
|
+
// either succeed (the LLM resolves the dependency another way) or
|
|
904
|
+
// hit the retry budget and prompt.
|
|
905
|
+
return { ...action, steps: [], no_manifest_detected: true };
|
|
906
|
+
}
|
|
907
|
+
return { ...action, steps };
|
|
908
|
+
}
|
|
909
|
+
return action;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// Detect manifest files in the project root and return install steps
|
|
913
|
+
// for each language. Returns [] when no manifest is found (caller can
|
|
914
|
+
// degrade to a no-op rather than hardcoding npm install).
|
|
915
|
+
//
|
|
916
|
+
// Order matters: the first match wins for the install. We pick the
|
|
917
|
+
// first detected, since most projects are single-language at the root.
|
|
918
|
+
// Monorepos with multiple manifests still install for the primary
|
|
919
|
+
// (and the LLM can run additional installs via subsequent signals).
|
|
920
|
+
function planDependencyInstall(projectRoot) {
|
|
921
|
+
const exists = (rel) => {
|
|
922
|
+
try {
|
|
923
|
+
return fs.existsSync(path.join(projectRoot, rel));
|
|
924
|
+
} catch {
|
|
925
|
+
return false;
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
// pnpm / yarn / npm: pick the lockfile that exists; fall back to npm.
|
|
929
|
+
if (exists('package.json')) {
|
|
930
|
+
if (exists('pnpm-lock.yaml')) {
|
|
931
|
+
return [{ args: ['pnpm', 'install', '--frozen-lockfile'], description: 'install pnpm deps' }];
|
|
932
|
+
}
|
|
933
|
+
if (exists('yarn.lock')) {
|
|
934
|
+
return [{ args: ['yarn', 'install', '--frozen-lockfile'], description: 'install yarn deps' }];
|
|
935
|
+
}
|
|
936
|
+
if (exists('bun.lockb')) {
|
|
937
|
+
return [{ args: ['bun', 'install', '--frozen-lockfile'], description: 'install bun deps' }];
|
|
938
|
+
}
|
|
939
|
+
return [{ args: ['npm', 'install'], description: 'install npm deps' }];
|
|
940
|
+
}
|
|
941
|
+
// Python: prefer uv > poetry > pipenv > pip
|
|
942
|
+
if (exists('pyproject.toml')) {
|
|
943
|
+
if (exists('uv.lock')) return [{ args: ['uv', 'sync'], description: 'install python deps via uv' }];
|
|
944
|
+
if (exists('poetry.lock')) {
|
|
945
|
+
return [{ args: ['poetry', 'install'], description: 'install python deps via poetry' }];
|
|
946
|
+
}
|
|
947
|
+
return [{ args: ['pip', 'install', '-e', '.'], description: 'install python project deps' }];
|
|
948
|
+
}
|
|
949
|
+
if (exists('requirements.txt')) {
|
|
950
|
+
return [{ args: ['pip', 'install', '-r', 'requirements.txt'], description: 'install pip requirements' }];
|
|
951
|
+
}
|
|
952
|
+
if (exists('Pipfile')) {
|
|
953
|
+
return [{ args: ['pipenv', 'install'], description: 'install python deps via pipenv' }];
|
|
954
|
+
}
|
|
955
|
+
// Rust
|
|
956
|
+
if (exists('Cargo.toml')) {
|
|
957
|
+
return [{ args: ['cargo', 'fetch'], description: 'fetch rust deps via cargo' }];
|
|
958
|
+
}
|
|
959
|
+
// Go
|
|
960
|
+
if (exists('go.mod')) {
|
|
961
|
+
return [{ args: ['go', 'mod', 'download'], description: 'download go modules' }];
|
|
962
|
+
}
|
|
963
|
+
// Ruby
|
|
964
|
+
if (exists('Gemfile')) {
|
|
965
|
+
return [{ args: ['bundle', 'install'], description: 'install ruby deps via bundler' }];
|
|
966
|
+
}
|
|
967
|
+
// Java / Kotlin
|
|
968
|
+
if (exists('pom.xml')) {
|
|
969
|
+
return [{ args: ['mvn', '-q', 'dependency:resolve'], description: 'resolve maven deps' }];
|
|
970
|
+
}
|
|
971
|
+
if (exists('build.gradle') || exists('build.gradle.kts')) {
|
|
972
|
+
return [{ args: ['./gradlew', '--quiet', 'dependencies'], description: 'resolve gradle deps' }];
|
|
973
|
+
}
|
|
974
|
+
// PHP
|
|
975
|
+
if (exists('composer.json')) {
|
|
976
|
+
return [{ args: ['composer', 'install'], description: 'install composer deps' }];
|
|
977
|
+
}
|
|
978
|
+
// .NET
|
|
979
|
+
if (exists('global.json') || exists('*.csproj')) {
|
|
980
|
+
return [{ args: ['dotnet', 'restore'], description: 'restore dotnet deps' }];
|
|
981
|
+
}
|
|
982
|
+
// Swift
|
|
983
|
+
if (exists('Package.swift')) {
|
|
984
|
+
return [{ args: ['swift', 'package', 'resolve'], description: 'resolve swift packages' }];
|
|
894
985
|
}
|
|
986
|
+
return [];
|
|
895
987
|
}
|
|
896
988
|
|
|
897
989
|
// Detect whether a branch exists, locally OR on origin. Used by
|
|
@@ -273,6 +273,11 @@ function handleBlocked(state, signal, profile, sideEffects) {
|
|
|
273
273
|
// Recoverable blockers: deterministic recovery per kind (initial set).
|
|
274
274
|
switch (kind) {
|
|
275
275
|
case 'missing_dependency':
|
|
276
|
+
// Emit an abstract install action. The CLI edge (autopilot.js
|
|
277
|
+
// decorateRunScript) detects the project's language(s) from
|
|
278
|
+
// manifest files and inlines the concrete `command`. Pre-2.2.19
|
|
279
|
+
// this hardcoded `npm install`, which failed on non-Node projects
|
|
280
|
+
// (Python, Rust, Go, Ruby, etc.).
|
|
276
281
|
return {
|
|
277
282
|
newState: state,
|
|
278
283
|
newProfile: profile,
|
|
@@ -280,7 +285,8 @@ function handleBlocked(state, signal, profile, sideEffects) {
|
|
|
280
285
|
type: 'run_script',
|
|
281
286
|
phase: state.phase,
|
|
282
287
|
reason: 'install_missing_dependency',
|
|
283
|
-
|
|
288
|
+
op: 'install_dependencies',
|
|
289
|
+
details: signal.details || null,
|
|
284
290
|
},
|
|
285
291
|
sideEffects,
|
|
286
292
|
verdict: 'retry',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ikunin/sprintpilot",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.19",
|
|
4
4
|
"description": "Sprintpilot — autopilot and multi-agent addon for BMad Method v6: git workflow, parallel agents, autonomous story execution",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|