devflow-kit 3.7.0 → 3.8.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.
- package/CHANGELOG.md +359 -1
- package/README.md +11 -11
- package/dist/agents/code.md +2 -4
- package/dist/agents/design.md +1 -1
- package/dist/agents/fix.md +6 -7
- package/dist/agents/git.md +1 -1
- package/dist/agents/harden.md +1 -1
- package/dist/agents/refactor.md +2 -4
- package/dist/agents/review.md +4 -4
- package/dist/agents/skim.md +2 -2
- package/dist/agents/triage.md +2 -2
- package/dist/cli/commands/capture.js +1 -1
- package/dist/cli/commands/curation-move-io.js +102 -0
- package/dist/cli/commands/curation-move-step.js +374 -0
- package/dist/cli/commands/{learning.js → curation.js} +302 -99
- package/dist/cli/commands/gate-level-prompts.js +426 -0
- package/dist/cli/commands/gates.js +34 -11
- package/dist/cli/commands/init-seed.js +11 -8
- package/dist/cli/commands/init.js +240 -54
- package/dist/cli/commands/memory.js +2 -2
- package/dist/cli/commands/prompt-io.js +37 -0
- package/dist/cli/commands/uninstall.js +81 -9
- package/dist/cli.js +2 -2
- package/dist/commands/bug-analysis.md +24 -35
- package/dist/commands/code-review.md +32 -36
- package/dist/commands/debug.md +4 -4
- package/dist/commands/dynamic-build.md +25 -24
- package/dist/commands/dynamic-plan.md +4 -4
- package/dist/commands/dynamic-profile.md +4 -4
- package/dist/commands/dynamic-tickets.md +4 -4
- package/dist/commands/explore.md +5 -5
- package/dist/commands/implement.md +52 -81
- package/dist/commands/plan.md +5 -5
- package/dist/commands/release.md +4 -4
- package/dist/commands/research.md +5 -5
- package/dist/commands/resolve.md +28 -30
- package/dist/commands/self-review.md +4 -12
- package/dist/core/assets.js +20 -20
- package/dist/core/curate-tuning-config.js +5 -6
- package/dist/core/curation-move-notice.js +84 -0
- package/dist/core/curation-move-skip.js +36 -0
- package/dist/core/curation-move.js +35 -0
- package/dist/core/curation-queue-cleanup.js +134 -0
- package/dist/core/curation-store.js +98 -0
- package/dist/core/{learning-variants.js → curation-variants.js} +65 -41
- package/dist/core/echo-value.js +11 -5
- package/dist/core/evidence-policy.js +1 -1
- package/dist/core/feature-switch.js +95 -19
- package/dist/core/fs-atomic.js +1 -1
- package/dist/core/gate-level.js +10 -2
- package/dist/core/git.js +16 -14
- package/dist/core/ledger-root.js +82 -22
- package/dist/core/linked-path.js +4 -4
- package/dist/core/manifest-lock.js +238 -0
- package/dist/core/manifest.js +58 -14
- package/dist/core/migrations.js +1 -13
- package/dist/core/observations.js +2 -2
- package/dist/core/plugins.js +15 -15
- package/dist/core/pool.js +26 -0
- package/dist/core/project-paths.js +103 -34
- package/dist/core/queue-drain.js +2 -2
- package/dist/core/tracked-areas.js +35 -0
- package/dist/core/tracker.js +1 -1
- package/dist/{learning-off → curation-off}/agents/code.md +2 -4
- package/dist/{learning-off → curation-off}/agents/fix.md +6 -7
- package/dist/{learning-off → curation-off}/agents/refactor.md +2 -4
- package/dist/{learning-off → curation-off}/agents/review.md +3 -3
- package/dist/{learning-off → curation-off}/agents/triage.md +1 -1
- package/dist/{learning-off → curation-off}/commands/bug-analysis.md +20 -31
- package/dist/{learning-off → curation-off}/commands/code-review.md +29 -33
- package/dist/{learning-off → curation-off}/commands/debug.md +1 -1
- package/dist/{learning-off → curation-off}/commands/dynamic-build.md +22 -21
- package/dist/{learning-off → curation-off}/commands/explore.md +1 -1
- package/dist/{learning-off → curation-off}/commands/implement.md +49 -78
- package/dist/{learning-off → curation-off}/commands/plan.md +1 -1
- package/dist/{learning-off → curation-off}/commands/resolve.md +25 -27
- package/dist/{learning-off → curation-off}/commands/self-review.md +1 -9
- package/dist/hud/components/{learning-counts.js → curation-counts.js} +36 -17
- package/dist/hud/config.js +1 -1
- package/dist/hud/index.js +6 -6
- package/dist/hud/render.js +3 -3
- package/dist/skills/git/references/pr/validate-branch.md +8 -5
- package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +13 -5
- package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +13 -5
- package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +13 -5
- package/dist/targets/claude-code/{learning-install.js → curation-install.js} +85 -65
- package/dist/targets/claude-code/installer.js +15 -15
- package/dist/targets/claude-code/language-stamp.js +5 -5
- package/dist/targets/claude-code/post-install.js +2 -2
- package/dist/targets/claude-code/tracker-servers.js +1 -1
- package/package.json +3 -2
- package/src/assets/agents/_partials/_code.mds +11 -11
- package/src/assets/agents/code.mds +2 -2
- package/src/assets/agents/curate.md +8 -8
- package/src/assets/agents/debug.mds +6 -6
- package/src/assets/agents/design.mds +18 -18
- package/src/assets/agents/fix.mds +6 -5
- package/src/assets/agents/git.mds +1 -1
- package/src/assets/agents/harden.mds +7 -7
- package/src/assets/agents/knowledge.mds +23 -23
- package/src/assets/agents/refactor.mds +2 -2
- package/src/assets/agents/research.mds +15 -15
- package/src/assets/agents/review.mds +43 -43
- package/src/assets/agents/skim.mds +11 -11
- package/src/assets/agents/triage.mds +22 -22
- package/src/assets/agents/validate.md +13 -26
- package/src/assets/commands/_partials/_decisions.mds +13 -13
- package/src/assets/commands/_partials/_engine.mds +8 -8
- package/src/assets/commands/_partials/_knowledge.mds +29 -15
- package/src/assets/commands/_partials/_preamble.mds +4 -4
- package/src/assets/commands/_partials/_settings.mds +2 -2
- package/src/assets/commands/_partials/_wave.mds +7 -7
- package/src/assets/commands/bug-analysis.mds +37 -48
- package/src/assets/commands/code-review.mds +46 -50
- package/src/assets/commands/debug.mds +14 -14
- package/src/assets/commands/dynamic-build.mds +38 -37
- package/src/assets/commands/dynamic-plan.mds +22 -22
- package/src/assets/commands/dynamic-profile.mds +2 -2
- package/src/assets/commands/dynamic-tickets.mds +8 -8
- package/src/assets/commands/explore.mds +20 -20
- package/src/assets/commands/implement.mds +77 -98
- package/src/assets/commands/plan.mds +27 -27
- package/src/assets/commands/release.mds +12 -12
- package/src/assets/commands/research.mds +19 -19
- package/src/assets/commands/resolve.mds +56 -50
- package/src/assets/commands/self-review.mds +20 -20
- package/src/assets/mds/git/_ops.mds +3 -1
- package/src/assets/mds/git/_ops_pr.mds +1 -1
- package/src/assets/mds/git/_ops_steps.mds +10 -4
- package/src/assets/mds/git/_pr.mds +7 -4
- package/src/assets/rules/quality.md +1 -0
- package/src/assets/scripts/hooks/background-memory-update +2 -2
- package/src/assets/scripts/hooks/capture-prompt +12 -12
- package/src/assets/scripts/hooks/capture-question +11 -11
- package/src/assets/scripts/hooks/capture-turn +12 -12
- package/src/assets/scripts/hooks/{learning-lock → curation-lock} +2 -2
- package/src/assets/scripts/hooks/curation-move +293 -0
- package/src/assets/scripts/hooks/ensure-devflow-init +45 -13
- package/src/assets/scripts/hooks/ensure-proxy +4 -4
- package/src/assets/scripts/hooks/ensure-root-gitignore +4 -4
- package/src/assets/scripts/hooks/git-marker +81 -0
- package/src/assets/scripts/hooks/json-helper.cjs +134 -51
- package/src/assets/scripts/hooks/json-parse +1 -1
- package/src/assets/scripts/hooks/lib/curation-move.cjs +1582 -0
- package/src/assets/scripts/hooks/lib/{learning-store.cjs → curation-store.cjs} +242 -118
- package/src/assets/scripts/hooks/lib/decisions-format.cjs +3 -3
- package/src/assets/scripts/hooks/lib/mkdir-lock.cjs +24 -7
- package/src/assets/scripts/hooks/lib/project-paths.cjs +132 -39
- package/src/assets/scripts/hooks/lib/render-decisions.cjs +34 -22
- package/src/assets/scripts/hooks/lib/tracked-areas.cjs +259 -0
- package/src/assets/scripts/hooks/memory-worker +1 -1
- package/src/assets/scripts/hooks/pre-compact-memory +6 -16
- package/src/assets/scripts/hooks/queue-append +228 -103
- package/src/assets/scripts/hooks/resolve-project-root +2 -2
- package/src/assets/scripts/hooks/session-start-context +143 -69
- package/src/assets/scripts/hooks/session-start-memory +3 -43
- package/src/assets/scripts/lib/project-config.cjs +12 -5
- package/src/assets/scripts/resolve-base.cjs +1043 -0
- package/src/assets/scripts/resolve-settings.cjs +42 -18
- package/src/assets/skills/agent-references/SKILL.md +5 -2
- package/src/assets/skills/agent-references/references/commands/implement-levels.md +27 -0
- package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +6 -4
- package/src/assets/skills/agent-references/references/commands/resolve-levels.md +24 -0
- package/src/assets/skills/apply-decisions/SKILL.md +1 -1
- package/src/assets/skills/docs-framework/SKILL.md +7 -7
- package/src/assets/skills/docs-framework/references/patterns.md +3 -4
- package/src/assets/skills/quality-gates/SKILL.md +1 -1
- package/src/assets/skills/worktree-support/references/roots.md +2 -2
- package/templates/lint-pack/.lint-pack.conf +86 -0
- package/templates/lint-pack/README.md +190 -0
- package/templates/lint-pack/lint-pack.sh +905 -0
- package/dist/core/learning-queue-cleanup.js +0 -27
- package/dist/core/learning-store.js +0 -61
- /package/dist/{learning-off → curation-off}/agents/debug.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/design.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/harden.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/knowledge.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/research.md +0 -0
- /package/dist/{learning-off → curation-off}/agents/skim.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-plan.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-profile.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/dynamic-tickets.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/release.md +0 -0
- /package/dist/{learning-off → curation-off}/commands/research.md +0 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
// src/assets/scripts/hooks/lib/tracked-areas.cjs
|
|
2
|
+
//
|
|
3
|
+
// Does git track any entry under one of devflow's data folders at a root? One
|
|
4
|
+
// `git ls-files` call over a fixed set of pathspecs answers `tracked`, `untracked` or
|
|
5
|
+
// `unknown`. The one-time ledger move and the backup sweep ask it before they lock, read
|
|
6
|
+
// or rename anything: a folder that git tracks is the repository's content, so they leave
|
|
7
|
+
// it alone.
|
|
8
|
+
//
|
|
9
|
+
// DESIGN: a leaf. It loads node built-ins and project-paths.cjs (which loads node
|
|
10
|
+
// built-ins only) and requires neither the store nor the move module (the move requires
|
|
11
|
+
// the store; the store never requires the move), so either of them, and a check for
|
|
12
|
+
// another folder, can use it. No function here prints or exits, and nothing throws:
|
|
13
|
+
// every failure is the answer `unknown`, with a cause from the closed set UNKNOWN_CAUSES.
|
|
14
|
+
//
|
|
15
|
+
// The folder names come from project-paths.cjs, which owns them: AREA_FOLDERS lists the
|
|
16
|
+
// data folders and the backup prefix names the folders the move leaves behind. To cover
|
|
17
|
+
// another folder, add its name to AREA_FOLDERS.
|
|
18
|
+
//
|
|
19
|
+
// The decision is the repository's index, never the disk. A root with no `.git` entry
|
|
20
|
+
// (a file or a folder) is not a git working tree and gets no git call. A root with one
|
|
21
|
+
// gets exactly one call, and every failure of that call (git missing, a timeout, a
|
|
22
|
+
// non-zero exit with nothing listed) is `unknown`: the caller cannot tell what git
|
|
23
|
+
// tracks and must change nothing. git's error text is never read.
|
|
24
|
+
|
|
25
|
+
'use strict';
|
|
26
|
+
|
|
27
|
+
const childProcess = require('child_process');
|
|
28
|
+
const fs = require('fs');
|
|
29
|
+
const path = require('path');
|
|
30
|
+
|
|
31
|
+
const { LEGACY_NAMES, CURATION_DIR_NAME } = require('./project-paths.cjs');
|
|
32
|
+
|
|
33
|
+
/** The data folders under `.devflow` that the check covers, by the names the path helpers hold. */
|
|
34
|
+
const AREA_FOLDERS = Object.freeze([LEGACY_NAMES.folder, CURATION_DIR_NAME]);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The spellings a case-folding file system opens as `.devflow` beyond ASCII letter case.
|
|
38
|
+
* git's `icase` folds ASCII letters only, but APFS (the macOS default) compares names by
|
|
39
|
+
* full Unicode case folding, so it also opens `.devflow` (U+FB02 folds to "fl"). The code
|
|
40
|
+
* is written with an escape, so the code point is named.
|
|
41
|
+
*/
|
|
42
|
+
const DEVFLOW_SPELLINGS = Object.freeze(['.devflow', '.dev\u{FB02}ow']);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The pathspecs of the check, each matched without regard to ASCII letter case, whatever
|
|
46
|
+
* the file system or `core.ignorecase`, and each spelled every way DEVFLOW_SPELLINGS lists:
|
|
47
|
+
* - each folder of AREA_FOLDERS, as a literal path: an index entry at the folder, or
|
|
48
|
+
* anywhere below it, matches
|
|
49
|
+
* - an entry named like a backup folder (`<backup prefix>*`), and every entry below one
|
|
50
|
+
* (`<backup prefix>*` + `/**`): the move leaves its old folder there, and the sweep
|
|
51
|
+
* merges the queue files in it into the new folder. The prefix has no glob character.
|
|
52
|
+
* - an index entry named `.devflow` ITSELF, which git records only for a symlink, a
|
|
53
|
+
* gitlink (submodule) or a plain file, never for a directory: the character class
|
|
54
|
+
* keeps `glob` from matching as a leading directory, so the many files under a
|
|
55
|
+
* normal `.devflow/` are not listed. Committed content then stands at the path where
|
|
56
|
+
* the folders are opened.
|
|
57
|
+
*/
|
|
58
|
+
const TRACKED_AREA_PATHSPECS = Object.freeze([
|
|
59
|
+
...DEVFLOW_SPELLINGS.flatMap(dir => [
|
|
60
|
+
...AREA_FOLDERS.map(area => ':(icase,literal)' + dir + '/' + area),
|
|
61
|
+
':(icase,glob)' + dir + '/' + LEGACY_NAMES.backupPrefix + '*',
|
|
62
|
+
':(icase,glob)' + dir + '/' + LEGACY_NAMES.backupPrefix + '*/**',
|
|
63
|
+
]),
|
|
64
|
+
...DEVFLOW_SPELLINGS.map(dir => ':(icase,glob)[.]' + dir.slice(1)),
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Why a check answered `unknown`, as one fixed word. The words come from this list
|
|
69
|
+
* alone: they name a spawn error code, a time out, a signal, an exit status or the step
|
|
70
|
+
* that failed, and never carry git's own text or a path.
|
|
71
|
+
*/
|
|
72
|
+
const UNKNOWN_CAUSES = Object.freeze({
|
|
73
|
+
rootNotAbsolute: 'root-not-absolute',
|
|
74
|
+
gitEntryUnreadable: 'git-entry-unreadable',
|
|
75
|
+
ceilingUnusable: 'ceiling-unusable',
|
|
76
|
+
checkFailed: 'check-failed',
|
|
77
|
+
gitNotFound: 'git-not-found',
|
|
78
|
+
gitTimedOut: 'git-timed-out',
|
|
79
|
+
gitStopped: 'git-stopped',
|
|
80
|
+
gitExitStatus: 'git-exit-status',
|
|
81
|
+
gitNotRun: 'git-not-run',
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/** The longest the git call may run (ms). */
|
|
85
|
+
const TRACKED_AREAS_GIT_TIMEOUT_MS = 5000;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The most output the git call may return, in bytes. A normal answer is empty; a
|
|
89
|
+
* listing past this bound (ENOBUFS) means entries exist, which reads as tracked.
|
|
90
|
+
*/
|
|
91
|
+
const TRACKED_AREAS_GIT_MAX_BUFFER = 4096;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The variables that make git read another repository, another index or other pathspec
|
|
95
|
+
* rules than the root's own. The call runs without them.
|
|
96
|
+
*/
|
|
97
|
+
const GIT_REDIRECT_VARIABLES = Object.freeze([
|
|
98
|
+
'GIT_DIR',
|
|
99
|
+
'GIT_WORK_TREE',
|
|
100
|
+
'GIT_INDEX_FILE',
|
|
101
|
+
'GIT_COMMON_DIR',
|
|
102
|
+
'GIT_OBJECT_DIRECTORY',
|
|
103
|
+
'GIT_ALTERNATE_OBJECT_DIRECTORIES',
|
|
104
|
+
'GIT_NAMESPACE',
|
|
105
|
+
'GIT_LITERAL_PATHSPECS',
|
|
106
|
+
'GIT_GLOB_PATHSPECS',
|
|
107
|
+
'GIT_NOGLOB_PATHSPECS',
|
|
108
|
+
'GIT_ICASE_PATHSPECS',
|
|
109
|
+
]);
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The environment of the git call: the process environment without the redirecting
|
|
113
|
+
* variables, with a fixed locale, no optional lock and a ceiling directory. Answers a new
|
|
114
|
+
* object. The ceiling is the folder that holds the root: git reads the root's own `.git`
|
|
115
|
+
* and never looks in a folder above it, so an empty or broken `.git` at the root is not
|
|
116
|
+
* answered from a work tree around the project.
|
|
117
|
+
*
|
|
118
|
+
* @param {NodeJS.ProcessEnv} base
|
|
119
|
+
* @param {string} ceiling - an absolute path with no list separator in it
|
|
120
|
+
* @returns {NodeJS.ProcessEnv}
|
|
121
|
+
*/
|
|
122
|
+
function trackedAreasGitEnv(base, ceiling) {
|
|
123
|
+
const env = { ...base };
|
|
124
|
+
for (const name of GIT_REDIRECT_VARIABLES) delete env[name];
|
|
125
|
+
env.LC_ALL = 'C';
|
|
126
|
+
env.GIT_OPTIONAL_LOCKS = '0';
|
|
127
|
+
env.GIT_CEILING_DIRECTORIES = ceiling;
|
|
128
|
+
return env;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The answer of one `git ls-files` call, from the result `spawnSync` gave, with the
|
|
133
|
+
* cause when the answer is `unknown` (null otherwise):
|
|
134
|
+
* tracked any output byte, whatever the exit status or error (the call listed an
|
|
135
|
+
* entry), and an output past the buffer cap (ENOBUFS)
|
|
136
|
+
* untracked exit 0 with nothing listed
|
|
137
|
+
* unknown every other result, with the cause: git-not-found (spawn error ENOENT),
|
|
138
|
+
* git-timed-out (ETIMEDOUT), git-not-run (another spawn error, or a result
|
|
139
|
+
* of the wrong shape), git-stopped (a signal ended it), git-exit-status (a
|
|
140
|
+
* non-zero exit with nothing listed)
|
|
141
|
+
* Pure: it reads only the fields `error.code`, `status`, `signal` and `stdout`.
|
|
142
|
+
*
|
|
143
|
+
* @param {unknown} result
|
|
144
|
+
* @returns {{ answer: 'tracked' | 'untracked' | 'unknown', cause: string | null }}
|
|
145
|
+
*/
|
|
146
|
+
function judgeTrackedAreas(result) {
|
|
147
|
+
if (result === null || typeof result !== 'object') return unknownBecause(UNKNOWN_CAUSES.gitNotRun);
|
|
148
|
+
const { error, status, signal, stdout } = /** @type {{ error?: { code?: unknown }, status?: unknown, signal?: unknown, stdout?: unknown }} */ (result);
|
|
149
|
+
if (error && error.code === 'ENOBUFS') return { answer: 'tracked', cause: null };
|
|
150
|
+
const listed = (typeof stdout === 'string' || Buffer.isBuffer(stdout)) && stdout.length > 0;
|
|
151
|
+
if (listed) return { answer: 'tracked', cause: null };
|
|
152
|
+
if (error) {
|
|
153
|
+
if (error.code === 'ENOENT') return unknownBecause(UNKNOWN_CAUSES.gitNotFound);
|
|
154
|
+
return unknownBecause(error.code === 'ETIMEDOUT' ? UNKNOWN_CAUSES.gitTimedOut : UNKNOWN_CAUSES.gitNotRun);
|
|
155
|
+
}
|
|
156
|
+
if (status === 0) return { answer: 'untracked', cause: null };
|
|
157
|
+
if (typeof status === 'number') return unknownBecause(UNKNOWN_CAUSES.gitExitStatus);
|
|
158
|
+
return unknownBecause(typeof signal === 'string' ? UNKNOWN_CAUSES.gitStopped : UNKNOWN_CAUSES.gitNotRun);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @param {string} cause - a value of UNKNOWN_CAUSES
|
|
163
|
+
* @returns {{ answer: 'unknown', cause: string }}
|
|
164
|
+
*/
|
|
165
|
+
function unknownBecause(cause) {
|
|
166
|
+
return { answer: 'unknown', cause };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* The answer of {@link judgeTrackedAreas}, without its cause.
|
|
171
|
+
*
|
|
172
|
+
* @param {unknown} result
|
|
173
|
+
* @returns {'tracked' | 'untracked' | 'unknown'}
|
|
174
|
+
*/
|
|
175
|
+
function classifyTrackedAreas(result) {
|
|
176
|
+
return judgeTrackedAreas(result).answer;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Whether anything (a file, a folder, a link) stands at `target`: true, false, or null
|
|
181
|
+
* when the probe failed for a reason other than absence.
|
|
182
|
+
*
|
|
183
|
+
* @param {string} target
|
|
184
|
+
* @returns {boolean | null}
|
|
185
|
+
*/
|
|
186
|
+
function entryExists(target) {
|
|
187
|
+
try {
|
|
188
|
+
fs.lstatSync(target);
|
|
189
|
+
return true;
|
|
190
|
+
} catch (err) {
|
|
191
|
+
return err && (err.code === 'ENOENT' || err.code === 'ENOTDIR') ? false : null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Whether git tracks any entry under the covered folders at `root`, or an entry named
|
|
197
|
+
* `.devflow` itself (a link, a gitlink or a file). The index decides, not the disk, and
|
|
198
|
+
* a staged entry counts. The answer carries the cause (a value of UNKNOWN_CAUSES) when
|
|
199
|
+
* it is `unknown`, and null otherwise.
|
|
200
|
+
*
|
|
201
|
+
* untracked no `.git` entry at the root (no git call is made), or git listed nothing
|
|
202
|
+
* tracked git listed an entry
|
|
203
|
+
* unknown a `.git` entry stands and git did not answer (see judgeTrackedAreas),
|
|
204
|
+
* the `.git` entry could not be probed, or the root is not an absolute path
|
|
205
|
+
* string (no git call is made)
|
|
206
|
+
*
|
|
207
|
+
* The call is `git --no-literal-pathspecs -c core.fsmonitor=false ls-files -z -- <paths>`
|
|
208
|
+
* with the root as its working directory: an argv with no shell, a time bound and an
|
|
209
|
+
* output bound. `--no-literal-pathspecs` leads the argv, so a forced literal-pathspec
|
|
210
|
+
* setting cannot turn the magic off and hide an entry. Reading the index makes git run
|
|
211
|
+
* the command a repository's config names in `core.fsmonitor`, so the call turns that off
|
|
212
|
+
* for itself and stays a pure read. The folder above the root's real path is a ceiling
|
|
213
|
+
* directory (trackedAreasGitEnv); a parent path that holds the list separator can not be
|
|
214
|
+
* named as one, and answers `unknown`.
|
|
215
|
+
*
|
|
216
|
+
* @param {unknown} root - an absolute path
|
|
217
|
+
* @returns {{ answer: 'tracked' | 'untracked' | 'unknown', cause: string | null }}
|
|
218
|
+
*/
|
|
219
|
+
function inspectTrackedAreas(root) {
|
|
220
|
+
if (typeof root !== 'string' || !path.isAbsolute(root)) return unknownBecause(UNKNOWN_CAUSES.rootNotAbsolute);
|
|
221
|
+
try {
|
|
222
|
+
const gitEntry = entryExists(path.join(root, '.git'));
|
|
223
|
+
if (gitEntry === null) return unknownBecause(UNKNOWN_CAUSES.gitEntryUnreadable);
|
|
224
|
+
if (gitEntry === false) return { answer: 'untracked', cause: null };
|
|
225
|
+
const ceiling = path.dirname(fs.realpathSync(root));
|
|
226
|
+
if (ceiling.includes(path.delimiter)) return unknownBecause(UNKNOWN_CAUSES.ceilingUnusable);
|
|
227
|
+
const result = childProcess.spawnSync('git', ['--no-literal-pathspecs', '-c', 'core.fsmonitor=false', 'ls-files', '-z', '--', ...TRACKED_AREA_PATHSPECS], {
|
|
228
|
+
cwd: root,
|
|
229
|
+
env: trackedAreasGitEnv(process.env, ceiling),
|
|
230
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
231
|
+
timeout: TRACKED_AREAS_GIT_TIMEOUT_MS,
|
|
232
|
+
maxBuffer: TRACKED_AREAS_GIT_MAX_BUFFER,
|
|
233
|
+
windowsHide: true,
|
|
234
|
+
shell: false,
|
|
235
|
+
});
|
|
236
|
+
return judgeTrackedAreas(result);
|
|
237
|
+
} catch (_) {
|
|
238
|
+
return unknownBecause(UNKNOWN_CAUSES.checkFailed);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* The answer of {@link inspectTrackedAreas}, without its cause.
|
|
244
|
+
*
|
|
245
|
+
* @param {unknown} root - an absolute path
|
|
246
|
+
* @returns {'tracked' | 'untracked' | 'unknown'}
|
|
247
|
+
*/
|
|
248
|
+
function checkTrackedAreas(root) {
|
|
249
|
+
return inspectTrackedAreas(root).answer;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
module.exports = {
|
|
253
|
+
TRACKED_AREA_PATHSPECS,
|
|
254
|
+
UNKNOWN_CAUSES,
|
|
255
|
+
judgeTrackedAreas,
|
|
256
|
+
classifyTrackedAreas,
|
|
257
|
+
inspectTrackedAreas,
|
|
258
|
+
checkTrackedAreas,
|
|
259
|
+
};
|
|
@@ -43,7 +43,7 @@ MEMORY_DIR="$PROJECT_DEVFLOW_DIR/memory"
|
|
|
43
43
|
|
|
44
44
|
# Memory gate: the machine switch, narrowed by this checkout's project.json /
|
|
45
45
|
# config.json `features.memory`, gates this hook entirely -- read by the helper
|
|
46
|
-
# every memory/
|
|
46
|
+
# every memory/curation gate shares (D-FEATURES-NARROW-ONLY, see queue-append).
|
|
47
47
|
source "$SCRIPT_DIR/queue-append" || { echo "memory-worker: failed to source queue-append" >&2; exit 1; }
|
|
48
48
|
queue_read_gates "$DEVFLOW_MANIFEST" "$PROJECT_ROOT"
|
|
49
49
|
MEMORY_ENABLED="$_QG_MEMORY"
|
|
@@ -54,7 +54,7 @@ MEMORY_DIR="$PROJECT_DEVFLOW_DIR/memory"
|
|
|
54
54
|
source "$SCRIPT_DIR/hook-log-init" "pre-compact-memory"
|
|
55
55
|
|
|
56
56
|
# Memory gate: the machine switch, narrowed by this checkout's project.json /
|
|
57
|
-
# config.json `features.memory`, read by the helper every memory/
|
|
57
|
+
# config.json `features.memory`, read by the helper every memory/curation gate
|
|
58
58
|
# shares (D-FEATURES-NARROW-ONLY, see queue-append).
|
|
59
59
|
source "$SCRIPT_DIR/queue-append" || { echo "pre-compact-memory: failed to source queue-append" >&2; exit 1; }
|
|
60
60
|
queue_read_gates "$DEVFLOW_MANIFEST" "$PROJECT_ROOT"
|
|
@@ -112,27 +112,18 @@ if cd "$CWD" 2>/dev/null && git rev-parse --git-dir >/dev/null 2>&1; then
|
|
|
112
112
|
dbg "GIT_BRANCH=$GIT_BRANCH HEAD=$GIT_HEAD_SHA"
|
|
113
113
|
fi
|
|
114
114
|
|
|
115
|
-
# Snapshot current WORKING-MEMORY.md (preserves session context through compaction).
|
|
116
|
-
# The snapshot goes into the backup the next session start injects, so a working
|
|
117
|
-
# memory a symbolic link leads to is treated as absent (D-HOOKS-NO-SYMLINK).
|
|
118
|
-
MEMORY_SNAPSHOT=""
|
|
119
|
-
if df_file_below "$PROJECT_ROOT" "$MEMORY_DIR/WORKING-MEMORY.md"; then
|
|
120
|
-
MEMORY_SNAPSHOT=$(head -c 65536 "$MEMORY_DIR/WORKING-MEMORY.md")
|
|
121
|
-
dbg "MEMORY_SNAPSHOT_LENGTH=${#MEMORY_SNAPSHOT}"
|
|
122
|
-
fi
|
|
123
|
-
|
|
124
115
|
# Write backup JSON
|
|
125
116
|
# D-BACKUP-RENAME: the backup is written whole into a copy beside it, then
|
|
126
117
|
# renamed over it; it is never truncated and refilled in place. Reason:
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
#
|
|
118
|
+
# a read landing between a truncate and the write that refills it would meet a
|
|
119
|
+
# partial file; a rename hands every read either the previous backup or the new
|
|
120
|
+
# one. SEC-2: the copy is created
|
|
130
121
|
# under umask 077, and only where nothing stands at its name: builtin tests come
|
|
131
122
|
# first, so an entry already there, a link to a FIFO or a device included, is
|
|
132
123
|
# never opened (noclobber alone would open one, as no regular file stands there),
|
|
133
124
|
# and noclobber then makes the create itself exclusive. The rename gives the
|
|
134
|
-
# backup the copy's inode and mode, so the backup
|
|
135
|
-
#
|
|
125
|
+
# backup the copy's inode and mode, so the Git-state backup is 0600 whatever
|
|
126
|
+
# the caller's umask (as in queue-append). A write or
|
|
136
127
|
# rename that fails removes whatever holds the copy's name and keeps the
|
|
137
128
|
# previous backup; the bootstrap below still runs.
|
|
138
129
|
#
|
|
@@ -157,7 +148,6 @@ elif [ ! -e "$BACKUP_TMP" ] && [ ! -L "$BACKUP_TMP" ] && (umask 077 && set -o no
|
|
|
157
148
|
--arg status "$GIT_STATUS" \
|
|
158
149
|
--arg log "$GIT_LOG" \
|
|
159
150
|
--arg diff "$GIT_DIFF_STAT" \
|
|
160
|
-
--arg memory "$MEMORY_SNAPSHOT" \
|
|
161
151
|
> "$BACKUP_TMP") && mv "$BACKUP_TMP" "$BACKUP_FILE"; then
|
|
162
152
|
log "Wrote backup: $BACKUP_FILE"
|
|
163
153
|
dbg "Wrote backup: $BACKUP_FILE"
|